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