diff options
38 files changed, 694 insertions, 173 deletions
diff --git a/.travis.yml b/.travis.yml index 14cf3e6d6c..a46d825612 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,12 +22,9 @@ matrix: env: DB=mysqli - php: 5.6 env: DB=mysqli - - php: 7.0 - env: DB=mysqli - php: hhvm env: DB=mysqli allow_failures: - - php: 7.0 - php: hhvm fast_finish: true diff --git a/phpBB/adm/style/acp_posting_buttons.html b/phpBB/adm/style/acp_posting_buttons.html index 70b6259689..c3c42f8e82 100644 --- a/phpBB/adm/style/acp_posting_buttons.html +++ b/phpBB/adm/style/acp_posting_buttons.html @@ -63,6 +63,7 @@ <!-- ENDIF --> <!-- ENDIF --> </select> + <!-- EVENT acp_posting_buttons_custom_tags_before --> <!-- BEGIN custom_tags --> <input type="button" class="button2" name="addbbcode{custom_tags.BBCODE_ID}" value="{custom_tags.BBCODE_TAG}" onclick="bbstyle({custom_tags.BBCODE_ID})" title="{custom_tags.BBCODE_HELPLINE}" /> <!-- END custom_tags --> diff --git a/phpBB/docs/events.md b/phpBB/docs/events.md index 0e71bf5c11..abe702eb25 100644 --- a/phpBB/docs/events.md +++ b/phpBB/docs/events.md @@ -343,6 +343,13 @@ acp_posting_buttons_before * Since: 3.1.0-b4 * Purpose: Add content before BBCode posting buttons in the ACP +acp_posting_buttons_custom_tags_before +=== +* Locations: + + adm/style/acp_posting_buttons.html +* Since: 3.1.10-RC1 +* Purpose: Add content before the custom BBCodes in the ACP + acp_profile_contact_before === * Locations: @@ -875,6 +882,22 @@ mcp_topic_options_before * Since: 3.1.6-RC1 * Purpose: Add some options (field, checkbox, ...) before the subject field when split a subject +mcp_topic_postrow_post_details_after +=== +* Locations: + + styles/prosilver/template/mcp_topic.html + + styles/subsilver2/template/mcp_topic.html +* Since: 3.1.10-RC1 +* Purpose: Add content after post details in topic moderation + +mcp_topic_postrow_post_details_before +=== +* Locations: + + styles/prosilver/template/mcp_topic.html + + styles/subsilver2/template/mcp_topic.html +* Since: 3.1.10-RC1 +* Purpose: Add content before post details in topic moderation + mcp_topic_topic_title_after === * Locations: @@ -1503,6 +1526,14 @@ posting_editor_subject_after * Since: 3.1.0-a2 * Purpose: Add field (e.g. textbox) to the posting screen after the subject +posting_editor_subject_append +=== +* Locations: + + styles/prosilver/template/posting_editor.html + + styles/subsilver2/template/posting_body.html +* Since: 3.1.10-RC1 +* Purpose: Add field, text, etc. to the posting after the subject text box + posting_editor_subject_before === * Locations: @@ -1511,6 +1542,14 @@ posting_editor_subject_before * Since: 3.1.0-a2 * Purpose: Add field (e.g. textbox) to the posting screen before the subject +posting_editor_subject_prepend +=== +* Locations: + + styles/prosilver/template/posting_editor.html + + styles/subsilver2/template/posting_body.html +* Since: 3.1.10-RC1 +* Purpose: Add field, text, etc. to the posting before the subject text box + posting_editor_submit_buttons === * Locations: @@ -1572,6 +1611,22 @@ posting_preview_poll_after * Since: 3.1.7-RC1 * Purpose: Add content after the poll preview block +posting_topic_review_row_post_details_after +=== +* Locations: + + styles/prosilver/template/posting_topic_review.html + + styles/subsilver2/template/posting_topic_review.html +* Since: 3.1.10-RC1 +* Purpose: Add content after post details in topic review + +posting_topic_review_row_post_details_before +=== +* Locations: + + styles/prosilver/template/posting_topic_review.html + + styles/subsilver2/template/posting_topic_review.html +* Since: 3.1.10-RC1 +* Purpose: Add content before post details in topic review + posting_topic_title_after === * Locations: @@ -2255,6 +2310,14 @@ viewforum_body_topic_row_prepend * Since: 3.1.7-RC1 * Purpose: Add content at the end of the topic list item. +viewforum_body_topicrow_row_before +=== +* Locations: + + styles/prosilver/template/viewforum_body.html + + styles/subsilver2/template/viewforum_body.html +* Since: 3.1.10-RC1 +* Purpose: Add content before list of topics. + viewforum_buttons_bottom_before === * Locations: diff --git a/phpBB/includes/functions_download.php b/phpBB/includes/functions_download.php index 2c6f62227c..c571de579e 100644 --- a/phpBB/includes/functions_download.php +++ b/phpBB/includes/functions_download.php @@ -284,7 +284,7 @@ function header_filename($file) // There be dragons here. // Not many follows the RFC... - if (strpos($user_agent, 'MSIE') !== false || strpos($user_agent, 'Safari') !== false || strpos($user_agent, 'Konqueror') !== false) + if (strpos($user_agent, 'MSIE') !== false || strpos($user_agent, 'Konqueror') !== false) { return "filename=" . rawurlencode($file); } diff --git a/phpBB/includes/mcp/mcp_forum.php b/phpBB/includes/mcp/mcp_forum.php index 6faf0de35b..9573ecbe0d 100644 --- a/phpBB/includes/mcp/mcp_forum.php +++ b/phpBB/includes/mcp/mcp_forum.php @@ -273,7 +273,7 @@ function mcp_forum_view($id, $mode, $action, $forum_info) 'TOPIC_ICON_IMG_WIDTH' => (!empty($icons[$row['icon_id']])) ? $icons[$row['icon_id']]['width'] : '', 'TOPIC_ICON_IMG_HEIGHT' => (!empty($icons[$row['icon_id']])) ? $icons[$row['icon_id']]['height'] : '', 'UNAPPROVED_IMG' => ($topic_unapproved || $posts_unapproved) ? $user->img('icon_topic_unapproved', ($topic_unapproved) ? 'TOPIC_UNAPPROVED' : 'POSTS_UNAPPROVED') : '', - 'DELETED_IMG' => ($topic_deleted) ? $user->img('icon_topic_deleted', 'POSTS_DELETED') : '', + 'DELETED_IMG' => ($topic_deleted) ? $user->img('icon_topic_deleted', 'TOPIC_DELETED') : '', 'TOPIC_AUTHOR' => get_username_string('username', $row['topic_poster'], $row['topic_first_poster_name'], $row['topic_first_poster_colour']), 'TOPIC_AUTHOR_COLOUR' => get_username_string('colour', $row['topic_poster'], $row['topic_first_poster_name'], $row['topic_first_poster_colour']), diff --git a/phpBB/includes/mcp/mcp_main.php b/phpBB/includes/mcp/mcp_main.php index d0908a0d8b..b2441aed1b 100644 --- a/phpBB/includes/mcp/mcp_main.php +++ b/phpBB/includes/mcp/mcp_main.php @@ -877,11 +877,12 @@ function mcp_delete_topic($topic_ids, $is_soft = false, $soft_delete_reason = '' } $template->assign_vars(array( - 'S_SHADOW_TOPICS' => $only_shadow, - 'S_SOFTDELETED' => $only_softdeleted, - 'S_TOPIC_MODE' => true, - 'S_ALLOWED_DELETE' => $auth->acl_get('m_delete', $forum_id), - 'S_ALLOWED_SOFTDELETE' => $auth->acl_get('m_softdelete', $forum_id), + 'S_SHADOW_TOPICS' => $only_shadow, + 'S_SOFTDELETED' => $only_softdeleted, + 'S_TOPIC_MODE' => true, + 'S_ALLOWED_DELETE' => $auth->acl_get('m_delete', $forum_id), + 'S_ALLOWED_SOFTDELETE' => $auth->acl_get('m_softdelete', $forum_id), + 'DELETE_TOPIC_PERMANENTLY_EXPLAIN' => $user->lang('DELETE_TOPIC_PERMANENTLY', sizeof($topic_ids)), )); $l_confirm = (sizeof($topic_ids) == 1) ? 'DELETE_TOPIC' : 'DELETE_TOPICS'; @@ -1116,9 +1117,10 @@ function mcp_delete_post($post_ids, $is_soft = false, $soft_delete_reason = '', } $template->assign_vars(array( - 'S_SOFTDELETED' => $only_softdeleted, - 'S_ALLOWED_DELETE' => $auth->acl_get('m_delete', $forum_id), - 'S_ALLOWED_SOFTDELETE' => $auth->acl_get('m_softdelete', $forum_id), + 'S_SOFTDELETED' => $only_softdeleted, + 'S_ALLOWED_DELETE' => $auth->acl_get('m_delete', $forum_id), + 'S_ALLOWED_SOFTDELETE' => $auth->acl_get('m_softdelete', $forum_id), + 'DELETE_POST_PERMANENTLY_EXPLAIN' => $user->lang('DELETE_POST_PERMANENTLY', sizeof($post_ids)), )); $l_confirm = (sizeof($post_ids) == 1) ? 'DELETE_POST' : 'DELETE_POSTS'; diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php index 0ea6eeffd7..f367ae1fc0 100644 --- a/phpBB/install/database_update.php +++ b/phpBB/install/database_update.php @@ -211,13 +211,6 @@ while (!$migrator->finished()) phpbb_end_update($cache, $config); } - $state = array_merge(array( - 'migration_schema_done' => false, - 'migration_data_done' => false, - ), - $migrator->last_run_migration['state'] - ); - // Are we approaching the time limit? If so we want to pause the update and continue after refreshing if ((time() - $update_start_time) >= $safe_time_limit) { diff --git a/phpBB/language/en/acp/search.php b/phpBB/language/en/acp/search.php index bda965b615..c52b71c121 100644 --- a/phpBB/language/en/acp/search.php +++ b/phpBB/language/en/acp/search.php @@ -54,7 +54,7 @@ $lang = array_merge($lang, array( 'DELETING_INDEX_IN_PROGRESS_EXPLAIN' => 'The search backend is currently cleaning its index. This can take a few minutes.', 'FULLTEXT_MYSQL_INCOMPATIBLE_DATABASE' => 'The MySQL fulltext backend can only be used with MySQL4 and above.', - 'FULLTEXT_MYSQL_NOT_SUPPORTED' => 'MySQL fulltext indexes can only be used with MyISAM or InnoDB tables. MySQL 5.6.4 or later is required for fulltext indexes on InnoDB tables.', + 'FULLTEXT_MYSQL_NOT_SUPPORTED' => 'MySQL fulltext indexes can only be used with MyISAM or InnoDB tables. MySQL 5.6.8 or later is required for fulltext indexes on InnoDB tables.', 'FULLTEXT_MYSQL_TOTAL_POSTS' => 'Total number of indexed posts', 'FULLTEXT_MYSQL_MIN_SEARCH_CHARS_EXPLAIN' => 'Words with at least this many characters will be indexed for searching. You or your host can only change this setting by changing the mysql configuration.', 'FULLTEXT_MYSQL_MAX_SEARCH_CHARS_EXPLAIN' => 'Words with no more than this many characters will be indexed for searching. You or your host can only change this setting by changing the mysql configuration.', diff --git a/phpBB/language/en/migrator.php b/phpBB/language/en/migrator.php index 244a5faadf..b3b5995cc0 100644 --- a/phpBB/language/en/migrator.php +++ b/phpBB/language/en/migrator.php @@ -50,6 +50,7 @@ $lang = array_merge($lang, array( 'MIGRATION_NOT_FULFILLABLE' => 'The migration "%1$s" is not fulfillable, missing migration "%2$s".', 'MIGRATION_NOT_VALID' => '%s is not a valid migration.', 'MIGRATION_SCHEMA_DONE' => 'Installed Schema: %1$s; Time: %2$.2f seconds', + 'MIGRATION_SCHEMA_IN_PROGRESS' => 'Installing Schema: %1$s; Time: %2$.2f seconds', 'MIGRATION_SCHEMA_RUNNING' => 'Installing Schema: %s.', 'MIGRATION_INVALID_DATA_MISSING_CONDITION' => 'A migration is invalid. An if statement helper is missing a condition.', @@ -60,9 +61,12 @@ $lang = array_merge($lang, array( 'MIGRATION_INVALID_DATA_UNDEFINED_METHOD' => 'A migration is invalid. An undefined migration tool method was encountered.', 'MODULE_ERROR' => 'An error occurred while creating a module: %s', + 'MODULE_EXISTS' => 'A module already exists: %s', + 'MODULE_EXIST_MULTIPLE' => 'Several modules with the given parent module langname already exist: %s. Try using before/after keys to clarify the module placement.', 'MODULE_INFO_FILE_NOT_EXIST' => 'A required module info file is missing: %2$s', 'MODULE_NOT_EXIST' => 'A required module does not exist: %s', + 'PARENT_MODULE_FIND_ERROR' => 'Unable to determine the parent module identifier: %s', 'PERMISSION_NOT_EXIST' => 'The permission setting "%s" unexpectedly does not exist.', 'ROLE_NOT_EXIST' => 'The permission role "%s" unexpectedly does not exist.', diff --git a/phpBB/language/en/posting.php b/phpBB/language/en/posting.php index 924395ed44..ef52f59753 100644 --- a/phpBB/language/en/posting.php +++ b/phpBB/language/en/posting.php @@ -90,14 +90,20 @@ $lang = array_merge($lang, array( 'DELETE_PERMANENTLY' => 'Delete permanently', 'DELETE_POST_CONFIRM' => 'Are you sure you want to delete this post?', 'DELETE_POST_PERMANENTLY_CONFIRM' => 'Are you sure you want to <strong>permanently</strong> delete this post?', - 'DELETE_POST_PERMANENTLY' => 'Permanently delete this post so it can not be recovered', + 'DELETE_POST_PERMANENTLY' => array( + 1 => 'Permanently delete this post so it can not be recovered', + 2 => 'Permanently delete %1$d posts so they can not be recovered', + ), 'DELETE_POSTS_CONFIRM' => 'Are you sure you want to delete these posts?', 'DELETE_POSTS_PERMANENTLY_CONFIRM' => 'Are you sure you want to <strong>permanently</strong> delete these posts?', 'DELETE_REASON' => 'Reason for deletion', 'DELETE_REASON_EXPLAIN' => 'The specified reason for deletion will be visible to moderators.', 'DELETE_POST_WARN' => 'Delete this post', 'DELETE_TOPIC_CONFIRM' => 'Are you sure you want to delete this topic?', - 'DELETE_TOPIC_PERMANENTLY' => 'Permanently delete this topic so it can not be recovered', + 'DELETE_TOPIC_PERMANENTLY' => array( + 1 => 'Permanently delete this topic so it can not be recovered', + 2 => 'Permanently delete %1$d topics so they can not be recovered', + ), 'DELETE_TOPIC_PERMANENTLY_CONFIRM' => 'Are you sure you want to <strong>permanently</strong> delete this topic?', 'DELETE_TOPICS_CONFIRM' => 'Are you sure you want to delete these topics?', 'DELETE_TOPICS_PERMANENTLY_CONFIRM' => 'Are you sure you want to <strong>permanently</strong> delete these topics?', diff --git a/phpBB/phpbb/content_visibility.php b/phpBB/phpbb/content_visibility.php index 147b8ebbff..bf7dc2c703 100644 --- a/phpBB/phpbb/content_visibility.php +++ b/phpBB/phpbb/content_visibility.php @@ -428,7 +428,35 @@ class content_visibility 'post_delete_time' => ((int) $time) ?: time(), 'post_delete_reason' => truncate_string($reason, 255, 255, false), ); - + /** + * Perform actions right before the query to change post visibility + * + * @event core.set_post_visibility_before_sql + * @var int visibility Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE} + * @var array post_id Array containing all post IDs to be modified. If blank, all posts within the topic are modified. + * @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 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 + */ + $vars = array( + 'visibility', + 'post_id', + 'topic_id', + 'forum_id', + 'user_id', + 'timestamp', + 'reason', + 'is_starter', + 'is_latest', + 'data', + ); + extract($this->phpbb_dispatcher->trigger_event('core.set_post_visibility_before_sql', compact($vars))); $sql = 'UPDATE ' . $this->posts_table . ' SET ' . $this->db->sql_build_array('UPDATE', $data) . ' WHERE ' . $this->db->sql_in_set('post_id', $post_ids); @@ -585,7 +613,35 @@ class content_visibility WHERE topic_id = ' . (int) $topic_id; $this->db->sql_query($sql); } - + /** + * Perform actions after all steps to changing post visibility + * + * @event core.set_post_visibility_after + * @var int visibility Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE} + * @var array post_id Array containing all post IDs to be modified. If blank, all posts within the topic are modified. + * @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 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 + */ + $vars = array( + 'visibility', + 'post_id', + 'topic_id', + 'forum_id', + 'user_id', + 'timestamp', + 'reason', + 'is_starter', + 'is_latest', + 'data', + ); + extract($this->phpbb_dispatcher->trigger_event('core.set_post_visibility_after', compact($vars))); return $data; } @@ -645,7 +701,31 @@ class content_visibility 'topic_delete_time' => ((int) $time) ?: time(), 'topic_delete_reason' => truncate_string($reason, 255, 255, false), ); - + /** + * Perform actions right before the query to change topic visibility + * + * @event core.set_topic_visibility_before_sql + * @var int visibility Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE} + * @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 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 + */ + $vars = array( + 'visibility', + 'topic_id', + 'forum_id', + 'user_id', + 'timestamp', + 'reason', + 'force_update_all', + 'data', + ); + extract($this->phpbb_dispatcher->trigger_event('core.set_topic_visibility_before_sql', compact($vars))); $sql = 'UPDATE ' . $this->topics_table . ' SET ' . $this->db->sql_build_array('UPDATE', $data) . ' WHERE topic_id = ' . (int) $topic_id; @@ -670,7 +750,31 @@ class content_visibility { $this->set_post_visibility($visibility, false, $topic_id, $forum_id, $user_id, $time, '', true, true); } - + /** + * Perform actions after all steps to changing topic visibility + * + * @event core.set_topic_visibility_after + * @var int visibility Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE} + * @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 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 + */ + $vars = array( + 'visibility', + 'topic_id', + 'forum_id', + 'user_id', + 'timestamp', + 'reason', + 'force_update_all', + 'data', + ); + extract($this->phpbb_dispatcher->trigger_event('core.set_topic_visibility_after', compact($vars))); return $data; } diff --git a/phpBB/phpbb/db/migration/helper.php b/phpBB/phpbb/db/migration/helper.php index e40deeb37b..bce2efff51 100644 --- a/phpBB/phpbb/db/migration/helper.php +++ b/phpBB/phpbb/db/migration/helper.php @@ -81,4 +81,36 @@ class helper return $steps; } + + /** + * Reverse the update steps from an array of data changes + * + * 'If' statements and custom methods will be skipped, for all + * other calls the reverse method of the tool class will be called + * + * @param array $steps Update changes from migration + * + * @return array + */ + public function reverse_update_data($steps) + { + $reversed_array = array(); + + foreach ($steps as $step) + { + $parts = explode('.', $step[0]); + $parameters = $step[1]; + + $class = $parts[0]; + $method = isset($parts[1]) ? $parts[1] : false; + + if ($class !== 'if' && $class !== 'custom') + { + array_unshift($parameters, $method); + $reversed_array[] = array($class . '.reverse', $parameters); + } + } + + return array_reverse($reversed_array); + } } diff --git a/phpBB/phpbb/db/migration/tool/config.php b/phpBB/phpbb/db/migration/tool/config.php index f93e7118c4..33aa8ff026 100644 --- a/phpBB/phpbb/db/migration/tool/config.php +++ b/phpBB/phpbb/db/migration/tool/config.php @@ -150,6 +150,11 @@ class config implements \phpbb\db\migration\tool\tool_interface $arguments[0], ); break; + + case 'reverse': + // Reversing a reverse is just the call itself + $call = array_shift($arguments); + break; } if ($call) diff --git a/phpBB/phpbb/db/migration/tool/config_text.php b/phpBB/phpbb/db/migration/tool/config_text.php index bf8ac55023..54b45f6f6d 100644 --- a/phpBB/phpbb/db/migration/tool/config_text.php +++ b/phpBB/phpbb/db/migration/tool/config_text.php @@ -115,6 +115,11 @@ class config_text implements \phpbb\db\migration\tool\tool_interface $arguments[] = ''; } break; + + case 'reverse': + // Reversing a reverse is just the call itself + $call = array_shift($arguments); + break; } if ($call) diff --git a/phpBB/phpbb/db/migration/tool/module.php b/phpBB/phpbb/db/migration/tool/module.php index 035625b095..6d5378e35f 100644 --- a/phpBB/phpbb/db/migration/tool/module.php +++ b/phpBB/phpbb/db/migration/tool/module.php @@ -36,6 +36,9 @@ class module implements \phpbb\db\migration\tool\tool_interface /** @var string */ protected $modules_table; + /** @var array */ + protected $module_categories = array(); + /** * Constructor * @@ -87,30 +90,8 @@ class module implements \phpbb\db\migration\tool\tool_interface $parent_sql = ''; if ($parent !== false) { - // Allows '' to be sent as 0 - $parent = $parent ?: 0; - - if (!is_numeric($parent)) - { - $sql = 'SELECT module_id - FROM ' . $this->modules_table . " - WHERE module_langname = '" . $this->db->sql_escape($parent) . "' - AND module_class = '" . $this->db->sql_escape($class) . "'"; - $result = $this->db->sql_query($sql); - $module_id = $this->db->sql_fetchfield('module_id'); - $this->db->sql_freeresult($result); - - if (!$module_id) - { - return false; - } - - $parent_sql = 'AND parent_id = ' . (int) $module_id; - } - else - { - $parent_sql = 'AND parent_id = ' . (int) $parent; - } + $parent = $this->get_parent_module_id($parent, $module); + $parent_sql = 'AND parent_id = ' . (int) $parent; } $sql = 'SELECT module_id @@ -171,15 +152,14 @@ class module implements \phpbb\db\migration\tool\tool_interface */ public function add($class, $parent = 0, $data = array()) { - // Allows '' to be sent as 0 - $parent = $parent ?: 0; - // allow sending the name as a string in $data to create a category if (!is_array($data)) { $data = array('module_langname' => $data); } + $parent = $data['parent_id'] = $this->get_parent_module_id($parent, $data); + if (!isset($data['module_langname'])) { // The "automatic" way @@ -210,31 +190,14 @@ class module implements \phpbb\db\migration\tool\tool_interface } // The "manual" way - if (!is_numeric($parent)) - { - $sql = 'SELECT module_id - FROM ' . $this->modules_table . " - WHERE module_langname = '" . $this->db->sql_escape($parent) . "' - AND module_class = '" . $this->db->sql_escape($class) . "'"; - $result = $this->db->sql_query($sql); - $module_id = $this->db->sql_fetchfield('module_id'); - $this->db->sql_freeresult($result); - - if (!$module_id) - { - throw new \phpbb\db\migration\exception('MODULE_NOT_EXIST', $parent); - } - - $parent = $data['parent_id'] = $module_id; - } - else if (!$this->exists($class, false, $parent)) + if (!$this->exists($class, false, $parent)) { throw new \phpbb\db\migration\exception('MODULE_NOT_EXIST', $parent); } if ($this->exists($class, $parent, $data['module_langname'])) { - return; + throw new \phpbb\db\migration\exception('MODULE_EXISTS', $module_id); } if (!class_exists('acp_modules')) @@ -373,26 +336,8 @@ class module implements \phpbb\db\migration\tool\tool_interface $parent_sql = ''; if ($parent !== false) { - // Allows '' to be sent as 0 - $parent = ($parent) ?: 0; - - if (!is_numeric($parent)) - { - $sql = 'SELECT module_id - FROM ' . $this->modules_table . " - WHERE module_langname = '" . $this->db->sql_escape($parent) . "' - AND module_class = '" . $this->db->sql_escape($class) . "'"; - $result = $this->db->sql_query($sql); - $module_id = $this->db->sql_fetchfield('module_id'); - $this->db->sql_freeresult($result); - - // we know it exists from the module_exists check - $parent_sql = 'AND parent_id = ' . (int) $module_id; - } - else - { - $parent_sql = 'AND parent_id = ' . (int) $parent; - } + $parent = $this->get_parent_module_id($parent, $module); + $parent_sql = 'AND parent_id = ' . (int) $parent; } $module_ids = array(); @@ -454,6 +399,11 @@ class module implements \phpbb\db\migration\tool\tool_interface case 'remove': $call = 'add'; break; + + case 'reverse': + // Reversing a reverse is just the call itself + $call = array_shift($arguments); + break; } if ($call) @@ -487,4 +437,110 @@ class module implements \phpbb\db\migration\tool\tool_interface return array_pop($module); } + + /** + * Get the list of installed module categories + * key - module_id + * value - module_langname + * + * @return null + */ + protected function get_categories_list() + { + // Select the top level categories + // and 2nd level [sub]categories which exist for ACP only + $sql = 'SELECT m2.module_id, m2.module_langname + FROM ' . $this->modules_table . ' m1, ' . $this->modules_table . " m2 + WHERE m1.parent_id = 0 + AND (m1.module_id = m2.module_id + OR m2.module_class = 'acp' AND m2.parent_id = m1.module_id) + ORDER BY m1.module_id, m2.module_id ASC"; + + $result = $this->db->sql_query($sql); + while ($row = $this->db->sql_fetchrow($result)) + { + $this->module_categories[(int) $row['module_id']] = $row['module_langname']; + } + $this->db->sql_freeresult($result); + } + + /** + * Get parent module id + * + * @param string|int $parent_id The parent module_id|module_langname + * @param int|string|array $data The module_id, module_langname for existance checking or module data array for adding + * @return int The parent module_id + * @throws \phpbb\db\migration\exception + */ + public function get_parent_module_id($parent_id, $data = '') + { + // Allow '' to be sent as 0 + $parent_id = $parent_id ?: 0; + + // If automatic adding is in action, convert array back to string to simplify things + if (is_array($data) && sizeof($data) == 1) + { + $data = $data['module_langname']; + } + + if (!is_numeric($parent_id)) + { + // Refresh the $module_categories array + $this->get_categories_list(); + + // Search for the parent module_langname + $ids = array_keys($this->module_categories, $parent_id); + + switch (sizeof($ids)) + { + // No parent with the given module_langname exist + case 0: + throw new \phpbb\db\migration\exception('MODULE_NOT_EXIST', $parent_id); + break; + + // Return the module id + case 1: + $parent_id = (int) $ids[0]; + break; + + // Several modules with the given module_langname were found + // Try to determine the parent_id by the neighbour module parent + default: + if (is_array($data) && (isset($data['before']) || isset($data['after']))) + { + $neighbour_module_langname = isset($data['before']) ? $data['before'] : $data['after']; + $sql = 'SELECT parent_id + FROM ' . $this->modules_table . " + WHERE module_langname = '" . $this->db->sql_escape($neighbour_module_langname) . "' + AND " . $this->db->sql_in_set('parent_id', $ids); + $result = $this->db->sql_query($sql); + $parent_id = (int) $this->db->sql_fetchfield('parent_id'); + if (!$parent_id) + { + throw new \phpbb\db\migration\exception('PARENT_MODULE_FIND_ERROR', $data['parent_id']); + } + } + else if (!empty($data) && !is_array($data)) + { + // The module_langname is set, checking for the module existance + // As more than 1 parents were found already, there's no way for null parent_id here + $sql = 'SELECT m2.module_id as module_parent_id + FROM ' . $this->modules_table . ' m1, ' . $this->modules_table . " m2 + WHERE " . ((is_numeric($data)) ? 'm1.module_id = ' . (int) $data : "m1.module_langname = '" . $this->db->sql_escape($data)) . "' + AND m2.module_id = m1.parent_id + AND " . $this->db->sql_in_set('m2.module_id', $ids); + $result = $this->db->sql_query($sql); + $parent_id = (int) $this->db->sql_fetchfield('module_parent_id'); + } + else + { + //Unable to get the parent module id, throwing an exception + throw new \phpbb\db\migration\exception('MODULE_EXIST_MULTIPLE', $parent_id); + } + break; + } + } + + return $parent_id; + } } diff --git a/phpBB/phpbb/db/migration/tool/permission.php b/phpBB/phpbb/db/migration/tool/permission.php index ceff6d7d5a..9688420025 100644 --- a/phpBB/phpbb/db/migration/tool/permission.php +++ b/phpBB/phpbb/db/migration/tool/permission.php @@ -637,6 +637,11 @@ class permission implements \phpbb\db\migration\tool\tool_interface $arguments[0], ); break; + + case 'reverse': + // Reversing a reverse is just the call itself + $call = array_shift($arguments); + break; } if ($call) diff --git a/phpBB/phpbb/db/migrator.php b/phpBB/phpbb/db/migrator.php index 7fc3e787e2..4c4c0a8672 100644 --- a/phpBB/phpbb/db/migrator.php +++ b/phpBB/phpbb/db/migrator.php @@ -80,14 +80,14 @@ class migrator * * @var array */ - public $last_run_migration = false; + protected $last_run_migration = false; /** * The output handler. A null handler is configured by default. * * @var migrator_output_handler_interface */ - public $output_handler; + protected $output_handler; /** * Constructor of the database migrator @@ -152,6 +152,7 @@ class migrator $this->migration_state[$migration['migration_name']] = $migration; $this->migration_state[$migration['migration_name']]['migration_depends_on'] = unserialize($migration['migration_depends_on']); + $this->migration_state[$migration['migration_name']]['migration_data_state'] = !empty($migration['migration_data_state']) ? unserialize($migration['migration_data_state']) : ''; } } @@ -161,6 +162,19 @@ class migrator } /** + * Get an array with information about the last migration run. + * + * The array contains 'name', 'class' and 'state'. 'effectively_installed' is set + * and set to true if the last migration was effectively_installed. + * + * @return array + */ + public function get_last_run_migration() + { + return $this->last_run_migration; + } + + /** * Sets the list of available migration class names to the given array. * * @param array $class_names An array of migration class names @@ -297,38 +311,70 @@ class migrator if (!$state['migration_schema_done']) { - $this->output_handler->write(array('MIGRATION_SCHEMA_RUNNING', $name), migrator_output_handler_interface::VERBOSITY_VERBOSE); + $verbosity = empty($state['migration_data_state']) ? + migrator_output_handler_interface::VERBOSITY_VERBOSE : migrator_output_handler_interface::VERBOSITY_DEBUG; + $this->output_handler->write(array('MIGRATION_SCHEMA_RUNNING', $name), $verbosity); $this->last_run_migration['task'] = 'process_schema_step'; + + $total_time = (is_array($state['migration_data_state']) && isset($state['migration_data_state']['_total_time'])) ? + $state['migration_data_state']['_total_time'] : 0.0; $elapsed_time = microtime(true); + $steps = $this->helper->get_schema_steps($migration->update_schema()); $result = $this->process_data_step($steps, $state['migration_data_state']); + $elapsed_time = microtime(true) - $elapsed_time; + $total_time += $elapsed_time; + + if (is_array($result)) + { + $result['_total_time'] = $total_time; + } $state['migration_data_state'] = ($result === true) ? '' : $result; $state['migration_schema_done'] = ($result === true); - $this->output_handler->write(array('MIGRATION_SCHEMA_DONE', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_NORMAL); + if ($state['migration_schema_done']) + { + $this->output_handler->write(array('MIGRATION_SCHEMA_DONE', $name, $total_time), migrator_output_handler_interface::VERBOSITY_NORMAL); + } + else + { + $this->output_handler->write(array('MIGRATION_SCHEMA_IN_PROGRESS', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_VERY_VERBOSE); + } } else if (!$state['migration_data_done']) { try { - $this->output_handler->write(array('MIGRATION_DATA_RUNNING', $name), migrator_output_handler_interface::VERBOSITY_VERBOSE); + $verbosity = empty($state['migration_data_state']) ? + migrator_output_handler_interface::VERBOSITY_VERBOSE : migrator_output_handler_interface::VERBOSITY_DEBUG; + $this->output_handler->write(array('MIGRATION_DATA_RUNNING', $name), $verbosity); $this->last_run_migration['task'] = 'process_data_step'; + $total_time = (is_array($state['migration_data_state']) && isset($state['migration_data_state']['_total_time'])) ? + $state['migration_data_state']['_total_time'] : 0.0; $elapsed_time = microtime(true); + $result = $this->process_data_step($migration->update_data(), $state['migration_data_state']); + $elapsed_time = microtime(true) - $elapsed_time; + $total_time += $elapsed_time; + + if (is_array($result)) + { + $result['_total_time'] = $total_time; + } $state['migration_data_state'] = ($result === true) ? '' : $result; $state['migration_data_done'] = ($result === true); $state['migration_end_time'] = ($result === true) ? time() : 0; - if ($state['migration_schema_done']) + if ($state['migration_data_done']) { - $this->output_handler->write(array('MIGRATION_DATA_DONE', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_NORMAL); + $this->output_handler->write(array('MIGRATION_DATA_DONE', $name, $total_time), migrator_output_handler_interface::VERBOSITY_NORMAL); } else { @@ -337,10 +383,12 @@ class migrator } catch (\phpbb\db\migration\exception $e) { - // Revert the schema changes + // Reset data state and revert the schema changes + $state['migration_data_state'] = ''; + $this->set_migration_state($name, $state); + $this->revert_do($name); - // Rethrow exception throw $e; } } @@ -416,19 +464,11 @@ class migrator if ($state['migration_data_done']) { - if ($state['migration_data_state'] !== 'revert_data') - { - $result = $this->process_data_step($migration->update_data(), $state['migration_data_state'], true); - - $state['migration_data_state'] = ($result === true) ? 'revert_data' : $result; - } - else - { - $result = $this->process_data_step($migration->revert_data(), '', false); + $steps = array_merge($this->helper->reverse_update_data($migration->update_data()), $migration->revert_data()); + $result = $this->process_data_step($steps, $state['migration_data_state']); - $state['migration_data_state'] = ($result === true) ? '' : $result; - $state['migration_data_done'] = ($result === true) ? false : true; - } + $state['migration_data_state'] = ($result === true) ? '' : $result; + $state['migration_data_done'] = ($result === true) ? false : true; $this->set_migration_state($name, $state); } @@ -446,8 +486,13 @@ class migrator WHERE migration_name = '" . $this->db->sql_escape($name) . "'"; $this->db->sql_query($sql); + $this->last_run_migration = false; unset($this->migration_state[$name]); } + else + { + $this->set_migration_state($name, $state); + } } return true; @@ -464,7 +509,12 @@ class migrator */ protected function process_data_step($steps, $state, $revert = false) { - $state = ($state) ? unserialize($state) : false; + if (sizeof($steps) === 0) + { + return true; + } + + $state = is_array($state) ? $state : false; // reverse order of steps if reverting if ($revert === true) @@ -472,54 +522,45 @@ class migrator $steps = array_reverse($steps); } - foreach ($steps as $step_identifier => $step) + $step = $last_result = 0; + if ($state) { - $last_result = 0; - if ($state) - { - // Continue until we reach the step that matches the last step called - if ($state['step'] != $step_identifier) - { - continue; - } - - // We send the result from last time to the callable function - $last_result = $state['result']; + $step = $state['step']; - // Set state to false since we reached the point we were at - $state = false; - } + // We send the result from last time to the callable function + $last_result = $state['result']; + } - try + try + { + // Result will be null or true if everything completed correctly + // Stop after each update step, to let the updater control the script runtime + $result = $this->run_step($steps[$step], $last_result, $revert); + if (($result !== null && $result !== true) || $step + 1 < sizeof($steps)) { - // Result will be null or true if everything completed correctly - $result = $this->run_step($step, $last_result, $revert); - if ($result !== null && $result !== true) - { - return serialize(array( - 'result' => $result, - 'step' => $step_identifier, - )); - } + return array( + 'result' => $result, + // Move on if the last call finished + 'step' => ($result !== null && $result !== true) ? $step : $step + 1, + ); } - catch (\phpbb\db\migration\exception $e) + } + catch (\phpbb\db\migration\exception $e) + { + // We should try rolling back here + foreach ($steps as $reverse_step_identifier => $reverse_step) { - // We should try rolling back here - foreach ($steps as $reverse_step_identifier => $reverse_step) + // If we've reached the current step we can break because we reversed everything that was run + if ($reverse_step_identifier == $step) { - // If we've reached the current step we can break because we reversed everything that was run - if ($reverse_step_identifier == $step_identifier) - { - break; - } - - // Reverse the step that was run - $result = $this->run_step($reverse_step, false, !$revert); + break; } - // rethrow the exception - throw $e; + // Reverse the step that was run + $result = $this->run_step($reverse_step, false, !$revert); } + + throw $e; } return true; @@ -587,6 +628,13 @@ class migrator throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_MISSING_STEP', $step); } + if ($reverse) + { + // We might get unexpected results when trying + // to revert this, so just avoid it + return false; + } + $condition = $parameters[0]; if (!$condition) @@ -664,6 +712,7 @@ class migrator { $migration_row = $state; $migration_row['migration_depends_on'] = serialize($state['migration_depends_on']); + $migration_row['migration_data_state'] = !empty($state['migration_data_state']) ? serialize($state['migration_data_state']) : ''; if (isset($this->migration_state[$name])) { diff --git a/phpBB/phpbb/db/migrator_output_handler_interface.php b/phpBB/phpbb/db/migrator_output_handler_interface.php index a923af99f6..9947b51dcc 100644 --- a/phpBB/phpbb/db/migrator_output_handler_interface.php +++ b/phpBB/phpbb/db/migrator_output_handler_interface.php @@ -15,11 +15,11 @@ namespace phpbb\db; interface migrator_output_handler_interface { - const VERBOSITY_QUIET = 0; - const VERBOSITY_NORMAL = 1; - const VERBOSITY_VERBOSE = 2; - const VERBOSITY_VERY_VERBOSE = 3; - const VERBOSITY_DEBUG = 4; + const VERBOSITY_QUIET = 16; + const VERBOSITY_NORMAL = 32; + const VERBOSITY_VERBOSE = 64; + const VERBOSITY_VERY_VERBOSE = 128; + const VERBOSITY_DEBUG = 256; /** * Write output using the configured closure. diff --git a/phpBB/phpbb/notification/method/messenger_base.php b/phpBB/phpbb/notification/method/messenger_base.php index 3c6d617c66..0bfbfd6b02 100644 --- a/phpBB/phpbb/notification/method/messenger_base.php +++ b/phpBB/phpbb/notification/method/messenger_base.php @@ -81,7 +81,7 @@ abstract class messenger_base extends \phpbb\notification\method\base $messenger->assign_vars(array_merge(array( 'USERNAME' => $user['username'], - 'U_NOTIFICATION_SETTINGS' => generate_board_url() . '/ucp.' . $this->php_ext . '?i=ucp_notifications&mode=notification_options', + 'U_NOTIFICATION_SETTINGS' => generate_board_url() . '/ucp.' . $this->php_ext . '?i=ucp_notifications&mode=notification_options', ), $notification->get_email_template_variables())); $messenger->send($notify_method); diff --git a/phpBB/phpbb/search/fulltext_mysql.php b/phpBB/phpbb/search/fulltext_mysql.php index 3ddbd85b36..9faf5ca08b 100644 --- a/phpBB/phpbb/search/fulltext_mysql.php +++ b/phpBB/phpbb/search/fulltext_mysql.php @@ -177,8 +177,10 @@ class fulltext_mysql extends \phpbb\search\base $engine === 'MyISAM' || // FULLTEXT is supported on InnoDB since MySQL 5.6.4 according to // http://dev.mysql.com/doc/refman/5.6/en/innodb-storage-engine.html + // We also require https://bugs.mysql.com/bug.php?id=67004 to be + // fixed for proper overall operation. Hence we require 5.6.8. $engine === 'InnoDB' && - phpbb_version_compare($this->db->sql_server_info(true), '5.6.4', '>='); + phpbb_version_compare($this->db->sql_server_info(true), '5.6.8', '>='); if (!$fulltext_supported) { diff --git a/phpBB/phpbb/search/fulltext_native.php b/phpBB/phpbb/search/fulltext_native.php index e2c02ffdab..63b0b24edf 100644 --- a/phpBB/phpbb/search/fulltext_native.php +++ b/phpBB/phpbb/search/fulltext_native.php @@ -1262,7 +1262,7 @@ class fulltext_native extends \phpbb\search\base if (!$total_results && $is_mysql) { // Count rows for the executed queries. Replace $select within $sql with SQL_CALC_FOUND_ROWS, and run it. - $sql_calc = str_replace('SELECT ' . $select, 'SELECT DISTINCT SQL_CALC_FOUND_ROWS p.post_id', $sql); + $sql_calc = str_replace('SELECT ' . $select, 'SELECT SQL_CALC_FOUND_ROWS ' . $select, $sql); $result = $this->db->sql_query($sql_calc); $this->db->sql_freeresult($result); diff --git a/phpBB/phpbb/session.php b/phpBB/phpbb/session.php index 83e87b7704..33d8df9cb8 100644 --- a/phpBB/phpbb/session.php +++ b/phpBB/phpbb/session.php @@ -1585,7 +1585,7 @@ class session $this->data = array_merge($this->data, $sql_ary); - if ($this->data['user_id'] != ANONYMOUS && !empty($config['new_member_post_limit']) && $this->data['user_new'] && $config['new_member_post_limit'] <= $this->data['user_posts']) + if ($this->data['user_id'] != ANONYMOUS && isset($config['new_member_post_limit']) && $this->data['user_new'] && $config['new_member_post_limit'] <= $this->data['user_posts']) { $this->leave_newly_registered(); } diff --git a/phpBB/posting.php b/phpBB/posting.php index 653740ae1c..f1e8452305 100644 --- a/phpBB/posting.php +++ b/phpBB/posting.php @@ -1741,6 +1741,7 @@ $page_data = array( 'L_POST_A' => $page_title, 'L_ICON' => ($mode == 'reply' || $mode == 'quote' || ($mode == 'edit' && $post_id != $post_data['topic_first_post_id'])) ? $user->lang['POST_ICON'] : $user->lang['TOPIC_ICON'], 'L_MESSAGE_BODY_EXPLAIN' => $user->lang('MESSAGE_BODY_EXPLAIN', (int) $config['max_post_chars']), + 'L_DELETE_POST_PERMANENTLY' => $user->lang('DELETE_POST_PERMANENTLY', 1), 'FORUM_NAME' => $post_data['forum_name'], 'FORUM_DESC' => ($post_data['forum_desc']) ? generate_text_for_display($post_data['forum_desc'], $post_data['forum_desc_uid'], $post_data['forum_desc_bitfield'], $post_data['forum_desc_options']) : '', diff --git a/phpBB/search.php b/phpBB/search.php index a0380590c1..cbee369086 100644 --- a/phpBB/search.php +++ b/phpBB/search.php @@ -126,6 +126,26 @@ gen_sort_selects($limit_days, $sort_by_text, $sort_days, $sort_key, $sort_dir, $ $phpbb_content_visibility = $phpbb_container->get('content.visibility'); $pagination = $phpbb_container->get('pagination'); +/** +* This event allows you to alter the above parameters, such as keywords and submit +* +* @event core.search_modify_submit_parameters +* @var string keywords The search keywords +* @var string author Specifies the author match, when ANONYMOUS is also a search-match +* @var int author_id ID of the author to search by +* @var string search_id Predefined search type name +* @var bool submit Whether or not the form has been submitted +* @since 3.1.10-RC1 +*/ +$vars = array( + 'keywords', + 'author', + 'author_id', + 'search_id', + 'submit', +); +extract($phpbb_dispatcher->trigger_event('core.search_modify_submit_parameters', compact($vars))); + if ($keywords || $author || $author_id || $search_id || $submit) { // clear arrays diff --git a/phpBB/styles/prosilver/template/confirm_delete_body.html b/phpBB/styles/prosilver/template/confirm_delete_body.html index f0a7ab2bdb..fbd881a940 100644 --- a/phpBB/styles/prosilver/template/confirm_delete_body.html +++ b/phpBB/styles/prosilver/template/confirm_delete_body.html @@ -7,7 +7,7 @@ <label> <strong>{L_DELETE_PERMANENTLY}{L_COLON}</strong> <input id="delete_permanent" name="delete_permanent" type="checkbox" value="1" {S_CHECKED_PERMANENT} /> - <!-- IF S_TOPIC_MODE -->{L_DELETE_TOPIC_PERMANENTLY}<!-- ELSE -->{L_DELETE_POST_PERMANENTLY}<!-- ENDIF --> + <!-- IF S_TOPIC_MODE -->{DELETE_TOPIC_PERMANENTLY_EXPLAIN}<!-- ELSE -->{DELETE_POST_PERMANENTLY_EXPLAIN}<!-- ENDIF --> </label> <!-- ENDIF --> @@ -43,7 +43,7 @@ <dd> <label for="delete_permanent"> <input id="delete_permanent" name="delete_permanent" type="checkbox" value="1" {S_CHECKED_PERMANENT} /> - <!-- IF S_TOPIC_MODE -->{L_DELETE_TOPIC_PERMANENTLY}<!-- ELSE -->{L_DELETE_POST_PERMANENTLY}<!-- ENDIF --> + <!-- IF S_TOPIC_MODE -->{DELETE_TOPIC_PERMANENTLY_EXPLAIN}<!-- ELSE -->{DELETE_POST_PERMANENTLY_EXPLAIN}<!-- ENDIF --> </label> </dd> </dl> diff --git a/phpBB/styles/prosilver/template/mcp_topic.html b/phpBB/styles/prosilver/template/mcp_topic.html index e6978191de..22d837b3d1 100644 --- a/phpBB/styles/prosilver/template/mcp_topic.html +++ b/phpBB/styles/prosilver/template/mcp_topic.html @@ -73,7 +73,7 @@ <dl> <dt><label for="to_topic_id">{L_MERGE_TOPIC_ID}{L_COLON}</label></dt> <dd> - <input class="inputbox autowidth" type="number" min="0" max="999999" name="to_topic_id" id="to_topic_id" value="{TO_TOPIC_ID}" /> + <input class="inputbox autowidth" type="number" min="0" max="9999999999" name="to_topic_id" id="to_topic_id" value="{TO_TOPIC_ID}" /> <a href="{U_SELECT_TOPIC}" >{L_SELECT_TOPIC}</a> </dd> <!-- IF TO_TOPIC_INFO --><dd>{TO_TOPIC_INFO}</dd><!-- ENDIF --> @@ -112,7 +112,10 @@ </ul> <h3><a href="{postrow.U_POST_DETAILS}">{postrow.POST_SUBJECT}</a></h3> + + <!-- EVENT mcp_topic_postrow_post_details_before --> <p class="author"><a href="#pr{postrow.POST_ID}">{postrow.MINI_POST_IMG}</a> {L_POSTED} {postrow.POST_DATE} {L_POST_BY_AUTHOR} <strong>{postrow.POST_AUTHOR_FULL}</strong><!-- IF postrow.U_MCP_DETAILS --> [ <a href="{postrow.U_MCP_DETAILS}">{L_POST_DETAILS}</a> ]<!-- ENDIF --></p> + <!-- EVENT mcp_topic_postrow_post_details_after --> <!-- IF postrow.S_POST_UNAPPROVED --> <p class="post-notice unapproved"> diff --git a/phpBB/styles/prosilver/template/posting_editor.html b/phpBB/styles/prosilver/template/posting_editor.html index f897bf7a3b..5258ea09a2 100644 --- a/phpBB/styles/prosilver/template/posting_editor.html +++ b/phpBB/styles/prosilver/template/posting_editor.html @@ -23,7 +23,11 @@ <!-- IF S_POST_ACTION or S_PRIVMSGS or S_EDIT_DRAFT --> <dl style="clear: left;"> <dt><label for="subject">{L_SUBJECT}{L_COLON}</label></dt> - <dd><input type="text" name="subject" id="subject" size="45" maxlength="<!-- IF S_NEW_MESSAGE -->120<!-- ELSE -->124<!-- ENDIF -->" tabindex="2" value="{SUBJECT}{DRAFT_SUBJECT}" class="inputbox autowidth" /></dd> + <dd> + <!-- EVENT posting_editor_subject_prepend --> + <input type="text" name="subject" id="subject" size="45" maxlength="<!-- IF S_NEW_MESSAGE -->120<!-- ELSE -->124<!-- ENDIF -->" tabindex="2" value="{SUBJECT}{DRAFT_SUBJECT}" class="inputbox autowidth" /> + <!-- EVENT posting_editor_subject_append --> + </dd> </dl> <!-- IF CAPTCHA_TEMPLATE and S_CONFIRM_CODE --> <!-- DEFINE $CAPTCHA_TAB_INDEX = 3 --> diff --git a/phpBB/styles/prosilver/template/posting_topic_review.html b/phpBB/styles/prosilver/template/posting_topic_review.html index 6909877196..c51d8032dc 100644 --- a/phpBB/styles/prosilver/template/posting_topic_review.html +++ b/phpBB/styles/prosilver/template/posting_topic_review.html @@ -43,7 +43,10 @@ </ul> <!-- ENDIF --> + <!-- EVENT posting_topic_review_row_post_details_before --> <p class="author"><!-- IF S_IS_BOT -->{topic_review_row.MINI_POST_IMG}<!-- ELSE --><a href="{topic_review_row.U_MINI_POST}">{topic_review_row.MINI_POST_IMG}</a><!-- ENDIF --> {L_POST_BY_AUTHOR} <strong>{topic_review_row.POST_AUTHOR_FULL}</strong> » {topic_review_row.POST_DATE} </p> + <!-- EVENT posting_topic_review_row_post_details_after --> + <div class="content">{topic_review_row.MESSAGE}</div> <!-- IF topic_review_row.S_HAS_ATTACHMENTS --> diff --git a/phpBB/styles/prosilver/template/viewforum_body.html b/phpBB/styles/prosilver/template/viewforum_body.html index bb18a5dd2c..643b78823f 100644 --- a/phpBB/styles/prosilver/template/viewforum_body.html +++ b/phpBB/styles/prosilver/template/viewforum_body.html @@ -121,6 +121,8 @@ <!-- ENDIF --> +<!-- EVENT viewforum_body_topic_row_before --> + <!-- BEGIN topicrow --> <!-- IF not topicrow.S_TOPIC_TYPE_SWITCH and not topicrow.S_FIRST_ROW --> @@ -145,7 +147,7 @@ <ul class="topiclist topics"> <!-- ENDIF --> - <!-- EVENT viewforum_body_topic_row_before --> + <!-- EVENT viewforum_body_topicrow_row_before --> <li class="row<!-- IF topicrow.S_ROW_COUNT is even --> bg1<!-- ELSE --> bg2<!-- ENDIF --><!-- IF topicrow.S_POST_GLOBAL --> global-announce<!-- ENDIF --><!-- IF topicrow.S_POST_ANNOUNCE --> announce<!-- ENDIF --><!-- IF topicrow.S_POST_STICKY --> sticky<!-- ENDIF --><!-- IF topicrow.S_TOPIC_REPORTED --> reported<!-- ENDIF -->"> <!-- EVENT viewforum_body_topic_row_prepend --> <dl class="icon {topicrow.TOPIC_IMG_STYLE}"> diff --git a/phpBB/styles/subsilver2/template/mcp_topic.html b/phpBB/styles/subsilver2/template/mcp_topic.html index c6a8236b20..eee7fa95ea 100644 --- a/phpBB/styles/subsilver2/template/mcp_topic.html +++ b/phpBB/styles/subsilver2/template/mcp_topic.html @@ -44,7 +44,7 @@ </tr> <tr> <td class="row1" nowrap="nowrap"><span class="gen">{L_MERGE_TOPIC_ID}</span></td> - <td class="row2" colspan="2"><input class="post" type="number" min="0" max="999999" name="to_topic_id" value="{TO_TOPIC_ID}" /> <a href="{U_SELECT_TOPIC}">{L_SELECT_TOPIC}</a></td> + <td class="row2" colspan="2"><input class="post" type="number" min="0" max="9999999999" name="to_topic_id" value="{TO_TOPIC_ID}" /> <a href="{U_SELECT_TOPIC}">{L_SELECT_TOPIC}</a></td> </tr> <!-- IF TO_TOPIC_INFO --> <tr> @@ -74,12 +74,14 @@ <td align="center"><b class="postauthor">{postrow.POST_AUTHOR_FULL}</b></td> <td width="100%"> + <!-- EVENT mcp_topic_postrow_post_details_before --> <table width="100%" cellspacing="0" cellpadding="0" border="0"> <tr style="vertical-align: top;"> <td class="gensmall" nowrap="nowrap"> <b>{L_POST_SUBJECT}{L_COLON}</b> </td> <td class="gensmall" width="100%">{postrow.POST_SUBJECT}</td> </tr> </table> + <!-- EVENT mcp_topic_postrow_post_details_after --> </td> <td width="5%" align="center"><a href="{postrow.U_POST_DETAILS}" class="imageset">{INFO_IMG}</a></td> </tr> diff --git a/phpBB/styles/subsilver2/template/posting_body.html b/phpBB/styles/subsilver2/template/posting_body.html index b984c9f96f..95f817e838 100644 --- a/phpBB/styles/subsilver2/template/posting_body.html +++ b/phpBB/styles/subsilver2/template/posting_body.html @@ -166,7 +166,11 @@ <!-- EVENT posting_editor_subject_before --> <tr> <td class="row1" width="22%"><b class="genmed">{L_SUBJECT}{L_COLON}</b></td> - <td class="row2" width="78%"><input class="post" style="width:450px" type="text" name="subject" size="45" maxlength="<!-- IF S_NEW_MESSAGE -->120<!-- ELSE -->124<!-- ENDIF -->" tabindex="2" value="{SUBJECT}" /></td> + <td class="row2" width="78%"> + <!-- EVENT posting_editor_subject_prepend --> + <input class="post" style="width:450px" type="text" name="subject" size="45" maxlength="<!-- IF S_NEW_MESSAGE -->120<!-- ELSE -->124<!-- ENDIF -->" tabindex="2" value="{SUBJECT}" /> + <!-- EVENT posting_editor_subject_append --> + </td> </tr> <!-- EVENT posting_editor_subject_after --> <tr> diff --git a/phpBB/styles/subsilver2/template/posting_topic_review.html b/phpBB/styles/subsilver2/template/posting_topic_review.html index 20976861fc..46329e19fa 100644 --- a/phpBB/styles/subsilver2/template/posting_topic_review.html +++ b/phpBB/styles/subsilver2/template/posting_topic_review.html @@ -18,6 +18,7 @@ </tr> <!-- BEGIN topic_review_row --> + <!-- EVENT posting_topic_review_row_post_details_before --> <!-- IF topic_review_row.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF --> <!-- IF topic_review_row.S_IGNORE_POST --> @@ -41,6 +42,7 @@ </table> </td> </tr> + <!-- EVENT posting_topic_review_row_post_details_after --> <!-- IF topic_review_row.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF --> diff --git a/phpBB/styles/subsilver2/template/viewforum_body.html b/phpBB/styles/subsilver2/template/viewforum_body.html index 2b8268d2c3..c76c081ba7 100644 --- a/phpBB/styles/subsilver2/template/viewforum_body.html +++ b/phpBB/styles/subsilver2/template/viewforum_body.html @@ -32,9 +32,11 @@ <th> {L_LAST_POST} </th> </tr> + <!-- EVENT viewforum_body_topic_row_before --> + <!-- BEGIN topicrow --> - <!-- EVENT viewforum_body_topic_row_before --> + <!-- EVENT viewforum_body_topicrow_row_before --> <tr> <!-- EVENT viewforum_body_topic_row_prepend --> <td class="row1" width="25" align="center">{topicrow.TOPIC_FOLDER_IMG}</td> diff --git a/tests/dbal/fixtures/migrator_module.xml b/tests/dbal/fixtures/migrator_module.xml index 32afe7e6f3..e172d7a145 100644 --- a/tests/dbal/fixtures/migrator_module.xml +++ b/tests/dbal/fixtures/migrator_module.xml @@ -20,7 +20,7 @@ <value>acp</value> <value>0</value> <value>1</value> - <value>4</value> + <value>6</value> <value>ACP_CAT</value> <value></value> <value></value> @@ -38,5 +38,57 @@ <value>test</value> <value></value> </row> + <row> + <value>3</value> + <value>1</value> + <value>1</value> + <value></value> + <value>acp</value> + <value>1</value> + <value>4</value> + <value>5</value> + <value>ACP_FORUM_BASED_PERMISSIONS</value> + <value></value> + <value></value> + </row> + <row> + <value>4</value> + <value>1</value> + <value>1</value> + <value></value> + <value>acp</value> + <value>0</value> + <value>7</value> + <value>12</value> + <value>ACP_CAT_FORUMS</value> + <value></value> + <value></value> + </row> + <row> + <value>5</value> + <value>1</value> + <value>1</value> + <value></value> + <value>acp</value> + <value>4</value> + <value>8</value> + <value>11</value> + <value>ACP_FORUM_BASED_PERMISSIONS</value> + <value></value> + <value></value> + </row> + <row> + <value>6</value> + <value>1</value> + <value>1</value> + <value></value> + <value>acp</value> + <value>5</value> + <value>9</value> + <value>10</value> + <value>ACP_FORUM_BASED_PERMISSIONS_CHILD_1</value> + <value></value> + <value></value> + </row> </table> </dataset> diff --git a/tests/dbal/migration/if.php b/tests/dbal/migration/if.php index 98a66526ed..481250ea77 100644 --- a/tests/dbal/migration/if.php +++ b/tests/dbal/migration/if.php @@ -36,13 +36,13 @@ class phpbb_dbal_migration_if extends \phpbb\db\migration\migration { global $migrator_test_if_true_failed; - $migrator_test_if_true_failed = false; + $migrator_test_if_true_failed = !$migrator_test_if_true_failed; } function test_false() { global $migrator_test_if_false_failed; - $migrator_test_if_false_failed = true; + $migrator_test_if_false_failed = !$migrator_test_if_false_failed; } } diff --git a/tests/dbal/migrator_test.php b/tests/dbal/migrator_test.php index 4c4306888c..798200eef1 100644 --- a/tests/dbal/migrator_test.php +++ b/tests/dbal/migrator_test.php @@ -156,6 +156,14 @@ class phpbb_dbal_migrator_test extends phpbb_database_test_case $this->assertFalse($migrator_test_if_true_failed, 'True test failed'); $this->assertFalse($migrator_test_if_false_failed, 'False test failed'); + + while ($this->migrator->migration_state('phpbb_dbal_migration_if') !== false) + { + $this->migrator->revert('phpbb_dbal_migration_if'); + } + + $this->assertFalse($migrator_test_if_true_failed, 'True test after revert failed'); + $this->assertFalse($migrator_test_if_false_failed, 'False test after revert failed'); } public function test_recall() diff --git a/tests/dbal/migrator_tool_module_test.php b/tests/dbal/migrator_tool_module_test.php index 08c3e979b8..49dff8b929 100644 --- a/tests/dbal/migrator_tool_module_test.php +++ b/tests/dbal/migrator_tool_module_test.php @@ -118,6 +118,44 @@ class phpbb_dbal_migrator_tool_module_test extends phpbb_database_test_case $this->fail($e); } $this->assertEquals(true, $this->tool->exists('acp', 'ACP_NEW_CAT', 'ACP_NEW_MODULE')); + + // Test adding module when plural parent module_langname exists + // PHPBB3-14703 + // Adding fail + try + { + $this->tool->add('acp', 'ACP_FORUM_BASED_PERMISSIONS', array( + 'module_basename' => 'acp_new_permissions_module', + 'module_langname' => 'ACP_NEW_PERMISSIONS_MODULE', + 'module_mode' => 'test', + 'module_auth' => '', + )); + $this->fail('Exception not thrown'); + } + catch (Exception $e) + { + $this->assertEquals('phpbb\db\migration\exception', get_class($e)); + $this->assertEquals('MODULE_EXIST_MULTIPLE', $e->getMessage()); + } + + // Test adding module when plural parent module_langname exists + // PHPBB3-14703 + // Adding success + try + { + $this->tool->add('acp', 'ACP_FORUM_BASED_PERMISSIONS', array( + 'module_basename' => 'acp_new_permissions_module', + 'module_langname' => 'ACP_NEW_PERMISSIONS_MODULE', + 'module_mode' => 'test', + 'module_auth' => '', + 'after' => 'ACP_FORUM_BASED_PERMISSIONS_CHILD_1', + )); + } + catch (Exception $e) + { + $this->fail($e); + } + $this->assertEquals(true, $this->tool->exists('acp', 'ACP_FORUM_BASED_PERMISSIONS', 'ACP_NEW_PERMISSIONS_MODULE')); } public function test_remove() diff --git a/tests/migrator/reverse_update_data_test.php b/tests/migrator/reverse_update_data_test.php new file mode 100644 index 0000000000..b85e48c64c --- /dev/null +++ b/tests/migrator/reverse_update_data_test.php @@ -0,0 +1,56 @@ +<?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. + * + */ + +class reverse_update_data_test extends phpbb_test_case +{ + /** @var \phpbb\db\migration\helper */ + protected $helper; + + public function setUp() + { + parent::setUp(); + + $this->helper = new \phpbb\db\migration\helper(); + } + + public function update_data_provider() + { + return array( + array( + array( + array('config.add', array('foobar', 1)), + array('if', array( + (false === true), + array('permission.add', array('some_data')), + )), + array('config.remove', array('foobar')), + array('custom', array(array($this, 'foo_bar'))), + array('tool.method', array('test_data')), + ), + array( + array('tool.reverse', array('method', 'test_data')), + array('config.reverse', array('remove', 'foobar')), + array('config.reverse', array('add', 'foobar', 1)), + ), + ), + ); + } + + /** + * @dataProvider update_data_provider + */ + public function test_get_schema_steps($data_changes, $expected) + { + $this->assertEquals($expected, $this->helper->reverse_update_data($data_changes)); + } +} |