diff options
44 files changed, 794 insertions, 207 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/build/build.xml b/build/build.xml index c412a747ac..2bece48edc 100644 --- a/build/build.xml +++ b/build/build.xml @@ -49,7 +49,7 @@  	-->  	<target name="composer">  		<exec dir="phpBB" -			command="php ../composer.phar install --dev" +			command="php ../composer.phar install"  			checkreturn="true"  			passthru="true" />  	</target> @@ -143,6 +143,7 @@  		<phingcall target="export">  			<property name="revision" value="release-${version}" />  			<property name="dir" value="build/old_versions/release-${version}" /> +			<property name="skip-composer" value="true" />  		</phingcall>  		<phingcall target="clean-diff-dir"> @@ -251,26 +252,35 @@  			<equals arg1="${composer-has-dependencies}" arg2="1" trim="true" />  			<then>  				<!-- We have non-dev composer dependencies --> -				<exec dir="." -					command="git ls-tree ${revision} composer.phar" -					checkreturn="true" -					outputProperty='composer-ls-tree-output' />  				<if> -					<equals arg1="${composer-ls-tree-output}" arg2="" trim="true" /> +					<not><isset property="skip-composer" /></not>  					<then> -						<fail message="There are composer dependencies, but composer.phar is missing." /> -					</then> -					<else> -						<!-- Export the phar, install dependencies, delete phar. -->  						<exec dir="." -							command="git archive ${revision} composer.phar | tar -xf - -C ${dir}" -							checkreturn="true" /> -						<exec dir="${dir}" -							command="php composer.phar install --no-dev --optimize-autoloader" +							command="git ls-tree ${revision} composer.phar"  							checkreturn="true" -							passthru="true" /> -						<delete file="${dir}/composer.phar" /> -					</else> +							outputProperty='composer-ls-tree-output' /> +						<if> +							<equals arg1="${composer-ls-tree-output}" arg2="" trim="true" /> +							<then> +								<fail message="There are composer dependencies, but composer.phar is missing." /> +							</then> +							<else> +								<!-- Export the phar, install dependencies, delete phar. --> +								<exec dir="." +									command="git archive ${revision} composer.phar | tar -xf - -C ${dir}" +									checkreturn="true" /> +								<exec dir="${dir}" +									command="php composer.phar install --no-dev --optimize-autoloader" +									checkreturn="true" +									passthru="true" /> +								<delete file="${dir}/composer.phar" /> + +								<phingcall target="clean-vendor-dir"> +									<property name="dir" value="${dir}" /> +								</phingcall> +							</else> +						</if> +					</then>  				</if>  			</then>  			<else> @@ -287,10 +297,6 @@  		<delete dir="${dir}/develop" />  		<delete dir="${dir}/install/data" /> -		<phingcall target="clean-vendor-dir"> -			<property name="dir" value="${dir}" /> -		</phingcall> -  		<echo msg="Setting permissions for checkout of ${revision} in ${dir}" />  		<!-- set permissions of all files to 644, directories to 755 -->  		<exec dir="${dir}" command="find . -type f|xargs chmod 644" escape="false" /> @@ -307,6 +313,7 @@  		<delete dir="${dir}/vendor/lusitanian/oauth/examples" />  		<delete dir="${dir}/vendor/lusitanian/oauth/tests" />  		<delete file="${dir}/vendor/lusitanian/oauth/.gitignore" /> +		<delete file="${dir}/vendor/lusitanian/oauth/.scrutinizer.yml" />  		<delete file="${dir}/vendor/lusitanian/oauth/.travis.yml" />  		<delete file="${dir}/vendor/lusitanian/oauth/phpunit.xml.dist" />  		<delete file="${dir}/vendor/lusitanian/oauth/README.md" /> @@ -381,10 +388,9 @@  		<delete file="${dir}/vendor/twig/twig/.editorconfig" />  		<delete file="${dir}/vendor/twig/twig/.gitignore" />  		<delete file="${dir}/vendor/twig/twig/.travis.yml" /> -		<delete file="${dir}/vendor/twig/twig/AUTHORS" />  		<delete file="${dir}/vendor/twig/twig/CHANGELOG" />  		<delete file="${dir}/vendor/twig/twig/phpunit.xml.dist" /> -		<delete file="${dir}/vendor/twig/twig/README.markdown" /> +		<delete file="${dir}/vendor/twig/twig/README.rst" />  	</target>  	<target name="clean-diff-dir"> 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 1fe8b915f1..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: @@ -959,6 +982,14 @@ memberlist_body_username_prepend  * Purpose: Add information before every username in the memberlist. Works in  all display modes (leader, group and normal memberlist). +memberlist_email_before +=== +* Locations: +    + styles/prosilver/template/memberlist_email.html +    + styles/subsilver2/template/memberlist_email.html +* Since: 3.1.10-RC1 +* Purpose: Allow adding customizations before the memberlist_email form. +  memberlist_search_fields_after  ===  * Locations: @@ -1495,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: @@ -1503,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: @@ -1564,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: @@ -2247,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/acp/acp_forums.php b/phpBB/includes/acp/acp_forums.php index 98273f06d9..1e69a4ad20 100644 --- a/phpBB/includes/acp/acp_forums.php +++ b/phpBB/includes/acp/acp_forums.php @@ -842,9 +842,26 @@ class acp_forums  			ORDER BY left_id";  		$result = $db->sql_query($sql); -		if ($row = $db->sql_fetchrow($result)) +		$rowset = array(); +		while ($row = $db->sql_fetchrow($result)) +		{ +			$rowset[(int) $row['forum_id']] = $row; +		} +		$db->sql_freeresult($result); + +		/** +		* Modify the forum list data +		* +		* @event core.acp_manage_forums_modify_forum_list +		* @var	array	rowset	Array with the forums list data +		* @since 3.1.10-RC1 +		*/ +		$vars = array('rowset'); +		extract($phpbb_dispatcher->trigger_event('core.acp_manage_forums_modify_forum_list', compact($vars))); + +		if (!empty($rowset))  		{ -			do +			foreach ($rowset as $row)  			{  				$forum_type = $row['forum_type']; @@ -888,7 +905,6 @@ class acp_forums  					'U_SYNC'			=> $url . '&action=sync')  				);  			} -			while ($row = $db->sql_fetchrow($result));  		}  		else if ($this->parent_id)  		{ @@ -904,7 +920,7 @@ class acp_forums  				'U_SYNC'			=> $url . '&action=sync')  			);  		} -		$db->sql_freeresult($result); +		unset($rowset);  		$template->assign_vars(array(  			'ERROR_MSG'		=> (sizeof($errors)) ? implode('<br />', $errors) : '', diff --git a/phpBB/includes/functions_admin.php b/phpBB/includes/functions_admin.php index 9c543eaac6..1dc246ec33 100644 --- a/phpBB/includes/functions_admin.php +++ b/phpBB/includes/functions_admin.php @@ -65,7 +65,7 @@ function recalc_nested_sets(&$new_id, $pkey, $table, $parent_id = 0, $where = ar  */  function make_forum_select($select_id = false, $ignore_id = false, $ignore_acl = false, $ignore_nonpost = false, $ignore_emptycat = true, $only_acl_post = false, $return_array = false)  { -	global $db, $user, $auth; +	global $db, $user, $auth, $phpbb_dispatcher;  	// This query is identical to the jumpbox one  	$sql = 'SELECT forum_id, forum_name, parent_id, forum_type, forum_flags, forum_options, left_id, right_id @@ -73,16 +73,33 @@ function make_forum_select($select_id = false, $ignore_id = false, $ignore_acl =  		ORDER BY left_id ASC';  	$result = $db->sql_query($sql, 600); +	$rowset = array(); +	while ($row = $db->sql_fetchrow($result)) +	{ +		$rowset[(int) $row['forum_id']] = $row; +	} +	$db->sql_freeresult($result); +  	$right = 0;  	$padding_store = array('0' => '');  	$padding = '';  	$forum_list = ($return_array) ? array() : ''; +	/** +	* Modify the forum list data +	* +	* @event core.make_forum_select_modify_forum_list +	* @var	array	rowset	Array with the forums list data +	* @since 3.1.10-RC1 +	*/ +	$vars = array('rowset'); +	extract($phpbb_dispatcher->trigger_event('core.make_forum_select_modify_forum_list', compact($vars))); +  	// Sometimes it could happen that forums will be displayed here not be displayed within the index page  	// This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions.  	// If this happens, the padding could be "broken" -	while ($row = $db->sql_fetchrow($result)) +	foreach ($rowset as $row)  	{  		if ($row['left_id'] < $right)  		{ @@ -133,8 +150,7 @@ function make_forum_select($select_id = false, $ignore_id = false, $ignore_acl =  			$forum_list .= '<option value="' . $row['forum_id'] . '"' . (($disabled) ? ' disabled="disabled" class="disabled-option"' : $selected) . '>' . $padding . $row['forum_name'] . '</option>';  		}  	} -	$db->sql_freeresult($result); -	unset($padding_store); +	unset($padding_store, $rowset);  	return $forum_list;  } diff --git a/phpBB/includes/functions_content.php b/phpBB/includes/functions_content.php index 87dd306e8a..8e60804d6e 100644 --- a/phpBB/includes/functions_content.php +++ b/phpBB/includes/functions_content.php @@ -163,16 +163,33 @@ function make_jumpbox($action, $forum_id = false, $select_all = false, $acl_list  		ORDER BY left_id ASC';  	$result = $db->sql_query($sql, 600); +	$rowset = array(); +	while ($row = $db->sql_fetchrow($result)) +	{ +		$rowset[(int) $row['forum_id']] = $row; +	} +	$db->sql_freeresult($result); +  	$right = $padding = 0;  	$padding_store = array('0' => 0);  	$display_jumpbox = false;  	$iteration = 0; +	/** +	* Modify the jumpbox forum list data +	* +	* @event core.make_jumpbox_modify_forum_list +	* @var	array	rowset	Array with the forums list data +	* @since 3.1.10-RC1 +	*/ +	$vars = array('rowset'); +	extract($phpbb_dispatcher->trigger_event('core.make_jumpbox_modify_forum_list', compact($vars))); +  	// Sometimes it could happen that forums will be displayed here not be displayed within the index page  	// This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions.  	// If this happens, the padding could be "broken" -	while ($row = $db->sql_fetchrow($result)) +	foreach ($rowset as $row)  	{  		if ($row['left_id'] < $right)  		{ @@ -254,8 +271,7 @@ function make_jumpbox($action, $forum_id = false, $select_all = false, $acl_list  		}  		$iteration++;  	} -	$db->sql_freeresult($result); -	unset($padding_store); +	unset($padding_store, $rowset);  	$url_parts = $phpbb_path_helper->get_url_parts($action); 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/memberlist_email.html b/phpBB/styles/prosilver/template/memberlist_email.html index 4a9f764d07..eea699da08 100644 --- a/phpBB/styles/prosilver/template/memberlist_email.html +++ b/phpBB/styles/prosilver/template/memberlist_email.html @@ -1,5 +1,7 @@  <!-- INCLUDE overall_header.html --> +<!-- EVENT memberlist_email_before --> +  <!-- IF S_CONTACT_ADMIN-->  <h2 class="titlespace">{L_CONTACT_ADMIN}</h2>  <!-- ELSEIF S_SEND_USER --> 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/memberlist_email.html b/phpBB/styles/subsilver2/template/memberlist_email.html index 78ab3e4b8d..04f36b2244 100644 --- a/phpBB/styles/subsilver2/template/memberlist_email.html +++ b/phpBB/styles/subsilver2/template/memberlist_email.html @@ -1,5 +1,7 @@  <!-- INCLUDE overall_header.html --> +<!-- EVENT memberlist_email_before --> +  <div id="pagecontent">  	<form action="{S_POST_ACTION}" method="post" name="postform"> 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)); +	} +}  | 
