[ Index ] |
PHP Cross Reference of phpBB-3.2.11-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)); 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'])); 229 } 230 } 231 if (!$found) 232 { 233 $messages[] = sprintf($this->user->lang['STYLE_NOT_INSTALLED'], htmlspecialchars($dir)); 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']), 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']), 613 'STYLE_PATH' => htmlspecialchars($style['style_path']), 614 'STYLE_VERSION' => htmlspecialchars($style_cfg['style_version']), 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 = 4; 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'])); 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 = 3; 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)); 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 // Generate template variables 963 $actions = array(); 964 $row = array( 965 // Style data 966 'STYLE_ID' => $style['style_id'], 967 'STYLE_NAME' => htmlspecialchars($style['style_name']), 968 'STYLE_PHPBB_VERSION' => $this->read_style_cfg($style['style_path'])['phpbb_version'], 969 'STYLE_PATH' => htmlspecialchars($style['style_path']), 970 'STYLE_COPYRIGHT' => strip_tags($style['style_copyright']), 971 'STYLE_ACTIVE' => $style['style_active'], 972 973 // Additional data 974 'DEFAULT' => ($style['style_id'] && $style['style_id'] == $this->default_style), 975 'USERS' => (isset($style['_users'])) ? $style['_users'] : '', 976 'LEVEL' => $level, 977 'PADDING' => (4 + 16 * $level), 978 'SHOW_COPYRIGHT' => ($style['style_id']) ? false : true, 979 'STYLE_PATH_FULL' => htmlspecialchars($this->styles_path_absolute . '/' . $style['style_path']) . '/', 980 981 // Comment to show below style 982 'COMMENT' => (isset($style['_note'])) ? $style['_note'] : '', 983 984 // The following variables should be used by hooks to add custom HTML code 985 'EXTRA' => '', 986 'EXTRA_OPTIONS' => '' 987 ); 988 989 // Status specific data 990 if ($style['style_id']) 991 { 992 // Style is installed 993 994 // Details 995 $actions[] = array( 996 'U_ACTION' => $this->u_action . '&action=details&id=' . $style['style_id'], 997 'L_ACTION' => $this->user->lang['DETAILS'] 998 ); 999 1000 // Activate/Deactive 1001 $action_name = ($style['style_active'] ? 'de' : '') . 'activate'; 1002 1003 $actions[] = array( 1004 'U_ACTION' => $this->u_action . '&action=' . $action_name . '&hash=' . generate_link_hash($action_name) . '&id=' . $style['style_id'], 1005 'L_ACTION' => $this->user->lang['STYLE_' . ($style['style_active'] ? 'DE' : '') . 'ACTIVATE'] 1006 ); 1007 1008 /* // Export 1009 $actions[] = array( 1010 'U_ACTION' => $this->u_action . '&action=export&hash=' . generate_link_hash('export') . '&id=' . $style['style_id'], 1011 'L_ACTION' => $this->user->lang['EXPORT'] 1012 ); */ 1013 1014 if ($style['style_name'] !== 'prosilver') 1015 { 1016 // Uninstall 1017 $actions[] = array( 1018 'U_ACTION' => $this->u_action . '&action=uninstall&hash=' . generate_link_hash('uninstall') . '&id=' . $style['style_id'], 1019 'L_ACTION' => $this->user->lang['STYLE_UNINSTALL'] 1020 ); 1021 } 1022 1023 // Preview 1024 $actions[] = array( 1025 'U_ACTION' => append_sid($this->phpbb_root_path . 'index.' . $this->php_ext, 'style=' . $style['style_id']), 1026 'L_ACTION' => $this->user->lang['PREVIEW'] 1027 ); 1028 } 1029 else 1030 { 1031 // Style is not installed 1032 if (empty($style['_available'])) 1033 { 1034 $actions[] = array( 1035 'HTML' => $this->user->lang['CANNOT_BE_INSTALLED'] 1036 ); 1037 } 1038 else 1039 { 1040 $actions[] = array( 1041 'U_ACTION' => $this->u_action . '&action=install&hash=' . generate_link_hash('install') . '&dir=' . urlencode($style['style_path']), 1042 'L_ACTION' => $this->user->lang['INSTALL_STYLE'] 1043 ); 1044 } 1045 } 1046 1047 // todo: add hook 1048 1049 // Assign template variables 1050 $this->template->assign_block_vars('styles_list', $row); 1051 foreach ($actions as $action) 1052 { 1053 $this->template->assign_block_vars('styles_list.actions', $action); 1054 } 1055 1056 // Increase counters 1057 $counter = ($style['style_id']) ? ($style['style_active'] ? 'active' : 'inactive') : (empty($style['_available']) ? 'cannotinstall' : 'caninstall'); 1058 if (!isset($this->style_counters)) 1059 { 1060 $this->style_counters = array( 1061 'total' => 0, 1062 'active' => 0, 1063 'inactive' => 0, 1064 'caninstall' => 0, 1065 'cannotinstall' => 0 1066 ); 1067 } 1068 $this->style_counters[$counter]++; 1069 $this->style_counters['total']++; 1070 } 1071 1072 /** 1073 * Show welcome message 1074 * 1075 * @param string $title main title 1076 * @param string $description page description 1077 */ 1078 protected function welcome_message($title, $description) 1079 { 1080 $this->template->assign_vars(array( 1081 'L_TITLE' => $this->user->lang[$title], 1082 'L_EXPLAIN' => (isset($this->user->lang[$description])) ? $this->user->lang[$description] : '' 1083 ) 1084 ); 1085 } 1086 1087 /** 1088 * Find all directories that have styles 1089 * 1090 * @return array Directory names 1091 */ 1092 protected function find_style_dirs() 1093 { 1094 $styles = array(); 1095 1096 $dp = @opendir($this->styles_path); 1097 if ($dp) 1098 { 1099 while (($file = readdir($dp)) !== false) 1100 { 1101 $dir = $this->styles_path . $file; 1102 if ($file[0] == '.' || !is_dir($dir)) 1103 { 1104 continue; 1105 } 1106 1107 if (file_exists("{$dir}/style.cfg")) 1108 { 1109 $styles[] = $file; 1110 } 1111 } 1112 closedir($dp); 1113 } 1114 1115 return $styles; 1116 } 1117 1118 /** 1119 * Sort styles 1120 */ 1121 public function sort_styles($style1, $style2) 1122 { 1123 if ($style1['style_active'] != $style2['style_active']) 1124 { 1125 return ($style1['style_active']) ? -1 : 1; 1126 } 1127 if (isset($style1['_available']) && $style1['_available'] != $style2['_available']) 1128 { 1129 return ($style1['_available']) ? -1 : 1; 1130 } 1131 return strcasecmp(isset($style1['style_name']) ? $style1['style_name'] : $style1['name'], isset($style2['style_name']) ? $style2['style_name'] : $style2['name']); 1132 } 1133 1134 /** 1135 * Read style configuration file 1136 * 1137 * @param string $dir style directory 1138 * @return array|bool Style data, false on error 1139 */ 1140 protected function read_style_cfg($dir) 1141 { 1142 // This should never happen, we give them a red warning because of its relevance. 1143 if (!file_exists($this->styles_path . $dir . '/style.cfg')) 1144 { 1145 trigger_error($this->user->lang('NO_STYLE_CFG', $dir), E_USER_WARNING); 1146 } 1147 1148 static $required = array('name', 'phpbb_version', 'copyright'); 1149 1150 $cfg = parse_cfg_file($this->styles_path . $dir . '/style.cfg'); 1151 1152 // Check if it is a valid file 1153 foreach ($required as $key) 1154 { 1155 if (!isset($cfg[$key])) 1156 { 1157 return false; 1158 } 1159 } 1160 1161 // Check data 1162 if (!isset($cfg['parent']) || !is_string($cfg['parent']) || $cfg['parent'] == $cfg['name']) 1163 { 1164 $cfg['parent'] = ''; 1165 } 1166 if (!isset($cfg['template_bitfield'])) 1167 { 1168 $cfg['template_bitfield'] = $this->default_bitfield(); 1169 } 1170 1171 return $cfg; 1172 } 1173 1174 /** 1175 * Install style 1176 * 1177 * @param array $style style data 1178 * @return int Style id 1179 */ 1180 protected function install_style($style) 1181 { 1182 global $user, $phpbb_log; 1183 1184 // Generate row 1185 $sql_ary = array(); 1186 foreach ($style as $key => $value) 1187 { 1188 if ($key != 'style_id' && substr($key, 0, 1) != '_') 1189 { 1190 $sql_ary[$key] = $value; 1191 } 1192 } 1193 1194 // Add to database 1195 $this->db->sql_transaction('begin'); 1196 1197 $sql = 'INSERT INTO ' . STYLES_TABLE . ' 1198 ' . $this->db->sql_build_array('INSERT', $sql_ary); 1199 $this->db->sql_query($sql); 1200 1201 $id = $this->db->sql_nextid(); 1202 1203 $this->db->sql_transaction('commit'); 1204 1205 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_STYLE_ADD', false, array($sql_ary['style_name'])); 1206 1207 return $id; 1208 } 1209 1210 /** 1211 * Lists all styles 1212 * 1213 * @return array Rows with styles data 1214 */ 1215 protected function get_styles() 1216 { 1217 $sql = 'SELECT * 1218 FROM ' . STYLES_TABLE; 1219 $result = $this->db->sql_query($sql); 1220 1221 $rows = $this->db->sql_fetchrowset($result); 1222 $this->db->sql_freeresult($result); 1223 1224 return $rows; 1225 } 1226 1227 /** 1228 * Count users for each style 1229 * 1230 * @return array Styles in following format: [style_id] = number of users 1231 */ 1232 protected function get_users() 1233 { 1234 $sql = 'SELECT user_style, COUNT(user_style) AS style_count 1235 FROM ' . USERS_TABLE . ' 1236 GROUP BY user_style'; 1237 $result = $this->db->sql_query($sql); 1238 1239 $style_count = array(); 1240 while ($row = $this->db->sql_fetchrow($result)) 1241 { 1242 $style_count[$row['user_style']] = $row['style_count']; 1243 } 1244 $this->db->sql_freeresult($result); 1245 1246 return $style_count; 1247 } 1248 1249 /** 1250 * Uninstall style 1251 * 1252 * @param array $style Style data 1253 * @return bool|string True on success, error message on error 1254 */ 1255 protected function uninstall_style($style) 1256 { 1257 $id = $style['style_id']; 1258 $path = $style['style_path']; 1259 1260 // Check if style has child styles 1261 $sql = 'SELECT style_id 1262 FROM ' . STYLES_TABLE . ' 1263 WHERE style_parent_id = ' . (int) $id . " OR style_parent_tree = '" . $this->db->sql_escape($path) . "'"; 1264 $result = $this->db->sql_query($sql); 1265 1266 $conflict = $this->db->sql_fetchrow($result); 1267 $this->db->sql_freeresult($result); 1268 1269 if ($conflict !== false) 1270 { 1271 return sprintf($this->user->lang['STYLE_UNINSTALL_DEPENDENT'], $style['style_name']); 1272 } 1273 1274 // Change default style for users 1275 $sql = 'UPDATE ' . USERS_TABLE . ' 1276 SET user_style = ' . (int) $this->default_style . ' 1277 WHERE user_style = ' . $id; 1278 $this->db->sql_query($sql); 1279 1280 // Uninstall style 1281 $sql = 'DELETE FROM ' . STYLES_TABLE . ' 1282 WHERE style_id = ' . $id; 1283 $this->db->sql_query($sql); 1284 return true; 1285 } 1286 1287 /** 1288 * Delete all files in style directory 1289 * 1290 * @param string $path Style directory 1291 * @param string $dir Directory to remove inside style's directory 1292 * @return bool True on success, false on error 1293 */ 1294 protected function delete_style_files($path, $dir = '') 1295 { 1296 $dirname = $this->styles_path . $path . $dir; 1297 $result = true; 1298 1299 $dp = @opendir($dirname); 1300 1301 if ($dp) 1302 { 1303 while (($file = readdir($dp)) !== false) 1304 { 1305 if ($file == '.' || $file == '..') 1306 { 1307 continue; 1308 } 1309 $filename = $dirname . '/' . $file; 1310 if (is_dir($filename)) 1311 { 1312 if (!$this->delete_style_files($path, $dir . '/' . $file)) 1313 { 1314 $result = false; 1315 } 1316 } 1317 else 1318 { 1319 if (!@unlink($filename)) 1320 { 1321 $result = false; 1322 } 1323 } 1324 } 1325 closedir($dp); 1326 } 1327 if (!@rmdir($dirname)) 1328 { 1329 return false; 1330 } 1331 1332 return $result; 1333 } 1334 1335 /** 1336 * Get list of items from posted data 1337 * 1338 * @param string $name Variable name 1339 * @param string|int $default Default value for array 1340 * @param bool $error If true, error will be triggered if list is empty 1341 * @return array Items 1342 */ 1343 protected function request_vars($name, $default, $error = false) 1344 { 1345 $item = $this->request->variable($name, $default); 1346 $items = $this->request->variable($name . 's', array($default)); 1347 1348 if (count($items) == 1 && $items[0] == $default) 1349 { 1350 $items = array(); 1351 } 1352 1353 if ($item != $default && !count($items)) 1354 { 1355 $items[] = $item; 1356 } 1357 1358 if ($error && !count($items)) 1359 { 1360 trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING); 1361 } 1362 1363 return $items; 1364 } 1365 1366 /** 1367 * Generates default bitfield 1368 * 1369 * This bitfield decides which bbcodes are defined in a template. 1370 * 1371 * @return string Bitfield 1372 */ 1373 protected function default_bitfield() 1374 { 1375 static $value; 1376 if (isset($value)) 1377 { 1378 return $value; 1379 } 1380 1381 // Hardcoded template bitfield to add for new templates 1382 $default_bitfield = '1111111111111'; 1383 1384 $bitfield = new bitfield(); 1385 for ($i = 0; $i < strlen($default_bitfield); $i++) 1386 { 1387 if ($default_bitfield[$i] == '1') 1388 { 1389 $bitfield->set($i); 1390 } 1391 } 1392 1393 return $bitfield->get_base64(); 1394 } 1395 1396 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Nov 11 20:33:01 2020 | Cross-referenced by PHPXref 0.7.1 |