diff options
Diffstat (limited to 'phpBB/includes')
61 files changed, 3424 insertions, 3665 deletions
diff --git a/phpBB/includes/acp/acp_bbcodes.php b/phpBB/includes/acp/acp_bbcodes.php index a3822a982a..e537d7a8b9 100644 --- a/phpBB/includes/acp/acp_bbcodes.php +++ b/phpBB/includes/acp/acp_bbcodes.php @@ -24,7 +24,7 @@ class acp_bbcodes function main($id, $mode) { - global $db, $user, $auth, $template, $cache; + global $db, $user, $auth, $template, $cache, $request; global $config, $phpbb_root_path, $phpbb_admin_path, $phpEx; $user->add_lang('acp/posting'); @@ -272,6 +272,18 @@ class acp_bbcodes $db->sql_query('DELETE FROM ' . BBCODES_TABLE . " WHERE bbcode_id = $bbcode_id"); $cache->destroy('sql', BBCODES_TABLE); add_log('admin', 'LOG_BBCODE_DELETE', $row['bbcode_tag']); + + if ($request->is_ajax()) + { + $json_response = new phpbb_json_response; + $json_response->send(array( + 'MESSAGE_TITLE' => $user->lang['INFORMATION'], + 'MESSAGE_TEXT' => $user->lang['BBCODE_DELETED'], + 'REFRESH_DATA' => array( + 'time' => 3 + ) + )); + } } else { diff --git a/phpBB/includes/acp/acp_bots.php b/phpBB/includes/acp/acp_bots.php index f080b3c9fb..b9dd6664f4 100644 --- a/phpBB/includes/acp/acp_bots.php +++ b/phpBB/includes/acp/acp_bots.php @@ -24,7 +24,7 @@ class acp_bots function main($id, $mode) { - global $config, $db, $user, $auth, $template, $cache; + global $config, $db, $user, $auth, $template, $cache, $request; global $phpbb_root_path, $phpbb_admin_path, $phpEx, $table_prefix; $action = request_var('action', ''); @@ -352,6 +352,14 @@ class acp_bots break; } + + if ($request->is_ajax() && ($action == 'activate' || $action == 'deactivate')) + { + $json_response = new phpbb_json_response; + $json_response->send(array( + 'text' => $user->lang['BOT_' . (($action == 'activate') ? 'DE' : '') . 'ACTIVATE'], + )); + } $s_options = ''; $_options = array('activate' => 'BOT_ACTIVATE', 'deactivate' => 'BOT_DEACTIVATE', 'delete' => 'DELETE'); diff --git a/phpBB/includes/acp/acp_forums.php b/phpBB/includes/acp/acp_forums.php index fad22fc285..3a3b2021eb 100644 --- a/phpBB/includes/acp/acp_forums.php +++ b/phpBB/includes/acp/acp_forums.php @@ -25,7 +25,7 @@ class acp_forums function main($id, $mode) { - global $db, $user, $auth, $template, $cache; + global $db, $user, $auth, $template, $cache, $request; global $config, $phpbb_admin_path, $phpbb_root_path, $phpEx; $user->add_lang('acp/forums'); @@ -255,6 +255,12 @@ class acp_forums add_log('admin', 'LOG_FORUM_' . strtoupper($action), $row['forum_name'], $move_forum_name); $cache->destroy('sql', FORUMS_TABLE); } + + if ($request->is_ajax()) + { + $json_response = new phpbb_json_response; + $json_response->send(array('success' => ($move_forum_name !== false))); + } break; diff --git a/phpBB/includes/acp/acp_icons.php b/phpBB/includes/acp/acp_icons.php index 49a092f16b..bfe17c5007 100644 --- a/phpBB/includes/acp/acp_icons.php +++ b/phpBB/includes/acp/acp_icons.php @@ -782,6 +782,18 @@ class acp_icons $cache->destroy('_icons'); $cache->destroy('sql', $table); + + if ($request->is_ajax()) + { + $json_response = new phpbb_json_response; + $json_response->send(array( + 'MESSAGE_TITLE' => $user->lang['INFORMATION'], + 'MESSAGE_TEXT' => $notice, + 'REFRESH_DATA' => array( + 'time' => 3 + ) + )); + } } else { diff --git a/phpBB/includes/acp/acp_main.php b/phpBB/includes/acp/acp_main.php index b30c294ce2..144b225766 100644 --- a/phpBB/includes/acp/acp_main.php +++ b/phpBB/includes/acp/acp_main.php @@ -24,7 +24,7 @@ class acp_main function main($id, $mode) { - global $config, $db, $user, $auth, $template; + global $config, $db, $user, $auth, $template, $request; global $phpbb_root_path, $phpbb_admin_path, $phpEx; // Show restore permissions notice @@ -129,6 +129,11 @@ class acp_main set_config('record_online_users', 1, true); set_config('record_online_date', time(), true); add_log('admin', 'LOG_RESET_ONLINE'); + + if ($request->is_ajax()) + { + trigger_error('RESET_ONLINE_SUCCESS'); + } break; case 'stats': @@ -179,6 +184,11 @@ class acp_main update_last_username(); add_log('admin', 'LOG_RESYNC_STATS'); + + if ($request->is_ajax()) + { + trigger_error('RESYNC_STATS_SUCCESS'); + } break; case 'user': @@ -241,7 +251,11 @@ class acp_main } add_log('admin', 'LOG_RESYNC_POSTCOUNTS'); - + + if ($request->is_ajax()) + { + trigger_error('RESYNC_POSTCOUNTS_SUCCESS'); + } break; case 'date': @@ -252,6 +266,11 @@ class acp_main set_config('board_startdate', time() - 1); add_log('admin', 'LOG_RESET_DATE'); + + if ($request->is_ajax()) + { + trigger_error('RESET_DATE_SUCCESS'); + } break; case 'db_track': @@ -327,14 +346,14 @@ class acp_main } add_log('admin', 'LOG_RESYNC_POST_MARKING'); - break; - - case 'purge_cache': - if ((int) $user->data['user_type'] !== USER_FOUNDER) + + if ($request->is_ajax()) { - trigger_error($user->lang['NO_AUTH_OPERATION'] . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error('RESYNC_POST_MARKING_SUCCESS'); } + break; + case 'purge_cache': global $cache; $cache->purge(); @@ -343,6 +362,11 @@ class acp_main cache_moderators(); add_log('admin', 'LOG_PURGE_CACHE'); + + if ($request->is_ajax()) + { + trigger_error('PURGE_CACHE_SUCCESS'); + } break; case 'purge_sessions': @@ -389,6 +413,11 @@ class acp_main $db->sql_query($sql); add_log('admin', 'LOG_PURGE_SESSIONS'); + + if ($request->is_ajax()) + { + trigger_error('PURGE_SESSIONS_SUCCESS'); + } break; } } @@ -397,11 +426,11 @@ class acp_main // Version check $user->add_lang('install'); - if ($auth->acl_get('a_server') && version_compare(PHP_VERSION, '5.2.0', '<')) + if ($auth->acl_get('a_server') && version_compare(PHP_VERSION, '5.3.2', '<')) { $template->assign_vars(array( 'S_PHP_VERSION_OLD' => true, - 'L_PHP_VERSION_OLD' => sprintf($user->lang['PHP_VERSION_OLD'], '<a href="http://www.phpbb.com/community/viewtopic.php?f=14&t=1958605">', '</a>'), + 'L_PHP_VERSION_OLD' => sprintf($user->lang['PHP_VERSION_OLD'], '<a href="http://www.phpbb.com/community/viewtopic.php?f=14&t=2152375">', '</a>'), )); } diff --git a/phpBB/includes/acp/acp_modules.php b/phpBB/includes/acp/acp_modules.php index 9d87bbbfbb..8528dc91c4 100644 --- a/phpBB/includes/acp/acp_modules.php +++ b/phpBB/includes/acp/acp_modules.php @@ -36,7 +36,7 @@ class acp_modules function main($id, $mode) { - global $db, $user, $auth, $template, $module; + global $db, $user, $auth, $template, $module, $request; global $config, $phpbb_admin_path, $phpbb_root_path, $phpEx; // Set a global define for modules we might include (the author is able to prevent execution of code by checking this constant) @@ -373,6 +373,15 @@ class acp_modules // Default management page if (sizeof($errors)) { + if ($request->is_ajax()) + { + $json_response = new phpbb_json_response; + $json_response->send(array( + 'MESSAGE_TITLE' => $user->lang('ERROR'), + 'MESSAGE_TEXT' => implode('<br />', $errors), + )); + } + $template->assign_vars(array( 'S_ERROR' => true, 'ERROR_MSG' => implode('<br />', $errors)) diff --git a/phpBB/includes/acp/acp_permissions.php b/phpBB/includes/acp/acp_permissions.php index 150b67b8f7..d728744c04 100644 --- a/phpBB/includes/acp/acp_permissions.php +++ b/phpBB/includes/acp/acp_permissions.php @@ -1105,7 +1105,7 @@ class acp_permissions { if ($user_id != $user->data['user_id']) { - $auth2 = new auth(); + $auth2 = new phpbb_auth(); $auth2->acl($userdata); $auth_setting = $auth2->acl_get($permission); } diff --git a/phpBB/includes/acp/acp_profile.php b/phpBB/includes/acp/acp_profile.php index 511148baf9..3ffffd3047 100644 --- a/phpBB/includes/acp/acp_profile.php +++ b/phpBB/includes/acp/acp_profile.php @@ -242,6 +242,15 @@ class acp_profile $db->sql_freeresult($result); add_log('admin', 'LOG_PROFILE_FIELD_ACTIVATE', $field_ident); + + if ($request->is_ajax()) + { + $json_response = new phpbb_json_response(); + $json_response->send(array( + 'text' => $user->lang('DEACTIVATE'), + )); + } + trigger_error($user->lang['PROFILE_FIELD_ACTIVATED'] . adm_back_link($this->u_action)); break; @@ -266,7 +275,16 @@ class acp_profile $field_ident = (string) $db->sql_fetchfield('field_ident'); $db->sql_freeresult($result); + if ($request->is_ajax()) + { + $json_response = new phpbb_json_response(); + $json_response->send(array( + 'text' => $user->lang('ACTIVATE'), + )); + } + add_log('admin', 'LOG_PROFILE_FIELD_DEACTIVATE', $field_ident); + trigger_error($user->lang['PROFILE_FIELD_DEACTIVATED'] . adm_back_link($this->u_action)); break; diff --git a/phpBB/includes/acp/acp_ranks.php b/phpBB/includes/acp/acp_ranks.php index ec5a76df87..d9ed5b17f1 100644 --- a/phpBB/includes/acp/acp_ranks.php +++ b/phpBB/includes/acp/acp_ranks.php @@ -24,7 +24,7 @@ class acp_ranks function main($id, $mode) { - global $db, $user, $auth, $template, $cache; + global $db, $user, $auth, $template, $cache, $request; global $config, $phpbb_root_path, $phpbb_admin_path, $phpEx; $user->add_lang('acp/posting'); @@ -122,6 +122,18 @@ class acp_ranks $cache->destroy('_ranks'); add_log('admin', 'LOG_RANK_REMOVED', $rank_title); + + if ($request->is_ajax()) + { + $json_response = new phpbb_json_response; + $json_response->send(array( + 'MESSAGE_TITLE' => $user->lang['INFORMATION'], + 'MESSAGE_TEXT' => $user->lang['RANK_REMOVED'], + 'REFRESH_DATA' => array( + 'time' => 3 + ) + )); + } } else { diff --git a/phpBB/includes/acp/acp_styles.php b/phpBB/includes/acp/acp_styles.php index 7b449d3b35..de1f678e38 100644 --- a/phpBB/includes/acp/acp_styles.php +++ b/phpBB/includes/acp/acp_styles.php @@ -20,2589 +20,1285 @@ if (!defined('IN_PHPBB')) */ class acp_styles { - var $u_action; - - var $style_cfg; - var $template_cfg; - var $theme_cfg; - - function main($id, $mode) + public $u_action; + + protected $u_base_action; + protected $s_hidden_fields; + protected $mode; + protected $styles_path; + protected $styles_path_absolute = 'styles'; + protected $default_style = 0; + + protected $db; + protected $user; + protected $template; + protected $request; + protected $cache; + protected $auth; + protected $phpbb_root_path; + protected $phpEx; + + public function main($id, $mode) { - global $db, $user, $auth, $template, $cache; - global $config, $phpbb_root_path, $phpbb_admin_path, $phpEx; - - // Hardcoded template bitfield to add for new templates - $bitfield = new bitfield(); - $bitfield->set(0); - $bitfield->set(1); - $bitfield->set(2); - $bitfield->set(3); - $bitfield->set(4); - $bitfield->set(8); - $bitfield->set(9); - $bitfield->set(11); - $bitfield->set(12); - $this->template_bitfield = $bitfield->get_base64(); - unset($bitfield); + global $db, $user, $phpbb_admin_path, $phpbb_root_path, $phpEx, $template, $request, $cache, $auth, $config; + + $this->db = $db; + $this->user = $user; + $this->template = $template; + $this->request = $request; + $this->cache = $cache; + $this->auth = $auth; + $this->config = $config; + $this->phpbb_root_path = $phpbb_root_path; + $this->phpEx = $phpEx; + + $this->default_style = $config['default_style']; + $this->styles_path = $this->phpbb_root_path . $this->styles_path_absolute . '/'; + + $this->u_base_action = append_sid("{$phpbb_admin_path}index.$phpEx", "i={$id}"); + $this->s_hidden_fields = array( + 'mode' => $mode, + ); - $user->add_lang('acp/styles'); + $this->user->add_lang('acp/styles'); $this->tpl_name = 'acp_styles'; $this->page_title = 'ACP_CAT_STYLES'; + $this->mode = $mode; - $action = request_var('action', ''); - $action = (isset($_POST['add'])) ? 'add' : $action; - $style_id = request_var('id', 0); - - // Fill the configuration variables - $this->style_cfg = $this->template_cfg = $this->theme_cfg = ' -# -# phpBB {MODE} configuration file -# -# @package phpBB3 -# @copyright (c) 2005 phpBB Group -# @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 -# -# -# At the left is the name, please do not change this -# At the right the value is entered -# For on/off options the valid values are on, off, 1, 0, true and false -# -# Values get trimmed, if you want to add a space in front or at the end of -# the value, then enclose the value with single or double quotes. -# Single and double quotes do not need to be escaped. -# -# - -# General Information about this {MODE} -name = {NAME} -copyright = {COPYRIGHT} -version = {VERSION} -'; - - $this->template_cfg .= ' -# Some configuration options - -# Template inheritance -# See http://blog.phpbb.com/2008/07/31/templating-just-got-easier/ -# Set value to empty or this template name to ignore template inheritance. -inherit_from = {INHERIT_FROM} -'; - - // Execute overall actions - switch ($action) + $action = $this->request->variable('action', ''); + $post_actions = array('install', 'activate', 'deactivate', 'uninstall'); + foreach ($post_actions as $key) { - case 'delete': - if ($style_id) - { - $this->remove($mode, $style_id); - return; - } - break; + if (isset($_POST[$key])) + { + $action = $key; + } + } + if ($action != '') + { + $this->s_hidden_fields['action'] = $action; + } - case 'export': - if ($style_id) - { - $this->export($mode, $style_id); - return; - } - break; + $this->template->assign_vars(array( + 'U_ACTION' => $this->u_base_action, + 'S_HIDDEN_FIELDS' => build_hidden_fields($this->s_hidden_fields) + ) + ); + // Execute actions + switch ($action) + { case 'install': - $this->install($mode); + $this->action_install(); return; - break; - - case 'add': - $this->add($mode); + case 'uninstall': + $this->action_uninstall(); + return; + case 'activate': + $this->action_activate(); + return; + case 'deactivate': + $this->action_deactivate(); return; - break; - case 'details': - if ($style_id) - { - $this->details($mode, $style_id); - return; - } - break; - - case 'edit': - if ($style_id) - { - switch ($mode) - { - case 'template': - return $this->edit_template($style_id); - case 'theme': - return $this->edit_theme($style_id); - } - } - break; - - case 'cache': - if ($style_id) - { - switch ($mode) - { - case 'template': - return $this->template_cache($style_id); - } - } - break; + $this->action_details(); + return; + default: + $this->frontend(); } + } - switch ($mode) + /** + * Main page + */ + protected function frontend() + { + // Check mode + switch ($this->mode) { case 'style': - - switch ($action) - { - case 'activate': - case 'deactivate': - - if ($style_id == $config['default_style']) - { - trigger_error($user->lang['DEACTIVATE_DEFAULT'] . adm_back_link($this->u_action), E_USER_WARNING); - } - - if (($action == 'deactivate' && confirm_box(true)) || $action == 'activate') - { - $sql = 'UPDATE ' . STYLES_TABLE . ' - SET style_active = ' . (($action == 'activate') ? 1 : 0) . ' - WHERE style_id = ' . $style_id; - $db->sql_query($sql); - - // Set style to default for any member using deactivated style - if ($action == 'deactivate') - { - $sql = 'UPDATE ' . USERS_TABLE . ' - SET user_style = ' . $config['default_style'] . " - WHERE user_style = $style_id"; - $db->sql_query($sql); - - $sql = 'UPDATE ' . FORUMS_TABLE . ' - SET forum_style = 0 - WHERE forum_style = ' . $style_id; - $db->sql_query($sql); - } - } - else if ($action == 'deactivate') - { - $s_hidden_fields = array( - 'i' => $id, - 'mode' => $mode, - 'action' => $action, - 'style_id' => $style_id, - ); - confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields($s_hidden_fields)); - } - break; - } - - $this->frontend('style', array('details'), array('export', 'delete')); - break; - - case 'template': - - switch ($action) - { - // Clear cache - case 'refresh': - - $sql = 'SELECT * - FROM ' . STYLES_TEMPLATE_TABLE . " - WHERE template_id = $style_id"; - $result = $db->sql_query($sql); - $template_row = $db->sql_fetchrow($result); - $db->sql_freeresult($result); - - if (!$template_row) - { - trigger_error($user->lang['NO_TEMPLATE'] . adm_back_link($this->u_action), E_USER_WARNING); - } - - if (confirm_box(true)) - { - $this->clear_template_cache($template_row); - - trigger_error($user->lang['TEMPLATE_CACHE_CLEARED'] . adm_back_link($this->u_action)); - } - else - { - confirm_box(false, $user->lang['CONFIRM_TEMPLATE_CLEAR_CACHE'], build_hidden_fields(array( - 'i' => $id, - 'mode' => $mode, - 'action' => $action, - 'id' => $style_id - ))); - } - - break; - } - - $this->frontend('template', array('edit', 'cache', 'details'), array('refresh', 'export', 'delete')); - break; - - case 'theme': - $this->frontend('theme', array('edit', 'details'), array('export', 'delete')); - break; + $this->welcome_message('ACP_STYLES', 'ACP_STYLES_EXPLAIN'); + $this->show_installed(); + return; + case 'install': + $this->welcome_message('INSTALL_STYLES', 'INSTALL_STYLES_EXPLAIN'); + $this->show_available(); + return; + case 'cache': + $this->action_cache(); + return; } + trigger_error($this->user->lang['NO_MODE'] . adm_back_link($this->u_action), E_USER_WARNING); } /** - * Build Frontend with supplied options + * Purge cache */ - function frontend($mode, $options, $actions) + protected function action_cache() { - global $user, $template, $db, $config, $phpbb_root_path, $phpEx; + $this->cache->purge(); - $sql_from = ''; - $sql_sort = 'LOWER(' . $mode . '_name)'; - $style_count = array(); + // Clear permissions + $this->auth->acl_clear_prefetch(); + cache_moderators(); - switch ($mode) - { - case 'style': - $sql_from = STYLES_TABLE; - $sql_sort = 'style_active DESC, ' . $sql_sort; + add_log('admin', 'LOG_PURGE_CACHE'); - $sql = 'SELECT user_style, COUNT(user_style) AS style_count - FROM ' . USERS_TABLE . ' - GROUP BY user_style'; - $result = $db->sql_query($sql); + trigger_error($this->user->lang['PURGED_CACHE'] . adm_back_link($this->u_base_action), E_USER_NOTICE); + } - while ($row = $db->sql_fetchrow($result)) + /** + * Install style(s) + */ + protected function action_install() + { + // Get list of styles to install + $dirs = $this->request_vars('dir', '', true); + + // Get list of styles that can be installed + $styles = $this->find_available(false); + + // Install each style + $messages = array(); + $installed_names = array(); + $installed_dirs = array(); + $last_installed = false; + foreach ($dirs as $dir) + { + $found = false; + foreach ($styles as &$style) + { + // Check if: + // 1. Directory matches directory we are looking for + // 2. Style is not installed yet + // 3. Style with same name or directory hasn't been installed already within this function + if ($style['style_path'] == $dir && empty($style['_installed']) && !in_array($style['style_path'], $installed_dirs) && !in_array($style['style_name'], $installed_names)) { - $style_count[$row['user_style']] = $row['style_count']; + // Install style + $style['style_active'] = 1; + $style['style_id'] = $this->install_style($style); + $style['_installed'] = true; + $found = true; + $last_installed = $style['style_id']; + $installed_names[] = $style['style_name']; + $installed_dirs[] = $style['style_path']; + $messages[] = sprintf($this->user->lang['STYLE_INSTALLED'], htmlspecialchars($style['style_name'])); } - $db->sql_freeresult($result); - - break; - - case 'template': - $sql_from = STYLES_TEMPLATE_TABLE; - break; - - case 'theme': - $sql_from = STYLES_THEME_TABLE; - break; - - default: - trigger_error($user->lang['NO_MODE'] . adm_back_link($this->u_action), E_USER_WARNING); - } - - $l_prefix = strtoupper($mode); - - $this->page_title = 'ACP_' . $l_prefix . 'S'; - - $template->assign_vars(array( - 'S_FRONTEND' => true, - 'S_STYLE' => ($mode == 'style') ? true : false, - - 'L_TITLE' => $user->lang[$this->page_title], - 'L_EXPLAIN' => $user->lang[$this->page_title . '_EXPLAIN'], - 'L_NAME' => $user->lang[$l_prefix . '_NAME'], - 'L_INSTALLED' => $user->lang['INSTALLED_' . $l_prefix], - 'L_UNINSTALLED' => $user->lang['UNINSTALLED_' . $l_prefix], - 'L_NO_UNINSTALLED' => $user->lang['NO_UNINSTALLED_' . $l_prefix], - 'L_CREATE' => $user->lang['CREATE_' . $l_prefix], - - 'U_ACTION' => $this->u_action, - ) - ); - - $sql = "SELECT * - FROM $sql_from - ORDER BY $sql_sort ASC"; - $result = $db->sql_query($sql); - - $installed = array(); - - $basis_options = '<option class="sep" value="">' . $user->lang['OPTIONAL_BASIS'] . '</option>'; - while ($row = $db->sql_fetchrow($result)) - { - $installed[] = $row[$mode . '_name']; - $basis_options .= '<option value="' . $row[$mode . '_id'] . '">' . $row[$mode . '_name'] . '</option>'; - - $stylevis = ($mode == 'style' && !$row['style_active']) ? 'activate' : 'deactivate'; - - $s_options = array(); - foreach ($options as $option) - { - $s_options[] = '<a href="' . $this->u_action . "&action=$option&id=" . $row[$mode . '_id'] . '">' . $user->lang[strtoupper($option)] . '</a>'; } - - $s_actions = array(); - foreach ($actions as $option) + if (!$found) { - $s_actions[] = '<a href="' . $this->u_action . "&action=$option&id=" . $row[$mode . '_id'] . '">' . $user->lang[strtoupper($option)] . '</a>'; + $messages[] = sprintf($this->user->lang['STYLE_NOT_INSTALLED'], htmlspecialchars($dir)); } - - $template->assign_block_vars('installed', array( - 'S_DEFAULT_STYLE' => ($mode == 'style' && $row['style_id'] == $config['default_style']) ? true : false, - 'U_EDIT' => $this->u_action . '&action=' . (($mode == 'style') ? 'details' : 'edit') . '&id=' . $row[$mode . '_id'], - 'U_STYLE_ACT_DEACT' => $this->u_action . '&action=' . $stylevis . '&id=' . $row[$mode . '_id'], - 'L_STYLE_ACT_DEACT' => $user->lang['STYLE_' . strtoupper($stylevis)], - 'S_OPTIONS' => implode(' | ', $s_options), - 'S_ACTIONS' => implode(' | ', $s_actions), - 'U_PREVIEW' => ($mode == 'style') ? append_sid("{$phpbb_root_path}index.$phpEx", "$mode=" . $row[$mode . '_id']) : '', - - 'NAME' => $row[$mode . '_name'], - 'STYLE_COUNT' => ($mode == 'style' && isset($style_count[$row['style_id']])) ? $style_count[$row['style_id']] : 0, - - 'S_INACTIVE' => ($mode == 'style' && !$row['style_active']) ? true : false, - ) - ); } - $db->sql_freeresult($result); - - // Grab uninstalled items - $new_ary = $cfg = array(); - $dp = @opendir("{$phpbb_root_path}styles"); - - if ($dp) + // Show message + if (!count($messages)) { - while (($file = readdir($dp)) !== false) - { - if ($file[0] == '.' || !is_dir($phpbb_root_path . 'styles/' . $file)) - { - continue; - } - - $subpath = ($mode != 'style') ? "$mode/" : ''; - if (file_exists("{$phpbb_root_path}styles/$file/$subpath$mode.cfg")) - { - if ($cfg = file("{$phpbb_root_path}styles/$file/$subpath$mode.cfg")) - { - $items = parse_cfg_file('', $cfg); - $name = (isset($items['name'])) ? trim($items['name']) : false; - - if ($name && !in_array($name, $installed)) - { - $new_ary[] = array( - 'path' => $file, - 'name' => $name, - 'copyright' => $items['copyright'], - ); - } - } - } - } - closedir($dp); + trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING); } + $message = implode('<br />', $messages); + $message .= '<br /><br />' . sprintf($this->user->lang['STYLE_INSTALLED_RETURN_STYLES'], $this->u_base_action . '&mode=style'); + $message .= '<br /><br />' . sprintf($this->user->lang['STYLE_INSTALLED_RETURN_UNINSTALLED'], $this->u_base_action . '&mode=install'); + trigger_error($message, E_USER_NOTICE); + } - unset($installed); + /** + * Confirm styles removal + */ + protected function action_uninstall() + { + // Get list of styles to uninstall + $ids = $this->request_vars('id', 0, true); - if (sizeof($new_ary)) + // Check if confirmation box was submitted + if (confirm_box(true)) { - foreach ($new_ary as $cfg) - { - $template->assign_block_vars('uninstalled', array( - 'NAME' => $cfg['name'], - 'COPYRIGHT' => $cfg['copyright'], - 'U_INSTALL' => $this->u_action . '&action=install&path=' . urlencode($cfg['path'])) - ); - } + // Uninstall + $this->action_uninstall_confirmed($ids, $this->request->variable('confirm_delete_files', false)); + return; } - unset($new_ary); - $template->assign_vars(array( - 'S_BASIS_OPTIONS' => $basis_options) - ); + // Confirm box + $s_hidden = build_hidden_fields(array( + 'action' => 'uninstall', + 'ids' => $ids + )); + $this->template->assign_var('S_CONFIRM_DELETE', true); + confirm_box(false, $this->user->lang['CONFIRM_UNINSTALL_STYLES'], $s_hidden, 'acp_styles.html'); + // Canceled - show styles list + $this->frontend(); } /** - * Provides a template editor which allows saving changes to template files on the filesystem or in the database. + * Uninstall styles(s) * - * @param int $template_id specifies which template set is being edited + * @param array $ids List of style IDs + * @param bool $delete_files If true, script will attempt to remove files for selected styles */ - function edit_template($template_id) + protected function action_uninstall_confirmed($ids, $delete_files) { - global $phpbb_root_path, $phpEx, $config, $db, $cache, $user, $template, $safe_mode; - - if (defined('PHPBB_DISABLE_ACP_EDITOR')) - { - trigger_error($user->lang['EDITOR_DISABLED'] . adm_back_link($this->u_action)); - } - - $this->page_title = 'EDIT_TEMPLATE'; - - $filelist = $filelist_cats = array(); - - $template_data = utf8_normalize_nfc(request_var('template_data', '', true)); - $template_data = htmlspecialchars_decode($template_data); - $template_file = utf8_normalize_nfc(request_var('template_file', '', true)); - $text_rows = max(5, min(999, request_var('text_rows', 20))); - $save_changes = (isset($_POST['save'])) ? true : false; - - // make sure template_file path doesn't go upwards - $template_file = preg_replace('#\.{2,}#', '.', $template_file); - - // Retrieve some information about the template - $sql = 'SELECT template_path, template_name - FROM ' . STYLES_TEMPLATE_TABLE . " - WHERE template_id = $template_id"; - $result = $db->sql_query($sql); - $template_info = $db->sql_fetchrow($result); - $db->sql_freeresult($result); - - if (!$template_info) - { - trigger_error($user->lang['NO_TEMPLATE'] . adm_back_link($this->u_action), E_USER_WARNING); - } - - // Get the filesystem location of the current file - $template_path = "{$phpbb_root_path}styles/{$template_info['template_path']}/template"; - $file = "$template_path/$template_file"; + $default = $this->default_style; + $uninstalled = array(); + $messages = array(); - if ($template_file) + // Check styles list + foreach ($ids as $id) { - $l_not_writable = sprintf($user->lang['TEMPLATE_FILE_NOT_WRITABLE'], htmlspecialchars($template_file)) . adm_back_link($this->u_action); - - if ($safe_mode) + if (!$id) { - trigger_error($l_not_writable, E_USER_WARNING); - } - - if (file_exists($file) && is_file($file) && is_readable($file)) - { - if (!phpbb_is_writable($file)) - { - trigger_error($l_not_writable, E_USER_WARNING); - } + trigger_error($this->user->lang['INVALID_STYLE_ID'] . adm_back_link($this->u_action), E_USER_WARNING); } - else + if ($id == $default) { - trigger_error($user->lang['NO_TEMPLATE'] . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($this->user->lang['UNINSTALL_DEFAULT'] . adm_back_link($this->u_action), E_USER_WARNING); } + $uninstalled[$id] = false; } - if ($save_changes && !check_form_key('acp_styles')) - { - trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING); - } - else if (!$save_changes) - { - add_form_key('acp_styles'); - } + // Order by reversed style_id, so parent styles would be removed after child styles + // This way parent and child styles can be removed in same function call + $sql = 'SELECT * + FROM ' . STYLES_TABLE . ' + WHERE style_id IN (' . implode(', ', $ids) . ') + ORDER BY style_id DESC'; + $result = $this->db->sql_query($sql); - // save changes to the template if the user submitted any - if ($save_changes && $template_file) + $rows = $this->db->sql_fetchrowset($result); + $this->db->sql_freeresult($result); + + // Uinstall each style + $uninstalled = array(); + foreach ($rows as $style) { - // Try to write the file - if (!($fp = @fopen($file, 'wb'))) + $result = $this->uninstall_style($style, $delete_files); + + if (is_string($result)) { - // File exists and is writeable, but still not able to be written to - trigger_error($l_not_writable, E_USER_WARNING); + $messages[] = $result; + continue; } - fwrite($fp, $template_data); - fclose($fp); - - // destroy the cached version of the template (filename without extension) - $this->clear_template_cache($template_info, array(substr($template_file, 0, -5))); - - $cache->destroy('sql', STYLES_TABLE); - - add_log('admin', 'LOG_TEMPLATE_EDIT', $template_info['template_name'], $template_file); - trigger_error($user->lang['TEMPLATE_FILE_UPDATED'] . adm_back_link($this->u_action . "&action=edit&id=$template_id&text_rows=$text_rows&template_file=$template_file")); - } + $messages[] = sprintf($this->user->lang['STYLE_UNINSTALLED'], $style['style_name']); + $uninstalled[] = $style['style_name']; - // Generate a category array containing template filenames - - $filelist = filelist($template_path, '', 'html'); - $filelist[''] = array_diff($filelist[''], array('bbcode.html')); - - if ($template_file) - { - $template_data = file_get_contents($file); - - if (!$template_data) + // Attempt to delete files + if ($delete_files) { - trigger_error($user->lang['NO_TEMPLATE'] . adm_back_link($this->u_action), E_USER_WARNING); + $messages[] = sprintf($this->user->lang[$this->delete_style_files($style['style_path']) ? 'DELETE_STYLE_FILES_SUCCESS' : 'DELETE_STYLE_FILES_FAILED'], $style['style_name']); } } - if (empty($filelist[''])) + if (empty($messages)) { - trigger_error($user->lang['NO_TEMPLATE'] . adm_back_link($this->u_action), E_USER_WARNING); + // Nothing to uninstall? + trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING); } - // Now create the categories - $filelist_cats[''] = array(); - foreach ($filelist as $pathfile => $file_ary) + // Log action + if (count($uninstalled)) { - // Use the directory name as category name - if (!empty($pathfile)) - { - $filelist_cats[$pathfile] = array(); - foreach ($file_ary as $file) - { - $filelist_cats[$pathfile][$pathfile . $file] = $file; - } - } - // or if it's in the main category use the word before the first underscore to group files - else - { - $cats = array(); - foreach ($file_ary as $file) - { - $cats[] = substr($file, 0, strpos($file, '_')); - $filelist_cats[substr($file, 0, strpos($file, '_'))][$file] = $file; - } - - $cats = array_values(array_unique($cats)); - - // we don't need any single element categories so put them into the misc '' category - for ($i = 0, $n = sizeof($cats); $i < $n; $i++) - { - if (sizeof($filelist_cats[$cats[$i]]) == 1 && $cats[$i] !== '') - { - $filelist_cats[''][key($filelist_cats[$cats[$i]])] = current($filelist_cats[$cats[$i]]); - unset($filelist_cats[$cats[$i]]); - } - } - unset($cats); - } + add_log('admin', 'LOG_STYLE_DELETE', implode(', ', $uninstalled)); } - unset($filelist); - - // Generate list of categorised template files - $tpl_options = ''; - ksort($filelist_cats); - foreach ($filelist_cats as $category => $tpl_ary) - { - ksort($tpl_ary); - if (!empty($category)) - { - $tpl_options .= '<option class="sep" value="">' . $category . '</option>'; - } + // Clear cache + $this->cache->purge(); - foreach ($tpl_ary as $filename => $file) - { - $selected = ($template_file == $filename) ? ' selected="selected"' : ''; - $tpl_options .= '<option value="' . $filename . '"' . $selected . '>' . $file . '</option>'; - } - } - - $template->assign_vars(array( - 'S_EDIT_TEMPLATE' => true, - 'S_HIDDEN_FIELDS' => build_hidden_fields(array('template_file' => $template_file)), - 'S_TEMPLATES' => $tpl_options, - - 'U_ACTION' => $this->u_action . "&action=edit&id=$template_id&text_rows=$text_rows", - 'U_BACK' => $this->u_action, - - 'L_EDIT' => $user->lang['EDIT_TEMPLATE'], - 'L_EDIT_EXPLAIN' => $user->lang['EDIT_TEMPLATE_EXPLAIN'], - 'L_EDITOR' => $user->lang['TEMPLATE_EDITOR'], - 'L_EDITOR_HEIGHT' => $user->lang['TEMPLATE_EDITOR_HEIGHT'], - 'L_FILE' => $user->lang['TEMPLATE_FILE'], - 'L_SELECT' => $user->lang['SELECT_TEMPLATE'], - 'L_SELECTED' => $user->lang['SELECTED_TEMPLATE'], - 'L_SELECTED_FILE' => $user->lang['SELECTED_TEMPLATE_FILE'], - - 'SELECTED_TEMPLATE' => $template_info['template_name'], - 'TEMPLATE_FILE' => $template_file, - 'TEMPLATE_DATA' => utf8_htmlspecialchars($template_data), - 'TEXT_ROWS' => $text_rows) - ); + // Show message + trigger_error(implode('<br />', $messages) . adm_back_link($this->u_action), E_USER_NOTICE); } /** - * Allows the admin to view cached versions of template files and clear single template cache files - * - * @param int $template_id specifies which template's cache is shown + * Activate styles */ - function template_cache($template_id) + protected function action_activate() { - global $phpbb_root_path, $phpEx, $config, $db, $cache, $user, $template; + // Get list of styles to activate + $ids = $this->request_vars('id', 0, true); - $source = str_replace('/', '.', request_var('source', '')); - $file_ary = array_diff(request_var('delete', array('')), array('')); - $submit = isset($_POST['submit']) ? true : false; + // Activate styles + $sql = 'UPDATE ' . STYLES_TABLE . ' + SET style_active = 1 + WHERE style_id IN (' . implode(', ', $ids) . ')'; + $this->db->sql_query($sql); - $sql = 'SELECT * - FROM ' . STYLES_TEMPLATE_TABLE . " - WHERE template_id = $template_id"; - $result = $db->sql_query($sql); - $template_row = $db->sql_fetchrow($result); - $db->sql_freeresult($result); + // Purge cache + $this->cache->destroy('sql', STYLES_TABLE); - if (!$template_row) - { - trigger_error($user->lang['NO_TEMPLATE'] . adm_back_link($this->u_action), E_USER_WARNING); - } - - // User wants to delete one or more files ... - if ($submit && $file_ary) - { - $this->clear_template_cache($template_row, $file_ary); - trigger_error($user->lang['TEMPLATE_CACHE_CLEARED'] . adm_back_link($this->u_action . "&action=cache&id=$template_id")); - } + // Show styles list + $this->frontend(); + } - $cache_prefix = 'tpl_' . str_replace('_', '-', $template_row['template_path']); + /** + * Deactivate styles + */ + protected function action_deactivate() + { + // Get list of styles to deactivate + $ids = $this->request_vars('id', 0, true); - // Someone wants to see the cached source ... so we'll highlight it, - // add line numbers and indent it appropriately. This could be nasty - // on larger source files ... - if ($source && file_exists("{$phpbb_root_path}cache/{$cache_prefix}_$source.html.$phpEx")) + // Check for default style + foreach ($ids as $id) { - adm_page_header($user->lang['TEMPLATE_CACHE']); - - $template->set_filenames(array( - 'body' => 'viewsource.html') - ); - - $template->assign_vars(array( - 'FILENAME' => str_replace('.', '/', $source) . '.html') - ); - - $code = str_replace(array("\r\n", "\r"), array("\n", "\n"), file_get_contents("{$phpbb_root_path}cache/{$cache_prefix}_$source.html.$phpEx")); - - $conf = array('highlight.bg', 'highlight.comment', 'highlight.default', 'highlight.html', 'highlight.keyword', 'highlight.string'); - foreach ($conf as $ini_var) - { - @ini_set($ini_var, str_replace('highlight.', 'syntax', $ini_var)); - } - - $marker = 'MARKER' . time(); - $code = highlight_string(str_replace("\n", $marker, $code), true); - $code = str_replace($marker, "\n", $code); - $str_from = array('<span style="color: ', '<font color="syntax', '</font>', '<code>', '</code>','[', ']', '.', ':'); - $str_to = array('<span class="', '<span class="syntax', '</span>', '', '', '[', ']', '.', ':'); - - $code = str_replace($str_from, $str_to, $code); - $code = preg_replace('#^(<span class="[a-z_]+">)\n?(.*?)\n?(</span>)$#ism', '$1$2$3', $code); - $code = substr($code, strlen('<span class="syntaxhtml">')); - $code = substr($code, 0, -1 * strlen('</ span>')); - $code = explode("\n", $code); - - foreach ($code as $key => $line) + if ($id == $this->default_style) { - $template->assign_block_vars('source', array( - 'LINENUM' => $key + 1, - 'LINE' => preg_replace('#([^ ;]) ([^ &])#', '$1 $2', $line)) - ); - unset($code[$key]); + trigger_error($this->user->lang['DEACTIVATE_DEFAULT'] . adm_back_link($this->u_action), E_USER_WARNING); } - - adm_page_footer(); } - // Get a list of cached template files and then retrieve additional information about them - $file_ary = $this->template_cache_filelist($template_row['template_path']); - - foreach ($file_ary as $file) - { - $file = str_replace('/', '.', $file); + // Reset default style for users who use selected styles + $sql = 'UPDATE ' . USERS_TABLE . ' + SET user_style = 0 + WHERE user_style IN (' . implode(', ', $ids) . ')'; + $this->db->sql_query($sql); - // perform some dirty guessing to get the path right. - // We assume that three dots in a row were '../' - $tpl_file = str_replace('.', '/', $file); - $tpl_file = str_replace('///', '../', $tpl_file); + // Deactivate styles + $sql = 'UPDATE ' . STYLES_TABLE . ' + SET style_active = 0 + WHERE style_id IN (' . implode(', ', $ids) . ')'; + $this->db->sql_query($sql); - $filename = "{$cache_prefix}_$file.html.$phpEx"; + // Purge cache + $this->cache->destroy('sql', STYLES_TABLE); - if (!file_exists("{$phpbb_root_path}cache/$filename")) - { - continue; - } - - $file_tpl = "{$phpbb_root_path}styles/{$template_row['template_path']}/template/$tpl_file.html"; - $inherited = false; - - if (isset($template_row['template_inherits_id']) && $template_row['template_inherits_id'] && !file_exists($file_tpl)) - { - $file_tpl = "{$phpbb_root_path}styles/{$template_row['template_inherit_path']}/template/$tpl_file.html"; - $inherited = true; - } - - $template->assign_block_vars('file', array( - 'U_VIEWSOURCE' => $this->u_action . "&action=cache&id=$template_id&source=$file", - - 'CACHED' => $user->format_date(filemtime("{$phpbb_root_path}cache/$filename")), - 'FILENAME' => $file, - 'FILENAME_PATH' => $file_tpl, - 'FILESIZE' => get_formatted_filesize(filesize("{$phpbb_root_path}cache/$filename")), - 'MODIFIED' => $user->format_date(filemtime($file_tpl)), - )); - } - - $template->assign_vars(array( - 'S_CACHE' => true, - 'S_TEMPLATE' => true, - - 'U_ACTION' => $this->u_action . "&action=cache&id=$template_id", - 'U_BACK' => $this->u_action) - ); + // Show styles list + $this->frontend(); } /** - * Provides a css editor and a basic easier to use stylesheet editing tool for less experienced (or lazy) users - * - * @param int $theme_id specifies which theme is being edited + * Show style details */ - function edit_theme($theme_id) + protected function action_details() { - global $phpbb_root_path, $phpEx, $config, $db, $cache, $user, $template, $safe_mode; - - $this->page_title = 'EDIT_THEME'; - - $filelist = $filelist_cats = array(); - - $theme_data = utf8_normalize_nfc(request_var('template_data', '', true)); - $theme_data = htmlspecialchars_decode($theme_data); - $theme_file = utf8_normalize_nfc(request_var('template_file', '', true)); - $text_rows = max(5, min(999, request_var('text_rows', 20))); - $save_changes = (isset($_POST['save'])) ? true : false; - - // make sure theme_file path doesn't go upwards - $theme_file = str_replace('..', '.', $theme_file); - - // Retrieve some information about the theme - $sql = 'SELECT theme_path, theme_name - FROM ' . STYLES_THEME_TABLE . " - WHERE theme_id = $theme_id"; - $result = $db->sql_query($sql); - - if (!($theme_info = $db->sql_fetchrow($result))) + $id = $this->request->variable('id', 0); + if (!$id) { - trigger_error($user->lang['NO_THEME'] . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING); } - $db->sql_freeresult($result); - // Get the filesystem location of the current file - $theme_path = "{$phpbb_root_path}styles/{$theme_info['theme_path']}/theme"; - $file = "$theme_path/$theme_file"; + // Get all styles + $styles = $this->get_styles(); + usort($styles, array($this, 'sort_styles')); - if ($theme_file) + // Find current style + $style = false; + foreach ($styles as $row) { - $l_not_writable = sprintf($user->lang['THEME_FILE_NOT_WRITABLE'], htmlspecialchars($theme_file)) . adm_back_link($this->u_action); - - if ($safe_mode) - { - trigger_error($l_not_writable, E_USER_WARNING); - } - - if (file_exists($file) && is_file($file) && is_readable($file)) + if ($row['style_id'] == $id) { - if (!phpbb_is_writable($file)) - { - trigger_error($l_not_writable, E_USER_WARNING); - } - } - else - { - trigger_error($user->lang['NO_THEME'] . adm_back_link($this->u_action), E_USER_WARNING); + $style = $row; + break; } } - // save changes to the theme if the user submitted any - if ($save_changes && $theme_file) + if ($style === false) { - $message = $user->lang['THEME_UPDATED']; - - if (!($fp = @fopen($file, 'wb'))) - { - trigger_error($l_not_writable, E_USER_WARNING); - } - fwrite($fp, $theme_data); - fclose($fp); - - $cache->destroy('sql', STYLES_THEME_TABLE); - add_log('admin', 'LOG_THEME_EDIT_FILE', $theme_info['theme_name'], $theme_file); - - trigger_error($message . adm_back_link($this->u_action . "&action=edit&id=$theme_id&template_file=$theme_file&text_rows=$text_rows")); + trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING); } - // Generate a category array containing theme filenames - $filelist = filelist($theme_path, '', 'css'); + // Find all available parent styles + $list = $this->find_possible_parents($styles, $id); - if ($theme_file) - { - $theme_data = file_get_contents($file); + // Add form key + $form_key = 'acp_styles'; + add_form_key($form_key); - if (!$theme_data) + // Change data + if ($this->request->variable('update', false)) + { + if (!check_form_key($form_key)) { - trigger_error($user->lang['NO_THEME'] . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($this->user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING); } - } - // Now create the categories - $filelist_cats[''] = array(); - foreach ($filelist as $pathfile => $file_ary) - { - // Use the directory name as category name - if (!empty($pathfile)) + $update = array( + 'style_name' => trim($this->request->variable('style_name', $style['style_name'])), + 'style_parent_id' => $this->request->variable('style_parent', (int) $style['style_parent_id']), + 'style_active' => $this->request->variable('style_active', (int) $style['style_active']), + ); + $update_action = $this->u_action . '&action=details&id=' . $id; + + // Check style name + if ($update['style_name'] != $style['style_name']) { - $filelist_cats[$pathfile] = array(); - foreach ($file_ary as $file) + if (!strlen($update['style_name'])) { - $filelist_cats[$pathfile][$pathfile . $file] = $file; + trigger_error($this->user->lang['STYLE_ERR_STYLE_NAME'] . adm_back_link($update_action), E_USER_WARNING); + } + foreach ($styles as $row) + { + if ($row['style_name'] == $update['style_name']) + { + trigger_error($this->user->lang['STYLE_ERR_NAME_EXIST'] . adm_back_link($update_action), E_USER_WARNING); + } } } - // or if it's in the main category use the word before the first underscore to group files else { - $cats = array(); - foreach ($file_ary as $file) - { - $cats[] = substr($file, 0, strpos($file, '_')); - $filelist_cats[substr($file, 0, strpos($file, '_'))][$file] = $file; - } - - $cats = array_values(array_unique($cats)); + unset($update['style_name']); + } - // we don't need any single element categories so put them into the misc '' category - for ($i = 0, $n = sizeof($cats); $i < $n; $i++) + // Check parent style id + if ($update['style_parent_id'] != $style['style_parent_id']) + { + if ($update['style_parent_id'] != 0) { - if (sizeof($filelist_cats[$cats[$i]]) == 1 && $cats[$i] !== '') + $found = false; + foreach ($list as $row) + { + if ($row['style_id'] == $update['style_parent_id']) + { + $found = true; + $update['style_parent_tree'] = ($row['style_parent_tree'] != '' ? $row['style_parent_tree'] . '/' : '') . $row['style_path']; + break; + } + } + if (!$found) { - $filelist_cats[''][key($filelist_cats[$cats[$i]])] = current($filelist_cats[$cats[$i]]); - unset($filelist_cats[$cats[$i]]); + trigger_error($this->user->lang['STYLE_ERR_INVALID_PARENT'] . adm_back_link($update_action), E_USER_WARNING); } } - unset($cats); + else + { + $update['style_parent_tree'] = ''; + } } - } - unset($filelist); - - // Generate list of categorised theme files - $tpl_options = ''; - ksort($filelist_cats); - foreach ($filelist_cats as $category => $tpl_ary) - { - ksort($tpl_ary); - - if (!empty($category)) + else { - $tpl_options .= '<option class="sep" value="">' . $category . '</option>'; + unset($update['style_parent_id']); } - foreach ($tpl_ary as $filename => $file) + // Check style_active + if ($update['style_active'] != $style['style_active']) { - $selected = ($theme_file == $filename) ? ' selected="selected"' : ''; - $tpl_options .= '<option value="' . $filename . '"' . $selected . '>' . $file . '</option>'; + if (!$update['style_active'] && $this->default_style == $style['style_id']) + { + trigger_error($this->user->lang['DEACTIVATE_DEFAULT'] . adm_back_link($update_action), E_USER_WARNING); + } } - } - - $template->assign_vars(array( - 'S_EDIT_THEME' => true, - 'S_HIDDEN_FIELDS' => build_hidden_fields(array('template_file' => $theme_file)), - 'S_TEMPLATES' => $tpl_options, - - 'U_ACTION' => $this->u_action . "&action=edit&id=$theme_id&text_rows=$text_rows", - 'U_BACK' => $this->u_action, - - 'L_EDIT' => $user->lang['EDIT_THEME'], - 'L_EDIT_EXPLAIN' => $user->lang['EDIT_THEME_EXPLAIN'], - 'L_EDITOR' => $user->lang['THEME_EDITOR'], - 'L_EDITOR_HEIGHT' => $user->lang['THEME_EDITOR_HEIGHT'], - 'L_FILE' => $user->lang['THEME_FILE'], - 'L_SELECT' => $user->lang['SELECT_THEME'], - 'L_SELECTED' => $user->lang['SELECTED_THEME'], - 'L_SELECTED_FILE' => $user->lang['SELECTED_THEME_FILE'], - - 'SELECTED_TEMPLATE' => $theme_info['theme_name'], - 'TEMPLATE_FILE' => $theme_file, - 'TEMPLATE_DATA' => utf8_htmlspecialchars($theme_data), - 'TEXT_ROWS' => $text_rows, - )); - } - - /** - * Remove style/template/theme - */ - function remove($mode, $style_id) - { - global $db, $template, $user, $phpbb_root_path, $cache, $config; - - $new_id = request_var('new_id', 0); - $update = (isset($_POST['update'])) ? true : false; - $sql_where = ''; - - switch ($mode) - { - case 'style': - $sql_from = STYLES_TABLE; - $sql_select = 'style_id, style_name, template_id, theme_id'; - $sql_where = 'AND style_active = 1'; - break; - - case 'template': - $sql_from = STYLES_TEMPLATE_TABLE; - $sql_select = 'template_id, template_name, template_path'; - break; - - case 'theme': - $sql_from = STYLES_THEME_TABLE; - $sql_select = 'theme_id, theme_name, theme_path'; - break; - } - - if ($mode === 'template' && ($conflicts = $this->check_inheritance($mode, $style_id))) - { - $l_type = strtoupper($mode); - $msg = $user->lang[$l_type . '_DELETE_DEPENDENT']; - foreach ($conflicts as $id => $values) + else { - $msg .= '<br />' . $values['template_name']; + unset($update['style_active']); } - trigger_error($msg . adm_back_link($this->u_action), E_USER_WARNING); - } - - $l_prefix = strtoupper($mode); - - $sql = "SELECT $sql_select - FROM $sql_from - WHERE {$mode}_id = $style_id"; - $result = $db->sql_query($sql); - $style_row = $db->sql_fetchrow($result); - $db->sql_freeresult($result); - - if (!$style_row) - { - trigger_error($user->lang['NO_' . $l_prefix] . adm_back_link($this->u_action), E_USER_WARNING); - } - - $s_only_component = $this->display_component_options($mode, $style_row[$mode . '_id'], $style_row); - - if ($s_only_component) - { - trigger_error($user->lang['ONLY_' . $l_prefix] . adm_back_link($this->u_action), E_USER_WARNING); - } - - if ($update) - { - if ($mode == 'style') + // Update data + if (count($update)) { - $sql = "DELETE FROM $sql_from - WHERE {$mode}_id = $style_id"; - $db->sql_query($sql); - - $sql = 'UPDATE ' . USERS_TABLE . " - SET user_style = $new_id - WHERE user_style = $style_id"; - $db->sql_query($sql); + $sql = 'UPDATE ' . STYLES_TABLE . ' + SET ' . $this->db->sql_build_array('UPDATE', $update) . " + WHERE style_id = $id"; + $this->db->sql_query($sql); - $sql = 'UPDATE ' . FORUMS_TABLE . " - SET forum_style = $new_id - WHERE forum_style = $style_id"; - $db->sql_query($sql); + $style = array_merge($style, $update); - if ($style_id == $config['default_style']) + if (isset($update['style_parent_id'])) { - set_config('default_style', $new_id); + // Update styles tree + $styles = $this->get_styles(); + if ($this->update_styles_tree($styles, $style)) + { + // Something was changed in styles tree, purge all cache + $this->cache->purge(); + } } + add_log('admin', 'LOG_STYLE_EDIT_DETAILS', $style['style_name']); + } - // Remove the components - $components = array('template', 'theme'); - foreach ($components as $component) + // Update default style + $default = $this->request->variable('style_default', 0); + if ($default) + { + if (!$style['style_active']) { - $new_id = request_var('new_' . $component . '_id', 0); - $component_id = $style_row[$component . '_id']; - $this->remove_component($component, $component_id, $new_id, $style_id); + trigger_error($this->user->lang['STYLE_DEFAULT_CHANGE_INACTIVE'] . adm_back_link($update_action), E_USER_WARNING); } + set_config('default_style', $id); + $this->cache->purge(); } - else - { - $this->remove_component($mode, $style_id, $new_id); - } - - $cache->destroy('sql', STYLES_TABLE); - add_log('admin', 'LOG_' . $l_prefix . '_DELETE', $style_row[$mode . '_name']); - $message = ($mode != 'style') ? $l_prefix . '_DELETED_FS' : $l_prefix . '_DELETED'; - trigger_error($user->lang[$message] . adm_back_link($this->u_action)); + // Show styles list + $this->frontend(); + return; } - $this->page_title = 'DELETE_' . $l_prefix; - - $template->assign_vars(array( - 'S_DELETE' => true, - - 'L_TITLE' => $user->lang[$this->page_title], - 'L_EXPLAIN' => $user->lang[$this->page_title . '_EXPLAIN'], - 'L_NAME' => $user->lang[$l_prefix . '_NAME'], - 'L_REPLACE' => $user->lang['REPLACE_' . $l_prefix], - 'L_REPLACE_EXPLAIN' => $user->lang['REPLACE_' . $l_prefix . '_EXPLAIN'], - - 'U_ACTION' => $this->u_action . "&action=delete&id=$style_id", - 'U_BACK' => $this->u_action, + // Show parent styles + foreach ($list as $row) + { + $this->template->assign_block_vars('parent_styles', array( + 'STYLE_ID' => $row['style_id'], + 'STYLE_NAME' => htmlspecialchars($row['style_name']), + 'LEVEL' => $row['level'], + 'SPACER' => str_repeat(' ', $row['level']), + ) + ); + } - 'NAME' => $style_row[$mode . '_name'], + // Show style details + $this->template->assign_vars(array( + 'S_STYLE_DETAILS' => true, + 'STYLE_ID' => $style['style_id'], + 'STYLE_NAME' => htmlspecialchars($style['style_name']), + 'STYLE_PATH' => htmlspecialchars($style['style_path']), + 'STYLE_COPYRIGHT' => strip_tags($style['style_copyright']), + 'STYLE_PARENT' => $style['style_parent_id'], + 'S_STYLE_ACTIVE' => $style['style_active'], + 'S_STYLE_DEFAULT' => ($style['style_id'] == $this->default_style) ) ); - - if ($mode == 'style') - { - $template->assign_vars(array( - 'S_DELETE_STYLE' => true, - )); - } } /** - * Remove template/theme entry from the database + * List installed styles */ - function remove_component($component, $component_id, $new_id, $style_id = false) + protected function show_installed() { - global $db; + // Get all installed styles + $styles = $this->get_styles(); - if (($new_id == 0) || ($component === 'template' && ($conflicts = $this->check_inheritance($component, $component_id)))) + if (!count($styles)) { - // We can not delete the template, as the user wants to keep the component or an other template is inheriting from this one. - return; + trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING); } - $component_in_use = array(); - if ($component != 'style') - { - $component_in_use = $this->component_in_use($component, $component_id, $style_id); - } + usort($styles, array($this, 'sort_styles')); - if (($new_id == -1) && !empty($component_in_use)) - { - // We can not delete the component, as it is still in use - return; - } + // Get users + $users = $this->get_users(); - switch ($component) + // Add users counter to rows + foreach ($styles as &$style) { - case 'template': - $sql_from = STYLES_TEMPLATE_TABLE; - break; - - case 'theme': - $sql_from = STYLES_THEME_TABLE; - break; + $style['_users'] = isset($users[$style['style_id']]) ? $users[$style['style_id']] : 0; } - $sql = "DELETE FROM $sql_from - WHERE {$component}_id = $component_id"; - $db->sql_query($sql); + // Set up styles list variables + // Addons should increase this number and update template variable + $this->styles_list_cols = 4; + $this->template->assign_var('STYLES_LIST_COLS', $this->styles_list_cols); - $sql = 'UPDATE ' . STYLES_TABLE . " - SET {$component}_id = $new_id - WHERE {$component}_id = $component_id"; - $db->sql_query($sql); - } + // Show styles list + $this->show_styles_list($styles, 0, 0); - /** - * Display the options which can be used to replace a style/template/theme - * - * @return boolean Returns true if the component is the only component and can not be deleted. - */ - function display_component_options($component, $component_id, $style_row = false, $style_id = false) - { - global $db, $template, $user; - - $is_only_component = true; - $component_in_use = array(); - if ($component != 'style') - { - $component_in_use = $this->component_in_use($component, $component_id, $style_id); - } - - $sql_where = ''; - switch ($component) - { - case 'style': - $sql_from = STYLES_TABLE; - $sql_where = 'WHERE style_active = 1'; - break; - - case 'template': - $sql_from = STYLES_TEMPLATE_TABLE; - $sql_where = 'WHERE template_inherits_id <> ' . $component_id; - break; - - case 'theme': - $sql_from = STYLES_THEME_TABLE; - break; - } - - $s_options = ''; - if (($component != 'style') && empty($component_in_use)) + // Show styles with invalid inherits_id + foreach ($styles as $style) { - // If it is not in use, there must be another component - $is_only_component = false; - - $sql = "SELECT {$component}_id, {$component}_name - FROM $sql_from - WHERE {$component}_id = {$component_id}"; - $result = $db->sql_query($sql); - $row = $db->sql_fetchrow($result); - $db->sql_freeresult($result); - - $s_options .= '<option value="-1" selected="selected">' . $user->lang['DELETE_' . strtoupper($component)] . '</option>'; - $s_options .= '<option value="0">' . sprintf($user->lang['KEEP_' . strtoupper($component)], $row[$component . '_name']) . '</option>'; - } - else - { - $sql = "SELECT {$component}_id, {$component}_name - FROM $sql_from - $sql_where - ORDER BY {$component}_name ASC"; - $result = $db->sql_query($sql); - - $s_keep_option = $s_options = ''; - while ($row = $db->sql_fetchrow($result)) + if (empty($style['_shown'])) { - if ($row[$component . '_id'] != $component_id) - { - $is_only_component = false; - $s_options .= '<option value="' . $row[$component . '_id'] . '">' . sprintf($user->lang['REPLACE_WITH_OPTION'], $row[$component . '_name']) . '</option>'; - } - else if ($component != 'style') - { - $s_keep_option = '<option value="0" selected="selected">' . sprintf($user->lang['KEEP_' . strtoupper($component)], $row[$component . '_name']) . '</option>'; - } - } - $db->sql_freeresult($result); - $s_options = $s_keep_option . $s_options; - } - - if (!$style_row) - { - $template->assign_var('S_REPLACE_' . strtoupper($component) . '_OPTIONS', $s_options); - } - else - { - $template->assign_var('S_REPLACE_OPTIONS', $s_options); - if ($component == 'style') - { - $components = array('template', 'theme'); - foreach ($components as $component) - { - $this->display_component_options($component, $style_row[$component . '_id'], false, $component_id, true); - } + $style['_note'] = sprintf($this->user->lang['REQUIRES_STYLE'], htmlspecialchars($style['style_parent_tree'])); + $this->list_style($style, 0); } } - return $is_only_component; - } - - /** - * Check whether the component is still used by another style or component - */ - function component_in_use($component, $component_id, $style_id = false) - { - global $db; - - $component_in_use = array(); + // Add buttons + $this->template->assign_block_vars('extra_actions', array( + 'ACTION_NAME' => 'activate', + 'L_ACTION' => $this->user->lang['STYLE_ACTIVATE'], + ) + ); - if ($style_id) - { - $sql = 'SELECT style_id, style_name - FROM ' . STYLES_TABLE . " - WHERE {$component}_id = {$component_id} - AND style_id <> {$style_id} - ORDER BY style_name ASC"; - } - else - { - $sql = 'SELECT style_id, style_name - FROM ' . STYLES_TABLE . " - WHERE {$component}_id = {$component_id} - ORDER BY style_name ASC"; - } - $result = $db->sql_query($sql); - while ($row = $db->sql_fetchrow($result)) - { - $component_in_use[] = $row['style_name']; - } - $db->sql_freeresult($result); + $this->template->assign_block_vars('extra_actions', array( + 'ACTION_NAME' => 'deactivate', + 'L_ACTION' => $this->user->lang['STYLE_DEACTIVATE'], + ) + ); - if ($component === 'template' && ($conflicts = $this->check_inheritance($component, $component_id))) + if (isset($this->style_counters) && $this->style_counters['total'] > 1) { - foreach ($conflicts as $temp_id => $conflict_data) - { - $component_in_use[] = $conflict_data['template_name']; - } + $this->template->assign_block_vars('extra_actions', array( + 'ACTION_NAME' => 'uninstall', + 'L_ACTION' => $this->user->lang['STYLE_UNINSTALL'], + ) + ); } - - return $component_in_use; } /** - * Export style or style elements + * Show list of styles that can be installed */ - function export($mode, $style_id) + protected function show_available() { - global $db, $template, $user, $phpbb_root_path, $cache, $phpEx, $config; - - $update = (isset($_POST['update'])) ? true : false; - - $inc_template = request_var('inc_template', 0); - $inc_theme = request_var('inc_theme', 0); - $store = request_var('store', 0); - $format = request_var('format', ''); - - $error = array(); - $methods = array('tar'); - - $available_methods = array('tar.gz' => 'zlib', 'tar.bz2' => 'bz2', 'zip' => 'zlib'); - foreach ($available_methods as $type => $module) - { - if (!@extension_loaded($module)) - { - continue; - } + // Get list of styles + $styles = $this->find_available(true); - $methods[] = $type; - } - - if (!in_array($format, $methods)) + // Show styles + if (empty($styles)) { - $format = 'tar'; + trigger_error($this->user->lang['NO_UNINSTALLED_STYLE'] . adm_back_link($this->u_base_action), E_USER_NOTICE); } - switch ($mode) - { - case 'style': - if ($update && ($inc_template + $inc_theme) < 1) - { - $error[] = $user->lang['STYLE_ERR_MORE_ELEMENTS']; - } - - $name = 'style_name'; - - $sql_select = 's.style_id, s.style_name, s.style_copyright'; - $sql_select .= ($inc_template) ? ', t.*' : ', t.template_name'; - $sql_select .= ($inc_theme) ? ', c.*' : ', c.theme_name'; - $sql_from = STYLES_TABLE . ' s, ' . STYLES_TEMPLATE_TABLE . ' t, ' . STYLES_THEME_TABLE . ' c'; - $sql_where = "s.style_id = $style_id AND t.template_id = s.template_id AND c.theme_id = s.theme_id"; - - $l_prefix = 'STYLE'; - break; - - case 'template': - $name = 'template_name'; - - $sql_select = '*'; - $sql_from = STYLES_TEMPLATE_TABLE; - $sql_where = "template_id = $style_id"; - - $l_prefix = 'TEMPLATE'; - break; - - case 'theme': - $name = 'theme_name'; - - $sql_select = '*'; - $sql_from = STYLES_THEME_TABLE; - $sql_where = "theme_id = $style_id"; + usort($styles, array($this, 'sort_styles')); - $l_prefix = 'THEME'; - break; - } + $this->styles_list_cols = 3; + $this->template->assign_vars(array( + 'STYLES_LIST_COLS' => $this->styles_list_cols, + 'STYLES_LIST_HIDE_COUNT' => true + ) + ); - if ($update && !sizeof($error)) + // Show styles + foreach ($styles as &$style) { - $sql = "SELECT $sql_select - FROM $sql_from - WHERE $sql_where"; - $result = $db->sql_query($sql); - $style_row = $db->sql_fetchrow($result); - $db->sql_freeresult($result); - - if (!$style_row) - { - trigger_error($user->lang['NO_' . $l_prefix] . adm_back_link($this->u_action), E_USER_WARNING); - } - - $var_ary = array('style_id', 'style_name', 'style_copyright', 'template_id', 'template_name', 'template_path', 'template_copyright', 'template_inherits_id', 'bbcode_bitfield', 'theme_id', 'theme_name', 'theme_path', 'theme_copyright'); - - foreach ($var_ary as $var) - { - if (!isset($style_row[$var])) - { - $style_row[$var] = ''; - } - } - - $files = $data = array(); - - if ($mode == 'style') - { - $style_cfg = str_replace(array('{MODE}', '{NAME}', '{COPYRIGHT}', '{VERSION}'), array($mode, $style_row['style_name'], $style_row['style_copyright'], $config['version']), $this->style_cfg); - - $style_cfg .= (!$inc_template) ? "\nrequired_template = {$style_row['template_name']}" : ''; - $style_cfg .= (!$inc_theme) ? "\nrequired_theme = {$style_row['theme_name']}" : ''; - - $data[] = array( - 'src' => $style_cfg, - 'prefix' => 'style.cfg' - ); - - unset($style_cfg); - } - - // Export template core code - if ($mode == 'template' || $inc_template) + // Check if style has a parent style in styles list + $has_parent = false; + if ($style['_inherit_name'] != '') { - $use_template_name = $style_row['template_name']; - - // Add the inherit from variable, depending on it's use... - if ($style_row['template_inherits_id']) + foreach ($styles as $parent_style) { - // Get the template name - $sql = 'SELECT template_name - FROM ' . STYLES_TEMPLATE_TABLE . ' - WHERE template_id = ' . (int) $style_row['template_inherits_id']; - $result = $db->sql_query($sql); - $use_template_name = (string) $db->sql_fetchfield('template_name'); - $db->sql_freeresult($result); - } - - $template_cfg = str_replace(array('{MODE}', '{NAME}', '{COPYRIGHT}', '{VERSION}', '{INHERIT_FROM}'), array($mode, $style_row['template_name'], $style_row['template_copyright'], $config['version'], $use_template_name), $this->template_cfg); - - $template_cfg .= "\n\nbbcode_bitfield = {$style_row['bbcode_bitfield']}"; - - $data[] = array( - 'src' => $template_cfg, - 'prefix' => 'template/template.cfg' - ); - - // This is potentially nasty memory-wise ... - $files[] = array( - 'src' => "styles/{$style_row['template_path']}/template/", - 'prefix-' => "styles/{$style_row['template_path']}/", - 'prefix+' => false, - 'exclude' => 'template.cfg' - ); - unset($template_cfg); - } - - // Export theme core code - if ($mode == 'theme' || $inc_theme) - { - $theme_cfg = str_replace(array('{MODE}', '{NAME}', '{COPYRIGHT}', '{VERSION}'), array($mode, $style_row['theme_name'], $style_row['theme_copyright'], $config['version']), $this->theme_cfg); - - // Read old cfg file - $items = $cache->obtain_cfg_items($style_row); - $items = $items['theme']; - - $files[] = array( - 'src' => "styles/{$style_row['theme_path']}/theme/", - 'prefix-' => "styles/{$style_row['theme_path']}/", - 'prefix+' => false, - 'exclude' => 'theme.cfg', - ); - - $data[] = array( - 'src' => $theme_cfg, - 'prefix' => 'theme/theme.cfg', - ); - - unset($items, $theme_cfg); - } - - switch ($format) - { - case 'tar': - $ext = '.tar'; - break; - - case 'zip': - $ext = '.zip'; - break; - - case 'tar.gz': - $ext = '.tar.gz'; - break; - - case 'tar.bz2': - $ext = '.tar.bz2'; - break; - - default: - $error[] = $user->lang[$l_prefix . '_ERR_ARCHIVE']; - } - - if (!sizeof($error)) - { - include($phpbb_root_path . 'includes/functions_compress.' . $phpEx); - - if ($mode == 'style') - { - $path = preg_replace('#[^\w-]+#', '_', $style_row['style_name']); - } - else - { - $path = $style_row[$mode . '_path']; - } - - if ($format == 'zip') - { - $compress = new compress_zip('w', $phpbb_root_path . "store/$path$ext"); - } - else - { - $compress = new compress_tar('w', $phpbb_root_path . "store/$path$ext", $ext); - } - - if (sizeof($files)) - { - foreach ($files as $file_ary) + if ($parent_style['style_name'] == $style['_inherit_name'] && empty($parent_style['_shown'])) { - $compress->add_file($file_ary['src'], $file_ary['prefix-'], $file_ary['prefix+'], $file_ary['exclude']); + // Show parent style first + $has_parent = true; } } - - if (sizeof($data)) - { - foreach ($data as $data_ary) - { - $compress->add_data($data_ary['src'], $data_ary['prefix']); - } - } - - $compress->close(); - - add_log('admin', 'LOG_' . $l_prefix . '_EXPORT', $style_row[$mode . '_name']); - - if (!$store) - { - $compress->download($path); - @unlink("{$phpbb_root_path}store/$path$ext"); - exit; - } - - trigger_error(sprintf($user->lang[$l_prefix . '_EXPORTED'], "store/$path$ext") . adm_back_link($this->u_action)); + } + if (!$has_parent) + { + $this->list_style($style, 0); + $this->show_available_child_styles($styles, $style['style_name'], 1); } } - $sql = "SELECT {$mode}_id, {$mode}_name - FROM " . (($mode == 'style') ? STYLES_TABLE : $sql_from) . " - WHERE {$mode}_id = $style_id"; - $result = $db->sql_query($sql); - $style_row = $db->sql_fetchrow($result); - $db->sql_freeresult($result); - - if (!$style_row) + // Show styles that do not have parent style in styles list + foreach ($styles as $style) { - trigger_error($user->lang['NO_' . $l_prefix] . adm_back_link($this->u_action), E_USER_WARNING); + if (empty($style['_shown'])) + { + $this->list_style($style, 0); + } } - $this->page_title = $l_prefix . '_EXPORT'; - - $format_buttons = ''; - foreach ($methods as $method) + // Add button + if (isset($this->style_counters) && $this->style_counters['caninstall'] > 0) { - $format_buttons .= '<label><input type="radio"' . ((!$format_buttons) ? ' id="format"' : '') . ' class="radio" value="' . $method . '" name="format"' . (($method == $format) ? ' checked="checked"' : '') . ' /> ' . $method . '</label>'; + $this->template->assign_block_vars('extra_actions', array( + 'ACTION_NAME' => 'install', + 'L_ACTION' => $this->user->lang['INSTALL_STYLES'], + ) + ); } - - $template->assign_vars(array( - 'S_EXPORT' => true, - 'S_ERROR_MSG' => (sizeof($error)) ? true : false, - 'S_STYLE' => ($mode == 'style') ? true : false, - - 'L_TITLE' => $user->lang[$this->page_title], - 'L_EXPLAIN' => $user->lang[$this->page_title . '_EXPLAIN'], - 'L_NAME' => $user->lang[$l_prefix . '_NAME'], - - 'U_ACTION' => $this->u_action . '&action=export&id=' . $style_id, - 'U_BACK' => $this->u_action, - - 'ERROR_MSG' => (sizeof($error)) ? implode('<br />', $error) : '', - 'NAME' => $style_row[$mode . '_name'], - 'FORMAT_BUTTONS' => $format_buttons) - ); } /** - * Display details + * Find styles available for installation + * + * @param bool $all if true, function will return all installable styles. if false, function will return only styles that can be installed + * @return array List of styles */ - function details($mode, $style_id) + protected function find_available($all) { - global $template, $db, $config, $user, $safe_mode, $cache, $phpbb_root_path; - - $update = (isset($_POST['update'])) ? true : false; - $l_type = strtoupper($mode); - - $error = array(); - $element_ary = array('template' => STYLES_TEMPLATE_TABLE, 'theme' => STYLES_THEME_TABLE); - - switch ($mode) - { - case 'style': - $sql_from = STYLES_TABLE; - break; - - case 'template': - $sql_from = STYLES_TEMPLATE_TABLE; - break; - - case 'theme': - $sql_from = STYLES_THEME_TABLE; - break; - } - - $sql = "SELECT * - FROM $sql_from - WHERE {$mode}_id = $style_id"; - $result = $db->sql_query($sql); - $style_row = $db->sql_fetchrow($result); - $db->sql_freeresult($result); - - if (!$style_row) - { - trigger_error($user->lang['NO_' . $l_type] . adm_back_link($this->u_action), E_USER_WARNING); + // Get list of installed styles + $installed = $this->get_styles(); + + $installed_dirs = array(); + $installed_names = array(); + foreach ($installed as $style) + { + $installed_dirs[] = $style['style_path']; + $installed_names[$style['style_name']] = array( + 'path' => $style['style_path'], + 'id' => $style['style_id'], + 'parent' => $style['style_parent_id'], + 'tree' => (strlen($style['style_parent_tree']) ? $style['style_parent_tree'] . '/' : '') . $style['style_path'], + ); } - $style_row['style_default'] = ($mode == 'style' && $config['default_style'] == $style_id) ? 1 : 0; + // Get list of directories + $dirs = $this->find_style_dirs(); - if ($update) + // Find styles that can be installed + $styles = array(); + foreach ($dirs as $dir) { - $name = utf8_normalize_nfc(request_var('name', '', true)); - $copyright = utf8_normalize_nfc(request_var('copyright', '', true)); - - $template_id = request_var('template_id', 0); - $theme_id = request_var('theme_id', 0); - - $style_active = request_var('style_active', 0); - $style_default = request_var('style_default', 0); - - // If the admin selected the style to be the default style, but forgot to activate it... we will do it for him - if ($style_default) - { - $style_active = 1; - } - - $sql = "SELECT {$mode}_id, {$mode}_name - FROM $sql_from - WHERE {$mode}_id <> $style_id - AND LOWER({$mode}_name) = '" . $db->sql_escape(strtolower($name)) . "'"; - $result = $db->sql_query($sql); - $conflict = $db->sql_fetchrow($result); - $db->sql_freeresult($result); - - if ($mode == 'style' && (!$template_id || !$theme_id)) + if (in_array($dir, $installed_dirs)) { - $error[] = $user->lang['STYLE_ERR_NO_IDS']; - } - - if ($mode == 'style' && $style_row['style_active'] && !$style_active && $config['default_style'] == $style_id) - { - $error[] = $user->lang['DEACTIVATE_DEFAULT']; - } - - if (!$name || $conflict) - { - $error[] = $user->lang[$l_type . '_ERR_STYLE_NAME']; + // Style is already installed + continue; } - - if (!sizeof($error)) + $cfg = $this->read_style_cfg($dir); + if ($cfg === false) { - // Check length settings - if (utf8_strlen($name) > 30) - { - $error[] = $user->lang[$l_type . '_ERR_NAME_LONG']; - } - - if (utf8_strlen($copyright) > 60) - { - $error[] = $user->lang[$l_type . '_ERR_COPY_LONG']; - } + // Invalid style.cfg + continue; } - } - - if ($update && sizeof($error)) - { - $style_row = array_merge($style_row, array( - 'template_id' => $template_id, - 'theme_id' => $theme_id, - 'style_active' => $style_active, - $mode . '_name' => $name, - $mode . '_copyright' => $copyright) - ); - } - // User has submitted form and no errors have occurred - if ($update && !sizeof($error)) - { - $sql_ary = array( - $mode . '_name' => $name, - $mode . '_copyright' => $copyright + // Style should be available for installation + $parent = $cfg['parent']; + $style = array( + 'style_id' => 0, + 'style_name' => $cfg['name'], + 'style_copyright' => $cfg['copyright'], + 'style_active' => 0, + 'style_path' => $dir, + 'bbcode_bitfield' => $cfg['template_bitfield'], + 'style_parent_id' => 0, + 'style_parent_tree' => '', + // Extra values for styles list + // All extra variable start with _ so they won't be confused with data that can be added to styles table + '_inherit_name' => $parent, + '_available' => true, + '_note' => '', ); - switch ($mode) - { - case 'style': - - $sql_ary += array( - 'template_id' => (int) $template_id, - 'theme_id' => (int) $theme_id, - 'style_active' => (int) $style_active, - ); - break; - - case 'theme': - break; - - case 'template': - break; - } - - if (sizeof($sql_ary)) + // Check style inheritance + if ($parent != '') { - $sql = "UPDATE $sql_from - SET " . $db->sql_build_array('UPDATE', $sql_ary) . " - WHERE {$mode}_id = $style_id"; - $db->sql_query($sql); - - // Making this the default style? - if ($mode == 'style' && $style_default) + if (isset($installed_names[$parent])) { - set_config('default_style', $style_id); + // Parent style is installed + $row = $installed_names[$parent]; + $style['style_parent_id'] = $row['id']; + $style['style_parent_tree'] = $row['tree']; } - } - - $cache->destroy('sql', STYLES_TABLE); - - add_log('admin', 'LOG_' . $l_type . '_EDIT_DETAILS', $name); - if (sizeof($error)) - { - trigger_error(implode('<br />', $error) . adm_back_link($this->u_action), E_USER_WARNING); - } - else - { - trigger_error($user->lang[$l_type . '_DETAILS_UPDATED'] . adm_back_link($this->u_action)); - } - } - - if ($mode == 'style') - { - foreach ($element_ary as $element => $table) - { - $sql = "SELECT {$element}_id, {$element}_name - FROM $table - ORDER BY {$element}_id ASC"; - $result = $db->sql_query($sql); - - ${$element . '_options'} = ''; - while ($row = $db->sql_fetchrow($result)) + else { - $selected = ($row[$element . '_id'] == $style_row[$element . '_id']) ? ' selected="selected"' : ''; - ${$element . '_options'} .= '<option value="' . $row[$element . '_id'] . '"' . $selected . '>' . $row[$element . '_name'] . '</option>'; + // Parent style is not installed yet + $style['_available'] = false; + $style['_note'] = sprintf($this->user->lang['REQUIRES_STYLE'], htmlspecialchars($parent)); } - $db->sql_freeresult($result); } - } - if ($mode == 'template') - { - $super = array(); - if (isset($style_row[$mode . '_inherits_id']) && $style_row['template_inherits_id']) + if ($all || $style['_available']) { - $super = $this->get_super($mode, $style_row['template_id']); + $styles[] = $style; } } - $this->page_title = 'EDIT_DETAILS_' . $l_type; - - $template->assign_vars(array( - 'S_DETAILS' => true, - 'S_ERROR_MSG' => (sizeof($error)) ? true : false, - 'S_STYLE' => ($mode == 'style') ? true : false, - 'S_TEMPLATE' => ($mode == 'template') ? true : false, - 'S_THEME' => ($mode == 'theme') ? true : false, - 'S_STYLE_ACTIVE' => (isset($style_row['style_active'])) ? $style_row['style_active'] : 0, - 'S_STYLE_DEFAULT' => (isset($style_row['style_default'])) ? $style_row['style_default'] : 0, - 'S_SUPERTEMPLATE' => (isset($style_row[$mode . '_inherits_id']) && $style_row[$mode . '_inherits_id']) ? $super['template_name'] : 0, - - 'S_TEMPLATE_OPTIONS' => ($mode == 'style') ? $template_options : '', - 'S_THEME_OPTIONS' => ($mode == 'style') ? $theme_options : '', - - 'U_ACTION' => $this->u_action . '&action=details&id=' . $style_id, - 'U_BACK' => $this->u_action, - - 'L_TITLE' => $user->lang[$this->page_title], - 'L_EXPLAIN' => $user->lang[$this->page_title . '_EXPLAIN'], - 'L_NAME' => $user->lang[$l_type . '_NAME'], - - 'ERROR_MSG' => (sizeof($error)) ? implode('<br />', $error) : '', - 'NAME' => $style_row[$mode . '_name'], - 'COPYRIGHT' => $style_row[$mode . '_copyright'], - ) - ); + return $styles; } /** - * Load css file contents + * Show styles list + * + * @param array $styles styles list + * @param int $parent parent style id + * @param int $level style inheritance level */ - function load_css_file($path, $filename) + protected function show_styles_list(&$styles, $parent, $level) { - global $phpbb_root_path; - - $file = "{$phpbb_root_path}styles/$path/theme/$filename"; - - if (file_exists($file) && ($content = file_get_contents($file))) - { - $content = trim($content); - } - else - { - $content = ''; - } - if (defined('DEBUG')) + foreach ($styles as &$style) { - $content = "/* BEGIN @include $filename */ \n $content \n /* END @include $filename */ \n"; + if (empty($style['_shown']) && $style['style_parent_id'] == $parent) + { + $this->list_style($style, $level); + $this->show_styles_list($styles, $style['style_id'], $level + 1); + } } - - return $content; } /** - * Returns a string containing the value that should be used for the theme_data column in the theme database table. - * Includes contents of files loaded via @import - * - * @param array $theme_row is an associative array containing the theme's current database entry - * @param mixed $stylesheet can either be the new content for the stylesheet or false to load from the standard file - * @param string $root_path should only be used in case you want to use a different root path than "{$phpbb_root_path}styles/{$theme_row['theme_path']}" + * Show available styles tree * - * @return string Stylesheet data for theme_data column in the theme table + * @param array $styles Styles list, passed as reference + * @param string $name Name of parent style + * @param string $level Styles tree level */ - function db_theme_data($theme_row, $stylesheet = false, $root_path = '') + protected function show_available_child_styles(&$styles, $name, $level) { - global $phpbb_root_path; - - if (!$root_path) - { - $root_path = $phpbb_root_path . 'styles/' . $theme_row['theme_path']; - } - - if (!$stylesheet) - { - $stylesheet = ''; - if (file_exists($root_path . '/theme/stylesheet.css')) - { - $stylesheet = file_get_contents($root_path . '/theme/stylesheet.css'); - } - } - - // Match CSS imports - $matches = array(); - preg_match_all('/@import url\((["\'])(.*)\1\);/i', $stylesheet, $matches); - - // remove commented stylesheets (very simple parser, allows only whitespace - // around an @import statement) - preg_match_all('#/\*\s*@import url\((["\'])(.*)\1\);\s\*/#i', $stylesheet, $commented); - $matches[2] = array_diff($matches[2], $commented[2]); - - if (sizeof($matches)) + foreach ($styles as &$style) { - foreach ($matches[0] as $idx => $match) + if (empty($style['_shown']) && $style['_inherit_name'] == $name) { - if (isset($matches[2][$idx])) - { - $stylesheet = str_replace($match, acp_styles::load_css_file($theme_row['theme_path'], $matches[2][$idx]), $stylesheet); - } + $this->list_style($style, $level); + $this->show_available_child_styles($styles, $style['style_name'], $level + 1); } } - - // adjust paths - return str_replace('./', 'styles/' . $theme_row['theme_path'] . '/theme/', $stylesheet); } /** - * Returns an array containing all template filenames for one template that are currently cached. + * Update styles tree * - * @param string $template_path contains the name of the template's folder in /styles/ - * - * @return array of filenames that exist in /styles/$template_path/template/ (without extension!) + * @param array $styles Styles list, passed as reference + * @param array $style Current style, false if root + * @return bool True if something was updated, false if not */ - function template_cache_filelist($template_path) + protected function update_styles_tree(&$styles, $style = false) { - global $phpbb_root_path, $phpEx, $user; - - $cache_prefix = 'tpl_' . str_replace('_', '-', $template_path); - - if (!($dp = @opendir("{$phpbb_root_path}cache"))) + $parent_id = ($style === false) ? 0 : $style['style_id']; + $parent_tree = ($style === false) ? '' : ($style['style_parent_tree'] == '' ? '' : $style['style_parent_tree']) . $style['style_path']; + $update = false; + $updated = false; + foreach ($styles as &$row) { - trigger_error($user->lang['TEMPLATE_ERR_CACHE_READ'] . adm_back_link($this->u_action), E_USER_WARNING); - } - - $file_ary = array(); - while ($file = readdir($dp)) - { - if ($file[0] == '.') - { - continue; - } - - if (is_file($phpbb_root_path . 'cache/' . $file) && (strpos($file, $cache_prefix) === 0)) + if ($row['style_parent_id'] == $parent_id) { - $file_ary[] = str_replace('.', '/', preg_replace('#^' . preg_quote($cache_prefix, '#') . '_(.*?)\.html\.' . $phpEx . '$#i', '\1', $file)); + if ($row['style_parent_tree'] != $parent_tree) + { + $row['style_parent_tree'] = $parent_tree; + $update = true; + } + $updated |= $this->update_styles_tree($styles, $row); } } - closedir($dp); - - return $file_ary; + if ($update) + { + $sql = 'UPDATE ' . STYLES_TABLE . " + SET style_parent_tree = '" . $this->db->sql_escape($parent_tree) . "' + WHERE style_parent_id = {$parent_id}"; + $this->db->sql_query($sql); + $updated = true; + } + return $updated; } /** - * Destroys cached versions of template files + * Find all possible parent styles for style * - * @param array $template_row contains the template's row in the STYLES_TEMPLATE_TABLE database table - * @param mixed $file_ary is optional and may contain an array of template file names which should be refreshed in the cache. - * The file names should be the original template file names and not the cache file names. + * @param array $styles list of styles + * @param int $id id of style + * @param int $parent current parent style id + * @param int $level current tree level + * @return array Style ids, names and levels */ - function clear_template_cache($template_row, $file_ary = false) + protected function find_possible_parents($styles, $id = -1, $parent = 0, $level = 0) { - global $phpbb_root_path, $phpEx, $user; - - $cache_prefix = 'tpl_' . str_replace('_', '-', $template_row['template_path']); - - if (!$file_ary || !is_array($file_ary)) - { - $file_ary = $this->template_cache_filelist($template_row['template_path']); - $log_file_list = $user->lang['ALL_FILES']; - } - else - { - $log_file_list = implode(', ', $file_ary); - } - - foreach ($file_ary as $file) - { - $file = str_replace('/', '.', $file); - - $file = "{$phpbb_root_path}cache/{$cache_prefix}_$file.html.$phpEx"; - if (file_exists($file) && is_file($file)) - { - @unlink($file); + $results = array(); + foreach ($styles as $style) + { + if ($style['style_id'] != $id && $style['style_parent_id'] == $parent) + { + $results[] = array( + 'style_id' => $style['style_id'], + 'style_name' => $style['style_name'], + 'style_path' => $style['style_path'], + 'style_parent_id' => $style['style_parent_id'], + 'style_parent_tree' => $style['style_parent_tree'], + 'level' => $level + ); + $results = array_merge($results, $this->find_possible_parents($styles, $id, $style['style_id'], $level + 1)); } } - unset($file_ary); - - add_log('admin', 'LOG_TEMPLATE_CACHE_CLEARED', $template_row['template_name'], $log_file_list); + return $results; } /** - * Install Style/Template/Theme + * Show item in styles list + * + * @param array $style style row + * @param array $level style inheritance level */ - function install($mode) + protected function list_style(&$style, $level) { - global $phpbb_root_path, $phpEx, $config, $db, $cache, $user, $template; - - $l_type = strtoupper($mode); - - $error = $installcfg = $style_row = array(); - $root_path = $cfg_file = ''; - $element_ary = array('template' => STYLES_TEMPLATE_TABLE, 'theme' => STYLES_THEME_TABLE); - - $install_path = request_var('path', ''); - $update = (isset($_POST['update'])) ? true : false; + // Mark row as shown + if (!empty($style['_shown'])) return; + $style['_shown'] = true; + + // Generate template variables + $actions = array(); + $row = array( + // Style data + 'STYLE_ID' => $style['style_id'], + 'STYLE_NAME' => htmlspecialchars($style['style_name']), + 'STYLE_PATH' => htmlspecialchars($style['style_path']), + 'STYLE_COPYRIGHT' => strip_tags($style['style_copyright']), + 'STYLE_ACTIVE' => $style['style_active'], + + // Additional data + 'DEFAULT' => ($style['style_id'] && $style['style_id'] == $this->default_style), + 'USERS' => (isset($style['_users'])) ? $style['_users'] : '', + 'LEVEL' => $level, + 'PADDING' => (4 + 16 * $level), + 'SHOW_COPYRIGHT' => ($style['style_id']) ? false : true, + 'STYLE_PATH_FULL' => htmlspecialchars($this->styles_path_absolute . '/' . $style['style_path']) . '/', + + // Comment to show below style + 'COMMENT' => (isset($style['_note'])) ? $style['_note'] : '', + + // The following variables should be used by hooks to add custom HTML code + 'EXTRA' => '', + 'EXTRA_OPTIONS' => '' + ); - // Installing, obtain cfg file contents - if ($install_path) + // Status specific data + if ($style['style_id']) { - $root_path = $phpbb_root_path . 'styles/' . $install_path . '/'; - $cfg_file = ($mode == 'style') ? "$root_path$mode.cfg" : "$root_path$mode/$mode.cfg"; - - if (!file_exists($cfg_file)) - { - $error[] = $user->lang[$l_type . '_ERR_NOT_' . $l_type]; - } - else - { - $installcfg = parse_cfg_file($cfg_file); - } - } + // Style is installed - // Installing - if (sizeof($installcfg)) - { - $name = $installcfg['name']; - $copyright = $installcfg['copyright']; - $version = $installcfg['version']; - - $style_row = array( - $mode . '_id' => 0, - $mode . '_name' => '', - $mode . '_copyright' => '' + // Details + $actions[] = array( + 'U_ACTION' => $this->u_action . '&action=details&id=' . $style['style_id'], + 'L_ACTION' => $this->user->lang['DETAILS'] ); - switch ($mode) - { - case 'style': - - $style_row = array( - 'style_id' => 0, - 'style_name' => $installcfg['name'], - 'style_copyright' => $installcfg['copyright'] - ); - - $reqd_template = (isset($installcfg['required_template'])) ? $installcfg['required_template'] : false; - $reqd_theme = (isset($installcfg['required_theme'])) ? $installcfg['required_theme'] : false; - - // Check to see if each element is already installed, if it is grab the id - foreach ($element_ary as $element => $table) - { - $style_row = array_merge($style_row, array( - $element . '_id' => 0, - $element . '_name' => '', - $element . '_copyright' => '') - ); - - $this->test_installed($element, $error, (${'reqd_' . $element}) ? $phpbb_root_path . 'styles/' . $reqd_template . '/' : $root_path, ${'reqd_' . $element}, $style_row[$element . '_id'], $style_row[$element . '_name'], $style_row[$element . '_copyright']); - - if (!$style_row[$element . '_name']) - { - $style_row[$element . '_name'] = $reqd_template; - } - - // Merge other information to installcfg... if present - $cfg_file = $phpbb_root_path . 'styles/' . $install_path . '/' . $element . '/' . $element . '.cfg'; - - if (file_exists($cfg_file)) - { - $cfg_contents = parse_cfg_file($cfg_file); - - // Merge only specific things. We may need them later. - foreach (array('inherit_from') as $key) - { - if (!empty($cfg_contents[$key]) && !isset($installcfg[$key])) - { - $installcfg[$key] = $cfg_contents[$key]; - } - } - } - } + // Activate + $actions[] = array( + 'U_ACTION' => $this->u_action . '&action=' . ($style['style_active'] ? 'de' : '') . 'activate&id=' . $style['style_id'], + 'L_ACTION' => $this->user->lang['STYLE_' . ($style['style_active'] ? 'DE' : '') . 'ACTIVATE'] + ); - break; +/* // Export + $actions[] = array( + 'U_ACTION' => $this->u_action . '&action=export&id=' . $style['style_id'], + 'L_ACTION' => $this->user->lang['EXPORT'] + ); */ - case 'template': - $this->test_installed('template', $error, $root_path, false, $style_row['template_id'], $style_row['template_name'], $style_row['template_copyright']); - break; + // Uninstall + $actions[] = array( + 'U_ACTION' => $this->u_action . '&action=uninstall&id=' . $style['style_id'], + 'L_ACTION' => $this->user->lang['STYLE_UNINSTALL'] + ); - case 'theme': - $this->test_installed('theme', $error, $root_path, false, $style_row['theme_id'], $style_row['theme_name'], $style_row['theme_copyright']); - break; - } + // Preview + $actions[] = array( + 'U_ACTION' => append_sid($this->phpbb_root_path . 'index.' . $this->phpEx, 'style=' . $style['style_id']), + 'L_ACTION' => $this->user->lang['PREVIEW'] + ); } else { - trigger_error($user->lang['NO_' . $l_type] . adm_back_link($this->u_action), E_USER_WARNING); - } - - $style_row['style_active'] = request_var('style_active', 1); - $style_row['style_default'] = request_var('style_default', 0); - - // User has submitted form and no errors have occurred - if ($update && !sizeof($error)) - { - if ($mode == 'style') + // Style is not installed + if (empty($style['_available'])) { - foreach ($element_ary as $element => $table) - { - ${$element . '_root_path'} = (${'reqd_' . $element}) ? $phpbb_root_path . 'styles/' . ${'reqd_' . $element} . '/' : false; - ${$element . '_path'} = (${'reqd_' . $element}) ? ${'reqd_' . $element} : false; - } - $this->install_style($error, 'install', $root_path, $style_row['style_id'], $style_row['style_name'], $install_path, $style_row['style_copyright'], $style_row['style_active'], $style_row['style_default'], $style_row, $template_root_path, $template_path, $theme_root_path, $theme_path); + $actions[] = array( + 'HTML' => $this->user->lang['CANNOT_BE_INSTALLED'] + ); } else { - $this->install_element($mode, $error, 'install', $root_path, $style_row[$mode . '_id'], $style_row[$mode . '_name'], $install_path, $style_row[$mode . '_copyright']); + $actions[] = array( + 'U_ACTION' => $this->u_action . '&action=install&dir=' . urlencode($style['style_path']), + 'L_ACTION' => $this->user->lang['INSTALL_STYLE'] + ); } + } - if (!sizeof($error)) - { - $cache->destroy('sql', STYLES_TABLE); + // todo: add hook - trigger_error($user->lang[$l_type . '_ADDED'] . adm_back_link($this->u_action)); - } + // Assign template variables + $this->template->assign_block_vars('styles_list', $row); + foreach($actions as $action) + { + $this->template->assign_block_vars('styles_list.actions', $action); } - $this->page_title = 'INSTALL_' . $l_type; - - $template->assign_vars(array( - 'S_DETAILS' => true, - 'S_INSTALL' => true, - 'S_ERROR_MSG' => (sizeof($error)) ? true : false, - 'S_LOCATION' => (isset($installcfg['inherit_from']) && $installcfg['inherit_from']) ? false : true, - 'S_STYLE' => ($mode == 'style') ? true : false, - 'S_TEMPLATE' => ($mode == 'template') ? true : false, - 'S_SUPERTEMPLATE' => (isset($installcfg['inherit_from'])) ? $installcfg['inherit_from'] : '', - 'S_THEME' => ($mode == 'theme') ? true : false, - - 'S_STYLE_ACTIVE' => (isset($style_row['style_active'])) ? $style_row['style_active'] : 0, - 'S_STYLE_DEFAULT' => (isset($style_row['style_default'])) ? $style_row['style_default'] : 0, - - 'U_ACTION' => $this->u_action . "&action=install&path=" . urlencode($install_path), - 'U_BACK' => $this->u_action, - - 'L_TITLE' => $user->lang[$this->page_title], - 'L_EXPLAIN' => $user->lang[$this->page_title . '_EXPLAIN'], - 'L_NAME' => $user->lang[$l_type . '_NAME'], - - 'ERROR_MSG' => (sizeof($error)) ? implode('<br />', $error) : '', - 'NAME' => $style_row[$mode . '_name'], - 'COPYRIGHT' => $style_row[$mode . '_copyright'], - 'TEMPLATE_NAME' => ($mode == 'style') ? $style_row['template_name'] : '', - 'THEME_NAME' => ($mode == 'style') ? $style_row['theme_name'] : '') - ); + // Increase counters + $counter = ($style['style_id']) ? ($style['style_active'] ? 'active' : 'inactive') : (empty($style['_available']) ? 'cannotinstall' : 'caninstall'); + if (!isset($this->style_counters)) + { + $this->style_counters = array( + 'total' => 0, + 'active' => 0, + 'inactive' => 0, + 'caninstall' => 0, + 'cannotinstall' => 0 + ); + } + $this->style_counters[$counter]++; + $this->style_counters['total']++; } /** - * Add new style + * Show welcome message + * + * @param string $title main title + * @param string $description page description */ - function add($mode) + protected function welcome_message($title, $description) { - global $phpbb_root_path, $phpEx, $config, $db, $cache, $user, $template; - - $l_type = strtoupper($mode); - $element_ary = array('template' => STYLES_TEMPLATE_TABLE, 'theme' => STYLES_THEME_TABLE); - $error = array(); - - $style_row = array( - $mode . '_name' => utf8_normalize_nfc(request_var('name', '', true)), - $mode . '_copyright' => utf8_normalize_nfc(request_var('copyright', '', true)), - 'template_id' => 0, - 'theme_id' => 0, - 'style_active' => request_var('style_active', 1), - 'style_default' => request_var('style_default', 0), + $this->template->assign_vars(array( + 'L_TITLE' => $this->user->lang[$title], + 'L_EXPLAIN' => (isset($this->user->lang[$description])) ? $this->user->lang[$description] : '' + ) ); + } - $basis = request_var('basis', 0); - $update = (isset($_POST['update'])) ? true : false; - - if ($basis) - { - switch ($mode) - { - case 'style': - $sql_select = 'template_id, theme_id'; - $sql_from = STYLES_TABLE; - break; - - case 'template': - $sql_select = 'template_id'; - $sql_from = STYLES_TEMPLATE_TABLE; - break; - - case 'theme': - $sql_select = 'theme_id'; - $sql_from = STYLES_THEME_TABLE; - break; - } - - $sql = "SELECT $sql_select - FROM $sql_from - WHERE {$mode}_id = $basis"; - $result = $db->sql_query($sql); - $row = $db->sql_fetchrow($result); - $db->sql_freeresult($result); - - if (!$row) - { - $error[] = $user->lang['NO_' . $l_type]; - } - - if (!sizeof($error)) - { - $style_row['template_id'] = (isset($row['template_id'])) ? $row['template_id'] : $style_row['template_id']; - $style_row['theme_id'] = (isset($row['theme_id'])) ? $row['theme_id'] : $style_row['theme_id']; - } - } - - if ($update) - { - $style_row['template_id'] = request_var('template_id', $style_row['template_id']); - $style_row['theme_id'] = request_var('theme_id', $style_row['theme_id']); - - if ($mode == 'style' && (!$style_row['template_id'] || !$style_row['theme_id'])) - { - $error[] = $user->lang['STYLE_ERR_NO_IDS']; - } - } - - // User has submitted form and no errors have occurred - if ($update && !sizeof($error)) - { - if ($mode == 'style') - { - $style_row['style_id'] = 0; - - $this->install_style($error, 'add', '', $style_row['style_id'], $style_row['style_name'], '', $style_row['style_copyright'], $style_row['style_active'], $style_row['style_default'], $style_row); - } - - if (!sizeof($error)) - { - $cache->destroy('sql', STYLES_TABLE); - - trigger_error($user->lang[$l_type . '_ADDED'] . adm_back_link($this->u_action)); - } - } + /** + * Find all directories that have styles + * + * @return array Directory names + */ + protected function find_style_dirs() + { + $styles = array(); - if ($mode == 'style') + $dp = @opendir($this->styles_path); + if ($dp) { - foreach ($element_ary as $element => $table) + while (($file = readdir($dp)) !== false) { - $sql = "SELECT {$element}_id, {$element}_name - FROM $table - ORDER BY {$element}_id ASC"; - $result = $db->sql_query($sql); + $dir = $this->styles_path . $file; + if ($file[0] == '.' || !is_dir($dir)) + { + continue; + } - ${$element . '_options'} = ''; - while ($row = $db->sql_fetchrow($result)) + if (file_exists("{$dir}/style.cfg")) { - $selected = ($row[$element . '_id'] == $style_row[$element . '_id']) ? ' selected="selected"' : ''; - ${$element . '_options'} .= '<option value="' . $row[$element . '_id'] . '"' . $selected . '>' . $row[$element . '_name'] . '</option>'; + $styles[] = $file; } - $db->sql_freeresult($result); } + closedir($dp); } - $this->page_title = 'ADD_' . $l_type; - - $template->assign_vars(array( - 'S_DETAILS' => true, - 'S_ADD' => true, - 'S_ERROR_MSG' => (sizeof($error)) ? true : false, - 'S_STYLE' => ($mode == 'style') ? true : false, - 'S_TEMPLATE' => ($mode == 'template') ? true : false, - 'S_THEME' => ($mode == 'theme') ? true : false, - 'S_BASIS' => ($basis) ? true : false, - - 'S_STYLE_ACTIVE' => (isset($style_row['style_active'])) ? $style_row['style_active'] : 0, - 'S_STYLE_DEFAULT' => (isset($style_row['style_default'])) ? $style_row['style_default'] : 0, - 'S_TEMPLATE_OPTIONS' => ($mode == 'style') ? $template_options : '', - 'S_THEME_OPTIONS' => ($mode == 'style') ? $theme_options : '', - - 'U_ACTION' => $this->u_action . '&action=add&basis=' . $basis, - 'U_BACK' => $this->u_action, - - 'L_TITLE' => $user->lang[$this->page_title], - 'L_EXPLAIN' => $user->lang[$this->page_title . '_EXPLAIN'], - 'L_NAME' => $user->lang[$l_type . '_NAME'], - - 'ERROR_MSG' => (sizeof($error)) ? implode('<br />', $error) : '', - 'NAME' => $style_row[$mode . '_name'], - 'COPYRIGHT' => $style_row[$mode . '_copyright']) - ); - + return $styles; } /** - - $reqd_template = (isset($installcfg['required_template'])) ? $installcfg['required_template'] : false; - $reqd_theme = (isset($installcfg['required_theme'])) ? $installcfg['required_theme'] : false; - - // Check to see if each element is already installed, if it is grab the id - foreach ($element_ary as $element => $table) - { - $style_row = array_merge($style_row, array( - $element . '_id' => 0, - $element . '_name' => '', - $element . '_copyright' => '') - ); - - $this->test_installed($element, $error, $root_path, ${'reqd_' . $element}, $style_row[$element . '_id'], $style_row[$element . '_name'], $style_row[$element . '_copyright']); - * Is this element installed? If not, grab its cfg details + * Sort styles */ - function test_installed($element, &$error, $root_path, $reqd_name, &$id, &$name, &$copyright) + public function sort_styles($style1, $style2) { - global $db, $user; - - switch ($element) + if ($style1['style_active'] != $style2['style_active']) { - case 'template': - $sql_from = STYLES_TEMPLATE_TABLE; - break; - - case 'theme': - $sql_from = STYLES_THEME_TABLE; - break; - } - - $l_element = strtoupper($element); - - $chk_name = ($reqd_name !== false) ? $reqd_name : $name; - - $sql = "SELECT {$element}_id, {$element}_name - FROM $sql_from - WHERE {$element}_name = '" . $db->sql_escape($chk_name) . "'"; - $result = $db->sql_query($sql); - - if ($row = $db->sql_fetchrow($result)) - { - $name = $row[$element . '_name']; - $id = $row[$element . '_id']; + return ($style1['style_active']) ? -1 : 1; } - else + if (isset($style1['_available']) && $style1['_available'] != $style2['_available']) { - if (!($cfg = @file("$root_path$element/$element.cfg"))) - { - $error[] = sprintf($user->lang['REQUIRES_' . $l_element], $reqd_name); - return false; - } - - $cfg = parse_cfg_file("$root_path$element/$element.cfg", $cfg); - - $name = $cfg['name']; - $copyright = $cfg['copyright']; - $id = 0; - - unset($cfg); + return ($style1['_available']) ? -1 : 1; } - $db->sql_freeresult($result); + return strcasecmp(isset($style1['style_name']) ? $style1['style_name'] : $style1['name'], isset($style2['style_name']) ? $style2['style_name'] : $style2['name']); } /** - * Install/Add style + * Read style configuration file + * + * @param string $dir style directory + * @return array|bool Style data, false on error */ - function install_style(&$error, $action, $root_path, &$id, $name, $path, $copyright, $active, $default, &$style_row, $template_root_path = false, $template_path = false, $theme_root_path = false, $theme_path = false) + protected function read_style_cfg($dir) { - global $config, $db, $user; - - $element_ary = array('template', 'theme'); + static $required = array('name', 'phpbb_version', 'copyright'); + $cfg = parse_cfg_file($this->styles_path . $dir . '/style.cfg'); - if (!$name) + // Check if it is a valid file + foreach ($required as $key) { - $error[] = $user->lang['STYLE_ERR_STYLE_NAME']; - } - - // Check length settings - if (utf8_strlen($name) > 30) - { - $error[] = $user->lang['STYLE_ERR_NAME_LONG']; + if (!isset($cfg[$key])) + { + return false; + } } - if (utf8_strlen($copyright) > 60) + // Check data + if (!isset($cfg['parent']) || !is_string($cfg['parent']) || $cfg['parent'] == $cfg['name']) { - $error[] = $user->lang['STYLE_ERR_COPY_LONG']; + $cfg['parent'] = ''; } - - // Check if the name already exist - $sql = 'SELECT style_id - FROM ' . STYLES_TABLE . " - WHERE style_name = '" . $db->sql_escape($name) . "'"; - $result = $db->sql_query($sql); - $row = $db->sql_fetchrow($result); - $db->sql_freeresult($result); - - if ($row) + if (!isset($cfg['template_bitfield'])) { - $error[] = $user->lang['STYLE_ERR_NAME_EXIST']; + $cfg['template_bitfield'] = $this->default_bitfield(); } - if (sizeof($error)) - { - return false; - } + return $cfg; + } - foreach ($element_ary as $element) + /** + * Install style + * + * @param $style style data + * @return int Style id + */ + protected function install_style($style) + { + // Generate row + $sql_ary = array(); + foreach ($style as $key => $value) { - // Zero id value ... need to install element ... run usual checks - // and do the install if necessary - if (!$style_row[$element . '_id']) + if ($key != 'style_id' && substr($key, 0, 1) != '_') { - $this->install_element($element, $error, $action, (${$element . '_root_path'}) ? ${$element . '_root_path'} : $root_path, $style_row[$element . '_id'], $style_row[$element . '_name'], (${$element . '_path'}) ? ${$element . '_path'} : $path, $style_row[$element . '_copyright']); + $sql_ary[$key] = $value; } } - if (!$style_row['template_id'] || !$style_row['theme_id']) - { - $error[] = $user->lang['STYLE_ERR_NO_IDS']; - } - - if (sizeof($error)) - { - return false; - } - - $db->sql_transaction('begin'); - - $sql_ary = array( - 'style_name' => $name, - 'style_copyright' => $copyright, - 'style_active' => (int) $active, - 'template_id' => (int) $style_row['template_id'], - 'theme_id' => (int) $style_row['theme_id'], - ); + // Add to database + $this->db->sql_transaction('begin'); $sql = 'INSERT INTO ' . STYLES_TABLE . ' - ' . $db->sql_build_array('INSERT', $sql_ary); - $db->sql_query($sql); + ' . $this->db->sql_build_array('INSERT', $sql_ary); + $this->db->sql_query($sql); - $id = $db->sql_nextid(); + $id = $this->db->sql_nextid(); - if ($default) - { - $sql = 'UPDATE ' . USERS_TABLE . " - SET user_style = $id - WHERE user_style = " . $config['default_style']; - $db->sql_query($sql); - - set_config('default_style', $id); - } + $this->db->sql_transaction('commit'); - $db->sql_transaction('commit'); + add_log('admin', 'LOG_STYLE_ADD', $sql_ary['style_name']); - add_log('admin', 'LOG_STYLE_ADD', $name); + return $id; } /** - * Install/add an element, doing various checks as we go + * Lists all styles + * + * @return array Rows with styles data */ - function install_element($mode, &$error, $action, $root_path, &$id, $name, $path, $copyright) + protected function get_styles() { - global $phpbb_root_path, $db, $user; - - // we parse the cfg here (again) - $cfg_data = parse_cfg_file("$root_path$mode/$mode.cfg"); + $sql = 'SELECT * + FROM ' . STYLES_TABLE; + $result = $this->db->sql_query($sql); - switch ($mode) - { - case 'template': - $sql_from = STYLES_TEMPLATE_TABLE; - break; + $rows = $this->db->sql_fetchrowset($result); + $this->db->sql_freeresult($result); - case 'theme': - $sql_from = STYLES_THEME_TABLE; - break; - } + return $rows; + } - $l_type = strtoupper($mode); + /** + * Count users for each style + * + * @return array Styles in following format: [style_id] = number of users + */ + protected function get_users() + { + $sql = 'SELECT user_style, COUNT(user_style) AS style_count + FROM ' . USERS_TABLE . ' + GROUP BY user_style'; + $result = $this->db->sql_query($sql); - if (!$name) + $style_count = array(); + while ($row = $this->db->sql_fetchrow($result)) { - $error[] = $user->lang[$l_type . '_ERR_STYLE_NAME']; + $style_count[$row['user_style']] = $row['style_count']; } + $this->db->sql_freeresult($result); - // Check length settings - if (utf8_strlen($name) > 30) - { - $error[] = $user->lang[$l_type . '_ERR_NAME_LONG']; - } + return $style_count; + } - if (utf8_strlen($copyright) > 60) - { - $error[] = $user->lang[$l_type . '_ERR_COPY_LONG']; - } + /** + * Uninstall style + * + * @param array $style Style data + * @return bool|string True on success, error message on error + */ + protected function uninstall_style($style) + { + $id = $style['style_id']; + $path = $style['style_path']; - // Check if the name already exist - $sql = "SELECT {$mode}_id - FROM $sql_from - WHERE {$mode}_name = '" . $db->sql_escape($name) . "'"; - $result = $db->sql_query($sql); - $row = $db->sql_fetchrow($result); - $db->sql_freeresult($result); + // Check if style has child styles + $sql = 'SELECT style_id + FROM ' . STYLES_TABLE . ' + WHERE style_parent_id = ' . (int) $id . " OR style_parent_tree = '" . $this->db->sql_escape($path) . "'"; + $result = $this->db->sql_query($sql); - if ($row) - { - // If it exist, we just use the style on installation - if ($action == 'install') - { - $id = $row[$mode . '_id']; - return false; - } + $conflict = $this->db->sql_fetchrow($result); + $this->db->sql_freeresult($result); - $error[] = $user->lang[$l_type . '_ERR_NAME_EXIST']; + if ($conflict !== false) + { + return sprintf($this->user->lang['STYLE_UNINSTALL_DEPENDENT'], $style['style_name']); } - if (isset($cfg_data['inherit_from']) && $cfg_data['inherit_from']) - { - if ($mode === 'template') - { - $select_bf = ', bbcode_bitfield'; - } - else - { - $select_bf = ''; - } + // Change default style for users + $sql = 'UPDATE ' . USERS_TABLE . ' + SET user_style = 0 + WHERE user_style = ' . $id; + $this->db->sql_query($sql); - $sql = "SELECT {$mode}_id, {$mode}_name, {$mode}_path $select_bf - FROM $sql_from - WHERE {$mode}_name = '" . $db->sql_escape($cfg_data['inherit_from']) . "' - AND {$mode}_inherits_id = 0"; - $result = $db->sql_query($sql); - $row = $db->sql_fetchrow($result); - $db->sql_freeresult($result); - if (!$row) - { - $error[] = sprintf($user->lang[$l_type . '_ERR_REQUIRED_OR_INCOMPLETE'], $cfg_data['inherit_from']); - } - else - { - $inherit_id = $row["{$mode}_id"]; - $inherit_path = $row["{$mode}_path"]; - $inherit_bf = ($mode === 'template') ? $row["bbcode_bitfield"] : false; - } - } - else - { - $inherit_id = 0; - $inherit_path = ''; - $inherit_bf = false; - } + // Uninstall style + $sql = 'DELETE FROM ' . STYLES_TABLE . ' + WHERE style_id = ' . $id; + $this->db->sql_query($sql); + return true; + } - if (sizeof($error)) - { - return false; - } + /** + * Delete all files in style directory + * + * @param string $path Style directory + * @param string $dir Directory to remove inside style's directory + * @return bool True on success, false on error + */ + protected function delete_style_files($path, $dir = '') + { + $dirname = $this->styles_path . $path . $dir; + $result = true; - $sql_ary = array( - $mode . '_name' => $name, - $mode . '_copyright' => $copyright, - $mode . '_path' => $path, - ); + $dp = @opendir($dirname); - switch ($mode) + if ($dp) { - case 'template': - // We check if the template author defined a different bitfield - if (!empty($cfg_data['template_bitfield'])) + while (($file = readdir($dp)) !== false) + { + if ($file == '.' || $file == '..') { - $sql_ary['bbcode_bitfield'] = $cfg_data['template_bitfield']; + continue; } - else if ($inherit_bf) + $filename = $dirname . '/' . $file; + if (is_dir($filename)) { - $sql_ary['bbcode_bitfield'] = $inherit_bf; + if (!$this->delete_style_files($path, $dir . '/' . $file)) + { + $result = false; + } } else { - $sql_ary['bbcode_bitfield'] = $this->template_bitfield; - } - - if (isset($cfg_data['inherit_from']) && $cfg_data['inherit_from']) - { - $sql_ary += array( - 'template_inherits_id' => $inherit_id, - 'template_inherit_path' => $inherit_path, - ); + if (!@unlink($filename)) + { + $result = false; + } } - break; - - case 'theme': - break; + } + closedir($dp); + } + if (!@rmdir($dirname)) + { + return false; } - $db->sql_transaction('begin'); - - $sql = "INSERT INTO $sql_from - " . $db->sql_build_array('INSERT', $sql_ary); - $db->sql_query($sql); - - $id = $db->sql_nextid(); - - $db->sql_transaction('commit'); - - add_log('admin', 'LOG_' . $l_type . '_ADD_FS', $name); + return $result; } /** - * Checks downwards dependencies + * Get list of items from posted data * - * @access public - * @param string $mode The element type to check - only template is supported - * @param int $id The template id - * @returns false if no component inherits, array with name, path and id for each subtemplate otherwise + * @param string $name Variable name + * @param string|int $default Default value for array + * @param bool $error If true, error will be triggered if list is empty + * @return array Items */ - function check_inheritance($mode, $id) + protected function request_vars($name, $default, $error = false) { - global $db; - - $l_type = strtoupper($mode); + $item = $this->request->variable($name, $default); + $items = $this->request->variable($name . 's', array($default)); - switch ($mode) + if (count($items) == 1 && $items[0] == $default) { - case 'template': - $sql_from = STYLES_TEMPLATE_TABLE; - break; - - case 'theme': - $sql_from = STYLES_THEME_TABLE; - break; + $items = array(); } - $sql = "SELECT {$mode}_id, {$mode}_name, {$mode}_path - FROM $sql_from - WHERE {$mode}_inherits_id = " . (int) $id; - $result = $db->sql_query($sql); - - $names = array(); - while ($row = $db->sql_fetchrow($result)) + if ($item != $default && !count($items)) { - - $names[$row["{$mode}_id"]] = array( - "{$mode}_id" => $row["{$mode}_id"], - "{$mode}_name" => $row["{$mode}_name"], - "{$mode}_path" => $row["{$mode}_path"], - ); + $items[] = $item; } - $db->sql_freeresult($result); - if (sizeof($names)) + if ($error && !count($items)) { - return $names; - } - else - { - return false; + trigger_error($this->user->lang['NO_MATCHING_STYLES_FOUND'] . adm_back_link($this->u_action), E_USER_WARNING); } + + return $items; } /** - * Checks upwards dependencies + * Generates default bitfield + * + * This bitfield decides which bbcodes are defined in a template. * - * @access public - * @param string $mode The element type to check - only template is supported - * @param int $id The template id - * @returns false if the component does not inherit, array with name, path and id otherwise + * @return string Bitfield */ - function get_super($mode, $id) + protected function default_bitfield() { - global $db; - - $l_type = strtoupper($mode); - - switch ($mode) - { - case 'template': - $sql_from = STYLES_TEMPLATE_TABLE; - break; - - case 'theme': - $sql_from = STYLES_THEME_TABLE; - break; - } - - $sql = "SELECT {$mode}_inherits_id - FROM $sql_from - WHERE {$mode}_id = " . (int) $id; - $result = $db->sql_query_limit($sql, 1); - - if ($row = $db->sql_fetchrow($result)) - { - $db->sql_freeresult($result); - } - else - { - return false; - } - - $super_id = $row["{$mode}_inherits_id"]; - - $sql = "SELECT {$mode}_id, {$mode}_name, {$mode}_path - FROM $sql_from - WHERE {$mode}_id = " . (int) $super_id; - - $result = $db->sql_query_limit($sql, 1); - if ($row = $db->sql_fetchrow($result)) + static $value; + if (isset($value)) { - $db->sql_freeresult($result); - return $row; + return $value; } - return false; + // Hardcoded template bitfield to add for new templates + $bitfield = new bitfield(); + $bitfield->set(0); + $bitfield->set(1); + $bitfield->set(2); + $bitfield->set(3); + $bitfield->set(4); + $bitfield->set(8); + $bitfield->set(9); + $bitfield->set(11); + $bitfield->set(12); + $value = $bitfield->get_base64(); + return $value; } + } diff --git a/phpBB/includes/acp/acp_users.php b/phpBB/includes/acp/acp_users.php index 97f4b1b5fd..44717452e8 100644 --- a/phpBB/includes/acp/acp_users.php +++ b/phpBB/includes/acp/acp_users.php @@ -128,7 +128,7 @@ class acp_users $dropdown_modes = array(); while ($row = $db->sql_fetchrow($result)) { - if (!$this->p_master->module_auth($row['module_auth'])) + if (!$this->p_master->module_auth_self($row['module_auth'])) { continue; } @@ -1554,7 +1554,7 @@ class acp_users || $user_row['user_allow_viewonline'] && !$sql_ary['user_allow_viewonline']) { // We also need to check if the user has the permission to cloak. - $user_auth = new auth(); + $user_auth = new phpbb_auth(); $user_auth->acl($user_row); $session_sql_ary = array( diff --git a/phpBB/includes/acp/auth.php b/phpBB/includes/acp/auth.php index 8f99450776..7d9fd267ff 100644 --- a/phpBB/includes/acp/auth.php +++ b/phpBB/includes/acp/auth.php @@ -19,7 +19,7 @@ if (!defined('IN_PHPBB')) * ACP Permission/Auth class * @package phpBB3 */ -class auth_admin extends auth +class auth_admin extends phpbb_auth { /** * Init auth settings @@ -130,7 +130,7 @@ class auth_admin extends auth { if ($user->data['user_id'] != $userdata['user_id']) { - $auth2 = new auth(); + $auth2 = new phpbb_auth(); $auth2->acl($userdata); } else diff --git a/phpBB/includes/acp/info/acp_styles.php b/phpBB/includes/acp/info/acp_styles.php index 221371c4cb..3137c4781b 100644 --- a/phpBB/includes/acp/info/acp_styles.php +++ b/phpBB/includes/acp/info/acp_styles.php @@ -17,11 +17,11 @@ class acp_styles_info return array( 'filename' => 'acp_styles', 'title' => 'ACP_CAT_STYLES', - 'version' => '1.0.0', + 'version' => '2.0.0', 'modes' => array( 'style' => array('title' => 'ACP_STYLES', 'auth' => 'acl_a_styles', 'cat' => array('ACP_STYLE_MANAGEMENT')), - 'template' => array('title' => 'ACP_TEMPLATES', 'auth' => 'acl_a_styles', 'cat' => array('ACP_STYLE_COMPONENTS')), - 'theme' => array('title' => 'ACP_THEMES', 'auth' => 'acl_a_styles', 'cat' => array('ACP_STYLE_COMPONENTS')), + 'install' => array('title' => 'ACP_STYLES_INSTALL', 'auth' => 'acl_a_styles', 'cat' => array('ACP_STYLE_MANAGEMENT')), + 'cache' => array('title' => 'ACP_STYLES_CACHE', 'auth' => 'acl_a_styles', 'cat' => array('ACP_STYLE_MANAGEMENT')), ), ); } diff --git a/phpBB/includes/auth.php b/phpBB/includes/auth/auth.php index 11e86a50fe..e3bccaf47b 100644 --- a/phpBB/includes/auth.php +++ b/phpBB/includes/auth/auth.php @@ -19,7 +19,7 @@ if (!defined('IN_PHPBB')) * Permission/Auth class * @package phpBB3 */ -class auth +class phpbb_auth { var $acl = array(); var $cache = array(); diff --git a/phpBB/includes/bbcode.php b/phpBB/includes/bbcode.php index 60274a308d..fde917e5b1 100644 --- a/phpBB/includes/bbcode.php +++ b/phpBB/includes/bbcode.php @@ -132,12 +132,13 @@ class bbcode { $this->template_bitfield = new bitfield($user->theme['bbcode_bitfield']); - $template_locator = new phpbb_template_locator(); - $template_path_provider = new phpbb_template_extension_path_provider($phpbb_extension_manager, new phpbb_template_path_provider()); - $template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $template_locator, $template_path_provider); - $template->set_template(); - $template_locator->set_filenames(array('bbcode.html' => 'bbcode.html')); - $this->template_filename = $template_locator->get_source_file_for_handle('bbcode.html'); + $style_resource_locator = new phpbb_style_resource_locator(); + $style_path_provider = new phpbb_style_extension_path_provider($phpbb_extension_manager, new phpbb_style_path_provider()); + $template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator); + $style = new phpbb_style($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, $style_path_provider, $template); + $style->set_style(); + $template->set_filenames(array('bbcode.html' => 'bbcode.html')); + $this->template_filename = $style_resource_locator->get_source_file_for_handle('bbcode.html'); } $bbcode_ids = $rowset = $sql = array(); diff --git a/phpBB/includes/captcha/captcha_factory.php b/phpBB/includes/captcha/captcha_factory.php index 62fb90d3c5..d57b333c69 100644 --- a/phpBB/includes/captcha/captcha_factory.php +++ b/phpBB/includes/captcha/captcha_factory.php @@ -25,7 +25,7 @@ class phpbb_captcha_factory /** * return an instance of class $name in file $name_plugin.php */ - function get_instance($name) + public static function get_instance($name) { global $phpbb_root_path, $phpEx; diff --git a/phpBB/includes/captcha/plugins/phpbb_captcha_gd_plugin.php b/phpBB/includes/captcha/plugins/phpbb_captcha_gd_plugin.php index a63b0e448f..6c1f3bf00b 100644 --- a/phpBB/includes/captcha/plugins/phpbb_captcha_gd_plugin.php +++ b/phpBB/includes/captcha/plugins/phpbb_captcha_gd_plugin.php @@ -49,7 +49,7 @@ class phpbb_captcha_gd extends phpbb_default_captcha } } - function get_instance() + public static function get_instance() { $instance = new phpbb_captcha_gd(); return $instance; diff --git a/phpBB/includes/captcha/plugins/phpbb_captcha_gd_wave_plugin.php b/phpBB/includes/captcha/plugins/phpbb_captcha_gd_wave_plugin.php index f523cae191..a5588178bb 100644 --- a/phpBB/includes/captcha/plugins/phpbb_captcha_gd_wave_plugin.php +++ b/phpBB/includes/captcha/plugins/phpbb_captcha_gd_wave_plugin.php @@ -39,7 +39,7 @@ class phpbb_captcha_gd_wave extends phpbb_default_captcha } } - function get_instance() + public static function get_instance() { return new phpbb_captcha_gd_wave(); } diff --git a/phpBB/includes/captcha/plugins/phpbb_captcha_nogd_plugin.php b/phpBB/includes/captcha/plugins/phpbb_captcha_nogd_plugin.php index 4a0418146d..96f13fbe1b 100644 --- a/phpBB/includes/captcha/plugins/phpbb_captcha_nogd_plugin.php +++ b/phpBB/includes/captcha/plugins/phpbb_captcha_nogd_plugin.php @@ -39,7 +39,7 @@ class phpbb_captcha_nogd extends phpbb_default_captcha } } - function get_instance() + public static function get_instance() { $instance = new phpbb_captcha_nogd(); return $instance; diff --git a/phpBB/includes/captcha/plugins/phpbb_captcha_qa_plugin.php b/phpBB/includes/captcha/plugins/phpbb_captcha_qa_plugin.php index e12ad00d25..99813189d7 100644 --- a/phpBB/includes/captcha/plugins/phpbb_captcha_qa_plugin.php +++ b/phpBB/includes/captcha/plugins/phpbb_captcha_qa_plugin.php @@ -98,7 +98,7 @@ class phpbb_captcha_qa /** * API function */ - function get_instance() + public static function get_instance() { $instance = new phpbb_captcha_qa(); diff --git a/phpBB/includes/captcha/plugins/phpbb_recaptcha_plugin.php b/phpBB/includes/captcha/plugins/phpbb_recaptcha_plugin.php index 0ea8b9c6e7..70c24a8c30 100644 --- a/phpBB/includes/captcha/plugins/phpbb_recaptcha_plugin.php +++ b/phpBB/includes/captcha/plugins/phpbb_recaptcha_plugin.php @@ -54,7 +54,7 @@ class phpbb_recaptcha extends phpbb_default_captcha $this->response = request_var('recaptcha_response_field', ''); } - function get_instance() + public static function get_instance() { $instance = new phpbb_recaptcha(); return $instance; diff --git a/phpBB/includes/db/db_tools.php b/phpBB/includes/db/db_tools.php index f9f4c97309..efb8b3ebd7 100644 --- a/phpBB/includes/db/db_tools.php +++ b/phpBB/includes/db/db_tools.php @@ -2114,7 +2114,7 @@ class phpbb_db_tools case 'mysql_40': case 'mysql_41': - $statements[] = 'ALTER TABLE ' . $table_name . ' ADD UNIQUE INDEX (' . implode(', ', $column) . ')'; + $statements[] = 'ALTER TABLE ' . $table_name . ' ADD UNIQUE INDEX ' . $index_name . '(' . implode(', ', $column) . ')'; break; case 'mssql': diff --git a/phpBB/includes/event/data.php b/phpBB/includes/event/data.php new file mode 100644 index 0000000000..70718ff0ae --- /dev/null +++ b/phpBB/includes/event/data.php @@ -0,0 +1,68 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + +use Symfony\Component\EventDispatcher\Event; + +class phpbb_event_data extends Event implements ArrayAccess +{ + private $data; + + public function __construct(array $data = array()) + { + $this->set_data($data); + } + + public function set_data(array $data = array()) + { + $this->data = $data; + } + + public function get_data() + { + return $this->data; + } + + /** + * Returns data filtered to only include specified keys. + * + * This effectively discards any keys added to data by hooks. + */ + public function get_data_filtered($keys) + { + return array_intersect_key($this->data, array_flip($keys)); + } + + public function offsetExists($offset) + { + return isset($this->data[$offset]); + } + + public function offsetGet($offset) + { + return isset($this->data[$offset]) ? $this->data[$offset] : null; + } + + public function offsetSet($offset, $value) + { + $this->data[$offset] = $value; + } + + public function offsetUnset($offset) + { + unset($this->data[$offset]); + } +} diff --git a/phpBB/includes/event/dispatcher.php b/phpBB/includes/event/dispatcher.php new file mode 100644 index 0000000000..2bf46b9b06 --- /dev/null +++ b/phpBB/includes/event/dispatcher.php @@ -0,0 +1,42 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + +use Symfony\Component\EventDispatcher\EventDispatcher; + +/** +* Extension of the Symfony2 EventDispatcher +* +* It provides an additional `trigger_event` method, which +* gives some syntactic sugar for dispatching events. Instead +* of creating the event object, the method will do that for +* you. +* +* Example: +* +* $vars = array('page_title'); +* extract($phpbb_dispatcher->trigger_event('core.index', compact($vars))); +* +*/ +class phpbb_event_dispatcher extends EventDispatcher +{ + public function trigger_event($eventName, $data = array()) + { + $event = new phpbb_event_data($data); + $this->dispatch($eventName, $event); + return $event->get_data_filtered(array_keys($data)); + } +} diff --git a/phpBB/includes/event/extension_subscriber_loader.php b/phpBB/includes/event/extension_subscriber_loader.php new file mode 100644 index 0000000000..d933b943d7 --- /dev/null +++ b/phpBB/includes/event/extension_subscriber_loader.php @@ -0,0 +1,46 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +class phpbb_event_extension_subscriber_loader +{ + private $dispatcher; + private $extension_manager; + + public function __construct(EventDispatcherInterface $dispatcher, phpbb_extension_manager $extension_manager) + { + $this->dispatcher = $dispatcher; + $this->extension_manager = $extension_manager; + } + + public function load() + { + $finder = $this->extension_manager->get_finder(); + $subscriber_classes = $finder + ->extension_directory('/event') + ->suffix('listener') + ->core_path('event/') + ->get_classes(); + + foreach ($subscriber_classes as $class) + { + $subscriber = new $class(); + $this->dispatcher->addSubscriber($subscriber); + } + } +} diff --git a/phpBB/includes/extension/controller.php b/phpBB/includes/extension/controller.php new file mode 100644 index 0000000000..c7fd439a19 --- /dev/null +++ b/phpBB/includes/extension/controller.php @@ -0,0 +1,77 @@ +<?php +/** +* +* @package extension +* @copyright (c) 2011 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + +/** +* Abstract class extended by extension front controller classes +* +* @package extension +*/ +abstract class phpbb_extension_controller implements phpbb_extension_controller_interface +{ + /** + * @var phpbb_request Request class object + */ + protected $request; + + /** + * @var dbal DBAL class object + */ + protected $db; + + /** + * @var user User class object + */ + protected $user; + + /** + * @var phpbb_template Template class object + */ + protected $template; + + /** + * @var array Config array + */ + protected $config; + + /** + * @var string PHP Extension + */ + protected $phpEx; + + /** + * @var string Relative path to board root + */ + protected $phpbb_root_path; + + /** + * Constructor method that provides the common phpBB objects as inherited class + * properties for automatic availability in extension controllers + */ + public function __construct() + { + global $request, $db, $user, $template, $config; + global $phpEx, $phpbb_root_path; + + $this->request = $request; + $this->db = $db; + $this->user = $user; + $this->template = $template; + $this->config = $config; + $this->phpEx = $phpEx; + $this->phpbb_root_path = $phpbb_root_path; + } +} diff --git a/phpBB/includes/extension/controller_interface.php b/phpBB/includes/extension/controller_interface.php new file mode 100644 index 0000000000..2b88925388 --- /dev/null +++ b/phpBB/includes/extension/controller_interface.php @@ -0,0 +1,31 @@ +<?php +/** +* +* @package extension +* @copyright (c) 2011 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + +/** +* The interface that extension classes have to implement to run front pages +* +* @package extension +*/ +interface phpbb_extension_controller_interface +{ + /** + * Handle the request to display a page from an extension + * + * @return null + */ + public function handle(); +} diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php index 23b9f1c658..87ca40917d 100644 --- a/phpBB/includes/extension/finder.php +++ b/phpBB/includes/extension/finder.php @@ -270,11 +270,12 @@ class phpbb_extension_finder * Finds all directories matching the configured options * * @param bool $cache Whether the result should be cached + * @param bool $extension_keys Whether the result should have extension name as array key * @return array An array of paths to found directories */ - public function get_directories($cache = true) + public function get_directories($cache = true, $extension_keys = false) { - return $this->find_with_root_path($cache, true); + return $this->find_with_root_path($cache, true, $extension_keys); } /** @@ -294,16 +295,25 @@ class phpbb_extension_finder * @param bool $cache Whether the result should be cached * @param bool $is_dir Directories will be returned when true, only files * otherwise + * @param bool $extension_keys If true, result will be associative array + * with extension name as key * @return array An array of paths to found items */ - protected function find_with_root_path($cache = true, $is_dir = false) + protected function find_with_root_path($cache = true, $is_dir = false, $extension_keys = false) { $items = $this->find($cache, $is_dir); $result = array(); foreach ($items as $item => $ext_name) { - $result[] = $this->phpbb_root_path . $item; + if ($extension_keys) + { + $result[$ext_name] = $this->phpbb_root_path . $item; + } + else + { + $result[] = $this->phpbb_root_path . $item; + } } return $result; diff --git a/phpBB/includes/extension/manager.php b/phpBB/includes/extension/manager.php index c38f0df32e..537c19aff8 100644 --- a/phpBB/includes/extension/manager.php +++ b/phpBB/includes/extension/manager.php @@ -352,6 +352,10 @@ class phpbb_extension_manager public function all_available() { $available = array(); + if (!is_dir($this->phpbb_root_path . 'ext/')) + { + return $available; + } $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($this->phpbb_root_path . 'ext/'), @@ -428,6 +432,28 @@ class phpbb_extension_manager } return $disabled; } + + /** + * Check to see if a given extension is available on the filesystem + * + * @param string $name Extension name to check NOTE: Can be user input + * @return bool Depending on whether or not the extension is available + */ + public function available($name) + { + return file_exists($this->get_extension_path($name, true)); + } + + /** + * Check to see if a given extension is enabled + * + * @param string $name Extension name to check + * @return bool Depending on whether or not the extension is enabled + */ + public function enabled($name) + { + return isset($this->extensions[$name]) && $this->extensions[$name]['ext_active']; + } /** * Instantiates a phpbb_extension_finder. diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php index 438e095071..28f06abbc1 100644 --- a/phpBB/includes/functions.php +++ b/phpBB/includes/functions.php @@ -2448,15 +2448,25 @@ function build_url($strip_vars = false) */ function meta_refresh($time, $url, $disable_cd_check = false) { - global $template; + global $template, $refresh_data, $request; - $url = redirect($url, true, $disable_cd_check); - $url = str_replace('&', '&', $url); + if ($request->is_ajax()) + { + $refresh_data = array( + 'time' => $time, + 'url' => str_replace('&', '&', $url) + ); + } + else + { + $url = redirect($url, true, $disable_cd_check); + $url = str_replace('&', '&', $url); - // For XHTML compatibility we change back & to & - $template->assign_vars(array( - 'META' => '<meta http-equiv="refresh" content="' . $time . ';url=' . $url . '" />') - ); + // For XHTML compatibility we change back & to & + $template->assign_vars(array( + 'META' => '<meta http-equiv="refresh" content="' . $time . ';url=' . $url . '" />') + ); + } return $url; } @@ -2619,7 +2629,7 @@ function check_form_key($form_name, $timespan = false, $return_page = '', $trigg */ function confirm_box($check, $title = '', $hidden = '', $html_body = 'confirm_body.html', $u_action = '') { - global $user, $template, $db; + global $user, $template, $db, $request; global $phpEx, $phpbb_root_path, $request; if (isset($_POST['cancel'])) @@ -2699,6 +2709,21 @@ function confirm_box($check, $title = '', $hidden = '', $html_body = 'confirm_bo WHERE user_id = " . $user->data['user_id']; $db->sql_query($sql); + + if ($request->is_ajax()) + { + $u_action .= '&confirm_uid=' . $user->data['user_id'] . '&sess=' . $user->session_id . '&sid=' . $user->session_id; + $json_response = new phpbb_json_response; + $json_response->send(array( + 'MESSAGE_TITLE' => (!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title], + 'MESSAGE_TEXT' => (!isset($user->lang[$title . '_CONFIRM'])) ? $title : $user->lang[$title . '_CONFIRM'], + + 'YES_VALUE' => $user->lang['YES'], + 'S_CONFIRM_ACTION' => str_replace('&', '&', $u_action), //inefficient, rewrite whole function + 'S_HIDDEN_FIELDS' => $hidden . $s_hidden_fields + )); + } + if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin']) { adm_page_footer(); @@ -3092,9 +3117,9 @@ function parse_cfg_file($filename, $lines = false) $parsed_items[$key] = $value; } - if (isset($parsed_items['inherit_from']) && isset($parsed_items['name']) && $parsed_items['inherit_from'] == $parsed_items['name']) + if (isset($parsed_items['parent']) && isset($parsed_items['name']) && $parsed_items['parent'] == $parsed_items['name']) { - unset($parsed_items['inherit_from']); + unset($parsed_items['parent']); } return $parsed_items; @@ -3723,7 +3748,7 @@ function phpbb_checkdnsrr($host, $type = 'MX') */ function msg_handler($errno, $msg_text, $errfile, $errline) { - global $cache, $db, $auth, $template, $config, $user; + global $cache, $db, $auth, $template, $config, $user, $request; global $phpEx, $phpbb_root_path, $msg_title, $msg_long_text; // Do not display notices if we suppress them via @ @@ -3922,6 +3947,20 @@ function msg_handler($errno, $msg_text, $errfile, $errline) 'S_USER_NOTICE' => ($errno == E_USER_NOTICE) ? true : false) ); + if ($request->is_ajax()) + { + global $refresh_data; + + $json_response = new phpbb_json_response; + $json_response->send(array( + 'MESSAGE_TITLE' => $msg_title, + 'MESSAGE_TEXT' => $msg_text, + 'S_USER_WARNING' => ($errno == E_USER_WARNING) ? true : false, + 'S_USER_NOTICE' => ($errno == E_USER_NOTICE) ? true : false, + 'REFRESH_DATA' => (!empty($refresh_data)) ? $refresh_data : null + )); + } + // We do not want the cron script to be called on error messages define('IN_CRON', true); @@ -4478,6 +4517,7 @@ function phpbb_http_login($param) function page_header($page_title = '', $display_online_list = true, $item_id = 0, $item = 'forum') { global $db, $config, $template, $SID, $_SID, $_EXTRA_URL, $user, $auth, $phpEx, $phpbb_root_path; + global $phpbb_dispatcher; if (defined('HEADER_INC')) { @@ -4727,10 +4767,11 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0 'S_SEARCH_HIDDEN_FIELDS' => build_hidden_fields($s_search_hidden_fields), + 'T_ASSETS_VERSION' => $config['assets_version'], 'T_ASSETS_PATH' => "{$web_path}assets", - 'T_THEME_PATH' => "{$web_path}styles/" . rawurlencode($user->theme['theme_path']) . '/theme', - 'T_TEMPLATE_PATH' => "{$web_path}styles/" . rawurlencode($user->theme['template_path']) . '/template', - 'T_SUPER_TEMPLATE_PATH' => (isset($user->theme['template_inherit_path']) && $user->theme['template_inherit_path']) ? "{$web_path}styles/" . rawurlencode($user->theme['template_inherit_path']) . '/template' : "{$web_path}styles/" . rawurlencode($user->theme['template_path']) . '/template', + 'T_THEME_PATH' => "{$web_path}styles/" . rawurlencode($user->theme['style_path']) . '/theme', + 'T_TEMPLATE_PATH' => "{$web_path}styles/" . rawurlencode($user->theme['style_path']) . '/template', + 'T_SUPER_TEMPLATE_PATH' => "{$web_path}styles/" . rawurlencode($user->theme['style_path']) . '/template', 'T_IMAGES_PATH' => "{$web_path}images/", 'T_SMILIES_PATH' => "{$web_path}{$config['smilies_path']}/", 'T_AVATAR_PATH' => "{$web_path}{$config['avatar_path']}/", @@ -4738,16 +4779,16 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0 'T_ICONS_PATH' => "{$web_path}{$config['icons_path']}/", 'T_RANKS_PATH' => "{$web_path}{$config['ranks_path']}/", 'T_UPLOAD_PATH' => "{$web_path}{$config['upload_path']}/", - 'T_STYLESHEET_LINK' => "{$web_path}styles/" . rawurlencode($user->theme['theme_path']) . '/theme/stylesheet.css', - 'T_STYLESHEET_LANG_LINK' => "{$web_path}styles/" . rawurlencode($user->theme['theme_path']) . '/theme/' . $user->lang_name . '/stylesheet.css', - 'T_STYLESHEET_NAME' => $user->theme['theme_name'], - 'T_JQUERY_LINK' => ($config['load_jquery_cdn'] && !empty($config['load_jquery_url'])) ? $config['load_jquery_url'] : "{$web_path}assets/javascript/jquery.js", + 'T_STYLESHEET_LINK' => "{$web_path}styles/" . rawurlencode($user->theme['style_path']) . '/theme/stylesheet.css?assets_version=' . $config['assets_version'], + 'T_STYLESHEET_LANG_LINK' => "{$web_path}styles/" . rawurlencode($user->theme['style_path']) . '/theme/' . $user->lang_name . '/stylesheet.css?assets_version=' . $config['assets_version'], + 'T_STYLESHEET_NAME' => $user->theme['style_name'], + 'T_JQUERY_LINK' => ($config['load_jquery_cdn'] && !empty($config['load_jquery_url'])) ? $config['load_jquery_url'] : "{$web_path}assets/javascript/jquery.js?assets_version=" . $config['assets_version'], 'S_JQUERY_FALLBACK' => ($config['load_jquery_cdn']) ? true : false, - 'T_THEME_NAME' => rawurlencode($user->theme['theme_path']), + 'T_THEME_NAME' => rawurlencode($user->theme['style_path']), 'T_THEME_LANG_NAME' => $user->data['user_lang'], - 'T_TEMPLATE_NAME' => $user->theme['template_path'], - 'T_SUPER_TEMPLATE_NAME' => rawurlencode((isset($user->theme['template_inherit_path']) && $user->theme['template_inherit_path']) ? $user->theme['template_inherit_path'] : $user->theme['template_path']), + 'T_TEMPLATE_NAME' => $user->theme['style_path'], + 'T_SUPER_TEMPLATE_NAME' => rawurlencode((isset($user->theme['style_parent_tree']) && $user->theme['style_parent_tree']) ? $user->theme['style_parent_tree'] : $user->theme['style_path']), 'T_IMAGES' => 'images', 'T_SMILIES' => $config['smilies_path'], 'T_AVATAR' => $config['avatar_path'], @@ -4761,6 +4802,9 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0 'A_COOKIE_SETTINGS' => addslashes('; path=' . $config['cookie_path'] . ((!$config['cookie_domain'] || $config['cookie_domain'] == 'localhost' || $config['cookie_domain'] == '127.0.0.1') ? '' : '; domain=' . $config['cookie_domain']) . ((!$config['cookie_secure']) ? '' : '; secure')), )); + $vars = array('page_title', 'display_online_list', 'item_id', 'item'); + extract($phpbb_dispatcher->trigger_event('core.page_header', compact($vars))); + // application/xhtml+xml not used because of IE header('Content-type: text/html; charset=UTF-8'); @@ -4817,6 +4861,7 @@ function page_footer($run_cron = true) $template->assign_vars(array( 'DEBUG_OUTPUT' => (defined('DEBUG')) ? $debug_output : '', 'TRANSLATION_INFO' => (!empty($user->lang['TRANSLATION_INFO'])) ? $user->lang['TRANSLATION_INFO'] : '', + 'CREDIT_LINE' => $user->lang('POWERED_BY', '<a href="http://www.phpbb.com/">phpBB</a>® Forum Software © phpBB Group'), 'U_ACP' => ($auth->acl_get('a_') && !empty($user->data['is_registered'])) ? append_sid("{$phpbb_root_path}adm/index.$phpEx", false, true, $user->session_id) : '') ); @@ -4906,7 +4951,7 @@ function exit_handler() } /** -* Handler for init calls in phpBB. This function is called in user::setup(); +* Handler for init calls in phpBB. This function is called in phpbb_user::setup(); * This function supports hooks. */ function phpbb_user_session_handler() diff --git a/phpBB/includes/functions_acp.php b/phpBB/includes/functions_acp.php index 03dec1787a..dc61859363 100644 --- a/phpBB/includes/functions_acp.php +++ b/phpBB/includes/functions_acp.php @@ -131,6 +131,7 @@ function adm_page_footer($copyright_html = true) 'DEBUG_OUTPUT' => (defined('DEBUG')) ? $debug_output : '', 'TRANSLATION_INFO' => (!empty($user->lang['TRANSLATION_INFO'])) ? $user->lang['TRANSLATION_INFO'] : '', 'S_COPYRIGHT_HTML' => $copyright_html, + 'CREDIT_LINE' => $user->lang('POWERED_BY', '<a href="http://www.phpbb.com/">phpBB</a>® Forum Software © phpBB Group'), 'T_JQUERY_LINK' => ($config['load_jquery_cdn'] && !empty($config['load_jquery_url'])) ? $config['load_jquery_url'] : "{$phpbb_root_path}assets/javascript/jquery.js", 'S_JQUERY_FALLBACK' => ($config['load_jquery_cdn']) ? true : false, 'VERSION' => $config['version']) diff --git a/phpBB/includes/functions_content.php b/phpBB/includes/functions_content.php index 3a0124934e..6b2ee98d7a 100644 --- a/phpBB/includes/functions_content.php +++ b/phpBB/includes/functions_content.php @@ -739,7 +739,7 @@ function smiley_text($text, $force_option = false) else { $root_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? generate_board_url() . '/' : $phpbb_root_path; - return preg_replace('#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/(.*?) \/><!\-\- s\1 \-\->#', '<img src="' . $root_path . $config['smilies_path'] . '/\2 />', $text); + return preg_replace('#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/(.*?) \/><!\-\- s\1 \-\->#', '<img class="smilies" src="' . $root_path . $config['smilies_path'] . '/\2 />', $text); } } @@ -1103,8 +1103,8 @@ function extension_allowed($forum_id, $extension, &$extensions) * @param string $string The text to truncate to the given length. String is specialchared. * @param int $max_length Maximum length of string (multibyte character count as 1 char / Html entity count as 1 char) * @param int $max_store_length Maximum character length of string (multibyte character count as 1 char / Html entity count as entity chars). -* @param bool $allow_reply Allow Re: in front of string -* NOTE: This parameter can cause undesired behavior (returning strings longer than $max_store_length) and is deprecated. +* @param bool $allow_reply Allow Re: in front of string +* NOTE: This parameter can cause undesired behavior (returning strings longer than $max_store_length) and is deprecated. * @param string $append String to be appended */ function truncate_string($string, $max_length = 60, $max_store_length = 255, $allow_reply = false, $append = '') @@ -1259,6 +1259,22 @@ function get_username_string($mode, $user_id, $username, $username_colour = '', } /** + * Add an option to the quick-mod tools. + * + * @param string $option The language key for the value of the option. + * @param string $lang_string The language string to use. + */ +function phpbb_add_quickmod_option($option, $lang_string) +{ + global $template, $user; + $lang_string = $user->lang($lang_string); + $template->assign_block_vars('quickmod', array( + 'VALUE' => $option, + 'TITLE' => $lang_string, + )); +} + +/** * @package phpBB3 */ class bitfield diff --git a/phpBB/includes/functions_display.php b/phpBB/includes/functions_display.php index 9335cabc15..18db64cc68 100644 --- a/phpBB/includes/functions_display.php +++ b/phpBB/includes/functions_display.php @@ -273,6 +273,7 @@ function display_forums($root_data = '', $display_moderators = true, $return_mod markread('topics', $forum_ids); $message = sprintf($user->lang['RETURN_FORUM'], '<a href="' . $redirect . '">', '</a>'); meta_refresh(3, $redirect); + trigger_error($user->lang['FORUMS_MARKED'] . '<br /><br />' . $message); } else diff --git a/phpBB/includes/functions_messenger.php b/phpBB/includes/functions_messenger.php index f4e49b1b18..e9073553d0 100644 --- a/phpBB/includes/functions_messenger.php +++ b/phpBB/includes/functions_messenger.php @@ -201,17 +201,19 @@ class messenger { // fall back to board default language if the user's language is // missing $template_file. If this does not exist either, - // $tpl->set_custom_template will do a trigger_error + // $tpl->set_filenames will do a trigger_error $template_lang = basename($config['default_lang']); } // tpl_msg now holds a template object we can use to parse the template file if (!isset($this->tpl_msg[$template_lang . $template_file])) { - $template_locator = new phpbb_template_locator(); - $template_path_provider = new phpbb_template_extension_path_provider($phpbb_extension_manager, new phpbb_template_path_provider()); - $this->tpl_msg[$template_lang . $template_file] = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $template_locator, $template_path_provider); - $tpl = &$this->tpl_msg[$template_lang . $template_file]; + $style_resource_locator = new phpbb_style_resource_locator(); + $style_path_provider = new phpbb_style_extension_path_provider($phpbb_extension_manager, new phpbb_style_path_provider()); + $tpl = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator); + $style = new phpbb_style($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, $style_path_provider, $tpl); + + $this->tpl_msg[$template_lang . $template_file] = $tpl; $fallback_template_path = false; @@ -229,7 +231,7 @@ class messenger } } - $tpl->set_custom_template($template_path, $template_lang . '_email', $fallback_template_path); + $style->set_custom_style($template_lang . '_email', array($template_path, $fallback_template_path), ''); $tpl->set_filenames(array( 'body' => $template_file . '.txt', diff --git a/phpBB/includes/functions_module.php b/phpBB/includes/functions_module.php index db7defdc48..ad76be9f2f 100644 --- a/phpBB/includes/functions_module.php +++ b/phpBB/includes/functions_module.php @@ -128,7 +128,7 @@ class p_master foreach ($this->module_cache['modules'] as $key => $row) { // Not allowed to view module? - if (!$this->module_auth($row['module_auth'])) + if (!$this->module_auth_self($row['module_auth'])) { unset($this->module_cache['modules'][$key]); continue; @@ -315,9 +315,23 @@ class p_master } /** - * Check module authorisation + * Check module authorisation. + * + * This is a non-static version that uses $this->acl_forum_id + * for the forum id. + */ + function module_auth_self($module_auth) + { + return self::module_auth($module_auth, $this->acl_forum_id); + } + + /** + * Check module authorisation. + * + * This is a static version, it must be given $forum_id. + * See also module_auth_self. */ - function module_auth($module_auth, $forum_id = false) + static function module_auth($module_auth, $forum_id) { global $auth, $config; global $request; @@ -362,11 +376,9 @@ class p_master $module_auth = implode(' ', $tokens); - // Make sure $id seperation is working fine + // Make sure $id separation is working fine $module_auth = str_replace(' , ', ',', $module_auth); - $forum_id = ($forum_id === false) ? $this->acl_forum_id : $forum_id; - $is_auth = false; eval('$is_auth = (int) (' . preg_replace(array('#acl_([a-z0-9_]+)(,\$id)?#', '#\$id#', '#aclf_([a-z0-9_]+)#', '#cfg_([a-z0-9_]+)#', '#request_([a-zA-Z0-9_]+)#'), array('(int) $auth->acl_get(\'\\1\'\\2)', '(int) $forum_id', '(int) $auth->acl_getf_global(\'\\1\')', '(int) $config[\'\\1\']', '$request->variable(\'\\1\', false)'), $module_auth) . ');'); diff --git a/phpBB/includes/functions_posting.php b/phpBB/includes/functions_posting.php index 4e4ce5bca7..f77f54679f 100644 --- a/phpBB/includes/functions_posting.php +++ b/phpBB/includes/functions_posting.php @@ -496,7 +496,14 @@ function upload_attachment($form_name, $forum_id, $local = false, $local_storage { if ($free_space <= $file->get('filesize')) { - $filedata['error'][] = $user->lang['ATTACH_QUOTA_REACHED']; + if ($auth->acl_get('a_')) + { + $filedata['error'][] = $user->lang['ATTACH_DISK_FULL']; + } + else + { + $filedata['error'][] = $user->lang['ATTACH_QUOTA_REACHED']; + } $filedata['post_attach'] = false; $file->remove(); @@ -1180,36 +1187,32 @@ function user_notification($mode, $subject, $topic_title, $forum_name, $forum_id $topic_title = ($topic_notification) ? $topic_title : $subject; $topic_title = censor_text($topic_title); - // Get banned User ID's - $sql = 'SELECT ban_userid - FROM ' . BANLIST_TABLE . ' - WHERE ban_userid <> 0 - AND ban_exclude <> 1'; - $result = $db->sql_query($sql); - - $sql_ignore_users = ANONYMOUS . ', ' . $user->data['user_id']; - while ($row = $db->sql_fetchrow($result)) + // Exclude guests, current user and banned users from notifications + if (!function_exists('phpbb_get_banned_user_ids')) { - $sql_ignore_users .= ', ' . (int) $row['ban_userid']; + include($phpbb_root_path . 'includes/functions_user.' . $phpEx); } - $db->sql_freeresult($result); + $sql_ignore_users = phpbb_get_banned_user_ids(); + $sql_ignore_users[ANONYMOUS] = ANONYMOUS; + $sql_ignore_users[$user->data['user_id']] = $user->data['user_id']; $notify_rows = array(); // -- get forum_userids || topic_userids $sql = 'SELECT u.user_id, u.username, u.user_email, u.user_lang, u.user_notify_type, u.user_jabber FROM ' . (($topic_notification) ? TOPICS_WATCH_TABLE : FORUMS_WATCH_TABLE) . ' w, ' . USERS_TABLE . ' u - WHERE w.' . (($topic_notification) ? 'topic_id' : 'forum_id') . ' = ' . (($topic_notification) ? $topic_id : $forum_id) . " - AND w.user_id NOT IN ($sql_ignore_users) - AND w.notify_status = " . NOTIFY_YES . ' + WHERE w.' . (($topic_notification) ? 'topic_id' : 'forum_id') . ' = ' . (($topic_notification) ? $topic_id : $forum_id) . ' + AND ' . $db->sql_in_set('w.user_id', $sql_ignore_users, true) . ' + AND w.notify_status = ' . NOTIFY_YES . ' AND u.user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ') AND u.user_id = w.user_id'; $result = $db->sql_query($sql); while ($row = $db->sql_fetchrow($result)) { - $notify_rows[$row['user_id']] = array( - 'user_id' => $row['user_id'], + $notify_user_id = (int) $row['user_id']; + $notify_rows[$notify_user_id] = array( + 'user_id' => $notify_user_id, 'username' => $row['username'], 'user_email' => $row['user_email'], 'user_jabber' => $row['user_jabber'], @@ -1219,30 +1222,29 @@ function user_notification($mode, $subject, $topic_title, $forum_name, $forum_id 'method' => $row['user_notify_type'], 'allowed' => false ); + + // Add users who have been already notified to ignore list + $sql_ignore_users[$notify_user_id] = $notify_user_id; } $db->sql_freeresult($result); // forum notification is sent to those not already receiving topic notifications if ($topic_notification) { - if (sizeof($notify_rows)) - { - $sql_ignore_users .= ', ' . implode(', ', array_keys($notify_rows)); - } - $sql = 'SELECT u.user_id, u.username, u.user_email, u.user_lang, u.user_notify_type, u.user_jabber FROM ' . FORUMS_WATCH_TABLE . ' fw, ' . USERS_TABLE . " u WHERE fw.forum_id = $forum_id - AND fw.user_id NOT IN ($sql_ignore_users) - AND fw.notify_status = " . NOTIFY_YES . ' + AND " . $db->sql_in_set('fw.user_id', $sql_ignore_users, true) . ' + AND fw.notify_status = ' . NOTIFY_YES . ' AND u.user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ') AND u.user_id = fw.user_id'; $result = $db->sql_query($sql); while ($row = $db->sql_fetchrow($result)) { - $notify_rows[$row['user_id']] = array( - 'user_id' => $row['user_id'], + $notify_user_id = (int) $row['user_id']; + $notify_rows[$notify_user_id] = array( + 'user_id' => $notify_user_id, 'username' => $row['username'], 'user_email' => $row['user_email'], 'user_jabber' => $row['user_jabber'], @@ -1273,7 +1275,6 @@ function user_notification($mode, $subject, $topic_title, $forum_name, $forum_id } } - // Now, we have to do a little step before really sending, we need to distinguish our users a little bit. ;) $msg_users = $delete_ids = $update_notification = array(); foreach ($notify_rows as $user_id => $row) diff --git a/phpBB/includes/functions_privmsgs.php b/phpBB/includes/functions_privmsgs.php index 91e453b8e0..434349714b 100644 --- a/phpBB/includes/functions_privmsgs.php +++ b/phpBB/includes/functions_privmsgs.php @@ -343,7 +343,7 @@ function check_rule(&$rules, &$rule_row, &$message_row, $user_id) $userdata = $db->sql_fetchrow($result); $db->sql_freeresult($result); - $auth2 = new auth(); + $auth2 = new phpbb_auth(); $auth2->acl($userdata); if (!$auth2->acl_get('a_') && !$auth2->acl_get('m_') && !$auth2->acl_getf_global('m_')) @@ -1666,6 +1666,7 @@ function pm_notification($mode, $author, $recipients, $subject, $message, $msg_i $subject = censor_text($subject); + // Exclude guests, current user and banned users from notifications unset($recipients[ANONYMOUS], $recipients[$user->data['user_id']]); if (!sizeof($recipients)) @@ -1673,18 +1674,12 @@ function pm_notification($mode, $author, $recipients, $subject, $message, $msg_i return; } - // Get banned User ID's - $sql = 'SELECT ban_userid - FROM ' . BANLIST_TABLE . ' - WHERE ' . $db->sql_in_set('ban_userid', array_map('intval', array_keys($recipients))) . ' - AND ban_exclude = 0'; - $result = $db->sql_query($sql); - - while ($row = $db->sql_fetchrow($result)) + if (!function_exists('phpbb_get_banned_user_ids')) { - unset($recipients[$row['ban_userid']]); + include($phpbb_root_path . 'includes/functions_user.' . $phpEx); } - $db->sql_freeresult($result); + $banned_users = phpbb_get_banned_user_ids(array_keys($recipients)); + $recipients = array_diff(array_keys($recipients), $banned_users); if (!sizeof($recipients)) { @@ -1693,7 +1688,7 @@ function pm_notification($mode, $author, $recipients, $subject, $message, $msg_i $sql = 'SELECT user_id, username, user_email, user_lang, user_notify_pm, user_notify_type, user_jabber FROM ' . USERS_TABLE . ' - WHERE ' . $db->sql_in_set('user_id', array_map('intval', array_keys($recipients))); + WHERE ' . $db->sql_in_set('user_id', $recipients); $result = $db->sql_query($sql); $msg_list_ary = array(); diff --git a/phpBB/includes/functions_profile_fields.php b/phpBB/includes/functions_profile_fields.php index 34d973b3a6..3399334f94 100644 --- a/phpBB/includes/functions_profile_fields.php +++ b/phpBB/includes/functions_profile_fields.php @@ -555,7 +555,7 @@ class custom_profile { global $user; // Date should display as the same date for every user regardless of timezone, so remove offset - // to compensate for the offset added by user::format_date() + // to compensate for the offset added by phpbb_user::format_date() return $user->format_date(gmmktime(0, 0, 0, $month, $day, $year) - ($user->timezone + $user->dst), $user->lang['DATE_FORMAT'], true); } diff --git a/phpBB/includes/functions_user.php b/phpBB/includes/functions_user.php index 509e1a953c..5b05c3a78d 100644 --- a/phpBB/includes/functions_user.php +++ b/phpBB/includes/functions_user.php @@ -1948,6 +1948,27 @@ function validate_jabber($jid) } /** +* Verifies whether a style ID corresponds to an active style. +* +* @param int $style_id The style_id of a style which should be checked if activated or not. +* @return boolean +*/ +function phpbb_style_is_active($style_id) +{ + global $db; + + $sql = 'SELECT style_active + FROM ' . STYLES_TABLE . ' + WHERE style_id = '. (int) $style_id; + $result = $db->sql_query($sql); + + $style_is_active = (bool) $db->sql_fetchfield('style_active'); + $db->sql_freeresult($result); + + return $style_is_active; +} + +/** * Remove avatar */ function avatar_delete($mode, $row, $clean_db = false) @@ -3691,3 +3712,36 @@ function remove_newly_registered($user_id, $user_data = false) return $user_data['group_id']; } + +/** +* Gets user ids of currently banned registered users. +* +* @param array $user_ids Array of users' ids to check for banning, +* leave empty to get complete list of banned ids +* @return array Array of banned users' ids if any, empty array otherwise +*/ +function phpbb_get_banned_user_ids($user_ids = array()) +{ + global $db; + + $sql_user_ids = (!empty($user_ids)) ? $db->sql_in_set('ban_userid', $user_ids) : 'ban_userid <> 0'; + + // Get banned User ID's + // Ignore stale bans which were not wiped yet + $banned_ids_list = array(); + $sql = 'SELECT ban_userid + FROM ' . BANLIST_TABLE . " + WHERE $sql_user_ids + AND ban_exclude <> 1 + AND (ban_end > " . time() . ' + OR ban_end = 0)'; + $result = $db->sql_query($sql); + while ($row = $db->sql_fetchrow($result)) + { + $user_id = (int) $row['ban_userid']; + $banned_ids_list[$user_id] = $user_id; + } + $db->sql_freeresult($result); + + return $banned_ids_list; +} diff --git a/phpBB/includes/json_response.php b/phpBB/includes/json_response.php new file mode 100644 index 0000000000..5dd904da09 --- /dev/null +++ b/phpBB/includes/json_response.php @@ -0,0 +1,41 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2011 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + +/** +* JSON class +* @package phpBB3 +*/ +class phpbb_json_response +{ + /** + * Send the data to the client and exit the script. + * + * @param array $data Any additional data to send. + * @param bool $exit Will exit the script if true. + */ + public function send($data, $exit = true) + { + header('Content-Type: application/json'); + echo json_encode($data); + + if ($exit) + { + garbage_collection(); + exit_handler(); + } + } +} diff --git a/phpBB/includes/mcp/mcp_queue.php b/phpBB/includes/mcp/mcp_queue.php index ef32b5f03c..59fa8b7263 100644 --- a/phpBB/includes/mcp/mcp_queue.php +++ b/phpBB/includes/mcp/mcp_queue.php @@ -451,6 +451,7 @@ function approve_post($post_id_list, $id, $mode) { global $db, $template, $user, $config; global $phpEx, $phpbb_root_path; + global $request; if (!check_ids($post_id_list, POSTS_TABLE, 'post_id', array('m_approve'))) { @@ -709,7 +710,20 @@ function approve_post($post_id_list, $id, $mode) $add_message = '<br /><br />' . sprintf($user->lang['RETURN_POST'], '<a href="' . $post_url . '">', '</a>'); } - trigger_error($user->lang[$success_msg] . '<br /><br />' . sprintf($user->lang['RETURN_PAGE'], "<a href=\"$redirect\">", '</a>') . $add_message); + $message = $user->lang[$success_msg] . '<br /><br />' . sprintf($user->lang['RETURN_PAGE'], "<a href=\"$redirect\">", '</a>') . $add_message; + + if ($request->is_ajax()) + { + $json_response = new phpbb_json_response; + $json_response->send(array( + 'MESSAGE_TITLE' => $user->lang['INFORMATION'], + 'MESSAGE_TEXT' => $message, + 'REFRESH_DATA' => null, + 'approved' => true + )); + } + + trigger_error($message); } } @@ -968,7 +982,20 @@ function disapprove_post($post_id_list, $id, $mode) } else { + $message = $user->lang[$success_msg] . '<br /><br />' . sprintf($user->lang['RETURN_PAGE'], "<a href=\"$redirect\">", '</a>'); + + if ($request->is_ajax()) + { + $json_response = new phpbb_json_response; + $json_response->send(array( + 'MESSAGE_TITLE' => $user->lang['INFORMATION'], + 'MESSAGE_TEXT' => $message, + 'REFRESH_DATA' => null, + 'approved' => false + )); + } + meta_refresh(3, $redirect); - trigger_error($user->lang[$success_msg] . '<br /><br />' . sprintf($user->lang['RETURN_PAGE'], "<a href=\"$redirect\">", '</a>')); + trigger_error($message); } } diff --git a/phpBB/includes/mcp/mcp_reports.php b/phpBB/includes/mcp/mcp_reports.php index 95e84e816b..69c6a4cfff 100644 --- a/phpBB/includes/mcp/mcp_reports.php +++ b/phpBB/includes/mcp/mcp_reports.php @@ -71,7 +71,7 @@ class mcp_reports // closed reports are accessed by report id $report_id = request_var('r', 0); - $sql = 'SELECT r.post_id, r.user_id, r.report_id, r.report_closed, report_time, r.report_text, rr.reason_title, rr.reason_description, u.username, u.username_clean, u.user_colour + $sql = 'SELECT r.post_id, r.user_id, r.report_id, r.report_closed, report_time, r.report_text, r.reported_post_text, rr.reason_title, rr.reason_description, u.username, u.username_clean, u.user_colour FROM ' . REPORTS_TABLE . ' r, ' . REPORTS_REASONS_TABLE . ' rr, ' . USERS_TABLE . ' u WHERE ' . (($report_id) ? 'r.report_id = ' . $report_id : "r.post_id = $post_id") . ' AND rr.reason_id = r.reason_id @@ -116,8 +116,9 @@ class mcp_reports $template->assign_vars(array( 'S_TOPIC_REVIEW' => true, 'S_BBCODE_ALLOWED' => $post_info['enable_bbcode'], - 'TOPIC_TITLE' => $post_info['topic_title']) - ); + 'TOPIC_TITLE' => $post_info['topic_title'], + 'REPORTED_POST_ID' => $post_id, + )); } $topic_tracking_info = $extensions = $attachments = array(); @@ -226,7 +227,7 @@ class mcp_reports 'REPORTER_NAME' => get_username_string('username', $report['user_id'], $report['username'], $report['user_colour']), 'U_VIEW_REPORTER_PROFILE' => get_username_string('profile', $report['user_id'], $report['username'], $report['user_colour']), - 'POST_PREVIEW' => $message, + 'POST_PREVIEW' => bbcode_nl2br($report['reported_post_text']), 'POST_SUBJECT' => ($post_info['post_subject']) ? $post_info['post_subject'] : $user->lang['NO_SUBJECT'], 'POST_DATE' => $user->format_date($post_info['post_time']), 'POST_IP' => $post_info['poster_ip'], diff --git a/phpBB/includes/mcp/mcp_warn.php b/phpBB/includes/mcp/mcp_warn.php index d8e655000f..c614beea3b 100644 --- a/phpBB/includes/mcp/mcp_warn.php +++ b/phpBB/includes/mcp/mcp_warn.php @@ -251,7 +251,7 @@ class mcp_warn // Check if can send a notification if ($config['allow_privmsg']) { - $auth2 = new auth(); + $auth2 = new phpbb_auth(); $auth2->acl($user_row); $s_can_notify = ($auth2->acl_get('u_readpm')) ? true : false; unset($auth2); @@ -307,7 +307,7 @@ class mcp_warn include($phpbb_root_path . 'includes/functions_display.' . $phpEx); } - $rank_title = $rank_img = ''; + get_user_rank($user_row['user_rank'], $user_row['user_posts'], $rank_title, $rank_img, $rank_img_src); $avatar_img = get_user_avatar($user_row['user_avatar'], $user_row['user_avatar_type'], $user_row['user_avatar_width'], $user_row['user_avatar_height']); $template->assign_vars(array( @@ -374,7 +374,7 @@ class mcp_warn // Check if can send a notification if ($config['allow_privmsg']) { - $auth2 = new auth(); + $auth2 = new phpbb_auth(); $auth2->acl($user_row); $s_can_notify = ($auth2->acl_get('u_readpm')) ? true : false; unset($auth2); @@ -412,7 +412,7 @@ class mcp_warn include($phpbb_root_path . 'includes/functions_display.' . $phpEx); } - $rank_title = $rank_img = ''; + get_user_rank($user_row['user_rank'], $user_row['user_posts'], $rank_title, $rank_img, $rank_img_src); $avatar_img = get_user_avatar($user_row['user_avatar'], $user_row['user_avatar_type'], $user_row['user_avatar_width'], $user_row['user_avatar_height']); // OK, they didn't submit a warning so lets build the page for them to do so diff --git a/phpBB/includes/session.php b/phpBB/includes/session.php index 6dad068ecb..bcdff54457 100644 --- a/phpBB/includes/session.php +++ b/phpBB/includes/session.php @@ -19,7 +19,7 @@ if (!defined('IN_PHPBB')) * Session class * @package phpBB3 */ -class session +class phpbb_session { var $cookie_data = array(); var $page = array(); @@ -1511,843 +1511,3 @@ class session $db->sql_query($sql); } } - - -/** -* Base user class -* -* This is the overarching class which contains (through session extend) -* all methods utilised for user functionality during a session. -* -* @package phpBB3 -*/ -class user extends session -{ - var $lang = array(); - var $help = array(); - var $theme = array(); - var $date_format; - var $timezone; - var $dst; - - var $lang_name = false; - var $lang_id = false; - var $lang_path; - var $img_lang; - var $img_array = array(); - - // Able to add new options (up to id 31) - var $keyoptions = array('viewimg' => 0, 'viewflash' => 1, 'viewsmilies' => 2, 'viewsigs' => 3, 'viewavatars' => 4, 'viewcensors' => 5, 'attachsig' => 6, 'bbcode' => 8, 'smilies' => 9, 'popuppm' => 10, 'sig_bbcode' => 15, 'sig_smilies' => 16, 'sig_links' => 17); - - /** - * Constructor to set the lang path - */ - function user() - { - global $phpbb_root_path; - - $this->lang_path = $phpbb_root_path . 'language/'; - } - - /** - * Function to set custom language path (able to use directory outside of phpBB) - * - * @param string $lang_path New language path used. - * @access public - */ - function set_custom_lang_path($lang_path) - { - $this->lang_path = $lang_path; - - if (substr($this->lang_path, -1) != '/') - { - $this->lang_path .= '/'; - } - } - - /** - * Setup basic user-specific items (style, language, ...) - */ - function setup($lang_set = false, $style = false) - { - global $db, $template, $config, $auth, $phpEx, $phpbb_root_path, $cache; - - if ($this->data['user_id'] != ANONYMOUS) - { - $this->lang_name = (file_exists($this->lang_path . $this->data['user_lang'] . "/common.$phpEx")) ? $this->data['user_lang'] : basename($config['default_lang']); - - $this->date_format = $this->data['user_dateformat']; - $this->timezone = $this->data['user_timezone'] * 3600; - $this->dst = $this->data['user_dst'] * 3600; - } - else - { - $this->lang_name = basename($config['default_lang']); - $this->date_format = $config['default_dateformat']; - $this->timezone = $config['board_timezone'] * 3600; - $this->dst = $config['board_dst'] * 3600; - - /** - * If a guest user is surfing, we try to guess his/her language first by obtaining the browser language - * If re-enabled we need to make sure only those languages installed are checked - * Commented out so we do not loose the code. - - if ($request->header('Accept-Language')) - { - $accept_lang_ary = explode(',', $request->header('Accept-Language')); - - foreach ($accept_lang_ary as $accept_lang) - { - // Set correct format ... guess full xx_YY form - $accept_lang = substr($accept_lang, 0, 2) . '_' . strtoupper(substr($accept_lang, 3, 2)); - $accept_lang = basename($accept_lang); - - if (file_exists($this->lang_path . $accept_lang . "/common.$phpEx")) - { - $this->lang_name = $config['default_lang'] = $accept_lang; - break; - } - else - { - // No match on xx_YY so try xx - $accept_lang = substr($accept_lang, 0, 2); - $accept_lang = basename($accept_lang); - - if (file_exists($this->lang_path . $accept_lang . "/common.$phpEx")) - { - $this->lang_name = $config['default_lang'] = $accept_lang; - break; - } - } - } - } - */ - } - - // We include common language file here to not load it every time a custom language file is included - $lang = &$this->lang; - - // Do not suppress error if in DEBUG_EXTRA mode - $include_result = (defined('DEBUG_EXTRA')) ? (include $this->lang_path . $this->lang_name . "/common.$phpEx") : (@include $this->lang_path . $this->lang_name . "/common.$phpEx"); - - if ($include_result === false) - { - die('Language file ' . $this->lang_path . $this->lang_name . "/common.$phpEx" . " couldn't be opened."); - } - - $this->add_lang($lang_set); - unset($lang_set); - - $style_request = request_var('style', 0); - if ($style_request && $auth->acl_get('a_styles') && !defined('ADMIN_START')) - { - global $SID, $_EXTRA_URL; - - $style = $style_request; - $SID .= '&style=' . $style; - $_EXTRA_URL = array('style=' . $style); - } - else - { - // Set up style - $style = ($style) ? $style : ((!$config['override_user_style']) ? $this->data['user_style'] : $config['default_style']); - } - - $sql = 'SELECT s.style_id, t.template_path, t.template_id, t.bbcode_bitfield, t.template_inherits_id, t.template_inherit_path, c.theme_path, c.theme_name, c.theme_id - FROM ' . STYLES_TABLE . ' s, ' . STYLES_TEMPLATE_TABLE . ' t, ' . STYLES_THEME_TABLE . " c - WHERE s.style_id = $style - AND t.template_id = s.template_id - AND c.theme_id = s.theme_id"; - $result = $db->sql_query($sql, 3600); - $this->theme = $db->sql_fetchrow($result); - $db->sql_freeresult($result); - - // User has wrong style - if (!$this->theme && $style == $this->data['user_style']) - { - $style = $this->data['user_style'] = $config['default_style']; - - $sql = 'UPDATE ' . USERS_TABLE . " - SET user_style = $style - WHERE user_id = {$this->data['user_id']}"; - $db->sql_query($sql); - - $sql = 'SELECT s.style_id, t.template_path, t.template_id, t.bbcode_bitfield, c.theme_path, c.theme_name, c.theme_id - FROM ' . STYLES_TABLE . ' s, ' . STYLES_TEMPLATE_TABLE . ' t, ' . STYLES_THEME_TABLE . " c - WHERE s.style_id = $style - AND t.template_id = s.template_id - AND c.theme_id = s.theme_id"; - $result = $db->sql_query($sql, 3600); - $this->theme = $db->sql_fetchrow($result); - $db->sql_freeresult($result); - } - - if (!$this->theme) - { - trigger_error('Could not get style data', E_USER_ERROR); - } - - // Now parse the cfg file and cache it - $parsed_items = $cache->obtain_cfg_items($this->theme); - - // We are only interested in the theme configuration for now - $parsed_items = $parsed_items['theme']; - - $check_for = array( - 'pagination_sep' => (string) ', ' - ); - - foreach ($check_for as $key => $default_value) - { - $this->theme[$key] = (isset($parsed_items[$key])) ? $parsed_items[$key] : $default_value; - settype($this->theme[$key], gettype($default_value)); - - if (is_string($default_value)) - { - $this->theme[$key] = htmlspecialchars($this->theme[$key]); - } - } - - $template->set_template(); - - $this->img_lang = $this->lang_name; - - // Call phpbb_user_session_handler() in case external application want to "bend" some variables or replace classes... - // After calling it we continue script execution... - phpbb_user_session_handler(); - - // If this function got called from the error handler we are finished here. - if (defined('IN_ERROR_HANDLER')) - { - return; - } - - // Disable board if the install/ directory is still present - // For the brave development army we do not care about this, else we need to comment out this everytime we develop locally - if (!defined('DEBUG_EXTRA') && !defined('ADMIN_START') && !defined('IN_INSTALL') && !defined('IN_LOGIN') && file_exists($phpbb_root_path . 'install') && !is_file($phpbb_root_path . 'install')) - { - // Adjust the message slightly according to the permissions - if ($auth->acl_gets('a_', 'm_') || $auth->acl_getf_global('m_')) - { - $message = 'REMOVE_INSTALL'; - } - else - { - $message = (!empty($config['board_disable_msg'])) ? $config['board_disable_msg'] : 'BOARD_DISABLE'; - } - trigger_error($message); - } - - // Is board disabled and user not an admin or moderator? - if ($config['board_disable'] && !defined('IN_LOGIN') && !$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_')) - { - if ($this->data['is_bot']) - { - send_status_line(503, 'Service Unavailable'); - } - - $message = (!empty($config['board_disable_msg'])) ? $config['board_disable_msg'] : 'BOARD_DISABLE'; - trigger_error($message); - } - - // Is load exceeded? - if ($config['limit_load'] && $this->load !== false) - { - if ($this->load > floatval($config['limit_load']) && !defined('IN_LOGIN') && !defined('IN_ADMIN')) - { - // Set board disabled to true to let the admins/mods get the proper notification - $config['board_disable'] = '1'; - - if (!$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_')) - { - if ($this->data['is_bot']) - { - send_status_line(503, 'Service Unavailable'); - } - trigger_error('BOARD_UNAVAILABLE'); - } - } - } - - if (isset($this->data['session_viewonline'])) - { - // Make sure the user is able to hide his session - if (!$this->data['session_viewonline']) - { - // Reset online status if not allowed to hide the session... - if (!$auth->acl_get('u_hideonline')) - { - $sql = 'UPDATE ' . SESSIONS_TABLE . ' - SET session_viewonline = 1 - WHERE session_user_id = ' . $this->data['user_id']; - $db->sql_query($sql); - $this->data['session_viewonline'] = 1; - } - } - else if (!$this->data['user_allow_viewonline']) - { - // the user wants to hide and is allowed to -> cloaking device on. - if ($auth->acl_get('u_hideonline')) - { - $sql = 'UPDATE ' . SESSIONS_TABLE . ' - SET session_viewonline = 0 - WHERE session_user_id = ' . $this->data['user_id']; - $db->sql_query($sql); - $this->data['session_viewonline'] = 0; - } - } - } - - - // Does the user need to change their password? If so, redirect to the - // ucp profile reg_details page ... of course do not redirect if we're already in the ucp - if (!defined('IN_ADMIN') && !defined('ADMIN_START') && $config['chg_passforce'] && !empty($this->data['is_registered']) && $auth->acl_get('u_chgpasswd') && $this->data['user_passchg'] < time() - ($config['chg_passforce'] * 86400)) - { - if (strpos($this->page['query_string'], 'mode=reg_details') === false && $this->page['page_name'] != "ucp.$phpEx") - { - redirect(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=profile&mode=reg_details')); - } - } - - return; - } - - /** - * More advanced language substitution - * Function to mimic sprintf() with the possibility of using phpBB's language system to substitute nullar/singular/plural forms. - * Params are the language key and the parameters to be substituted. - * This function/functionality is inspired by SHS` and Ashe. - * - * Example call: <samp>$user->lang('NUM_POSTS_IN_QUEUE', 1);</samp> - * - * If the first parameter is an array, the elements are used as keys and subkeys to get the language entry: - * Example: <samp>$user->lang(array('datetime', 'AGO'), 1)</samp> uses $user->lang['datetime']['AGO'] as language entry. - */ - function lang() - { - $args = func_get_args(); - $key = $args[0]; - - if (is_array($key)) - { - $lang = &$this->lang[array_shift($key)]; - - foreach ($key as $_key) - { - $lang = &$lang[$_key]; - } - } - else - { - $lang = &$this->lang[$key]; - } - - // Return if language string does not exist - if (!isset($lang) || (!is_string($lang) && !is_array($lang))) - { - return $key; - } - - // If the language entry is a string, we simply mimic sprintf() behaviour - if (is_string($lang)) - { - if (sizeof($args) == 1) - { - return $lang; - } - - // Replace key with language entry and simply pass along... - $args[0] = $lang; - return call_user_func_array('sprintf', $args); - } - else if (sizeof($lang) == 0) - { - // If the language entry is an empty array, we just return the language key - return $args[0]; - } - - // It is an array... now handle different nullar/singular/plural forms - $key_found = false; - - // We now get the first number passed and will select the key based upon this number - for ($i = 1, $num_args = sizeof($args); $i < $num_args; $i++) - { - if (is_int($args[$i]) || is_float($args[$i])) - { - if ($args[$i] == 0 && isset($lang[0])) - { - // We allow each translation using plural forms to specify a version for the case of 0 things, - // so that "0 users" may be displayed as "No users". - $key_found = 0; - break; - } - else - { - $use_plural_form = $this->get_plural_form($args[$i]); - if (isset($lang[$use_plural_form])) - { - // The key we should use exists, so we use it. - $key_found = $use_plural_form; - } - else - { - // If the key we need to use does not exist, we fall back to the previous one. - $numbers = array_keys($lang); - - foreach ($numbers as $num) - { - if ($num > $use_plural_form) - { - break; - } - - $key_found = $num; - } - } - break; - } - } - } - - // Ok, let's check if the key was found, else use the last entry (because it is mostly the plural form) - if ($key_found === false) - { - $numbers = array_keys($lang); - $key_found = end($numbers); - } - - // Use the language string we determined and pass it to sprintf() - $args[0] = $lang[$key_found]; - return call_user_func_array('sprintf', $args); - } - - /** - * Determine which plural form we should use. - * For some languages this is not as simple as for English. - * - * @param $number int|float The number we want to get the plural case for. Float numbers are floored. - * @param $force_rule mixed False to use the plural rule of the language package - * or an integer to force a certain plural rule - * @return int The plural-case we need to use for the number plural-rule combination - */ - function get_plural_form($number, $force_rule = false) - { - $number = (int) $number; - - // Default to English system - $plural_rule = ($force_rule !== false) ? $force_rule : ((isset($this->lang['PLURAL_RULE'])) ? $this->lang['PLURAL_RULE'] : 1); - - return phpbb_get_plural_form($plural_rule, $number); - } - - /** - * Add Language Items - use_db and use_help are assigned where needed (only use them to force inclusion) - * - * @param mixed $lang_set specifies the language entries to include - * @param bool $use_db internal variable for recursion, do not use - * @param bool $use_help internal variable for recursion, do not use - * @param string $ext_name The extension to load language from, or empty for core files - * - * Examples: - * <code> - * $lang_set = array('posting', 'help' => 'faq'); - * $lang_set = array('posting', 'viewtopic', 'help' => array('bbcode', 'faq')) - * $lang_set = array(array('posting', 'viewtopic'), 'help' => array('bbcode', 'faq')) - * $lang_set = 'posting' - * $lang_set = array('help' => 'faq', 'db' => array('help:faq', 'posting')) - * </code> - */ - function add_lang($lang_set, $use_db = false, $use_help = false, $ext_name = '') - { - global $phpEx; - - if (is_array($lang_set)) - { - foreach ($lang_set as $key => $lang_file) - { - // Please do not delete this line. - // We have to force the type here, else [array] language inclusion will not work - $key = (string) $key; - - if ($key == 'db') - { - $this->add_lang($lang_file, true, $use_help, $ext_name); - } - else if ($key == 'help') - { - $this->add_lang($lang_file, $use_db, true, $ext_name); - } - else if (!is_array($lang_file)) - { - $this->set_lang($this->lang, $this->help, $lang_file, $use_db, $use_help, $ext_name); - } - else - { - $this->add_lang($lang_file, $use_db, $use_help, $ext_name); - } - } - unset($lang_set); - } - else if ($lang_set) - { - $this->set_lang($this->lang, $this->help, $lang_set, $use_db, $use_help, $ext_name); - } - } - - /** - * Add Language Items from an extension - use_db and use_help are assigned where needed (only use them to force inclusion) - * - * @param string $ext_name The extension to load language from, or empty for core files - * @param mixed $lang_set specifies the language entries to include - * @param bool $use_db internal variable for recursion, do not use - * @param bool $use_help internal variable for recursion, do not use - */ - function add_lang_ext($ext_name, $lang_set, $use_db = false, $use_help = false) - { - if ($ext_name === '/') - { - $ext_name = ''; - } - - $this->add_lang($lang_set, $use_db, $use_help, $ext_name); - } - - /** - * Set language entry (called by add_lang) - * @access private - */ - function set_lang(&$lang, &$help, $lang_file, $use_db = false, $use_help = false, $ext_name = '') - { - global $phpbb_root_path, $phpEx; - - // Make sure the language name is set (if the user setup did not happen it is not set) - if (!$this->lang_name) - { - global $config; - $this->lang_name = basename($config['default_lang']); - } - - // $lang == $this->lang - // $help == $this->help - // - add appropriate variables here, name them as they are used within the language file... - if (!$use_db) - { - if ($use_help && strpos($lang_file, '/') !== false) - { - $filename = dirname($lang_file) . '/help_' . basename($lang_file); - } - else - { - $filename = (($use_help) ? 'help_' : '') . $lang_file; - } - - if ($ext_name) - { - global $phpbb_extension_manager; - $ext_path = $phpbb_extension_manager->get_extension_path($ext_name, true); - - $lang_path = $ext_path . 'language/'; - } - else - { - $lang_path = $this->lang_path; - } - - if (strpos($phpbb_root_path . $filename, $lang_path . $this->lang_name . '/') === 0) - { - $language_filename = $phpbb_root_path . $filename; - } - else - { - $language_filename = $lang_path . $this->lang_name . '/' . $filename . '.' . $phpEx; - } - - if (!file_exists($language_filename)) - { - global $config; - - if ($this->lang_name == 'en') - { - // The user's selected language is missing the file, the board default's language is missing the file, and the file doesn't exist in /en. - $language_filename = str_replace($lang_path . 'en', $lang_path . $this->data['user_lang'], $language_filename); - trigger_error('Language file ' . $language_filename . ' couldn\'t be opened.', E_USER_ERROR); - } - else if ($this->lang_name == basename($config['default_lang'])) - { - // Fall back to the English Language - $this->lang_name = 'en'; - $this->set_lang($lang, $help, $lang_file, $use_db, $use_help, $ext_name); - } - else if ($this->lang_name == $this->data['user_lang']) - { - // Fall back to the board default language - $this->lang_name = basename($config['default_lang']); - $this->set_lang($lang, $help, $lang_file, $use_db, $use_help, $ext_name); - } - - // Reset the lang name - $this->lang_name = (file_exists($lang_path . $this->data['user_lang'] . "/common.$phpEx")) ? $this->data['user_lang'] : basename($config['default_lang']); - return; - } - - // Do not suppress error if in DEBUG_EXTRA mode - $include_result = (defined('DEBUG_EXTRA')) ? (include $language_filename) : (@include $language_filename); - - if ($include_result === false) - { - trigger_error('Language file ' . $language_filename . ' couldn\'t be opened.', E_USER_ERROR); - } - } - else if ($use_db) - { - // Get Database Language Strings - // Put them into $lang if nothing is prefixed, put them into $help if help: is prefixed - // For example: help:faq, posting - } - } - - /** - * Format user date - * - * @param int $gmepoch unix timestamp - * @param string $format date format in date() notation. | used to indicate relative dates, for example |d m Y|, h:i is translated to Today, h:i. - * @param bool $forcedate force non-relative date format. - * - * @return mixed translated date - */ - function format_date($gmepoch, $format = false, $forcedate = false) - { - static $midnight; - static $date_cache; - - $format = (!$format) ? $this->date_format : $format; - $now = time(); - $delta = $now - $gmepoch; - - if (!isset($date_cache[$format])) - { - // Is the user requesting a friendly date format (i.e. 'Today 12:42')? - $date_cache[$format] = array( - 'is_short' => strpos($format, '|'), - 'format_short' => substr($format, 0, strpos($format, '|')) . '||' . substr(strrchr($format, '|'), 1), - 'format_long' => str_replace('|', '', $format), - 'lang' => $this->lang['datetime'], - ); - - // Short representation of month in format? Some languages use different terms for the long and short format of May - if ((strpos($format, '\M') === false && strpos($format, 'M') !== false) || (strpos($format, '\r') === false && strpos($format, 'r') !== false)) - { - $date_cache[$format]['lang']['May'] = $this->lang['datetime']['May_short']; - } - } - - // Zone offset - $zone_offset = $this->timezone + $this->dst; - - // Show date < 1 hour ago as 'xx min ago' but not greater than 60 seconds in the future - // A small tolerence is given for times in the future but in the same minute are displayed as '< than a minute ago' - if ($delta < 3600 && $delta > -60 && ($delta >= -5 || (($now / 60) % 60) == (($gmepoch / 60) % 60)) && $date_cache[$format]['is_short'] !== false && !$forcedate && isset($this->lang['datetime']['AGO'])) - { - return $this->lang(array('datetime', 'AGO'), max(0, (int) floor($delta / 60))); - } - - if (!$midnight) - { - list($d, $m, $y) = explode(' ', gmdate('j n Y', time() + $zone_offset)); - $midnight = gmmktime(0, 0, 0, $m, $d, $y) - $zone_offset; - } - - if ($date_cache[$format]['is_short'] !== false && !$forcedate && !($gmepoch < $midnight - 86400 || $gmepoch > $midnight + 172800)) - { - $day = false; - - if ($gmepoch > $midnight + 86400) - { - $day = 'TOMORROW'; - } - else if ($gmepoch > $midnight) - { - $day = 'TODAY'; - } - else if ($gmepoch > $midnight - 86400) - { - $day = 'YESTERDAY'; - } - - if ($day !== false) - { - return str_replace('||', $this->lang['datetime'][$day], strtr(@gmdate($date_cache[$format]['format_short'], $gmepoch + $zone_offset), $date_cache[$format]['lang'])); - } - } - - return strtr(@gmdate($date_cache[$format]['format_long'], $gmepoch + $zone_offset), $date_cache[$format]['lang']); - } - - /** - * Get language id currently used by the user - */ - function get_iso_lang_id() - { - global $config, $db; - - if (!empty($this->lang_id)) - { - return $this->lang_id; - } - - if (!$this->lang_name) - { - $this->lang_name = $config['default_lang']; - } - - $sql = 'SELECT lang_id - FROM ' . LANG_TABLE . " - WHERE lang_iso = '" . $db->sql_escape($this->lang_name) . "'"; - $result = $db->sql_query($sql); - $this->lang_id = (int) $db->sql_fetchfield('lang_id'); - $db->sql_freeresult($result); - - return $this->lang_id; - } - - /** - * Get users profile fields - */ - function get_profile_fields($user_id) - { - global $db; - - if (isset($this->profile_fields)) - { - return; - } - - $sql = 'SELECT * - FROM ' . PROFILE_FIELDS_DATA_TABLE . " - WHERE user_id = $user_id"; - $result = $db->sql_query_limit($sql, 1); - $this->profile_fields = (!($row = $db->sql_fetchrow($result))) ? array() : $row; - $db->sql_freeresult($result); - } - - /** - * Specify/Get image - */ - function img($img, $alt = '') - { - $alt = (!empty($this->lang[$alt])) ? $this->lang[$alt] : $alt; - return '<span class="imageset ' . $img . '">' . $alt . '</span>'; - } - - /** - * Get option bit field from user options. - * - * @param int $key option key, as defined in $keyoptions property. - * @param int $data bit field value to use, or false to use $this->data['user_options'] - * @return bool true if the option is set in the bit field, false otherwise - */ - function optionget($key, $data = false) - { - $var = ($data !== false) ? $data : $this->data['user_options']; - return phpbb_optionget($this->keyoptions[$key], $var); - } - - /** - * Set option bit field for user options. - * - * @param int $key Option key, as defined in $keyoptions property. - * @param bool $value True to set the option, false to clear the option. - * @param int $data Current bit field value, or false to use $this->data['user_options'] - * @return int|bool If $data is false, the bit field is modified and - * written back to $this->data['user_options'], and - * return value is true if the bit field changed and - * false otherwise. If $data is not false, the new - * bitfield value is returned. - */ - function optionset($key, $value, $data = false) - { - $var = ($data !== false) ? $data : $this->data['user_options']; - - $new_var = phpbb_optionset($this->keyoptions[$key], $value, $var); - - if ($data === false) - { - if ($new_var != $var) - { - $this->data['user_options'] = $new_var; - return true; - } - else - { - return false; - } - } - else - { - return $new_var; - } - } - - /** - * Funtion to make the user leave the NEWLY_REGISTERED system group. - * @access public - */ - function leave_newly_registered() - { - global $db; - - if (empty($this->data['user_new'])) - { - return false; - } - - if (!function_exists('remove_newly_registered')) - { - global $phpbb_root_path, $phpEx; - - include($phpbb_root_path . 'includes/functions_user.' . $phpEx); - } - if ($group = remove_newly_registered($this->data['user_id'], $this->data)) - { - $this->data['group_id'] = $group; - - } - $this->data['user_permissions'] = ''; - $this->data['user_new'] = 0; - - return true; - } - - /** - * Returns all password protected forum ids the user is currently NOT authenticated for. - * - * @return array Array of forum ids - * @access public - */ - function get_passworded_forums() - { - global $db; - - $sql = 'SELECT f.forum_id, fa.user_id - FROM ' . FORUMS_TABLE . ' f - LEFT JOIN ' . FORUMS_ACCESS_TABLE . " fa - ON (fa.forum_id = f.forum_id - AND fa.session_id = '" . $db->sql_escape($this->session_id) . "') - WHERE f.forum_password <> ''"; - $result = $db->sql_query($sql); - - $forum_ids = array(); - while ($row = $db->sql_fetchrow($result)) - { - $forum_id = (int) $row['forum_id']; - - if ($row['user_id'] != $this->data['user_id']) - { - $forum_ids[$forum_id] = $forum_id; - } - } - $db->sql_freeresult($result); - - return $forum_ids; - } -} diff --git a/phpBB/includes/startup.php b/phpBB/includes/startup.php index fc45cd882b..f75d70e366 100644 --- a/phpBB/includes/startup.php +++ b/phpBB/includes/startup.php @@ -147,5 +147,36 @@ if (function_exists('date_default_timezone_set') && function_exists('date_defaul date_default_timezone_set(@date_default_timezone_get()); } +// Autoloading of dependencies. +// Three options are supported: +// 1. If dependencies are installed with Composer, Composer will create a +// vendor/.composer/autoload.php. If this file exists it will be +// automatically used by phpBB. This is the default mode that phpBB +// will use when shipped. +// 2. To disable composer autoloading, PHPBB_NO_COMPOSER_AUTOLOAD can be specified. +// Additionally specify PHPBB_AUTOLOAD=/path/to/autoload.php in the +// environment. This is useful for running CLI scripts and tests. +// /path/to/autoload.php should define and register class loaders +// for all of phpBB's dependencies. +// 3. You can also set PHPBB_NO_COMPOSER_AUTOLOAD without setting PHPBB_AUTOLOAD. +// In this case autoloading needs to be defined before running any phpBB +// script. This might be useful in cases when phpBB is integrated into a +// larger program. +if (getenv('PHPBB_NO_COMPOSER_AUTOLOAD')) +{ + if (getenv('PHPBB_AUTOLOAD')) + { + require(getenv('PHPBB_AUTOLOAD')); + } +} +else +{ + if (!file_exists($phpbb_root_path . 'vendor/.composer/autoload.php')) + { + trigger_error('You have not set up composer dependencies. See http://getcomposer.org/.', E_USER_ERROR); + } + require($phpbb_root_path . 'vendor/.composer/autoload.php'); +} + $starttime = explode(' ', microtime()); $starttime = $starttime[1] + $starttime[0]; diff --git a/phpBB/includes/style/extension_path_provider.php b/phpBB/includes/style/extension_path_provider.php new file mode 100644 index 0000000000..4eac300424 --- /dev/null +++ b/phpBB/includes/style/extension_path_provider.php @@ -0,0 +1,119 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2011 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + +/** +* Provides a style resource locator with core style paths and extension style paths +* +* Finds installed style paths and makes them available to the resource locator. +* +* @package phpBB3 +*/ +class phpbb_style_extension_path_provider extends phpbb_extension_provider implements phpbb_style_path_provider_interface +{ + /** + * Optional prefix for style paths searched within extensions. + * + * Empty by default. Relative to the extension directory. As an example, it + * could be adm/ for admin style. + * + * @var string + */ + protected $ext_dir_prefix = ''; + + /** + * A provider of paths to be searched for styles + * @var phpbb_style_path_provider + */ + protected $base_path_provider; + + /** + * Constructor stores extension manager + * + * @param phpbb_extension_manager $extension_manager phpBB extension manager + * @param phpbb_style_path_provider $base_path_provider A simple path provider + * to provide paths to be located in extensions + */ + public function __construct(phpbb_extension_manager $extension_manager, phpbb_style_path_provider $base_path_provider) + { + parent::__construct($extension_manager); + $this->base_path_provider = $base_path_provider; + } + + /** + * Sets a prefix for style paths searched within extensions. + * + * The prefix is inserted between the extension's path e.g. ext/foo/ and + * the looked up style path, e.g. styles/bar/. So it should not have a + * leading slash, but should have a trailing slash. + * + * @param string $ext_dir_prefix The prefix including trailing slash + * @return null + */ + public function set_ext_dir_prefix($ext_dir_prefix) + { + $this->ext_dir_prefix = $ext_dir_prefix; + } + + /** + * Finds style paths using the extension manager + * + * Locates a path (e.g. styles/prosilver/) in all active extensions. + * Then appends the core style paths based in the current working + * directory. + * + * @return array List of style paths + */ + public function find() + { + $directories = array(); + + $finder = $this->extension_manager->get_finder(); + foreach ($this->base_path_provider as $key => $paths) + { + if ($key == 'style') + { + foreach ($paths as $path) + { + $directories['style'][] = $path; + if ($path && !phpbb_is_absolute($path)) + { + $result = $finder->directory('/' . $this->ext_dir_prefix . $path) + ->get_directories(true, true); + foreach ($result as $ext => $ext_path) + { + $directories[$ext][] = $ext_path; + } + } + } + } + } + + return $directories; + } + + /** + * Overwrites the current style paths + * + * @param array $styles An array of style paths. The first element is the main style. + * @return null + */ + public function set_styles(array $styles) + { + $this->base_path_provider->set_styles($styles); + $this->items = null; + } +} diff --git a/phpBB/includes/style/path_provider.php b/phpBB/includes/style/path_provider.php new file mode 100644 index 0000000000..731d682e88 --- /dev/null +++ b/phpBB/includes/style/path_provider.php @@ -0,0 +1,62 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2011 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + +/** +* Provides a style resource locator with paths +* +* Finds installed style paths and makes them available to the resource locator. +* +* @package phpBB3 +*/ +class phpbb_style_path_provider implements IteratorAggregate, phpbb_style_path_provider_interface +{ + protected $paths = array(); + + /** + * Ignores the extension dir prefix + * + * @param string $ext_dir_prefix The prefix including trailing slash + * @return null + */ + public function set_ext_dir_prefix($ext_dir_prefix) + { + } + + /** + * Overwrites the current style paths + * + * The first element of the passed styles map, is considered the main + * style. + * + * @param array $styles An array of style paths. The first element is the main style. + * @return null + */ + public function set_styles(array $styles) + { + $this->paths = array('style' => $styles); + } + + /** + * Retrieve an iterator over all style paths + * + * @return ArrayIterator An iterator for the array of style paths + */ + public function getIterator() + { + return new ArrayIterator($this->paths); + } +} diff --git a/phpBB/includes/style/path_provider_interface.php b/phpBB/includes/style/path_provider_interface.php new file mode 100644 index 0000000000..1a6153a4d3 --- /dev/null +++ b/phpBB/includes/style/path_provider_interface.php @@ -0,0 +1,42 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2011 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + +/** +* Provides a style resource locator with paths +* +* Finds installed style paths and makes them available to the resource locator. +* +* @package phpBB3 +*/ +interface phpbb_style_path_provider_interface extends Traversable +{ + /** + * Defines a prefix to use for style paths in extensions + * + * @param string $ext_dir_prefix The prefix including trailing slash + * @return null + */ + public function set_ext_dir_prefix($ext_dir_prefix); + + /** + * Overwrites the current style paths + * + * @param array $styles An array of style paths. The first element is the main style. + * @return null + */ + public function set_styles(array $styles); +} diff --git a/phpBB/includes/style/resource_locator.php b/phpBB/includes/style/resource_locator.php new file mode 100644 index 0000000000..fafa11c352 --- /dev/null +++ b/phpBB/includes/style/resource_locator.php @@ -0,0 +1,291 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2011 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + + +/** +* Style resource locator. +* Maintains mapping from template handles to source template file paths. +* Locates style files: resources (such as .js and .css files) and templates. +* +* Style resource locator is aware of styles tree, and can return actual +* filesystem paths (i.e., the "child" style or the "parent" styles) +* depending on what files exist. +* +* Root paths stored in locator are paths to style directories. Templates are +* stored in subdirectory that $template_path points to. +* +* @package phpBB3 +*/ +class phpbb_style_resource_locator implements phpbb_template_locator +{ + /** + * Paths to style directories. + * @var array + */ + private $roots = array(); + + /** + * Location of templates directory within style directories. + * Must have trailing slash. Empty if templates are stored in root + * style directory, such as admin control panel templates. + * @var string + */ + public $template_path = 'template/'; + + /** + * Map from root index to handles to source template file paths. + * Normally it only contains paths for handles that are used + * (or are likely to be used) by the page being rendered and not + * all templates that exist on the filesystem. + * @var array + */ + private $files = array(); + + /** + * Map from handles to source template file names. + * Covers the same data as $files property but maps to basenames + * instead of paths. + * @var array + */ + private $filenames = array(); + + /** + * Sets the list of style paths + * + * These paths will be searched for style files in the provided order. + * Paths may be outside of phpBB, but templates loaded from these paths + * will still be cached. + * + * @param array $style_paths An array of paths to style directories + * @return null + */ + public function set_paths($style_paths) + { + $this->roots = array(); + $this->files = array(); + $this->filenames = array(); + + foreach ($style_paths as $key => $paths) + { + foreach ($paths as $path) + { + // Make sure $path has no ending slash + if (substr($path, -1) === '/') + { + $path = substr($path, 0, -1); + } + $this->roots[$key][] = $path; + } + } + } + + /** + * Sets the template filenames for handles. $filename_array + * should be a hash of handle => filename pairs. + * + * @param array $filname_array Should be a hash of handle => filename pairs. + */ + public function set_filenames(array $filename_array) + { + foreach ($filename_array as $handle => $filename) + { + if (empty($filename)) + { + trigger_error("style resource locator: set_filenames: Empty filename specified for $handle", E_USER_ERROR); + } + + $this->filename[$handle] = $filename; + + foreach ($this->roots as $root_key => $root_paths) + { + foreach ($root_paths as $root_index => $root) + { + $this->files[$root_key][$root_index][$handle] = $root . '/' . $this->template_path . $filename; + } + } + } + } + + /** + * Determines the filename for a template handle. + * + * The filename comes from array used in a set_filenames call, + * which should have been performed prior to invoking this function. + * Return value is a file basename (without path). + * + * @param $handle string Template handle + * @return string Filename corresponding to the template handle + */ + public function get_filename_for_handle($handle) + { + if (!isset($this->filename[$handle])) + { + trigger_error("style resource locator: get_filename_for_handle: No file specified for handle $handle", E_USER_ERROR); + } + return $this->filename[$handle]; + } + + /** + * Determines the source file path for a template handle without + * regard for styles tree. + * + * This function returns the path in "primary" style directory + * corresponding to the given template handle. That path may or + * may not actually exist on the filesystem. Because this function + * does not perform stat calls to determine whether the path it + * returns actually exists, it is faster than get_source_file_for_handle. + * + * Use get_source_file_for_handle to obtain the actual path that is + * guaranteed to exist (which might come from the parent style + * directory if primary style has parent styles). + * + * This function will trigger an error if the handle was never + * associated with a template file via set_filenames. + * + * @param $handle string Template handle + * @return string Path to source file path in primary style directory + */ + public function get_virtual_source_file_for_handle($handle) + { + // If we don't have a file assigned to this handle, die. + if (!isset($this->files['style'][0][$handle])) + { + trigger_error("style resource locator: No file specified for handle $handle", E_USER_ERROR); + } + + $source_file = $this->files['style'][0][$handle]; + return $source_file; + } + + /** + * Determines the source file path for a template handle, accounting + * for styles tree and verifying that the path exists. + * + * This function returns the actual path that may be compiled for + * the specified template handle. It will trigger an error if + * the template handle was never associated with a template path + * via set_filenames or if the template file does not exist on the + * filesystem. + * + * Use get_virtual_source_file_for_handle to just resolve a template + * handle to a path without any filesystem or styles tree checks. + * + * @param string $handle Template handle (i.e. "friendly" template name) + * @param bool $find_all If true, each root path will be checked and function + * will return array of files instead of string and will not + * trigger a error if template does not exist + * @return string Source file path + */ + public function get_source_file_for_handle($handle, $find_all = false) + { + // If we don't have a file assigned to this handle, die. + if (!isset($this->files['style'][0][$handle])) + { + trigger_error("style resource locator: No file specified for handle $handle", E_USER_ERROR); + } + + // locate a source file that exists + $source_file = $this->files['style'][0][$handle]; + $tried = $source_file; + $found = false; + $found_all = array(); + foreach ($this->roots as $root_key => $root_paths) + { + foreach ($root_paths as $root_index => $root) + { + $source_file = $this->files[$root_key][$root_index][$handle]; + $tried .= ', ' . $source_file; + if (file_exists($source_file)) + { + $found = true; + break; + } + } + if ($found) + { + if ($find_all) + { + $found_all[] = $source_file; + $found = false; + } + else + { + break; + } + } + } + + // search failed + if (!$found && !$find_all) + { + trigger_error("style resource locator: File for handle $handle does not exist. Could not find: $tried", E_USER_ERROR); + } + + return ($find_all) ? $found_all : $source_file; + } + + /** + * Locates source file path, accounting for styles tree and verifying that + * the path exists. + * + * Unlike previous functions, this function works without template handle + * and it can search for more than one file. If more than one file name is + * specified, it will return location of file that it finds first. + * + * @param array $files List of files to locate. + * @param bool $return_default Determines what to return if file does not + * exist. If true, function will return location where file is + * supposed to be. If false, function will return false. + * @param bool $return_full_path If true, function will return full path + * to file. If false, function will return file name. This + * parameter can be used to check which one of set of files + * is available. + * @return string or boolean Source file path if file exists or $return_default is + * true. False if file does not exist and $return_default is false + */ + public function get_first_file_location($files, $return_default = false, $return_full_path = true) + { + // set default value + $default_result = false; + + // check all available paths + foreach ($this->roots as $root_paths) + { + foreach ($root_paths as $path) + { + // check all files + foreach ($files as $filename) + { + $source_file = $path . '/' . $filename; + if (file_exists($source_file)) + { + return ($return_full_path) ? $source_file : $filename; + } + + // assign first file as result if $return_default is true + if ($return_default && $default_result === false) + { + $default_result = $source_file; + } + } + } + } + + // search failed + return $default_result; + } +} diff --git a/phpBB/includes/style/style.php b/phpBB/includes/style/style.php new file mode 100644 index 0000000000..3f470015f6 --- /dev/null +++ b/phpBB/includes/style/style.php @@ -0,0 +1,186 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2005 phpBB Group, sections (c) 2001 ispi of Lincoln Inc +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + +/** +* Base Style class. +* @package phpBB3 +*/ +class phpbb_style +{ + /** + * Template class. + * Handles everything related to templates. + * @var phpbb_template + */ + private $template; + + /** + * phpBB root path + * @var string + */ + private $phpbb_root_path; + + /** + * PHP file extension + * @var string + */ + private $phpEx; + + /** + * phpBB config instance + * @var phpbb_config + */ + private $config; + + /** + * Current user + * @var phpbb_user + */ + private $user; + + /** + * Style resource locator + * @var phpbb_style_resource_locator + */ + private $locator; + + /** + * Style path provider + * @var phpbb_style_path_provider + */ + private $provider; + + /** + * Constructor. + * + * @param string $phpbb_root_path phpBB root path + * @param user $user current user + * @param phpbb_style_resource_locator $locator style resource locator + * @param phpbb_style_path_provider $provider style path provider + * @param phpbb_template $template template + */ + public function __construct($phpbb_root_path, $phpEx, $config, $user, phpbb_style_resource_locator $locator, phpbb_style_path_provider_interface $provider, phpbb_template $template) + { + $this->phpbb_root_path = $phpbb_root_path; + $this->phpEx = $phpEx; + $this->config = $config; + $this->user = $user; + $this->locator = $locator; + $this->provider = $provider; + $this->template = $template; + } + + /** + * Set style location based on (current) user's chosen style. + */ + public function set_style() + { + $style_name = $this->user->theme['style_path']; + $style_dirs = ($this->user->theme['style_parent_id']) ? array_reverse(explode('/', $this->user->theme['style_parent_tree'])) : array(); + $paths = array($this->get_style_path($style_name)); + foreach ($style_dirs as $dir) + { + $paths[] = $this->get_style_path($dir); + } + + // Add 'all' path, used as last fallback path by hooks and extensions + $paths[] = $this->get_style_path('all'); + + return $this->set_custom_style($style_name, $paths); + } + + /** + * Set custom style location (able to use directory outside of phpBB). + * + * Note: Templates are still compiled to phpBB's cache directory. + * + * @param string $name Name of style, used for cache prefix. Examples: "admin", "prosilver" + * @param array or string $paths Array of style paths, relative to current root directory + * @param string $template_path Path to templates, relative to style directory. False if path should not be changed. + */ + public function set_custom_style($name, $paths, $template_path = false) + { + if (is_string($paths)) + { + $paths = array($paths); + } + + $this->provider->set_styles($paths); + $this->locator->set_paths($this->provider); + + $this->template->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $name) . '_'; + + $this->template->context = new phpbb_template_context(); + + if ($template_path !== false) + { + $this->template->template_path = $this->locator->template_path = $template_path; + } + + return true; + } + + /** + * Get location of style directory for specific style_path + * + * @param string $path Style path, such as "prosilver" + * @return string Path to style directory, relative to current path + */ + public function get_style_path($path) + { + return $this->phpbb_root_path . 'styles/' . $path; + } + + /** + * Defines a prefix to use for style paths in extensions + * + * @param string $ext_dir_prefix The prefix including trailing slash + * @return null + */ + public function set_ext_dir_prefix($ext_dir_prefix) + { + $this->provider->set_ext_dir_prefix($ext_dir_prefix); + } + + /** + * Locates source file path, accounting for styles tree and verifying that + * the path exists. + * + * @param string or array $files List of files to locate. If there is only + * one file, $files can be a string to make code easier to read. + * @param bool $return_default Determines what to return if file does not + * exist. If true, function will return location where file is + * supposed to be. If false, function will return false. + * @param bool $return_full_path If true, function will return full path + * to file. If false, function will return file name. This + * parameter can be used to check which one of set of files + * is available. + * @return string or boolean Source file path if file exists or $return_default is + * true. False if file does not exist and $return_default is false + */ + public function locate($files, $return_default = false, $return_full_path = true) + { + // convert string to array + if (is_string($files)) + { + $files = array($files); + } + + // use resource locator to find files + return $this->locator->get_first_file_location($files, $return_default, $return_full_path); + } +} diff --git a/phpBB/includes/template/compile.php b/phpBB/includes/template/compile.php index b0009421c5..82b301c1a2 100644 --- a/phpBB/includes/template/compile.php +++ b/phpBB/includes/template/compile.php @@ -26,20 +26,26 @@ stream_filter_register('phpbb_template', 'phpbb_template_filter'); class phpbb_template_compile { /** - * Whether <!-- PHP --> tags are allowed + * Array of parameters to forward to template filter * - * @var bool + * @var array */ - private $allow_php; + private $filter_params; /** * Constructor. * * @param bool @allow_php Whether PHP code will be allowed in templates (inline PHP code, PHP tag and INCLUDEPHP tag) + * @param phpbb_style_resource_locator $locator Resource locator + * @param string $phpbb_root_path Path to phpBB root directory */ - public function __construct($allow_php) + public function __construct($allow_php, $locator, $phpbb_root_path) { - $this->allow_php = $allow_php; + $this->filter_params = array( + 'allow_php' => $allow_php, + 'locator' => $locator, + 'phpbb_root_path' => $phpbb_root_path + ); } /** @@ -116,7 +122,7 @@ class phpbb_template_compile */ private function compile_stream_to_stream($source_stream, $dest_stream) { - stream_filter_append($source_stream, 'phpbb_template', null, array('allow_php' => $this->allow_php)); + stream_filter_append($source_stream, 'phpbb_template', null, $this->filter_params); stream_copy_to_stream($source_stream, $dest_stream); } } diff --git a/phpBB/includes/template/extension_path_provider.php b/phpBB/includes/template/extension_path_provider.php deleted file mode 100644 index 2897f60ac1..0000000000 --- a/phpBB/includes/template/extension_path_provider.php +++ /dev/null @@ -1,130 +0,0 @@ -<?php -/** -* -* @package phpBB3 -* @copyright (c) 2011 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 -* -*/ - -/** -* @ignore -*/ -if (!defined('IN_PHPBB')) -{ - exit; -} - -/** -* Provides a template locator with core template paths and extension template paths -* -* Finds installed template paths and makes them available to the locator. -* -* @package phpBB3 -*/ -class phpbb_template_extension_path_provider extends phpbb_extension_provider implements phpbb_template_path_provider_interface -{ - /** - * Optional prefix for template paths searched within extensions. - * - * Empty by default. Relative to the extension directory. As an example, it - * could be adm/ for admin templates. - * - * @var string - */ - protected $ext_dir_prefix = ''; - - /** - * A provider of paths to be searched for templates - * @var phpbb_template_path_provider - */ - protected $base_path_provider; - - /** - * Constructor stores extension manager - * - * @param phpbb_extension_manager $extension_manager phpBB extension manager - * @param phpbb_template_path_provider $base_path_provider A simple path provider - * to provide paths to be located in extensions - */ - public function __construct(phpbb_extension_manager $extension_manager, phpbb_template_path_provider $base_path_provider) - { - parent::__construct($extension_manager); - $this->base_path_provider = $base_path_provider; - } - - /** - * Sets a prefix for template paths searched within extensions. - * - * The prefix is inserted between the extension's path e.g. ext/foo/ and - * the looked up template path, e.g. styles/bar/template/some.html. So it - * should not have a leading slash, but should have a trailing slash. - * - * @param string $ext_dir_prefix The prefix including trailing slash - * @return null - */ - public function set_ext_dir_prefix($ext_dir_prefix) - { - $this->ext_dir_prefix = $ext_dir_prefix; - } - - /** - * Finds template paths using the extension manager - * - * Locates a path (e.g. styles/prosilver/template/) in all active extensions. - * Then appends the core template paths based in the current working - * directory. - * - * @return array List of template paths - */ - public function find() - { - $directories = array(); - - $finder = $this->extension_manager->get_finder(); - foreach ($this->base_path_provider as $path) - { - if ($path && !phpbb_is_absolute($path)) - { - $directories = array_merge($directories, $finder - ->directory('/' . $this->ext_dir_prefix . $path) - ->get_directories() - ); - } - } - - foreach ($this->base_path_provider as $path) - { - $directories[] = $path; - } - - return $directories; - } - - /** - * Overwrites the current template names and paths - * - * @param array $templates An associative map from template names to paths. - * The first element is the main template. - * If the path is false, it will be generated from - * the supplied name. - * @param string $style_root_path The root directory for styles identified - * by name only. - * @return null - */ - public function set_templates(array $templates, $style_root_path) - { - $this->base_path_provider->set_templates($templates, $style_root_path); - $this->items = null; - } - - /** - * Retrieves the path to the main template passed into set_templates() - * - * @return string Main template path - */ - public function get_main_template_path() - { - return $this->base_path_provider->get_main_template_path(); - } -} diff --git a/phpBB/includes/template/filter.php b/phpBB/includes/template/filter.php index f55c8d7e9c..4a2593b757 100644 --- a/phpBB/includes/template/filter.php +++ b/phpBB/includes/template/filter.php @@ -76,6 +76,18 @@ class phpbb_template_filter extends php_user_filter private $allow_php; /** + * Resource locator. + * + * @var phpbb_template_locator + */ + private $locator; + + /** + * @var string phpBB root path + */ + private $phpbb_root_path; + + /** * Stream filter * * Is invoked for evey chunk of the stream, allowing us @@ -126,14 +138,16 @@ class phpbb_template_filter extends php_user_filter /** * Initializer, called on creation. * - * Get the allow_php option from params, which is passed - * to stream_filter_append. + * Get the allow_php option, root directory and locator from params, + * which are passed to stream_filter_append. */ public function onCreate() { $this->chunk = ''; $this->in_php = false; $this->allow_php = $this->params['allow_php']; + $this->locator = $this->params['locator']; + $this->phpbb_root_path = $this->params['phpbb_root_path']; return true; } @@ -281,6 +295,10 @@ class phpbb_template_filter extends php_user_filter return ($this->allow_php) ? '<?php ' . $this->compile_tag_include_php($matches[2]) . ' ?>' : ''; break; + case 'INCLUDEJS': + return '<?php ' . $this->compile_tag_include_js($matches[2]) . ' ?>'; + break; + case 'PHP': if ($this->allow_php) { @@ -857,6 +875,45 @@ class phpbb_template_filter extends php_user_filter } /** + * Compile INCLUDEJS tag + * + * @param string $tag_args Expression given with INCLUDEJS in source template + * @return string compiled template code + */ + private function compile_tag_include_js($tag_args) + { + // Process dynamic includes + if ($tag_args[0] == '{') + { + $var = $this->get_varref($tag_args, $is_expr); + if (!$is_expr) + { + return " \$_template->_js_include($var, true);"; + } + return ''; + } + + // Locate file + $filename = $this->locator->get_first_file_location(array($tag_args), false, true); + + if ($filename === false) + { + // File does not exist, find it during run time + return ' $_template->_js_include(\'' . addslashes($tag_args) . '\', true); '; + } + + if (substr($filename, 0, strlen($this->phpbb_root_path)) != $this->phpbb_root_path) + { + // Absolute path, include as is + return ' $_template->_js_include(\'' . addslashes($filename) . '\', false); '; + } + + // Relative path, remove root path from it + $filename = substr($filename, strlen($this->phpbb_root_path)); + return ' global $phpbb_root_path; $_template->_js_include($phpbb_root_path . \'' . addslashes($filename) . '\', false); '; + } + + /** * Generates a reference to the given variable inside the given (possibly nested) * block namespace. This is a string of the form: * ' . $_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['varname'] . ' diff --git a/phpBB/includes/template/locator.php b/phpBB/includes/template/locator.php index d4502d7984..01c79eec4e 100644 --- a/phpBB/includes/template/locator.php +++ b/phpBB/includes/template/locator.php @@ -17,107 +17,31 @@ if (!defined('IN_PHPBB')) /** -* Template locator. Maintains mapping from template handles to source paths. +* Resource locator interface. * -* Template locator is aware of template inheritance, and can return actual -* filesystem paths (i.e., the "primary" template or the "parent" template) +* Objects implementing this interface maintain mapping from template handles +* to source template file paths and locate templates. +* +* Locates style files. +* +* Resource locator is aware of styles tree, and can return actual +* filesystem paths (i.e., the "child" style or the "parent" styles) * depending on what files exist. * +* Root paths stored in locator are paths to style directories. Templates are +* stored in subdirectory that $template_path points to. +* * @package phpBB3 */ -class phpbb_template_locator +interface phpbb_template_locator { /** - * Paths to directories that templates are stored in. - * @var array - */ - private $roots = array(); - - /** - * Index of the main template in the roots array - * @var int - */ - private $main_root_id = 0; - - /** - * Map from root index to handles to source template file paths. - * Normally it only contains paths for handles that are used - * (or are likely to be used) by the page being rendered and not - * all templates that exist on the filesystem. - * @var array - */ - private $files = array(); - - /** - * Map from handles to source template file names. - * Covers the same data as $files property but maps to basenames - * instead of paths. - * @var array - */ - private $filenames = array(); - - /** - * Set main template location (must have been added through set_paths first). - * - * @param string $template_path Path to template directory - * @return null - */ - public function set_main_template($template) - { - $this->main_root_id = array_search($template, $this->roots, true); - } - - /** - * Sets the list of template paths - * - * These paths will be searched for template files in the provided order. - * Paths may be outside of phpBB, but templates loaded from these paths - * will still be cached. - * - * @param array $template_paths An array of paths to template directories - * @return null - */ - public function set_paths($template_paths) - { - $this->roots = array(); - $this->files = array(); - $this->filenames = array(); - $this->main_root_id = 0; - - foreach ($template_paths as $path) - { - // Make sure $path has no ending slash - if (substr($path, -1) === '/') - { - $path = substr($path, 0, -1); - } - $this->roots[] = $path; - } - } - - /** * Sets the template filenames for handles. $filename_array * should be a hash of handle => filename pairs. * * @param array $filname_array Should be a hash of handle => filename pairs. */ - public function set_filenames(array $filename_array) - { - foreach ($filename_array as $handle => $filename) - { - if (empty($filename)) - { - trigger_error("template locator: set_filenames: Empty filename specified for $handle", E_USER_ERROR); - } - - $this->filename[$handle] = $filename; - - foreach ($this->roots as $root_index => $root) - { - $this->files[$root_index][$handle] = $root . '/' . $filename; - } - } - } + public function set_filenames(array $filename_array); /** * Determines the filename for a template handle. @@ -129,50 +53,33 @@ class phpbb_template_locator * @param $handle string Template handle * @return string Filename corresponding to the template handle */ - public function get_filename_for_handle($handle) - { - if (!isset($this->filename[$handle])) - { - trigger_error("template locator: get_filename_for_handle: No file specified for handle $handle", E_USER_ERROR); - } - return $this->filename[$handle]; - } + public function get_filename_for_handle($handle); /** * Determines the source file path for a template handle without - * regard for template inheritance. + * regard for styles tree. * - * This function returns the path in "primary" template directory + * This function returns the path in "primary" style directory * corresponding to the given template handle. That path may or * may not actually exist on the filesystem. Because this function * does not perform stat calls to determine whether the path it * returns actually exists, it is faster than get_source_file_for_handle. * * Use get_source_file_for_handle to obtain the actual path that is - * guaranteed to exist (which might come from the parent/fallback - * template directory if template inheritance is used). + * guaranteed to exist (which might come from the parent style + * directory if primary style has parent styles). * * This function will trigger an error if the handle was never * associated with a template file via set_filenames. * * @param $handle string Template handle - * @return string Path to source file path in primary template directory + * @return string Path to source file path in primary style directory */ - public function get_virtual_source_file_for_handle($handle) - { - // If we don't have a file assigned to this handle, die. - if (!isset($this->files[$this->main_root_id][$handle])) - { - trigger_error("template locator: No file specified for handle $handle", E_USER_ERROR); - } - - $source_file = $this->files[$this->main_root_id][$handle]; - return $source_file; - } + public function get_virtual_source_file_for_handle($handle); /** * Determines the source file path for a template handle, accounting - * for template inheritance and verifying that the path exists. + * for styles tree and verifying that the path exists. * * This function returns the actual path that may be compiled for * the specified template handle. It will trigger an error if @@ -181,34 +88,34 @@ class phpbb_template_locator * filesystem. * * Use get_virtual_source_file_for_handle to just resolve a template - * handle to a path without any filesystem or inheritance checks. + * handle to a path without any filesystem or styles tree checks. * * @param string $handle Template handle (i.e. "friendly" template name) + * @param bool $find_all If true, each root path will be checked and function + * will return array of files instead of string and will not + * trigger a error if template does not exist * @return string Source file path */ - public function get_source_file_for_handle($handle) - { - // If we don't have a file assigned to this handle, die. - if (!isset($this->files[$this->main_root_id][$handle])) - { - trigger_error("template locator: No file specified for handle $handle", E_USER_ERROR); - } - - // locate a source file that exists - $source_file = $this->files[0][$handle]; - $tried = $source_file; - for ($i = 1, $n = count($this->roots); $i < $n && !file_exists($source_file); $i++) - { - $source_file = $this->files[$i][$handle]; - $tried .= ', ' . $source_file; - } - - // search failed - if (!file_exists($source_file)) - { - trigger_error("template locator: File for handle $handle does not exist. Could not find: $tried", E_USER_ERROR); - } + public function get_source_file_for_handle($handle, $find_all = false); - return $source_file; - } + /** + * Locates source file path, accounting for styles tree and verifying that + * the path exists. + * + * Unlike previous functions, this function works without template handle + * and it can search for more than one file. If more than one file name is + * specified, it will return location of file that it finds first. + * + * @param array $files List of files to locate. + * @param bool $return_default Determines what to return if file does not + * exist. If true, function will return location where file is + * supposed to be. If false, function will return false. + * @param bool $return_full_path If true, function will return full path + * to file. If false, function will return file name. This + * parameter can be used to check which one of set of files + * is available. + * @return string or boolean Source file path if file exists or $return_default is + * true. False if file does not exist and $return_default is false + */ + public function get_first_file_location($files, $return_default = false, $return_full_path = true); } diff --git a/phpBB/includes/template/path_provider.php b/phpBB/includes/template/path_provider.php deleted file mode 100644 index b0b545973d..0000000000 --- a/phpBB/includes/template/path_provider.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php -/** -* -* @package phpBB3 -* @copyright (c) 2011 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 -* -*/ - -/** -* @ignore -*/ -if (!defined('IN_PHPBB')) -{ - exit; -} - -/** -* Provides a template locator with paths -* -* Finds installed template paths and makes them available to the locator. -* -* @package phpBB3 -*/ -class phpbb_template_path_provider implements IteratorAggregate, phpbb_template_path_provider_interface -{ - protected $main_template_name = ''; - protected $paths = array(); - - /** - * Ignores the extension dir prefix - * - * @param string $ext_dir_prefix The prefix including trailing slash - * @return null - */ - public function set_ext_dir_prefix($ext_dir_prefix) - { - } - - /** - * Overwrites the current template names and paths - * - * The first element of the passed templates map, is considered the main - * template and can be retrieved through get_main_template_path(). - * - * @param array $templates An associative map from template names to paths. - * The first element is the main template. - * If the path is false, it will be generated from - * the supplied name. - * @param string $style_root_path The root directory for styles identified - * by name only. - * @return null - */ - public function set_templates(array $templates, $style_root_path) - { - $this->paths = array(); - - foreach ($templates as $name => $path) - { - if (!$path) - { - $path = $style_root_path . $this->template_root_for_style($name); - } - - $this->paths[] = $path; - } - - $this->main_template_path = $this->paths[0]; - } - - /** - * Retrieves the path to the main template passed into set_templates() - * - * @return string Main template path - */ - public function get_main_template_path() - { - return $this->main_template_path; - } - - /** - * Converts a style name to relative (to board root or extension) path to - * the style's template files. - * - * @param $style_name string Style name - * @return string Path to style template files - */ - private function template_root_for_style($style_name) - { - return 'styles/' . $style_name . '/template'; - } - - /** - * Retrieve an iterator over all template paths - * - * @return ArrayIterator An iterator for the array of template paths - */ - public function getIterator() - { - return new ArrayIterator($this->paths); - } -} diff --git a/phpBB/includes/template/path_provider_interface.php b/phpBB/includes/template/path_provider_interface.php deleted file mode 100644 index 8b3a406d2a..0000000000 --- a/phpBB/includes/template/path_provider_interface.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php -/** -* -* @package phpBB3 -* @copyright (c) 2011 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 -* -*/ - -/** -* @ignore -*/ -if (!defined('IN_PHPBB')) -{ - exit; -} - -/** -* Provides a template locator with paths -* -* Finds installed template paths and makes them available to the locator. -* -* @package phpBB3 -*/ -interface phpbb_template_path_provider_interface extends Traversable -{ - /** - * Defines a prefix to use for template paths in extensions - * - * @param string $ext_dir_prefix The prefix including trailing slash - * @return null - */ - public function set_ext_dir_prefix($ext_dir_prefix); - - /** - * Overwrites the current template names and paths - * - * @param array $templates An associative map from template names to paths. - * The first element is the main template. - * If the path is false, it will be generated from - * the supplied name. - * @param string $style_root_path The root directory for styles identified - * by name only. - * @return null - */ - public function set_templates(array $templates, $style_root_path); - - /** - * Retrieves the path to the main template passed into set_templates() - * - * @return string Main template path - */ - public function get_main_template_path(); -} diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php index bac5445511..e6512c8417 100644 --- a/phpBB/includes/template/template.php +++ b/phpBB/includes/template/template.php @@ -32,33 +32,39 @@ if (!defined('IN_PHPBB')) class phpbb_template { /** - * @var phpbb_template_context Template context. + * Template context. * Stores template data used during template rendering. + * @var phpbb_template_context */ - private $context; + public $context; /** - * @var string Path of the cache directory for the template + * Path of the cache directory for the template + * @var string */ public $cachepath = ''; /** - * @var string phpBB root path + * phpBB root path + * @var string */ private $phpbb_root_path; /** - * @var phpEx PHP file extension + * PHP file extension + * @var string */ private $phpEx; /** - * @var phpbb_config phpBB config instance + * phpBB config instance + * @var phpbb_config */ private $config; /** - * @var user current user + * Current user + * @var phpbb_user */ private $user; @@ -69,10 +75,10 @@ class phpbb_template private $locator; /** - * Template path provider - * @var phpbb_template_path_provider + * Location of templates directory within style directories + * @var string */ - private $provider; + public $template_path = 'template/'; /** * Constructor. @@ -80,68 +86,15 @@ class phpbb_template * @param string $phpbb_root_path phpBB root path * @param user $user current user * @param phpbb_template_locator $locator template locator - * @param phpbb_template_path_provider $provider template path provider */ - public function __construct($phpbb_root_path, $phpEx, $config, $user, phpbb_template_locator $locator, phpbb_template_path_provider_interface $provider) + public function __construct($phpbb_root_path, $phpEx, $config, $user, phpbb_template_locator $locator) { $this->phpbb_root_path = $phpbb_root_path; $this->phpEx = $phpEx; $this->config = $config; $this->user = $user; $this->locator = $locator; - $this->provider = $provider; - } - - /** - * Set template location based on (current) user's chosen style. - */ - public function set_template() - { - $template_name = $this->user->theme['template_path']; - $fallback_name = ($this->user->theme['template_inherits_id']) ? $this->user->theme['template_inherit_path'] : false; - - return $this->set_custom_template(false, $template_name, false, $fallback_name); - } - - /** - * Defines a prefix to use for template paths in extensions - * - * @param string $ext_dir_prefix The prefix including trailing slash - * @return null - */ - public function set_ext_dir_prefix($ext_dir_prefix) - { - $this->provider->set_ext_dir_prefix($ext_dir_prefix); - } - - /** - * Set custom template location (able to use directory outside of phpBB). - * - * Note: Templates are still compiled to phpBB's cache directory. - * - * @param string $template_path Path to template directory - * @param string $template_name Name of template - * @param string $fallback_template_path Path to fallback template - * @param string $fallback_template_name Name of fallback template - */ - public function set_custom_template($template_path, $template_name, $fallback_template_path = false, $fallback_template_name = false) - { - $templates = array($template_name => $template_path); - - if ($fallback_template_name !== false) - { - $templates[$fallback_template_name] = $fallback_template_path; - } - - $this->provider->set_templates($templates, $this->phpbb_root_path); - $this->locator->set_paths($this->provider); - $this->locator->set_main_template($this->provider->get_main_template_path()); - - $this->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $template_name) . '_'; - - $this->context = new phpbb_template_context(); - - return true; + $this->template_path = $this->locator->template_path; } /** @@ -333,7 +286,7 @@ class phpbb_template return new phpbb_template_renderer_include($output_file, $this); } - $compile = new phpbb_template_compile($this->config['tpl_allow_php']); + $compile = new phpbb_template_compile($this->config['tpl_allow_php'], $this->locator, $this->phpbb_root_path); if ($compile->compile_file_to_file($source_file, $output_file) !== false) { @@ -501,4 +454,62 @@ class phpbb_template } include($file); } + + /** + * Locates source template path, accounting for styles tree and verifying that + * the path exists. + * + * @param string or array $files List of templates to locate. If there is only + * one template, $files can be a string to make code easier to read. + * @param bool $return_default Determines what to return if template does not + * exist. If true, function will return location where template is + * supposed to be. If false, function will return false. + * @param bool $return_full_path If true, function will return full path + * to template. If false, function will return template file name. + * This parameter can be used to check which one of set of template + * files is available. + * @return string or boolean Source template path if template exists or $return_default is + * true. False if template does not exist and $return_default is false + */ + public function locate($files, $return_default = false, $return_full_path = true) + { + // add tempalte path prefix + $templates = array(); + if (is_string($files)) + { + $templates[] = $this->template_path . $files; + } + else + { + foreach ($files as $file) + { + $templates[] = $this->template_path . $file; + } + } + + // use resource locator to find files + return $this->locator->get_first_file_location($templates, $return_default, $return_full_path); + } + + /** + * Include JS file + * + * @param string $file file name + * @param bool $locate True if file needs to be located + */ + public function _js_include($file, $locate = false) + { + // Locate file + if ($locate) + { + $file = $this->locator->get_first_file_location(array($file), true, true); + } + + $file .= (strpos($file, '?') === false) ? '?' : '&'; + $file .= 'assets_version=' . $this->config['assets_version']; + + // Add HTML code + $code = '<script src="' . htmlspecialchars($file) . '"></script>'; + $this->context->append_var('SCRIPTS', $code); + } } diff --git a/phpBB/includes/ucp/ucp_prefs.php b/phpBB/includes/ucp/ucp_prefs.php index 11f2d231fe..0c9f20f266 100644 --- a/phpBB/includes/ucp/ucp_prefs.php +++ b/phpBB/includes/ucp/ucp_prefs.php @@ -60,7 +60,14 @@ class ucp_prefs if ($submit) { - $data['style'] = ($config['override_user_style']) ? $config['default_style'] : $data['style']; + if ($config['override_user_style']) + { + $data['style'] = (int) $config['default_style']; + } + else if (!phpbb_style_is_active($data['style'])) + { + $data['style'] = (int) $user->data['user_style']; + } $error = validate_data($data, array( 'dateformat' => array('string', false, 1, 30), diff --git a/phpBB/includes/ucp/ucp_remind.php b/phpBB/includes/ucp/ucp_remind.php index bcd4e261fe..4f65ed1866 100644 --- a/phpBB/includes/ucp/ucp_remind.php +++ b/phpBB/includes/ucp/ucp_remind.php @@ -66,7 +66,7 @@ class ucp_remind } // Check users permissions - $auth2 = new auth(); + $auth2 = new phpbb_auth(); $auth2->acl($user_row); if (!$auth2->acl_get('u_chgpasswd')) diff --git a/phpBB/includes/ucp/ucp_zebra.php b/phpBB/includes/ucp/ucp_zebra.php index 004f3b80aa..efe928b387 100644 --- a/phpBB/includes/ucp/ucp_zebra.php +++ b/phpBB/includes/ucp/ucp_zebra.php @@ -25,7 +25,7 @@ class ucp_zebra function main($id, $mode) { - global $config, $db, $user, $auth, $template, $phpbb_root_path, $phpEx; + global $config, $db, $user, $auth, $template, $phpbb_root_path, $phpEx, $request; $submit = (isset($_POST['submit']) || isset($_GET['add']) || isset($_GET['remove'])) ? true : false; $s_hidden_fields = ''; @@ -198,8 +198,24 @@ class ucp_zebra } } } - - if ($updated) + + if ($request->is_ajax()) + { + $message = ($updated) ? $user->lang[$l_mode . '_UPDATED'] : implode('<br />', $error); + + $json_response = new phpbb_json_response; + $json_response->send(array( + 'success' => $updated, + + 'MESSAGE_TITLE' => $user->lang['INFORMATION'], + 'MESSAGE_TEXT' => $message, + 'REFRESH_DATA' => array( + 'time' => 3, + 'url' => $this->u_action + ) + )); + } + else if ($updated) { meta_refresh(3, $this->u_action); $message = $user->lang[$l_mode . '_UPDATED'] . '<br />' . implode('<br />', $error) . ((sizeof($error)) ? '<br />' : '') . '<br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $this->u_action . '">', '</a>'); diff --git a/phpBB/includes/user.php b/phpBB/includes/user.php new file mode 100644 index 0000000000..ce9c804f23 --- /dev/null +++ b/phpBB/includes/user.php @@ -0,0 +1,851 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2005 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + +/** +* Base user class +* +* This is the overarching class which contains (through session extend) +* all methods utilised for user functionality during a session. +* +* @package phpBB3 +*/ +class phpbb_user extends phpbb_session +{ + var $lang = array(); + var $help = array(); + var $theme = array(); + var $date_format; + var $timezone; + var $dst; + + var $lang_name = false; + var $lang_id = false; + var $lang_path; + var $img_lang; + var $img_array = array(); + + // Able to add new options (up to id 31) + var $keyoptions = array('viewimg' => 0, 'viewflash' => 1, 'viewsmilies' => 2, 'viewsigs' => 3, 'viewavatars' => 4, 'viewcensors' => 5, 'attachsig' => 6, 'bbcode' => 8, 'smilies' => 9, 'popuppm' => 10, 'sig_bbcode' => 15, 'sig_smilies' => 16, 'sig_links' => 17); + + /** + * Constructor to set the lang path + */ + function __construct() + { + global $phpbb_root_path; + + $this->lang_path = $phpbb_root_path . 'language/'; + } + + /** + * Function to set custom language path (able to use directory outside of phpBB) + * + * @param string $lang_path New language path used. + * @access public + */ + function set_custom_lang_path($lang_path) + { + $this->lang_path = $lang_path; + + if (substr($this->lang_path, -1) != '/') + { + $this->lang_path .= '/'; + } + } + + /** + * Setup basic user-specific items (style, language, ...) + */ + function setup($lang_set = false, $style_id = false) + { + global $db, $phpbb_style, $template, $config, $auth, $phpEx, $phpbb_root_path, $cache; + + if ($this->data['user_id'] != ANONYMOUS) + { + $this->lang_name = (file_exists($this->lang_path . $this->data['user_lang'] . "/common.$phpEx")) ? $this->data['user_lang'] : basename($config['default_lang']); + + $this->date_format = $this->data['user_dateformat']; + $this->timezone = $this->data['user_timezone'] * 3600; + $this->dst = $this->data['user_dst'] * 3600; + } + else + { + $this->lang_name = basename($config['default_lang']); + $this->date_format = $config['default_dateformat']; + $this->timezone = $config['board_timezone'] * 3600; + $this->dst = $config['board_dst'] * 3600; + + /** + * If a guest user is surfing, we try to guess his/her language first by obtaining the browser language + * If re-enabled we need to make sure only those languages installed are checked + * Commented out so we do not loose the code. + + if ($request->header('Accept-Language')) + { + $accept_lang_ary = explode(',', $request->header('Accept-Language')); + + foreach ($accept_lang_ary as $accept_lang) + { + // Set correct format ... guess full xx_YY form + $accept_lang = substr($accept_lang, 0, 2) . '_' . strtoupper(substr($accept_lang, 3, 2)); + $accept_lang = basename($accept_lang); + + if (file_exists($this->lang_path . $accept_lang . "/common.$phpEx")) + { + $this->lang_name = $config['default_lang'] = $accept_lang; + break; + } + else + { + // No match on xx_YY so try xx + $accept_lang = substr($accept_lang, 0, 2); + $accept_lang = basename($accept_lang); + + if (file_exists($this->lang_path . $accept_lang . "/common.$phpEx")) + { + $this->lang_name = $config['default_lang'] = $accept_lang; + break; + } + } + } + } + */ + } + + // We include common language file here to not load it every time a custom language file is included + $lang = &$this->lang; + + // Do not suppress error if in DEBUG_EXTRA mode + $include_result = (defined('DEBUG_EXTRA')) ? (include $this->lang_path . $this->lang_name . "/common.$phpEx") : (@include $this->lang_path . $this->lang_name . "/common.$phpEx"); + + if ($include_result === false) + { + die('Language file ' . $this->lang_path . $this->lang_name . "/common.$phpEx" . " couldn't be opened."); + } + + $this->add_lang($lang_set); + unset($lang_set); + + $style_request = request_var('style', 0); + if ($style_request && $auth->acl_get('a_styles') && !defined('ADMIN_START')) + { + global $SID, $_EXTRA_URL; + + $style_id = $style_request; + $SID .= '&style=' . $style_id; + $_EXTRA_URL = array('style=' . $style_id); + } + else + { + // Set up style + $style_id = ($style_id) ? $style_id : ((!$config['override_user_style']) ? $this->data['user_style'] : $config['default_style']); + } + + $sql = 'SELECT * + FROM ' . STYLES_TABLE . " s + WHERE s.style_id = $style_id"; + $result = $db->sql_query($sql, 3600); + $this->theme = $db->sql_fetchrow($result); + $db->sql_freeresult($result); + + // User has wrong style + if (!$this->theme && $style_id == $this->data['user_style']) + { + $style_id = $this->data['user_style'] = $config['default_style']; + + $sql = 'UPDATE ' . USERS_TABLE . " + SET user_style = $style_id + WHERE user_id = {$this->data['user_id']}"; + $db->sql_query($sql); + + $sql = 'SELECT * + FROM ' . STYLES_TABLE . " s + WHERE s.style_id = $style_id"; + $result = $db->sql_query($sql, 3600); + $this->theme = $db->sql_fetchrow($result); + $db->sql_freeresult($result); + } + + if (!$this->theme) + { + trigger_error('Could not get style data', E_USER_ERROR); + } + + // Now parse the cfg file and cache it + $parsed_items = $cache->obtain_cfg_items($this->theme); + + // We are only interested in the theme configuration for now + $parsed_items = $parsed_items['theme']; + + $check_for = array( + 'pagination_sep' => (string) ', ' + ); + + foreach ($check_for as $key => $default_value) + { + $this->theme[$key] = (isset($parsed_items[$key])) ? $parsed_items[$key] : $default_value; + settype($this->theme[$key], gettype($default_value)); + + if (is_string($default_value)) + { + $this->theme[$key] = htmlspecialchars($this->theme[$key]); + } + } + + $phpbb_style->set_style(); + + $this->img_lang = $this->lang_name; + + // Call phpbb_user_session_handler() in case external application want to "bend" some variables or replace classes... + // After calling it we continue script execution... + phpbb_user_session_handler(); + + // If this function got called from the error handler we are finished here. + if (defined('IN_ERROR_HANDLER')) + { + return; + } + + // Disable board if the install/ directory is still present + // For the brave development army we do not care about this, else we need to comment out this everytime we develop locally + if (!defined('DEBUG_EXTRA') && !defined('ADMIN_START') && !defined('IN_INSTALL') && !defined('IN_LOGIN') && file_exists($phpbb_root_path . 'install') && !is_file($phpbb_root_path . 'install')) + { + // Adjust the message slightly according to the permissions + if ($auth->acl_gets('a_', 'm_') || $auth->acl_getf_global('m_')) + { + $message = 'REMOVE_INSTALL'; + } + else + { + $message = (!empty($config['board_disable_msg'])) ? $config['board_disable_msg'] : 'BOARD_DISABLE'; + } + trigger_error($message); + } + + // Is board disabled and user not an admin or moderator? + if ($config['board_disable'] && !defined('IN_LOGIN') && !$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_')) + { + if ($this->data['is_bot']) + { + send_status_line(503, 'Service Unavailable'); + } + + $message = (!empty($config['board_disable_msg'])) ? $config['board_disable_msg'] : 'BOARD_DISABLE'; + trigger_error($message); + } + + // Is load exceeded? + if ($config['limit_load'] && $this->load !== false) + { + if ($this->load > floatval($config['limit_load']) && !defined('IN_LOGIN') && !defined('IN_ADMIN')) + { + // Set board disabled to true to let the admins/mods get the proper notification + $config['board_disable'] = '1'; + + if (!$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_')) + { + if ($this->data['is_bot']) + { + send_status_line(503, 'Service Unavailable'); + } + trigger_error('BOARD_UNAVAILABLE'); + } + } + } + + if (isset($this->data['session_viewonline'])) + { + // Make sure the user is able to hide his session + if (!$this->data['session_viewonline']) + { + // Reset online status if not allowed to hide the session... + if (!$auth->acl_get('u_hideonline')) + { + $sql = 'UPDATE ' . SESSIONS_TABLE . ' + SET session_viewonline = 1 + WHERE session_user_id = ' . $this->data['user_id']; + $db->sql_query($sql); + $this->data['session_viewonline'] = 1; + } + } + else if (!$this->data['user_allow_viewonline']) + { + // the user wants to hide and is allowed to -> cloaking device on. + if ($auth->acl_get('u_hideonline')) + { + $sql = 'UPDATE ' . SESSIONS_TABLE . ' + SET session_viewonline = 0 + WHERE session_user_id = ' . $this->data['user_id']; + $db->sql_query($sql); + $this->data['session_viewonline'] = 0; + } + } + } + + + // Does the user need to change their password? If so, redirect to the + // ucp profile reg_details page ... of course do not redirect if we're already in the ucp + if (!defined('IN_ADMIN') && !defined('ADMIN_START') && $config['chg_passforce'] && !empty($this->data['is_registered']) && $auth->acl_get('u_chgpasswd') && $this->data['user_passchg'] < time() - ($config['chg_passforce'] * 86400)) + { + if (strpos($this->page['query_string'], 'mode=reg_details') === false && $this->page['page_name'] != "ucp.$phpEx") + { + redirect(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=profile&mode=reg_details')); + } + } + + return; + } + + /** + * More advanced language substitution + * Function to mimic sprintf() with the possibility of using phpBB's language system to substitute nullar/singular/plural forms. + * Params are the language key and the parameters to be substituted. + * This function/functionality is inspired by SHS` and Ashe. + * + * Example call: <samp>$user->lang('NUM_POSTS_IN_QUEUE', 1);</samp> + * + * If the first parameter is an array, the elements are used as keys and subkeys to get the language entry: + * Example: <samp>$user->lang(array('datetime', 'AGO'), 1)</samp> uses $user->lang['datetime']['AGO'] as language entry. + */ + function lang() + { + $args = func_get_args(); + $key = $args[0]; + + if (is_array($key)) + { + $lang = &$this->lang[array_shift($key)]; + + foreach ($key as $_key) + { + $lang = &$lang[$_key]; + } + } + else + { + $lang = &$this->lang[$key]; + } + + // Return if language string does not exist + if (!isset($lang) || (!is_string($lang) && !is_array($lang))) + { + return $key; + } + + // If the language entry is a string, we simply mimic sprintf() behaviour + if (is_string($lang)) + { + if (sizeof($args) == 1) + { + return $lang; + } + + // Replace key with language entry and simply pass along... + $args[0] = $lang; + return call_user_func_array('sprintf', $args); + } + else if (sizeof($lang) == 0) + { + // If the language entry is an empty array, we just return the language key + return $args[0]; + } + + // It is an array... now handle different nullar/singular/plural forms + $key_found = false; + + // We now get the first number passed and will select the key based upon this number + for ($i = 1, $num_args = sizeof($args); $i < $num_args; $i++) + { + if (is_int($args[$i]) || is_float($args[$i])) + { + if ($args[$i] == 0 && isset($lang[0])) + { + // We allow each translation using plural forms to specify a version for the case of 0 things, + // so that "0 users" may be displayed as "No users". + $key_found = 0; + break; + } + else + { + $use_plural_form = $this->get_plural_form($args[$i]); + if (isset($lang[$use_plural_form])) + { + // The key we should use exists, so we use it. + $key_found = $use_plural_form; + } + else + { + // If the key we need to use does not exist, we fall back to the previous one. + $numbers = array_keys($lang); + + foreach ($numbers as $num) + { + if ($num > $use_plural_form) + { + break; + } + + $key_found = $num; + } + } + break; + } + } + } + + // Ok, let's check if the key was found, else use the last entry (because it is mostly the plural form) + if ($key_found === false) + { + $numbers = array_keys($lang); + $key_found = end($numbers); + } + + // Use the language string we determined and pass it to sprintf() + $args[0] = $lang[$key_found]; + return call_user_func_array('sprintf', $args); + } + + /** + * Determine which plural form we should use. + * For some languages this is not as simple as for English. + * + * @param $number int|float The number we want to get the plural case for. Float numbers are floored. + * @param $force_rule mixed False to use the plural rule of the language package + * or an integer to force a certain plural rule + * @return int The plural-case we need to use for the number plural-rule combination + */ + function get_plural_form($number, $force_rule = false) + { + $number = (int) $number; + + // Default to English system + $plural_rule = ($force_rule !== false) ? $force_rule : ((isset($this->lang['PLURAL_RULE'])) ? $this->lang['PLURAL_RULE'] : 1); + + return phpbb_get_plural_form($plural_rule, $number); + } + + /** + * Add Language Items - use_db and use_help are assigned where needed (only use them to force inclusion) + * + * @param mixed $lang_set specifies the language entries to include + * @param bool $use_db internal variable for recursion, do not use + * @param bool $use_help internal variable for recursion, do not use + * @param string $ext_name The extension to load language from, or empty for core files + * + * Examples: + * <code> + * $lang_set = array('posting', 'help' => 'faq'); + * $lang_set = array('posting', 'viewtopic', 'help' => array('bbcode', 'faq')) + * $lang_set = array(array('posting', 'viewtopic'), 'help' => array('bbcode', 'faq')) + * $lang_set = 'posting' + * $lang_set = array('help' => 'faq', 'db' => array('help:faq', 'posting')) + * </code> + */ + function add_lang($lang_set, $use_db = false, $use_help = false, $ext_name = '') + { + global $phpEx; + + if (is_array($lang_set)) + { + foreach ($lang_set as $key => $lang_file) + { + // Please do not delete this line. + // We have to force the type here, else [array] language inclusion will not work + $key = (string) $key; + + if ($key == 'db') + { + $this->add_lang($lang_file, true, $use_help, $ext_name); + } + else if ($key == 'help') + { + $this->add_lang($lang_file, $use_db, true, $ext_name); + } + else if (!is_array($lang_file)) + { + $this->set_lang($this->lang, $this->help, $lang_file, $use_db, $use_help, $ext_name); + } + else + { + $this->add_lang($lang_file, $use_db, $use_help, $ext_name); + } + } + unset($lang_set); + } + else if ($lang_set) + { + $this->set_lang($this->lang, $this->help, $lang_set, $use_db, $use_help, $ext_name); + } + } + + /** + * Add Language Items from an extension - use_db and use_help are assigned where needed (only use them to force inclusion) + * + * @param string $ext_name The extension to load language from, or empty for core files + * @param mixed $lang_set specifies the language entries to include + * @param bool $use_db internal variable for recursion, do not use + * @param bool $use_help internal variable for recursion, do not use + */ + function add_lang_ext($ext_name, $lang_set, $use_db = false, $use_help = false) + { + if ($ext_name === '/') + { + $ext_name = ''; + } + + $this->add_lang($lang_set, $use_db, $use_help, $ext_name); + } + + /** + * Set language entry (called by add_lang) + * @access private + */ + function set_lang(&$lang, &$help, $lang_file, $use_db = false, $use_help = false, $ext_name = '') + { + global $phpbb_root_path, $phpEx; + + // Make sure the language name is set (if the user setup did not happen it is not set) + if (!$this->lang_name) + { + global $config; + $this->lang_name = basename($config['default_lang']); + } + + // $lang == $this->lang + // $help == $this->help + // - add appropriate variables here, name them as they are used within the language file... + if (!$use_db) + { + if ($use_help && strpos($lang_file, '/') !== false) + { + $filename = dirname($lang_file) . '/help_' . basename($lang_file); + } + else + { + $filename = (($use_help) ? 'help_' : '') . $lang_file; + } + + if ($ext_name) + { + global $phpbb_extension_manager; + $ext_path = $phpbb_extension_manager->get_extension_path($ext_name, true); + + $lang_path = $ext_path . 'language/'; + } + else + { + $lang_path = $this->lang_path; + } + + if (strpos($phpbb_root_path . $filename, $lang_path . $this->lang_name . '/') === 0) + { + $language_filename = $phpbb_root_path . $filename; + } + else + { + $language_filename = $lang_path . $this->lang_name . '/' . $filename . '.' . $phpEx; + } + + if (!file_exists($language_filename)) + { + global $config; + + if ($this->lang_name == 'en') + { + // The user's selected language is missing the file, the board default's language is missing the file, and the file doesn't exist in /en. + $language_filename = str_replace($lang_path . 'en', $lang_path . $this->data['user_lang'], $language_filename); + trigger_error('Language file ' . $language_filename . ' couldn\'t be opened.', E_USER_ERROR); + } + else if ($this->lang_name == basename($config['default_lang'])) + { + // Fall back to the English Language + $this->lang_name = 'en'; + $this->set_lang($lang, $help, $lang_file, $use_db, $use_help, $ext_name); + } + else if ($this->lang_name == $this->data['user_lang']) + { + // Fall back to the board default language + $this->lang_name = basename($config['default_lang']); + $this->set_lang($lang, $help, $lang_file, $use_db, $use_help, $ext_name); + } + + // Reset the lang name + $this->lang_name = (file_exists($lang_path . $this->data['user_lang'] . "/common.$phpEx")) ? $this->data['user_lang'] : basename($config['default_lang']); + return; + } + + // Do not suppress error if in DEBUG_EXTRA mode + $include_result = (defined('DEBUG_EXTRA')) ? (include $language_filename) : (@include $language_filename); + + if ($include_result === false) + { + trigger_error('Language file ' . $language_filename . ' couldn\'t be opened.', E_USER_ERROR); + } + } + else if ($use_db) + { + // Get Database Language Strings + // Put them into $lang if nothing is prefixed, put them into $help if help: is prefixed + // For example: help:faq, posting + } + } + + /** + * Format user date + * + * @param int $gmepoch unix timestamp + * @param string $format date format in date() notation. | used to indicate relative dates, for example |d m Y|, h:i is translated to Today, h:i. + * @param bool $forcedate force non-relative date format. + * + * @return mixed translated date + */ + function format_date($gmepoch, $format = false, $forcedate = false) + { + static $midnight; + static $date_cache; + + $format = (!$format) ? $this->date_format : $format; + $now = time(); + $delta = $now - $gmepoch; + + if (!isset($date_cache[$format])) + { + // Is the user requesting a friendly date format (i.e. 'Today 12:42')? + $date_cache[$format] = array( + 'is_short' => strpos($format, '|'), + 'format_short' => substr($format, 0, strpos($format, '|')) . '||' . substr(strrchr($format, '|'), 1), + 'format_long' => str_replace('|', '', $format), + 'lang' => $this->lang['datetime'], + ); + + // Short representation of month in format? Some languages use different terms for the long and short format of May + if ((strpos($format, '\M') === false && strpos($format, 'M') !== false) || (strpos($format, '\r') === false && strpos($format, 'r') !== false)) + { + $date_cache[$format]['lang']['May'] = $this->lang['datetime']['May_short']; + } + } + + // Zone offset + $zone_offset = $this->timezone + $this->dst; + + // Show date < 1 hour ago as 'xx min ago' but not greater than 60 seconds in the future + // A small tolerence is given for times in the future but in the same minute are displayed as '< than a minute ago' + if ($delta < 3600 && $delta > -60 && ($delta >= -5 || (($now / 60) % 60) == (($gmepoch / 60) % 60)) && $date_cache[$format]['is_short'] !== false && !$forcedate && isset($this->lang['datetime']['AGO'])) + { + return $this->lang(array('datetime', 'AGO'), max(0, (int) floor($delta / 60))); + } + + if (!$midnight) + { + list($d, $m, $y) = explode(' ', gmdate('j n Y', time() + $zone_offset)); + $midnight = gmmktime(0, 0, 0, $m, $d, $y) - $zone_offset; + } + + if ($date_cache[$format]['is_short'] !== false && !$forcedate && !($gmepoch < $midnight - 86400 || $gmepoch > $midnight + 172800)) + { + $day = false; + + if ($gmepoch > $midnight + 86400) + { + $day = 'TOMORROW'; + } + else if ($gmepoch > $midnight) + { + $day = 'TODAY'; + } + else if ($gmepoch > $midnight - 86400) + { + $day = 'YESTERDAY'; + } + + if ($day !== false) + { + return str_replace('||', $this->lang['datetime'][$day], strtr(@gmdate($date_cache[$format]['format_short'], $gmepoch + $zone_offset), $date_cache[$format]['lang'])); + } + } + + return strtr(@gmdate($date_cache[$format]['format_long'], $gmepoch + $zone_offset), $date_cache[$format]['lang']); + } + + /** + * Get language id currently used by the user + */ + function get_iso_lang_id() + { + global $config, $db; + + if (!empty($this->lang_id)) + { + return $this->lang_id; + } + + if (!$this->lang_name) + { + $this->lang_name = $config['default_lang']; + } + + $sql = 'SELECT lang_id + FROM ' . LANG_TABLE . " + WHERE lang_iso = '" . $db->sql_escape($this->lang_name) . "'"; + $result = $db->sql_query($sql); + $this->lang_id = (int) $db->sql_fetchfield('lang_id'); + $db->sql_freeresult($result); + + return $this->lang_id; + } + + /** + * Get users profile fields + */ + function get_profile_fields($user_id) + { + global $db; + + if (isset($this->profile_fields)) + { + return; + } + + $sql = 'SELECT * + FROM ' . PROFILE_FIELDS_DATA_TABLE . " + WHERE user_id = $user_id"; + $result = $db->sql_query_limit($sql, 1); + $this->profile_fields = (!($row = $db->sql_fetchrow($result))) ? array() : $row; + $db->sql_freeresult($result); + } + + /** + * Specify/Get image + */ + function img($img, $alt = '') + { + $alt = (!empty($this->lang[$alt])) ? $this->lang[$alt] : $alt; + return '<span class="imageset ' . $img . '">' . $alt . '</span>'; + } + + /** + * Get option bit field from user options. + * + * @param int $key option key, as defined in $keyoptions property. + * @param int $data bit field value to use, or false to use $this->data['user_options'] + * @return bool true if the option is set in the bit field, false otherwise + */ + function optionget($key, $data = false) + { + $var = ($data !== false) ? $data : $this->data['user_options']; + return phpbb_optionget($this->keyoptions[$key], $var); + } + + /** + * Set option bit field for user options. + * + * @param int $key Option key, as defined in $keyoptions property. + * @param bool $value True to set the option, false to clear the option. + * @param int $data Current bit field value, or false to use $this->data['user_options'] + * @return int|bool If $data is false, the bit field is modified and + * written back to $this->data['user_options'], and + * return value is true if the bit field changed and + * false otherwise. If $data is not false, the new + * bitfield value is returned. + */ + function optionset($key, $value, $data = false) + { + $var = ($data !== false) ? $data : $this->data['user_options']; + + $new_var = phpbb_optionset($this->keyoptions[$key], $value, $var); + + if ($data === false) + { + if ($new_var != $var) + { + $this->data['user_options'] = $new_var; + return true; + } + else + { + return false; + } + } + else + { + return $new_var; + } + } + + /** + * Funtion to make the user leave the NEWLY_REGISTERED system group. + * @access public + */ + function leave_newly_registered() + { + global $db; + + if (empty($this->data['user_new'])) + { + return false; + } + + if (!function_exists('remove_newly_registered')) + { + global $phpbb_root_path, $phpEx; + + include($phpbb_root_path . 'includes/functions_user.' . $phpEx); + } + if ($group = remove_newly_registered($this->data['user_id'], $this->data)) + { + $this->data['group_id'] = $group; + + } + $this->data['user_permissions'] = ''; + $this->data['user_new'] = 0; + + return true; + } + + /** + * Returns all password protected forum ids the user is currently NOT authenticated for. + * + * @return array Array of forum ids + * @access public + */ + function get_passworded_forums() + { + global $db; + + $sql = 'SELECT f.forum_id, fa.user_id + FROM ' . FORUMS_TABLE . ' f + LEFT JOIN ' . FORUMS_ACCESS_TABLE . " fa + ON (fa.forum_id = f.forum_id + AND fa.session_id = '" . $db->sql_escape($this->session_id) . "') + WHERE f.forum_password <> ''"; + $result = $db->sql_query($sql); + + $forum_ids = array(); + while ($row = $db->sql_fetchrow($result)) + { + $forum_id = (int) $row['forum_id']; + + if ($row['user_id'] != $this->data['user_id']) + { + $forum_ids[$forum_id] = $forum_id; + } + } + $db->sql_freeresult($result); + + return $forum_ids; + } +} |