[ Index ] |
PHP Cross Reference of phpBB-3.3.12-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * 4 * This file is part of the phpBB Forum Software package. 5 * 6 * @copyright (c) phpBB Limited <https://www.phpbb.com> 7 * @license GNU General Public License, version 2 (GPL-2.0) 8 * 9 * For full copyright and license information, please see 10 * the docs/CREDITS.txt file. 11 * 12 */ 13 14 /** 15 * @ignore 16 */ 17 if (!defined('IN_PHPBB')) 18 { 19 exit; 20 } 21 22 class acp_styles 23 { 24 public $u_action; 25 26 protected $u_base_action; 27 protected $s_hidden_fields; 28 protected $mode; 29 protected $styles_path; 30 protected $styles_path_absolute = 'styles'; 31 protected $default_style = 0; 32 protected $styles_list_cols = 0; 33 protected $reserved_style_names = array('adm', 'admin', 'all'); 34 35 /** @var \phpbb\config\config */ 36 protected $config; 37 38 /** @var \phpbb\db\driver\driver_interface */ 39 protected $db; 40 41 /** @var \phpbb\user */ 42 protected $user; 43 44 /** @var \phpbb\template\template */ 45 protected $template; 46 47 /** @var \phpbb\request\request_interface */ 48 protected $request; 49 50 /** @var \phpbb\cache\driver\driver_interface */ 51 protected $cache; 52 53 /** @var \phpbb\auth\auth */ 54 protected $auth; 55 56 /** @var \phpbb\textformatter\cache_interface */ 57 protected $text_formatter_cache; 58 59 /** @var string */ 60 protected $phpbb_root_path; 61 62 /** @var string */ 63 protected $php_ext; 64 65 /** @var \phpbb\event\dispatcher_interface */ 66 protected $dispatcher; 67 68 public function main($id, $mode) 69 { 70 global $db, $user, $phpbb_admin_path, $phpbb_root_path, $phpEx, $template, $request, $cache, $auth, $config, $phpbb_dispatcher, $phpbb_container; 71 72 $this->db = $db; 73 $this->user = $user; 74 $this->template = $template; 75 $this->request = $request; 76 $this->cache = $cache; 77 $this->auth = $auth; 78 $this->text_formatter_cache = $phpbb_container->get('text_formatter.cache'); 79 $this->config = $config; 80 $this->phpbb_root_path = $phpbb_root_path; 81 $this->php_ext = $phpEx; 82 $this->dispatcher = $phpbb_dispatcher; 83 84 $this->default_style = $config['default_style']; 85 $this->styles_path = $this->phpbb_root_path . $this->styles_path_absolute . '/'; 86 87 $this->u_base_action = append_sid("{$phpbb_admin_path}index.{$this->php_ext}", "i={$id}"); 88 $this->s_hidden_fields = array( 89 'mode' => $mode, 90 ); 91 92 $this->user->add_lang('acp/styles'); 93 94 $this->tpl_name = 'acp_styles'; 95 $this->page_title = 'ACP_CAT_STYLES'; 96 $this->mode = $mode; 97 98 $action = $this->request->variable('action', ''); 99 $post_actions = array('install', 'activate', 'deactivate', 'uninstall'); 100 101 foreach ($post_actions as $key) 102 { 103 if ($this->request->is_set_post($key)) 104 { 105 $action = $key; 106 } 107 } 108 109 // The uninstall action uses confirm_box() to verify the validity of the request, 110 // so there is no need to check for a valid token here. 111 if (in_array($action, $post_actions) && $action != 'uninstall') 112 { 113 $is_valid_request = check_link_hash($request->variable('hash', ''), $action) || check_form_key('styles_management'); 114 115 if (!$is_valid_request) 116 { 117 trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING); 118 } 119 } 120 121 if ($action != '') 122 { 123 $this->s_hidden_fields['action'] = $action; 124 } 125 126 $this->template->assign_vars(array( 127 'U_ACTION' => $this->u_base_action, 128 'S_HIDDEN_FIELDS' => build_hidden_fields($this->s_hidden_fields) 129 ) 130 ); 131 132 /** 133 * Run code before ACP styles action execution 134 * 135 * @event core.acp_styles_action_before 136 * @var int id Module ID 137 * @var string mode Active module 138 * @var string action Module that should be run 139 * @since 3.1.7-RC1 140 */ 141 $vars = array('id', 'mode', 'action'); 142 extract($this->dispatcher->trigger_event('core.acp_styles_action_before', compact($vars))); 143 144 // Execute actions 145 switch ($action) 146 { 147 case 'install': 148 $this->action_install(); 149 return; 150 case 'uninstall': 151 $this->action_uninstall(); 152 return; 153 case 'activate': 154 $this->action_activate(); 155 return; 156 case 'deactivate': 157 $this->action_deactivate(); 158 return; 159 case 'details': 160 $this->action_details(); 161 return; 162 default: 163 $this->frontend(); 164 } 165 } 166 167 /** 168 * Main page 169 */ 170 protected function frontend() 171 { 172 add_form_key('styles_management'); 173 174 // Check mode 175 switch ($this->mode) 176 { 177 case 'style': 178 $this->welcome_message('ACP_STYLES', 'ACP_STYLES_EXPLAIN'); 179 $this->show_installed(); 180 return; 181 case 'install': 182 $this->welcome_message('INSTALL_STYLES', 'INSTALL_STYLES_EXPLAIN'); 183 $this->show_available(); 184 return; 185 } 186 trigger_error($this->user->lang['NO_MODE'] . adm_back_link($this->u_action), E_USER_WARNING); 187 } 188 189 /** 190 * Install style(s) 191 */ 192 protected function action_install() 193 { 194 // Get list of styles to install 195 $dirs = $this->request_vars('dir', '', true); 196 197 // Get list of styles that can be installed 198 $styles = $this->find_available(false); 199 200 // Install each style 201 $messages = array(); 202 $installed_names = array(); 203 $installed_dirs = array(); 204 foreach ($dirs as $dir) 205 { 206 if (in_array($dir, $this->reserved_style_names)) 207 { 208 $messages[] = $this->user->lang('STYLE_NAME_RESERVED', htmlspecialchars($dir, ENT_COMPAT)); 209 continue; 210 } 211 212 $found = false; 213 foreach ($styles as &$style) 214 { 215 // Check if: 216 // 1. Directory matches directory we are looking for 217 // 2. Style is not installed yet 218 // 3. Style with same name or directory hasn't been installed already within this function 219 if ($style['style_path'] == $dir && empty($style['_installed']) && !in_array($style['style_path'], $installed_dirs) && !in_array($style['style_name'], $installed_names)) 220 { 221 // Install style 222 $style['style_active'] = 1; 223 $style['style_id'] = $this->install_style($style); 224 $style['_installed'] = true; 225 $found = true; 226 $installed_names[] = $style['style_name']; 227 $installed_dirs[] = $style['style_path']; 228 $messages[] = sprintf($this->user->lang['STYLE_INSTALLED'], htmlspecialchars($style['style_name'], ENT_COMPAT)); 229 } 230 } 231 if (!$found) 232 { 233 $messages[] = sprintf($this->user->lang['STYLE_NOT_INSTALLED'], htmlspecialchars($dir, ENT_COMPAT)); 234 } 235 } 236 237 // Invalidate the text formatter's cache for the new styles to take effect 238 if (!empty($installed_names)) 239 { 240 $this->text_formatter_cache->invalidate(); 241 } 242 243 // Show message 244 if (!count($messages)) 245 { 246 trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING); 247 } 248 $message = implode('<br />', $messages); 249 $message .= '<br /><br /><a href="' . $this->u_base_action . '&mode=style' . '">« ' . $this->user->lang('STYLE_INSTALLED_RETURN_INSTALLED_STYLES') . '</a>'; 250 $message .= '<br /><br /><a href="' . $this->u_base_action . '&mode=install' . '">» ' . $this->user->lang('STYLE_INSTALLED_RETURN_UNINSTALLED_STYLES') . '</a>'; 251 trigger_error($message, E_USER_NOTICE); 252 } 253 254 /** 255 * Confirm styles removal 256 */ 257 protected function action_uninstall() 258 { 259 // Get list of styles to uninstall 260 $ids = $this->request_vars('id', 0, true); 261 262 // Don't remove prosilver, you can still deactivate it. 263 $sql = 'SELECT style_id 264 FROM ' . STYLES_TABLE . " 265 WHERE style_name = '" . $this->db->sql_escape('prosilver') . "'"; 266 $result = $this->db->sql_query($sql); 267 $prosilver_id = (int) $this->db->sql_fetchfield('style_id'); 268 $this->db->sql_freeresult($result); 269 270 if ($prosilver_id && in_array($prosilver_id, $ids)) 271 { 272 trigger_error($this->user->lang('UNINSTALL_PROSILVER') . adm_back_link($this->u_action), E_USER_WARNING); 273 } 274 275 // Check if confirmation box was submitted 276 if (confirm_box(true)) 277 { 278 // Uninstall 279 $this->action_uninstall_confirmed($ids, $this->request->variable('confirm_delete_files', false)); 280 return; 281 } 282 283 // Confirm box 284 $s_hidden = build_hidden_fields(array( 285 'action' => 'uninstall', 286 'ids' => $ids 287 )); 288 $this->template->assign_var('S_CONFIRM_DELETE', true); 289 confirm_box(false, $this->user->lang['CONFIRM_UNINSTALL_STYLES'], $s_hidden, 'acp_styles.html'); 290 291 // Canceled - show styles list 292 $this->frontend(); 293 } 294 295 /** 296 * Uninstall styles(s) 297 * 298 * @param array $ids List of style IDs 299 * @param bool $delete_files If true, script will attempt to remove files for selected styles 300 */ 301 protected function action_uninstall_confirmed($ids, $delete_files) 302 { 303 global $user, $phpbb_log; 304 305 $default = $this->default_style; 306 $uninstalled = array(); 307 $messages = array(); 308 309 // Check styles list 310 foreach ($ids as $id) 311 { 312 if (!$id) 313 { 314 trigger_error($this->user->lang['INVALID_STYLE_ID'] . adm_back_link($this->u_action), E_USER_WARNING); 315 } 316 if ($id == $default) 317 { 318 trigger_error($this->user->lang['UNINSTALL_DEFAULT'] . adm_back_link($this->u_action), E_USER_WARNING); 319 } 320 $uninstalled[$id] = false; 321 } 322 323 // Order by reversed style_id, so parent styles would be removed after child styles 324 // This way parent and child styles can be removed in same function call 325 $sql = 'SELECT * 326 FROM ' . STYLES_TABLE . ' 327 WHERE style_id IN (' . implode(', ', $ids) . ') 328 ORDER BY style_id DESC'; 329 $result = $this->db->sql_query($sql); 330 331 $rows = $this->db->sql_fetchrowset($result); 332 $this->db->sql_freeresult($result); 333 334 // Uinstall each style 335 $uninstalled = array(); 336 foreach ($rows as $style) 337 { 338 $result = $this->uninstall_style($style, $delete_files); 339 340 if (is_string($result)) 341 { 342 $messages[] = $result; 343 continue; 344 } 345 $messages[] = sprintf($this->user->lang['STYLE_UNINSTALLED'], $style['style_name']); 346 $uninstalled[] = $style['style_name']; 347 348 // Attempt to delete files 349 if ($delete_files) 350 { 351 $messages[] = sprintf($this->user->lang[$this->delete_style_files($style['style_path']) ? 'DELETE_STYLE_FILES_SUCCESS' : 'DELETE_STYLE_FILES_FAILED'], $style['style_name']); 352 } 353 } 354 355 if (empty($messages)) 356 { 357 // Nothing to uninstall? 358 trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING); 359 } 360 361 // Log action 362 if (count($uninstalled)) 363 { 364 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_STYLE_DELETE', false, array(implode(', ', $uninstalled))); 365 } 366 367 // Clear cache 368 $this->cache->purge(); 369 370 // Show message 371 trigger_error(implode('<br />', $messages) . adm_back_link($this->u_action), E_USER_NOTICE); 372 } 373 374 /** 375 * Activate styles 376 */ 377 protected function action_activate() 378 { 379 // Get list of styles to activate 380 $ids = $this->request_vars('id', 0, true); 381 382 // Activate styles 383 $sql = 'UPDATE ' . STYLES_TABLE . ' 384 SET style_active = 1 385 WHERE style_id IN (' . implode(', ', $ids) . ')'; 386 $this->db->sql_query($sql); 387 388 // Purge cache 389 $this->cache->destroy('sql', STYLES_TABLE); 390 391 // Show styles list 392 $this->frontend(); 393 } 394 395 /** 396 * Deactivate styles 397 */ 398 protected function action_deactivate() 399 { 400 // Get list of styles to deactivate 401 $ids = $this->request_vars('id', 0, true); 402 403 // Check for default style 404 foreach ($ids as $id) 405 { 406 if ($id == $this->default_style) 407 { 408 trigger_error($this->user->lang['DEACTIVATE_DEFAULT'] . adm_back_link($this->u_action), E_USER_WARNING); 409 } 410 } 411 412 // Reset default style for users who use selected styles 413 $sql = 'UPDATE ' . USERS_TABLE . ' 414 SET user_style = ' . (int) $this->default_style . ' 415 WHERE user_style IN (' . implode(', ', $ids) . ')'; 416 $this->db->sql_query($sql); 417 418 // Deactivate styles 419 $sql = 'UPDATE ' . STYLES_TABLE . ' 420 SET style_active = 0 421 WHERE style_id IN (' . implode(', ', $ids) . ')'; 422 $this->db->sql_query($sql); 423 424 // Purge cache 425 $this->cache->destroy('sql', STYLES_TABLE); 426 427 // Show styles list 428 $this->frontend(); 429 } 430 431 /** 432 * Show style details 433 */ 434 protected function action_details() 435 { 436 global $user, $phpbb_log; 437 438 $id = $this->request->variable('id', 0); 439 if (!$id) 440 { 441 trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING); 442 } 443 444 // Get all styles 445 $styles = $this->get_styles(); 446 usort($styles, array($this, 'sort_styles')); 447 448 // Find current style 449 $style = false; 450 foreach ($styles as $row) 451 { 452 if ($row['style_id'] == $id) 453 { 454 $style = $row; 455 break; 456 } 457 } 458 459 if ($style === false) 460 { 461 trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING); 462 } 463 464 // Read style configuration file 465 $style_cfg = $this->read_style_cfg($style['style_path']); 466 467 // Find all available parent styles 468 $list = $this->find_possible_parents($styles, $id); 469 470 // Add form key 471 $form_key = 'acp_styles'; 472 add_form_key($form_key); 473 474 // Change data 475 if ($this->request->variable('update', false)) 476 { 477 if (!check_form_key($form_key)) 478 { 479 trigger_error($this->user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING); 480 } 481 482 $update = array( 483 'style_name' => trim($this->request->variable('style_name', $style['style_name'])), 484 'style_parent_id' => $this->request->variable('style_parent', (int) $style['style_parent_id']), 485 'style_active' => $this->request->variable('style_active', (int) $style['style_active']), 486 ); 487 $update_action = $this->u_action . '&action=details&id=' . $id; 488 489 // Check style name 490 if ($update['style_name'] != $style['style_name']) 491 { 492 if (!strlen($update['style_name'])) 493 { 494 trigger_error($this->user->lang['STYLE_ERR_STYLE_NAME'] . adm_back_link($update_action), E_USER_WARNING); 495 } 496 foreach ($styles as $row) 497 { 498 if ($row['style_name'] == $update['style_name']) 499 { 500 trigger_error($this->user->lang['STYLE_ERR_NAME_EXIST'] . adm_back_link($update_action), E_USER_WARNING); 501 } 502 } 503 } 504 else 505 { 506 unset($update['style_name']); 507 } 508 509 // Check parent style id 510 if ($update['style_parent_id'] != $style['style_parent_id']) 511 { 512 if ($update['style_parent_id'] != 0) 513 { 514 $found = false; 515 foreach ($list as $row) 516 { 517 if ($row['style_id'] == $update['style_parent_id']) 518 { 519 $found = true; 520 $update['style_parent_tree'] = ($row['style_parent_tree'] != '' ? $row['style_parent_tree'] . '/' : '') . $row['style_path']; 521 break; 522 } 523 } 524 if (!$found) 525 { 526 trigger_error($this->user->lang['STYLE_ERR_INVALID_PARENT'] . adm_back_link($update_action), E_USER_WARNING); 527 } 528 } 529 else 530 { 531 $update['style_parent_tree'] = ''; 532 } 533 } 534 else 535 { 536 unset($update['style_parent_id']); 537 } 538 539 // Check style_active 540 if ($update['style_active'] != $style['style_active']) 541 { 542 if (!$update['style_active'] && $this->default_style == $style['style_id']) 543 { 544 trigger_error($this->user->lang['DEACTIVATE_DEFAULT'] . adm_back_link($update_action), E_USER_WARNING); 545 } 546 } 547 else 548 { 549 unset($update['style_active']); 550 } 551 552 // Update data 553 if (count($update)) 554 { 555 $sql = 'UPDATE ' . STYLES_TABLE . ' 556 SET ' . $this->db->sql_build_array('UPDATE', $update) . " 557 WHERE style_id = $id"; 558 $this->db->sql_query($sql); 559 560 $style = array_merge($style, $update); 561 562 if (isset($update['style_parent_id'])) 563 { 564 // Update styles tree 565 $styles = $this->get_styles(); 566 if ($this->update_styles_tree($styles, $style)) 567 { 568 // Something was changed in styles tree, purge all cache 569 $this->cache->purge(); 570 } 571 } 572 573 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_STYLE_EDIT_DETAILS', false, array($style['style_name'])); 574 } 575 576 // Update default style 577 $default = $this->request->variable('style_default', 0); 578 if ($default) 579 { 580 if (!$style['style_active']) 581 { 582 trigger_error($this->user->lang['STYLE_DEFAULT_CHANGE_INACTIVE'] . adm_back_link($update_action), E_USER_WARNING); 583 } 584 $this->config->set('default_style', $id); 585 $this->cache->purge(); 586 } 587 588 // Show styles list 589 $this->frontend(); 590 return; 591 } 592 593 // Show page title 594 $this->welcome_message('ACP_STYLES', null); 595 596 // Show parent styles 597 foreach ($list as $row) 598 { 599 $this->template->assign_block_vars('parent_styles', array( 600 'STYLE_ID' => $row['style_id'], 601 'STYLE_NAME' => htmlspecialchars($row['style_name'], ENT_COMPAT), 602 'LEVEL' => $row['level'], 603 'SPACER' => str_repeat(' ', $row['level']), 604 ) 605 ); 606 } 607 608 // Show style details 609 $this->template->assign_vars(array( 610 'S_STYLE_DETAILS' => true, 611 'STYLE_ID' => $style['style_id'], 612 'STYLE_NAME' => htmlspecialchars($style['style_name'], ENT_COMPAT), 613 'STYLE_PATH' => htmlspecialchars($style['style_path'], ENT_COMPAT), 614 'STYLE_VERSION' => htmlspecialchars($style_cfg['style_version'], ENT_COMPAT), 615 'STYLE_COPYRIGHT' => strip_tags($style['style_copyright']), 616 'STYLE_PARENT' => $style['style_parent_id'], 617 'S_STYLE_ACTIVE' => $style['style_active'], 618 'S_STYLE_DEFAULT' => ($style['style_id'] == $this->default_style) 619 ) 620 ); 621 } 622 623 /** 624 * List installed styles 625 */ 626 protected function show_installed() 627 { 628 // Get all installed styles 629 $styles = $this->get_styles(); 630 631 if (!count($styles)) 632 { 633 trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING); 634 } 635 636 usort($styles, array($this, 'sort_styles')); 637 638 // Get users 639 $users = $this->get_users(); 640 641 // Add users counter to rows 642 foreach ($styles as &$style) 643 { 644 $style['_users'] = isset($users[$style['style_id']]) ? $users[$style['style_id']] : 0; 645 } 646 647 // Set up styles list variables 648 // Addons should increase this number and update template variable 649 $this->styles_list_cols = 5; 650 $this->template->assign_var('STYLES_LIST_COLS', $this->styles_list_cols); 651 652 // Show styles list 653 $this->show_styles_list($styles, 0, 0); 654 655 // Show styles with invalid inherits_id 656 foreach ($styles as $style) 657 { 658 if (empty($style['_shown'])) 659 { 660 $style['_note'] = sprintf($this->user->lang['REQUIRES_STYLE'], htmlspecialchars($style['style_parent_tree'], ENT_COMPAT)); 661 $this->list_style($style, 0); 662 } 663 } 664 665 // Add buttons 666 $this->template->assign_block_vars('extra_actions', array( 667 'ACTION_NAME' => 'activate', 668 'L_ACTION' => $this->user->lang['STYLE_ACTIVATE'], 669 ) 670 ); 671 672 $this->template->assign_block_vars('extra_actions', array( 673 'ACTION_NAME' => 'deactivate', 674 'L_ACTION' => $this->user->lang['STYLE_DEACTIVATE'], 675 ) 676 ); 677 678 if (isset($this->style_counters) && $this->style_counters['total'] > 1) 679 { 680 $this->template->assign_block_vars('extra_actions', array( 681 'ACTION_NAME' => 'uninstall', 682 'L_ACTION' => $this->user->lang['STYLE_UNINSTALL'], 683 ) 684 ); 685 } 686 } 687 688 /** 689 * Show list of styles that can be installed 690 */ 691 protected function show_available() 692 { 693 // Get list of styles 694 $styles = $this->find_available(true); 695 696 // Show styles 697 if (empty($styles)) 698 { 699 trigger_error($this->user->lang['NO_UNINSTALLED_STYLE'] . adm_back_link($this->u_base_action), E_USER_NOTICE); 700 } 701 702 usort($styles, array($this, 'sort_styles')); 703 704 $this->styles_list_cols = 4; 705 $this->template->assign_vars(array( 706 'STYLES_LIST_COLS' => $this->styles_list_cols, 707 'STYLES_LIST_HIDE_COUNT' => true 708 ) 709 ); 710 711 // Show styles 712 foreach ($styles as &$style) 713 { 714 // Check if style has a parent style in styles list 715 $has_parent = false; 716 if ($style['_inherit_name'] != '') 717 { 718 foreach ($styles as $parent_style) 719 { 720 if ($parent_style['style_name'] == $style['_inherit_name'] && empty($parent_style['_shown'])) 721 { 722 // Show parent style first 723 $has_parent = true; 724 } 725 } 726 } 727 if (!$has_parent) 728 { 729 $this->list_style($style, 0); 730 $this->show_available_child_styles($styles, $style['style_name'], 1); 731 } 732 } 733 734 // Show styles that do not have parent style in styles list 735 foreach ($styles as $style) 736 { 737 if (empty($style['_shown'])) 738 { 739 $this->list_style($style, 0); 740 } 741 } 742 743 // Add button 744 if (isset($this->style_counters) && $this->style_counters['caninstall'] > 0) 745 { 746 $this->template->assign_block_vars('extra_actions', array( 747 'ACTION_NAME' => 'install', 748 'L_ACTION' => $this->user->lang['INSTALL_STYLES'], 749 ) 750 ); 751 } 752 } 753 754 /** 755 * Find styles available for installation 756 * 757 * @param bool $all if true, function will return all installable styles. if false, function will return only styles that can be installed 758 * @return array List of styles 759 */ 760 protected function find_available($all) 761 { 762 // Get list of installed styles 763 $installed = $this->get_styles(); 764 765 $installed_dirs = array(); 766 $installed_names = array(); 767 foreach ($installed as $style) 768 { 769 $installed_dirs[] = $style['style_path']; 770 $installed_names[$style['style_name']] = array( 771 'path' => $style['style_path'], 772 'id' => $style['style_id'], 773 'parent' => $style['style_parent_id'], 774 'tree' => (strlen($style['style_parent_tree']) ? $style['style_parent_tree'] . '/' : '') . $style['style_path'], 775 ); 776 } 777 778 // Get list of directories 779 $dirs = $this->find_style_dirs(); 780 781 // Find styles that can be installed 782 $styles = array(); 783 foreach ($dirs as $dir) 784 { 785 if (in_array($dir, $installed_dirs)) 786 { 787 // Style is already installed 788 continue; 789 } 790 $cfg = $this->read_style_cfg($dir); 791 if ($cfg === false) 792 { 793 // Invalid style.cfg 794 continue; 795 } 796 797 // Style should be available for installation 798 $parent = $cfg['parent']; 799 $style = array( 800 'style_id' => 0, 801 'style_name' => $cfg['name'], 802 'style_copyright' => $cfg['copyright'], 803 'style_active' => 0, 804 'style_path' => $dir, 805 'bbcode_bitfield' => $cfg['template_bitfield'], 806 'style_parent_id' => 0, 807 'style_parent_tree' => '', 808 // Extra values for styles list 809 // All extra variable start with _ so they won't be confused with data that can be added to styles table 810 '_inherit_name' => $parent, 811 '_available' => true, 812 '_note' => '', 813 ); 814 815 // Check style inheritance 816 if ($parent != '') 817 { 818 if (isset($installed_names[$parent])) 819 { 820 // Parent style is installed 821 $row = $installed_names[$parent]; 822 $style['style_parent_id'] = $row['id']; 823 $style['style_parent_tree'] = $row['tree']; 824 } 825 else 826 { 827 // Parent style is not installed yet 828 $style['_available'] = false; 829 $style['_note'] = sprintf($this->user->lang['REQUIRES_STYLE'], htmlspecialchars($parent, ENT_COMPAT)); 830 } 831 } 832 833 if ($all || $style['_available']) 834 { 835 $styles[] = $style; 836 } 837 } 838 839 return $styles; 840 } 841 842 /** 843 * Show styles list 844 * 845 * @param array $styles styles list 846 * @param int $parent parent style id 847 * @param int $level style inheritance level 848 */ 849 protected function show_styles_list(&$styles, $parent, $level) 850 { 851 foreach ($styles as &$style) 852 { 853 if (empty($style['_shown']) && $style['style_parent_id'] == $parent) 854 { 855 $this->list_style($style, $level); 856 $this->show_styles_list($styles, $style['style_id'], $level + 1); 857 } 858 } 859 } 860 861 /** 862 * Show available styles tree 863 * 864 * @param array $styles Styles list, passed as reference 865 * @param string $name Name of parent style 866 * @param int $level Styles tree level 867 */ 868 protected function show_available_child_styles(&$styles, $name, $level) 869 { 870 foreach ($styles as &$style) 871 { 872 if (empty($style['_shown']) && $style['_inherit_name'] == $name) 873 { 874 $this->list_style($style, $level); 875 $this->show_available_child_styles($styles, $style['style_name'], $level + 1); 876 } 877 } 878 } 879 880 /** 881 * Update styles tree 882 * 883 * @param array $styles Styles list, passed as reference 884 * @param array|false $style Current style, false if root 885 * @return bool True if something was updated, false if not 886 */ 887 protected function update_styles_tree(&$styles, $style = false) 888 { 889 $parent_id = ($style === false) ? 0 : $style['style_id']; 890 $parent_tree = ($style === false) ? '' : ($style['style_parent_tree'] == '' ? '' : $style['style_parent_tree']) . $style['style_path']; 891 $update = false; 892 $updated = false; 893 foreach ($styles as &$row) 894 { 895 if ($row['style_parent_id'] == $parent_id) 896 { 897 if ($row['style_parent_tree'] != $parent_tree) 898 { 899 $row['style_parent_tree'] = $parent_tree; 900 $update = true; 901 } 902 $updated |= $this->update_styles_tree($styles, $row); 903 } 904 } 905 if ($update) 906 { 907 $sql = 'UPDATE ' . STYLES_TABLE . " 908 SET style_parent_tree = '" . $this->db->sql_escape($parent_tree) . "' 909 WHERE style_parent_id = {$parent_id}"; 910 $this->db->sql_query($sql); 911 $updated = true; 912 } 913 return $updated; 914 } 915 916 /** 917 * Find all possible parent styles for style 918 * 919 * @param array $styles list of styles 920 * @param int $id id of style 921 * @param int $parent current parent style id 922 * @param int $level current tree level 923 * @return array Style ids, names and levels 924 */ 925 protected function find_possible_parents($styles, $id = -1, $parent = 0, $level = 0) 926 { 927 $results = array(); 928 foreach ($styles as $style) 929 { 930 if ($style['style_id'] != $id && $style['style_parent_id'] == $parent) 931 { 932 $results[] = array( 933 'style_id' => $style['style_id'], 934 'style_name' => $style['style_name'], 935 'style_path' => $style['style_path'], 936 'style_parent_id' => $style['style_parent_id'], 937 'style_parent_tree' => $style['style_parent_tree'], 938 'level' => $level 939 ); 940 $results = array_merge($results, $this->find_possible_parents($styles, $id, $style['style_id'], $level + 1)); 941 } 942 } 943 return $results; 944 } 945 946 /** 947 * Show item in styles list 948 * 949 * @param array $style style row 950 * @param int $level style inheritance level 951 */ 952 protected function list_style(&$style, $level) 953 { 954 // Mark row as shown 955 if (!empty($style['_shown'])) 956 { 957 return; 958 } 959 960 $style['_shown'] = true; 961 962 $style_cfg = $this->read_style_cfg($style['style_path']); 963 964 // Generate template variables 965 $actions = array(); 966 $row = array( 967 // Style data 968 'STYLE_ID' => $style['style_id'], 969 'STYLE_NAME' => htmlspecialchars($style['style_name'], ENT_COMPAT), 970 'STYLE_VERSION' => $style_cfg['style_version'] ?? '-', 971 'STYLE_PHPBB_VERSION' => $style_cfg['phpbb_version'], 972 'STYLE_PATH' => htmlspecialchars($style['style_path'], ENT_COMPAT), 973 'STYLE_COPYRIGHT' => strip_tags($style['style_copyright']), 974 'STYLE_ACTIVE' => $style['style_active'], 975 976 // Additional data 977 'DEFAULT' => ($style['style_id'] && $style['style_id'] == $this->default_style), 978 'USERS' => (isset($style['_users'])) ? $style['_users'] : '', 979 'LEVEL' => $level, 980 'PADDING' => (4 + 16 * $level), 981 'SHOW_COPYRIGHT' => ($style['style_id']) ? false : true, 982 'STYLE_PATH_FULL' => htmlspecialchars($this->styles_path_absolute . '/' . $style['style_path'], ENT_COMPAT) . '/', 983 984 // Comment to show below style 985 'COMMENT' => (isset($style['_note'])) ? $style['_note'] : '', 986 987 // The following variables should be used by hooks to add custom HTML code 988 'EXTRA' => '', 989 'EXTRA_OPTIONS' => '' 990 ); 991 992 // Status specific data 993 if ($style['style_id']) 994 { 995 // Style is installed 996 997 // Details 998 $actions[] = array( 999 'U_ACTION' => $this->u_action . '&action=details&id=' . $style['style_id'], 1000 'L_ACTION' => $this->user->lang['DETAILS'] 1001 ); 1002 1003 // Activate/Deactive 1004 $action_name = ($style['style_active'] ? 'de' : '') . 'activate'; 1005 1006 $actions[] = array( 1007 'U_ACTION' => $this->u_action . '&action=' . $action_name . '&hash=' . generate_link_hash($action_name) . '&id=' . $style['style_id'], 1008 'L_ACTION' => $this->user->lang['STYLE_' . ($style['style_active'] ? 'DE' : '') . 'ACTIVATE'] 1009 ); 1010 1011 /* // Export 1012 $actions[] = array( 1013 'U_ACTION' => $this->u_action . '&action=export&hash=' . generate_link_hash('export') . '&id=' . $style['style_id'], 1014 'L_ACTION' => $this->user->lang['EXPORT'] 1015 ); */ 1016 1017 if ($style['style_name'] !== 'prosilver') 1018 { 1019 // Uninstall 1020 $actions[] = array( 1021 'U_ACTION' => $this->u_action . '&action=uninstall&hash=' . generate_link_hash('uninstall') . '&id=' . $style['style_id'], 1022 'L_ACTION' => $this->user->lang['STYLE_UNINSTALL'] 1023 ); 1024 } 1025 1026 // Preview 1027 $actions[] = array( 1028 'U_ACTION' => append_sid($this->phpbb_root_path . 'index.' . $this->php_ext, 'style=' . $style['style_id']), 1029 'L_ACTION' => $this->user->lang['PREVIEW'] 1030 ); 1031 } 1032 else 1033 { 1034 // Style is not installed 1035 if (empty($style['_available'])) 1036 { 1037 $actions[] = array( 1038 'HTML' => $this->user->lang['CANNOT_BE_INSTALLED'] 1039 ); 1040 } 1041 else 1042 { 1043 $actions[] = array( 1044 'U_ACTION' => $this->u_action . '&action=install&hash=' . generate_link_hash('install') . '&dir=' . urlencode($style['style_path']), 1045 'L_ACTION' => $this->user->lang['INSTALL_STYLE'] 1046 ); 1047 } 1048 } 1049 1050 // todo: add hook 1051 1052 // Assign template variables 1053 $this->template->assign_block_vars('styles_list', $row); 1054 foreach ($actions as $action) 1055 { 1056 $this->template->assign_block_vars('styles_list.actions', $action); 1057 } 1058 1059 // Increase counters 1060 $counter = ($style['style_id']) ? ($style['style_active'] ? 'active' : 'inactive') : (empty($style['_available']) ? 'cannotinstall' : 'caninstall'); 1061 if (!isset($this->style_counters)) 1062 { 1063 $this->style_counters = array( 1064 'total' => 0, 1065 'active' => 0, 1066 'inactive' => 0, 1067 'caninstall' => 0, 1068 'cannotinstall' => 0 1069 ); 1070 } 1071 $this->style_counters[$counter]++; 1072 $this->style_counters['total']++; 1073 } 1074 1075 /** 1076 * Show welcome message 1077 * 1078 * @param string $title main title 1079 * @param string $description page description 1080 */ 1081 protected function welcome_message($title, $description) 1082 { 1083 $this->template->assign_vars(array( 1084 'L_TITLE' => $this->user->lang[$title], 1085 'L_EXPLAIN' => (isset($this->user->lang[$description])) ? $this->user->lang[$description] : '' 1086 ) 1087 ); 1088 } 1089 1090 /** 1091 * Find all directories that have styles 1092 * 1093 * @return array Directory names 1094 */ 1095 protected function find_style_dirs() 1096 { 1097 $styles = array(); 1098 1099 $dp = @opendir($this->styles_path); 1100 if ($dp) 1101 { 1102 while (($file = readdir($dp)) !== false) 1103 { 1104 $dir = $this->styles_path . $file; 1105 if ($file[0] == '.' || !is_dir($dir)) 1106 { 1107 continue; 1108 } 1109 1110 if (file_exists("{$dir}/style.cfg")) 1111 { 1112 $styles[] = $file; 1113 } 1114 } 1115 closedir($dp); 1116 } 1117 1118 return $styles; 1119 } 1120 1121 /** 1122 * Sort styles 1123 */ 1124 public function sort_styles($style1, $style2) 1125 { 1126 if ($style1['style_active'] != $style2['style_active']) 1127 { 1128 return ($style1['style_active']) ? -1 : 1; 1129 } 1130 if (isset($style1['_available']) && $style1['_available'] != $style2['_available']) 1131 { 1132 return ($style1['_available']) ? -1 : 1; 1133 } 1134 return strcasecmp(isset($style1['style_name']) ? $style1['style_name'] : $style1['name'], isset($style2['style_name']) ? $style2['style_name'] : $style2['name']); 1135 } 1136 1137 /** 1138 * Read style configuration file 1139 * 1140 * @param string $dir style directory 1141 * @return array|bool Style data, false on error 1142 */ 1143 protected function read_style_cfg($dir) 1144 { 1145 // This should never happen, we give them a red warning because of its relevance. 1146 if (!file_exists($this->styles_path . $dir . '/style.cfg')) 1147 { 1148 trigger_error($this->user->lang('NO_STYLE_CFG', $dir), E_USER_WARNING); 1149 } 1150 1151 static $required = array('name', 'phpbb_version', 'copyright'); 1152 1153 $cfg = parse_cfg_file($this->styles_path . $dir . '/style.cfg'); 1154 1155 // Check if it is a valid file 1156 foreach ($required as $key) 1157 { 1158 if (!isset($cfg[$key])) 1159 { 1160 return false; 1161 } 1162 } 1163 1164 // Check data 1165 if (!isset($cfg['parent']) || !is_string($cfg['parent']) || $cfg['parent'] == $cfg['name']) 1166 { 1167 $cfg['parent'] = ''; 1168 } 1169 if (!isset($cfg['template_bitfield'])) 1170 { 1171 $cfg['template_bitfield'] = $this->default_bitfield(); 1172 } 1173 1174 return $cfg; 1175 } 1176 1177 /** 1178 * Install style 1179 * 1180 * @param array $style style data 1181 * @return int Style id 1182 */ 1183 protected function install_style($style) 1184 { 1185 global $user, $phpbb_log; 1186 1187 // Generate row 1188 $sql_ary = array(); 1189 foreach ($style as $key => $value) 1190 { 1191 if ($key != 'style_id' && substr($key, 0, 1) != '_') 1192 { 1193 $sql_ary[$key] = $value; 1194 } 1195 } 1196 1197 // Add to database 1198 $this->db->sql_transaction('begin'); 1199 1200 $sql = 'INSERT INTO ' . STYLES_TABLE . ' 1201 ' . $this->db->sql_build_array('INSERT', $sql_ary); 1202 $this->db->sql_query($sql); 1203 1204 $id = $this->db->sql_nextid(); 1205 1206 $this->db->sql_transaction('commit'); 1207 1208 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_STYLE_ADD', false, array($sql_ary['style_name'])); 1209 1210 return $id; 1211 } 1212 1213 /** 1214 * Lists all styles 1215 * 1216 * @return array Rows with styles data 1217 */ 1218 protected function get_styles() 1219 { 1220 $sql = 'SELECT * 1221 FROM ' . STYLES_TABLE; 1222 $result = $this->db->sql_query($sql); 1223 1224 $rows = $this->db->sql_fetchrowset($result); 1225 $this->db->sql_freeresult($result); 1226 1227 return $rows; 1228 } 1229 1230 /** 1231 * Count users for each style 1232 * 1233 * @return array Styles in following format: [style_id] = number of users 1234 */ 1235 protected function get_users() 1236 { 1237 $sql = 'SELECT user_style, COUNT(user_style) AS style_count 1238 FROM ' . USERS_TABLE . ' 1239 GROUP BY user_style'; 1240 $result = $this->db->sql_query($sql); 1241 1242 $style_count = array(); 1243 while ($row = $this->db->sql_fetchrow($result)) 1244 { 1245 $style_count[$row['user_style']] = $row['style_count']; 1246 } 1247 $this->db->sql_freeresult($result); 1248 1249 return $style_count; 1250 } 1251 1252 /** 1253 * Uninstall style 1254 * 1255 * @param array $style Style data 1256 * @return bool|string True on success, error message on error 1257 */ 1258 protected function uninstall_style($style) 1259 { 1260 $id = $style['style_id']; 1261 $path = $style['style_path']; 1262 1263 // Check if style has child styles 1264 $sql = 'SELECT style_id 1265 FROM ' . STYLES_TABLE . ' 1266 WHERE style_parent_id = ' . (int) $id . " OR style_parent_tree = '" . $this->db->sql_escape($path) . "'"; 1267 $result = $this->db->sql_query($sql); 1268 1269 $conflict = $this->db->sql_fetchrow($result); 1270 $this->db->sql_freeresult($result); 1271 1272 if ($conflict !== false) 1273 { 1274 return sprintf($this->user->lang['STYLE_UNINSTALL_DEPENDENT'], $style['style_name']); 1275 } 1276 1277 // Change default style for users 1278 $sql = 'UPDATE ' . USERS_TABLE . ' 1279 SET user_style = ' . (int) $this->default_style . ' 1280 WHERE user_style = ' . $id; 1281 $this->db->sql_query($sql); 1282 1283 // Uninstall style 1284 $sql = 'DELETE FROM ' . STYLES_TABLE . ' 1285 WHERE style_id = ' . $id; 1286 $this->db->sql_query($sql); 1287 return true; 1288 } 1289 1290 /** 1291 * Delete all files in style directory 1292 * 1293 * @param string $path Style directory 1294 * @param string $dir Directory to remove inside style's directory 1295 * @return bool True on success, false on error 1296 */ 1297 protected function delete_style_files($path, $dir = '') 1298 { 1299 $dirname = $this->styles_path . $path . $dir; 1300 $result = true; 1301 1302 $dp = @opendir($dirname); 1303 1304 if ($dp) 1305 { 1306 while (($file = readdir($dp)) !== false) 1307 { 1308 if ($file == '.' || $file == '..') 1309 { 1310 continue; 1311 } 1312 $filename = $dirname . '/' . $file; 1313 if (is_dir($filename)) 1314 { 1315 if (!$this->delete_style_files($path, $dir . '/' . $file)) 1316 { 1317 $result = false; 1318 } 1319 } 1320 else 1321 { 1322 if (!@unlink($filename)) 1323 { 1324 $result = false; 1325 } 1326 } 1327 } 1328 closedir($dp); 1329 } 1330 if (!@rmdir($dirname)) 1331 { 1332 return false; 1333 } 1334 1335 return $result; 1336 } 1337 1338 /** 1339 * Get list of items from posted data 1340 * 1341 * @param string $name Variable name 1342 * @param string|int $default Default value for array 1343 * @param bool $error If true, error will be triggered if list is empty 1344 * @return array Items 1345 */ 1346 protected function request_vars($name, $default, $error = false) 1347 { 1348 $item = $this->request->variable($name, $default); 1349 $items = $this->request->variable($name . 's', array($default)); 1350 1351 if (count($items) == 1 && $items[0] == $default) 1352 { 1353 $items = array(); 1354 } 1355 1356 if ($item != $default && !count($items)) 1357 { 1358 $items[] = $item; 1359 } 1360 1361 if ($error && !count($items)) 1362 { 1363 trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING); 1364 } 1365 1366 return $items; 1367 } 1368 1369 /** 1370 * Generates default bitfield 1371 * 1372 * This bitfield decides which bbcodes are defined in a template. 1373 * 1374 * @return string Bitfield 1375 */ 1376 protected function default_bitfield() 1377 { 1378 static $value; 1379 if (isset($value)) 1380 { 1381 return $value; 1382 } 1383 1384 // Hardcoded template bitfield to add for new templates 1385 $default_bitfield = '1111111111111'; 1386 1387 $bitfield = new bitfield(); 1388 for ($i = 0; $i < strlen($default_bitfield); $i++) 1389 { 1390 if ($default_bitfield[$i] == '1') 1391 { 1392 $bitfield->set($i); 1393 } 1394 } 1395 1396 return $bitfield->get_base64(); 1397 } 1398 1399 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Jun 23 12:25:44 2024 | Cross-referenced by PHPXref 0.7.1 |