Verzeichnisstruktur phpBB-3.1.0


Veröffentlicht
27.10.2014

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

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