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