Verzeichnisstruktur phpBB-3.2.0
- Veröffentlicht
- 06.01.2017
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
acp_styles.php
0001 <?php
0002 /**
0003 *
0004 * This file is part of the phpBB Forum Software package.
0005 *
0006 * @copyright (c) phpBB Limited <https://www.phpbb.com>
0007 * @license GNU General Public License, version 2 (GPL-2.0)
0008 *
0009 * For full copyright and license information, please see
0010 * the docs/CREDITS.txt file.
0011 *
0012 */
0013
0014 /**
0015 * @ignore
0016 */
0017 if (!defined('IN_PHPBB'))
0018 {
0019 exit;
0020 }
0021
0022 class acp_styles
0023 {
0024 public $u_action;
0025
0026 protected $u_base_action;
0027 protected $s_hidden_fields;
0028 protected $mode;
0029 protected $styles_path;
0030 protected $styles_path_absolute = 'styles';
0031 protected $default_style = 0;
0032 protected $styles_list_cols = 0;
0033 protected $reserved_style_names = array('adm', 'admin', 'all');
0034
0035 /** @var \phpbb\config\config */
0036 protected $config;
0037
0038 /** @var \phpbb\db\driver\driver_interface */
0039 protected $db;
0040
0041 /** @var \phpbb\user */
0042 protected $user;
0043
0044 /** @var \phpbb\template\template */
0045 protected $template;
0046
0047 /** @var \phpbb\request\request_interface */
0048 protected $request;
0049
0050 /** @var \phpbb\cache\driver\driver_interface */
0051 protected $cache;
0052
0053 /** @var \phpbb\auth\auth */
0054 protected $auth;
0055
0056 /** @var \phpbb\textformatter\cache_interface */
0057 protected $text_formatter_cache;
0058
0059 /** @var string */
0060 protected $phpbb_root_path;
0061
0062 /** @var string */
0063 protected $php_ext;
0064
0065 /** @var \phpbb\event\dispatcher_interface */
0066 protected $dispatcher;
0067
0068 public function main($id, $mode)
0069 {
0070 global $db, $user, $phpbb_admin_path, $phpbb_root_path, $phpEx, $template, $request, $cache, $auth, $config, $phpbb_dispatcher, $phpbb_container;
0071
0072 $this->db = $db;
0073 $this->user = $user;
0074 $this->template = $template;
0075 $this->request = $request;
0076 $this->cache = $cache;
0077 $this->auth = $auth;
0078 $this->text_formatter_cache = $phpbb_container->get('text_formatter.cache');
0079 $this->config = $config;
0080 $this->phpbb_root_path = $phpbb_root_path;
0081 $this->php_ext = $phpEx;
0082 $this->dispatcher = $phpbb_dispatcher;
0083
0084 $this->default_style = $config['default_style'];
0085 $this->styles_path = $this->phpbb_root_path . $this->styles_path_absolute . '/';
0086
0087 $this->u_base_action = append_sid("{$phpbb_admin_path}index.{$this->php_ext}", "i={$id}");
0088 $this->s_hidden_fields = array(
0089 'mode' => $mode,
0090 );
0091
0092 $this->user->add_lang('acp/styles');
0093
0094 $this->tpl_name = 'acp_styles';
0095 $this->page_title = 'ACP_CAT_STYLES';
0096 $this->mode = $mode;
0097
0098 $action = $this->request->variable('action', '');
0099 $post_actions = array('install', 'activate', 'deactivate', 'uninstall');
0100
0101 foreach ($post_actions as $key)
0102 {
0103 if ($this->request->is_set_post($key))
0104 {
0105 $action = $key;
0106 }
0107 }
0108
0109 // The uninstall action uses confirm_box() to verify the validity of the request,
0110 // so there is no need to check for a valid token here.
0111 if (in_array($action, $post_actions) && $action != 'uninstall')
0112 {
0113 $is_valid_request = check_link_hash($request->variable('hash', ''), $action) || check_form_key('styles_management');
0114
0115 if (!$is_valid_request)
0116 {
0117 trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING);
0118 }
0119 }
0120
0121 if ($action != '')
0122 {
0123 $this->s_hidden_fields['action'] = $action;
0124 }
0125
0126 $this->template->assign_vars(array(
0127 'U_ACTION' => $this->u_base_action,
0128 'S_HIDDEN_FIELDS' => build_hidden_fields($this->s_hidden_fields)
0129 )
0130 );
0131
0132 /**
0133 * Run code before ACP styles action execution
0134 *
0135 * @event core.acp_styles_action_before
0136 * @var int id Module ID
0137 * @var string mode Active module
0138 * @var string action Module that should be run
0139 * @since 3.1.7-RC1
0140 */
0141 $vars = array('id', 'mode', 'action');
0142 extract($this->dispatcher->trigger_event('core.acp_styles_action_before', compact($vars)));
0143
0144 // Execute actions
0145 switch ($action)
0146 {
0147 case 'install':
0148 $this->action_install();
0149 return;
0150 case 'uninstall':
0151 $this->action_uninstall();
0152 return;
0153 case 'activate':
0154 $this->action_activate();
0155 return;
0156 case 'deactivate':
0157 $this->action_deactivate();
0158 return;
0159 case 'details':
0160 $this->action_details();
0161 return;
0162 default:
0163 $this->frontend();
0164 }
0165 }
0166
0167 /**
0168 * Main page
0169 */
0170 protected function frontend()
0171 {
0172 add_form_key('styles_management');
0173
0174 // Check mode
0175 switch ($this->mode)
0176 {
0177 case 'style':
0178 $this->welcome_message('ACP_STYLES', 'ACP_STYLES_EXPLAIN');
0179 $this->show_installed();
0180 return;
0181 case 'install':
0182 $this->welcome_message('INSTALL_STYLES', 'INSTALL_STYLES_EXPLAIN');
0183 $this->show_available();
0184 return;
0185 }
0186 trigger_error($this->user->lang['NO_MODE'] . adm_back_link($this->u_action), E_USER_WARNING);
0187 }
0188
0189 /**
0190 * Install style(s)
0191 */
0192 protected function action_install()
0193 {
0194 // Get list of styles to install
0195 $dirs = $this->request_vars('dir', '', true);
0196
0197 // Get list of styles that can be installed
0198 $styles = $this->find_available(false);
0199
0200 // Install each style
0201 $messages = array();
0202 $installed_names = array();
0203 $installed_dirs = array();
0204 foreach ($dirs as $dir)
0205 {
0206 if (in_array($dir, $this->reserved_style_names))
0207 {
0208 $messages[] = $this->user->lang('STYLE_NAME_RESERVED', htmlspecialchars($dir));
0209 continue;
0210 }
0211
0212 $found = false;
0213 foreach ($styles as &$style)
0214 {
0215 // Check if:
0216 // 1. Directory matches directory we are looking for
0217 // 2. Style is not installed yet
0218 // 3. Style with same name or directory hasn't been installed already within this function
0219 if ($style['style_path'] == $dir && empty($style['_installed']) && !in_array($style['style_path'], $installed_dirs) && !in_array($style['style_name'], $installed_names))
0220 {
0221 // Install style
0222 $style['style_active'] = 1;
0223 $style['style_id'] = $this->install_style($style);
0224 $style['_installed'] = true;
0225 $found = true;
0226 $installed_names[] = $style['style_name'];
0227 $installed_dirs[] = $style['style_path'];
0228 $messages[] = sprintf($this->user->lang['STYLE_INSTALLED'], htmlspecialchars($style['style_name']));
0229 }
0230 }
0231 if (!$found)
0232 {
0233 $messages[] = sprintf($this->user->lang['STYLE_NOT_INSTALLED'], htmlspecialchars($dir));
0234 }
0235 }
0236
0237 // Invalidate the text formatter's cache for the new styles to take effect
0238 if (!empty($installed_names))
0239 {
0240 $this->text_formatter_cache->invalidate();
0241 }
0242
0243 // Show message
0244 if (!count($messages))
0245 {
0246 trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING);
0247 }
0248 $message = implode('<br />', $messages);
0249 $message .= '<br /><br /><a href="' . $this->u_base_action . '&mode=style' . '">« ' . $this->user->lang('STYLE_INSTALLED_RETURN_INSTALLED_STYLES') . '</a>';
0250 $message .= '<br /><br /><a href="' . $this->u_base_action . '&mode=install' . '">» ' . $this->user->lang('STYLE_INSTALLED_RETURN_UNINSTALLED_STYLES') . '</a>';
0251 trigger_error($message, E_USER_NOTICE);
0252 }
0253
0254 /**
0255 * Confirm styles removal
0256 */
0257 protected function action_uninstall()
0258 {
0259 // Get list of styles to uninstall
0260 $ids = $this->request_vars('id', 0, true);
0261
0262 // Check if confirmation box was submitted
0263 if (confirm_box(true))
0264 {
0265 // Uninstall
0266 $this->action_uninstall_confirmed($ids, $this->request->variable('confirm_delete_files', false));
0267 return;
0268 }
0269
0270 // Confirm box
0271 $s_hidden = build_hidden_fields(array(
0272 'action' => 'uninstall',
0273 'ids' => $ids
0274 ));
0275 $this->template->assign_var('S_CONFIRM_DELETE', true);
0276 confirm_box(false, $this->user->lang['CONFIRM_UNINSTALL_STYLES'], $s_hidden, 'acp_styles.html');
0277
0278 // Canceled - show styles list
0279 $this->frontend();
0280 }
0281
0282 /**
0283 * Uninstall styles(s)
0284 *
0285 * @param array $ids List of style IDs
0286 * @param bool $delete_files If true, script will attempt to remove files for selected styles
0287 */
0288 protected function action_uninstall_confirmed($ids, $delete_files)
0289 {
0290 global $user, $phpbb_log;
0291
0292 $default = $this->default_style;
0293 $uninstalled = array();
0294 $messages = array();
0295
0296 // Check styles list
0297 foreach ($ids as $id)
0298 {
0299 if (!$id)
0300 {
0301 trigger_error($this->user->lang['INVALID_STYLE_ID'] . adm_back_link($this->u_action), E_USER_WARNING);
0302 }
0303 if ($id == $default)
0304 {
0305 trigger_error($this->user->lang['UNINSTALL_DEFAULT'] . adm_back_link($this->u_action), E_USER_WARNING);
0306 }
0307 $uninstalled[$id] = false;
0308 }
0309
0310 // Order by reversed style_id, so parent styles would be removed after child styles
0311 // This way parent and child styles can be removed in same function call
0312 $sql = 'SELECT *
0313 FROM ' . STYLES_TABLE . '
0314 WHERE style_id IN (' . implode(', ', $ids) . ')
0315 ORDER BY style_id DESC';
0316 $result = $this->db->sql_query($sql);
0317
0318 $rows = $this->db->sql_fetchrowset($result);
0319 $this->db->sql_freeresult($result);
0320
0321 // Uinstall each style
0322 $uninstalled = array();
0323 foreach ($rows as $style)
0324 {
0325 $result = $this->uninstall_style($style, $delete_files);
0326
0327 if (is_string($result))
0328 {
0329 $messages[] = $result;
0330 continue;
0331 }
0332 $messages[] = sprintf($this->user->lang['STYLE_UNINSTALLED'], $style['style_name']);
0333 $uninstalled[] = $style['style_name'];
0334
0335 // Attempt to delete files
0336 if ($delete_files)
0337 {
0338 $messages[] = sprintf($this->user->lang[$this->delete_style_files($style['style_path']) ? 'DELETE_STYLE_FILES_SUCCESS' : 'DELETE_STYLE_FILES_FAILED'], $style['style_name']);
0339 }
0340 }
0341
0342 if (empty($messages))
0343 {
0344 // Nothing to uninstall?
0345 trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING);
0346 }
0347
0348 // Log action
0349 if (count($uninstalled))
0350 {
0351 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_STYLE_DELETE', false, array(implode(', ', $uninstalled)));
0352 }
0353
0354 // Clear cache
0355 $this->cache->purge();
0356
0357 // Show message
0358 trigger_error(implode('<br />', $messages) . adm_back_link($this->u_action), E_USER_NOTICE);
0359 }
0360
0361 /**
0362 * Activate styles
0363 */
0364 protected function action_activate()
0365 {
0366 // Get list of styles to activate
0367 $ids = $this->request_vars('id', 0, true);
0368
0369 // Activate styles
0370 $sql = 'UPDATE ' . STYLES_TABLE . '
0371 SET style_active = 1
0372 WHERE style_id IN (' . implode(', ', $ids) . ')';
0373 $this->db->sql_query($sql);
0374
0375 // Purge cache
0376 $this->cache->destroy('sql', STYLES_TABLE);
0377
0378 // Show styles list
0379 $this->frontend();
0380 }
0381
0382 /**
0383 * Deactivate styles
0384 */
0385 protected function action_deactivate()
0386 {
0387 // Get list of styles to deactivate
0388 $ids = $this->request_vars('id', 0, true);
0389
0390 // Check for default style
0391 foreach ($ids as $id)
0392 {
0393 if ($id == $this->default_style)
0394 {
0395 trigger_error($this->user->lang['DEACTIVATE_DEFAULT'] . adm_back_link($this->u_action), E_USER_WARNING);
0396 }
0397 }
0398
0399 // Reset default style for users who use selected styles
0400 $sql = 'UPDATE ' . USERS_TABLE . '
0401 SET user_style = 0
0402 WHERE user_style IN (' . implode(', ', $ids) . ')';
0403 $this->db->sql_query($sql);
0404
0405 // Deactivate styles
0406 $sql = 'UPDATE ' . STYLES_TABLE . '
0407 SET style_active = 0
0408 WHERE style_id IN (' . implode(', ', $ids) . ')';
0409 $this->db->sql_query($sql);
0410
0411 // Purge cache
0412 $this->cache->destroy('sql', STYLES_TABLE);
0413
0414 // Show styles list
0415 $this->frontend();
0416 }
0417
0418 /**
0419 * Show style details
0420 */
0421 protected function action_details()
0422 {
0423 global $user, $phpbb_log;
0424
0425 $id = $this->request->variable('id', 0);
0426 if (!$id)
0427 {
0428 trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING);
0429 }
0430
0431 // Get all styles
0432 $styles = $this->get_styles();
0433 usort($styles, array($this, 'sort_styles'));
0434
0435 // Find current style
0436 $style = false;
0437 foreach ($styles as $row)
0438 {
0439 if ($row['style_id'] == $id)
0440 {
0441 $style = $row;
0442 break;
0443 }
0444 }
0445
0446 if ($style === false)
0447 {
0448 trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING);
0449 }
0450
0451 // Find all available parent styles
0452 $list = $this->find_possible_parents($styles, $id);
0453
0454 // Add form key
0455 $form_key = 'acp_styles';
0456 add_form_key($form_key);
0457
0458 // Change data
0459 if ($this->request->variable('update', false))
0460 {
0461 if (!check_form_key($form_key))
0462 {
0463 trigger_error($this->user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING);
0464 }
0465
0466 $update = array(
0467 'style_name' => trim($this->request->variable('style_name', $style['style_name'])),
0468 'style_parent_id' => $this->request->variable('style_parent', (int) $style['style_parent_id']),
0469 'style_active' => $this->request->variable('style_active', (int) $style['style_active']),
0470 );
0471 $update_action = $this->u_action . '&action=details&id=' . $id;
0472
0473 // Check style name
0474 if ($update['style_name'] != $style['style_name'])
0475 {
0476 if (!strlen($update['style_name']))
0477 {
0478 trigger_error($this->user->lang['STYLE_ERR_STYLE_NAME'] . adm_back_link($update_action), E_USER_WARNING);
0479 }
0480 foreach ($styles as $row)
0481 {
0482 if ($row['style_name'] == $update['style_name'])
0483 {
0484 trigger_error($this->user->lang['STYLE_ERR_NAME_EXIST'] . adm_back_link($update_action), E_USER_WARNING);
0485 }
0486 }
0487 }
0488 else
0489 {
0490 unset($update['style_name']);
0491 }
0492
0493 // Check parent style id
0494 if ($update['style_parent_id'] != $style['style_parent_id'])
0495 {
0496 if ($update['style_parent_id'] != 0)
0497 {
0498 $found = false;
0499 foreach ($list as $row)
0500 {
0501 if ($row['style_id'] == $update['style_parent_id'])
0502 {
0503 $found = true;
0504 $update['style_parent_tree'] = ($row['style_parent_tree'] != '' ? $row['style_parent_tree'] . '/' : '') . $row['style_path'];
0505 break;
0506 }
0507 }
0508 if (!$found)
0509 {
0510 trigger_error($this->user->lang['STYLE_ERR_INVALID_PARENT'] . adm_back_link($update_action), E_USER_WARNING);
0511 }
0512 }
0513 else
0514 {
0515 $update['style_parent_tree'] = '';
0516 }
0517 }
0518 else
0519 {
0520 unset($update['style_parent_id']);
0521 }
0522
0523 // Check style_active
0524 if ($update['style_active'] != $style['style_active'])
0525 {
0526 if (!$update['style_active'] && $this->default_style == $style['style_id'])
0527 {
0528 trigger_error($this->user->lang['DEACTIVATE_DEFAULT'] . adm_back_link($update_action), E_USER_WARNING);
0529 }
0530 }
0531 else
0532 {
0533 unset($update['style_active']);
0534 }
0535
0536 // Update data
0537 if (count($update))
0538 {
0539 $sql = 'UPDATE ' . STYLES_TABLE . '
0540 SET ' . $this->db->sql_build_array('UPDATE', $update) . "
0541 WHERE style_id = $id";
0542 $this->db->sql_query($sql);
0543
0544 $style = array_merge($style, $update);
0545
0546 if (isset($update['style_parent_id']))
0547 {
0548 // Update styles tree
0549 $styles = $this->get_styles();
0550 if ($this->update_styles_tree($styles, $style))
0551 {
0552 // Something was changed in styles tree, purge all cache
0553 $this->cache->purge();
0554 }
0555 }
0556
0557 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_STYLE_EDIT_DETAILS', false, array($style['style_name']));
0558 }
0559
0560 // Update default style
0561 $default = $this->request->variable('style_default', 0);
0562 if ($default)
0563 {
0564 if (!$style['style_active'])
0565 {
0566 trigger_error($this->user->lang['STYLE_DEFAULT_CHANGE_INACTIVE'] . adm_back_link($update_action), E_USER_WARNING);
0567 }
0568 $this->config->set('default_style', $id);
0569 $this->cache->purge();
0570 }
0571
0572 // Show styles list
0573 $this->frontend();
0574 return;
0575 }
0576
0577 // Show page title
0578 $this->welcome_message('ACP_STYLES', null);
0579
0580 // Show parent styles
0581 foreach ($list as $row)
0582 {
0583 $this->template->assign_block_vars('parent_styles', array(
0584 'STYLE_ID' => $row['style_id'],
0585 'STYLE_NAME' => htmlspecialchars($row['style_name']),
0586 'LEVEL' => $row['level'],
0587 'SPACER' => str_repeat(' ', $row['level']),
0588 )
0589 );
0590 }
0591
0592 // Show style details
0593 $this->template->assign_vars(array(
0594 'S_STYLE_DETAILS' => true,
0595 'STYLE_ID' => $style['style_id'],
0596 'STYLE_NAME' => htmlspecialchars($style['style_name']),
0597 'STYLE_PATH' => htmlspecialchars($style['style_path']),
0598 'STYLE_COPYRIGHT' => strip_tags($style['style_copyright']),
0599 'STYLE_PARENT' => $style['style_parent_id'],
0600 'S_STYLE_ACTIVE' => $style['style_active'],
0601 'S_STYLE_DEFAULT' => ($style['style_id'] == $this->default_style)
0602 )
0603 );
0604 }
0605
0606 /**
0607 * List installed styles
0608 */
0609 protected function show_installed()
0610 {
0611 // Get all installed styles
0612 $styles = $this->get_styles();
0613
0614 if (!count($styles))
0615 {
0616 trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING);
0617 }
0618
0619 usort($styles, array($this, 'sort_styles'));
0620
0621 // Get users
0622 $users = $this->get_users();
0623
0624 // Add users counter to rows
0625 foreach ($styles as &$style)
0626 {
0627 $style['_users'] = isset($users[$style['style_id']]) ? $users[$style['style_id']] : 0;
0628 }
0629
0630 // Set up styles list variables
0631 // Addons should increase this number and update template variable
0632 $this->styles_list_cols = 4;
0633 $this->template->assign_var('STYLES_LIST_COLS', $this->styles_list_cols);
0634
0635 // Show styles list
0636 $this->show_styles_list($styles, 0, 0);
0637
0638 // Show styles with invalid inherits_id
0639 foreach ($styles as $style)
0640 {
0641 if (empty($style['_shown']))
0642 {
0643 $style['_note'] = sprintf($this->user->lang['REQUIRES_STYLE'], htmlspecialchars($style['style_parent_tree']));
0644 $this->list_style($style, 0);
0645 }
0646 }
0647
0648 // Add buttons
0649 $this->template->assign_block_vars('extra_actions', array(
0650 'ACTION_NAME' => 'activate',
0651 'L_ACTION' => $this->user->lang['STYLE_ACTIVATE'],
0652 )
0653 );
0654
0655 $this->template->assign_block_vars('extra_actions', array(
0656 'ACTION_NAME' => 'deactivate',
0657 'L_ACTION' => $this->user->lang['STYLE_DEACTIVATE'],
0658 )
0659 );
0660
0661 if (isset($this->style_counters) && $this->style_counters['total'] > 1)
0662 {
0663 $this->template->assign_block_vars('extra_actions', array(
0664 'ACTION_NAME' => 'uninstall',
0665 'L_ACTION' => $this->user->lang['STYLE_UNINSTALL'],
0666 )
0667 );
0668 }
0669 }
0670
0671 /**
0672 * Show list of styles that can be installed
0673 */
0674 protected function show_available()
0675 {
0676 // Get list of styles
0677 $styles = $this->find_available(true);
0678
0679 // Show styles
0680 if (empty($styles))
0681 {
0682 trigger_error($this->user->lang['NO_UNINSTALLED_STYLE'] . adm_back_link($this->u_base_action), E_USER_NOTICE);
0683 }
0684
0685 usort($styles, array($this, 'sort_styles'));
0686
0687 $this->styles_list_cols = 3;
0688 $this->template->assign_vars(array(
0689 'STYLES_LIST_COLS' => $this->styles_list_cols,
0690 'STYLES_LIST_HIDE_COUNT' => true
0691 )
0692 );
0693
0694 // Show styles
0695 foreach ($styles as &$style)
0696 {
0697 // Check if style has a parent style in styles list
0698 $has_parent = false;
0699 if ($style['_inherit_name'] != '')
0700 {
0701 foreach ($styles as $parent_style)
0702 {
0703 if ($parent_style['style_name'] == $style['_inherit_name'] && empty($parent_style['_shown']))
0704 {
0705 // Show parent style first
0706 $has_parent = true;
0707 }
0708 }
0709 }
0710 if (!$has_parent)
0711 {
0712 $this->list_style($style, 0);
0713 $this->show_available_child_styles($styles, $style['style_name'], 1);
0714 }
0715 }
0716
0717 // Show styles that do not have parent style in styles list
0718 foreach ($styles as $style)
0719 {
0720 if (empty($style['_shown']))
0721 {
0722 $this->list_style($style, 0);
0723 }
0724 }
0725
0726 // Add button
0727 if (isset($this->style_counters) && $this->style_counters['caninstall'] > 0)
0728 {
0729 $this->template->assign_block_vars('extra_actions', array(
0730 'ACTION_NAME' => 'install',
0731 'L_ACTION' => $this->user->lang['INSTALL_STYLES'],
0732 )
0733 );
0734 }
0735 }
0736
0737 /**
0738 * Find styles available for installation
0739 *
0740 * @param bool $all if true, function will return all installable styles. if false, function will return only styles that can be installed
0741 * @return array List of styles
0742 */
0743 protected function find_available($all)
0744 {
0745 // Get list of installed styles
0746 $installed = $this->get_styles();
0747
0748 $installed_dirs = array();
0749 $installed_names = array();
0750 foreach ($installed as $style)
0751 {
0752 $installed_dirs[] = $style['style_path'];
0753 $installed_names[$style['style_name']] = array(
0754 'path' => $style['style_path'],
0755 'id' => $style['style_id'],
0756 'parent' => $style['style_parent_id'],
0757 'tree' => (strlen($style['style_parent_tree']) ? $style['style_parent_tree'] . '/' : '') . $style['style_path'],
0758 );
0759 }
0760
0761 // Get list of directories
0762 $dirs = $this->find_style_dirs();
0763
0764 // Find styles that can be installed
0765 $styles = array();
0766 foreach ($dirs as $dir)
0767 {
0768 if (in_array($dir, $installed_dirs))
0769 {
0770 // Style is already installed
0771 continue;
0772 }
0773 $cfg = $this->read_style_cfg($dir);
0774 if ($cfg === false)
0775 {
0776 // Invalid style.cfg
0777 continue;
0778 }
0779
0780 // Style should be available for installation
0781 $parent = $cfg['parent'];
0782 $style = array(
0783 'style_id' => 0,
0784 'style_name' => $cfg['name'],
0785 'style_copyright' => $cfg['copyright'],
0786 'style_active' => 0,
0787 'style_path' => $dir,
0788 'bbcode_bitfield' => $cfg['template_bitfield'],
0789 'style_parent_id' => 0,
0790 'style_parent_tree' => '',
0791 // Extra values for styles list
0792 // All extra variable start with _ so they won't be confused with data that can be added to styles table
0793 '_inherit_name' => $parent,
0794 '_available' => true,
0795 '_note' => '',
0796 );
0797
0798 // Check style inheritance
0799 if ($parent != '')
0800 {
0801 if (isset($installed_names[$parent]))
0802 {
0803 // Parent style is installed
0804 $row = $installed_names[$parent];
0805 $style['style_parent_id'] = $row['id'];
0806 $style['style_parent_tree'] = $row['tree'];
0807 }
0808 else
0809 {
0810 // Parent style is not installed yet
0811 $style['_available'] = false;
0812 $style['_note'] = sprintf($this->user->lang['REQUIRES_STYLE'], htmlspecialchars($parent));
0813 }
0814 }
0815
0816 if ($all || $style['_available'])
0817 {
0818 $styles[] = $style;
0819 }
0820 }
0821
0822 return $styles;
0823 }
0824
0825 /**
0826 * Show styles list
0827 *
0828 * @param array $styles styles list
0829 * @param int $parent parent style id
0830 * @param int $level style inheritance level
0831 */
0832 protected function show_styles_list(&$styles, $parent, $level)
0833 {
0834 foreach ($styles as &$style)
0835 {
0836 if (empty($style['_shown']) && $style['style_parent_id'] == $parent)
0837 {
0838 $this->list_style($style, $level);
0839 $this->show_styles_list($styles, $style['style_id'], $level + 1);
0840 }
0841 }
0842 }
0843
0844 /**
0845 * Show available styles tree
0846 *
0847 * @param array $styles Styles list, passed as reference
0848 * @param string $name Name of parent style
0849 * @param int $level Styles tree level
0850 */
0851 protected function show_available_child_styles(&$styles, $name, $level)
0852 {
0853 foreach ($styles as &$style)
0854 {
0855 if (empty($style['_shown']) && $style['_inherit_name'] == $name)
0856 {
0857 $this->list_style($style, $level);
0858 $this->show_available_child_styles($styles, $style['style_name'], $level + 1);
0859 }
0860 }
0861 }
0862
0863 /**
0864 * Update styles tree
0865 *
0866 * @param array $styles Styles list, passed as reference
0867 * @param array|false $style Current style, false if root
0868 * @return bool True if something was updated, false if not
0869 */
0870 protected function update_styles_tree(&$styles, $style = false)
0871 {
0872 $parent_id = ($style === false) ? 0 : $style['style_id'];
0873 $parent_tree = ($style === false) ? '' : ($style['style_parent_tree'] == '' ? '' : $style['style_parent_tree']) . $style['style_path'];
0874 $update = false;
0875 $updated = false;
0876 foreach ($styles as &$row)
0877 {
0878 if ($row['style_parent_id'] == $parent_id)
0879 {
0880 if ($row['style_parent_tree'] != $parent_tree)
0881 {
0882 $row['style_parent_tree'] = $parent_tree;
0883 $update = true;
0884 }
0885 $updated |= $this->update_styles_tree($styles, $row);
0886 }
0887 }
0888 if ($update)
0889 {
0890 $sql = 'UPDATE ' . STYLES_TABLE . "
0891 SET style_parent_tree = '" . $this->db->sql_escape($parent_tree) . "'
0892 WHERE style_parent_id = {$parent_id}";
0893 $this->db->sql_query($sql);
0894 $updated = true;
0895 }
0896 return $updated;
0897 }
0898
0899 /**
0900 * Find all possible parent styles for style
0901 *
0902 * @param array $styles list of styles
0903 * @param int $id id of style
0904 * @param int $parent current parent style id
0905 * @param int $level current tree level
0906 * @return array Style ids, names and levels
0907 */
0908 protected function find_possible_parents($styles, $id = -1, $parent = 0, $level = 0)
0909 {
0910 $results = array();
0911 foreach ($styles as $style)
0912 {
0913 if ($style['style_id'] != $id && $style['style_parent_id'] == $parent)
0914 {
0915 $results[] = array(
0916 'style_id' => $style['style_id'],
0917 'style_name' => $style['style_name'],
0918 'style_path' => $style['style_path'],
0919 'style_parent_id' => $style['style_parent_id'],
0920 'style_parent_tree' => $style['style_parent_tree'],
0921 'level' => $level
0922 );
0923 $results = array_merge($results, $this->find_possible_parents($styles, $id, $style['style_id'], $level + 1));
0924 }
0925 }
0926 return $results;
0927 }
0928
0929 /**
0930 * Show item in styles list
0931 *
0932 * @param array $style style row
0933 * @param int $level style inheritance level
0934 */
0935 protected function list_style(&$style, $level)
0936 {
0937 // Mark row as shown
0938 if (!empty($style['_shown']))
0939 {
0940 return;
0941 }
0942
0943 $style['_shown'] = true;
0944
0945 // Generate template variables
0946 $actions = array();
0947 $row = array(
0948 // Style data
0949 'STYLE_ID' => $style['style_id'],
0950 'STYLE_NAME' => htmlspecialchars($style['style_name']),
0951 'STYLE_PATH' => htmlspecialchars($style['style_path']),
0952 'STYLE_COPYRIGHT' => strip_tags($style['style_copyright']),
0953 'STYLE_ACTIVE' => $style['style_active'],
0954
0955 // Additional data
0956 'DEFAULT' => ($style['style_id'] && $style['style_id'] == $this->default_style),
0957 'USERS' => (isset($style['_users'])) ? $style['_users'] : '',
0958 'LEVEL' => $level,
0959 'PADDING' => (4 + 16 * $level),
0960 'SHOW_COPYRIGHT' => ($style['style_id']) ? false : true,
0961 'STYLE_PATH_FULL' => htmlspecialchars($this->styles_path_absolute . '/' . $style['style_path']) . '/',
0962
0963 // Comment to show below style
0964 'COMMENT' => (isset($style['_note'])) ? $style['_note'] : '',
0965
0966 // The following variables should be used by hooks to add custom HTML code
0967 'EXTRA' => '',
0968 'EXTRA_OPTIONS' => ''
0969 );
0970
0971 // Status specific data
0972 if ($style['style_id'])
0973 {
0974 // Style is installed
0975
0976 // Details
0977 $actions[] = array(
0978 'U_ACTION' => $this->u_action . '&action=details&id=' . $style['style_id'],
0979 'L_ACTION' => $this->user->lang['DETAILS']
0980 );
0981
0982 // Activate/Deactive
0983 $action_name = ($style['style_active'] ? 'de' : '') . 'activate';
0984
0985 $actions[] = array(
0986 'U_ACTION' => $this->u_action . '&action=' . $action_name . '&hash=' . generate_link_hash($action_name) . '&id=' . $style['style_id'],
0987 'L_ACTION' => $this->user->lang['STYLE_' . ($style['style_active'] ? 'DE' : '') . 'ACTIVATE']
0988 );
0989
0990 /* // Export
0991 $actions[] = array(
0992 'U_ACTION' => $this->u_action . '&action=export&hash=' . generate_link_hash('export') . '&id=' . $style['style_id'],
0993 'L_ACTION' => $this->user->lang['EXPORT']
0994 ); */
0995
0996 // Uninstall
0997 $actions[] = array(
0998 'U_ACTION' => $this->u_action . '&action=uninstall&hash=' . generate_link_hash('uninstall') . '&id=' . $style['style_id'],
0999 'L_ACTION' => $this->user->lang['STYLE_UNINSTALL']
1000 );
1001
1002 // Preview
1003 $actions[] = array(
1004 'U_ACTION' => append_sid($this->phpbb_root_path . 'index.' . $this->php_ext, 'style=' . $style['style_id']),
1005 'L_ACTION' => $this->user->lang['PREVIEW']
1006 );
1007 }
1008 else
1009 {
1010 // Style is not installed
1011 if (empty($style['_available']))
1012 {
1013 $actions[] = array(
1014 'HTML' => $this->user->lang['CANNOT_BE_INSTALLED']
1015 );
1016 }
1017 else
1018 {
1019 $actions[] = array(
1020 'U_ACTION' => $this->u_action . '&action=install&hash=' . generate_link_hash('install') . '&dir=' . urlencode($style['style_path']),
1021 'L_ACTION' => $this->user->lang['INSTALL_STYLE']
1022 );
1023 }
1024 }
1025
1026 // todo: add hook
1027
1028 // Assign template variables
1029 $this->template->assign_block_vars('styles_list', $row);
1030 foreach ($actions as $action)
1031 {
1032 $this->template->assign_block_vars('styles_list.actions', $action);
1033 }
1034
1035 // Increase counters
1036 $counter = ($style['style_id']) ? ($style['style_active'] ? 'active' : 'inactive') : (empty($style['_available']) ? 'cannotinstall' : 'caninstall');
1037 if (!isset($this->style_counters))
1038 {
1039 $this->style_counters = array(
1040 'total' => 0,
1041 'active' => 0,
1042 'inactive' => 0,
1043 'caninstall' => 0,
1044 'cannotinstall' => 0
1045 );
1046 }
1047 $this->style_counters[$counter]++;
1048 $this->style_counters['total']++;
1049 }
1050
1051 /**
1052 * Show welcome message
1053 *
1054 * @param string $title main title
1055 * @param string $description page description
1056 */
1057 protected function welcome_message($title, $description)
1058 {
1059 $this->template->assign_vars(array(
1060 'L_TITLE' => $this->user->lang[$title],
1061 'L_EXPLAIN' => (isset($this->user->lang[$description])) ? $this->user->lang[$description] : ''
1062 )
1063 );
1064 }
1065
1066 /**
1067 * Find all directories that have styles
1068 *
1069 * @return array Directory names
1070 */
1071 protected function find_style_dirs()
1072 {
1073 $styles = array();
1074
1075 $dp = @opendir($this->styles_path);
1076 if ($dp)
1077 {
1078 while (($file = readdir($dp)) !== false)
1079 {
1080 $dir = $this->styles_path . $file;
1081 if ($file[0] == '.' || !is_dir($dir))
1082 {
1083 continue;
1084 }
1085
1086 if (file_exists("{$dir}/style.cfg"))
1087 {
1088 $styles[] = $file;
1089 }
1090 }
1091 closedir($dp);
1092 }
1093
1094 return $styles;
1095 }
1096
1097 /**
1098 * Sort styles
1099 */
1100 public function sort_styles($style1, $style2)
1101 {
1102 if ($style1['style_active'] != $style2['style_active'])
1103 {
1104 return ($style1['style_active']) ? -1 : 1;
1105 }
1106 if (isset($style1['_available']) && $style1['_available'] != $style2['_available'])
1107 {
1108 return ($style1['_available']) ? -1 : 1;
1109 }
1110 return strcasecmp(isset($style1['style_name']) ? $style1['style_name'] : $style1['name'], isset($style2['style_name']) ? $style2['style_name'] : $style2['name']);
1111 }
1112
1113 /**
1114 * Read style configuration file
1115 *
1116 * @param string $dir style directory
1117 * @return array|bool Style data, false on error
1118 */
1119 protected function read_style_cfg($dir)
1120 {
1121 static $required = array('name', 'phpbb_version', 'copyright');
1122 $cfg = parse_cfg_file($this->styles_path . $dir . '/style.cfg');
1123
1124 // Check if it is a valid file
1125 foreach ($required as $key)
1126 {
1127 if (!isset($cfg[$key]))
1128 {
1129 return false;
1130 }
1131 }
1132
1133 // Check data
1134 if (!isset($cfg['parent']) || !is_string($cfg['parent']) || $cfg['parent'] == $cfg['name'])
1135 {
1136 $cfg['parent'] = '';
1137 }
1138 if (!isset($cfg['template_bitfield']))
1139 {
1140 $cfg['template_bitfield'] = $this->default_bitfield();
1141 }
1142
1143 return $cfg;
1144 }
1145
1146 /**
1147 * Install style
1148 *
1149 * @param array $style style data
1150 * @return int Style id
1151 */
1152 protected function install_style($style)
1153 {
1154 global $user, $phpbb_log;
1155
1156 // Generate row
1157 $sql_ary = array();
1158 foreach ($style as $key => $value)
1159 {
1160 if ($key != 'style_id' && substr($key, 0, 1) != '_')
1161 {
1162 $sql_ary[$key] = $value;
1163 }
1164 }
1165
1166 // Add to database
1167 $this->db->sql_transaction('begin');
1168
1169 $sql = 'INSERT INTO ' . STYLES_TABLE . '
1170 ' . $this->db->sql_build_array('INSERT', $sql_ary);
1171 $this->db->sql_query($sql);
1172
1173 $id = $this->db->sql_nextid();
1174
1175 $this->db->sql_transaction('commit');
1176
1177 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_STYLE_ADD', false, array($sql_ary['style_name']));
1178
1179 return $id;
1180 }
1181
1182 /**
1183 * Lists all styles
1184 *
1185 * @return array Rows with styles data
1186 */
1187 protected function get_styles()
1188 {
1189 $sql = 'SELECT *
1190 FROM ' . STYLES_TABLE;
1191 $result = $this->db->sql_query($sql);
1192
1193 $rows = $this->db->sql_fetchrowset($result);
1194 $this->db->sql_freeresult($result);
1195
1196 return $rows;
1197 }
1198
1199 /**
1200 * Count users for each style
1201 *
1202 * @return array Styles in following format: [style_id] = number of users
1203 */
1204 protected function get_users()
1205 {
1206 $sql = 'SELECT user_style, COUNT(user_style) AS style_count
1207 FROM ' . USERS_TABLE . '
1208 GROUP BY user_style';
1209 $result = $this->db->sql_query($sql);
1210
1211 $style_count = array();
1212 while ($row = $this->db->sql_fetchrow($result))
1213 {
1214 $style_count[$row['user_style']] = $row['style_count'];
1215 }
1216 $this->db->sql_freeresult($result);
1217
1218 return $style_count;
1219 }
1220
1221 /**
1222 * Uninstall style
1223 *
1224 * @param array $style Style data
1225 * @return bool|string True on success, error message on error
1226 */
1227 protected function uninstall_style($style)
1228 {
1229 $id = $style['style_id'];
1230 $path = $style['style_path'];
1231
1232 // Check if style has child styles
1233 $sql = 'SELECT style_id
1234 FROM ' . STYLES_TABLE . '
1235 WHERE style_parent_id = ' . (int) $id . " OR style_parent_tree = '" . $this->db->sql_escape($path) . "'";
1236 $result = $this->db->sql_query($sql);
1237
1238 $conflict = $this->db->sql_fetchrow($result);
1239 $this->db->sql_freeresult($result);
1240
1241 if ($conflict !== false)
1242 {
1243 return sprintf($this->user->lang['STYLE_UNINSTALL_DEPENDENT'], $style['style_name']);
1244 }
1245
1246 // Change default style for users
1247 $sql = 'UPDATE ' . USERS_TABLE . '
1248 SET user_style = 0
1249 WHERE user_style = ' . $id;
1250 $this->db->sql_query($sql);
1251
1252 // Uninstall style
1253 $sql = 'DELETE FROM ' . STYLES_TABLE . '
1254 WHERE style_id = ' . $id;
1255 $this->db->sql_query($sql);
1256 return true;
1257 }
1258
1259 /**
1260 * Delete all files in style directory
1261 *
1262 * @param string $path Style directory
1263 * @param string $dir Directory to remove inside style's directory
1264 * @return bool True on success, false on error
1265 */
1266 protected function delete_style_files($path, $dir = '')
1267 {
1268 $dirname = $this->styles_path . $path . $dir;
1269 $result = true;
1270
1271 $dp = @opendir($dirname);
1272
1273 if ($dp)
1274 {
1275 while (($file = readdir($dp)) !== false)
1276 {
1277 if ($file == '.' || $file == '..')
1278 {
1279 continue;
1280 }
1281 $filename = $dirname . '/' . $file;
1282 if (is_dir($filename))
1283 {
1284 if (!$this->delete_style_files($path, $dir . '/' . $file))
1285 {
1286 $result = false;
1287 }
1288 }
1289 else
1290 {
1291 if (!@unlink($filename))
1292 {
1293 $result = false;
1294 }
1295 }
1296 }
1297 closedir($dp);
1298 }
1299 if (!@rmdir($dirname))
1300 {
1301 return false;
1302 }
1303
1304 return $result;
1305 }
1306
1307 /**
1308 * Get list of items from posted data
1309 *
1310 * @param string $name Variable name
1311 * @param string|int $default Default value for array
1312 * @param bool $error If true, error will be triggered if list is empty
1313 * @return array Items
1314 */
1315 protected function request_vars($name, $default, $error = false)
1316 {
1317 $item = $this->request->variable($name, $default);
1318 $items = $this->request->variable($name . 's', array($default));
1319
1320 if (count($items) == 1 && $items[0] == $default)
1321 {
1322 $items = array();
1323 }
1324
1325 if ($item != $default && !count($items))
1326 {
1327 $items[] = $item;
1328 }
1329
1330 if ($error && !count($items))
1331 {
1332 trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING);
1333 }
1334
1335 return $items;
1336 }
1337
1338 /**
1339 * Generates default bitfield
1340 *
1341 * This bitfield decides which bbcodes are defined in a template.
1342 *
1343 * @return string Bitfield
1344 */
1345 protected function default_bitfield()
1346 {
1347 static $value;
1348 if (isset($value))
1349 {
1350 return $value;
1351 }
1352
1353 // Hardcoded template bitfield to add for new templates
1354 $bitfield = new bitfield();
1355 $bitfield->set(0);
1356 $bitfield->set(1);
1357 $bitfield->set(2);
1358 $bitfield->set(3);
1359 $bitfield->set(4);
1360 $bitfield->set(8);
1361 $bitfield->set(9);
1362 $bitfield->set(11);
1363 $bitfield->set(12);
1364 $value = $bitfield->get_base64();
1365 return $value;
1366 }
1367
1368 }
1369