[ Index ]

PHP Cross Reference of phpBB-3.2.11-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 = 4;
 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 = 3;
 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          // Generate template variables
 963          $actions = array();
 964          $row = array(
 965              // Style data
 966              'STYLE_ID'        => $style['style_id'],
 967              'STYLE_NAME'    => htmlspecialchars($style['style_name']),
 968              'STYLE_PHPBB_VERSION'    => $this->read_style_cfg($style['style_path'])['phpbb_version'],
 969              'STYLE_PATH'    => htmlspecialchars($style['style_path']),
 970              'STYLE_COPYRIGHT'    => strip_tags($style['style_copyright']),
 971              'STYLE_ACTIVE'    => $style['style_active'],
 972  
 973              // Additional data
 974              'DEFAULT'        => ($style['style_id'] && $style['style_id'] == $this->default_style),
 975              'USERS'            => (isset($style['_users'])) ? $style['_users'] : '',
 976              'LEVEL'            => $level,
 977              'PADDING'        => (4 + 16 * $level),
 978              'SHOW_COPYRIGHT'    => ($style['style_id']) ? false : true,
 979              'STYLE_PATH_FULL'    => htmlspecialchars($this->styles_path_absolute . '/' . $style['style_path']) . '/',
 980  
 981              // Comment to show below style
 982              'COMMENT'        => (isset($style['_note'])) ? $style['_note'] : '',
 983  
 984              // The following variables should be used by hooks to add custom HTML code
 985              'EXTRA'            => '',
 986              'EXTRA_OPTIONS'    => ''
 987          );
 988  
 989          // Status specific data
 990          if ($style['style_id'])
 991          {
 992              // Style is installed
 993  
 994              // Details
 995              $actions[] = array(
 996                  'U_ACTION'    => $this->u_action . '&amp;action=details&amp;id=' . $style['style_id'],
 997                  'L_ACTION'    => $this->user->lang['DETAILS']
 998              );
 999  
1000              // Activate/Deactive
1001              $action_name = ($style['style_active'] ? 'de' : '') . 'activate';
1002  
1003              $actions[] = array(
1004                  'U_ACTION'    => $this->u_action . '&amp;action=' . $action_name . '&amp;hash=' . generate_link_hash($action_name) . '&amp;id=' . $style['style_id'],
1005                  'L_ACTION'    => $this->user->lang['STYLE_' . ($style['style_active'] ? 'DE' : '') . 'ACTIVATE']
1006              );
1007  
1008  /*            // Export
1009              $actions[] = array(
1010                  'U_ACTION'    => $this->u_action . '&amp;action=export&amp;hash=' . generate_link_hash('export') . '&amp;id=' . $style['style_id'],
1011                  'L_ACTION'    => $this->user->lang['EXPORT']
1012              ); */
1013  
1014              if ($style['style_name'] !== 'prosilver')
1015              {
1016                  // Uninstall
1017                  $actions[] = array(
1018                      'U_ACTION'    => $this->u_action . '&amp;action=uninstall&amp;hash=' . generate_link_hash('uninstall') . '&amp;id=' . $style['style_id'],
1019                      'L_ACTION'    => $this->user->lang['STYLE_UNINSTALL']
1020                  );
1021              }
1022  
1023              // Preview
1024              $actions[] = array(
1025                  'U_ACTION'    => append_sid($this->phpbb_root_path . 'index.' . $this->php_ext, 'style=' . $style['style_id']),
1026                  'L_ACTION'    => $this->user->lang['PREVIEW']
1027              );
1028          }
1029          else
1030          {
1031              // Style is not installed
1032              if (empty($style['_available']))
1033              {
1034                  $actions[] = array(
1035                      'HTML'        => $this->user->lang['CANNOT_BE_INSTALLED']
1036                  );
1037              }
1038              else
1039              {
1040                  $actions[] = array(
1041                      'U_ACTION'    => $this->u_action . '&amp;action=install&amp;hash=' . generate_link_hash('install') . '&amp;dir=' . urlencode($style['style_path']),
1042                      'L_ACTION'    => $this->user->lang['INSTALL_STYLE']
1043                  );
1044              }
1045          }
1046  
1047          // todo: add hook
1048  
1049          // Assign template variables
1050          $this->template->assign_block_vars('styles_list', $row);
1051          foreach ($actions as $action)
1052          {
1053              $this->template->assign_block_vars('styles_list.actions', $action);
1054          }
1055  
1056          // Increase counters
1057          $counter = ($style['style_id']) ? ($style['style_active'] ? 'active' : 'inactive') : (empty($style['_available']) ? 'cannotinstall' : 'caninstall');
1058          if (!isset($this->style_counters))
1059          {
1060              $this->style_counters = array(
1061                  'total'        => 0,
1062                  'active'    => 0,
1063                  'inactive'    => 0,
1064                  'caninstall'    => 0,
1065                  'cannotinstall'    => 0
1066                  );
1067          }
1068          $this->style_counters[$counter]++;
1069          $this->style_counters['total']++;
1070      }
1071  
1072      /**
1073      * Show welcome message
1074      *
1075      * @param string $title main title
1076      * @param string $description page description
1077      */
1078  	protected function welcome_message($title, $description)
1079      {
1080          $this->template->assign_vars(array(
1081              'L_TITLE'    => $this->user->lang[$title],
1082              'L_EXPLAIN'    => (isset($this->user->lang[$description])) ? $this->user->lang[$description] : ''
1083              )
1084          );
1085      }
1086  
1087      /**
1088      * Find all directories that have styles
1089      *
1090      * @return array Directory names
1091      */
1092  	protected function find_style_dirs()
1093      {
1094          $styles = array();
1095  
1096          $dp = @opendir($this->styles_path);
1097          if ($dp)
1098          {
1099              while (($file = readdir($dp)) !== false)
1100              {
1101                  $dir = $this->styles_path . $file;
1102                  if ($file[0] == '.' || !is_dir($dir))
1103                  {
1104                      continue;
1105                  }
1106  
1107                  if (file_exists("{$dir}/style.cfg"))
1108                  {
1109                      $styles[] = $file;
1110                  }
1111              }
1112              closedir($dp);
1113          }
1114  
1115          return $styles;
1116      }
1117  
1118      /**
1119      * Sort styles
1120      */
1121  	public function sort_styles($style1, $style2)
1122      {
1123          if ($style1['style_active'] != $style2['style_active'])
1124          {
1125              return ($style1['style_active']) ? -1 : 1;
1126          }
1127          if (isset($style1['_available']) && $style1['_available'] != $style2['_available'])
1128          {
1129              return ($style1['_available']) ? -1 : 1;
1130          }
1131          return strcasecmp(isset($style1['style_name']) ? $style1['style_name'] : $style1['name'], isset($style2['style_name']) ? $style2['style_name'] : $style2['name']);
1132      }
1133  
1134      /**
1135      * Read style configuration file
1136      *
1137      * @param string $dir style directory
1138      * @return array|bool Style data, false on error
1139      */
1140  	protected function read_style_cfg($dir)
1141      {
1142          // This should never happen, we give them a red warning because of its relevance.
1143          if (!file_exists($this->styles_path . $dir . '/style.cfg'))
1144          {
1145              trigger_error($this->user->lang('NO_STYLE_CFG', $dir), E_USER_WARNING);
1146          }
1147  
1148          static $required = array('name', 'phpbb_version', 'copyright');
1149  
1150          $cfg = parse_cfg_file($this->styles_path . $dir . '/style.cfg');
1151  
1152          // Check if it is a valid file
1153          foreach ($required as $key)
1154          {
1155              if (!isset($cfg[$key]))
1156              {
1157                  return false;
1158              }
1159          }
1160  
1161          // Check data
1162          if (!isset($cfg['parent']) || !is_string($cfg['parent']) || $cfg['parent'] == $cfg['name'])
1163          {
1164              $cfg['parent'] = '';
1165          }
1166          if (!isset($cfg['template_bitfield']))
1167          {
1168              $cfg['template_bitfield'] = $this->default_bitfield();
1169          }
1170  
1171          return $cfg;
1172      }
1173  
1174      /**
1175      * Install style
1176      *
1177      * @param array $style style data
1178      * @return int Style id
1179      */
1180  	protected function install_style($style)
1181      {
1182          global $user, $phpbb_log;
1183  
1184          // Generate row
1185          $sql_ary = array();
1186          foreach ($style as $key => $value)
1187          {
1188              if ($key != 'style_id' && substr($key, 0, 1) != '_')
1189              {
1190                  $sql_ary[$key] = $value;
1191              }
1192          }
1193  
1194          // Add to database
1195          $this->db->sql_transaction('begin');
1196  
1197          $sql = 'INSERT INTO ' . STYLES_TABLE . '
1198              ' . $this->db->sql_build_array('INSERT', $sql_ary);
1199          $this->db->sql_query($sql);
1200  
1201          $id = $this->db->sql_nextid();
1202  
1203          $this->db->sql_transaction('commit');
1204  
1205          $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_STYLE_ADD', false, array($sql_ary['style_name']));
1206  
1207          return $id;
1208      }
1209  
1210      /**
1211      * Lists all styles
1212      *
1213      * @return array Rows with styles data
1214      */
1215  	protected function get_styles()
1216      {
1217          $sql = 'SELECT *
1218              FROM ' . STYLES_TABLE;
1219          $result = $this->db->sql_query($sql);
1220  
1221          $rows = $this->db->sql_fetchrowset($result);
1222          $this->db->sql_freeresult($result);
1223  
1224          return $rows;
1225      }
1226  
1227      /**
1228      * Count users for each style
1229      *
1230      * @return array Styles in following format: [style_id] = number of users
1231      */
1232  	protected function get_users()
1233      {
1234          $sql = 'SELECT user_style, COUNT(user_style) AS style_count
1235              FROM ' . USERS_TABLE . '
1236              GROUP BY user_style';
1237          $result = $this->db->sql_query($sql);
1238  
1239          $style_count = array();
1240          while ($row = $this->db->sql_fetchrow($result))
1241          {
1242              $style_count[$row['user_style']] = $row['style_count'];
1243          }
1244          $this->db->sql_freeresult($result);
1245  
1246          return $style_count;
1247      }
1248  
1249      /**
1250      * Uninstall style
1251      *
1252      * @param array $style Style data
1253      * @return bool|string True on success, error message on error
1254      */
1255  	protected function uninstall_style($style)
1256      {
1257          $id = $style['style_id'];
1258          $path = $style['style_path'];
1259  
1260          // Check if style has child styles
1261          $sql = 'SELECT style_id
1262              FROM ' . STYLES_TABLE . '
1263              WHERE style_parent_id = ' . (int) $id . " OR style_parent_tree = '" . $this->db->sql_escape($path) . "'";
1264          $result = $this->db->sql_query($sql);
1265  
1266          $conflict = $this->db->sql_fetchrow($result);
1267          $this->db->sql_freeresult($result);
1268  
1269          if ($conflict !== false)
1270          {
1271              return sprintf($this->user->lang['STYLE_UNINSTALL_DEPENDENT'], $style['style_name']);
1272          }
1273  
1274          // Change default style for users
1275          $sql = 'UPDATE ' . USERS_TABLE . '
1276              SET user_style = ' . (int) $this->default_style . '
1277              WHERE user_style = ' . $id;
1278          $this->db->sql_query($sql);
1279  
1280          // Uninstall style
1281          $sql = 'DELETE FROM ' . STYLES_TABLE . '
1282              WHERE style_id = ' . $id;
1283          $this->db->sql_query($sql);
1284          return true;
1285      }
1286  
1287      /**
1288      * Delete all files in style directory
1289      *
1290      * @param string $path Style directory
1291      * @param string $dir Directory to remove inside style's directory
1292      * @return bool True on success, false on error
1293      */
1294  	protected function delete_style_files($path, $dir = '')
1295      {
1296          $dirname = $this->styles_path . $path . $dir;
1297          $result = true;
1298  
1299          $dp = @opendir($dirname);
1300  
1301          if ($dp)
1302          {
1303              while (($file = readdir($dp)) !== false)
1304              {
1305                  if ($file == '.' || $file == '..')
1306                  {
1307                      continue;
1308                  }
1309                  $filename = $dirname . '/' . $file;
1310                  if (is_dir($filename))
1311                  {
1312                      if (!$this->delete_style_files($path, $dir . '/' . $file))
1313                      {
1314                          $result = false;
1315                      }
1316                  }
1317                  else
1318                  {
1319                      if (!@unlink($filename))
1320                      {
1321                          $result = false;
1322                      }
1323                  }
1324              }
1325              closedir($dp);
1326          }
1327          if (!@rmdir($dirname))
1328          {
1329              return false;
1330          }
1331  
1332          return $result;
1333      }
1334  
1335      /**
1336      * Get list of items from posted data
1337      *
1338      * @param string $name Variable name
1339      * @param string|int $default Default value for array
1340      * @param bool $error If true, error will be triggered if list is empty
1341      * @return array Items
1342      */
1343  	protected function request_vars($name, $default, $error = false)
1344      {
1345          $item = $this->request->variable($name, $default);
1346          $items = $this->request->variable($name . 's', array($default));
1347  
1348          if (count($items) == 1 && $items[0] == $default)
1349          {
1350              $items = array();
1351          }
1352  
1353          if ($item != $default && !count($items))
1354          {
1355              $items[] = $item;
1356          }
1357  
1358          if ($error && !count($items))
1359          {
1360              trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING);
1361          }
1362  
1363          return $items;
1364      }
1365  
1366      /**
1367      * Generates default bitfield
1368      *
1369      * This bitfield decides which bbcodes are defined in a template.
1370      *
1371      * @return string Bitfield
1372      */
1373  	protected function default_bitfield()
1374      {
1375          static $value;
1376          if (isset($value))
1377          {
1378              return $value;
1379          }
1380  
1381          // Hardcoded template bitfield to add for new templates
1382          $default_bitfield = '1111111111111';
1383  
1384          $bitfield = new bitfield();
1385          for ($i = 0; $i < strlen($default_bitfield); $i++)
1386          {
1387              if ($default_bitfield[$i] == '1')
1388              {
1389                  $bitfield->set($i);
1390              }
1391          }
1392  
1393          return $bitfield->get_base64();
1394      }
1395  
1396  }


Generated: Wed Nov 11 20:33:01 2020 Cross-referenced by PHPXref 0.7.1