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.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

acp_styles.php

Zuletzt modifiziert: 09.10.2024, 12:52 - Dateigröße: 34.11 KiB


 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 . '&amp;mode=style' . '">&laquo; ' . $this->user->lang('STYLE_INSTALLED_RETURN_INSTALLED_STYLES') . '</a>';
 0250          $message .= '<br /><br /><a href="' . $this->u_base_action . '&amp;mode=install' . '">&raquo; ' . $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 . '&amp;action=details&amp;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('&nbsp; ', $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 . '&amp;action=details&amp;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 . '&amp;action=' . $action_name . '&amp;hash=' . generate_link_hash($action_name) . '&amp;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 . '&amp;action=export&amp;hash=' . generate_link_hash('export') . '&amp;id=' . $style['style_id'],
 0993                  'L_ACTION'    => $this->user->lang['EXPORT']
 0994              ); */
 0995   
 0996              // Uninstall
 0997              $actions[] = array(
 0998                  'U_ACTION'    => $this->u_action . '&amp;action=uninstall&amp;hash=' . generate_link_hash('uninstall') . '&amp;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 . '&amp;action=install&amp;hash=' . generate_link_hash('install') . '&amp;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