diff options
Diffstat (limited to 'phpBB/includes')
-rw-r--r-- | phpBB/includes/acp/acp_styles.php | 472 |
1 files changed, 123 insertions, 349 deletions
diff --git a/phpBB/includes/acp/acp_styles.php b/phpBB/includes/acp/acp_styles.php index 61d7d51efc..4f61027a09 100644 --- a/phpBB/includes/acp/acp_styles.php +++ b/phpBB/includes/acp/acp_styles.php @@ -820,7 +820,7 @@ parse_css_file = {PARSE_CSS_FILE} // 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) + 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]]); @@ -858,6 +858,15 @@ parse_css_file = {PARSE_CSS_FILE} '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' => htmlspecialchars($template_data), @@ -998,27 +1007,23 @@ parse_css_file = {PARSE_CSS_FILE} */ function edit_theme($theme_id) { - global $phpbb_root_path, $phpbb_admin_path, $phpEx, $config, $db, $cache, $user, $template, $safe_mode; + global $phpbb_root_path, $phpEx, $config, $db, $cache, $user, $template, $safe_mode; $this->page_title = 'EDIT_THEME'; + $filelist = $filelist_cats = array(); + // we want newlines no carriage returns! - $_POST['css_data'] = (isset($_POST['css_data']) && !empty($_POST['css_data'])) ? str_replace(array("\r\n", "\r"), array("\n", "\n"), $_POST['css_data']) : ''; + $_POST['template_data'] = (isset($_POST['template_data']) && !empty($_POST['template_data'])) ? str_replace(array("\r\n", "\r"), array("\n", "\n"), $_POST['template_data']) : ''; - // get user input + $theme_data = (STRIP) ? stripslashes($_POST['template_data']) : $_POST['template_data']; + $theme_file = request_var('template_file', ''); $text_rows = max(5, min(999, request_var('text_rows', 20))); - $hide_css = request_var('hidecss', false); - $show_css = !$hide_css && request_var('showcss', false); - $edit_class = request_var('css_class', ''); - $custom_class = request_var('custom_class', ''); - $css_data = (STRIP) ? stripslashes($_POST['css_data']) : $_POST['css_data']; - $submit = isset($_POST['submit']) ? true : false; - $add_custom = isset($_POST['add_custom']) ? true : false; - $matches = array(); - - // no curly brackets inside a CSS block please - $css_data = str_replace(array('{', '}'), '', $css_data); + $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_storedb, theme_path, theme_name, theme_data FROM ' . STYLES_THEME_TABLE . " @@ -1031,383 +1036,152 @@ parse_css_file = {PARSE_CSS_FILE} } $db->sql_freeresult($result); - $stylesheet_path = $phpbb_root_path . 'styles/' . $theme_info['theme_path'] . '/theme/stylesheet.css'; - // Get the CSS data from either database or filesystem - if (!$theme_info['theme_storedb']) + // save changes to the theme if the user submitted any + if ($save_changes) { - if (!file_exists($stylesheet_path) || !($stylesheet = file_get_contents($stylesheet_path))) - { - trigger_error($user->lang['NO_THEME'] . adm_back_link($this->u_action), E_USER_WARNING); - } - } - else - { - $stylesheet = &$theme_info['theme_data']; - } - - // Pull out a list of classes - $classes = array(); - if (preg_match_all('/^([a-z0-9\.,:#_\->* \t]+?)[ \t\n]*?\{.*?\}/msi', $stylesheet, $matches)) - { - $classes = $matches[1]; - } - - // Generate html for the list of classes - $s_hidden_fields = array(); - $s_classes = ''; - sort($classes); - foreach ($classes as $class) - { - $selected = ($class == $edit_class) ? ' selected="selected"' : ''; - $s_classes .= '<option value="' . $class . '" title="' . $class . '"' . $selected . '>' . truncate_string($class, 40, false, '...') . '</option>'; - } - - $template->assign_vars(array( - 'S_EDIT_THEME' => true, - 'S_SHOWCSS' => $show_css, - 'S_CLASSES' => $s_classes, - 'S_CLASS' => $edit_class, - - 'U_ACTION' => $this->u_action . "&action=edit&id=$theme_id&showcss=$show_css&text_rows=$text_rows", - 'U_BACK' => $this->u_action, - - 'SELECTED_THEME' => $theme_info['theme_name'], - 'TEXT_ROWS' => $text_rows) - ); - - // only continue if we are really editing anything - if (!$edit_class && !$add_custom) - { - return; - } - - // These are the elements for the simple view - $match_elements = array( - 'colors' => array('background-color', 'color',), - 'sizes' => array('font-size', 'line-height',), - 'images' => array('background-image',), - 'repeat' => array('background-repeat',), - 'other' => array('font-weight', 'font-family', 'font-style', 'text-decoration',), - ); - - // Used in an sprintf statement to generate appropriate output for rawcss mode - $map_elements = array( - 'colors' => '%s', - 'sizes' => '%1.10f', - 'images' => 'url(\'./%s\')', - 'repeat' => '%s', - 'other' => '%s', - ); - - $units = array('px', '%', 'em', 'pt'); - $repeat_types = array( - '' => $user->lang['UNSET'], - 'none' => $user->lang['REPEAT_NO'], - 'repeat-x' => $user->lang['REPEAT_X'], - 'repeat-y' => $user->lang['REPEAT_Y'], - 'both' => $user->lang['REPEAT_ALL'], - ); - - // Fill css_data with the class contents from the stylesheet - // in case we just selected a class and it's not filled yet - if (!$css_data && !$submit && !isset($_POST['hidecss']) && !isset($_POST['showcss']) && !$add_custom) - { - preg_match('#^[ \t]*?' . preg_quote($edit_class, '#') . '[ \t\n]*?\{(.*?)\}#ms', $stylesheet, $matches); + // Get the filesystem location of the current file + $file = "{$phpbb_root_path}styles/{$theme_info['theme_path']}/theme/$theme_file"; + $additional = ''; + $message = $user->lang['THEME_UPDATED']; - if (!isset($matches[1])) + // If the theme is stored on the filesystem try to write the file else store it in the database + if (!$safe_mode && !$theme_info['theme_storedb'] && file_exists($file) && @is_writable($file)) { - trigger_error($user->lang['NO_CLASS'] . adm_back_link($this->u_action), E_USER_WARNING); + if (!($fp = fopen($file, 'wb'))) + { + trigger_error($user->lang['NO_THEME'] . adm_back_link($this->u_action), E_USER_WARNING); + } + fwrite($fp, $theme_data); + fclose($fp); } - - $css_data = implode(";\n", array_diff(array_map('trim', explode("\n", preg_replace("#;[\n]*#s", "\n", $matches[1]))), array(''))); - if ($css_data) + else { - $css_data .= ';'; - } - } - - // If we don't show raw css and the user did not submit any modification - // then generate a list of css elements and output them via the template - if (!$show_css && !$submit && !$add_custom) - { - $css_elements = array_diff(array_map('trim', explode("\n", preg_replace("#;[\n]*#s", "\n", $css_data))), array('')); + // Write stylesheet to db + $sql_ary = array( + 'theme_mtime' => time(), + 'theme_storedb' => 1, + 'theme_data' => $this->db_theme_data($theme_info, $theme_data), + ); + $sql = 'UPDATE ' . STYLES_THEME_TABLE . ' + SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' + WHERE theme_id = ' . $theme_id; + $db->sql_query($sql); - // Grab list of potential images for the "images" type - $img_filelist = filelist($phpbb_root_path . 'styles/' . $theme_info['theme_name'] . '/theme'); + $cache->destroy('sql', STYLES_THEME_TABLE); - foreach ($match_elements as $type => $match_ary) - { - foreach ($match_ary as $match) + // notify the user if the theme was not stored in the db before his modification + if (!$theme_info['theme_storedb']) { - $var = str_replace('-', '_', $match); - $value = ''; - $unit = ''; - - if (sizeof($css_elements)) - { - // first read in the setting - foreach ($css_elements as $key => $element) - { - if (preg_match('#^' . preg_quote($match, '#') . ':[ \t\n]*?(.*?)$#', $element, $matches)) - { - switch ($type) - { - case 'sizes': - $value = trim($matches[1]); - - if (preg_match('#(.*?)(px|%|em|pt)#', $matches[1], $matches)) - { - $unit = trim($matches[2]); - $value = trim($matches[1]); - } - break; - - case 'images': - if (preg_match('#url\(\'(.*?)\'\)#', $matches[1], $matches)) - { - $value = trim($matches[1]); - } - break; - - case 'colors': - $value = trim($matches[1]); - if ($value[0] == '#') - { - $value = substr($value, 1); - } - break; - - default: - $value = trim($matches[1]); - } - - // Remove this element from array - unset($css_elements[$key]); - break; - } - } - } - - // then display it in the template - switch ($type) - { - case 'sizes': - // generate a list of units - $s_units = ''; - foreach ($units as $unit_option) - { - $selected = ($unit_option == $unit) ? ' selected="selected"' : ''; - $s_units .= "<option value=\"$unit_option\"$selected>$unit_option</option>"; - } - $s_units = '<option value=""' . (($unit == '') ? ' selected="selected"' : '') . '>' . $user->lang['NO_UNIT'] . '</option>' . $s_units; - - $template->assign_vars(array( - strtoupper($var) => htmlspecialchars($value), - 'S_' . strtoupper($var) . '_UNITS' => $s_units) - ); - break; - - case 'images': - // generate a list of images for this setting - $s_imglist = ''; - foreach ($img_filelist as $path => $img_ary) - { - foreach ($img_ary as $img) - { - $img = htmlspecialchars(((substr($path, 0, 1) == '/') ? substr($path, 1) : $path) . $img); - - $selected = (preg_match('#' . preg_quote($img) . '$#', $value)) ? ' selected="selected"' : ''; - $s_imglist .= "<option value=\"$img\"$selected>$img</option>"; - } - } - $s_imglist = '<option value=""' . (($value == '') ? ' selected="selected"' : '') . '>' . $user->lang['NO_IMAGE'] . '</option>' . $s_imglist; - - $template->assign_vars(array( - 'S_' . strtoupper($var) => $s_imglist) - ); - unset($s_imglist); - break; - - case 'repeat': - // generate a list of repeat options - $s_repeat_types = ''; - foreach ($repeat_types as $repeat_type => $repeat_lang) - { - $selected = ($value == $repeat_type) ? ' selected="selected"' : ''; - $s_repeat_types .= "<option value=\"$repeat_type\"$selected>$repeat_lang</option>"; - } - - $template->assign_vars(array( - 'S_' . strtoupper($var) => $s_repeat_types) - ); - - default: - $template->assign_vars(array( - strtoupper($var) => htmlspecialchars($value)) - ); - } + add_log('admin', 'LOG_THEME_EDIT_DETAILS', $theme_info['theme_name']); + $message .= '<br />' . $user->lang['EDIT_THEME_STORED_DB']; } } + $cache->destroy('sql', STYLES_THEME_TABLE); + add_log('admin', (!$theme_info['theme_storedb']) ? 'LOG_THEME_EDIT_FILE' : 'LOG_THEME_EDIT', $theme_info['theme_name'], (!$theme_info['theme_storedb']) ? $theme_file : ''); - // Any remaining elements must be custom data so we save that in a hidden field - if (sizeof($css_elements)) - { - $s_hidden_fields['cssother'] = implode(' ;; ', $css_elements); - } - - unset($img_filelist, $css_elements); + trigger_error($message . adm_back_link($this->u_action . "&action=edit&id=$theme_id&template_file=$theme_file&text_rows=$text_rows")); } - // else if we are showing raw css or the user submitted data from the simple view - // then we need to turn the given information into raw css - else if (!$css_data && !$add_custom) - { - foreach ($match_elements as $type => $match_ary) - { - foreach ($match_ary as $match) - { - $var = str_replace('-', '_', $match); - $value = ''; - $unit = ''; - - // retrieve and validate data for this setting - switch ($type) - { - case 'sizes': - $value = request_var($var, 0.0); - $unit = request_var($var . '_unit', ''); - - if ((request_var($var, '') === '') || !in_array($unit, $units)) - { - $value = ''; - } - break; - - case 'images': - $value = str_replace('..', '.', request_var($var, '')); - if (!file_exists("{$phpbb_root_path}styles/{$theme_info['theme_path']}/theme/" . $value)) - { - $value = ''; - } - break; - - case 'colors': - $value = request_var($var, ''); - if (preg_match('#^(?:[A-F0-9]{6}|[A-F0-9]{3})$#', $value)) - { - $value = '#' . $value; - } - break; - - case 'repeat': - $value = request_var($var, ''); - if (!isset($repeat_types[$value])) - { - $value = ''; - } - break; - default: - $value = htmlspecialchars_decode(request_var($var, '')); - } + // Generate a category array containing theme filenames + if (!$theme_info['theme_storedb']) + { + $theme_path = "{$phpbb_root_path}styles/{$theme_info['theme_path']}/theme"; - // use the element mapping to create raw css code - if ($value !== '') - { - $css_data .= $match . ': ' . ($type == 'sizes' ? rtrim(sprintf($map_elements[$type], $value), '0') : sprintf($map_elements[$type], $value)) . $unit . ";\n"; - } - } - } + $filelist = filelist($theme_path, '', 'css'); - // append additional data sent to us - if ($other = request_var('cssother', '')) + if ($theme_file) { - $css_data .= str_replace(' ;; ', ";\n", $other) . ';'; - $css_data = preg_replace("#\*/;\n#", "*/\n", $css_data); + if (!file_exists($theme_path . "/$theme_file") || !($theme_data = file_get_contents($theme_path . "/$theme_file"))) + { + trigger_error($user->lang['NO_THEME'] . adm_back_link($this->u_action), E_USER_WARNING); + } } } - // make sure we have $show_css set, so we can link to the show_css page if we need to - else if (!$hide_css) + else { - $show_css = true; + $theme_data = &$theme_info['theme_data']; } - if ($submit || $add_custom) + // Now create the categories + $filelist_cats[''] = array(); + foreach ($filelist as $pathfile => $file_ary) { - if ($submit) + // Use the directory name as category name + if (!empty($pathfile)) { - // if the user submitted a modification replace the old class definition in the stylesheet - // with the new one - if (preg_match('#^' . preg_quote($edit_class, '#') . '[ \t\n]*?\{(.*?)\}#ms', $stylesheet)) + $filelist_cats[$pathfile] = array(); + foreach ($file_ary as $file) { - $stylesheet = preg_replace('#^(' . preg_quote($edit_class, '#') . '[ \t\n]*?\{).*?(\})#ms', "$1\n\t" . str_replace("\n", "\n\t", $css_data) . "\n$2", $stylesheet); + $filelist_cats[$pathfile][$pathfile . $file] = $file; } - $message = $user->lang['THEME_UPDATED']; } + // or if it's in the main category use the word before the first underscore to group files else { - // check whether the custom class name is valid - if (!preg_match('/^[a-z0-9\.,:#_\ \t->*]+$/i', $custom_class)) - { - trigger_error($user->lang['THEME_ERR_CLASS_CHARS'] . adm_back_link($this->u_action . "&action=edit&id=$theme_id&text_rows=$text_rows"), E_USER_WARNING); - } - else + $cats = array(); + foreach ($file_ary as $file) { - // append an empty class definition to the stylesheet - $stylesheet .= "\n$custom_class {\n\t\n}"; - $message = $user->lang['THEME_CLASS_ADDED']; + $cats[] = substr($file, 0, strpos($file, '_')); + $filelist_cats[substr($file, 0, strpos($file, '_'))][$file] = $file; } - } - // where should we store the CSS? - if (!$safe_mode && !$theme_info['theme_storedb'] && file_exists($stylesheet_path) && @is_writable($stylesheet_path)) - { - // write stylesheet to file - if (!($fp = fopen($stylesheet_path, 'wb'))) + $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++) { - trigger_error($user->lang['NO_THEME'] . adm_back_link($this->u_action), E_USER_WARNING); + 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]]); + } } - fwrite($fp, $stylesheet); - fclose($fp); + unset($cats); } - else - { - // Write stylesheet to db - $sql_ary = array( - 'theme_mtime' => time(), - 'theme_storedb' => 1, - 'theme_data' => $this->db_theme_data($theme_info, $stylesheet), - ); - $sql = 'UPDATE ' . STYLES_THEME_TABLE . ' - SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' - WHERE theme_id = ' . $theme_id; - $db->sql_query($sql); + } + unset($filelist); - $cache->destroy('sql', STYLES_THEME_TABLE); + // Generate list of categorised theme files + $tpl_options = ''; + ksort($filelist_cats); + foreach ($filelist_cats as $category => $tpl_ary) + { + ksort($tpl_ary); - // notify the user if the template was not stored in the db before his modification - if (!$theme_info['theme_storedb']) - { - add_log('admin', 'LOG_THEME_EDIT_DETAILS', $theme_info['theme_name']); - $message .= '<br />' . $user->lang['EDIT_THEME_STORED_DB']; - } + if (!empty($category)) + { + $tpl_options .= '<option class="sep" value="">' . $category . '</option>'; } - $cache->destroy('sql', STYLES_THEME_TABLE); - add_log('admin', ($add_custom) ? 'LOG_THEME_EDIT_ADD' : 'LOG_THEME_EDIT', $theme_info['theme_name'], ($add_custom) ? $custom_class : $edit_class); - - trigger_error($message . adm_back_link($this->u_action . "&action=edit&id=$theme_id&css_class=$edit_class&showcss=$show_css&text_rows=$text_rows")); + foreach ($tpl_ary as $filename => $file) + { + $selected = ($theme_file == $filename) ? ' selected="selected"' : ''; + $tpl_options .= '<option value="' . $filename . '"' . $selected . '>' . $file . '</option>'; + } } - unset($matches); - - $s_hidden_fields['css_class'] = $edit_class; $template->assign_vars(array( - 'S_HIDDEN_FIELDS' => build_hidden_fields($s_hidden_fields), + 'S_EDIT_THEME' => true, + 'S_HIDDEN_FIELDS' => build_hidden_fields(array('template_file' => $theme_file)), + 'S_THEME_IN_DB' => $theme_info['theme_storedb'], + 'S_TEMPLATES' => $tpl_options, - 'U_SWATCH' => append_sid("{$phpbb_admin_path}swatch.$phpEx", 'form=acp_theme') . '&name=', - 'UA_SWATCH' => append_sid("{$phpbb_admin_path}swatch.$phpEx", 'form=acp_theme', false) . '&name=', + 'U_ACTION' => $this->u_action . "&action=edit&id=$theme_id&text_rows=$text_rows", + 'U_BACK' => $this->u_action, - 'CSS_DATA' => htmlspecialchars($css_data)) + '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' => htmlspecialchars($theme_data), + 'TEXT_ROWS' => $text_rows) ); } |