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