diff options
Diffstat (limited to 'phpBB/phpbb')
-rw-r--r-- | phpBB/phpbb/console/command/extension/enable.php | 7 | ||||
-rw-r--r-- | phpBB/phpbb/content_visibility.php | 126 | ||||
-rw-r--r-- | phpBB/phpbb/datetime.php | 6 | ||||
-rw-r--r-- | phpBB/phpbb/db/migration/data/v32x/email_force_sender.php | 37 | ||||
-rw-r--r-- | phpBB/phpbb/db/migration/data/v32x/merge_duplicate_bbcodes.php | 75 | ||||
-rw-r--r-- | phpBB/phpbb/db/migration/data/v32x/update_prosilver_bitfield.php | 39 | ||||
-rw-r--r-- | phpBB/phpbb/install/helper/iohandler/ajax_iohandler.php | 6 | ||||
-rw-r--r-- | phpBB/phpbb/install/module/update_filesystem/task/diff_files.php | 43 | ||||
-rw-r--r-- | phpBB/phpbb/install/module/update_filesystem/task/download_updated_files.php | 7 | ||||
-rw-r--r-- | phpBB/phpbb/search/fulltext_mysql.php | 19 | ||||
-rw-r--r-- | phpBB/phpbb/textformatter/s9e/bbcode_merger.php | 180 | ||||
-rw-r--r-- | phpBB/phpbb/textformatter/s9e/factory.php | 9 | ||||
-rw-r--r-- | phpBB/phpbb/textformatter/s9e/parser.php | 6 | ||||
-rw-r--r-- | phpBB/phpbb/textreparser/base.php | 4 |
14 files changed, 489 insertions, 75 deletions
diff --git a/phpBB/phpbb/console/command/extension/enable.php b/phpBB/phpbb/console/command/extension/enable.php index f92de0069c..a6f5b10e86 100644 --- a/phpBB/phpbb/console/command/extension/enable.php +++ b/phpBB/phpbb/console/command/extension/enable.php @@ -37,6 +37,13 @@ class enable extends command $io = new SymfonyStyle($input, $output); $name = $input->getArgument('extension-name'); + + if (!$this->manager->is_available($name)) + { + $io->error($this->user->lang('CLI_EXTENSION_NOT_EXIST', $name)); + return 1; + } + $extension = $this->manager->get_extension($name); if (!$extension->is_enableable()) diff --git a/phpBB/phpbb/content_visibility.php b/phpBB/phpbb/content_visibility.php index bf7dc2c703..237300894b 100644 --- a/phpBB/phpbb/content_visibility.php +++ b/phpBB/phpbb/content_visibility.php @@ -131,6 +131,42 @@ class content_visibility return (int) $data[$mode . '_approved'] + (int) $data[$mode . '_unapproved'] + (int) $data[$mode . '_softdeleted']; } + + /** + * Check topic/post visibility for a given forum ID + * + * Note: Read permissions are not checked. + * + * @param $mode string Either "topic" or "post" + * @param $forum_id int The forum id is used for permission checks + * @param $data array Array with item information to check visibility + * @return bool True if the item is visible, false if not + */ + public function is_visible($mode, $forum_id, $data) + { + $is_visible = $this->auth->acl_get('m_approve', $forum_id) || $data[$mode . '_visibility'] == ITEM_APPROVED; + + /** + * Allow changing the result of calling is_visible + * + * @event core.phpbb_content_visibility_is_visible + * @var bool is_visible Default visibility condition, to be modified by extensions if needed. + * @var string mode Either "topic" or "post" + * @var int forum_id Forum id of the current item + * @var array data Array of item information + * @since 3.2.2-RC1 + */ + $vars = array( + 'is_visible', + 'mode', + 'forum_id', + 'data', + ); + extract($this->phpbb_dispatcher->trigger_event('core.phpbb_content_visibility_is_visible', compact($vars))); + + return $is_visible; + } + /** * Create topic/post visibility SQL for a given forum ID * @@ -176,10 +212,14 @@ class content_visibility if ($this->auth->acl_get('m_approve', $forum_id)) { - return $where_sql . '1 = 1'; + $where_sql .= '1 = 1'; + } + else + { + $where_sql .= $table_alias . $mode . '_visibility = ' . ITEM_APPROVED; } - return $where_sql . $table_alias . $mode . '_visibility = ' . ITEM_APPROVED; + return '(' . $where_sql . ')'; } /** @@ -195,16 +235,21 @@ class content_visibility */ public function get_forums_visibility_sql($mode, $forum_ids = array(), $table_alias = '') { - $where_sql = '('; + $where_sql = ''; - $approve_forums = array_intersect($forum_ids, array_keys($this->auth->acl_getf('m_approve', true))); + $approve_forums = array_keys($this->auth->acl_getf('m_approve', true)); + if (!empty($forum_ids) && !empty($approve_forums)) + { + $approve_forums = array_intersect($forum_ids, $approve_forums); + $forum_ids = array_diff($forum_ids, $approve_forums); + } $get_forums_visibility_sql_overwrite = false; /** * Allow changing the result of calling get_forums_visibility_sql * * @event core.phpbb_content_visibility_get_forums_visibility_before - * @var string where_sql The action the user tried to execute + * @var string where_sql Extra visibility conditions. It must end with either an SQL "AND" or an "OR" * @var string mode Either "topic" or "post" depending on the query this is being used in * @var array forum_ids Array of forum ids which the posts/topics are limited to * @var string table_alias Table alias to prefix in SQL queries @@ -229,33 +274,13 @@ class content_visibility return $get_forums_visibility_sql_overwrite; } - if (sizeof($approve_forums)) - { - // Remove moderator forums from the rest - $forum_ids = array_diff($forum_ids, $approve_forums); - - if (!sizeof($forum_ids)) - { - // The user can see all posts/topics in all specified forums - return $where_sql . $this->db->sql_in_set($table_alias . 'forum_id', $approve_forums) . ')'; - } - else - { - // Moderator can view all posts/topics in some forums - $where_sql .= $this->db->sql_in_set($table_alias . 'forum_id', $approve_forums) . ' OR '; - } - } - else - { - // The user is just a normal user - return $where_sql . $table_alias . $mode . '_visibility = ' . ITEM_APPROVED . ' - AND ' . $this->db->sql_in_set($table_alias . 'forum_id', $forum_ids, false, true) . ')'; - } - + // Moderator can view all posts/topics in the moderated forums + $where_sql .= '(' . $this->db->sql_in_set($table_alias . 'forum_id', $approve_forums, false, true) . ' OR '; + // Normal user can view approved items only $where_sql .= '(' . $table_alias . $mode . '_visibility = ' . ITEM_APPROVED . ' - AND ' . $this->db->sql_in_set($table_alias . 'forum_id', $forum_ids) . '))'; + AND ' . $this->db->sql_in_set($table_alias . 'forum_id', $forum_ids, false, true) . '))'; - return $where_sql; + return '(' . $where_sql . ')'; } /** @@ -281,12 +306,12 @@ class content_visibility * Allow changing the result of calling get_global_visibility_sql * * @event core.phpbb_content_visibility_get_global_visibility_before - * @var array where_sqls The action the user tried to execute + * @var array where_sqls Array of extra visibility conditions. Will be joined by imploding with "OR". * @var string mode Either "topic" or "post" depending on the query this is being used in * @var array exclude_forum_ids Array of forum ids the current user doesn't have access to * @var string table_alias Table alias to prefix in SQL queries * @var array approve_forums Array of forums where the user has m_approve permissions - * @var string visibility_sql_overwrite Forces the function to return an implosion of where_sqls (joined by "OR") + * @var string visibility_sql_overwrite If not empty, forces the function to return visibility_sql_overwrite after executing the event * @since 3.1.3-RC1 */ $vars = array( @@ -304,24 +329,17 @@ class content_visibility return $visibility_sql_overwrite; } - if (sizeof($exclude_forum_ids)) - { - $where_sqls[] = '(' . $this->db->sql_in_set($table_alias . 'forum_id', $exclude_forum_ids, true) . ' - AND ' . $table_alias . $mode . '_visibility = ' . ITEM_APPROVED . ')'; - } - else - { - $where_sqls[] = $table_alias . $mode . '_visibility = ' . ITEM_APPROVED; - } + // Include approved items in all forums but the excluded + $where_sqls[] = '(' . $this->db->sql_in_set($table_alias . 'forum_id', $exclude_forum_ids, true, true) . ' + AND ' . $table_alias . $mode . '_visibility = ' . ITEM_APPROVED . ')'; + // If user has moderator permissions, add everything in the moderated forums if (sizeof($approve_forums)) { $where_sqls[] = $this->db->sql_in_set($table_alias . 'forum_id', $approve_forums); - return '(' . implode(' OR ', $where_sqls) . ')'; } - // There is only one element, so we just return that one - return $where_sqls[0]; + return '(' . implode(' OR ', $where_sqls) . ')'; } /** @@ -437,12 +455,13 @@ class content_visibility * @var int topic_id Topic of the post IDs to be modified. * @var int forum_id Forum ID that the topic_id resides in. * @var int user_id User ID doing this action. - * @var int timestamp Timestamp of this action. + * @var int time Timestamp of this action. * @var string reason Reason specified by the user for this change. * @var bool is_starter Are we changing the topic's starter? * @var bool is_latest Are we changing the topic's latest post? * @var array data The data array for this action. * @since 3.1.10-RC1 + * @changed 3.2.2-RC1 Use time instead of non-existent timestamp */ $vars = array( 'visibility', @@ -450,7 +469,7 @@ class content_visibility 'topic_id', 'forum_id', 'user_id', - 'timestamp', + 'time', 'reason', 'is_starter', 'is_latest', @@ -622,12 +641,13 @@ class content_visibility * @var int topic_id Topic of the post IDs to be modified. * @var int forum_id Forum ID that the topic_id resides in. * @var int user_id User ID doing this action. - * @var int timestamp Timestamp of this action. + * @var int time Timestamp of this action. * @var string reason Reason specified by the user for this change. * @var bool is_starter Are we changing the topic's starter? * @var bool is_latest Are we changing the topic's latest post? * @var array data The data array for this action. * @since 3.1.10-RC1 + * @changed 3.2.2-RC1 Use time instead of non-existent timestamp */ $vars = array( 'visibility', @@ -635,7 +655,7 @@ class content_visibility 'topic_id', 'forum_id', 'user_id', - 'timestamp', + 'time', 'reason', 'is_starter', 'is_latest', @@ -709,18 +729,19 @@ class content_visibility * @var int topic_id Topic of the post IDs to be modified. * @var int forum_id Forum ID that the topic_id resides in. * @var int user_id User ID doing this action. - * @var int timestamp Timestamp of this action. + * @var int time Timestamp of this action. * @var string reason Reason specified by the user for this change. * @var bool force_update_all Force an update on all posts within the topic, regardless of their current approval state. * @var array data The data array for this action. * @since 3.1.10-RC1 + * @changed 3.2.2-RC1 Use time instead of non-existent timestamp */ $vars = array( 'visibility', 'topic_id', 'forum_id', 'user_id', - 'timestamp', + 'time', 'reason', 'force_update_all', 'data', @@ -758,18 +779,19 @@ class content_visibility * @var int topic_id Topic of the post IDs to be modified. * @var int forum_id Forum ID that the topic_id resides in. * @var int user_id User ID doing this action. - * @var int timestamp Timestamp of this action. + * @var int time Timestamp of this action. * @var string reason Reason specified by the user for this change. * @var bool force_update_all Force an update on all posts within the topic, regardless of their current approval state. * @var array data The data array for this action. * @since 3.1.10-RC1 + * @changed 3.2.2-RC1 Use time instead of non-existent timestamp */ $vars = array( 'visibility', 'topic_id', 'forum_id', 'user_id', - 'timestamp', + 'time', 'reason', 'force_update_all', 'data', diff --git a/phpBB/phpbb/datetime.php b/phpBB/phpbb/datetime.php index 63cdba90fd..4b799b6219 100644 --- a/phpBB/phpbb/datetime.php +++ b/phpBB/phpbb/datetime.php @@ -60,6 +60,12 @@ class datetime extends \DateTime public function format($format = '', $force_absolute = false) { $format = $format ? $format : $this->user->date_format; + + if (substr($this->user->lang_name, 0,2) != 'en') + { + $format = preg_replace('/([^\\\])S/','$1', $format); + } + $format = self::format_cache($format, $this->user); $relative = ($format['is_short'] && !$force_absolute); $now = new self($this->user, 'now', $this->user->timezone); diff --git a/phpBB/phpbb/db/migration/data/v32x/email_force_sender.php b/phpBB/phpbb/db/migration/data/v32x/email_force_sender.php new file mode 100644 index 0000000000..5319b7f76e --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v32x/email_force_sender.php @@ -0,0 +1,37 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v32x; + +class email_force_sender extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v32x\v321', + ); + } + + public function effectively_installed() + { + return isset($this->config['email_force_sender']); + } + + public function update_data() + { + return array( + array('config.add', array('email_force_sender', '0')), + array('config.remove', array('email_function_name')), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v32x/merge_duplicate_bbcodes.php b/phpBB/phpbb/db/migration/data/v32x/merge_duplicate_bbcodes.php new file mode 100644 index 0000000000..3bf442bab5 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v32x/merge_duplicate_bbcodes.php @@ -0,0 +1,75 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v32x; + +class merge_duplicate_bbcodes extends \phpbb\db\migration\migration +{ + public function update_data() + { + return [ + ['custom', [[$this, 'update_bbcodes_table']]], + ]; + } + + public function update_bbcodes_table() + { + $sql = 'SELECT bbcode_id, bbcode_tag, bbcode_helpline, bbcode_match, bbcode_tpl FROM ' . BBCODES_TABLE; + $result = $this->sql_query($sql); + $bbcodes = []; + while ($row = $this->db->sql_fetchrow($result)) + { + $variant = (substr($row['bbcode_tag'], -1) === '=') ? 'with': 'without'; + $bbcode_name = rtrim($row['bbcode_tag'], '='); + $bbcodes[$bbcode_name][$variant] = $row; + } + $this->db->sql_freeresult($result); + + foreach ($bbcodes as $bbcode_name => $variants) + { + if (count($variants) === 2) + { + $this->merge_bbcodes($variants['without'], $variants['with']); + } + } + } + + protected function merge_bbcodes(array $without, array $with) + { + $merged = $this->container->get('text_formatter.s9e.bbcode_merger')->merge_bbcodes( + [ + 'usage' => $without['bbcode_match'], + 'template' => $without['bbcode_tpl'] + ], + [ + 'usage' => $with['bbcode_match'], + 'template' => $with['bbcode_tpl'] + ] + ); + $bbcode_data = [ + 'bbcode_tag' => $without['bbcode_tag'], + 'bbcode_helpline' => $without['bbcode_helpline'] . ' | ' . $with['bbcode_helpline'], + 'bbcode_match' => $merged['usage'], + 'bbcode_tpl' => $merged['template'] + ]; + + $sql = 'UPDATE ' . BBCODES_TABLE . ' + SET ' . $this->db->sql_build_array('UPDATE', $bbcode_data) . ' + WHERE bbcode_id = ' . $without['bbcode_id']; + $this->sql_query($sql); + + $sql = 'DELETE FROM ' . BBCODES_TABLE . ' + WHERE bbcode_id = ' . $with['bbcode_id']; + $this->sql_query($sql); + } +} diff --git a/phpBB/phpbb/db/migration/data/v32x/update_prosilver_bitfield.php b/phpBB/phpbb/db/migration/data/v32x/update_prosilver_bitfield.php new file mode 100644 index 0000000000..6e51a01834 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v32x/update_prosilver_bitfield.php @@ -0,0 +1,39 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v32x; + +class update_prosilver_bitfield extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v32x\v321', + ); + } + + public function update_data() + { + return array( + array('custom', array(array($this, 'update_bbcode_bitfield'))), + ); + } + + public function update_bbcode_bitfield() + { + $sql = 'UPDATE ' . STYLES_TABLE . " + SET bbcode_bitfield = '//g=' + WHERE style_path = 'prosilver'"; + $this->sql_query($sql); + } +} diff --git a/phpBB/phpbb/install/helper/iohandler/ajax_iohandler.php b/phpBB/phpbb/install/helper/iohandler/ajax_iohandler.php index bce0149890..dd584eff30 100644 --- a/phpBB/phpbb/install/helper/iohandler/ajax_iohandler.php +++ b/phpBB/phpbb/install/helper/iohandler/ajax_iohandler.php @@ -187,6 +187,7 @@ class ajax_iohandler extends iohandler_base $tpl_ary['KEY'] = $input_name; $tpl_ary['S_EXPLAIN'] = false; $tpl_ary['DISABLED'] = isset($input_options['disabled']) ? $input_options['disabled'] : false; + $tpl_ary['IS_SECONDARY'] = isset($input_options['is_secondary']) ? $input_options['is_secondary'] : false; if (isset($input_options['default'])) { @@ -218,6 +219,11 @@ class ajax_iohandler extends iohandler_base $this->template->assign_block_vars($block_name, $tpl_ary); } + if (isset($form['database_update_submit']) && !$form['database_update_submit']['disabled']) + { + $this->template->assign_var('FORM_TITLE', $this->language->lang('UPDATE_CONTINUE_UPDATE_PROCESS')); + } + $this->template->assign_var('S_NOT_ONLY_BUTTON_FORM', $not_button_form); if (!$not_button_form) diff --git a/phpBB/phpbb/install/module/update_filesystem/task/diff_files.php b/phpBB/phpbb/install/module/update_filesystem/task/diff_files.php index 1792a3b723..8151a24f2d 100644 --- a/phpBB/phpbb/install/module/update_filesystem/task/diff_files.php +++ b/phpBB/phpbb/install/module/update_filesystem/task/diff_files.php @@ -103,8 +103,8 @@ class diff_files extends task_base $old_path = $this->update_helper->get_path_to_old_update_files(); $new_path = $this->update_helper->get_path_to_new_update_files(); - $files_to_diff = $this->installer_config->get('update_files', array()); - $files_to_diff = $files_to_diff['update_with_diff']; + $update_files = $this->installer_config->get('update_files', array()); + $files_to_diff = $update_files['update_with_diff']; // Set progress bar $this->iohandler->set_task_count(count($files_to_diff), true); @@ -154,7 +154,6 @@ class diff_files extends task_base } $diff = new \diff3($file_contents[0], $file_contents[1], $file_contents[2]); - unset($file_contents); // Handle conflicts if ($diff->get_num_conflicts() !== 0) @@ -162,12 +161,20 @@ class diff_files extends task_base $merge_conflicts[] = $filename; } - // Save merged output - $this->cache->put( - '_file_' . md5($filename), - base64_encode(implode("\n", $diff->merged_output())) - ); + if ($diff->merged_output() !== $file_contents[1]) + { + // Save merged output + $this->cache->put( + '_file_' . md5($filename), + base64_encode(implode("\n", $diff->merged_output())) + ); + } + else + { + unset($update_files['update_with_diff'][$key]); + } + unset($file_contents); unset($diff); } else @@ -199,6 +206,16 @@ class diff_files extends task_base $this->installer_config->set('merge_conflict_list', $merge_conflicts); $this->installer_config->set('file_diff_update_count', $progress_count); + foreach ($update_files as $type => $files) + { + if (empty($files)) + { + unset($update_files[$type]); + } + } + + $this->installer_config->set('update_files', $update_files); + // Request refresh throw new resource_limit_reached_exception(); } @@ -206,6 +223,16 @@ class diff_files extends task_base $this->iohandler->finish_progress('ALL_FILES_DIFFED'); $this->installer_config->set('merge_conflict_list', $merge_conflicts); + + foreach ($update_files as $type => $files) + { + if (empty($files)) + { + unset($update_files[$type]); + } + } + + $this->installer_config->set('update_files', $update_files); } /** diff --git a/phpBB/phpbb/install/module/update_filesystem/task/download_updated_files.php b/phpBB/phpbb/install/module/update_filesystem/task/download_updated_files.php index 21aa93b7ea..0b83e9a79d 100644 --- a/phpBB/phpbb/install/module/update_filesystem/task/download_updated_files.php +++ b/phpBB/phpbb/install/module/update_filesystem/task/download_updated_files.php @@ -99,13 +99,14 @@ class download_updated_files extends task_base // Add form to continue update $this->iohandler->add_user_form_group('UPDATE_CONTINUE_UPDATE_PROCESS', array( 'update_recheck_files_submit' => array( - 'label' => 'UPDATE_RECHECK_UPDATE_FILES', - 'type' => 'submit', + 'label' => 'UPDATE_RECHECK_UPDATE_FILES', + 'type' => 'submit', + 'is_secondary' => empty($file_update_info), ), 'database_update_submit' => array( 'label' => 'UPDATE_CONTINUE_UPDATE_PROCESS', 'type' => 'submit', - 'disabled' => count($file_update_info) > 0, + 'disabled' => !empty($file_update_info), ), )); diff --git a/phpBB/phpbb/search/fulltext_mysql.php b/phpBB/phpbb/search/fulltext_mysql.php index da1aad1c3a..c8df1951e3 100644 --- a/phpBB/phpbb/search/fulltext_mysql.php +++ b/phpBB/phpbb/search/fulltext_mysql.php @@ -591,6 +591,7 @@ class fulltext_mysql extends \phpbb\search\base WHERE MATCH ($sql_match) AGAINST ('" . $this->db->sql_escape(htmlspecialchars_decode($this->search_query)) . "' IN BOOLEAN MODE) $sql_where_options ORDER BY $sql_sort"; + $this->db->sql_return_on_error(true); $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start); while ($row = $this->db->sql_fetchrow($result)) @@ -602,7 +603,7 @@ class fulltext_mysql extends \phpbb\search\base $id_ary = array_unique($id_ary); // if the total result count is not cached yet, retrieve it from the db - if (!$result_count) + if (!$result_count && count($id_ary)) { $sql_found_rows = 'SELECT FOUND_ROWS() as result_count'; $result = $this->db->sql_query($sql_found_rows); @@ -1004,6 +1005,11 @@ class fulltext_mysql extends \phpbb\search\base } } + if (!isset($this->stats['post_text'])) + { + $this->db->sql_query('ALTER TABLE ' . POSTS_TABLE . ' ADD FULLTEXT post_text (post_text)'); + } + $this->db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE); return false; @@ -1039,6 +1045,11 @@ class fulltext_mysql extends \phpbb\search\base $alter[] = 'DROP INDEX post_content'; } + if (isset($this->stats['post_text'])) + { + $alter[] = 'DROP INDEX post_text'; + } + if (sizeof($alter)) { $this->db->sql_query('ALTER TABLE ' . POSTS_TABLE . ' ' . implode(', ', $alter)); @@ -1059,7 +1070,7 @@ class fulltext_mysql extends \phpbb\search\base $this->get_stats(); } - return isset($this->stats['post_subject']) && isset($this->stats['post_content']); + return isset($this->stats['post_subject']) && isset($this->stats['post_content']) && isset($this->stats['post_text']); } /** @@ -1103,6 +1114,10 @@ class fulltext_mysql extends \phpbb\search\base { $this->stats['post_subject'] = $row; } + else if ($row['Key_name'] == 'post_text') + { + $this->stats['post_text'] = $row; + } else if ($row['Key_name'] == 'post_content') { $this->stats['post_content'] = $row; diff --git a/phpBB/phpbb/textformatter/s9e/bbcode_merger.php b/phpBB/phpbb/textformatter/s9e/bbcode_merger.php new file mode 100644 index 0000000000..72b1473751 --- /dev/null +++ b/phpBB/phpbb/textformatter/s9e/bbcode_merger.php @@ -0,0 +1,180 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\textformatter\s9e; + +use phpbb\textformatter\s9e\factory; +use s9e\TextFormatter\Configurator\Helpers\TemplateHelper; +use s9e\TextFormatter\Configurator\Items\UnsafeTemplate; + +class bbcode_merger +{ + /** + * @var \s9e\TextFormatter\Configurator $configurator Configurator instance used to inspect BBCodes + */ + protected $configurator; + + /** + * @param \phpbb\textformatter\s9e\factory $factory + */ + public function __construct(factory $factory) + { + $this->configurator = $factory->get_configurator(); + } + + /** + * Merge two BBCode definitions + * + * All of the arrays contain a "usage" element and a "template" element + * + * @param array $without BBCode definition without an attribute + * @param array $with BBCode definition with an attribute + * @return array Merged definition + */ + public function merge_bbcodes(array $without, array $with) + { + $without = $this->create_bbcode($without); + $with = $this->create_bbcode($with); + + // Select the appropriate strategy for merging this BBCode + if ($this->is_content_bbcode($without, $with)) + { + $merged = $this->merge_content_bbcode($without, $with); + } + else + { + $merged = $this->merge_optional_bbcode($without, $with); + } + + $merged['template'] = $this->normalize_template($merged['template']); + + return $merged; + } + + /** + * Create a custom BBCode for inspection + * + * @param array $definition Original BBCode definition + * @return array Updated definition containing a BBCode object and a Tag + */ + protected function create_bbcode(array $definition) + { + $bbcode = $this->configurator->BBCodes->addCustom( + $definition['usage'], + new UnsafeTemplate($definition['template']) + ); + + $definition['bbcode'] = $bbcode; + $definition['tag'] = $this->configurator->tags[$bbcode->tagName]; + + return $definition; + } + + /** + * Indent given template for readability + * + * @param string $template + * @return string + */ + protected function indent_template($template) + { + $dom = TemplateHelper::loadTemplate($template); + $dom->formatOutput = true; + $template = TemplateHelper::saveTemplate($dom); + + // Remove the first level of indentation if the template starts with whitespace + if (preg_match('(^\\n +)', $template, $m)) + { + $template = str_replace($m[0], "\n", $template); + } + + return trim($template); + } + + /** + * Test whether the two definitions form a "content"-style BBCode + * + * Such BBCodes include the [URL] BBCode, which uses its text content as + * attribute if none is provided + * + * @param array $without BBCode definition without an attribute + * @param array $with BBCode definition with an attribute + * @return array Merged definition + */ + protected function is_content_bbcode(array $without, array $with) + { + // Test whether we find the same non-TEXT token between "]" and "[" in the usage + // as between ">" and "<" in the template + return (preg_match('(\\]\\s*(\\{(?!TEXT)[^}]+\\})\\s*\\[)', $without['usage'], $m) + && preg_match('(>[^<]*?' . preg_quote($m[1]) . '[^>]*?<)s', $without['template'])); + } + + /** + * Merge the two BBCode definitions of a "content"-style BBCode + * + * @param array $without BBCode definition without an attribute + * @param array $with BBCode definition with an attribute + * @return array Merged definition + */ + protected function merge_content_bbcode(array $without, array $with) + { + // Convert [X={X}] into [X={X;useContent}] + $usage = preg_replace('(\\})', ';useContent}', $with['usage'], 1); + + // Use the template from the definition that uses an attribute + $template = $with['tag']->template; + + return ['usage' => $usage, 'template' => $template]; + } + + /** + * Merge the two BBCode definitions of a BBCode with an optional argument + * + * Such BBCodes include the [QUOTE] BBCode, which takes an optional argument + * but otherwise does not behave differently + * + * @param array $without BBCode definition without an attribute + * @param array $with BBCode definition with an attribute + * @return array Merged definition + */ + protected function merge_optional_bbcode(array $without, array $with) + { + // Convert [X={X}] into [X={X?}] + $usage = preg_replace('(\\})', '?}', $with['usage'], 1); + + // Build a template for both versions + $template = '<xsl:choose><xsl:when test="@' . $with['bbcode']->defaultAttribute . '">' . $with['tag']->template . '</xsl:when><xsl:otherwise>' . $without['tag']->template . '</xsl:otherwise></xsl:choose>'; + + return ['usage' => $usage, 'template' => $template]; + } + + /** + * Normalize a template + * + * @param string $template + * @return string + */ + protected function normalize_template($template) + { + // Normalize the template to simplify it + $template = $this->configurator->templateNormalizer->normalizeTemplate($template); + + // Convert xsl:value-of elements back to {L_} tokens where applicable + $template = preg_replace('(<xsl:value-of select="\\$(L_\\w+)"/>)', '{$1}', $template); + + // Beautify the template + $template = $this->indent_template($template); + + return $template; + } +} diff --git a/phpBB/phpbb/textformatter/s9e/factory.php b/phpBB/phpbb/textformatter/s9e/factory.php index d5ad8283d9..1e85856898 100644 --- a/phpBB/phpbb/textformatter/s9e/factory.php +++ b/phpBB/phpbb/textformatter/s9e/factory.php @@ -78,7 +78,7 @@ class factory implements \phpbb\textformatter\cache_interface 'b' => '[B]{TEXT}[/B]', 'code' => '[CODE lang={IDENTIFIER;optional}]{TEXT}[/CODE]', 'color' => '[COLOR={COLOR}]{TEXT}[/COLOR]', - 'email' => '[EMAIL={EMAIL;useContent} subject={TEXT;optional;postFilter=rawurlencode} body={TEXT;optional;postFilter=rawurlencode}]{TEXT}[/EMAIL]', + 'email' => '[EMAIL={EMAIL;useContent} subject={TEXT1;optional;postFilter=rawurlencode} body={TEXT2;optional;postFilter=rawurlencode}]{TEXT}[/EMAIL]', 'flash' => '[FLASH={NUMBER1},{NUMBER2} width={NUMBER1;postFilter=#flashwidth} height={NUMBER2;postFilter=#flashheight} url={URL;useContent} /]', 'i' => '[I]{TEXT}[/I]', 'img' => '[IMG src={IMAGEURL;useContent}]', @@ -266,12 +266,13 @@ class factory implements \phpbb\textformatter\cache_interface ->addParameterByName('logger') ->addParameterByName('max_img_height') ->addParameterByName('max_img_width') - ->markAsSafeAsURL(); + ->markAsSafeAsURL() + ->setJS('UrlFilter.filter'); // Add default BBCodes foreach ($this->get_default_bbcodes($configurator) as $bbcode) { - $configurator->BBCodes->addCustom($bbcode['usage'], $bbcode['template']); + $configurator->BBCodes->addCustom($bbcode['usage'], new UnsafeTemplate($bbcode['template'])); } if (isset($configurator->tags['QUOTE'])) { @@ -355,8 +356,6 @@ class factory implements \phpbb\textformatter\cache_interface $configurator->registeredVars['max_img_width'] = 0; // Load the Emoji plugin and modify its tag's template to obey viewsmilies - $configurator->Emoji->omitImageSize(); - $configurator->Emoji->useSVG(); $tag = $configurator->Emoji->getTag(); $tag->template = '<xsl:choose><xsl:when test="$S_VIEWSMILIES">' . str_replace('class="emoji"', 'class="emoji smilies"', $tag->template) . '</xsl:when><xsl:otherwise><xsl:value-of select="."/></xsl:otherwise></xsl:choose>'; diff --git a/phpBB/phpbb/textformatter/s9e/parser.php b/phpBB/phpbb/textformatter/s9e/parser.php index 05ddfffa11..3698dca224 100644 --- a/phpBB/phpbb/textformatter/s9e/parser.php +++ b/phpBB/phpbb/textformatter/s9e/parser.php @@ -13,7 +13,7 @@ namespace phpbb\textformatter\s9e; -use s9e\TextFormatter\Parser\BuiltInFilters; +use s9e\TextFormatter\Parser\AttributeFilters\UrlFilter; use s9e\TextFormatter\Parser\Logger; /** @@ -196,7 +196,7 @@ class parser implements \phpbb\textformatter\parser_interface public function get_errors() { $errors = array(); - foreach ($this->parser->getLogger()->get() as $entry) + foreach ($this->parser->getLogger()->getLogs() as $entry) { list(, $msg, $context) = $entry; @@ -365,7 +365,7 @@ class parser implements \phpbb\textformatter\parser_interface static public function filter_img_url($url, array $url_config, Logger $logger, $max_height, $max_width) { // Validate the URL - $url = BuiltInFilters::filterUrl($url, $url_config, $logger); + $url = UrlFilter::filter($url, $url_config, $logger); if ($url === false) { return false; diff --git a/phpBB/phpbb/textreparser/base.php b/phpBB/phpbb/textreparser/base.php index 27d7bc1f27..2ee6ea2cb3 100644 --- a/phpBB/phpbb/textreparser/base.php +++ b/phpBB/phpbb/textreparser/base.php @@ -153,8 +153,8 @@ abstract class base implements reparser_interface { // Look for the closing tag inside of a e element, in an element of the same name, e.g. // <e>[/url]</e></URL> - $match = '<e>[/' . $bbcode . ']</e></' . strtoupper($bbcode) . '>'; - if (strpos($record['text'], $match) !== false) + $match = '<e>[/' . $bbcode . ']</e></' . $bbcode . '>'; + if (stripos($record['text'], $match) !== false) { return true; } |