[ Index ]

PHP Cross Reference of phpBB-3.3.3-deutsch

title

Body

[close]

/includes/acp/ -> acp_styles.php (source)

   1  <?php
   2  /**
   3  *
   4  * This file is part of the phpBB Forum Software package.
   5  *
   6  * @copyright (c) phpBB Limited <https://www.phpbb.com>
   7  * @license GNU General Public License, version 2 (GPL-2.0)
   8  *
   9  * For full copyright and license information, please see
  10  * the docs/CREDITS.txt file.
  11  *
  12  */
  13  
  14  /**
  15  * @ignore
  16  */
  17  if (!defined('IN_PHPBB'))
  18  {
  19      exit;
  20  }
  21  
  22  class acp_styles
  23  {
  24      public $u_action;
  25  
  26      protected $u_base_action;
  27      protected $s_hidden_fields;
  28      protected $mode;
  29      protected $styles_path;
  30      protected $styles_path_absolute = 'styles';
  31      protected $default_style = 0;
  32      protected $styles_list_cols = 0;
  33      protected $reserved_style_names = array('adm', 'admin', 'all');
  34  
  35      /** @var \phpbb\config\config */
  36      protected $config;
  37  
  38      /** @var \phpbb\db\driver\driver_interface */
  39      protected $db;
  40  
  41      /** @var \phpbb\user */
  42      protected $user;
  43  
  44      /** @var \phpbb\template\template */
  45      protected $template;
  46  
  47      /** @var \phpbb\request\request_interface */
  48      protected $request;
  49  
  50      /** @var \phpbb\cache\driver\driver_interface */
  51      protected $cache;
  52  
  53      /** @var \phpbb\auth\auth */
  54      protected $auth;
  55  
  56      /** @var \phpbb\textformatter\cache_interface */
  57      protected $text_formatter_cache;
  58  
  59      /** @var string */
  60      protected $phpbb_root_path;
  61  
  62      /** @var string */
  63      protected $php_ext;
  64  
  65      /** @var \phpbb\event\dispatcher_interface */
  66      protected $dispatcher;
  67  
  68  	public function main($id, $mode)
  69      {
  70          global $db, $user, $phpbb_admin_path, $phpbb_root_path, $phpEx, $template, $request, $cache, $auth, $config, $phpbb_dispatcher, $phpbb_container;
  71  
  72          $this->db = $db;
  73          $this->user = $user;
  74          $this->template = $template;
  75          $this->request = $request;
  76          $this->cache = $cache;
  77          $this->auth = $auth;
  78          $this->text_formatter_cache = $phpbb_container->get('text_formatter.cache');
  79          $this->config = $config;
  80          $this->phpbb_root_path = $phpbb_root_path;
  81          $this->php_ext = $phpEx;
  82          $this->dispatcher = $phpbb_dispatcher;
  83  
  84          $this->default_style = $config['default_style'];
  85          $this->styles_path = $this->phpbb_root_path . $this->styles_path_absolute . '/';
  86  
  87          $this->u_base_action = append_sid("{$phpbb_admin_path}index.{$this->php_ext}", "i={$id}");
  88          $this->s_hidden_fields = array(
  89              'mode'        => $mode,
  90          );
  91  
  92          $this->user->add_lang('acp/styles');
  93  
  94          $this->tpl_name = 'acp_styles';
  95          $this->page_title = 'ACP_CAT_STYLES';
  96          $this->mode = $mode;
  97  
  98          $action = $this->request->variable('action', '');
  99          $post_actions = array('install', 'activate', 'deactivate', 'uninstall');
 100  
 101          foreach ($post_actions as $key)
 102          {
 103              if ($this->request->is_set_post($key))
 104              {
 105                  $action = $key;
 106              }
 107          }
 108  
 109          // The uninstall action uses confirm_box() to verify the validity of the request,
 110          // so there is no need to check for a valid token here.
 111          if (in_array($action, $post_actions) && $action != 'uninstall')
 112          {
 113              $is_valid_request = check_link_hash($request->variable('hash', ''), $action) || check_form_key('styles_management');
 114  
 115              if (!$is_valid_request)
 116              {
 117                  trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING);
 118              }
 119          }
 120  
 121          if ($action != '')
 122          {
 123              $this->s_hidden_fields['action'] = $action;
 124          }
 125  
 126          $this->template->assign_vars(array(
 127              'U_ACTION'            => $this->u_base_action,
 128              'S_HIDDEN_FIELDS'    => build_hidden_fields($this->s_hidden_fields)
 129              )
 130          );
 131  
 132          /**
 133           * Run code before ACP styles action execution
 134           *
 135           * @event core.acp_styles_action_before
 136           * @var    int     id          Module ID
 137           * @var    string  mode        Active module
 138           * @var    string  action      Module that should be run
 139           * @since 3.1.7-RC1
 140           */
 141          $vars = array('id', 'mode', 'action');
 142          extract($this->dispatcher->trigger_event('core.acp_styles_action_before', compact($vars)));
 143  
 144          // Execute actions
 145          switch ($action)
 146          {
 147              case 'install':
 148                  $this->action_install();
 149                  return;
 150              case 'uninstall':
 151                  $this->action_uninstall();
 152                  return;
 153              case 'activate':
 154                  $this->action_activate();
 155                  return;
 156              case 'deactivate':
 157                  $this->action_deactivate();
 158                  return;
 159              case 'details':
 160                  $this->action_details();
 161                  return;
 162              default:
 163                  $this->frontend();
 164          }
 165      }
 166  
 167      /**
 168      * Main page
 169      */
 170  	protected function frontend()
 171      {
 172          add_form_key('styles_management');
 173  
 174          // Check mode
 175          switch ($this->mode)
 176          {
 177              case 'style':
 178                  $this->welcome_message('ACP_STYLES', 'ACP_STYLES_EXPLAIN');
 179                  $this->show_installed();
 180                  return;
 181              case 'install':
 182                  $this->welcome_message('INSTALL_STYLES', 'INSTALL_STYLES_EXPLAIN');
 183                  $this->show_available();
 184                  return;
 185          }
 186          trigger_error($this->user->lang['NO_MODE'] . adm_back_link($this->u_action), E_USER_WARNING);
 187      }
 188  
 189      /**
 190      * Install style(s)
 191      */
 192  	protected function action_install()
 193      {
 194          // Get list of styles to install
 195          $dirs = $this->request_vars('dir', '', true);
 196  
 197          // Get list of styles that can be installed
 198          $styles = $this->find_available(false);
 199  
 200          // Install each style
 201          $messages = array();
 202          $installed_names = array();
 203          $installed_dirs = array();
 204          foreach ($dirs as $dir)
 205          {
 206              if (in_array($dir, $this->reserved_style_names))
 207              {
 208                  $messages[] = $this->user->lang('STYLE_NAME_RESERVED', htmlspecialchars($dir));
 209                  continue;
 210              }
 211  
 212              $found = false;
 213              foreach ($styles as &$style)
 214              {
 215                  // Check if:
 216                  // 1. Directory matches directory we are looking for
 217                  // 2. Style is not installed yet
 218                  // 3. Style with same name or directory hasn't been installed already within this function
 219                  if ($style['style_path'] == $dir && empty($style['_installed']) && !in_array($style['style_path'], $installed_dirs) && !in_array($style['style_name'], $installed_names))
 220                  {
 221                      // Install style
 222                      $style['style_active'] = 1;
 223                      $style['style_id'] = $this->install_style($style);
 224                      $style['_installed'] = true;
 225                      $found = true;
 226                      $installed_names[] = $style['style_name'];
 227                      $installed_dirs[] = $style['style_path'];
 228                      $messages[] = sprintf($this->user->lang['STYLE_INSTALLED'], htmlspecialchars($style['style_name']));
 229                  }
 230              }
 231              if (!$found)
 232              {
 233                  $messages[] = sprintf($this->user->lang['STYLE_NOT_INSTALLED'], htmlspecialchars($dir));
 234              }
 235          }
 236  
 237          // Invalidate the text formatter's cache for the new styles to take effect
 238          if (!empty($installed_names))
 239          {
 240              $this->text_formatter_cache->invalidate();
 241          }
 242  
 243          // Show message
 244          if (!count($messages))
 245          {
 246              trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING);
 247          }
 248          $message = implode('<br />', $messages);
 249          $message .= '<br /><br /><a href="' . $this->u_base_action . '&amp;mode=style' . '">&laquo; ' . $this->user->lang('STYLE_INSTALLED_RETURN_INSTALLED_STYLES') . '</a>';
 250          $message .= '<br /><br /><a href="' . $this->u_base_action . '&amp;mode=install' . '">&raquo; ' . $this->user->lang('STYLE_INSTALLED_RETURN_UNINSTALLED_STYLES') . '</a>';
 251          trigger_error($message, E_USER_NOTICE);
 252      }
 253  
 254      /**
 255      * Confirm styles removal
 256      */
 257  	protected function action_uninstall()
 258      {
 259          // Get list of styles to uninstall
 260          $ids = $this->request_vars('id', 0, true);
 261  
 262          // Don't remove prosilver, you can still deactivate it.
 263          $sql = 'SELECT style_id
 264              FROM ' . STYLES_TABLE . "
 265              WHERE style_name = '" . $this->db->sql_escape('prosilver') . "'";
 266          $result = $this->db->sql_query($sql);
 267          $prosilver_id = (int) $this->db->sql_fetchfield('style_id');
 268          $this->db->sql_freeresult($result);
 269  
 270          if ($prosilver_id && in_array($prosilver_id, $ids))
 271          {
 272              trigger_error($this->user->lang('UNINSTALL_PROSILVER') . adm_back_link($this->u_action), E_USER_WARNING);
 273          }
 274  
 275          // Check if confirmation box was submitted
 276          if (confirm_box(true))
 277          {
 278              // Uninstall
 279              $this->action_uninstall_confirmed($ids, $this->request->variable('confirm_delete_files', false));
 280              return;
 281          }
 282  
 283          // Confirm box
 284          $s_hidden = build_hidden_fields(array(
 285              'action'    => 'uninstall',
 286              'ids'        => $ids
 287          ));
 288          $this->template->assign_var('S_CONFIRM_DELETE', true);
 289          confirm_box(false, $this->user->lang['CONFIRM_UNINSTALL_STYLES'], $s_hidden, 'acp_styles.html');
 290  
 291          // Canceled - show styles list
 292          $this->frontend();
 293      }
 294  
 295      /**
 296      * Uninstall styles(s)
 297      *
 298      * @param array $ids List of style IDs
 299      * @param bool $delete_files If true, script will attempt to remove files for selected styles
 300      */
 301  	protected function action_uninstall_confirmed($ids, $delete_files)
 302      {
 303          global $user, $phpbb_log;
 304  
 305          $default = $this->default_style;
 306          $uninstalled = array();
 307          $messages = array();
 308  
 309          // Check styles list
 310          foreach ($ids as $id)
 311          {
 312              if (!$id)
 313              {
 314                  trigger_error($this->user->lang['INVALID_STYLE_ID'] . adm_back_link($this->u_action), E_USER_WARNING);
 315              }
 316              if ($id == $default)
 317              {
 318                  trigger_error($this->user->lang['UNINSTALL_DEFAULT'] . adm_back_link($this->u_action), E_USER_WARNING);
 319              }
 320              $uninstalled[$id] = false;
 321          }
 322  
 323          // Order by reversed style_id, so parent styles would be removed after child styles
 324          // This way parent and child styles can be removed in same function call
 325          $sql = 'SELECT *
 326              FROM ' . STYLES_TABLE . '
 327              WHERE style_id IN (' . implode(', ', $ids) . ')
 328              ORDER BY style_id DESC';
 329          $result = $this->db->sql_query($sql);
 330  
 331          $rows = $this->db->sql_fetchrowset($result);
 332          $this->db->sql_freeresult($result);
 333  
 334          // Uinstall each style
 335          $uninstalled = array();
 336          foreach ($rows as $style)
 337          {
 338              $result = $this->uninstall_style($style, $delete_files);
 339  
 340              if (is_string($result))
 341              {
 342                  $messages[] = $result;
 343                  continue;
 344              }
 345              $messages[] = sprintf($this->user->lang['STYLE_UNINSTALLED'], $style['style_name']);
 346              $uninstalled[] = $style['style_name'];
 347  
 348              // Attempt to delete files
 349              if ($delete_files)
 350              {
 351                  $messages[] = sprintf($this->user->lang[$this->delete_style_files($style['style_path']) ? 'DELETE_STYLE_FILES_SUCCESS' : 'DELETE_STYLE_FILES_FAILED'], $style['style_name']);
 352              }
 353          }
 354  
 355          if (empty($messages))
 356          {
 357              // Nothing to uninstall?
 358              trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING);
 359          }
 360  
 361          // Log action
 362          if (count($uninstalled))
 363          {
 364              $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_STYLE_DELETE', false, array(implode(', ', $uninstalled)));
 365          }
 366  
 367          // Clear cache
 368          $this->cache->purge();
 369  
 370          // Show message
 371          trigger_error(implode('<br />', $messages) . adm_back_link($this->u_action), E_USER_NOTICE);
 372      }
 373  
 374      /**
 375      * Activate styles
 376      */
 377  	protected function action_activate()
 378      {
 379          // Get list of styles to activate
 380          $ids = $this->request_vars('id', 0, true);
 381  
 382          // Activate styles
 383          $sql = 'UPDATE ' . STYLES_TABLE . '
 384              SET style_active = 1
 385              WHERE style_id IN (' . implode(', ', $ids) . ')';
 386          $this->db->sql_query($sql);
 387  
 388          // Purge cache
 389          $this->cache->destroy('sql', STYLES_TABLE);
 390  
 391          // Show styles list
 392          $this->frontend();
 393      }
 394  
 395      /**
 396      * Deactivate styles
 397      */
 398  	protected function action_deactivate()
 399      {
 400          // Get list of styles to deactivate
 401          $ids = $this->request_vars('id', 0, true);
 402  
 403          // Check for default style
 404          foreach ($ids as $id)
 405          {
 406              if ($id == $this->default_style)
 407              {
 408                  trigger_error($this->user->lang['DEACTIVATE_DEFAULT'] . adm_back_link($this->u_action), E_USER_WARNING);
 409              }
 410          }
 411  
 412          // Reset default style for users who use selected styles
 413          $sql = 'UPDATE ' . USERS_TABLE . '
 414              SET user_style = ' . (int) $this->default_style . '
 415              WHERE user_style IN (' . implode(', ', $ids) . ')';
 416          $this->db->sql_query($sql);
 417  
 418          // Deactivate styles
 419          $sql = 'UPDATE ' . STYLES_TABLE . '
 420              SET style_active = 0
 421              WHERE style_id IN (' . implode(', ', $ids) . ')';
 422          $this->db->sql_query($sql);
 423  
 424          // Purge cache
 425          $this->cache->destroy('sql', STYLES_TABLE);
 426  
 427          // Show styles list
 428          $this->frontend();
 429      }
 430  
 431      /**
 432      * Show style details
 433      */
 434  	protected function action_details()
 435      {
 436          global $user, $phpbb_log;
 437  
 438          $id = $this->request->variable('id', 0);
 439          if (!$id)
 440          {
 441              trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING);
 442          }
 443  
 444          // Get all styles
 445          $styles = $this->get_styles();
 446          usort($styles, array($this, 'sort_styles'));
 447  
 448          // Find current style
 449          $style = false;
 450          foreach ($styles as $row)
 451          {
 452              if ($row['style_id'] == $id)
 453              {
 454                  $style = $row;
 455                  break;
 456              }
 457          }
 458  
 459          if ($style === false)
 460          {
 461              trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING);
 462          }
 463  
 464          // Read style configuration file
 465          $style_cfg = $this->read_style_cfg($style['style_path']);
 466  
 467          // Find all available parent styles
 468          $list = $this->find_possible_parents($styles, $id);
 469  
 470          // Add form key
 471          $form_key = 'acp_styles';
 472          add_form_key($form_key);
 473  
 474          // Change data
 475          if ($this->request->variable('update', false))
 476          {
 477              if (!check_form_key($form_key))
 478              {
 479                  trigger_error($this->user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING);
 480              }
 481  
 482              $update = array(
 483                  'style_name'        => trim($this->request->variable('style_name', $style['style_name'])),
 484                  'style_parent_id'    => $this->request->variable('style_parent', (int) $style['style_parent_id']),
 485                  'style_active'        => $this->request->variable('style_active', (int) $style['style_active']),
 486              );
 487              $update_action = $this->u_action . '&amp;action=details&amp;id=' . $id;
 488  
 489              // Check style name
 490              if ($update['style_name'] != $style['style_name'])
 491              {
 492                  if (!strlen($update['style_name']))
 493                  {
 494                      trigger_error($this->user->lang['STYLE_ERR_STYLE_NAME'] . adm_back_link($update_action), E_USER_WARNING);
 495                  }
 496                  foreach ($styles as $row)
 497                  {
 498                      if ($row['style_name'] == $update['style_name'])
 499                      {
 500                          trigger_error($this->user->lang['STYLE_ERR_NAME_EXIST'] . adm_back_link($update_action), E_USER_WARNING);
 501                      }
 502                  }
 503              }
 504              else
 505              {
 506                  unset($update['style_name']);
 507              }
 508  
 509              // Check parent style id
 510              if ($update['style_parent_id'] != $style['style_parent_id'])
 511              {
 512                  if ($update['style_parent_id'] != 0)
 513                  {
 514                      $found = false;
 515                      foreach ($list as $row)
 516                      {
 517                          if ($row['style_id'] == $update['style_parent_id'])
 518                          {
 519                              $found = true;
 520                              $update['style_parent_tree'] = ($row['style_parent_tree'] != '' ? $row['style_parent_tree'] . '/' : '') . $row['style_path'];
 521                              break;
 522                          }
 523                      }
 524                      if (!$found)
 525                      {
 526                          trigger_error($this->user->lang['STYLE_ERR_INVALID_PARENT'] . adm_back_link($update_action), E_USER_WARNING);
 527                      }
 528                  }
 529                  else
 530                  {
 531                      $update['style_parent_tree'] = '';
 532                  }
 533              }
 534              else
 535              {
 536                  unset($update['style_parent_id']);
 537              }
 538  
 539              // Check style_active
 540              if ($update['style_active'] != $style['style_active'])
 541              {
 542                  if (!$update['style_active'] && $this->default_style == $style['style_id'])
 543                  {
 544                      trigger_error($this->user->lang['DEACTIVATE_DEFAULT'] . adm_back_link($update_action), E_USER_WARNING);
 545                  }
 546              }
 547              else
 548              {
 549                  unset($update['style_active']);
 550              }
 551  
 552              // Update data
 553              if (count($update))
 554              {
 555                  $sql = 'UPDATE ' . STYLES_TABLE . '
 556                      SET ' . $this->db->sql_build_array('UPDATE', $update) . "
 557                      WHERE style_id = $id";
 558                  $this->db->sql_query($sql);
 559  
 560                  $style = array_merge($style, $update);
 561  
 562                  if (isset($update['style_parent_id']))
 563                  {
 564                      // Update styles tree
 565                      $styles = $this->get_styles();
 566                      if ($this->update_styles_tree($styles, $style))
 567                      {
 568                          // Something was changed in styles tree, purge all cache
 569                          $this->cache->purge();
 570                      }
 571                  }
 572  
 573                  $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_STYLE_EDIT_DETAILS', false, array($style['style_name']));
 574              }
 575  
 576              // Update default style
 577              $default = $this->request->variable('style_default', 0);
 578              if ($default)
 579              {
 580                  if (!$style['style_active'])
 581                  {
 582                      trigger_error($this->user->lang['STYLE_DEFAULT_CHANGE_INACTIVE'] . adm_back_link($update_action), E_USER_WARNING);
 583                  }
 584                  $this->config->set('default_style', $id);
 585                  $this->cache->purge();
 586              }
 587  
 588              // Show styles list
 589              $this->frontend();
 590              return;
 591          }
 592  
 593          // Show page title
 594          $this->welcome_message('ACP_STYLES', null);
 595  
 596          // Show parent styles
 597          foreach ($list as $row)
 598          {
 599              $this->template->assign_block_vars('parent_styles', array(
 600                  'STYLE_ID'        => $row['style_id'],
 601                  'STYLE_NAME'    => htmlspecialchars($row['style_name']),
 602                  'LEVEL'            => $row['level'],
 603                  'SPACER'        => str_repeat('&nbsp; ', $row['level']),
 604                  )
 605              );
 606          }
 607  
 608          // Show style details
 609          $this->template->assign_vars(array(
 610              'S_STYLE_DETAILS'    => true,
 611              'STYLE_ID'            => $style['style_id'],
 612              'STYLE_NAME'        => htmlspecialchars($style['style_name']),
 613              'STYLE_PATH'        => htmlspecialchars($style['style_path']),
 614              'STYLE_VERSION'        => htmlspecialchars($style_cfg['style_version']),
 615              'STYLE_COPYRIGHT'    => strip_tags($style['style_copyright']),
 616              'STYLE_PARENT'        => $style['style_parent_id'],
 617              'S_STYLE_ACTIVE'    => $style['style_active'],
 618              'S_STYLE_DEFAULT'    => ($style['style_id'] == $this->default_style)
 619              )
 620          );
 621      }
 622  
 623      /**
 624      * List installed styles
 625      */
 626  	protected function show_installed()
 627      {
 628          // Get all installed styles
 629          $styles = $this->get_styles();
 630  
 631          if (!count($styles))
 632          {
 633              trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING);
 634          }
 635  
 636          usort($styles, array($this, 'sort_styles'));
 637  
 638          // Get users
 639          $users = $this->get_users();
 640  
 641          // Add users counter to rows
 642          foreach ($styles as &$style)
 643          {
 644              $style['_users'] = isset($users[$style['style_id']]) ? $users[$style['style_id']] : 0;
 645          }
 646  
 647          // Set up styles list variables
 648          // Addons should increase this number and update template variable
 649          $this->styles_list_cols = 5;
 650          $this->template->assign_var('STYLES_LIST_COLS', $this->styles_list_cols);
 651  
 652          // Show styles list
 653          $this->show_styles_list($styles, 0, 0);
 654  
 655          // Show styles with invalid inherits_id
 656          foreach ($styles as $style)
 657          {
 658              if (empty($style['_shown']))
 659              {
 660                  $style['_note'] = sprintf($this->user->lang['REQUIRES_STYLE'], htmlspecialchars($style['style_parent_tree']));
 661                  $this->list_style($style, 0);
 662              }
 663          }
 664  
 665          // Add buttons
 666          $this->template->assign_block_vars('extra_actions', array(
 667                  'ACTION_NAME'    => 'activate',
 668                  'L_ACTION'        => $this->user->lang['STYLE_ACTIVATE'],
 669              )
 670          );
 671  
 672          $this->template->assign_block_vars('extra_actions', array(
 673                  'ACTION_NAME'    => 'deactivate',
 674                  'L_ACTION'        => $this->user->lang['STYLE_DEACTIVATE'],
 675              )
 676          );
 677  
 678          if (isset($this->style_counters) && $this->style_counters['total'] > 1)
 679          {
 680              $this->template->assign_block_vars('extra_actions', array(
 681                      'ACTION_NAME'    => 'uninstall',
 682                      'L_ACTION'        => $this->user->lang['STYLE_UNINSTALL'],
 683                  )
 684              );
 685          }
 686      }
 687  
 688      /**
 689      * Show list of styles that can be installed
 690      */
 691  	protected function show_available()
 692      {
 693          // Get list of styles
 694          $styles = $this->find_available(true);
 695  
 696          // Show styles
 697          if (empty($styles))
 698          {
 699              trigger_error($this->user->lang['NO_UNINSTALLED_STYLE'] . adm_back_link($this->u_base_action), E_USER_NOTICE);
 700          }
 701  
 702          usort($styles, array($this, 'sort_styles'));
 703  
 704          $this->styles_list_cols = 4;
 705          $this->template->assign_vars(array(
 706              'STYLES_LIST_COLS'    => $this->styles_list_cols,
 707              'STYLES_LIST_HIDE_COUNT'    => true
 708              )
 709          );
 710  
 711          // Show styles
 712          foreach ($styles as &$style)
 713          {
 714              // Check if style has a parent style in styles list
 715              $has_parent = false;
 716              if ($style['_inherit_name'] != '')
 717              {
 718                  foreach ($styles as $parent_style)
 719                  {
 720                      if ($parent_style['style_name'] == $style['_inherit_name'] && empty($parent_style['_shown']))
 721                      {
 722                          // Show parent style first
 723                          $has_parent = true;
 724                      }
 725                  }
 726              }
 727              if (!$has_parent)
 728              {
 729                  $this->list_style($style, 0);
 730                  $this->show_available_child_styles($styles, $style['style_name'], 1);
 731              }
 732          }
 733  
 734          // Show styles that do not have parent style in styles list
 735          foreach ($styles as $style)
 736          {
 737              if (empty($style['_shown']))
 738              {
 739                  $this->list_style($style, 0);
 740              }
 741          }
 742  
 743          // Add button
 744          if (isset($this->style_counters) && $this->style_counters['caninstall'] > 0)
 745          {
 746              $this->template->assign_block_vars('extra_actions', array(
 747                      'ACTION_NAME'    => 'install',
 748                      'L_ACTION'        => $this->user->lang['INSTALL_STYLES'],
 749                  )
 750              );
 751          }
 752      }
 753  
 754      /**
 755      * Find styles available for installation
 756      *
 757      * @param bool $all if true, function will return all installable styles. if false, function will return only styles that can be installed
 758      * @return array List of styles
 759      */
 760  	protected function find_available($all)
 761      {
 762          // Get list of installed styles
 763          $installed = $this->get_styles();
 764  
 765          $installed_dirs = array();
 766          $installed_names = array();
 767          foreach ($installed as $style)
 768          {
 769              $installed_dirs[] = $style['style_path'];
 770              $installed_names[$style['style_name']] = array(
 771                  'path'        => $style['style_path'],
 772                  'id'        => $style['style_id'],
 773                  'parent'    => $style['style_parent_id'],
 774                  'tree'        => (strlen($style['style_parent_tree']) ? $style['style_parent_tree'] . '/' : '') . $style['style_path'],
 775              );
 776          }
 777  
 778          // Get list of directories
 779          $dirs = $this->find_style_dirs();
 780  
 781          // Find styles that can be installed
 782          $styles = array();
 783          foreach ($dirs as $dir)
 784          {
 785              if (in_array($dir, $installed_dirs))
 786              {
 787                  // Style is already installed
 788                  continue;
 789              }
 790              $cfg = $this->read_style_cfg($dir);
 791              if ($cfg === false)
 792              {
 793                  // Invalid style.cfg
 794                  continue;
 795              }
 796  
 797              // Style should be available for installation
 798              $parent = $cfg['parent'];
 799              $style = array(
 800                  'style_id'            => 0,
 801                  'style_name'        => $cfg['name'],
 802                  'style_copyright'    => $cfg['copyright'],
 803                  'style_active'        => 0,
 804                  'style_path'        => $dir,
 805                  'bbcode_bitfield'    => $cfg['template_bitfield'],
 806                  'style_parent_id'    => 0,
 807                  'style_parent_tree'    => '',
 808                  // Extra values for styles list
 809                  // All extra variable start with _ so they won't be confused with data that can be added to styles table
 810                  '_inherit_name'            => $parent,
 811                  '_available'            => true,
 812                  '_note'                    => '',
 813              );
 814  
 815              // Check style inheritance
 816              if ($parent != '')
 817              {
 818                  if (isset($installed_names[$parent]))
 819                  {
 820                      // Parent style is installed
 821                      $row = $installed_names[$parent];
 822                      $style['style_parent_id'] = $row['id'];
 823                      $style['style_parent_tree'] = $row['tree'];
 824                  }
 825                  else
 826                  {
 827                      // Parent style is not installed yet
 828                      $style['_available'] = false;
 829                      $style['_note'] = sprintf($this->user->lang['REQUIRES_STYLE'], htmlspecialchars($parent));
 830                  }
 831              }
 832  
 833              if ($all || $style['_available'])
 834              {
 835                  $styles[] = $style;
 836              }
 837          }
 838  
 839          return $styles;
 840      }
 841  
 842      /**
 843      * Show styles list
 844      *
 845      * @param array $styles styles list
 846      * @param int $parent parent style id
 847      * @param int $level style inheritance level
 848      */
 849  	protected function show_styles_list(&$styles, $parent, $level)
 850      {
 851          foreach ($styles as &$style)
 852          {
 853              if (empty($style['_shown']) && $style['style_parent_id'] == $parent)
 854              {
 855                  $this->list_style($style, $level);
 856                  $this->show_styles_list($styles, $style['style_id'], $level + 1);
 857              }
 858          }
 859      }
 860  
 861      /**
 862      * Show available styles tree
 863      *
 864      * @param array $styles Styles list, passed as reference
 865      * @param string $name Name of parent style
 866      * @param int $level Styles tree level
 867      */
 868  	protected function show_available_child_styles(&$styles, $name, $level)
 869      {
 870          foreach ($styles as &$style)
 871          {
 872              if (empty($style['_shown']) && $style['_inherit_name'] == $name)
 873              {
 874                  $this->list_style($style, $level);
 875                  $this->show_available_child_styles($styles, $style['style_name'], $level + 1);
 876              }
 877          }
 878      }
 879  
 880      /**
 881      * Update styles tree
 882      *
 883      * @param array $styles Styles list, passed as reference
 884      * @param array|false $style Current style, false if root
 885      * @return bool True if something was updated, false if not
 886      */
 887  	protected function update_styles_tree(&$styles, $style = false)
 888      {
 889          $parent_id = ($style === false) ? 0 : $style['style_id'];
 890          $parent_tree = ($style === false) ? '' : ($style['style_parent_tree'] == '' ? '' : $style['style_parent_tree']) . $style['style_path'];
 891          $update = false;
 892          $updated = false;
 893          foreach ($styles as &$row)
 894          {
 895              if ($row['style_parent_id'] == $parent_id)
 896              {
 897                  if ($row['style_parent_tree'] != $parent_tree)
 898                  {
 899                      $row['style_parent_tree'] = $parent_tree;
 900                      $update = true;
 901                  }
 902                  $updated |= $this->update_styles_tree($styles, $row);
 903              }
 904          }
 905          if ($update)
 906          {
 907              $sql = 'UPDATE ' . STYLES_TABLE . "
 908                  SET style_parent_tree = '" . $this->db->sql_escape($parent_tree) . "'
 909                  WHERE style_parent_id = {$parent_id}";
 910              $this->db->sql_query($sql);
 911              $updated = true;
 912          }
 913          return $updated;
 914      }
 915  
 916      /**
 917      * Find all possible parent styles for style
 918      *
 919      * @param array $styles list of styles
 920      * @param int $id id of style
 921      * @param int $parent current parent style id
 922      * @param int $level current tree level
 923      * @return array Style ids, names and levels
 924      */
 925  	protected function find_possible_parents($styles, $id = -1, $parent = 0, $level = 0)
 926      {
 927          $results = array();
 928          foreach ($styles as $style)
 929          {
 930              if ($style['style_id'] != $id && $style['style_parent_id'] == $parent)
 931              {
 932                  $results[] = array(
 933                      'style_id'        => $style['style_id'],
 934                      'style_name'    => $style['style_name'],
 935                      'style_path'    => $style['style_path'],
 936                      'style_parent_id'    => $style['style_parent_id'],
 937                      'style_parent_tree'    => $style['style_parent_tree'],
 938                      'level'            => $level
 939                  );
 940                  $results = array_merge($results, $this->find_possible_parents($styles, $id, $style['style_id'], $level + 1));
 941              }
 942          }
 943          return $results;
 944      }
 945  
 946      /**
 947      * Show item in styles list
 948      *
 949      * @param array $style style row
 950      * @param int $level style inheritance level
 951      */
 952  	protected function list_style(&$style, $level)
 953      {
 954          // Mark row as shown
 955          if (!empty($style['_shown']))
 956          {
 957              return;
 958          }
 959  
 960          $style['_shown'] = true;
 961  
 962          $style_cfg = $this->read_style_cfg($style['style_path']);
 963  
 964          // Generate template variables
 965          $actions = array();
 966          $row = array(
 967              // Style data
 968              'STYLE_ID'                => $style['style_id'],
 969              'STYLE_NAME'            => htmlspecialchars($style['style_name']),
 970              'STYLE_VERSION'            => $style_cfg['style_version'] ?? '-',
 971              'STYLE_PHPBB_VERSION'    => $style_cfg['phpbb_version'],
 972              'STYLE_PATH'            => htmlspecialchars($style['style_path']),
 973              'STYLE_COPYRIGHT'        => strip_tags($style['style_copyright']),
 974              'STYLE_ACTIVE'            => $style['style_active'],
 975  
 976              // Additional data
 977              'DEFAULT'            => ($style['style_id'] && $style['style_id'] == $this->default_style),
 978              'USERS'                => (isset($style['_users'])) ? $style['_users'] : '',
 979              'LEVEL'                => $level,
 980              'PADDING'            => (4 + 16 * $level),
 981              'SHOW_COPYRIGHT'    => ($style['style_id']) ? false : true,
 982              'STYLE_PATH_FULL'    => htmlspecialchars($this->styles_path_absolute . '/' . $style['style_path']) . '/',
 983  
 984              // Comment to show below style
 985              'COMMENT'        => (isset($style['_note'])) ? $style['_note'] : '',
 986  
 987              // The following variables should be used by hooks to add custom HTML code
 988              'EXTRA'            => '',
 989              'EXTRA_OPTIONS'    => ''
 990          );
 991  
 992          // Status specific data
 993          if ($style['style_id'])
 994          {
 995              // Style is installed
 996  
 997              // Details
 998              $actions[] = array(
 999                  'U_ACTION'    => $this->u_action . '&amp;action=details&amp;id=' . $style['style_id'],
1000                  'L_ACTION'    => $this->user->lang['DETAILS']
1001              );
1002  
1003              // Activate/Deactive
1004              $action_name = ($style['style_active'] ? 'de' : '') . 'activate';
1005  
1006              $actions[] = array(
1007                  'U_ACTION'    => $this->u_action . '&amp;action=' . $action_name . '&amp;hash=' . generate_link_hash($action_name) . '&amp;id=' . $style['style_id'],
1008                  'L_ACTION'    => $this->user->lang['STYLE_' . ($style['style_active'] ? 'DE' : '') . 'ACTIVATE']
1009              );
1010  
1011  /*            // Export
1012              $actions[] = array(
1013                  'U_ACTION'    => $this->u_action . '&amp;action=export&amp;hash=' . generate_link_hash('export') . '&amp;id=' . $style['style_id'],
1014                  'L_ACTION'    => $this->user->lang['EXPORT']
1015              ); */
1016  
1017              if ($style['style_name'] !== 'prosilver')
1018              {
1019                  // Uninstall
1020                  $actions[] = array(
1021                      'U_ACTION'    => $this->u_action . '&amp;action=uninstall&amp;hash=' . generate_link_hash('uninstall') . '&amp;id=' . $style['style_id'],
1022                      'L_ACTION'    => $this->user->lang['STYLE_UNINSTALL']
1023                  );
1024              }
1025  
1026              // Preview
1027              $actions[] = array(
1028                  'U_ACTION'    => append_sid($this->phpbb_root_path . 'index.' . $this->php_ext, 'style=' . $style['style_id']),
1029                  'L_ACTION'    => $this->user->lang['PREVIEW']
1030              );
1031          }
1032          else
1033          {
1034              // Style is not installed
1035              if (empty($style['_available']))
1036              {
1037                  $actions[] = array(
1038                      'HTML'        => $this->user->lang['CANNOT_BE_INSTALLED']
1039                  );
1040              }
1041              else
1042              {
1043                  $actions[] = array(
1044                      'U_ACTION'    => $this->u_action . '&amp;action=install&amp;hash=' . generate_link_hash('install') . '&amp;dir=' . urlencode($style['style_path']),
1045                      'L_ACTION'    => $this->user->lang['INSTALL_STYLE']
1046                  );
1047              }
1048          }
1049  
1050          // todo: add hook
1051  
1052          // Assign template variables
1053          $this->template->assign_block_vars('styles_list', $row);
1054          foreach ($actions as $action)
1055          {
1056              $this->template->assign_block_vars('styles_list.actions', $action);
1057          }
1058  
1059          // Increase counters
1060          $counter = ($style['style_id']) ? ($style['style_active'] ? 'active' : 'inactive') : (empty($style['_available']) ? 'cannotinstall' : 'caninstall');
1061          if (!isset($this->style_counters))
1062          {
1063              $this->style_counters = array(
1064                  'total'        => 0,
1065                  'active'    => 0,
1066                  'inactive'    => 0,
1067                  'caninstall'    => 0,
1068                  'cannotinstall'    => 0
1069                  );
1070          }
1071          $this->style_counters[$counter]++;
1072          $this->style_counters['total']++;
1073      }
1074  
1075      /**
1076      * Show welcome message
1077      *
1078      * @param string $title main title
1079      * @param string $description page description
1080      */
1081  	protected function welcome_message($title, $description)
1082      {
1083          $this->template->assign_vars(array(
1084              'L_TITLE'    => $this->user->lang[$title],
1085              'L_EXPLAIN'    => (isset($this->user->lang[$description])) ? $this->user->lang[$description] : ''
1086              )
1087          );
1088      }
1089  
1090      /**
1091      * Find all directories that have styles
1092      *
1093      * @return array Directory names
1094      */
1095  	protected function find_style_dirs()
1096      {
1097          $styles = array();
1098  
1099          $dp = @opendir($this->styles_path);
1100          if ($dp)
1101          {
1102              while (($file = readdir($dp)) !== false)
1103              {
1104                  $dir = $this->styles_path . $file;
1105                  if ($file[0] == '.' || !is_dir($dir))
1106                  {
1107                      continue;
1108                  }
1109  
1110                  if (file_exists("{$dir}/style.cfg"))
1111                  {
1112                      $styles[] = $file;
1113                  }
1114              }
1115              closedir($dp);
1116          }
1117  
1118          return $styles;
1119      }
1120  
1121      /**
1122      * Sort styles
1123      */
1124  	public function sort_styles($style1, $style2)
1125      {
1126          if ($style1['style_active'] != $style2['style_active'])
1127          {
1128              return ($style1['style_active']) ? -1 : 1;
1129          }
1130          if (isset($style1['_available']) && $style1['_available'] != $style2['_available'])
1131          {
1132              return ($style1['_available']) ? -1 : 1;
1133          }
1134          return strcasecmp(isset($style1['style_name']) ? $style1['style_name'] : $style1['name'], isset($style2['style_name']) ? $style2['style_name'] : $style2['name']);
1135      }
1136  
1137      /**
1138      * Read style configuration file
1139      *
1140      * @param string $dir style directory
1141      * @return array|bool Style data, false on error
1142      */
1143  	protected function read_style_cfg($dir)
1144      {
1145          // This should never happen, we give them a red warning because of its relevance.
1146          if (!file_exists($this->styles_path . $dir . '/style.cfg'))
1147          {
1148              trigger_error($this->user->lang('NO_STYLE_CFG', $dir), E_USER_WARNING);
1149          }
1150  
1151          static $required = array('name', 'phpbb_version', 'copyright');
1152  
1153          $cfg = parse_cfg_file($this->styles_path . $dir . '/style.cfg');
1154  
1155          // Check if it is a valid file
1156          foreach ($required as $key)
1157          {
1158              if (!isset($cfg[$key]))
1159              {
1160                  return false;
1161              }
1162          }
1163  
1164          // Check data
1165          if (!isset($cfg['parent']) || !is_string($cfg['parent']) || $cfg['parent'] == $cfg['name'])
1166          {
1167              $cfg['parent'] = '';
1168          }
1169          if (!isset($cfg['template_bitfield']))
1170          {
1171              $cfg['template_bitfield'] = $this->default_bitfield();
1172          }
1173  
1174          return $cfg;
1175      }
1176  
1177      /**
1178      * Install style
1179      *
1180      * @param array $style style data
1181      * @return int Style id
1182      */
1183  	protected function install_style($style)
1184      {
1185          global $user, $phpbb_log;
1186  
1187          // Generate row
1188          $sql_ary = array();
1189          foreach ($style as $key => $value)
1190          {
1191              if ($key != 'style_id' && substr($key, 0, 1) != '_')
1192              {
1193                  $sql_ary[$key] = $value;
1194              }
1195          }
1196  
1197          // Add to database
1198          $this->db->sql_transaction('begin');
1199  
1200          $sql = 'INSERT INTO ' . STYLES_TABLE . '
1201              ' . $this->db->sql_build_array('INSERT', $sql_ary);
1202          $this->db->sql_query($sql);
1203  
1204          $id = $this->db->sql_nextid();
1205  
1206          $this->db->sql_transaction('commit');
1207  
1208          $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_STYLE_ADD', false, array($sql_ary['style_name']));
1209  
1210          return $id;
1211      }
1212  
1213      /**
1214      * Lists all styles
1215      *
1216      * @return array Rows with styles data
1217      */
1218  	protected function get_styles()
1219      {
1220          $sql = 'SELECT *
1221              FROM ' . STYLES_TABLE;
1222          $result = $this->db->sql_query($sql);
1223  
1224          $rows = $this->db->sql_fetchrowset($result);
1225          $this->db->sql_freeresult($result);
1226  
1227          return $rows;
1228      }
1229  
1230      /**
1231      * Count users for each style
1232      *
1233      * @return array Styles in following format: [style_id] = number of users
1234      */
1235  	protected function get_users()
1236      {
1237          $sql = 'SELECT user_style, COUNT(user_style) AS style_count
1238              FROM ' . USERS_TABLE . '
1239              GROUP BY user_style';
1240          $result = $this->db->sql_query($sql);
1241  
1242          $style_count = array();
1243          while ($row = $this->db->sql_fetchrow($result))
1244          {
1245              $style_count[$row['user_style']] = $row['style_count'];
1246          }
1247          $this->db->sql_freeresult($result);
1248  
1249          return $style_count;
1250      }
1251  
1252      /**
1253      * Uninstall style
1254      *
1255      * @param array $style Style data
1256      * @return bool|string True on success, error message on error
1257      */
1258  	protected function uninstall_style($style)
1259      {
1260          $id = $style['style_id'];
1261          $path = $style['style_path'];
1262  
1263          // Check if style has child styles
1264          $sql = 'SELECT style_id
1265              FROM ' . STYLES_TABLE . '
1266              WHERE style_parent_id = ' . (int) $id . " OR style_parent_tree = '" . $this->db->sql_escape($path) . "'";
1267          $result = $this->db->sql_query($sql);
1268  
1269          $conflict = $this->db->sql_fetchrow($result);
1270          $this->db->sql_freeresult($result);
1271  
1272          if ($conflict !== false)
1273          {
1274              return sprintf($this->user->lang['STYLE_UNINSTALL_DEPENDENT'], $style['style_name']);
1275          }
1276  
1277          // Change default style for users
1278          $sql = 'UPDATE ' . USERS_TABLE . '
1279              SET user_style = ' . (int) $this->default_style . '
1280              WHERE user_style = ' . $id;
1281          $this->db->sql_query($sql);
1282  
1283          // Uninstall style
1284          $sql = 'DELETE FROM ' . STYLES_TABLE . '
1285              WHERE style_id = ' . $id;
1286          $this->db->sql_query($sql);
1287          return true;
1288      }
1289  
1290      /**
1291      * Delete all files in style directory
1292      *
1293      * @param string $path Style directory
1294      * @param string $dir Directory to remove inside style's directory
1295      * @return bool True on success, false on error
1296      */
1297  	protected function delete_style_files($path, $dir = '')
1298      {
1299          $dirname = $this->styles_path . $path . $dir;
1300          $result = true;
1301  
1302          $dp = @opendir($dirname);
1303  
1304          if ($dp)
1305          {
1306              while (($file = readdir($dp)) !== false)
1307              {
1308                  if ($file == '.' || $file == '..')
1309                  {
1310                      continue;
1311                  }
1312                  $filename = $dirname . '/' . $file;
1313                  if (is_dir($filename))
1314                  {
1315                      if (!$this->delete_style_files($path, $dir . '/' . $file))
1316                      {
1317                          $result = false;
1318                      }
1319                  }
1320                  else
1321                  {
1322                      if (!@unlink($filename))
1323                      {
1324                          $result = false;
1325                      }
1326                  }
1327              }
1328              closedir($dp);
1329          }
1330          if (!@rmdir($dirname))
1331          {
1332              return false;
1333          }
1334  
1335          return $result;
1336      }
1337  
1338      /**
1339      * Get list of items from posted data
1340      *
1341      * @param string $name Variable name
1342      * @param string|int $default Default value for array
1343      * @param bool $error If true, error will be triggered if list is empty
1344      * @return array Items
1345      */
1346  	protected function request_vars($name, $default, $error = false)
1347      {
1348          $item = $this->request->variable($name, $default);
1349          $items = $this->request->variable($name . 's', array($default));
1350  
1351          if (count($items) == 1 && $items[0] == $default)
1352          {
1353              $items = array();
1354          }
1355  
1356          if ($item != $default && !count($items))
1357          {
1358              $items[] = $item;
1359          }
1360  
1361          if ($error && !count($items))
1362          {
1363              trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING);
1364          }
1365  
1366          return $items;
1367      }
1368  
1369      /**
1370      * Generates default bitfield
1371      *
1372      * This bitfield decides which bbcodes are defined in a template.
1373      *
1374      * @return string Bitfield
1375      */
1376  	protected function default_bitfield()
1377      {
1378          static $value;
1379          if (isset($value))
1380          {
1381              return $value;
1382          }
1383  
1384          // Hardcoded template bitfield to add for new templates
1385          $default_bitfield = '1111111111111';
1386  
1387          $bitfield = new bitfield();
1388          for ($i = 0; $i < strlen($default_bitfield); $i++)
1389          {
1390              if ($default_bitfield[$i] == '1')
1391              {
1392                  $bitfield->set($i);
1393              }
1394          }
1395  
1396          return $bitfield->get_base64();
1397      }
1398  
1399  }


Generated: Sun Feb 14 20:08:31 2021 Cross-referenced by PHPXref 0.7.1