diff options
Diffstat (limited to 'phpBB/includes')
102 files changed, 9362 insertions, 658 deletions
diff --git a/phpBB/includes/acp/acp_board.php b/phpBB/includes/acp/acp_board.php index 93e1dd71fe..6543427677 100644 --- a/phpBB/includes/acp/acp_board.php +++ b/phpBB/includes/acp/acp_board.php @@ -329,6 +329,7 @@ class acp_board  						'load_online_time'	=> array('lang' => 'ONLINE_LENGTH',		'validate' => 'int:0',	'type' => 'text:4:3', 'explain' => true, 'append' => ' ' . $user->lang['MINUTES']),  						'legend2'				=> 'GENERAL_OPTIONS', +						'load_notifications'	=> array('lang' => 'LOAD_NOTIFICATIONS',	'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true),  						'load_db_track'			=> array('lang' => 'YES_POST_MARKING',		'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true),  						'load_db_lastread'		=> array('lang' => 'YES_READ_MARKING',		'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true),  						'load_anon_lastread'	=> array('lang' => 'YES_ANON_READ_MARKING',	'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true), @@ -428,8 +429,8 @@ class acp_board  						'board_email_form'		=> array('lang' => 'BOARD_EMAIL_FORM',		'validate' => 'bool',	'type' => 'radio:enabled_disabled', 'explain' => true),  						'email_function_name'	=> array('lang' => 'EMAIL_FUNCTION_NAME',	'validate' => 'string',	'type' => 'text:20:50', 'explain' => true),  						'email_package_size'	=> array('lang' => 'EMAIL_PACKAGE_SIZE',	'validate' => 'int:0',	'type' => 'text:5:5', 'explain' => true), -						'board_contact'			=> array('lang' => 'CONTACT_EMAIL',			'validate' => 'string',	'type' => 'text:25:100', 'explain' => true), -						'board_email'			=> array('lang' => 'ADMIN_EMAIL',			'validate' => 'string',	'type' => 'text:25:100', 'explain' => true), +						'board_contact'			=> array('lang' => 'CONTACT_EMAIL',			'validate' => 'email',	'type' => 'text:25:100', 'explain' => true), +						'board_email'			=> array('lang' => 'ADMIN_EMAIL',			'validate' => 'email',	'type' => 'text:25:100', 'explain' => true),  						'board_email_sig'		=> array('lang' => 'EMAIL_SIG',				'validate' => 'string',	'type' => 'textarea:5:30', 'explain' => true),  						'board_hide_emails'		=> array('lang' => 'BOARD_HIDE_EMAILS',		'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true), diff --git a/phpBB/includes/acp/acp_extensions.php b/phpBB/includes/acp/acp_extensions.php index a0bcf62ecc..24211196bd 100644 --- a/phpBB/includes/acp/acp_extensions.php +++ b/phpBB/includes/acp/acp_extensions.php @@ -37,7 +37,7 @@ class acp_extensions  		$this->template = $template;  		$this->user = $user; -		$user->add_lang(array('install', 'acp/extensions')); +		$user->add_lang(array('install', 'acp/extensions', 'migrator'));  		$this->page_title = 'ACP_EXTENSIONS'; @@ -103,11 +103,18 @@ class acp_extensions  					trigger_error($user->lang['EXTENSION_NOT_AVAILABLE'] . adm_back_link($this->u_action));  				} -				if ($phpbb_extension_manager->enable_step($ext_name)) +				try  				{ -					$template->assign_var('S_NEXT_STEP', true); +					if ($phpbb_extension_manager->enable_step($ext_name)) +					{ +						$template->assign_var('S_NEXT_STEP', true); -					meta_refresh(0, $this->u_action . '&action=enable&ext_name=' . urlencode($ext_name)); +						meta_refresh(0, $this->u_action . '&action=enable&ext_name=' . urlencode($ext_name)); +					} +				} +				catch (phpbb_db_migration_exception $e) +				{ +					$template->assign_var('MIGRATOR_ERROR', $e->getLocalisedMessage($user));  				}  				$this->tpl_name = 'acp_ext_enable'; @@ -156,11 +163,18 @@ class acp_extensions  			break;  			case 'purge': -				if ($phpbb_extension_manager->purge_step($ext_name)) +				try  				{ -					$template->assign_var('S_NEXT_STEP', true); +					if ($phpbb_extension_manager->purge_step($ext_name)) +					{ +						$template->assign_var('S_NEXT_STEP', true); -					meta_refresh(0, $this->u_action . '&action=purge&ext_name=' . urlencode($ext_name)); +						meta_refresh(0, $this->u_action . '&action=purge&ext_name=' . urlencode($ext_name)); +					} +				} +				catch (phpbb_db_migration_exception $e) +				{ +					$template->assign_var('MIGRATOR_ERROR', $e->getLocalisedMessage($user));  				}  				$this->tpl_name = 'acp_ext_purge'; diff --git a/phpBB/includes/acp/acp_groups.php b/phpBB/includes/acp/acp_groups.php index 56063759c9..0e67dd51aa 100644 --- a/phpBB/includes/acp/acp_groups.php +++ b/phpBB/includes/acp/acp_groups.php @@ -125,13 +125,34 @@ class acp_groups  				{  					trigger_error($user->lang['NO_GROUP'] . adm_back_link($this->u_action), E_USER_WARNING);  				} +				else if (empty($mark_ary)) +				{ +					trigger_error($user->lang['NO_USERS'] . adm_back_link($this->u_action . '&action=list&g=' . $group_id), E_USER_WARNING); +				}  				if (confirm_box(true))  				{  					$group_name = ($group_row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $group_row['group_name']] : $group_row['group_name']; +					group_user_attributes('default', $group_id, $mark_ary, false, $group_name, $group_row);	 +					trigger_error($user->lang['GROUP_DEFS_UPDATED'] . adm_back_link($this->u_action . '&action=list&g=' . $group_id)); +				} +				else +				{ +					confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array( +						'mark'		=> $mark_ary, +						'g'			=> $group_id, +						'i'			=> $id, +						'mode'		=> $mode, +						'action'	=> $action)) +					); +				} -					if (!sizeof($mark_ary)) +				break; +			case 'set_default_on_all': +					if (confirm_box(true))  					{ +						$group_name = ($group_row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $group_row['group_name']] : $group_row['group_name']; +							  						$start = 0;  						do @@ -162,28 +183,25 @@ class acp_groups  							$db->sql_freeresult($result);  						}  						while ($start); +							 +						trigger_error($user->lang['GROUP_DEFS_UPDATED'] . adm_back_link($this->u_action . '&action=list&g=' . $group_id));  					}  					else  					{ -						group_user_attributes('default', $group_id, $mark_ary, false, $group_name, $group_row); +						confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array( +							'mark'		=> $mark_ary, +							'g'			=> $group_id, +							'i'			=> $id, +							'mode'		=> $mode, +							'action'	=> $action)) +						);  					} - -					trigger_error($user->lang['GROUP_DEFS_UPDATED'] . adm_back_link($this->u_action . '&action=list&g=' . $group_id)); -				} -				else -				{ -					confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array( -						'mark'		=> $mark_ary, -						'g'			=> $group_id, -						'i'			=> $id, -						'mode'		=> $mode, -						'action'	=> $action)) -					); -				} -  			break; -  			case 'deleteusers': +				if (empty($mark_ary)) +				{ +					trigger_error($user->lang['NO_USERS'] . adm_back_link($this->u_action . '&action=list&g=' . $group_id), E_USER_WARNING); +				}  			case 'delete':  				if (!$group_id)  				{ @@ -683,7 +701,7 @@ class acp_groups  					'U_ACTION'			=> $this->u_action . "&g=$group_id",  					'U_BACK'			=> $this->u_action,  					'U_FIND_USERNAME'	=> append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=searchuser&form=list&field=usernames'), -					'U_DEFAULT_ALL'		=> "{$this->u_action}&action=default&g=$group_id", +					'U_DEFAULT_ALL'		=> "{$this->u_action}&action=set_default_on_all&g=$group_id",  				));  				// Grab the members diff --git a/phpBB/includes/acp/acp_modules.php b/phpBB/includes/acp/acp_modules.php index 52a82004e8..fce26bf45f 100644 --- a/phpBB/includes/acp/acp_modules.php +++ b/phpBB/includes/acp/acp_modules.php @@ -535,8 +535,14 @@ class acp_modules  	/**  	* Get available module information from module files +	* +	* @param string $module +	* @param bool|string $module_class +	* @param bool $use_all_available Use all available instead of just all +	* 						enabled extensions +	* @return array  	*/ -	function get_module_infos($module = '', $module_class = false) +	function get_module_infos($module = '', $module_class = false, $use_all_available = false)  	{  		global $phpbb_root_path, $phpEx; @@ -556,7 +562,7 @@ class acp_modules  				->extension_directory("/$module_class")  				->core_path("includes/$module_class/info/")  				->core_prefix($module_class . '_') -				->get_classes(); +				->get_classes(true, $use_all_available);  			foreach ($modules as $module)  			{ diff --git a/phpBB/includes/acp/info/acp_extensions.php b/phpBB/includes/acp/info/acp_extensions.php index 03d7059165..174b365af0 100644 --- a/phpBB/includes/acp/info/acp_extensions.php +++ b/phpBB/includes/acp/info/acp_extensions.php @@ -16,10 +16,10 @@ class acp_extensions_info  	{  		return array(  			'filename'	=> 'acp_extensions', -			'title'		=> 'ACP_EXTENSIONS_MANAGEMENT', +			'title'		=> 'ACP_EXTENSION_MANAGEMENT',  			'version'	=> '1.0.0',  			'modes'		=> array( -				'main'		=> array('title' => 'ACP_EXTENSIONS', 'auth' => 'acl_a_extensions', 'cat' => array('ACP_EXTENSIONS_MANAGEMENT')), +				'main'		=> array('title' => 'ACP_EXTENSIONS', 'auth' => 'acl_a_extensions', 'cat' => array('ACP_EXTENSION_MANAGEMENT')),  			),  		);  	} diff --git a/phpBB/includes/constants.php b/phpBB/includes/constants.php index 68c96a2759..36576e5344 100644 --- a/phpBB/includes/constants.php +++ b/phpBB/includes/constants.php @@ -240,6 +240,8 @@ define('LOGIN_ATTEMPT_TABLE',		$table_prefix . 'login_attempts');  define('MIGRATIONS_TABLE',			$table_prefix . 'migrations');  define('MODERATOR_CACHE_TABLE',		$table_prefix . 'moderator_cache');  define('MODULES_TABLE',				$table_prefix . 'modules'); +define('NOTIFICATION_TYPES_TABLE',	$table_prefix . 'notification_types'); +define('NOTIFICATIONS_TABLE',		$table_prefix . 'notifications');  define('POLL_OPTIONS_TABLE',		$table_prefix . 'poll_options');  define('POLL_VOTES_TABLE',			$table_prefix . 'poll_votes');  define('POSTS_TABLE',				$table_prefix . 'posts'); @@ -273,6 +275,7 @@ define('TOPICS_POSTED_TABLE',		$table_prefix . 'topics_posted');  define('TOPICS_TRACK_TABLE',		$table_prefix . 'topics_track');  define('TOPICS_WATCH_TABLE',		$table_prefix . 'topics_watch');  define('USER_GROUP_TABLE',			$table_prefix . 'user_group'); +define('USER_NOTIFICATIONS_TABLE',	$table_prefix . 'user_notifications');  define('USERS_TABLE',				$table_prefix . 'users');  define('WARNINGS_TABLE',			$table_prefix . 'warnings');  define('WORDS_TABLE',				$table_prefix . 'words'); diff --git a/phpBB/includes/datetime.php b/phpBB/includes/datetime.php index b3462ddf67..3c6d4971b9 100644 --- a/phpBB/includes/datetime.php +++ b/phpBB/includes/datetime.php @@ -143,7 +143,7 @@ class phpbb_datetime extends DateTime  				'is_short'		=> strpos($format, self::RELATIVE_WRAPPER) !== false,  				'format_short'	=> substr($format, 0, strpos($format, self::RELATIVE_WRAPPER)) . self::RELATIVE_WRAPPER . self::RELATIVE_WRAPPER . substr(strrchr($format, self::RELATIVE_WRAPPER), 1),  				'format_long'	=> str_replace(self::RELATIVE_WRAPPER, '', $format), -				'lang'			=> $user->lang['datetime'], +				'lang'			=> array_filter($user->lang['datetime'], 'is_string'),  			);  			// Short representation of month in format? Some languages use different terms for the long and short format of May diff --git a/phpBB/includes/db/db_tools.php b/phpBB/includes/db/db_tools.php index e8c26fa502..983cdc18ea 100644 --- a/phpBB/includes/db/db_tools.php +++ b/phpBB/includes/db/db_tools.php @@ -303,7 +303,7 @@ class phpbb_db_tools  	* @param phpbb_db_driver	$db					Database connection  	* @param bool		$return_statements	True if only statements should be returned and no SQL being executed  	*/ -	function phpbb_db_tools(&$db, $return_statements = false) +	function phpbb_db_tools(phpbb_db_driver $db, $return_statements = false)  	{  		$this->db = $db;  		$this->return_statements = $return_statements; diff --git a/phpBB/includes/db/migration/data/30x/3_0_1.php b/phpBB/includes/db/migration/data/30x/3_0_1.php new file mode 100644 index 0000000000..c996a0138a --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_1.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_1 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.1', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_1_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.1')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_10.php b/phpBB/includes/db/migration/data/30x/3_0_10.php new file mode 100644 index 0000000000..122f93d6b4 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_10.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_10 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.10', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_10_rc3'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.10')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_10_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_10_rc1.php new file mode 100644 index 0000000000..0ed05812dc --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_10_rc1.php @@ -0,0 +1,30 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_10_rc1 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.10-rc1', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_9'); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('email_max_chunk_size', 50)), + +			array('config.update', array('version', '3.0.10-rc1')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_10_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_10_rc2.php new file mode 100644 index 0000000000..b14b3b00aa --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_10_rc2.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_10_rc2 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.10-rc2', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_10_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.10-rc2')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_10_rc3.php b/phpBB/includes/db/migration/data/30x/3_0_10_rc3.php new file mode 100644 index 0000000000..473057d65d --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_10_rc3.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_10_rc3 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.10-rc3', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_10_rc2'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.10-rc3')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_11.php b/phpBB/includes/db/migration/data/30x/3_0_11.php new file mode 100644 index 0000000000..e063c699cc --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_11.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_11 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.11', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_11_rc2'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.11')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_11_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_11_rc1.php new file mode 100644 index 0000000000..dddfc0e0e7 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_11_rc1.php @@ -0,0 +1,95 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_11_rc1 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.11-rc1', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_10'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array(&$this, 'cleanup_deactivated_styles'))), +			array('custom', array(array(&$this, 'delete_orphan_private_messages'))), + +			array('config.update', array('version', '3.0.11-rc1')), +		); +	} + +	public function cleanup_deactivated_styles() +	{ +		// Updates users having current style a deactivated one +		$sql = 'SELECT style_id +			FROM ' . STYLES_TABLE . ' +			WHERE style_active = 0'; +		$result = $this->sql_query($sql); + +		$deactivated_style_ids = array(); +		while ($style_id = $this->db->sql_fetchfield('style_id', false, $result)) +		{ +			$deactivated_style_ids[] = (int) $style_id; +		} +		$this->db->sql_freeresult($result); + +		if (!empty($deactivated_style_ids)) +		{ +			$sql = 'UPDATE ' . USERS_TABLE . ' +				SET user_style = ' . (int) $this->config['default_style'] .' +				WHERE ' . $this->db->sql_in_set('user_style', $deactivated_style_ids); +			$this->sql_query($sql); +		} +	} + +	public function delete_orphan_private_messages() +	{ +		// Delete orphan private messages +		$batch_size = 500; + +		$sql_array = array( +			'SELECT'	=> 'p.msg_id', +			'FROM'		=> array( +				PRIVMSGS_TABLE	=> 'p', +			), +			'LEFT_JOIN'	=> array( +				array( +					'FROM'	=> array(PRIVMSGS_TO_TABLE => 't'), +					'ON'	=> 'p.msg_id = t.msg_id', +				), +			), +			'WHERE'		=> 't.user_id IS NULL', +		); +		$sql = $this->db->sql_build_query('SELECT', $sql_array); + +		$result = $this->db->sql_query_limit($sql, $batch_size); + +		$delete_pms = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$delete_pms[] = (int) $row['msg_id']; +		} +		$this->db->sql_freeresult($result); + +		if (!empty($delete_pms)) +		{ +			$sql = 'DELETE FROM ' . PRIVMSGS_TABLE . ' +				WHERE ' . $this->db->sql_in_set('msg_id', $delete_pms); +			$this->sql_query($sql); + +			// Return false to have the Migrator call this function again +			return false; +		} +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_11_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_11_rc2.php new file mode 100644 index 0000000000..fac8523e8c --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_11_rc2.php @@ -0,0 +1,50 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_11_rc2 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.11-rc2', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_11_rc1'); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns' => array( +				$this->table_prefix . 'profile_fields' => array( +					'field_show_novalue' => array('BOOL', 0), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns' => array( +				$this->table_prefix . 'profile_fields' => array( +					'field_show_novalue', +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.11-rc2')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_12_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_12_rc1.php new file mode 100644 index 0000000000..6a31a51201 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_12_rc1.php @@ -0,0 +1,123 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +/** @todo DROP LOGIN_ATTEMPT_TABLE.attempt_id in 3.0.12-RC1 **/ + +class phpbb_db_migration_data_30x_3_0_12_rc1 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.12-rc1', '>='); +	} + + 	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_11'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array(&$this, 'update_module_auth'))), +			array('custom', array(array(&$this, 'update_bots'))), +			array('custom', array(array(&$this, 'disable_bots_from_receiving_pms'))), + +			array('config.update', array('version', '3.0.12-rc1')), +		); +	} + +	public function disable_bots_from_receiving_pms() +	{ +		// Disable receiving pms for bots +		$sql = 'SELECT user_id +			FROM ' . BOTS_TABLE; +		$result = $this->db->sql_query($sql); + +		$bot_user_ids = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$bot_user_ids[] = (int) $row['user_id']; +		} +		$this->db->sql_freeresult($result); + +		if (!empty($bot_user_ids)) +		{ +			$sql = 'UPDATE ' . USERS_TABLE . ' +				SET user_allow_pm = 0 +				WHERE ' . $this->db->sql_in_set('user_id', $bot_user_ids); +			$this->sql_query($sql); +		} +	} + +	public function update_module_auth() +	{ +		$sql = 'UPDATE ' . MODULES_TABLE . ' +			SET module_auth = \'acl_u_sig\' +			WHERE module_class = \'ucp\' +				AND module_basename = \'profile\' +				AND module_mode = \'signature\''; +		$this->sql_query($sql); +	} + +	public function update_bots() +	{ +		// Update bots +		if (!function_exists('user_delete')) +		{ +			include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext); +		} + +		$bots_updates = array( +			// Bot Deletions +			'NG-Search [Bot]'		=> false, +			'Nutch/CVS [Bot]'		=> false, +			'OmniExplorer [Bot]'	=> false, +			'Seekport [Bot]'		=> false, +			'Synoo [Bot]'			=> false, +			'WiseNut [Bot]'			=> false, + +			// Bot Updates +			// Bot name to bot user agent map +			'Baidu [Spider]'	=> 'Baiduspider', +			'Exabot [Bot]'		=> 'Exabot', +			'Voyager [Bot]'		=> 'voyager/', +			'W3C [Validator]'	=> 'W3C_Validator', +		); + +		foreach ($bots_updates as $bot_name => $bot_agent) +		{ +			$sql = 'SELECT user_id +				FROM ' . USERS_TABLE . ' +				WHERE user_type = ' . USER_IGNORE . " +					AND username_clean = '" . $this->db->sql_escape(utf8_clean_string($bot_name)) . "'"; +			$result = $this->db->sql_query($sql); +			$bot_user_id = (int) $this->db->sql_fetchfield('user_id'); +			$this->db->sql_freeresult($result); + +			if ($bot_user_id) +			{ +				if ($bot_agent === false) +				{ +					$sql = 'DELETE FROM ' . BOTS_TABLE . " +						WHERE user_id = $bot_user_id"; +					$this->sql_query($sql); + +					user_delete('remove', $bot_user_id); +				} +				else +				{ +					$sql = 'UPDATE ' . BOTS_TABLE . " +						SET bot_agent = '" .  $this->db->sql_escape($bot_agent) . "' +						WHERE user_id = $bot_user_id"; +					$this->sql_query($sql); +				} +			} +		} +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_1_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_1_rc1.php new file mode 100644 index 0000000000..562ccf077c --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_1_rc1.php @@ -0,0 +1,108 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_1_rc1 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.1-rc1', '>='); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns' => array( +				$this->table_prefix . 'forums' => array( +					'display_subforum_list' => array('BOOL', 1), +				), +				$this->table_prefix . 'sessions' => array( +					'session_forum_id' => array('UINT', 0), +				), +			), +			'drop_keys' => array( +				$this->table_prefix . 'groups' => array( +					'group_legend', +				), +			), +			'add_index' => array( +				$this->table_prefix . 'sessions' => array( +					'session_forum_id' => array('session_forum_id'), +				), +				$this->table_prefix . 'groups' => array( +					'group_legend_name' => array('group_legend', 'group_name'), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns' => array( +				$this->table_prefix . 'forums' => array( +					'display_subforum_list', +				), +				$this->table_prefix . 'sessions' => array( +					'session_forum_id', +				), +			), +			'add_index' => array( +				$this->table_prefix . 'groups' => array( +					'group_legend' => array('group_legend'), +				), +			), +			'drop_keys' => array( +				$this->table_prefix . 'sessions' => array( +					'session_forum_id', +				), +				$this->table_prefix . 'groups' => array( +					'group_legend_name', +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array(&$this, 'fix_unset_last_view_time'))), +			array('custom', array(array(&$this, 'reset_smiley_size'))), + +			array('config.update', array('version', '3.0.1-rc1')), +		); +	} + +	public function fix_unset_last_view_time() +	{ +		$sql = 'UPDATE ' . $this->table_prefix . "topics +			SET topic_last_view_time = topic_last_post_time +			WHERE topic_last_view_time = 0"; +		$this->sql_query($sql); +	} + +	public function reset_smiley_size() +	{ +		// Update smiley sizes +		$smileys = array('icon_e_surprised.gif', 'icon_eek.gif', 'icon_cool.gif', 'icon_lol.gif', 'icon_mad.gif', 'icon_razz.gif', 'icon_redface.gif', 'icon_cry.gif', 'icon_evil.gif', 'icon_twisted.gif', 'icon_rolleyes.gif', 'icon_exclaim.gif', 'icon_question.gif', 'icon_idea.gif', 'icon_arrow.gif', 'icon_neutral.gif', 'icon_mrgreen.gif', 'icon_e_ugeek.gif'); + +		foreach ($smileys as $smiley) +		{ +			if (file_exists($this->phpbb_root_path . 'images/smilies/' . $smiley)) +			{ +				list($width, $height) = getimagesize($this->phpbb_root_path . 'images/smilies/' . $smiley); + +				$sql = 'UPDATE ' . SMILIES_TABLE . ' +					SET smiley_width = ' . $width . ', smiley_height = ' . $height . " +					WHERE smiley_url = '" . $this->db->sql_escape($smiley) . "'"; + +				$this->sql_query($sql); +			} +		} +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_2.php b/phpBB/includes/db/migration/data/30x/3_0_2.php new file mode 100644 index 0000000000..eed5acef82 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_2.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_2 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.2', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_2_rc2'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.2')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_2_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_2_rc1.php new file mode 100644 index 0000000000..a960e90765 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_2_rc1.php @@ -0,0 +1,32 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_2_rc1 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.2-rc1', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_1'); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('referer_validation', '1')), +			array('config.add', array('check_attachment_content', '1')), +			array('config.add', array('mime_triggers', 'body|head|html|img|plaintext|a href|pre|script|table|title')), + +			array('config.update', array('version', '3.0.2-rc1')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_2_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_2_rc2.php new file mode 100644 index 0000000000..8917dfea77 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_2_rc2.php @@ -0,0 +1,80 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_2_rc2 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.2-rc2', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_2_rc1'); +	} + +	public function update_schema() +	{ +		return array( +			'change_columns' => array( +				$this->table_prefix . 'drafts' => array( +					'draft_subject' => array('STEXT_UNI', ''), +				), +				$this->table_prefix . 'forums' => array( +					'forum_last_post_subject' => array('STEXT_UNI', ''), +				), +				$this->table_prefix . 'posts' => array( +					'post_subject' => array('STEXT_UNI', '', 'true_sort'), +				), +				$this->table_prefix . 'privmsgs' => array( +					'message_subject' => array('STEXT_UNI', ''), +				), +				$this->table_prefix . 'topics' => array( +					'topic_title' => array('STEXT_UNI', '', 'true_sort'), +					'topic_last_post_subject' => array('STEXT_UNI', ''), +				), +			), +			'drop_keys' => array( +				$this->table_prefix . 'sessions' => array( +					'session_forum_id', +				), +			), +			'add_index' => array( +				$this->table_prefix . 'sessions' => array( +					'session_fid' => array('session_forum_id'), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_index' => array( +				$this->table_prefix . 'sessions' => array( +					'session_forum_id' => array( +						'session_forum_id', +					), +				), +			), +			'drop_keys' => array( +				$this->table_prefix . 'sessions' => array( +					'session_fid', +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.2-rc2')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_3.php b/phpBB/includes/db/migration/data/30x/3_0_3.php new file mode 100644 index 0000000000..8984cf7b76 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_3.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_3 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.3', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_3_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.3')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_3_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_3_rc1.php new file mode 100644 index 0000000000..4b102e1a2e --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_3_rc1.php @@ -0,0 +1,83 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_3_rc1 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.3-rc1', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_2'); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns' => array( +				$this->table_prefix . 'styles_template' => array( +					'template_inherits_id' => array('UINT:4', 0), +					'template_inherit_path' => array('VCHAR', ''), +				), +				$this->table_prefix . 'groups' => array( +					'group_max_recipients' => array('UINT', 0), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns' => array( +				$this->table_prefix . 'styles_template' => array( +					'template_inherits_id', +					'template_inherit_path', +				), +				$this->table_prefix . 'groups' => array( +					'group_max_recipients', +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('enable_queue_trigger', '0')), +			array('config.add', array('queue_trigger_posts', '3')), +			array('config.add', array('pm_max_recipients', '0')), +			array('custom', array(array(&$this, 'set_group_default_max_recipients'))), +			array('config.add', array('dbms_version', $this->db->sql_server_info(true))), +			array('permission.add', array('u_masspm_group', true, 'u_masspm')), +			array('custom', array(array(&$this, 'correct_acp_email_permissions'))), + +			array('config.update', array('version', '3.0.3-rc1')), +		); +	} + +	public function correct_acp_email_permissions() +	{ +		$sql = 'UPDATE ' . $this->table_prefix . 'modules +			SET module_auth = \'acl_a_email && cfg_email_enable\' +			WHERE module_class = \'acp\' +				AND module_basename = \'email\''; +		$this->sql_query($sql); +	} + +	public function set_group_default_max_recipients() +	{ +		// Set maximum number of recipients for the registered users, bots, guests group +		$sql = 'UPDATE ' . GROUPS_TABLE . ' SET group_max_recipients = 5 +			WHERE ' . $this->db->sql_in_set('group_name', array('GUESTS', 'REGISTERED', 'REGISTERED_COPPA', 'BOTS')); +		$this->sql_query($sql); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_4.php b/phpBB/includes/db/migration/data/30x/3_0_4.php new file mode 100644 index 0000000000..9a0c132e78 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_4.php @@ -0,0 +1,49 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_4 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.4', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_4_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array(&$this, 'rename_log_delete_topic'))), + +			array('config.update', array('version', '3.0.4')), +		); +	} + +	public function rename_log_delete_topic() +	{ +		if ($this->db->sql_layer == 'oracle') +		{ +			// log_operation is CLOB - but we can change this later +			$sql = 'UPDATE ' . $this->table_prefix . "log +				SET log_operation = 'LOG_DELETE_TOPIC' +				WHERE log_operation LIKE 'LOG_TOPIC_DELETED'"; +			$this->sql_query($sql); +		} +		else +		{ +			$sql = 'UPDATE ' . $this->table_prefix . "log +				SET log_operation = 'LOG_DELETE_TOPIC' +				WHERE log_operation = 'LOG_TOPIC_DELETED'"; +			$this->sql_query($sql); +		} +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_4_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_4_rc1.php new file mode 100644 index 0000000000..8ad75a557b --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_4_rc1.php @@ -0,0 +1,123 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_4_rc1 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.4-rc1', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_3'); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns' => array( +				$this->table_prefix . 'profile_fields' => array( +					'field_show_profile' => array('BOOL', 0), +				), +			), +			'change_columns' => array( +				$this->table_prefix . 'styles' => array( +					'style_id' => array('UINT', NULL, 'auto_increment'), +					'template_id' => array('UINT', 0), +					'theme_id' => array('UINT', 0), +					'imageset_id' => array('UINT', 0), +				), +				$this->table_prefix . 'styles_imageset' => array( +					'imageset_id' => array('UINT', NULL, 'auto_increment'), +				), +				$this->table_prefix . 'styles_imageset_data' => array( +					'image_id' => array('UINT', NULL, 'auto_increment'), +					'imageset_id' => array('UINT', 0), +				), +				$this->table_prefix . 'styles_theme' => array( +					'theme_id' => array('UINT', NULL, 'auto_increment'), +				), +				$this->table_prefix . 'styles_template' => array( +					'template_id' => array('UINT', NULL, 'auto_increment'), +				), +				$this->table_prefix . 'styles_template_data' => array( +					'template_id' => array('UINT', 0), +				), +				$this->table_prefix . 'forums' => array( +					'forum_style' => array('UINT', 0), +				), +				$this->table_prefix . 'users' => array( +					'user_style' => array('UINT', 0), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns' => array( +				$this->table_prefix . 'profile_fields' => array( +					'field_show_profile', +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array(&$this, 'update_custom_profile_fields'))), + +			array('config.update', array('version', '3.0.4-rc1')), +		); +	} + +	public function update_custom_profile_fields() +	{ +		// Update the Custom Profile Fields based on previous settings to the new format +		$sql = 'SELECT field_id, field_required, field_show_on_reg, field_hide +				FROM ' . PROFILE_FIELDS_TABLE; +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$sql_ary = array( +				'field_required'	=> 0, +				'field_show_on_reg'	=> 0, +				'field_hide'		=> 0, +				'field_show_profile'=> 0, +			); + +			if ($row['field_required']) +			{ +				$sql_ary['field_required'] = $sql_ary['field_show_on_reg'] = $sql_ary['field_show_profile'] = 1; +			} +			else if ($row['field_show_on_reg']) +			{ +				$sql_ary['field_show_on_reg'] = $sql_ary['field_show_profile'] = 1; +			} +			else if ($row['field_hide']) +			{ +				// Only administrators and moderators can see this CPF, if the view is enabled, they can see it, otherwise just admins in the acp_users module +				$sql_ary['field_hide'] = 1; +			} +			else +			{ +				// equivelant to "none", which is the "Display in user control panel" option +				$sql_ary['field_show_profile'] = 1; +			} + +			$this->sql_query('UPDATE ' . $this->table_prefix . 'profile_fields SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' WHERE field_id = ' . $row['field_id'], $errored, $error_ary); +		} + +		$this->db->sql_freeresult($result); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_5.php b/phpBB/includes/db/migration/data/30x/3_0_5.php new file mode 100644 index 0000000000..16d2dee457 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_5.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_5 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.5', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_5_rc1part2'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.5')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_5_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_5_rc1.php new file mode 100644 index 0000000000..ea17cc1e31 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_5_rc1.php @@ -0,0 +1,124 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_5_rc1 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.5-rc1', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_4'); +	} + +	public function update_schema() +	{ +		return array( +			'change_columns' => array( +				$this->table_prefix . 'forums' => array( +					'forum_style' => array('UINT', 0), +				), +			), +		); +	} + +	public function update_data() +	{ +		$search_indexing_state = $this->config['search_indexing_state']; + +		return array( +			array('config.add', array('captcha_gd_wave', 0)), +			array('config.add', array('captcha_gd_3d_noise', 1)), +			array('config.add', array('captcha_gd_fonts', 1)), +			array('config.add', array('confirm_refresh', 1)), +			array('config.add', array('max_num_search_keywords', 10)), +			array('config.remove', array('search_indexing_state')), +			array('config.add', array('search_indexing_state', $search_indexing_state, true)), +			array('custom', array(array(&$this, 'hash_old_passwords'))), +			array('custom', array(array(&$this, 'update_ichiro_bot'))), +		); +	} + +	public function hash_old_passwords() +	{ +		$sql = 'SELECT user_id, user_password +				FROM ' . $this->table_prefix . 'users +				WHERE user_pass_convert = 1'; +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			if (strlen($row['user_password']) == 32) +			{ +				$sql_ary = array( +					'user_password'	=> phpbb_hash($row['user_password']), +				); + +				$this->sql_query('UPDATE ' . $this->table_prefix . 'users SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' WHERE user_id = ' . $row['user_id']); +			} +		} +		$this->db->sql_freeresult($result); +	} + +	public function update_ichiro_bot() +	{ +		// Adjust bot entry +		$sql = 'UPDATE ' . $this->table_prefix . "bots +			SET bot_agent = 'ichiro/' +			WHERE bot_agent = 'ichiro/2'"; +		$this->sql_query($sql); +	} + +	public function remove_duplicate_auth_options() +	{ +		// Before we are able to add a unique key to auth_option, we need to remove duplicate entries +		$sql = 'SELECT auth_option +			FROM ' . $this->table_prefix . 'acl_options +			GROUP BY auth_option +			HAVING COUNT(*) >= 2'; +		$result = $this->db->sql_query($sql); + +		$auth_options = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$auth_options[] = $row['auth_option']; +		} +		$this->db->sql_freeresult($result); + +		// Remove specific auth options +		if (!empty($auth_options)) +		{ +			foreach ($auth_options as $option) +			{ +				// Select auth_option_ids... the largest id will be preserved +				$sql = 'SELECT auth_option_id +					FROM ' . ACL_OPTIONS_TABLE . " +					WHERE auth_option = '" . $db->sql_escape($option) . "' +					ORDER BY auth_option_id DESC"; +				// sql_query_limit not possible here, due to bug in postgresql layer +				$result = $this->db->sql_query($sql); + +				// Skip first row, this is our original auth option we want to preserve +				$row = $this->db->sql_fetchrow($result); + +				while ($row = $this->db->sql_fetchrow($result)) +				{ +					// Ok, remove this auth option... +					$this->sql_query('DELETE FROM ' . ACL_OPTIONS_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']); +					$this->sql_query('DELETE FROM ' . ACL_ROLES_DATA_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']); +					$this->sql_query('DELETE FROM ' . ACL_GROUPS_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']); +					$this->sql_query('DELETE FROM ' . ACL_USERS_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']); +				} +				$this->db->sql_freeresult($result); +			} +		} +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_5_rc1part2.php b/phpBB/includes/db/migration/data/30x/3_0_5_rc1part2.php new file mode 100644 index 0000000000..8538347b1a --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_5_rc1part2.php @@ -0,0 +1,42 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_5_rc1part2 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.5-rc1', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_5_rc1'); +	} + +	public function update_schema() +	{ +		return array( +			'drop_keys'			=> array( +				$this->table_prefix . 'acl_options'		=> array('auth_option'), +			), +			'add_unique_index'	=> array( +				$this->table_prefix . 'acl_options'		=> array( +					'auth_option'		=> array('auth_option'), +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.5-rc1')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_6.php b/phpBB/includes/db/migration/data/30x/3_0_6.php new file mode 100644 index 0000000000..bb651dc7cd --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_6.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_6 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.6', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_6_rc4'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.6')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_6_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_6_rc1.php new file mode 100644 index 0000000000..38c282ebf0 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_6_rc1.php @@ -0,0 +1,324 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_6_rc1 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.6-rc1', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_5'); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns' => array( +				$this->table_prefix . 'confirm' => array( +					'attempts' => array('UINT', 0), +				), +				$this->table_prefix . 'users' => array( +					'user_new' => array('BOOL', 1), +					'user_reminded' => array('TINT:4', 0), +					'user_reminded_time' => array('TIMESTAMP', 0), +				), +				$this->table_prefix . 'groups' => array( +					'group_skip_auth' => array('BOOL', 0, 'after' => 'group_founder_manage'), +				), +				$this->table_prefix . 'privmsgs' => array( +					'message_reported' => array('BOOL', 0), +				), +				$this->table_prefix . 'reports' => array( +					'pm_id' => array('UINT', 0), +				), +				$this->table_prefix . 'profile_fields'	=> array( +					'field_show_on_vt' => array('BOOL', 0), +				), +				$this->table_prefix . 'forums' => array( +					'forum_options' => array('UINT:20', 0), +				), +			), +			'change_columns' => array( +				$this->table_prefix . 'users' => array( +					'user_options' => array('UINT:11', 230271), +				), +			), +			'add_index' => array( +				$this->table_prefix . 'reports' => array( +					'post_id' => array('post_id'), +					'pm_id' => array('pm_id'), +				), +				$this->table_prefix . 'posts' => array( +					'post_username' => array('post_username:255'), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns' => array( +				$this->table_prefix . 'confirm' => array( +					'attempts', +				), +				$this->table_prefix . 'users' => array( +					'user_new', +					'user_reminded', +					'user_reminded_time', +				), +				$this->table_prefix . 'groups' => array( +					'group_skip_auth', +				), +				$this->table_prefix . 'privmsgs' => array( +					'message_reported', +				), +				$this->table_prefix . 'reports' => array( +					'pm_id', +				), +				$this->table_prefix . 'profile_fields'	=> array( +					'field_show_on_vt', +				), +				$this->table_prefix . 'forums' => array( +					'forum_options', +				), +			), +			'drop_keys' => array( +				$this->table_prefix . 'reports' => array( +					'post_id', +					'pm_id', +				), +				$this->table_prefix . 'posts' => array( +					'post_username', +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('captcha_plugin', 'phpbb_captcha_nogd')), +			array('if', array( +				($this->config['captcha_gd']), +				array('config.update', array('captcha_plugin', 'phpbb_captcha_gd')), +			)), + +			array('config.add', array('feed_enable', 0)), +			array('config.add', array('feed_limit', 10)), +			array('config.add', array('feed_overall_forums', 1)), +			array('config.add', array('feed_overall_forums_limit', 15)), +			array('config.add', array('feed_overall_topics', 0)), +			array('config.add', array('feed_overall_topics_limit', 15)), +			array('config.add', array('feed_forum', 1)), +			array('config.add', array('feed_topic', 1)), +			array('config.add', array('feed_item_statistics', 1)), + +			array('config.add', array('smilies_per_page', 50)), +			array('config.add', array('allow_pm_report', 1)), +			array('config.add', array('min_post_chars', 1)), +			array('config.add', array('allow_quick_reply', 1)), +			array('config.add', array('new_member_post_limit', 0)), +			array('config.add', array('new_member_group_default', 0)), +			array('config.add', array('delete_time', $this->config['edit_time'])), + +			array('config.add', array('allow_avatar', 0)), +			array('if', array( +				($this->config['allow_avatar_upload'] || $this->config['allow_avatar_local'] || $this->config['allow_avatar_remote']), +				array('config.update', array('allow_avatar', 1)), +			)), +			array('config.add', array('allow_avatar_remote_upload', 0)), +			array('if', array( +				($this->config['allow_avatar_remote'] && $this->config['allow_avatar_upload']), +				array('config.update', array('allow_avatar_remote_upload', 1)), +			)), + +			array('module.add', array( +				'acp', +				'ACP_BOARD_CONFIGURATION', +				array( +					'module_basename'	=> 'acp_board', +					'modes'				=> array('feed'), +				), +			)), +			array('module.add', array( +				'acp', +				'ACP_CAT_USERS', +				array( +					'module_basename'	=> 'acp_users', +					'modes'				=> array('warnings'), +				), +			)), +			array('module.add', array( +				'acp', +				'ACP_SERVER_CONFIGURATION', +				array( +					'module_basename'	=> 'acp_send_statistics', +					'modes'				=> array('send_statistics'), +				), +			)), +			array('module.add', array( +				'acp', +				'ACP_FORUM_BASED_PERMISSIONS', +				array( +					'module_basename'	=> 'acp_permissions', +					'modes'				=> array('setting_forum_copy'), +				), +			)), +			array('module.add', array( +				'mcp', +				'MCP_REPORTS', +				array( +					'module_basename'	=> 'mcp_pm_reports', +					'modes'				=> array('pm_reports','pm_reports_closed','pm_report_details'), +				), +			)), +			array('custom', array(array(&$this, 'add_newly_registered_group'))), +			array('custom', array(array(&$this, 'set_user_options_default'))), + +			array('config.update', array('version', '3.0.6-rc1')), +		); +	} + +	public function set_user_options_default() +	{ +		// 229376 is the added value to enable all three signature options +		$sql = 'UPDATE ' . USERS_TABLE . ' SET user_options = user_options + 229376'; +		$this->sql_query($sql); +	} + +	public function add_newly_registered_group() +	{ +		// Add newly_registered group... but check if it already exists (we always supported running the updater on any schema) +		$sql = 'SELECT group_id +			FROM ' . GROUPS_TABLE . " +			WHERE group_name = 'NEWLY_REGISTERED'"; +		$result = $this->db->sql_query($sql); +		$group_id = (int) $this->db->sql_fetchfield('group_id'); +		$this->db->sql_freeresult($result); + +		if (!$group_id) +		{ +			$sql = 'INSERT INTO ' .  GROUPS_TABLE . " (group_name, group_type, group_founder_manage, group_colour, group_legend, group_avatar, group_desc, group_desc_uid, group_max_recipients) VALUES ('NEWLY_REGISTERED', 3, 0, '', 0, '', '', '', 5)"; +			$this->sql_query($sql); + +			$group_id = $this->db->sql_nextid(); +		} + +		// Insert new user role... at the end of the chain +		$sql = 'SELECT role_id +			FROM ' . ACL_ROLES_TABLE . " +			WHERE role_name = 'ROLE_USER_NEW_MEMBER' +				AND role_type = 'u_'"; +		$result = $this->db->sql_query($sql); +		$u_role = (int) $this->db->sql_fetchfield('role_id'); +		$this->db->sql_freeresult($result); + +		if (!$u_role) +		{ +			$sql = 'SELECT MAX(role_order) as max_order_id +				FROM ' . ACL_ROLES_TABLE . " +				WHERE role_type = 'u_'"; +			$result = $this->db->sql_query($sql); +			$next_order_id = (int) $this->db->sql_fetchfield('max_order_id'); +			$this->db->sql_freeresult($result); + +			$next_order_id++; + +			$sql = 'INSERT INTO ' . ACL_ROLES_TABLE . " (role_name, role_description, role_type, role_order) VALUES ('ROLE_USER_NEW_MEMBER', 'ROLE_DESCRIPTION_USER_NEW_MEMBER', 'u_', $next_order_id)"; +			$this->sql_query($sql); +			$u_role = $this->db->sql_nextid(); + +			// Now add the correct data to the roles... +			// The standard role says that new users are not able to send a PM, Mass PM, are not able to PM groups +			$sql = 'INSERT INTO ' . ACL_ROLES_DATA_TABLE . " (role_id, auth_option_id, auth_setting) SELECT $u_role, auth_option_id, 0 FROM " . ACL_OPTIONS_TABLE . " WHERE auth_option LIKE 'u_%' AND auth_option IN ('u_sendpm', 'u_masspm', 'u_masspm_group')"; +			$this->sql_query($sql); + +			// Add user role to group +			$sql = 'INSERT INTO ' . ACL_GROUPS_TABLE . " (group_id, forum_id, auth_option_id, auth_role_id, auth_setting) VALUES ($group_id, 0, 0, $u_role, 0)"; +			$this->sql_query($sql); +		} + +		// Insert new forum role +		$sql = 'SELECT role_id +			FROM ' . ACL_ROLES_TABLE . " +			WHERE role_name = 'ROLE_FORUM_NEW_MEMBER' +				AND role_type = 'f_'"; +		$result = $this->db->sql_query($sql); +		$f_role = (int) $this->db->sql_fetchfield('role_id'); +		$this->db->sql_freeresult($result); + +		if (!$f_role) +		{ +			$sql = 'SELECT MAX(role_order) as max_order_id +				FROM ' . ACL_ROLES_TABLE . " +				WHERE role_type = 'f_'"; +			$result = $this->db->sql_query($sql); +			$next_order_id = (int) $this->db->sql_fetchfield('max_order_id'); +			$this->db->sql_freeresult($result); + +			$next_order_id++; + +			$sql = 'INSERT INTO ' . ACL_ROLES_TABLE . " (role_name, role_description, role_type, role_order) VALUES  ('ROLE_FORUM_NEW_MEMBER', 'ROLE_DESCRIPTION_FORUM_NEW_MEMBER', 'f_', $next_order_id)"; +			$this->sql_query($sql); +			$f_role = $this->db->sql_nextid(); + +			$sql = 'INSERT INTO ' . ACL_ROLES_DATA_TABLE . " (role_id, auth_option_id, auth_setting) SELECT $f_role, auth_option_id, 0 FROM " . ACL_OPTIONS_TABLE . " WHERE auth_option LIKE 'f_%' AND auth_option IN ('f_noapprove')"; +			$this->sql_query($sql); +		} + +		// Set every members user_new column to 0 (old users) only if there is no one yet (this makes sure we do not execute this more than once) +		$sql = 'SELECT 1 +			FROM ' . USERS_TABLE . ' +			WHERE user_new = 0'; +		$result = $this->db->sql_query_limit($sql, 1); +		$row = $this->db->sql_fetchrow($result); +		$this->db->sql_freeresult($result); + +		if (!$row) +		{ +			$sql = 'UPDATE ' . USERS_TABLE . ' SET user_new = 0'; +			$this->sql_query($sql); +		} + +		// To mimick the old "feature" we will assign the forum role to every forum, regardless of the setting (this makes sure there are no "this does not work!!!! YUO!!!" posts... +		// Check if the role is already assigned... +		$sql = 'SELECT forum_id +			FROM ' . ACL_GROUPS_TABLE . ' +			WHERE group_id = ' . $group_id . ' +				AND auth_role_id = ' . $f_role; +		$result = $this->db->sql_query($sql); +		$is_options = (int) $this->db->sql_fetchfield('forum_id'); +		$this->db->sql_freeresult($result); + +		// Not assigned at all... :/ +		if (!$is_options) +		{ +			// Get postable forums +			$sql = 'SELECT forum_id +				FROM ' . FORUMS_TABLE . ' +				WHERE forum_type != ' . FORUM_LINK; +			$result = $this->db->sql_query($sql); + +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				$this->sql_query('INSERT INTO ' . ACL_GROUPS_TABLE . ' (group_id, forum_id, auth_option_id, auth_role_id, auth_setting) VALUES (' . $group_id . ', ' . (int) $row['forum_id'] . ', 0, ' . $f_role . ', 0)'); +			} +			$this->db->sql_freeresult($result); +		} + +		// Clear permissions... +		include_once($this->phpbb_root_path . 'includes/acp/auth.' . $this->php_ext); +		$auth_admin = new auth_admin(); +		$auth_admin->acl_clear_prefetch(); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_6_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_6_rc2.php new file mode 100644 index 0000000000..a939dbd489 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_6_rc2.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_6_rc2 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.6-rc2', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_6_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.6-rc2')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_6_rc3.php b/phpBB/includes/db/migration/data/30x/3_0_6_rc3.php new file mode 100644 index 0000000000..b3f09d8ab8 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_6_rc3.php @@ -0,0 +1,40 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_6_rc3 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.6-rc3', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_6_rc2'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array(&$this, 'update_cp_fields'))), + +			array('config.update', array('version', '3.0.6-rc3')), +		); +	} + +	public function update_cp_fields() +	{ +		// Update the Custom Profile Fields based on previous settings to the new format +		$sql = 'UPDATE ' . PROFILE_FIELDS_TABLE . ' +			SET field_show_on_vt = 1 +			WHERE field_hide = 0 +				AND (field_required = 1 OR field_show_on_reg = 1 OR field_show_profile = 1)'; +		$this->sql_query($sql); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_6_rc4.php b/phpBB/includes/db/migration/data/30x/3_0_6_rc4.php new file mode 100644 index 0000000000..fc2923f99b --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_6_rc4.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_6_rc4 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.6-rc4', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_6_rc3'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.6-rc4')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_7.php b/phpBB/includes/db/migration/data/30x/3_0_7.php new file mode 100644 index 0000000000..9ff2e9e4ab --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_7.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_7 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.7', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_7_rc2'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.7')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_7_pl1.php b/phpBB/includes/db/migration/data/30x/3_0_7_pl1.php new file mode 100644 index 0000000000..c9cc9d19ac --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_7_pl1.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_7_pl1 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.7-pl1', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_7'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.7-pl1')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_7_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_7_rc1.php new file mode 100644 index 0000000000..ffebf66f2d --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_7_rc1.php @@ -0,0 +1,76 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_7_rc1 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.7-rc1', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_6'); +	} + +	public function update_schema() +	{ +		return array( +			'drop_keys' => array( +				$this->table_prefix . 'log' => array( +					'log_time', +				), +			), +			'add_index' => array( +				$this->table_prefix . 'topics_track' => array( +					'topic_id' => array('topic_id'), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_index' => array( +				$this->table_prefix . 'log' => array( +					'log_time'	=> array('log_time'), +				), +			), +			'drop_keys' => array( +				$this->table_prefix . 'topics_track' => array( +					'topic_id', +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('feed_overall', 1)), +			array('config.add', array('feed_http_auth', 0)), +			array('config.add', array('feed_limit_post', $this->config['feed_limit'])), +			array('config.add', array('feed_limit_topic', $this->config['feed_overall_topics_limit'])), +			array('config.add', array('feed_topics_new', $this->config['feed_overall_topics'])), +			array('config.add', array('feed_topics_active', $this->config['feed_overall_topics'])), +			array('custom', array(array(&$this, 'delete_text_templates'))), + +			array('config.update', array('version', '3.0.7-rc1')), +		); +	} + +	public function delete_text_templates() +	{ +		// Delete all text-templates from the template_data +		$sql = 'DELETE FROM ' . STYLES_TEMPLATE_DATA_TABLE . ' +			WHERE template_filename ' . $this->db->sql_like_expression($this->db->any_char . '.txt'); +		$this->sql_query($sql); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_7_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_7_rc2.php new file mode 100644 index 0000000000..55bc2bc679 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_7_rc2.php @@ -0,0 +1,73 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_7_rc2 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.7-rc2', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_7_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array(&$this, 'update_email_hash'))), + +			array('config.update', array('version', '3.0.7-rc2')), +		); +	} + +	public function update_email_hash($start = 0) +	{ +		$limit = 1000; + +		$sql = 'SELECT user_id, user_email, user_email_hash +			FROM ' . USERS_TABLE . ' +			WHERE user_type <> ' . USER_IGNORE . " +				AND user_email <> ''"; +		$result = $this->db->sql_query_limit($sql, $limit, $start); + +		$i = 0; +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$i++; + +			// Snapshot of the phpbb_email_hash() function +			// We cannot call it directly because the auto updater updates the DB first. :/ +			$user_email_hash = sprintf('%u', crc32(strtolower($row['user_email']))) . strlen($row['user_email']); + +			if ($user_email_hash != $row['user_email_hash']) +			{ +				$sql_ary = array( +					'user_email_hash'	=> $user_email_hash, +				); + +				$sql = 'UPDATE ' . USERS_TABLE . ' +					SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' +					WHERE user_id = ' . (int) $row['user_id']; +				$this->sql_query($sql); +			} +		} +		$this->db->sql_freeresult($result); + +		if ($i < $limit) +		{ +			// Completed +			return; +		} + +		// Return the next start, will be sent to $start when this function is called again +		return $start + $limit; +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_8.php b/phpBB/includes/db/migration/data/30x/3_0_8.php new file mode 100644 index 0000000000..8998ef9627 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_8.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_8 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.8', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_8_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.8')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_8_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_8_rc1.php new file mode 100644 index 0000000000..aeff35333e --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_8_rc1.php @@ -0,0 +1,221 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_8_rc1 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.8-rc1', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_7_pl1'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array(&$this, 'update_file_extension_group_names'))), +			array('custom', array(array(&$this, 'update_module_auth'))), +			array('custom', array(array(&$this, 'update_bots'))), +			array('custom', array(array(&$this, 'delete_orphan_shadow_topics'))), +			array('module.add', array( +				'acp', +				'ACP_MESSAGES', +				array( +					'module_basename'	=> 'acp_board', +					'modes'				=> array('post'), +				), +			)), +			array('config.add', array('load_unreads_search', 1)), +			array('config.update_if_equals', array(600, 'queue_interval', 60)), +			array('config.update_if_equals', array(50, 'email_package_size', 20)), + +			array('config.update', array('version', '3.0.8-rc1')), +		); +	} + +	public function update_file_extension_group_names() +	{ +		// Update file extension group names to use language strings. +		$sql = 'SELECT lang_dir +			FROM ' . LANG_TABLE; +		$result = $this->db->sql_query($sql); + +		$extension_groups_updated = array(); +		while ($lang_dir = $this->db->sql_fetchfield('lang_dir')) +		{ +			$lang_dir = basename($lang_dir); + +			// The language strings we need are either in language/.../acp/attachments.php +			// in the update package if we're updating to 3.0.8-RC1 or later, +			// or they are in language/.../install.php when we're updating from 3.0.7-PL1 or earlier. +			// On an already updated board, they can also already be in language/.../acp/attachments.php +			// in the board root. +			$lang_files = array( +				"{$this->phpbb_root_path}install/update/new/language/$lang_dir/acp/attachments.{$this->php_ext}", +				"{$this->phpbb_root_path}language/$lang_dir/install.{$this->php_ext}", +				"{$this->phpbb_root_path}language/$lang_dir/acp/attachments.{$this->php_ext}", +			); + +			foreach ($lang_files as $lang_file) +			{ +				if (!file_exists($lang_file)) +				{ +					continue; +				} + +				$lang = array(); +				include($lang_file); + +				foreach($lang as $lang_key => $lang_val) +				{ +					if (isset($extension_groups_updated[$lang_key]) || strpos($lang_key, 'EXT_GROUP_') !== 0) +					{ +						continue; +					} + +					$sql_ary = array( +						'group_name'	=> substr($lang_key, 10), // Strip off 'EXT_GROUP_' +					); + +					$sql = 'UPDATE ' . EXTENSION_GROUPS_TABLE . ' +						SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . " +						WHERE group_name = '" . $this->db->sql_escape($lang_val) . "'"; +					$this->sql_query($sql); + +					$extension_groups_updated[$lang_key] = true; +				} +			} +		} +		$this->db->sql_freeresult($result); +	} + +	public function update_module_auth() +	{ +		$sql = 'UPDATE ' . MODULES_TABLE . ' +			SET module_auth = \'cfg_allow_avatar && (cfg_allow_avatar_local || cfg_allow_avatar_remote || cfg_allow_avatar_upload || cfg_allow_avatar_remote_upload)\' +			WHERE module_class = \'ucp\' +				AND module_basename = \'profile\' +				AND module_mode = \'avatar\''; +		$this->sql_query($sql); +	} + +	public function update_bots() +	{ +		$bot_name = 'Bing [Bot]'; +		$bot_name_clean = utf8_clean_string($bot_name); + +		$sql = 'SELECT user_id +			FROM ' . USERS_TABLE . " +			WHERE username_clean = '" . $this->db->sql_escape($bot_name_clean) . "'"; +		$result = $this->db->sql_query($sql); +		$bing_already_added = (bool) $this->db->sql_fetchfield('user_id'); +		$this->db->sql_freeresult($result); + +		if (!$bing_already_added) +		{ +			$bot_agent = 'bingbot/'; +			$bot_ip = ''; +			$sql = 'SELECT group_id, group_colour +				FROM ' . GROUPS_TABLE . " +				WHERE group_name = 'BOTS'"; +			$result = $this->db->sql_query($sql); +			$group_row = $this->db->sql_fetchrow($result); +			$this->db->sql_freeresult($result); + +			if (!$group_row) +			{ +				// default fallback, should never get here +				$group_row['group_id'] = 6; +				$group_row['group_colour'] = '9E8DA7'; +			} + +			if (!function_exists('user_add')) +			{ +				include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext); +			} + +			$user_row = array( +				'user_type'				=> USER_IGNORE, +				'group_id'				=> $group_row['group_id'], +				'username'				=> $bot_name, +				'user_regdate'			=> time(), +				'user_password'			=> '', +				'user_colour'			=> $group_row['group_colour'], +				'user_email'			=> '', +				'user_lang'				=> $this->config['default_lang'], +				'user_style'			=> $this->config['default_style'], +				'user_timezone'			=> 0, +				'user_dateformat'		=> $this->config['default_dateformat'], +				'user_allow_massemail'	=> 0, +			); + +			$user_id = user_add($user_row); + +			$sql = 'INSERT INTO ' . BOTS_TABLE . ' ' . $this->db->sql_build_array('INSERT', array( +				'bot_active'	=> 1, +				'bot_name'		=> (string) $bot_name, +				'user_id'		=> (int) $user_id, +				'bot_agent'		=> (string) $bot_agent, +				'bot_ip'		=> (string) $bot_ip, +			)); + +			$this->sql_query($sql); +		} +	} + +	public function delete_orphan_shadow_topics() +	{ +		// Delete shadow topics pointing to not existing topics +		$batch_size = 500; + +		// Set of affected forums we have to resync +		$sync_forum_ids = array(); + +		$sql_array = array( +			'SELECT'	=> 't1.topic_id, t1.forum_id', +			'FROM'		=> array( +				TOPICS_TABLE	=> 't1', +			), +			'LEFT_JOIN'	=> array( +				array( +					'FROM'	=> array(TOPICS_TABLE	=> 't2'), +					'ON'	=> 't1.topic_moved_id = t2.topic_id', +				), +			), +			'WHERE'		=> 't1.topic_moved_id <> 0 +						AND t2.topic_id IS NULL', +		); +		$sql = $this->db->sql_build_query('SELECT', $sql_array); +		$result = $this->db->sql_query_limit($sql, $batch_size); + +		$topic_ids = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$topic_ids[] = (int) $row['topic_id']; + +			$sync_forum_ids[(int) $row['forum_id']] = (int) $row['forum_id']; +		} +		$this->db->sql_freeresult($result); + +		if (!empty($topic_ids)) +		{ +			$sql = 'DELETE FROM ' . TOPICS_TABLE . ' +				WHERE ' . $this->db->sql_in_set('topic_id', $topic_ids); +			$this->db->sql_query($sql); + +			// Sync the forums we have deleted shadow topics from. +			sync('forum', 'forum_id', $sync_forum_ids, true, true); + +			return false; +		} +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_9.php b/phpBB/includes/db/migration/data/30x/3_0_9.php new file mode 100644 index 0000000000..d5269ea6f0 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_9.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_9 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.9', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_9_rc4'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.9')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_9_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_9_rc1.php new file mode 100644 index 0000000000..1f8622798e --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_9_rc1.php @@ -0,0 +1,124 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_9_rc1 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.9-rc1', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_8'); +	} + +	public function update_schema() +	{ +		return array( +			'add_tables' => array( +				$this->table_prefix . 'login_attempts' => array( +					'COLUMNS' => array( +						// this column was removed from the database updater +						// after 3.0.9-RC3 was released. It might still exist +						// in 3.0.9-RCX installations and has to be dropped in +						// 3.0.12 after the db_tools class is capable of properly +						// removing a primary key. +						// 'attempt_id'			=> array('UINT', NULL, 'auto_increment'), +						'attempt_ip'			=> array('VCHAR:40', ''), +						'attempt_browser'		=> array('VCHAR:150', ''), +						'attempt_forwarded_for'	=> array('VCHAR:255', ''), +						'attempt_time'			=> array('TIMESTAMP', 0), +						'user_id'				=> array('UINT', 0), +						'username'				=> array('VCHAR_UNI:255', 0), +						'username_clean'		=> array('VCHAR_CI', 0), +					), +					//'PRIMARY_KEY' => 'attempt_id', +					'KEYS' => array( +						'att_ip' => array('INDEX', array('attempt_ip', 'attempt_time')), +						'att_for' => array('INDEX', array('attempt_forwarded_for', 'attempt_time')), +						'att_time' => array('INDEX', array('attempt_time')), +						'user_id' => array('INDEX', 'user_id'), +					), +				), +			), +			'change_columns' => array( +				$this->table_prefix . 'bbcodes' => array( +					'bbcode_id' => array('USINT', 0), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_tables' => array( +				$this->table_prefix . 'login_attempts', +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('ip_login_limit_max', 50)), +			array('config.add', array('ip_login_limit_time', 21600)), +			array('config.add', array('ip_login_limit_use_forwarded', 0)), +			array('custom', array(array(&$this, 'update_file_extension_group_names'))), +			array('custom', array(array(&$this, 'fix_firebird_qa_captcha'))), + +			array('config.update', array('version', '3.0.9-rc1')), +		); +	} + +	public function update_file_extension_group_names() +	{ +		// Update file extension group names to use language strings, again. +		$sql = 'SELECT group_id, group_name +			FROM ' . EXTENSION_GROUPS_TABLE . ' +			WHERE group_name ' . $this->db->sql_like_expression('EXT_GROUP_' . $this->db->any_char); +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$sql_ary = array( +				'group_name'	=> substr($row['group_name'], 10), // Strip off 'EXT_GROUP_' +			); + +			$sql = 'UPDATE ' . EXTENSION_GROUPS_TABLE . ' +				SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' +				WHERE group_id = ' . $row['group_id']; +			$this->sql_query($sql); +		} +		$this->db->sql_freeresult($result); +	} + +	public function fix_firebird_qa_captcha() +	{ +		// Recover from potentially broken Q&A CAPTCHA table on firebird +		// Q&A CAPTCHA was uninstallable, so it's safe to remove these +		// without data loss +		if ($this->db_tools->sql_layer == 'firebird') +		{ +			$tables = array( +				$this->table_prefix . 'captcha_questions', +				$this->table_prefix . 'captcha_answers', +				$this->table_prefix . 'qa_confirm', +			); +			foreach ($tables as $table) +			{ +				if ($this->db_tools->sql_table_exists($table)) +				{ +					$this->db_tools->sql_table_drop($table); +				} +			} +		} +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_9_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_9_rc2.php new file mode 100644 index 0000000000..c0e662aa45 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_9_rc2.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_9_rc2 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.9-rc2', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_9_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.9-rc2')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_9_rc3.php b/phpBB/includes/db/migration/data/30x/3_0_9_rc3.php new file mode 100644 index 0000000000..d6d1f14b2e --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_9_rc3.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_9_rc3 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.9-rc3', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_9_rc2'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.9-rc3')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/30x/3_0_9_rc4.php b/phpBB/includes/db/migration/data/30x/3_0_9_rc4.php new file mode 100644 index 0000000000..e673249343 --- /dev/null +++ b/phpBB/includes/db/migration/data/30x/3_0_9_rc4.php @@ -0,0 +1,28 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_30x_3_0_9_rc4 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.0.9-rc4', '>='); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_9_rc3'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.9-rc4')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/310/dev.php b/phpBB/includes/db/migration/data/310/dev.php new file mode 100644 index 0000000000..13b36bbf30 --- /dev/null +++ b/phpBB/includes/db/migration/data/310/dev.php @@ -0,0 +1,405 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_310_dev extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.1.0-dev', '>='); +	} + +	static public function depends_on() +	{ +		return array( +			'phpbb_db_migration_data_310_extensions', +			'phpbb_db_migration_data_310_style_update_p2', +			'phpbb_db_migration_data_310_timezone_p2', +			'phpbb_db_migration_data_310_reported_posts_display', +		); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns'		=> array( +				$this->table_prefix . 'groups'		=> array( +					'group_teampage'	=> array('UINT', 0, 'after' => 'group_legend'), +				), +				$this->table_prefix . 'profile_fields'	=> array( +					'field_show_on_pm'		=> array('BOOL', 0), +				), +				$this->table_prefix . 'styles'		=> array( +					'style_path'			=> array('VCHAR:100', ''), +					'bbcode_bitfield'		=> array('VCHAR:255', 'kNg='), +					'style_parent_id'		=> array('UINT:4', 0), +					'style_parent_tree'		=> array('TEXT', ''), +				), +				$this->table_prefix . 'reports'		=> array( +					'reported_post_text'		=> array('MTEXT_UNI', ''), +					'reported_post_uid'			=> array('VCHAR:8', ''), +					'reported_post_bitfield'	=> array('VCHAR:255', ''), +				), +			), +			'change_columns'	=> array( +				$this->table_prefix . 'groups'		=> array( +					'group_legend'		=> array('UINT', 0), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns'		=> array( +				$this->table_prefix . 'groups'		=> array( +					'group_teampage', +				), +				$this->table_prefix . 'profile_fields'	=> array( +					'field_show_on_pm', +				), +				$this->table_prefix . 'styles'		=> array( +					'style_path', +					'bbcode_bitfield', +					'style_parent_id', +					'style_parent_tree', +				), +				$this->table_prefix . 'reports'		=> array( +					'reported_post_text', +					'reported_post_uid', +					'reported_post_bitfield', +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('search_type', 'phpbb_search_' . $this->config['search_type'])), + +			array('config.add', array('fulltext_postgres_ts_name', 'simple')), +			array('config.add', array('fulltext_postgres_min_word_len', 4)), +			array('config.add', array('fulltext_postgres_max_word_len', 254)), +			array('config.add', array('fulltext_sphinx_stopwords', 0)), +			array('config.add', array('fulltext_sphinx_indexer_mem_limit', 512)), + +			array('config.add', array('load_jquery_cdn', 0)), +			array('config.add', array('load_jquery_url', '//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js')), + +			array('config.add', array('use_system_cron', 0)), + +			array('config.add', array('legend_sort_groupname', 0)), +			array('config.add', array('teampage_forums', 1)), +			array('config.add', array('teampage_memberships', 1)), + +			array('config.add', array('load_cpf_pm', 0)), + +			array('config.add', array('display_last_subject', 1)), + +			array('config.add', array('assets_version', 1)), + +			array('config.add', array('site_home_url', '')), +			array('config.add', array('site_home_text', '')), + +			array('permission.add', array('u_chgprofileinfo', true, 'u_sig')), + +			array('module.add', array( +				'acp', +				'ACP_GROUPS', +				array( +					'module_basename'	=> 'acp_groups', +					'modes'				=> array('position'), +				), +			)), +			array('module.add', array( +				'acp', +				'ACP_ATTACHMENTS', +				array( +					'module_basename'	=> 'acp_attachments', +					'modes'				=> array('manage'), +				), +			)), +			array('module.add', array( +				'acp', +				'ACP_STYLE_MANAGEMENT', +				array( +					'module_basename'	=> 'acp_styles', +					'modes'				=> array('install', 'cache'), +				), +			)), +			array('module.add', array( +				'ucp', +				'UCP_PROFILE', +				array( +					'module_basename'	=> 'ucp_profile', +					'modes'				=> array('autologin_keys'), +				), +			)), +			// Module will be renamed later +			array('module.add', array( +				'acp', +				'ACP_CAT_STYLES', +				'ACP_LANGUAGE' +			)), + +			array('module.remove', array( +				'acp', +				false, +				'ACP_TEMPLATES', +			)), +			array('module.remove', array( +				'acp', +				false, +				'ACP_THEMES', +			)), +			array('module.remove', array( +				'acp', +				false, +				'ACP_IMAGESETS', +			)), + +			array('custom', array(array($this, 'rename_module_basenames'))), +			array('custom', array(array($this, 'rename_styles_module'))), +			array('custom', array(array($this, 'add_group_teampage'))), +			array('custom', array(array($this, 'update_group_legend'))), +			array('custom', array(array($this, 'localise_global_announcements'))), +			array('custom', array(array($this, 'update_ucp_pm_basename'))), +			array('custom', array(array($this, 'update_ucp_profile_auth'))), +			array('custom', array(array($this, 'move_customise_modules'))), + +			array('config.update', array('version', '3.1.0-dev')), +		); +	} + +	public function move_customise_modules() +	{ +		// Move language management to new location in the Customise tab +		// First get language module id +		$sql = 'SELECT module_id FROM ' . MODULES_TABLE . " +			WHERE module_basename = 'acp_language'"; +		$result = $this->db->sql_query($sql); +		$language_module_id = $this->db->sql_fetchfield('module_id'); +		$this->db->sql_freeresult($result); +		// Next get language management module id of the one just created +		$sql = 'SELECT module_id FROM ' . MODULES_TABLE . " +			WHERE module_langname = 'ACP_LANGUAGE'"; +		$result = $this->db->sql_query($sql); +		$language_management_module_id = $this->db->sql_fetchfield('module_id'); +		$this->db->sql_freeresult($result); + +		if (!class_exists('acp_modules')) +		{ +			include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext); +		} +		// acp_modules calls adm_back_link, which is undefined at this point +		if (!function_exists('adm_back_link')) +		{ +			include($this->phpbb_root_path . 'includes/functions_acp.' . $this->php_ext); +		} +		$module_manager = new acp_modules(); +		$module_manager->module_class = 'acp'; +		$module_manager->move_module($language_module_id, $language_management_module_id); +	} + +	public function update_ucp_pm_basename() +	{ +		$sql = 'SELECT module_id, module_basename +			FROM ' . MODULES_TABLE . " +			WHERE module_basename <> 'ucp_pm' AND +				module_langname='UCP_PM'"; +		$result = $this->db->sql_query_limit($sql, 1); + +		if ($row = $this->db->sql_fetchrow($result)) +		{ +			// This update is still not applied. Applying it + +			$sql = 'UPDATE ' . MODULES_TABLE . " +				SET module_basename = 'ucp_pm' +				WHERE  module_id = " . (int) $row['module_id']; + +			$this->sql_query($sql); +		} +		$this->db->sql_freeresult($result); +	} + +	public function update_ucp_profile_auth() +	{ +		// Update the auth setting for the module +		$sql = 'UPDATE ' . MODULES_TABLE . " +			SET module_auth = 'acl_u_chgprofileinfo' +			WHERE module_class = 'ucp' +				AND module_basename = 'ucp_profile' +				AND module_mode = 'profile_info'"; +		$this->sql_query($sql); +	} + +	public function rename_styles_module() +	{ +		// Rename styles module to Customise +		$sql = 'UPDATE ' . MODULES_TABLE . " +			SET module_langname = 'ACP_CAT_CUSTOMISE' +			WHERE module_langname = 'ACP_CAT_STYLES'"; +		$this->sql_query($sql); +	} + +	public function rename_module_basenames() +	{ +		// rename all module basenames to full classname +		$sql = 'SELECT module_id, module_basename, module_class +			FROM ' . MODULES_TABLE; +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$module_id = (int) $row['module_id']; +			unset($row['module_id']); + +			if (!empty($row['module_basename']) && !empty($row['module_class'])) +			{ +				// all the class names start with class name or with phpbb_ for auto loading +				if (strpos($row['module_basename'], $row['module_class'] . '_') !== 0 && +					strpos($row['module_basename'], 'phpbb_') !== 0) +				{ +					$row['module_basename'] = $row['module_class'] . '_' . $row['module_basename']; + +					$sql_update = $this->db->sql_build_array('UPDATE', $row); + +					$sql = 'UPDATE ' . MODULES_TABLE . ' +						SET ' . $sql_update . ' +						WHERE module_id = ' . $module_id; +					$this->sql_query($sql); +				} +			} +		} + +		$this->db->sql_freeresult($result); +	} + +	public function add_group_teampage() +	{ +		$sql = 'UPDATE ' . GROUPS_TABLE . ' +			SET group_teampage = 1 +			WHERE group_type = ' . GROUP_SPECIAL . " +				AND group_name = 'ADMINISTRATORS'"; +		$this->sql_query($sql); + +		$sql = 'UPDATE ' . GROUPS_TABLE . ' +			SET group_teampage = 2 +			WHERE group_type = ' . GROUP_SPECIAL . " +				AND group_name = 'GLOBAL_MODERATORS'"; +		$this->sql_query($sql); +	} + +	public function update_group_legend() +	{ +		$sql = 'SELECT group_id +			FROM ' . GROUPS_TABLE . ' +			WHERE group_legend = 1 +			ORDER BY group_name ASC'; +		$result = $this->db->sql_query($sql); + +		$next_legend = 1; +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$sql = 'UPDATE ' . GROUPS_TABLE . ' +				SET group_legend = ' . $next_legend . ' +				WHERE group_id = ' . (int) $row['group_id']; +			$this->sql_query($sql); + +			$next_legend++; +		} +		$this->db->sql_freeresult($result); +	} + +	public function localise_global_announcements() +	{ +		// Localise Global Announcements +		$sql = 'SELECT topic_id, topic_approved, (topic_replies + 1) AS topic_posts, topic_last_post_id, topic_last_post_subject, topic_last_post_time, topic_last_poster_id, topic_last_poster_name, topic_last_poster_colour +			FROM ' . TOPICS_TABLE . ' +			WHERE forum_id = 0 +				AND topic_type = ' . POST_GLOBAL; +		$result = $this->db->sql_query($sql); + +		$global_announcements = $update_lastpost_data = array(); +		$update_lastpost_data['forum_last_post_time'] = 0; +		$update_forum_data = array( +			'forum_posts'		=> 0, +			'forum_topics'		=> 0, +			'forum_topics_real'	=> 0, +		); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$global_announcements[] = (int) $row['topic_id']; + +			$update_forum_data['forum_posts'] += (int) $row['topic_posts']; +			$update_forum_data['forum_topics_real']++; +			if ($row['topic_approved']) +			{ +				$update_forum_data['forum_topics']++; +			} + +			if ($update_lastpost_data['forum_last_post_time'] < $row['topic_last_post_time']) +			{ +				$update_lastpost_data = array( +					'forum_last_post_id'		=> (int) $row['topic_last_post_id'], +					'forum_last_post_subject'	=> $row['topic_last_post_subject'], +					'forum_last_post_time'		=> (int) $row['topic_last_post_time'], +					'forum_last_poster_id'		=> (int) $row['topic_last_poster_id'], +					'forum_last_poster_name'	=> $row['topic_last_poster_name'], +					'forum_last_poster_colour'	=> $row['topic_last_poster_colour'], +				); +			} +		} +		$this->db->sql_freeresult($result); + +		if (!empty($global_announcements)) +		{ +			// Update the post/topic-count for the forum and the last-post if needed +			$sql = 'SELECT forum_id +				FROM ' . FORUMS_TABLE . ' +				WHERE forum_type = ' . FORUM_POST; +			$result = $this->db->sql_query_limit($sql, 1); +			$ga_forum_id = $this->db->sql_fetchfield('forum_id'); +			$this->db->sql_freeresult($result); + +			$sql = 'SELECT forum_last_post_time +				FROM ' . FORUMS_TABLE . ' +				WHERE forum_id = ' . $ga_forum_id; +			$result = $this->db->sql_query($sql); +			$lastpost = (int) $this->db->sql_fetchfield('forum_last_post_time'); +			$this->db->sql_freeresult($result); + +			$sql_update = 'forum_posts = forum_posts + ' . $update_forum_data['forum_posts'] . ', '; +			$sql_update .= 'forum_topics_real = forum_topics_real + ' . $update_forum_data['forum_topics_real'] . ', '; +			$sql_update .= 'forum_topics = forum_topics + ' . $update_forum_data['forum_topics']; +			if ($lastpost < $update_lastpost_data['forum_last_post_time']) +			{ +				$sql_update .= ', ' . $this->db->sql_build_array('UPDATE', $update_lastpost_data); +			} + +			$sql = 'UPDATE ' . FORUMS_TABLE . ' +				SET ' . $sql_update . ' +				WHERE forum_id = ' . $ga_forum_id; +			$this->sql_query($sql); + +			// Update some forum_ids +			$table_ary = array(TOPICS_TABLE, POSTS_TABLE, LOG_TABLE, DRAFTS_TABLE, TOPICS_TRACK_TABLE); +			foreach ($table_ary as $table) +			{ +				$sql = "UPDATE $table +					SET forum_id = $ga_forum_id +					WHERE " . $this->db->sql_in_set('topic_id', $global_announcements); +				$this->sql_query($sql); +			} +			unset($table_ary); +		} +	} +} diff --git a/phpBB/includes/db/migration/data/310/extensions.php b/phpBB/includes/db/migration/data/310/extensions.php new file mode 100644 index 0000000000..6a9caa1cfc --- /dev/null +++ b/phpBB/includes/db/migration/data/310/extensions.php @@ -0,0 +1,69 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_310_extensions extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return $this->db_tools->sql_table_exists($this->table_prefix . 'ext'); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_11'); +	} + +	public function update_schema() +	{ +		return array( +			'add_tables'		=> array( +				$this->table_prefix . 'ext'	=> array( +					'COLUMNS'			=> array( +						'ext_name'		=> array('VCHAR', ''), +						'ext_active'	=> array('BOOL', 0), +						'ext_state'		=> array('TEXT', ''), +					), +					'KEYS'				=> array( +						'ext_name'		=> array('UNIQUE', 'ext_name'), +					), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_tables'		=> array( +				$this->table_prefix . 'ext', +			), +		); +	} + +	public function update_data() +	{ +		return array( +			// Module will be renamed later +			array('module.add', array( +				'acp', +				'ACP_CAT_STYLES', +				'ACP_EXTENSION_MANAGEMENT' +			)), +			array('module.add', array( +				'acp', +				'ACP_EXTENSION_MANAGEMENT', +				array( +					'module_basename'	=> 'acp_extensions', +					'modes'				=> array('main'), +				), +			)), +			array('permission.add', array('a_extensions', true, 'a_styles')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/310/notifications.php b/phpBB/includes/db/migration/data/310/notifications.php new file mode 100644 index 0000000000..82bfd4cb2d --- /dev/null +++ b/phpBB/includes/db/migration/data/310/notifications.php @@ -0,0 +1,160 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_310_notifications extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return $this->db_tools->sql_table_exists($this->table_prefix . 'notifications'); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_310_dev'); +	} + +	public function update_schema() +	{ +		return array( +			'add_tables'		=> array( +				$this->table_prefix . 'notification_types'	=> array( +					'COLUMNS'			=> array( +						'notification_type'			=> array('VCHAR:255', ''), +						'notification_type_enabled'	=> array('BOOL', 1), +					), +					'PRIMARY_KEY'		=> array('notification_type', 'notification_type_enabled'), +				), +				$this->table_prefix . 'notifications'		=> array( +					'COLUMNS'			=> array( +						'notification_id'  				=> array('UINT', NULL, 'auto_increment'), +						'item_type'			   			=> array('VCHAR:255', ''), +						'item_id'		  				=> array('UINT', 0), +						'item_parent_id'   				=> array('UINT', 0), +						'user_id'						=> array('UINT', 0), +						'notification_read'				=> array('BOOL', 0), +						'notification_time'				=> array('TIMESTAMP', 1), +						'notification_data'			   	=> array('TEXT_UNI', ''), +					), +					'PRIMARY_KEY'		=> 'notification_id', +					'KEYS'				=> array( +						'item_ident'		=> array('INDEX', array('item_type', 'item_id')), +						'user'				=> array('INDEX', array('user_id', 'notification_read')), +					), +				), +				$this->table_prefix . 'user_notifications'	=> array( +					'COLUMNS'			=> array( +						'item_type'			=> array('VCHAR:255', ''), +						'item_id'			=> array('UINT', 0), +						'user_id'			=> array('UINT', 0), +						'method'			=> array('VCHAR:255', ''), +						'notify'			=> array('BOOL', 1), +					), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_tables'	=> array( +				$this->table_prefix . 'notification_types', +				$this->table_prefix . 'notifications', +				$this->table_prefix . 'user_notifications', +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('module.add', array( +				'ucp', +				'UCP_MAIN', +				array( +					'module_basename'	=> 'ucp_notifications', +					'modes'				=> array('notification_list'), +				), +			)), +			array('module.add', array( +				'ucp', +				'UCP_PREFS', +				array( +					'module_basename'	=> 'ucp_notifications', +					'modes'				=> array('notification_options'), +				), +			)), +			array('config.add', array('load_notifications', 1)), +			array('custom', array(array($this, 'convert_notifications'))), +		); +	} + +	public function convert_notifications() +	{ +		$convert_notifications = array( +			array( +				'check'			=> ($this->config['allow_topic_notify']), +				'item_type'		=> 'post', +			), +			array( +				'check'			=> ($this->config['allow_forum_notify']), +				'item_type'		=> 'topic', +			), +			array( +				'check'			=> ($this->config['allow_bookmarks']), +				'item_type'		=> 'bookmark', +			), +			array( +				'check'			=> ($this->config['allow_privmsg']), +				'item_type'		=> 'pm', +			), +		); + +		foreach ($convert_notifications as $convert_data) +		{ +			if ($convert_data['check']) +			{ +				$sql = 'SELECT user_id, user_notify_type +					FROM ' . USERS_TABLE . ' +						WHERE user_notify = 1'; +				$result = $this->db->sql_query($sql); +				while ($row = $this->db->sql_fetchrow($result)) +				{ +					$this->sql_query('INSERT INTO ' . $this->table_prefix . 'user_notifications ' . $this->db->sql_build_array('INSERT', array( +						'item_type'		=> $convert_data['item_type'], +						'item_id'		=> 0, +						'user_id'		=> $row['user_id'], +						'method'		=> '', +					))); + +					if ($row['user_notify_type'] == NOTIFY_EMAIL || $row['user_notify_type'] == NOTIFY_BOTH) +					{ +						$this->sql_query('INSERT INTO ' . $this->table_prefix . 'user_notifications ' . $this->db->sql_build_array('INSERT', array( +							'item_type'		=> $convert_data['item_type'], +							'item_id'		=> 0, +							'user_id'		=> $row['user_id'], +							'method'		=> 'email', +						))); +					} + +					if ($row['user_notify_type'] == NOTIFY_IM || $row['user_notify_type'] == NOTIFY_BOTH) +					{ +						$this->sql_query('INSERT INTO ' . $this->table_prefix . 'user_notifications ' . $this->db->sql_build_array('INSERT', array( +							'item_type'		=> $convert_data['item_type'], +							'item_id'		=> 0, +							'user_id'		=> $row['user_id'], +							'method'		=> 'jabber', +						))); +					} +				} +				$this->db->sql_freeresult($result); +			} +		} +	} +} diff --git a/phpBB/includes/db/migration/data/310/reported_posts_display.php b/phpBB/includes/db/migration/data/310/reported_posts_display.php new file mode 100644 index 0000000000..80a0a0e43f --- /dev/null +++ b/phpBB/includes/db/migration/data/310/reported_posts_display.php @@ -0,0 +1,47 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_310_reported_posts_display extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return $this->db_tools->sql_column_exists($this->table_prefix . 'reports', 'reported_post_enable_bbcode'); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_11'); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns'		=> array( +				$this->table_prefix . 'reports'		=> array( +					'reported_post_enable_bbcode'		=> array('BOOL', 1), +					'reported_post_enable_smilies'		=> array('BOOL', 1), +					'reported_post_enable_magic_url'	=> array('BOOL', 1), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns'		=> array( +				$this->table_prefix . 'reports'		=> array( +					'reported_post_enable_bbcode', +					'reported_post_enable_smilies', +					'reported_post_enable_magic_url', +				), +			), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/310/style_update_p1.php b/phpBB/includes/db/migration/data/310/style_update_p1.php new file mode 100644 index 0000000000..e324ce7f24 --- /dev/null +++ b/phpBB/includes/db/migration/data/310/style_update_p1.php @@ -0,0 +1,157 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_310_style_update_p1 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_table_exists($this->table_prefix . 'styles_imageset'); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_11'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'styles_update'))), +		); +	} + +	public function styles_update() +	{ +		// Get list of valid 3.1 styles +		$available_styles = array('prosilver'); + +		$iterator = new DirectoryIterator($this->phpbb_root_path . 'styles'); +		$skip_dirs = array('.', '..', 'prosilver'); +		foreach ($iterator as $fileinfo) +		{ +			if ($fileinfo->isDir() && !in_array($fileinfo->getFilename(), $skip_dirs) && file_exists($fileinfo->getPathname() . '/style.cfg')) +			{ +				$style_cfg = parse_cfg_file($fileinfo->getPathname() . '/style.cfg'); +				if (isset($style_cfg['phpbb_version']) && version_compare($style_cfg['phpbb_version'], '3.1.0-dev', '>=')) +				{ +					// 3.1 style +					$available_styles[] = $fileinfo->getFilename(); +				} +			} +		} + +		// Get all installed styles +		if ($this->db_tools->sql_table_exists($this->table_prefix . 'styles_imageset')) +		{ +			$sql = 'SELECT s.style_id, t.template_path, t.template_id, t.bbcode_bitfield, t.template_inherits_id, t.template_inherit_path, c.theme_path, c.theme_id, i.imageset_path +				FROM ' . STYLES_TABLE . ' s, ' . $this->table_prefix . 'styles_template t, ' . $this->table_prefix . 'styles_theme c, ' . $this->table_prefix . "styles_imageset i +				WHERE t.template_id = s.template_id +					AND c.theme_id = s.theme_id +					AND i.imageset_id = s.imageset_id"; +		} +		else +		{ +			$sql = 'SELECT s.style_id, t.template_path, t.template_id, t.bbcode_bitfield, t.template_inherits_id, t.template_inherit_path, c.theme_path, c.theme_id +				FROM ' . STYLES_TABLE . ' s, ' . $this->table_prefix . 'styles_template t, ' . $this->table_prefix . "stles_theme c +				WHERE t.template_id = s.template_id +					AND c.theme_id = s.theme_id"; +		} +		$result = $this->db->sql_query($sql); + +		$styles = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$styles[] = $row; +		} +		$this->db->sql_freeresult($result); + +		// Decide which styles to keep, all others will be deleted +		$valid_styles = array(); +		foreach ($styles as $style_row) +		{ +			if ( +				// Delete styles with parent style (not supported yet) +				$style_row['template_inherits_id'] == 0 && +				// Check if components match +				$style_row['template_path'] == $style_row['theme_path'] && (!isset($style_row['imageset_path']) || $style_row['template_path'] == $style_row['imageset_path']) && +				// Check if components are valid +				in_array($style_row['template_path'], $available_styles) +				) +			{ +				// Valid style. Keep it +				$sql_ary = array( +					'style_path'	=> $style_row['template_path'], +					'bbcode_bitfield'	=> $style_row['bbcode_bitfield'], +					'style_parent_id'	=> 0, +					'style_parent_tree'	=> '', +				); +				$this->sql_query('UPDATE ' . STYLES_TABLE . ' +					SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' +					WHERE style_id = ' . $style_row['style_id']); +				$valid_styles[] = (int) $style_row['style_id']; +			} +		} + +		// Remove old entries from styles table +		if (!sizeof($valid_styles)) +		{ +			// No valid styles: remove everything and add prosilver +			$this->sql_query('DELETE FROM ' . STYLES_TABLE, $errored, $error_ary); + +			$sql_ary = array( +				'style_name'		=> 'prosilver', +				'style_copyright'	=> '© phpBB Group', +				'style_active'		=> 1, +				'style_path'		=> 'prosilver', +				'bbcode_bitfield'	=> 'lNg=', +				'style_parent_id'	=> 0, +				'style_parent_tree'	=> '', + +				// Will be removed in the next step +				'imageset_id'		=> 0, +				'template_id'		=> 0, +				'theme_id'			=> 0, +			); + +			$sql = 'INSERT INTO ' . STYLES_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary); +			$this->sql_query($sql); + +			$sql = 'SELECT style_id +				FROM ' . $table . " +				WHERE style_name = 'prosilver'"; +			$result = $this->sql_query($sql); +			$default_style = $this->db->sql_fetchfield($result); +			$this->db->sql_freeresult($result); + +			set_config('default_style', $default_style); + +			$sql = 'UPDATE ' . USERS_TABLE . ' SET user_style = 0'; +			$this->sql_query($sql); +		} +		else +		{ +			// There are valid styles in styles table. Remove styles that are outdated +			$this->sql_query('DELETE FROM ' . STYLES_TABLE . ' +				WHERE ' . $this->db->sql_in_set('style_id', $valid_styles, true)); + +			// Change default style +			if (!in_array($this->config['default_style'], $valid_styles)) +			{ +				$this->sql_query('UPDATE ' . CONFIG_TABLE . " +					SET config_value = '" . $valid_styles[0] . "' +					WHERE config_name = 'default_style'"); +			} + +			// Reset styles for users +			$this->sql_query('UPDATE ' . USERS_TABLE . ' +				SET user_style = 0 +				WHERE ' . $this->db->sql_in_set('user_style', $valid_styles, true)); +		} +	} +} diff --git a/phpBB/includes/db/migration/data/310/style_update_p2.php b/phpBB/includes/db/migration/data/310/style_update_p2.php new file mode 100644 index 0000000000..7b10518a66 --- /dev/null +++ b/phpBB/includes/db/migration/data/310/style_update_p2.php @@ -0,0 +1,129 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_310_style_update_p2 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_table_exists($this->table_prefix . 'styles_imageset'); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_310_style_update_p1'); +	} + +	public function update_schema() +	{ +		return array( +			'drop_columns'	=> array( +				$this->table_prefix . 'styles'		=> array( +					'imageset_id', +					'template_id', +					'theme_id', +				), +			), + +			'drop_tables'	=> array( +				$this->table_prefix . 'styles_imageset', +				$this->table_prefix . 'styles_imageset_data', +				$this->table_prefix . 'styles_template', +				$this->table_prefix . 'styles_template_data', +				$this->table_prefix . 'styles_theme', +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_columns'	=> array( +				$this->table_prefix . 'styles'		=> array( +					'imageset_id'	=> array('UINT', 0), +					'template_id'	=> array('UINT', 0), +					'theme_id'		=> array('UINT', 0), +				), +			), + +			'add_tables'	=> array( +				$this->table_prefix . 'styles_imageset'		=> array( +					'COLUMNS'		=> array( +						'imageset_id'				=> array('UINT', NULL, 'auto_increment'), +						'imageset_name'				=> array('VCHAR_UNI:255', ''), +						'imageset_copyright'		=> array('VCHAR_UNI', ''), +						'imageset_path'				=> array('VCHAR:100', ''), +					), +					'PRIMARY_KEY'		=> 'imageset_id', +					'KEYS'				=> array( +						'imgset_nm'			=> array('UNIQUE', 'imageset_name'), +					), +				), +				$this->table_prefix . 'styles_imageset_data'	=> array( +					'COLUMNS'		=> array( +						'image_id'				=> array('UINT', NULL, 'auto_increment'), +						'image_name'			=> array('VCHAR:200', ''), +						'image_filename'		=> array('VCHAR:200', ''), +						'image_lang'			=> array('VCHAR:30', ''), +						'image_height'			=> array('USINT', 0), +						'image_width'			=> array('USINT', 0), +						'imageset_id'			=> array('UINT', 0), +					), +					'PRIMARY_KEY'		=> 'image_id', +					'KEYS'				=> array( +						'i_d'			=> array('INDEX', 'imageset_id'), +					), +				), +				$this->table_prefix . 'styles_template'		=> array( +					'COLUMNS'		=> array( +						'template_id'			=> array('UINT', NULL, 'auto_increment'), +						'template_name'			=> array('VCHAR_UNI:255', ''), +						'template_copyright'	=> array('VCHAR_UNI', ''), +						'template_path'			=> array('VCHAR:100', ''), +						'bbcode_bitfield'		=> array('VCHAR:255', 'kNg='), +						'template_storedb'		=> array('BOOL', 0), +						'template_inherits_id'		=> array('UINT:4', 0), +						'template_inherit_path'		=> array('VCHAR', ''), +					), +					'PRIMARY_KEY'	=> 'template_id', +					'KEYS'			=> array( +						'tmplte_nm'				=> array('UNIQUE', 'template_name'), +					), +				), +				$this->table_prefix . 'styles_template_data'	=> array( +					'COLUMNS'		=> array( +						'template_id'			=> array('UINT', 0), +						'template_filename'		=> array('VCHAR:100', ''), +						'template_included'		=> array('TEXT', ''), +						'template_mtime'		=> array('TIMESTAMP', 0), +						'template_data'			=> array('MTEXT_UNI', ''), +					), +					'KEYS'			=> array( +						'tid'					=> array('INDEX', 'template_id'), +						'tfn'					=> array('INDEX', 'template_filename'), +					), +				), +				$this->table_prefix . 'styles_theme'			=> array( +					'COLUMNS'		=> array( +						'theme_id'				=> array('UINT', NULL, 'auto_increment'), +						'theme_name'			=> array('VCHAR_UNI:255', ''), +						'theme_copyright'		=> array('VCHAR_UNI', ''), +						'theme_path'			=> array('VCHAR:100', ''), +						'theme_storedb'			=> array('BOOL', 0), +						'theme_mtime'			=> array('TIMESTAMP', 0), +						'theme_data'			=> array('MTEXT_UNI', ''), +					), +					'PRIMARY_KEY'	=> 'theme_id', +					'KEYS'			=> array( +						'theme_name'		=> array('UNIQUE', 'theme_name'), +					), +				), +			), +		); +	} +} diff --git a/phpBB/includes/update_helpers.php b/phpBB/includes/db/migration/data/310/timezone.php index 69d678b2f8..6e50cbe45f 100644 --- a/phpBB/includes/update_helpers.php +++ b/phpBB/includes/db/migration/data/310/timezone.php @@ -1,16 +1,67 @@  <?php  /**  * -* @package phpBB3 +* @package migration  * @copyright (c) 2012 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +*  */ -/** -* phpBB Update Helpers -*/ -class phpbb_update_helpers +class phpbb_db_migration_data_310_timezone extends phpbb_db_migration  { +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_dst'); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_11'); +	} + +	public function update_schema() +	{ +		return array( +			'change_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_timezone'		=> array('VCHAR:100', ''), +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'update_timezones'))), +		); +	} + +	public function update_timezones() +	{ +		// Update user timezones +		$sql = 'SELECT user_dst, user_timezone +			FROM ' . $this->table_prefix . 'users +			GROUP BY user_timezone, user_dst'; +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$sql = 'UPDATE ' . $this->table_prefix . "users +				SET user_timezone = '" . $this->db->sql_escape($this->convert_phpbb30_timezone($row['user_timezone'], $row['user_dst'])) . "' +				WHERE user_timezone = '" . $this->db->sql_escape($row['user_timezone']) . "' +					AND user_dst = " . (int) $row['user_dst']; +			$this->sql_query($sql); +		} +		$this->db->sql_freeresult($result); + +		// Update board default timezone +		$sql = 'UPDATE ' . $this->table_prefix . "config +			SET config_value = '" . $this->convert_phpbb30_timezone($this->config['board_timezone'], $this->config['board_dst']) . "' +			WHERE config_name = 'board_timezone'"; +		$this->sql_query($sql); +	} +  	/**  	* Determine the new timezone for a given phpBB 3.0 timezone and  	* "Daylight Saving Time" option @@ -19,7 +70,7 @@ class phpbb_update_helpers  	*	@param	$dst		int		Users daylight saving time  	*	@return		string		Users new php Timezone which is used since 3.1  	*/ -	function convert_phpbb30_timezone($timezone, $dst) +	public function convert_phpbb30_timezone($timezone, $dst)  	{  		$offset = $timezone + $dst; diff --git a/phpBB/includes/db/migration/data/310/timezone_p2.php b/phpBB/includes/db/migration/data/310/timezone_p2.php new file mode 100644 index 0000000000..113b979e4f --- /dev/null +++ b/phpBB/includes/db/migration/data/310/timezone_p2.php @@ -0,0 +1,43 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_310_timezone_p2 extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_dst'); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_310_timezone'); +	} + +	public function update_schema() +	{ +		return array( +			'drop_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_dst', +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_dst'		=> array('BOOL', 0), +				), +			), +		); +	} +} diff --git a/phpBB/includes/db/migration/exception.php b/phpBB/includes/db/migration/exception.php index ffdcd97780..e84330dd71 100644 --- a/phpBB/includes/db/migration/exception.php +++ b/phpBB/includes/db/migration/exception.php @@ -52,4 +52,28 @@ class phpbb_db_migration_exception extends \Exception  	{  		return $this->message . ': ' . var_export($this->parameters, true);  	} + +	/** +	* Get the parameters +	* +	* @return array +	*/ +	public function getParameters() +	{ +		return $this->parameters; +	} + +	/** +	* Get localised message (with $user->lang())  +	*  +	* @param phpbb_user $user +	* @return string +	*/ +	public function getLocalisedMessage(phpbb_user $user) +	{ +		$parameters = $this->getParameters(); +		array_unshift($parameters, $this->getMessage()); + +		return call_user_func_array(array($user, 'lang'), $parameters); +	}  } diff --git a/phpBB/includes/db/migration/tool/config.php b/phpBB/includes/db/migration/tool/config.php index d9cc20053e..458a25fb66 100644 --- a/phpBB/includes/db/migration/tool/config.php +++ b/phpBB/includes/db/migration/tool/config.php @@ -49,7 +49,7 @@ class phpbb_db_migration_tool_config implements phpbb_db_migration_tool_interfac  	{  		if (isset($this->config[$config_name]))  		{ -			throw new phpbb_db_migration_exception('CONFIG_ALREADY_EXISTS', $config_name); +			throw new phpbb_db_migration_exception('CONFIG_ALREADY_EXIST', $config_name);  		}  		$this->config->set($config_name, $config_value, !$is_dynamic); diff --git a/phpBB/includes/db/migration/tool/module.php b/phpBB/includes/db/migration/tool/module.php index afe1f21ec5..ad94c5aadb 100644 --- a/phpBB/includes/db/migration/tool/module.php +++ b/phpBB/includes/db/migration/tool/module.php @@ -183,25 +183,7 @@ class phpbb_db_migration_tool_module implements phpbb_db_migration_tool_interfac  			$basename = str_replace(array('/', '\\'), '', $basename);  			$class = str_replace(array('/', '\\'), '', $class); -			$include_path = ($include_path === false) ? $this->phpbb_root_path . 'includes/' : $include_path; -			$info_file = "$class/info/$basename.{$this->php_ext}"; - -			// The manual and automatic ways both failed... -			if (!file_exists($include_path . $info_file)) -			{ -				throw new phpbb_db_migration_exception('MODULE_INFO_FILE_NOT_EXIST', $class, $info_file); -			} - -			$classname = "{$basename}_info"; - -			if (!class_exists($classname)) -			{ -				include($include_path . $info_file); -			} - -			$info = new $classname; -			$module = $info->module(); -			unset($info); +			$module = $this->get_module_info($class, $basename);  			$result = '';  			foreach ($module['modes'] as $mode => $module_info) @@ -242,14 +224,14 @@ class phpbb_db_migration_tool_module implements phpbb_db_migration_tool_interfac  			if (!$module_id)  			{ -				throw new phpbb_db_migration_exception('MODULE_PARENT_NOT_EXIST', $parent); +				throw new phpbb_db_migration_exception('MODULE_NOT_EXIST', $parent);  			}  			$parent = $data['parent_id'] = $module_id;  		}  		else if (!$this->exists($class, false, $parent))  		{ -			throw new phpbb_db_migration_exception('MODULE_PARENT_NOT_EXIST', $parent); +			throw new phpbb_db_migration_exception('MODULE_NOT_EXIST', $parent);  		}  		if ($this->exists($class, $parent, $data['module_langname'])) @@ -373,30 +355,13 @@ class phpbb_db_migration_tool_module implements phpbb_db_migration_tool_interfac  			$basename = str_replace(array('/', '\\'), '', $module['module_basename']);  			$class = str_replace(array('/', '\\'), '', $class); -			$include_path = ($include_path === false) ? $this->phpbb_root_path . 'includes/' : $include_path; -			$info_file = "$class/info/$basename.{$this->php_ext}"; - -			if (!file_exists($include_path . $info_file)) -			{ -				throw new phpbb_db_migration_exception('MODULE_NOT_EXIST', $info_file); -			} - -			$classname = "{$basename}_info"; - -			if (!class_exists($classname)) -			{ -				include($include_path . $info_file); -			} - -			$info = new $classname; -			$module_info = $info->module(); -			unset($info); +			$module_info = $this->get_module_info($class, $basename);  			foreach ($module_info['modes'] as $mode => $info)  			{  				if (!isset($module['modes']) || in_array($mode, $module['modes']))  				{ -					$this->remove($class, $parent, $info['title']) . '<br />'; +					$this->remove($class, $parent, $info['title']);  				}  			}  		} @@ -477,7 +442,7 @@ class phpbb_db_migration_tool_module implements phpbb_db_migration_tool_interfac  				$result = $acp_modules->delete_module($module_id);  				if (!empty($result))  				{ -					throw new phpbb_db_migration_exception('CANNOT_REMOVE_MODULE', $module_id); +					throw new phpbb_db_migration_exception('MODULE_NOT_REMOVABLE', $module_id, $result);  				}  			} @@ -510,4 +475,28 @@ class phpbb_db_migration_tool_module implements phpbb_db_migration_tool_interfac  			return call_user_func_array(array(&$this, $call), $arguments);  		}  	} + +	/** +	* Wrapper for acp_modules::get_module_infos() +	* +	* @param string $class Module Class +	* @param string $basename Module Basename +	* @return array Module Information +	*/ +	protected function get_module_info($class, $basename) +	{ +		if (!class_exists('acp_modules')) +		{ +			include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext); +		} +		$acp_modules = new acp_modules(); +		$module = $acp_modules->get_module_infos($basename, $class, true); + +		if (empty($module)) +		{ +			throw new phpbb_db_migration_exception('MODULE_INFO_FILE_NOT_EXIST', $class, $basename); +		} + +		return array_pop($module); +	}  } diff --git a/phpBB/includes/db/migration/tool/permission.php b/phpBB/includes/db/migration/tool/permission.php index 001d090f5a..4231fbe1dd 100644 --- a/phpBB/includes/db/migration/tool/permission.php +++ b/phpBB/includes/db/migration/tool/permission.php @@ -107,7 +107,7 @@ class phpbb_db_migration_tool_permission implements phpbb_db_migration_tool_inte  	{  		if ($this->exists($auth_option, $global))  		{ -			throw new phpbb_db_migration_exception('PERMISSION_ALREADY_EXISTS', $auth_option); +			throw new phpbb_db_migration_exception('PERMISSION_ALREADY_EXIST', $auth_option);  		}  		// We've added permissions, so set to true to notify the user. @@ -252,7 +252,7 @@ class phpbb_db_migration_tool_permission implements phpbb_db_migration_tool_inte  		if ($role_id)  		{ -			throw new phpbb_db_migration_exception('ROLE_ALREADY_EXISTS', $old_role_name); +			return;  		}  		$sql = 'SELECT MAX(role_order) AS max_role_order @@ -290,7 +290,7 @@ class phpbb_db_migration_tool_permission implements phpbb_db_migration_tool_inte  		if (!$role_id)  		{ -			throw new phpbb_db_migration_exception('ROLE_NOT_EXISTS', $old_role_name); +			throw new phpbb_db_migration_exception('ROLE_NOT_EXIST', $old_role_name);  		}  		$sql = 'UPDATE ' . ACL_ROLES_TABLE . " diff --git a/phpBB/includes/db/migrator.php b/phpBB/includes/db/migrator.php index 4456600b0a..de9c06948c 100644 --- a/phpBB/includes/db/migrator.php +++ b/phpBB/includes/db/migrator.php @@ -31,6 +31,9 @@ class phpbb_db_migrator  	/** @var phpbb_db_tools */  	protected $db_tools; +	/** @var phpbb_extension_manager */ +	protected $extension_manager; +  	/** @var string */  	protected $table_prefix; @@ -91,6 +94,16 @@ class phpbb_db_migrator  	}  	/** +	* Set Extension Manager (required) +	* +	* Not in constructor to prevent circular reference error +	*/ +	public function set_extension_manager(phpbb_extension_manager $extension_manager) +	{ +		$this->extension_manager = $extension_manager; +	} + +	/**  	* Loads all migrations and their application state from the database.  	*  	* @return null @@ -99,18 +112,26 @@ class phpbb_db_migrator  	{  		$this->migration_state = array(); +		// prevent errors in case the table does not exist yet +		$this->db->sql_return_on_error(true); +  		$sql = "SELECT *  			FROM " . $this->migrations_table;  		$result = $this->db->sql_query($sql); -		while ($migration = $this->db->sql_fetchrow($result)) +		if (!$this->db->sql_error_triggered)  		{ -			$this->migration_state[$migration['migration_name']] = $migration; +			while ($migration = $this->db->sql_fetchrow($result)) +			{ +				$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_depends_on'] = unserialize($migration['migration_depends_on']); +			}  		}  		$this->db->sql_freeresult($result); + +		$this->db->sql_return_on_error(false);  	}  	/** @@ -172,55 +193,32 @@ class phpbb_db_migrator  	* 	If FALSE, we will not check. You SHOULD check at least once  	* 	to prevent errors (if including multiple directories, check  	* 	with the last call to prevent throwing errors unnecessarily). -	* @param bool $recursive Set to true to also load data files from subdirectories  	* @return array Array of migration names  	*/ -	public function load_migrations($path, $check_fulfillable = true, $recursive = true) +	public function load_migrations($path, $check_fulfillable = true)  	{  		if (!is_dir($path))  		{  			throw new phpbb_db_migration_exception('DIRECTORY INVALID', $path);  		} -		$handle = opendir($path); -		while (($file = readdir($handle)) !== false) -		{ -			if ($file == '.' || $file == '..') -			{ -				continue; -			} +		$migrations = array(); -			// Recursion through subdirectories -			if (is_dir($path . $file) && $recursive) -			{ -				$this->load_migrations($path . $file . '/', $check_fulfillable, $recursive); -			} +		$finder = $this->extension_manager->get_finder(); +		$files = $finder +			->extension_directory("/") +			->find_from_paths(array('/' => $path)); +		foreach ($files as $file) +		{ +			$migrations[$file['path'] . $file['filename']] = ''; +		} +		$migrations = $finder->get_classes_from_files($migrations); -			if (strpos($file, '_') !== 0 && strrpos($file, '.' . $this->php_ext) === (strlen($file) - strlen($this->php_ext) - 1)) +		foreach ($migrations as $migration) +		{ +			if (!in_array($migration, $this->migrations))  			{ -				// We try to find what class existed by comparing the classes declared before and after including the file. -				$declared_classes = get_declared_classes(); - -				include ($path . $file); - -				$added_classes = array_diff(get_declared_classes(), $declared_classes); - -				if ( -					// If two classes have been added and phpbb_db_migration is one of them, we've only added one real migration -					!(sizeof($added_classes) == 2 && in_array('phpbb_db_migration', $added_classes)) && -					// Otherwise there should only be one class added -					sizeof($added_classes) != 1 -				) -				{ -					throw new phpbb_db_migration_exception('MIGRATION DATA FILE INVALID', $path . $file); -				} - -				$name = array_pop($added_classes); - -				if (!in_array($name, $this->migrations)) -				{ -					$this->migrations[] = $name; -				} +				$this->migrations[] = $migration;  			}  		} @@ -228,9 +226,10 @@ class phpbb_db_migrator  		{  			foreach ($this->migrations as $name)  			{ -				if ($this->unfulfillable($name)) +				$unfulfillable = $this->unfulfillable($name); +				if ($unfulfillable !== false)  				{ -					throw new phpbb_db_migration_exception('MIGRATION NOT FULFILLABLE', $name); +					throw new phpbb_db_migration_exception('MIGRATION_NOT_FULFILLABLE', $name, $unfulfillable);  				}  			}  		} @@ -307,23 +306,22 @@ class phpbb_db_migrator  			'class'	=> $migration,  		); -		if ($migration->effectively_installed()) +		if (!isset($this->migration_state[$name]))  		{ -			$state = array( -				'migration_depends_on'	=> $migration->depends_on(), -				'migration_schema_done' => true, -				'migration_data_done'	=> true, -				'migration_data_state'	=> '', -				'migration_start_time'	=> 0, -				'migration_end_time'	=> 0, -			); -		} -		else -		{ -			if (!isset($this->migration_state[$name])) +			if ($migration->effectively_installed()) +			{ +				$state = array( +					'migration_depends_on'	=> $migration->depends_on(), +					'migration_schema_done' => true, +					'migration_data_done'	=> true, +					'migration_data_state'	=> '', +					'migration_start_time'	=> 0, +					'migration_end_time'	=> 0, +				); +			} +			else  			{  				$state['migration_start_time'] = time(); -				$this->insert_migration($name, $state);  			}  		} @@ -352,14 +350,7 @@ class phpbb_db_migrator  			}  		} -		$insert = $state; -		$insert['migration_depends_on'] = serialize($state['migration_depends_on']); -		$sql = 'UPDATE ' . $this->migrations_table . ' -			SET ' . $this->db->sql_build_array('UPDATE', $insert) . " -			WHERE migration_name = '" . $this->db->sql_escape($name) . "'"; -		$this->db->sql_query($sql); - -		$this->migration_state[$name] = $state; +		$this->insert_migration($name, $state);  		return true;  	} @@ -425,20 +416,13 @@ class phpbb_db_migrator  			}  			else  			{ -				$result = $this->process_data_step($migration->revert_data(), $state['migration_data_state'], false); +				$result = $this->process_data_step($migration->revert_data(), '', false);  				$state['migration_data_state'] = ($result === true) ? '' : $result;  				$state['migration_data_done'] = ($result === true) ? false : true;  			} -			$insert = $state; -			$insert['migration_depends_on'] = serialize($state['migration_depends_on']); -			$sql = 'UPDATE ' . $this->migrations_table . ' -				SET ' . $this->db->sql_build_array('UPDATE', $insert) . " -				WHERE migration_name = '" . $this->db->sql_escape($name) . "'"; -			$this->db->sql_query($sql); - -			$this->migration_state[$name] = $state; +			$this->insert_migration($name, $state);  		}  		else  		{ @@ -651,7 +635,7 @@ class phpbb_db_migrator  	}  	/** -	* Insert migration row into the database +	* Insert/Update migration row into the database  	*  	* @param string $name Name of the migration  	* @param array $state @@ -660,12 +644,22 @@ class phpbb_db_migrator  	protected function insert_migration($name, $state)  	{  		$migration_row = $state; -		$migration_row['migration_name'] = $name;  		$migration_row['migration_depends_on'] = serialize($state['migration_depends_on']); -		$sql = 'INSERT INTO ' . $this->migrations_table . ' -			' . $this->db->sql_build_array('INSERT', $migration_row); -		$this->db->sql_query($sql); +		if (isset($this->migration_state[$name])) +		{ +			$sql = 'UPDATE ' . $this->migrations_table . ' +				SET ' . $this->db->sql_build_array('UPDATE', $migration_row) . " +				WHERE migration_name = '" . $this->db->sql_escape($name) . "'"; +			$this->db->sql_query($sql); +		} +		else +		{ +			$migration_row['migration_name'] = $name; +			$sql = 'INSERT INTO ' . $this->migrations_table . ' +				' . $this->db->sql_build_array('INSERT', $migration_row); +			$this->db->sql_query($sql); +		}  		$this->migration_state[$name] = $state;  	} @@ -674,7 +668,7 @@ class phpbb_db_migrator  	* Checks if a migration's dependencies can even theoretically be satisfied.  	*  	* @param string	$name The class name of the migration -	* @return bool Whether the migration cannot be fulfilled +	* @return bool|string False if fulfillable, string of missing migration name if unfulfillable  	*/  	public function unfulfillable($name)  	{ @@ -685,7 +679,7 @@ class phpbb_db_migrator  		if (!class_exists($name))  		{ -			return true; +			return $name;  		}  		$migration = $this->get_migration($name); @@ -693,9 +687,10 @@ class phpbb_db_migrator  		foreach ($depends as $depend)  		{ -			if ($this->unfulfillable($depend)) +			$unfulfillable = $this->unfulfillable($depend); +			if ($unfulfillable !== false)  			{ -				return true; +				return $unfulfillable;  			}  		} @@ -715,7 +710,7 @@ class phpbb_db_migrator  			{  				// skip unfulfillable migrations, but fulfillables mean we  				// are not finished yet -				if ($this->unfulfillable($name)) +				if ($this->unfulfillable($name) !== false)  				{  					continue;  				} diff --git a/phpBB/includes/extension/base.php b/phpBB/includes/extension/base.php index 9d076eb6c5..d51589d719 100644 --- a/phpBB/includes/extension/base.php +++ b/phpBB/includes/extension/base.php @@ -15,6 +15,8 @@ if (!defined('IN_PHPBB'))  	exit;  } +use Symfony\Component\DependencyInjection\ContainerInterface; +  /**  * A base class for extensions without custom enable/disable/purge code.  * @@ -22,6 +24,19 @@ if (!defined('IN_PHPBB'))  */  class phpbb_extension_base implements phpbb_extension_interface  { +	/** @var ContainerInterface */ +	protected $container; + +	/** +	* Constructor +	* +	* @param ContainerInterface $container Container object +	*/ +	public function __construct(ContainerInterface $container) +	{ +		$this->container = $container; +	} +  	/**  	* Single enable step that does nothing  	* diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php index fb19b98429..f71e32bc8d 100644 --- a/phpBB/includes/extension/finder.php +++ b/phpBB/includes/extension/finder.php @@ -247,15 +247,28 @@ class phpbb_extension_finder  	* phpBB naming rules an incorrect class name will be returned.  	*  	* @param bool $cache Whether the result should be cached +	* @param bool $use_all_available Use all available instead of just all +	* 						enabled extensions  	* @return array An array of found class names  	*/ -	public function get_classes($cache = true) +	public function get_classes($cache = true, $use_all_available = false)  	{  		$this->query['extension_suffix'] .= $this->php_ext;  		$this->query['core_suffix'] .= $this->php_ext; -		$files = $this->find($cache, false); +		$files = $this->find($cache, false, $use_all_available); +		return $this->get_classes_from_files($files); +	} + +	/** +	* Get class names from a list of files +	* +	* @param array $files Array of files (from find()) +	* @return array Array of class names +	*/ +	public function get_classes_from_files($files) +	{  		$classes = array();  		foreach ($files as $file => $ext_name)  		{ @@ -270,23 +283,27 @@ class phpbb_extension_finder  	* Finds all directories matching the configured options  	*  	* @param bool $cache Whether the result should be cached +	* @param bool $use_all_available Use all available instead of just all +	* 						enabled extensions  	* @param bool $extension_keys Whether the result should have extension name as array key  	* @return array An array of paths to found directories  	*/ -	public function get_directories($cache = true, $extension_keys = false) +	public function get_directories($cache = true, $use_all_available = false, $extension_keys = false)  	{ -		return $this->find_with_root_path($cache, true, $extension_keys); +		return $this->find_with_root_path($cache, true, $use_all_available, $extension_keys);  	}  	/**  	* Finds all files matching the configured options.  	*  	* @param bool $cache Whether the result should be cached +	* @param bool $use_all_available Use all available instead of just all +	* 						enabled extensions  	* @return array An array of paths to found files  	*/ -	public function get_files($cache = true) +	public function get_files($cache = true, $use_all_available = false)  	{ -		return $this->find_with_root_path($cache, false); +		return $this->find_with_root_path($cache, false, $use_all_available);  	}  	/** @@ -295,13 +312,15 @@ class phpbb_extension_finder  	* @param bool $cache Whether the result should be cached  	* @param bool $is_dir Directories will be returned when true, only files  	*                     otherwise +	* @param bool $use_all_available Use all available instead of just all +	* 						enabled extensions  	* @param bool $extension_keys If true, result will be associative array  	*					with extension name as key  	* @return array An array of paths to found items  	*/ -	protected function find_with_root_path($cache = true, $is_dir = false, $extension_keys = false) +	protected function find_with_root_path($cache = true, $is_dir = false, $use_all_available = false, $extension_keys = false)  	{ -		$items = $this->find($cache, $is_dir); +		$items = $this->find($cache, $is_dir, $use_all_available);  		$result = array();  		foreach ($items as $item => $ext_name) @@ -325,27 +344,59 @@ class phpbb_extension_finder  	* @param bool $cache Whether the result should be cached  	* @param bool $is_dir Directories will be returned when true, only files  	*                     otherwise +	* @param bool $use_all_available Use all available instead of just all +	* 						enabled extensions  	* @return array An array of paths to found items  	*/ -	public function find($cache = true, $is_dir = false) +	public function find($cache = true, $is_dir = false, $use_all_available = false)  	{ -		$this->query['is_dir'] = $is_dir; -		$query = md5(serialize($this->query)); +		if ($use_all_available) +		{ +			$extensions = $this->extension_manager->all_available(); +		} +		else +		{ +			$extensions = $this->extension_manager->all_enabled(); +		} -		if (!defined('DEBUG') && $cache && isset($this->cached_queries[$query])) +		if ($this->query['core_path'])  		{ -			return $this->cached_queries[$query]; +			$extensions['/'] = $this->phpbb_root_path . $this->query['core_path'];  		}  		$files = array(); +		$file_list = $this->find_from_paths($extensions, $cache, $is_dir); -		$extensions = $this->extension_manager->all_enabled(); +		foreach ($file_list as $file) +		{ +			$files[$file['named_path']] = $file['ext_name']; +		} -		if ($this->query['core_path']) +		return $files; +	} + +	/** +	* Finds all file system entries matching the configured options from +	* an array of paths +	* +	* @param array $extensions Array of extensions (name => full relative path) +	* @param bool $cache Whether the result should be cached +	* @param bool $is_dir Directories will be returned when true, only files +	*                     otherwise +	* @return array An array of paths to found items +	*/ +	public function find_from_paths($extensions, $cache = true, $is_dir = false) +	{ +		$this->query['is_dir'] = $is_dir; +		$query = md5(serialize($this->query) . serialize($extensions)); + +		if (!defined('DEBUG') && $cache && isset($this->cached_queries[$query]))  		{ -			$extensions['/'] = $this->phpbb_root_path . $this->query['core_path']; +			return $this->cached_queries[$query];  		} +		$files = array(); +  		foreach ($extensions as $name => $path)  		{  			$ext_name = $name; @@ -419,7 +470,12 @@ class phpbb_extension_finder  						(!$prefix || substr($filename, 0, strlen($prefix)) === $prefix) &&  						(!$directory || preg_match($directory_pattern, $relative_path)))  					{ -						$files[str_replace(DIRECTORY_SEPARATOR, '/', $location . $name . substr($relative_path, 1))] = $ext_name; +						$files[] = array( +							'named_path'	=> str_replace(DIRECTORY_SEPARATOR, '/', $location . $name . substr($relative_path, 1)), +							'ext_name'		=> $ext_name, +							'path'			=> str_replace(array(DIRECTORY_SEPARATOR, $this->phpbb_root_path), array('/', ''), $file_info->getPath()) . '/', +							'filename'		=> $filename, +						);  					}  				}  			} diff --git a/phpBB/includes/extension/manager.php b/phpBB/includes/extension/manager.php index de6f364320..0d760681b9 100644 --- a/phpBB/includes/extension/manager.php +++ b/phpBB/includes/extension/manager.php @@ -15,6 +15,8 @@ if (!defined('IN_PHPBB'))  	exit;  } +use Symfony\Component\DependencyInjection\ContainerInterface; +  /**  * The extension manager provides means to activate/deactivate extensions.  * @@ -22,8 +24,12 @@ if (!defined('IN_PHPBB'))  */  class phpbb_extension_manager  { +	/** @var ContainerInterface */ +	protected $container; +  	protected $db;  	protected $config; +	protected $migrator;  	protected $cache;  	protected $php_ext;  	protected $extensions; @@ -34,6 +40,7 @@ class phpbb_extension_manager  	/**  	* Creates a manager and loads information from database  	* +	* @param ContainerInterface $container A container  	* @param phpbb_db_driver $db A database connection  	* @param phpbb_config $config phpbb_config  	* @param string $extension_table The name of the table holding extensions @@ -42,8 +49,9 @@ class phpbb_extension_manager  	* @param phpbb_cache_driver_interface $cache A cache instance or null  	* @param string $cache_name The name of the cache variable, defaults to _ext  	*/ -	public function __construct(phpbb_db_driver $db, phpbb_config $config, $extension_table, $phpbb_root_path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null, $cache_name = '_ext') +	public function __construct(ContainerInterface $container, phpbb_db_driver $db, phpbb_config $config, $extension_table, $phpbb_root_path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null, $cache_name = '_ext')  	{ +		$this->container = $container;  		$this->phpbb_root_path = $phpbb_root_path;  		$this->db = $db;  		$this->config = $config; @@ -61,6 +69,14 @@ class phpbb_extension_manager  	}  	/** +	* Set migrator (get around circular reference) +	*/ +	public function set_migrator(phpbb_db_migrator $migrator) +	{ +		$this->migrator = $migrator; +	} + +	/**  	* Loads all extension information from the database  	*  	* @return null @@ -126,11 +142,11 @@ class phpbb_extension_manager  		if (class_exists($extension_class_name))  		{ -			return new $extension_class_name; +			return new $extension_class_name($this->container);  		}  		else  		{ -			return new phpbb_extension_base; +			return new phpbb_extension_base($this->container);  		}  	} @@ -166,6 +182,12 @@ class phpbb_extension_manager  		$old_state = (isset($this->extensions[$name]['ext_state'])) ? unserialize($this->extensions[$name]['ext_state']) : false; +		// Returns false if not completed +		if (!$this->handle_migrations($name, 'enable')) +		{ +			return true; +		} +  		$extension = $this->get_extension($name);  		$state = $extension->enable_step($old_state); @@ -317,6 +339,12 @@ class phpbb_extension_manager  		$old_state = unserialize($this->extensions[$name]['ext_state']); +		// Returns false if not completed +		if (!$this->handle_migrations($name, 'purge')) +		{ +			return true; +		} +  		$extension = $this->get_extension($name);  		$state = $extension->purge_step($old_state); @@ -490,4 +518,58 @@ class phpbb_extension_manager  	{  		return new phpbb_extension_finder($this, $this->phpbb_root_path, $this->cache, $this->php_ext, $this->cache_name . '_finder');  	} + +	/** +	* Handle installing/reverting migrations +	* +	* @param string $extension_name Name of the extension +	* @param string $mode enable or purge +	* @return bool True if completed, False if not completed +	*/ +	protected function handle_migrations($extension_name, $mode) +	{ +		$migrations_path = $this->phpbb_root_path . $this->get_extension_path($extension_name) . 'migrations/'; +		if (!file_exists($migrations_path) || !is_dir($migrations_path)) +		{ +			return true; +		} + +		$migrations = $this->migrator->load_migrations($migrations_path); + +		// What is a safe limit of execution time? Half the max execution time should be safe. +		$safe_time_limit = (ini_get('max_execution_time') / 2); +		$start_time = time(); + +		if ($mode == 'enable') +		{ +			while (!$this->migrator->finished()) +			{ +				$this->migrator->update(); + +				// Are we approaching the time limit? If so we want to pause the update and continue after refreshing +				if ((time() - $start_time) >= $safe_time_limit) +				{ +					return false; +				} +			} +		} +		else if ($mode == 'purge') +		{ +			foreach ($migrations as $migration) +			{ +				while ($this->migrator->migration_state($migration) !== false) +				{ +					$this->migrator->revert($migration); + +					// Are we approaching the time limit? If so we want to pause the update and continue after refreshing +					if ((time() - $start_time) >= $safe_time_limit) +					{ +						return false; +					} +				} +			} +		} + +		return true; +	}  } diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php index d0ef2759d5..6a1f144967 100644 --- a/phpBB/includes/functions.php +++ b/phpBB/includes/functions.php @@ -97,7 +97,18 @@ function request_var($var_name, $default, $multibyte = false, $cookie = false, $  }  /** -* Set config value. Creates missing config entry. +* Sets a configuration option's value. +* +* Please note that this function does not update the is_dynamic value for +* an already existing config option. +* +* @param string $config_name   The configuration option's name +* @param string $config_value  New configuration value +* @param bool   $is_dynamic    Whether this variable should be cached (false) or +*                              if it changes too frequently (true) to be +*                              efficiently cached. +* +* @return null  *  * @deprecated  */ @@ -119,7 +130,15 @@ function set_config($config_name, $config_value, $is_dynamic = false, phpbb_conf  }  /** -* Set dynamic config value with arithmetic operation. +* Increments an integer config value directly in the database. +* +* @param string $config_name   The configuration option's name +* @param int    $increment     Amount to increment by +* @param bool   $is_dynamic    Whether this variable should be cached (false) or +*                              if it changes too frequently (true) to be +*                              efficiently cached. +* +* @return null  *  * @deprecated  */ @@ -1328,7 +1347,7 @@ function phpbb_timezone_select($user, $default = '', $truncate = false)  function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $user_id = 0)  {  	global $db, $user, $config; -	global $request; +	global $request, $phpbb_container;  	$post_time = ($post_time === 0 || $post_time > time()) ? time() : (int) $post_time; @@ -1336,6 +1355,20 @@ function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $  	{  		if ($forum_id === false || !sizeof($forum_id))  		{ +			// Mark all forums read (index page) + +			$phpbb_notifications = $phpbb_container->get('notification_manager'); + +			// Mark all topic notifications read for this user +			$phpbb_notifications->mark_notifications_read(array( +				'topic', +				'quote', +				'bookmark', +				'post', +				'approve_topic', +				'approve_post', +			), false, $user->data['user_id'], $post_time); +  			if ($config['load_db_lastread'] && $user->data['is_registered'])  			{  				// Mark all forums read (index page) @@ -1390,6 +1423,32 @@ function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $  			$forum_id = array($forum_id);  		} +		$phpbb_notifications = $phpbb_container->get('notification_manager'); + +		$phpbb_notifications->mark_notifications_read_by_parent(array( +			'topic', +			'approve_topic', +		), $forum_id, $user->data['user_id'], $post_time); + +		// Mark all post/quote notifications read for this user in this forum +		$topic_ids = array(); +		$sql = 'SELECT topic_id +			FROM ' . TOPICS_TABLE . ' +			WHERE ' . $db->sql_in_set('forum_id', $forum_id); +		$result = $db->sql_query($sql); +		while ($row = $db->sql_fetchrow($result)) +		{ +			$topic_ids[] = $row['topic_id']; +		} +		$db->sql_freeresult($result); + +		$phpbb_notifications->mark_notifications_read_by_parent(array( +			'quote', +			'bookmark', +			'post', +			'approve_post', +		), $topic_ids, $user->data['user_id'], $post_time); +  		// Add 0 to forums array to mark global announcements correctly  		// $forum_id[] = 0; @@ -1487,6 +1546,21 @@ function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $  			return;  		} +		$phpbb_notifications = $phpbb_container->get('notification_manager'); + +		// Mark post notifications read for this user in this topic +		$phpbb_notifications->mark_notifications_read(array( +			'topic', +			'approve_topic', +		), $topic_id, $user->data['user_id'], $post_time); + +		$phpbb_notifications->mark_notifications_read_by_parent(array( +			'quote', +			'bookmark', +			'post', +			'approve_post', +		), $topic_id, $user->data['user_id'], $post_time); +  		if ($config['load_db_lastread'] && $user->data['is_registered'])  		{  			$sql = 'UPDATE ' . TOPICS_TRACK_TABLE . " @@ -4994,7 +5068,7 @@ function phpbb_build_hidden_fields_for_query_params($request, $exclude = null)  function page_header($page_title = '', $display_online_list = true, $item_id = 0, $item = 'forum')  {  	global $db, $config, $template, $SID, $_SID, $_EXTRA_URL, $user, $auth, $phpEx, $phpbb_root_path; -	global $phpbb_dispatcher, $request; +	global $phpbb_dispatcher, $request, $phpbb_container;  	if (defined('HEADER_INC'))  	{ @@ -5183,8 +5257,26 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0  		$timezone_name = $user->lang['timezones'][$timezone_name];  	} +	// Output the notifications +	$notifications = false; +	if ($config['load_notifications'] && $user->data['user_id'] != ANONYMOUS && $user->data['user_type'] != USER_IGNORE) +	{ +		$phpbb_notifications = $phpbb_container->get('notification_manager'); + +		$notifications = $phpbb_notifications->load_notifications(array( +			'all_unread'	=> true, +			'limit'			=> 5, +		)); + +		foreach ($notifications['notifications'] as $notification) +		{ +			$template->assign_block_vars('notifications', $notification->prepare_for_display()); +		} +	} +  	$hidden_fields_for_jumpbox = phpbb_build_hidden_fields_for_query_params($request, array('f')); +  	// The following assigns all _common_ variables that may be used at any point in a template.  	$template->assign_vars(array(  		'SITENAME'						=> $config['sitename'], @@ -5201,6 +5293,12 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0  		'PRIVATE_MESSAGE_INFO_UNREAD'	=> $l_privmsgs_text_unread,  		'HIDDEN_FIELDS_FOR_JUMPBOX'	=> $hidden_fields_for_jumpbox, +		'UNREAD_NOTIFICATIONS_COUNT'	=> ($notifications !== false) ? $notifications['unread_count'] : '', +		'NOTIFICATIONS_COUNT'			=> ($notifications !== false) ? $user->lang('NOTIFICATIONS_COUNT', $notifications['unread_count']) : '', +		'U_VIEW_ALL_NOTIFICATIONS'		=> append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=ucp_notifications'), +		'U_NOTIFICATION_SETTINGS'		=> append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=ucp_notifications&mode=notification_options'), +		'S_NOTIFICATIONS_DISPLAY'		=> $config['load_notifications'], +  		'S_USER_NEW_PRIVMSG'			=> $user->data['user_new_privmsg'],  		'S_USER_UNREAD_PRIVMSG'			=> $user->data['user_unread_privmsg'],  		'S_USER_NEW'					=> $user->data['user_new'], @@ -5582,7 +5680,7 @@ function phpbb_convert_30_dbms_to_31($dbms)  		/*  		$reflection = new \ReflectionClass($dbms); -   	  +  		if ($reflection->isSubclassOf('phpbb_db_driver'))  		{  			return $dbms; diff --git a/phpBB/includes/functions_acp.php b/phpBB/includes/functions_acp.php index 32fd76e74d..d6bd9e35dd 100644 --- a/phpBB/includes/functions_acp.php +++ b/phpBB/includes/functions_acp.php @@ -443,6 +443,13 @@ function validate_config_vars($config_vars, &$cfg_array, &$error)  				}  			break; +			case 'email': +				if (!preg_match('/^' . get_preg_expression('email') . '$/i', $cfg_array[$config_name])) +				{ +					$error[] = $user->lang['EMAIL_INVALID_EMAIL']; +				} +			break; +  			// Absolute path  			case 'script_path':  				if (!$cfg_array[$config_name]) diff --git a/phpBB/includes/functions_admin.php b/phpBB/includes/functions_admin.php index 5529f2af46..baf107bcda 100644 --- a/phpBB/includes/functions_admin.php +++ b/phpBB/includes/functions_admin.php @@ -618,7 +618,7 @@ function move_posts($post_ids, $topic_id, $auto_sync = true)  */  function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_sync = true, $call_delete_posts = true)  { -	global $db, $config; +	global $db, $config, $phpbb_container;  	$approved_topics = 0;  	$forum_ids = $topic_ids = array(); @@ -715,6 +715,14 @@ function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_s  		set_config_count('num_topics', $approved_topics * (-1), true);  	} +	$phpbb_notifications = $phpbb_container->get('notification_manager'); + +	$phpbb_notifications->delete_notifications(array( +		'topic', +		'approve_topic', +		'topic_in_queue', +	), $topic_ids); +  	return $return;  } @@ -723,7 +731,7 @@ function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_s  */  function delete_posts($where_type, $where_ids, $auto_sync = true, $posted_sync = true, $post_count_sync = true, $call_delete_topics = true)  { -	global $db, $config, $phpbb_root_path, $phpEx, $auth, $user; +	global $db, $config, $phpbb_root_path, $phpEx, $auth, $user, $phpbb_container;  	if ($where_type === 'range')  	{ @@ -892,6 +900,16 @@ function delete_posts($where_type, $where_ids, $auto_sync = true, $posted_sync =  		delete_topics('topic_id', $remove_topics, $auto_sync, $post_count_sync, false);  	} +	$phpbb_notifications = $phpbb_container->get('notification_manager'); + +	$phpbb_notifications->delete_notifications(array( +		'quote', +		'bookmark', +		'post', +		'approve_post', +		'post_in_queue', +	), $post_ids); +  	return sizeof($post_ids);  } diff --git a/phpBB/includes/functions_messenger.php b/phpBB/includes/functions_messenger.php index d0a02567ad..821f0d970d 100644 --- a/phpBB/includes/functions_messenger.php +++ b/phpBB/includes/functions_messenger.php @@ -393,6 +393,28 @@ class messenger  	}  	/** +	* Generates a valid message id to be used in emails +	* +	* @return string message id +	*/ +	function generate_message_id() +	{ +		global $config; + +		$domain = 'phpbb.generated'; +		if ($config['server_name']) +		{ +			$domain = $config['server_name']; +		} +		else if (!empty($_SERVER['SERVER_NAME'])) +		{ +			$domain = $_SERVER['SERVER_NAME']; +		} + +		return md5(unique_id(time())) . '@' . $domain; +	} + +	/**  	* Return email header  	*/  	function build_header($to, $cc, $bcc) @@ -418,7 +440,7 @@ class messenger  		$headers[] = 'Return-Path: <' . $config['board_email'] . '>';  		$headers[] = 'Sender: <' . $config['board_email'] . '>';  		$headers[] = 'MIME-Version: 1.0'; -		$headers[] = 'Message-ID: <' . md5(unique_id(time())) . '@' . $config['server_name'] . '>'; +		$headers[] = 'Message-ID: <' . $this->generate_message_id() . '>';  		$headers[] = 'Date: ' . date('r', time());  		$headers[] = 'Content-Type: text/plain; charset=UTF-8'; // format=flowed  		$headers[] = 'Content-Transfer-Encoding: 8bit'; // 7bit diff --git a/phpBB/includes/functions_posting.php b/phpBB/includes/functions_posting.php index 8aea27a9ef..baef7bcda5 100644 --- a/phpBB/includes/functions_posting.php +++ b/phpBB/includes/functions_posting.php @@ -61,7 +61,7 @@ function generate_smilies($mode, $forum_id)  			'body' => 'posting_smilies.html')  		); -		generate_pagination(append_sid("{$phpbb_root_path}posting.$phpEx", 'mode=smilies&f=' . $forum_id), $smiley_count, $config['smilies_per_page'], $start);	 +		generate_pagination(append_sid("{$phpbb_root_path}posting.$phpEx", 'mode=smilies&f=' . $forum_id), $smiley_count, $config['smilies_per_page'], $start);  	}  	$display_link = false; @@ -1175,238 +1175,6 @@ function topic_review($topic_id, $forum_id, $mode = 'topic_review', $cur_post_id  	return true;  } -/** -* User Notification -*/ -function user_notification($mode, $subject, $topic_title, $forum_name, $forum_id, $topic_id, $post_id, $author_name = '') -{ -	global $db, $user, $config, $phpbb_root_path, $phpEx, $auth; - -	$topic_notification = ($mode == 'reply' || $mode == 'quote') ? true : false; -	$forum_notification = ($mode == 'post') ? true : false; - -	if (!$topic_notification && !$forum_notification) -	{ -		trigger_error('NO_MODE'); -	} - -	if (($topic_notification && !$config['allow_topic_notify']) || ($forum_notification && !$config['allow_forum_notify'])) -	{ -		return; -	} - -	$topic_title = ($topic_notification) ? $topic_title : $subject; -	$topic_title = censor_text($topic_title); - -	// Exclude guests, current user and banned users from notifications -	if (!function_exists('phpbb_get_banned_user_ids')) -	{ -		include($phpbb_root_path . 'includes/functions_user.' . $phpEx); -	} -	$sql_ignore_users = phpbb_get_banned_user_ids(); -	$sql_ignore_users[ANONYMOUS] = ANONYMOUS; -	$sql_ignore_users[$user->data['user_id']] = $user->data['user_id']; - -	$notify_rows = array(); - -	// -- get forum_userids	|| topic_userids -	$sql = 'SELECT u.user_id, u.username, u.user_email, u.user_lang, u.user_notify_type, u.user_jabber -		FROM ' . (($topic_notification) ? TOPICS_WATCH_TABLE : FORUMS_WATCH_TABLE) . ' w, ' . USERS_TABLE . ' u -		WHERE w.' . (($topic_notification) ? 'topic_id' : 'forum_id') . ' = ' . (($topic_notification) ? $topic_id : $forum_id) . ' -			AND ' . $db->sql_in_set('w.user_id', $sql_ignore_users, true) . ' -			AND w.notify_status = ' . NOTIFY_YES . ' -			AND u.user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ') -			AND u.user_id = w.user_id'; -	$result = $db->sql_query($sql); - -	while ($row = $db->sql_fetchrow($result)) -	{ -		$notify_user_id = (int) $row['user_id']; -		$notify_rows[$notify_user_id] = array( -			'user_id'		=> $notify_user_id, -			'username'		=> $row['username'], -			'user_email'	=> $row['user_email'], -			'user_jabber'	=> $row['user_jabber'], -			'user_lang'		=> $row['user_lang'], -			'notify_type'	=> ($topic_notification) ? 'topic' : 'forum', -			'template'		=> ($topic_notification) ? 'topic_notify' : 'newtopic_notify', -			'method'		=> $row['user_notify_type'], -			'allowed'		=> false -		); - -		// Add users who have been already notified to ignore list -		$sql_ignore_users[$notify_user_id] = $notify_user_id; -	} -	$db->sql_freeresult($result); - -	// forum notification is sent to those not already receiving topic notifications -	if ($topic_notification) -	{ -		$sql = 'SELECT u.user_id, u.username, u.user_email, u.user_lang, u.user_notify_type, u.user_jabber -			FROM ' . FORUMS_WATCH_TABLE . ' fw, ' . USERS_TABLE . " u -			WHERE fw.forum_id = $forum_id -				AND " . $db->sql_in_set('fw.user_id', $sql_ignore_users, true) . ' -				AND fw.notify_status = ' . NOTIFY_YES . ' -				AND u.user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ') -				AND u.user_id = fw.user_id'; -		$result = $db->sql_query($sql); - -		while ($row = $db->sql_fetchrow($result)) -		{ -			$notify_user_id = (int) $row['user_id']; -			$notify_rows[$notify_user_id] = array( -				'user_id'		=> $notify_user_id, -				'username'		=> $row['username'], -				'user_email'	=> $row['user_email'], -				'user_jabber'	=> $row['user_jabber'], -				'user_lang'		=> $row['user_lang'], -				'notify_type'	=> 'forum', -				'template'		=> 'forum_notify', -				'method'		=> $row['user_notify_type'], -				'allowed'		=> false -			); -		} -		$db->sql_freeresult($result); -	} - -	if (!sizeof($notify_rows)) -	{ -		return; -	} - -	// Make sure users are allowed to read the forum -	foreach ($auth->acl_get_list(array_keys($notify_rows), 'f_read', $forum_id) as $forum_id => $forum_ary) -	{ -		foreach ($forum_ary as $auth_option => $user_ary) -		{ -			foreach ($user_ary as $user_id) -			{ -				$notify_rows[$user_id]['allowed'] = true; -			} -		} -	} - -	// Now, we have to do a little step before really sending, we need to distinguish our users a little bit. ;) -	$msg_users = $delete_ids = $update_notification = array(); -	foreach ($notify_rows as $user_id => $row) -	{ -		if (!$row['allowed'] || !trim($row['user_email'])) -		{ -			$delete_ids[$row['notify_type']][] = $row['user_id']; -		} -		else -		{ -			$msg_users[] = $row; -			$update_notification[$row['notify_type']][] = $row['user_id']; - -			/* -			* We also update the forums watch table for this user when we are -			* sending out a topic notification to prevent sending out another -			* notification in case this user is also subscribed to the forum -			* this topic was posted in. -			* Since an UPDATE query is used, this has no effect on users only -			* subscribed to the topic (i.e. no row is created) and should not -			* be a performance issue. -			*/ -			if ($row['notify_type'] === 'topic') -			{ -				$update_notification['forum'][] = $row['user_id']; -			} -		} -	} -	unset($notify_rows); - -	// Now, we are able to really send out notifications -	if (sizeof($msg_users)) -	{ -		include_once($phpbb_root_path . 'includes/functions_messenger.' . $phpEx); -		$messenger = new messenger(); - -		$msg_list_ary = array(); -		foreach ($msg_users as $row) -		{ -			$pos = (!isset($msg_list_ary[$row['template']])) ? 0 : sizeof($msg_list_ary[$row['template']]); - -			$msg_list_ary[$row['template']][$pos]['method']	= $row['method']; -			$msg_list_ary[$row['template']][$pos]['email']	= $row['user_email']; -			$msg_list_ary[$row['template']][$pos]['jabber']	= $row['user_jabber']; -			$msg_list_ary[$row['template']][$pos]['name']	= $row['username']; -			$msg_list_ary[$row['template']][$pos]['lang']	= $row['user_lang']; -			$msg_list_ary[$row['template']][$pos]['user_id']= $row['user_id']; -		} -		unset($msg_users); - -		foreach ($msg_list_ary as $email_template => $email_list) -		{ -			foreach ($email_list as $addr) -			{ -				$messenger->template($email_template, $addr['lang']); - -				$messenger->to($addr['email'], $addr['name']); -				$messenger->im($addr['jabber'], $addr['name']); - -				$messenger->assign_vars(array( -					'USERNAME'		=> htmlspecialchars_decode($addr['name']), -					'TOPIC_TITLE'	=> htmlspecialchars_decode($topic_title), -					'FORUM_NAME'	=> htmlspecialchars_decode($forum_name), -					'AUTHOR_NAME'	=> htmlspecialchars_decode($author_name), - -					'U_FORUM'				=> generate_board_url() . "/viewforum.$phpEx?f=$forum_id", -					'U_TOPIC'				=> generate_board_url() . "/viewtopic.$phpEx?f=$forum_id&t=$topic_id", -					'U_NEWEST_POST'			=> generate_board_url() . "/viewtopic.$phpEx?f=$forum_id&t=$topic_id&p=$post_id&e=$post_id", -					'U_STOP_WATCHING_TOPIC'	=> generate_board_url() . "/viewtopic.$phpEx?uid={$addr['user_id']}&f=$forum_id&t=$topic_id&unwatch=topic", -					'U_STOP_WATCHING_FORUM'	=> generate_board_url() . "/viewforum.$phpEx?uid={$addr['user_id']}&f=$forum_id&unwatch=forum", -				)); - -				$messenger->send($addr['method']); -			} -		} -		unset($msg_list_ary); - -		$messenger->save_queue(); -	} - -	// Handle the DB updates -	$db->sql_transaction('begin'); - -	if (!empty($update_notification['topic'])) -	{ -		$sql = 'UPDATE ' . TOPICS_WATCH_TABLE . ' -			SET notify_status = ' . NOTIFY_NO . " -			WHERE topic_id = $topic_id -				AND " . $db->sql_in_set('user_id', $update_notification['topic']); -		$db->sql_query($sql); -	} - -	if (!empty($update_notification['forum'])) -	{ -		$sql = 'UPDATE ' . FORUMS_WATCH_TABLE . ' -			SET notify_status = ' . NOTIFY_NO . " -			WHERE forum_id = $forum_id -				AND " . $db->sql_in_set('user_id', $update_notification['forum']); -		$db->sql_query($sql); -	} - -	// Now delete the user_ids not authorised to receive notifications on this topic/forum -	if (!empty($delete_ids['topic'])) -	{ -		$sql = 'DELETE FROM ' . TOPICS_WATCH_TABLE . " -			WHERE topic_id = $topic_id -				AND " . $db->sql_in_set('user_id', $delete_ids['topic']); -		$db->sql_query($sql); -	} - -	if (!empty($delete_ids['forum'])) -	{ -		$sql = 'DELETE FROM ' . FORUMS_WATCH_TABLE . " -			WHERE forum_id = $forum_id -				AND " . $db->sql_in_set('user_id', $delete_ids['forum']); -		$db->sql_query($sql); -	} - -	$db->sql_transaction('commit'); -} -  //  // Post handling functions  // @@ -1642,7 +1410,7 @@ function delete_post($forum_id, $topic_id, $post_id, &$data)  */  function submit_post($mode, $subject, $username, $topic_type, &$poll, &$data, $update_message = true, $update_search_index = true)  { -	global $db, $auth, $user, $config, $phpEx, $template, $phpbb_root_path; +	global $db, $auth, $user, $config, $phpEx, $template, $phpbb_root_path, $phpbb_container;  	// We do not handle erasing posts here  	if ($mode == 'delete') @@ -2454,10 +2222,76 @@ function submit_post($mode, $subject, $username, $topic_type, &$poll, &$data, $u  	}  	// Send Notifications -	if (($mode == 'reply' || $mode == 'quote' || $mode == 'post') && $post_approval) +	$notification_data = array_merge($data, array( +		'topic_title'		=> (isset($data['topic_title'])) ? $data['topic_title'] : $subject, +		'post_username'		=> $username, +		'poster_id'			=> $poster_id, +		'post_text'			=> $data['message'], +		'post_time'			=> $current_time, +		'post_subject'		=> $subject, +	)); + +	$phpbb_notifications = $phpbb_container->get('notification_manager'); + +	if ($post_approval)  	{ -		$username = ($username) ? $username : $user->data['username']; -		user_notification($mode, $subject, $data['topic_title'], $data['forum_name'], $data['forum_id'], $data['topic_id'], $data['post_id'], $username); +		switch ($mode) +		{ +			case 'post': +				$phpbb_notifications->add_notifications(array( +					'quote', +					'topic', +				), $notification_data); +			break; + +			case 'reply': +			case 'quote': +				$phpbb_notifications->add_notifications(array( +					'quote', +					'bookmark', +					'post', +				), $notification_data); +			break; + +			case 'edit_topic': +			case 'edit_first_post': +			case 'edit': +			case 'edit_last_post': +				$phpbb_notifications->update_notifications(array( +					'quote', +					'bookmark', +					'topic', +					'post', +				), $notification_data); +			break; +		} +	} +	else +	{ +		switch ($mode) +		{ +			case 'post': +				$phpbb_notifications->add_notifications('topic_in_queue', $notification_data); +			break; + +			case 'reply': +			case 'quote': +				$phpbb_notifications->add_notifications('post_in_queue', $notification_data); +			break; + +			case 'edit_topic': +			case 'edit_first_post': +			case 'edit': +			case 'edit_last_post': +				$phpbb_notifications->delete_notifications('topic', $data['topic_id']); + +				$phpbb_notifications->delete_notifications(array( +					'quote', +					'bookmark', +					'post', +				), $data['post_id']); +			break; +		}  	}  	$params = $add_anchor = ''; diff --git a/phpBB/includes/functions_privmsgs.php b/phpBB/includes/functions_privmsgs.php index ba939d490e..14278a2529 100644 --- a/phpBB/includes/functions_privmsgs.php +++ b/phpBB/includes/functions_privmsgs.php @@ -876,7 +876,11 @@ function update_unread_status($unread, $msg_id, $user_id, $folder_id)  		return;  	} -	global $db, $user; +	global $db, $user, $phpbb_container; + +	$phpbb_notifications = $phpbb_container->get('notification_manager'); + +	$phpbb_notifications->mark_notifications_read('pm', $msg_id, $user_id);  	$sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . "  		SET pm_unread = 0 @@ -981,7 +985,7 @@ function handle_mark_actions($user_id, $mark_action)  */  function delete_pm($user_id, $msg_ids, $folder_id)  { -	global $db, $user, $phpbb_root_path, $phpEx; +	global $db, $user, $phpbb_root_path, $phpEx, $phpbb_container;  	$user_id	= (int) $user_id;  	$folder_id	= (int) $folder_id; @@ -1093,6 +1097,10 @@ function delete_pm($user_id, $msg_ids, $folder_id)  		$user->data['user_unread_privmsg'] -= $num_unread;  	} +	$phpbb_notifications = $phpbb_container->get('notification_manager'); + +	$phpbb_notifications->delete_notifications('pm', array_keys($delete_rows)); +  	// Now we have to check which messages we can delete completely  	$sql = 'SELECT msg_id  		FROM ' . PRIVMSGS_TO_TABLE . ' @@ -1157,7 +1165,7 @@ function phpbb_delete_user_pms($user_id)  */  function phpbb_delete_users_pms($user_ids)  { -	global $db, $user, $phpbb_root_path, $phpEx; +	global $db, $user, $phpbb_root_path, $phpEx, $phpbb_container;  	$user_id_sql = $db->sql_in_set('user_id', $user_ids);  	$author_id_sql = $db->sql_in_set('author_id', $user_ids); @@ -1202,6 +1210,8 @@ function phpbb_delete_users_pms($user_ids)  	$db->sql_transaction('begin'); +	$phpbb_notifications = $phpbb_container->get('notification_manager'); +  	if (!empty($undelivered_msg))  	{  		// A pm is delivered, if for any recipient the message was moved @@ -1270,6 +1280,8 @@ function phpbb_delete_users_pms($user_ids)  				WHERE folder_id = ' . PRIVMSGS_NO_BOX . '  					AND ' . $db->sql_in_set('msg_id', $delivered_msg);  			$db->sql_query($sql); + +			$phpbb_notifications->delete_notifications('pm', $delivered_msg);  		}  		if (!empty($undelivered_msg)) @@ -1281,6 +1293,8 @@ function phpbb_delete_users_pms($user_ids)  			$sql = 'DELETE FROM ' . PRIVMSGS_TABLE . '  				WHERE ' . $db->sql_in_set('msg_id', $undelivered_msg);  			$db->sql_query($sql); + +			$phpbb_notifications->delete_notifications('pm', $undelivered_msg);  		}  	} @@ -1323,6 +1337,8 @@ function phpbb_delete_users_pms($user_ids)  			$sql = 'DELETE FROM ' . PRIVMSGS_TABLE . '  				WHERE ' . $db->sql_in_set('msg_id', $delete_ids);  			$db->sql_query($sql); + +			$phpbb_notifications->delete_notifications('pm', $delete_ids);  		}  	} @@ -1559,7 +1575,7 @@ function get_folder_status($folder_id, $folder)  */  function submit_pm($mode, $subject, &$data, $put_in_outbox = true)  { -	global $db, $auth, $config, $phpEx, $template, $user, $phpbb_root_path; +	global $db, $auth, $config, $phpEx, $template, $user, $phpbb_root_path, $phpbb_container;  	// We do not handle erasing pms here  	if ($mode == 'delete') @@ -1859,95 +1875,23 @@ function submit_pm($mode, $subject, &$data, $put_in_outbox = true)  	$db->sql_transaction('commit');  	// Send Notifications -	if ($mode != 'edit') -	{ -		pm_notification($mode, $data['from_username'], $recipients, $subject, $data['message'], $data['msg_id']); -	} - -	return $data['msg_id']; -} - -/** -* PM Notification -*/ -function pm_notification($mode, $author, $recipients, $subject, $message, $msg_id) -{ -	global $db, $user, $config, $phpbb_root_path, $phpEx, $auth; - -	$subject = censor_text($subject); - -	// Exclude guests, current user and banned users from notifications -	unset($recipients[ANONYMOUS], $recipients[$user->data['user_id']]); - -	if (!sizeof($recipients)) -	{ -		return; -	} - -	if (!function_exists('phpbb_get_banned_user_ids')) -	{ -		include($phpbb_root_path . 'includes/functions_user.' . $phpEx); -	} -	$banned_users = phpbb_get_banned_user_ids(array_keys($recipients)); -	$recipients = array_diff(array_keys($recipients), $banned_users); - -	if (!sizeof($recipients)) -	{ -		return; -	} +	$pm_data = array_merge($data, array( +		'message_subject'		=> $subject, +		'recipients'			=> $recipients, +	)); -	$sql = 'SELECT user_id, username, user_email, user_lang, user_notify_pm, user_notify_type, user_jabber -		FROM ' . USERS_TABLE . ' -		WHERE ' . $db->sql_in_set('user_id', $recipients); -	$result = $db->sql_query($sql); +	$phpbb_notifications = $phpbb_container->get('notification_manager'); -	$msg_list_ary = array(); -	while ($row = $db->sql_fetchrow($result)) +	if ($mode == 'edit')  	{ -		if ($row['user_notify_pm'] == 1 && trim($row['user_email'])) -		{ -			$msg_list_ary[] = array( -				'method'	=> $row['user_notify_type'], -				'email'		=> $row['user_email'], -				'jabber'	=> $row['user_jabber'], -				'name'		=> $row['username'], -				'lang'		=> $row['user_lang'] -			); -		} +		$phpbb_notifications->update_notifications('pm', $pm_data);  	} -	$db->sql_freeresult($result); - -	if (!sizeof($msg_list_ary)) +	else  	{ -		return; +		$phpbb_notifications->add_notifications('pm', $pm_data);  	} -	include_once($phpbb_root_path . 'includes/functions_messenger.' . $phpEx); -	$messenger = new messenger(); - -	foreach ($msg_list_ary as $pos => $addr) -	{ -		$messenger->template('privmsg_notify', $addr['lang']); - -		$messenger->to($addr['email'], $addr['name']); -		$messenger->im($addr['jabber'], $addr['name']); - -		$messenger->assign_vars(array( -			'SUBJECT'		=> htmlspecialchars_decode($subject), -			'AUTHOR_NAME'	=> htmlspecialchars_decode($author), -			'USERNAME'		=> htmlspecialchars_decode($addr['name']), - -			'U_INBOX'			=> generate_board_url() . "/ucp.$phpEx?i=pm&folder=inbox", -			'U_VIEW_MESSAGE'	=> generate_board_url() . "/ucp.$phpEx?i=pm&mode=view&p=$msg_id", -		)); - -		$messenger->send($addr['method']); -	} -	unset($msg_list_ary); - -	$messenger->save_queue(); - -	unset($messenger); +	return $data['msg_id'];  }  /** diff --git a/phpBB/includes/mcp/mcp_pm_reports.php b/phpBB/includes/mcp/mcp_pm_reports.php index 86650947c7..99ff397a66 100644 --- a/phpBB/includes/mcp/mcp_pm_reports.php +++ b/phpBB/includes/mcp/mcp_pm_reports.php @@ -33,7 +33,7 @@ class mcp_pm_reports  	function main($id, $mode)  	{  		global $auth, $db, $user, $template, $cache; -		global $config, $phpbb_root_path, $phpEx, $action; +		global $config, $phpbb_root_path, $phpEx, $action, $phpbb_container;  		include_once($phpbb_root_path . 'includes/functions_posting.' . $phpEx);  		include_once($phpbb_root_path . 'includes/functions_privmsgs.' . $phpEx); @@ -89,6 +89,10 @@ class mcp_pm_reports  					trigger_error('NO_REPORT');  				} +				$phpbb_notifications = $phpbb_container->get('notification_manager'); + +				$phpbb_notifications->mark_notifications_read_by_parent('report_pm', $report_id, $user->data['user_id']); +  				$pm_id = $report['pm_id'];  				$report_id = $report['report_id']; diff --git a/phpBB/includes/mcp/mcp_queue.php b/phpBB/includes/mcp/mcp_queue.php index 0b195aa9d8..24afa1f210 100644 --- a/phpBB/includes/mcp/mcp_queue.php +++ b/phpBB/includes/mcp/mcp_queue.php @@ -33,7 +33,7 @@ class mcp_queue  	function main($id, $mode)  	{  		global $auth, $db, $user, $template, $cache; -		global $config, $phpbb_root_path, $phpEx, $action; +		global $config, $phpbb_root_path, $phpEx, $action, $phpbb_container;  		include_once($phpbb_root_path . 'includes/functions_posting.' . $phpEx); @@ -78,12 +78,16 @@ class mcp_queue  				$post_id = request_var('p', 0);  				$topic_id = request_var('t', 0); +				$phpbb_notifications = $phpbb_container->get('notification_manager'); +  				if ($topic_id)  				{  					$topic_info = get_topic_data(array($topic_id), 'm_approve');  					if (isset($topic_info[$topic_id]['topic_first_post_id']))  					{  						$post_id = (int) $topic_info[$topic_id]['topic_first_post_id']; + +						$phpbb_notifications->mark_notifications_read('topic_in_queue', $topic_id, $user->data['user_id']);  					}  					else  					{ @@ -91,6 +95,8 @@ class mcp_queue  					}  				} +				$phpbb_notifications->mark_notifications_read('post_in_queue', $post_id, $user->data['user_id']); +  				$post_info = get_post_data(array($post_id), 'm_approve', true);  				if (!sizeof($post_info)) @@ -451,7 +457,7 @@ function approve_post($post_id_list, $id, $mode)  {  	global $db, $template, $user, $config;  	global $phpEx, $phpbb_root_path; -	global $request; +	global $request, $phpbb_container;  	if (!check_ids($post_id_list, POSTS_TABLE, 'post_id', array('m_approve')))  	{ @@ -597,54 +603,51 @@ function approve_post($post_id_list, $id, $mode)  		sync('forum', 'forum_id', array_keys($forum_id_list), true, true);  		unset($topic_id_list, $forum_id_list); -		$messenger = new messenger(); - -		// Notify Poster? -		if ($notify_poster) -		{ -			foreach ($post_info as $post_id => $post_data) -			{ -				if ($post_data['poster_id'] == ANONYMOUS) -				{ -					continue; -				} - -				$email_template = ($post_data['post_id'] == $post_data['topic_first_post_id'] && $post_data['post_id'] == $post_data['topic_last_post_id']) ? 'topic_approved' : 'post_approved'; - -				$messenger->template($email_template, $post_data['user_lang']); - -				$messenger->to($post_data['user_email'], $post_data['username']); -				$messenger->im($post_data['user_jabber'], $post_data['username']); - -				$messenger->assign_vars(array( -					'USERNAME'		=> htmlspecialchars_decode($post_data['username']), -					'POST_SUBJECT'	=> htmlspecialchars_decode(censor_text($post_data['post_subject'])), -					'TOPIC_TITLE'	=> htmlspecialchars_decode(censor_text($post_data['topic_title'])), - -					'U_VIEW_TOPIC'	=> generate_board_url() . "/viewtopic.$phpEx?f={$post_data['forum_id']}&t={$post_data['topic_id']}&e=0", -					'U_VIEW_POST'	=> generate_board_url() . "/viewtopic.$phpEx?f={$post_data['forum_id']}&t={$post_data['topic_id']}&p=$post_id&e=$post_id") -				); - -				$messenger->send($post_data['user_notify_type']); -			} -		} - -		$messenger->save_queue(); -  		// Send out normal user notifications  		$email_sig = str_replace('<br />', "\n", "-- \n" . $config['board_email_sig']); +		$phpbb_notifications = $phpbb_container->get('notification_manager'); + +		// Handle notifications  		foreach ($post_info as $post_id => $post_data)  		{  			if ($post_id == $post_data['topic_first_post_id'] && $post_id == $post_data['topic_last_post_id'])  			{ -				// Forum Notifications -				user_notification('post', $post_data['topic_title'], $post_data['topic_title'], $post_data['forum_name'], $post_data['forum_id'], $post_data['topic_id'], $post_id); +				$phpbb_notifications->delete_notifications('topic_in_queue', $post_data['topic_id']); + +				$phpbb_notifications->add_notifications(array( +					'quote', +					'topic', +				), $post_data); + +				$phpbb_notifications->mark_notifications_read('quote', $post_data['post_id'], $user->data['user_id']); +				$phpbb_notifications->mark_notifications_read('topic', $post_data['topic_id'], $user->data['user_id']); + +				if ($notify_poster) +				{ +					$phpbb_notifications->add_notifications('approve_topic', $post_data); +				}  			}  			else  			{ -				// Topic Notifications -				user_notification('reply', $post_data['post_subject'], $post_data['topic_title'], $post_data['forum_name'], $post_data['forum_id'], $post_data['topic_id'], $post_id); +				$phpbb_notifications->delete_notifications('post_in_queue', $post_id); + +				$phpbb_notifications->add_notifications(array( +					'quote', +					'bookmark', +					'post', +				), $post_data); +				 +				$phpbb_notifications->mark_notifications_read(array( +					'quote', +					'bookmark', +					'post', +				),$post_data['post_id'], $user->data['user_id']); + +				if ($notify_poster) +				{ +					$phpbb_notifications->add_notifications('approve_post', $post_data); +				}  			}  		} @@ -734,7 +737,7 @@ function disapprove_post($post_id_list, $id, $mode)  {  	global $db, $template, $user, $config;  	global $phpEx, $phpbb_root_path; -	global $request; +	global $request, $phpbb_container;  	if (!check_ids($post_id_list, POSTS_TABLE, 'post_id', array('m_approve')))  	{ @@ -867,20 +870,29 @@ function disapprove_post($post_id_list, $id, $mode)  			}  		} -		$messenger = new messenger(); +		$phpbb_notifications = $phpbb_container->get('notification_manager'); + +		foreach ($post_info as $post_id => $post_data) +		{ +			if ($post_id == $post_data['topic_first_post_id'] && $post_id == $post_data['topic_last_post_id']) +			{ +				$phpbb_notifications->delete_notifications('topic_in_queue', $post_data['topic_id']); +			} +			else +			{ +				$phpbb_notifications->delete_notifications('post_in_queue', $post_id); +			} +		}  		// Notify Poster?  		if ($notify_poster)  		{  			$lang_reasons = array(); +			// Handle notifications  			foreach ($post_info as $post_id => $post_data)  			{ -				if ($post_data['poster_id'] == ANONYMOUS) -				{ -					continue; -				} - +				$post_data['disapprove_reason'] = '';  				if (isset($disapprove_reason_lang))  				{  					// Okay we need to get the reason from the posters language @@ -906,33 +918,30 @@ function disapprove_post($post_id_list, $id, $mode)  						}  					} -					$email_disapprove_reason = $lang_reasons[$post_data['user_lang']]; -					$email_disapprove_reason .= ($reason) ? "\n\n" . $reason : ''; +					$post_data['disapprove_reason'] = $lang_reasons[$post_data['user_lang']]; +					$post_data['disapprove_reason'] .= ($reason) ? "\n\n" . $reason : '';  				} -				$email_template = ($post_data['post_id'] == $post_data['topic_first_post_id'] && $post_data['post_id'] == $post_data['topic_last_post_id']) ? 'topic_disapproved' : 'post_disapproved'; - -				$messenger->template($email_template, $post_data['user_lang']); - -				$messenger->to($post_data['user_email'], $post_data['username']); -				$messenger->im($post_data['user_jabber'], $post_data['username']); - -				$messenger->assign_vars(array( -					'USERNAME'		=> htmlspecialchars_decode($post_data['username']), -					'REASON'		=> htmlspecialchars_decode($email_disapprove_reason), -					'POST_SUBJECT'	=> htmlspecialchars_decode(censor_text($post_data['post_subject'])), -					'TOPIC_TITLE'	=> htmlspecialchars_decode(censor_text($post_data['topic_title']))) -				); - -				$messenger->send($post_data['user_notify_type']); +				if ($post_id == $post_data['topic_first_post_id'] && $post_id == $post_data['topic_last_post_id']) +				{ +					if ($notify_poster) +					{ +						$phpbb_notifications->add_notifications('disapprove_topic', $post_data); +					} +				} +				else +				{ +					if ($notify_poster) +					{ +						$phpbb_notifications->add_notifications('disapprove_post', $post_data); +					} +				}  			}  			unset($lang_reasons);  		}  		unset($post_info, $disapprove_reason, $email_disapprove_reason, $disapprove_reason_lang); -		$messenger->save_queue(); -  		if ($num_disapproved_topics)  		{  			$success_msg = ($num_disapproved_topics == 1) ? 'TOPIC_DISAPPROVED_SUCCESS' : 'TOPICS_DISAPPROVED_SUCCESS'; diff --git a/phpBB/includes/mcp/mcp_reports.php b/phpBB/includes/mcp/mcp_reports.php index 8da303f6e3..0a600d7057 100644 --- a/phpBB/includes/mcp/mcp_reports.php +++ b/phpBB/includes/mcp/mcp_reports.php @@ -33,7 +33,7 @@ class mcp_reports  	function main($id, $mode)  	{  		global $auth, $db, $user, $template, $cache; -		global $config, $phpbb_root_path, $phpEx, $action; +		global $config, $phpbb_root_path, $phpEx, $action, $phpbb_container;  		include_once($phpbb_root_path . 'includes/functions_posting.' . $phpEx); @@ -87,6 +87,10 @@ class mcp_reports  					trigger_error('NO_REPORT');  				} +				$phpbb_notifications = $phpbb_container->get('notification_manager'); + +				$phpbb_notifications->mark_notifications_read('report_post', $post_id, $user->data['user_id']); +  				if (!$report_id && $report['report_closed'])  				{  					trigger_error('REPORT_CLOSED'); @@ -436,7 +440,7 @@ class mcp_reports  function close_report($report_id_list, $mode, $action, $pm = false)  {  	global $db, $template, $user, $config, $auth; -	global $phpEx, $phpbb_root_path; +	global $phpEx, $phpbb_root_path, $phpbb_container;  	$pm_where = ($pm) ? ' AND r.post_id = 0 ' : ' AND r.pm_id = 0 ';  	$id_column = ($pm) ? 'pm_id' : 'post_id'; @@ -622,11 +626,11 @@ function close_report($report_id_list, $mode, $action, $pm = false)  			}  		} -		$messenger = new messenger(); -  		// Notify reporters  		if (sizeof($notify_reporters))  		{ +			$phpbb_notifications = $phpbb_container->get('notification_manager'); +  			foreach ($notify_reporters as $report_id => $reporter)  			{  				if ($reporter['user_id'] == ANONYMOUS) @@ -636,30 +640,25 @@ function close_report($report_id_list, $mode, $action, $pm = false)  				$post_id = $reporter[$id_column]; -				$messenger->template((($pm) ? 'pm_report_' : 'report_') . $action . 'd', $reporter['user_lang']); - -				$messenger->to($reporter['user_email'], $reporter['username']); -				$messenger->im($reporter['user_jabber'], $reporter['username']); -  				if ($pm)  				{ -					$messenger->assign_vars(array( -						'USERNAME'		=> htmlspecialchars_decode($reporter['username']), -						'CLOSER_NAME'	=> htmlspecialchars_decode($user->data['username']), -						'PM_SUBJECT'	=> htmlspecialchars_decode(censor_text($post_info[$post_id]['message_subject'])), -					)); +					$phpbb_notifications->add_notifications('report_pm_closed', array_merge($post_info[$post_id], array( +						'reporter'			=> $reporter['user_id'], +						'closer_id'			=> $user->data['user_id'], +						'from_user_id'		=> $post_info[$post_id]['author_id'], +					))); + +					$phpbb_notifications->delete_notifications('report_pm', $post_id);  				}  				else  				{ -					$messenger->assign_vars(array( -						'USERNAME'		=> htmlspecialchars_decode($reporter['username']), -						'CLOSER_NAME'	=> htmlspecialchars_decode($user->data['username']), -						'POST_SUBJECT'	=> htmlspecialchars_decode(censor_text($post_info[$post_id]['post_subject'])), -						'TOPIC_TITLE'	=> htmlspecialchars_decode(censor_text($post_info[$post_id]['topic_title']))) -					); -				} +					$phpbb_notifications->add_notifications('report_post_closed', array_merge($post_info[$post_id], array( +						'reporter'			=> $reporter['user_id'], +						'closer_id'			=> $user->data['user_id'], +					))); -				$messenger->send($reporter['user_notify_type']); +					$phpbb_notifications->delete_notifications('report_post', $post_id); +				}  			}  		} @@ -674,8 +673,6 @@ function close_report($report_id_list, $mode, $action, $pm = false)  		unset($notify_reporters, $post_info, $reports); -		$messenger->save_queue(); -  		$success_msg = (sizeof($report_id_list) == 1) ? "{$pm_prefix}REPORT_" . strtoupper($action) . 'D_SUCCESS' : "{$pm_prefix}REPORTS_" . strtoupper($action) . 'D_SUCCESS';  	}  	else diff --git a/phpBB/includes/notification/manager.php b/phpBB/includes/notification/manager.php new file mode 100644 index 0000000000..ff83d4bb37 --- /dev/null +++ b/phpBB/includes/notification/manager.php @@ -0,0 +1,853 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Notifications service class +* @package notifications +*/ +class phpbb_notification_manager +{ +	/** @var array */ +	protected $notification_types; + +	/** @var array */ +	protected $notification_methods; + +	/** @var ContainerBuilder */ +	protected $phpbb_container; + +	/** @var phpbb_user_loader */ +	protected $user_loader; + +	/** @var phpbb_db_driver */ +	protected $db; + +	/** @var phpbb_user */ +	protected $user; + +	/** @var string */ +	protected $phpbb_root_path; + +	/** @var string */ +	protected $php_ext; + +	/** @var string */ +	protected $notification_types_table; + +	/** @var string */ +	protected $notifications_table; + +	/** @var string */ +	protected $user_notifications_table; + +	/** +	* Notification Constructor +	*  +	* @param array $notification_types +	* @param array $notification_methods +	* @param ContainerBuilder $phpbb_container +	* @param phpbb_user_loader $user_loader +	* @param phpbb_db_driver $db +	* @param phpbb_user $user +	* @param string $phpbb_root_path +	* @param string $php_ext +	* @param string $notification_types_table +	* @param string $notifications_table +	* @param string $user_notifications_table +	* @return phpbb_notification_manager +	*/ +	public function __construct($notification_types, $notification_methods, $phpbb_container, phpbb_user_loader $user_loader, phpbb_db_driver $db, $user, $phpbb_root_path, $php_ext, $notification_types_table, $notifications_table, $user_notifications_table) +	{ +		$this->notification_types = $notification_types; +		$this->notification_methods = $notification_methods; +		$this->phpbb_container = $phpbb_container; + +		$this->user_loader = $user_loader; +		$this->db = $db; +		$this->user = $user; + +		$this->phpbb_root_path = $phpbb_root_path; +		$this->php_ext = $php_ext; + +		$this->notification_types_table = $notification_types_table; +		$this->notifications_table = $notifications_table; +		$this->user_notifications_table = $user_notifications_table; +	} + +	/** +	* Load the user's notifications +	* +	* @param array $options Optional options to control what notifications are loaded +	*				notification_id		Notification id to load (or array of notification ids) +	*				user_id				User id to load notifications for (Default: $user->data['user_id']) +	*				order_by			Order by (Default: notification_time) +	*				order_dir			Order direction (Default: DESC) +	* 				limit				Number of notifications to load (Default: 5) +	* 				start				Notifications offset (Default: 0) +	* 				all_unread			Load all unread notifications? If set to true, count_unread is set to true (Default: false) +	* 				count_unread		Count all unread notifications? (Default: false) +	* 				count_total			Count all notifications? (Default: false) +	* @return array Array of information based on the request with keys: +	*	'notifications'		array of notification type objects +	*	'unread_count'		number of unread notifications the user has if count_unread is true in the options +	*	'total_count'		number of notifications the user has if count_total is true in the options +	*/ +	public function load_notifications(array $options = array()) +	{ +		// Merge default options +		$options = array_merge(array( +			'notification_id'	=> false, +			'user_id'			=> $this->user->data['user_id'], +			'order_by'			=> 'notification_time', +			'order_dir'			=> 'DESC', +			'limit'				=> 0, +			'start'				=> 0, +			'all_unread'		=> false, +			'count_unread'		=> false, +			'count_total'		=> false, +		), $options); + +		// If all_unread, count_unread must be true +		$options['count_unread'] = ($options['all_unread']) ? true : $options['count_unread']; + +		// Anonymous users and bots never receive notifications +		if ($options['user_id'] == $this->user->data['user_id'] && ($this->user->data['user_id'] == ANONYMOUS || $this->user->data['user_type'] == USER_IGNORE)) +		{ +			return array( +				'notifications'		=> array(), +				'unread_count'		=> 0, +				'total_count'		=> 0, +			); +		} + +		$notifications = $user_ids = array(); +		$load_special = array(); +		$total_count = $unread_count = 0; + +		if ($options['count_unread']) +		{ +			// Get the total number of unread notifications +			$sql = 'SELECT COUNT(n.notification_id) AS unread_count +				FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt +				WHERE n.user_id = ' . (int) $options['user_id'] . ' +					AND n.notification_read = 0 +					AND nt.notification_type = n.item_type +					AND nt.notification_type_enabled = 1'; +			$result = $this->db->sql_query($sql); +			$unread_count = (int) $this->db->sql_fetchfield('unread_count', $result); +			$this->db->sql_freeresult($result); +		} + +		if ($options['count_total']) +		{ +			// Get the total number of notifications +			$sql = 'SELECT COUNT(n.notification_id) AS total_count +				FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt +				WHERE n.user_id = ' . (int) $options['user_id'] . ' +					AND nt.notification_type = n.item_type +					AND nt.notification_type_enabled = 1'; +			$result = $this->db->sql_query($sql); +			$total_count = (int) $this->db->sql_fetchfield('total_count', $result); +			$this->db->sql_freeresult($result); +		} + +		if (!$options['count_total'] || $total_count) +		{ +			$rowset = array(); + +			// Get the main notifications +			$sql = 'SELECT n.* +				FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt +				WHERE n.user_id = ' . (int) $options['user_id'] . +					(($options['notification_id']) ? ((is_array($options['notification_id'])) ? ' AND ' . $this->db->sql_in_set('n.notification_id', $options['notification_id']) : ' AND n.notification_id = ' . (int) $options['notification_id']) : '') . ' +					AND nt.notification_type = n.item_type +					AND nt.notification_type_enabled = 1 +				ORDER BY n.' . $this->db->sql_escape($options['order_by']) . ' ' . $this->db->sql_escape($options['order_dir']); +			$result = $this->db->sql_query_limit($sql, $options['limit'], $options['start']); + +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				$rowset[$row['notification_id']] = $row; +			} +			$this->db->sql_freeresult($result); + +			// Get all unread notifications +			if ($unread_count && $options['all_unread'] && !empty($rowset)) +			{ +				$sql = 'SELECT n.* +				FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt +					WHERE n.user_id = ' . (int) $options['user_id'] . ' +						AND n.notification_read = 0 +						AND ' . $this->db->sql_in_set('n.notification_id', array_keys($rowset), true) . ' +						AND nt.notification_type = n.item_type +						AND nt.notification_type_enabled = 1 +					ORDER BY n.' . $this->db->sql_escape($options['order_by']) . ' ' . $this->db->sql_escape($options['order_dir']); +				$result = $this->db->sql_query_limit($sql, $options['limit'], $options['start']); + +				while ($row = $this->db->sql_fetchrow($result)) +				{ +					$rowset[$row['notification_id']] = $row; +				} +				$this->db->sql_freeresult($result); +			} + +			foreach ($rowset as $row) +			{ +				$notification = $this->get_item_type_class($row['item_type'], $row); + +				// Array of user_ids to query all at once +				$user_ids = array_merge($user_ids, $notification->users_to_query()); + +				// Some notification types also require querying additional tables themselves +				if (!isset($load_special[$row['item_type']])) +				{ +					$load_special[$row['item_type']] = array(); +				} +				$load_special[$row['item_type']] = array_merge($load_special[$row['item_type']], $notification->get_load_special()); + +				$notifications[$row['notification_id']] = $notification; +			} + +			$this->user_loader->load_users($user_ids); + +			// Allow each type to load its own special items +			foreach ($load_special as $item_type => $data) +			{ +				$item_class = $this->get_item_type_class($item_type); + +				$item_class->load_special($data, $notifications); +			} +		} + +		return array( +			'notifications'		=> $notifications, +			'unread_count'		=> $unread_count, +			'total_count'		=> $total_count, +		); +	} + +	/** +	* Mark notifications read +	* +	* @param bool|string|array $item_type Type identifier or array of item types (only acceptable if the $data is identical for the specified types). False to mark read for all item types +	* @param bool|int|array $item_id Item id or array of item ids. False to mark read for all item ids +	* @param bool|int|array $user_id User id or array of user ids. False to mark read for all user ids +	* @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False) +	*/ +	public function mark_notifications_read($item_type, $item_id, $user_id, $time = false) +	{ +		$time = ($time !== false) ? $time : time(); + +		$sql = 'UPDATE ' . $this->notifications_table . " +			SET notification_read = 1 +			WHERE notification_time <= " . (int) $time . +				(($item_type !== false) ? ' AND ' . (is_array($item_type) ? $this->db->sql_in_set('item_type', $item_type) : " item_type = '" . $this->db->sql_escape($item_type) . "'") : '') . +				(($item_id !== false) ? ' AND ' . (is_array($item_id) ? $this->db->sql_in_set('item_id', $item_id) : 'item_id = ' . (int) $item_id) : ''); +		$this->db->sql_query($sql); +	} + +	/** +	* Mark notifications read from a parent identifier +	* +	* @param string|array $item_type Type identifier or array of item types (only acceptable if the $data is identical for the specified types) +	* @param bool|int|array $item_parent_id Item parent id or array of item parent ids. False to mark read for all item parent ids +	* @param bool|int|array $user_id User id or array of user ids. False to mark read for all user ids +	* @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False) +	*/ +	public function mark_notifications_read_by_parent($item_type, $item_parent_id, $user_id, $time = false) +	{ +		if (is_array($item_type)) +		{ +			foreach ($item_type as $type) +			{ +				$this->mark_notifications_read_by_parent($type, $item_parent_id, $user_id, $time); +			} + +			return; +		} + +		$time = ($time !== false) ? $time : time(); + +		$sql = 'UPDATE ' . $this->notifications_table . " +			SET notification_read = 1 +			WHERE item_type = '" . $this->db->sql_escape($item_type) . "' +				AND notification_time <= " . (int) $time . +				(($item_parent_id !== false) ? ' AND ' . (is_array($item_parent_id) ? $this->db->sql_in_set('item_parent_id', $item_parent_id) : 'item_parent_id = ' . (int) $item_parent_id) : '') . +				(($user_id !== false) ? ' AND ' . (is_array($user_id) ? $this->db->sql_in_set('user_id', $user_id) : 'user_id = ' . (int) $user_id) : ''); +		$this->db->sql_query($sql); +	} + +	/** +	* Mark notifications read +	* +	* @param int|array $notification_id Notification id or array of notification ids. +	* @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False) +	*/ +	public function mark_notifications_read_by_id($notification_id, $time = false) +	{ +		$time = ($time !== false) ? $time : time(); + +		$sql = 'UPDATE ' . $this->notifications_table . " +			SET notification_read = 1 +			WHERE notification_time <= " . (int) $time . ' +				AND ' . ((is_array($notification_id)) ? $this->db->sql_in_set('notification_id', $notification_id) : 'notification_id = ' . (int) $notification_id); +		$this->db->sql_query($sql); +	} + +	/** +	* Add a notification +	* +	* @param string|array $item_type Type identifier or array of item types (only acceptable if the $data is identical for the specified types) +	*			Note: If you send an array of types, any user who could receive multiple notifications from this single item will only receive +	* 			a single notification. If they MUST receive multiple notifications, call this function multiple times instead of sending an array +	* @param array $data Data specific for this type that will be inserted +	* @param array $options Optional options to control what notifications are loaded +	* 			ignore_users	array of data to specify which users should not receive certain types of notifications +	* @return array Information about what users were notified and how they were notified +	*/ +	public function add_notifications($item_type, $data, array $options = array()) +	{ +		$options = array_merge(array( +			'ignore_users'		=> array(), +		), $options); + +		if (is_array($item_type)) +		{ +			$notified_users = array(); +			$temp_options = $options; + +			foreach ($item_type as $type) +			{ +				$temp_options['ignore_users'] = $options['ignore_users'] + $notified_users; +				$notified_users += $this->add_notifications($type, $data, $temp_options); +			} + +			return $notified_users; +		} + +		$item_id = $this->get_item_type_class($item_type)->get_item_id($data); + +		// find out which users want to receive this type of notification +		$notify_users = $this->get_item_type_class($item_type)->find_users_for_notification($data, $options); + +		$this->add_notifications_for_users($item_type, $data, $notify_users); + +		return $notify_users; +	} + +	/** +	* Add a notification for specific users +	* +	* @param string|array $item_type Type identifier or array of item types (only acceptable if the $data is identical for the specified types) +	* @param array $data Data specific for this type that will be inserted +	* @param array $notify_users User list to notify +	*/ +	public function add_notifications_for_users($item_type, $data, $notify_users) +	{ +		if (is_array($item_type)) +		{ +			foreach ($item_type as $type) +			{ +				$this->add_notifications_for_users($type, $data, $notify_users); +			} + +			return; +		} + +		$sql = 'SELECT notification_type +			FROM ' . $this->notification_types_table . " +			WHERE notification_type = '" . $this->db->sql_escape($item_type) . "'"; +		$result = $this->db->sql_query($sql); + +		if ($this->db->sql_fetchrow($result) === false) +		{ +			// Does not exist in the database, must add the item type +			$sql = 'INSERT INTO ' . $this->notification_types_table . ' ' . $this->db->sql_build_array('INSERT', array( +				'notification_type'				=> $item_type, +				'notification_type_enabled'		=> 1, +			)); +			$this->db->sql_query($sql); +		} + +		$this->db->sql_freeresult($result); + +		$item_id = $this->get_item_type_class($item_type)->get_item_id($data); + +		$user_ids = array(); +		$notification_objects = $notification_methods = array(); +		$new_rows = array(); + +		// Never send notifications to the anonymous user! +		unset($notify_users[ANONYMOUS]); + +		// Make sure not to send new notifications to users who've already been notified about this item +		// This may happen when an item was added, but now new users are able to see the item +		$sql = 'SELECT n.user_id +			FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . " nt +			WHERE n.item_type = '" . $this->db->sql_escape($item_type) . "' +				AND n.item_id = " . (int) $item_id . ' +				AND nt.notification_type = n.item_type +				AND nt.notification_type_enabled = 1'; +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			unset($notify_users[$row['user_id']]); +		} +		$this->db->sql_freeresult($result); + +		if (!sizeof($notify_users)) +		{ +			return; +		} + +		// Allow notifications to perform actions before creating the insert array (such as run a query to cache some data needed for all notifications) +		$notification = $this->get_item_type_class($item_type); +		$pre_create_data = $notification->pre_create_insert_array($data, $notify_users); +		unset($notification); + +		// Go through each user so we can insert a row in the DB and then notify them by their desired means +		foreach ($notify_users as $user => $methods) +		{ +			$notification = $this->get_item_type_class($item_type); + +			$notification->user_id = (int) $user; + +			// Store the creation array in our new rows that will be inserted later +			$new_rows[] = $notification->create_insert_array($data, $pre_create_data); + +			// Users are needed to send notifications +			$user_ids = array_merge($user_ids, $notification->users_to_query()); + +			foreach ($methods as $method) +			{ +				// setup the notification methods and add the notification to the queue +				if ($method) // blank means we just insert it as a notification, but do not notify them by any other means +				{ +					if (!isset($notification_methods[$method])) +					{ +						$notification_methods[$method] = $this->get_method_class($method); +					} + +					$notification_methods[$method]->add_to_queue($notification); +				} +			} +		} + +		// insert into the db +		$this->db->sql_multi_insert($this->notifications_table, $new_rows); + +		// We need to load all of the users to send notifications +		$this->user_loader->load_users($user_ids); + +		// run the queue for each method to send notifications +		foreach ($notification_methods as $method) +		{ +			$method->notify(); +		} +	} + +	/** +	* Update a notification +	* +	* @param string|array $item_type Type identifier or array of item types (only acceptable if the $data is identical for the specified types) +	* @param array $data Data specific for this type that will be updated +	*/ +	public function update_notifications($item_type, $data) +	{ +		if (is_array($item_type)) +		{ +			foreach ($item_type as $type) +			{ +				$this->update_notifications($type, $data); +			} + +			return; +		} + +		$notification = $this->get_item_type_class($item_type); + +		// Allow the notifications class to over-ride the update_notifications functionality +		if (method_exists($notification, 'update_notifications')) +		{ +			// Return False to over-ride the rest of the update +			if ($notification->update_notifications($data) === false) +			{ +				return; +			} +		} + +		$item_id = $notification->get_item_id($data); +		$update_array = $notification->create_update_array($data); + +		$sql = 'UPDATE ' . $this->notifications_table . ' +			SET ' . $this->db->sql_build_array('UPDATE', $update_array) . " +			WHERE item_type = '" . $this->db->sql_escape($item_type) . "' +				AND item_id = " . (int) $item_id; +		$this->db->sql_query($sql); +	} + +	/** +	* Delete a notification +	* +	* @param string|array $item_type Type identifier or array of item types (only acceptable if the $item_id is identical for the specified types) +	* @param int|array $item_id Identifier within the type (or array of ids) +	* @param array $data Data specific for this type that will be updated +	*/ +	public function delete_notifications($item_type, $item_id) +	{ +		if (is_array($item_type)) +		{ +			foreach ($item_type as $type) +			{ +				$this->delete_notifications($type, $item_id); +			} + +			return; +		} + +		$sql = 'DELETE FROM ' . $this->notifications_table . " +			WHERE item_type = '" . $this->db->sql_escape($item_type) . "' +				AND " . (is_array($item_id) ? $this->db->sql_in_set('item_id', $item_id) : 'item_id = ' . (int) $item_id); +		$this->db->sql_query($sql); +	} + +	/** +	* Get all of the subscription types +	* +	* @return array Array of item types +	*/ +	public function get_subscription_types() +	{ +		$subscription_types = array(); + +		foreach ($this->notification_types as $type_name => $data) +		{ +			$type = $this->get_item_type_class($type_name); + +			if ($type instanceof phpbb_notification_type_interface && $type->is_available()) +			{ +				$options = array_merge(array( +					'id'		=> $type->get_type(), +					'lang'		=> 'NOTIFICATION_TYPE_' . strtoupper($type->get_type()), +					'group'		=> 'NOTIFICATION_GROUP_MISCELLANEOUS', +				), (($type::$notification_option !== false) ? $type::$notification_option : array())); + +				$subscription_types[$options['group']][$options['id']] = $options; +			} +		} + +		// Move Miscellaneous to the very last section +		if (isset($subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS'])) +		{ +			$miscellaneous = $subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS']; +			unset($subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS']); +			$subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS'] = $miscellaneous; +		} + +		return $subscription_types; +	} + +	/** +	* Get all of the subscription methods +	* +	* @return array Array of methods +	*/ +	public function get_subscription_methods() +	{ +		$subscription_methods = array(); + +		foreach ($this->notification_methods as $method_name => $data) +		{ +			$method = $this->get_method_class($method_name); + +			if ($method instanceof phpbb_notification_method_interface && $method->is_available()) +			{ +				$subscription_methods[$method_name] = array( +					'id'		=> $method->get_type(), +					'lang'		=> 'NOTIFICATION_METHOD_' . strtoupper($method->get_type()), +				); +			} +		} + +		return $subscription_methods; +	} + +	/** +	* Get global subscriptions (item_id = 0) +	* +	* @param bool|int $user_id The user_id to add the subscription for (bool false for current user) +	* +	* @return array Subscriptions +	*/ +	public function get_global_subscriptions($user_id = false) +	{ +		$user_id = ($user_id === false) ? $this->user->data['user_id'] : $user_id; + +		$subscriptions = array(); + +		foreach ($this->get_subscription_types() as $group_name => $types) +		{ +			foreach ($types as $id => $type) +			{ +				$sql = 'SELECT method, notify +					FROM ' . $this->user_notifications_table . ' +					WHERE user_id = ' . (int) $user_id . " +						AND item_type = '" . $this->db->sql_escape($id) . "' +						AND item_id = 0"; +				$result = $this->db->sql_query($sql); + +				$row = $this->db->sql_fetchrow($result); +				if (!$row) +				{ +					// No rows at all, default to '' +					$subscriptions[$id] = array(''); +				} +				else +				{ +					do +					{ +						if (!$row['notify']) +						{ +							continue; +						} + +						if (!isset($subscriptions[$id])) +						{ +							$subscriptions[$id] = array(); +						} + +						$subscriptions[$id][] = $row['method']; +					} +					while ($row = $this->db->sql_fetchrow($result)); +				} + +				$this->db->sql_freeresult($result); +			} +		} + +		return $subscriptions; +	} + +	/** +	* Add a subscription +	* +	* @param string $item_type Type identifier of the subscription +	* @param int $item_id The id of the item +	* @param string $method The method of the notification e.g. '', 'email', or 'jabber' +	* @param bool|int $user_id The user_id to add the subscription for (bool false for current user) +	*/ +	public function add_subscription($item_type, $item_id = 0, $method = '', $user_id = false) +	{ +		if ($method !== '') +		{ +			$this->add_subscription($item_type, $item_type, '', $user_id); +		} + +		$user_id = ($user_id === false) ? $this->user->data['user_id'] : $user_id; + +		$sql = 'SELECT notify +			FROM ' . $this->user_notifications_table . " +			WHERE item_type = '" . $this->db->sql_escape($item_type) . "' +				AND item_id = " . (int) $item_id . ' +				AND user_id = ' .(int) $user_id . " +				AND method = '" . $this->db->sql_escape($method) . "'"; +		$this->db->sql_query($sql); +		$current = $this->db->sql_fetchfield('notify'); +		$this->db->sql_freeresult(); + +		if ($current === false) +		{ +			$sql = 'INSERT INTO ' . $this->user_notifications_table . ' ' . +				$this->db->sql_build_array('INSERT', array( +					'item_type'		=> $item_type, +					'item_id'		=> (int) $item_id, +					'user_id'		=> (int) $user_id, +					'method'		=> $method, +					'notify'		=> 1, +				)); +			$this->db->sql_query($sql); +		} +		else if (!$current) +		{ +			$sql = 'UPDATE ' . $this->user_notifications_table . " +				SET notify = 1 +				WHERE item_type = '" . $this->db->sql_escape($item_type) . "' +					AND item_id = " . (int) $item_id . ' +					AND user_id = ' .(int) $user_id . " +					AND method = '" . $this->db->sql_escape($method) . "'"; +			$this->db->sql_query($sql); +		} +	} + +	/** +	* Delete a subscription +	* +	* @param string $item_type Type identifier of the subscription +	* @param int $item_id The id of the item +	* @param string $method The method of the notification e.g. '', 'email', or 'jabber' +	* @param bool|int $user_id The user_id to add the subscription for (bool false for current user) +	*/ +	public function delete_subscription($item_type, $item_id = 0, $method = '', $user_id = false) +	{ +		$user_id = ($user_id === false) ? $this->user->data['user_id'] : $user_id; + +		// If no method, make sure that no other notification methods for this item are selected before deleting +		if ($method === '') +		{ +			$sql = 'SELECT COUNT(*) as num_notifications +				FROM ' . $this->user_notifications_table . " +				WHERE item_type = '" . $this->db->sql_escape($item_type) . "' +					AND item_id = " . (int) $item_id . ' +					AND user_id = ' .(int) $user_id . " +					AND method <> '' +					AND notify = 1"; +			$this->db->sql_query($sql); +			$num_notifications = $this->db->sql_fetchfield('num_notifications'); +			$this->db->sql_freeresult(); + +			if ($num_notifications) +			{ +				return; +			} +		} + +		$sql = 'UPDATE ' . $this->user_notifications_table . " +			SET notify = 0 +			WHERE item_type = '" . $this->db->sql_escape($item_type) . "' +				AND item_id = " . (int) $item_id . ' +				AND user_id = ' .(int) $user_id . " +				AND method = '" . $this->db->sql_escape($method) . "'"; +		$this->db->sql_query($sql); + +		if (!$this->db->sql_affectedrows()) +		{ +			$sql = 'INSERT INTO ' . $this->user_notifications_table . ' ' . +				$this->db->sql_build_array('INSERT', array( +					'item_type'		=> $item_type, +					'item_id'		=> (int) $item_id, +					'user_id'		=> (int) $user_id, +					'method'		=> $method, +					'notify'		=> 0, +				)); +			$this->db->sql_query($sql); +		} +	} + +	/** +	* Disable all notifications of a certain type +	* +	* This should be called when an extension which has notification types +	* is disabled so that all those notifications are hidden and do not +	* cause errors +	* +	* @param string $item_type Type identifier of the subscription +	*/ +	public function disable_notifications($item_type) +	{ +		$sql = 'UPDATE ' . $this->notification_types_table . " +			SET notification_type_enabled = 0 +			WHERE notification_type = '" . $this->db->sql_escape($item_type) . "'"; +		$this->db->sql_query($sql); +	} + +	/** +	* Purge all notifications of a certain type +	* +	* This should be called when an extension which has notification types +	* is purged so that all those notifications are removed +	* +	* @param string $item_type Type identifier of the subscription +	*/ +	public function purge_notifications($item_type) +	{ +		$sql = 'DELETE FROM ' . $this->notifications_table . " +			WHERE item_type = '" . $this->db->sql_escape($item_type) . "'"; +		$this->db->sql_query($sql); + +		$sql = 'DELETE FROM ' . $this->notification_types_table . " +			WHERE notification_type = '" . $this->db->sql_escape($item_type) . "'"; +		$this->db->sql_query($sql); +	} + +	/** +	* Enable all notifications of a certain type +	* +	* This should be called when an extension which has notification types +	* that was disabled is re-enabled so that all those notifications that +	* were hidden are shown again +	* +	* @param string $item_type Type identifier of the subscription +	*/ +	public function enable_notifications($item_type) +	{ +		$sql = 'UPDATE ' . $this->notification_types_table . " +			SET notification_type_enabled = 1 +			WHERE notification_type = '" . $this->db->sql_escape($item_type) . "'"; +		$this->db->sql_query($sql); +	} + +	/** +	* Delete all notifications older than a certain time +	* +	* @param int $timestamp Unix timestamp to delete all notifications that were created before +	*/ +	public function prune_notifications($timestamp) +	{ +		$sql = 'DELETE FROM ' . $this->notifications_table . ' +			WHERE notification_time < ' . (int) $timestamp; +		$this->db->sql_query($sql); +	} + +	/** +	* Helper to get the notifications item type class and set it up +	*/ +	public function get_item_type_class($item_type, $data = array()) +	{ +		$item_type = (strpos($item_type, 'notification.type.') === 0) ? $item_type : 'notification.type.' . $item_type; + +		$item = $this->load_object($item_type); + +		$item->set_initial_data($data); + +		return $item; +	} + +	/** +	* Helper to get the notifications method class and set it up +	*/ +	public function get_method_class($method_name) +	{ +		$method_name = (strpos($method_name, 'notification.method.') === 0) ? $method_name : 'notification.method.' . $method_name; + +		return $this->load_object($method_name); +	} + +	/** +	* Helper to load objects (notification types/methods) +	*/ +	protected function load_object($object_name) +	{ +		$object = $this->phpbb_container->get($object_name); +		 +		if (method_exists($object, 'set_notification_manager')) +		{ +			$object->set_notification_manager($this); +		} +		 +		return $object; +	} +} diff --git a/phpBB/includes/notification/method/base.php b/phpBB/includes/notification/method/base.php new file mode 100644 index 0000000000..22418c9be8 --- /dev/null +++ b/phpBB/includes/notification/method/base.php @@ -0,0 +1,116 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Base notifications method class +* @package notifications +*/ +abstract class phpbb_notification_method_base implements phpbb_notification_method_interface +{ +	/** @var phpbb_notification_manager */ +	protected $notification_manager; + +	/** @var phpbb_user_loader */ +	protected $user_loader; + +	/** @var phpbb_db_driver */ +	protected $db; + +	/** @var phpbb_cache_service */ +	protected $cache; + +	/** @var phpbb_template */ +	protected $template; + +	/** @var phpbb_extension_manager */ +	protected $extension_manager; + +	/** @var phpbb_user */ +	protected $user; + +	/** @var phpbb_auth */ +	protected $auth; + +	/** @var phpbb_config */ +	protected $config; + +	/** @var string */ +	protected $phpbb_root_path; + +	/** @var string */ +	protected $php_ext; + +	/** +	* Queue of messages to be sent +	* +	* @var array +	*/ +	protected $queue = array(); + +	/** +	* Notification Method Base Constructor +	*  +	* @param phpbb_user_loader $user_loader +	* @param phpbb_db_driver $db +	* @param phpbb_cache_driver_interface $cache +	* @param phpbb_user $user +	* @param phpbb_auth $auth +	* @param phpbb_config $config +	* @param string $phpbb_root_path +	* @param string $php_ext +	* @return phpbb_notification_method_base +	*/ +	public function __construct(phpbb_user_loader $user_loader, phpbb_db_driver $db, phpbb_cache_driver_interface $cache, $user, phpbb_auth $auth, phpbb_config $config, $phpbb_root_path, $php_ext) +	{ +		$this->user_loader = $user_loader; +		$this->db = $db; +		$this->cache = $cache; +		$this->user = $user; +		$this->auth = $auth; +		$this->config = $config; +		$this->phpbb_root_path = $phpbb_root_path; +		$this->php_ext = $php_ext; +	} + +	/** +	* Set notification manager (required) +	*  +	* @param phpbb_notification_manager $notification_manager +	*/ +	public function set_notification_manager(phpbb_notification_manager $notification_manager) +	{ +		$this->notification_manager = $notification_manager; +	} + +	/** +	* Add a notification to the queue +	* +	* @param phpbb_notification_type_interface $notification +	*/ +	public function add_to_queue(phpbb_notification_type_interface $notification) +	{ +		$this->queue[] = $notification; +	} + +	/** +	* Empty the queue +	*/ +	protected function empty_queue() +	{ +		$this->queue = array(); +	} +} diff --git a/phpBB/includes/notification/method/email.php b/phpBB/includes/notification/method/email.php new file mode 100644 index 0000000000..429dfda2ba --- /dev/null +++ b/phpBB/includes/notification/method/email.php @@ -0,0 +1,129 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Email notification method class +* This class handles sending emails for notifications +* +* @package notifications +*/ +class phpbb_notification_method_email extends phpbb_notification_method_base +{ +	/** +	* Get notification method name +	* +	* @return string +	*/ +	public function get_type() +	{ +		return 'email'; +	} + +	/** +	* Notify method (since jabber gets sent through the same messenger, we let the jabber class inherit from this to reduce code duplication) +	* +	* @var mixed +	*/ +	protected $notify_method = NOTIFY_EMAIL; + +	/** +	* Base directory to prepend to the email template name +	* +	* @var string +	*/ +	protected $email_template_base_dir = ''; + +	/** +	* Is this method available for the user? +	* This is checked on the notifications options +	*/ +	public function is_available() +	{ +		// Email is always available +		return true; +	} + +	/** +	* Parse the queue and notify the users +	*/ +	public function notify() +	{ +		if (!sizeof($this->queue)) +		{ +			return; +		} + +		// Load all users we want to notify (we need their email address) +		$user_ids = $users = array(); +		foreach ($this->queue as $notification) +		{ +			$user_ids[] = $notification->user_id; +		} + +		// We do not send emails to banned users +		if (!function_exists('phpbb_get_banned_user_ids')) +		{ +			include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext); +		} +		$banned_users = phpbb_get_banned_user_ids($user_ids); + +		// Load all the users we need +		$this->user_loader->load_users($user_ids); + +		// Load the messenger +		if (!class_exists('messenger')) +		{ +			include($this->phpbb_root_path . 'includes/functions_messenger.' . $this->php_ext); +		} +		$messenger = new messenger(); +		$board_url = generate_board_url(); + +		// Time to go through the queue and send emails +		foreach ($this->queue as $notification) +		{ +			if ($notification->get_email_template() === false) +			{ +				continue; +			} + +			$user = $this->user_loader->get_user($notification->user_id); + +			if ($user['user_type'] == USER_IGNORE || in_array($notification->user_id, $banned_users)) +			{ +				continue; +			} + +			$messenger->template($this->email_template_base_dir . $notification->get_email_template(), $user['user_lang']); + +			$messenger->to($user['user_email'], $user['username']); + +			$messenger->assign_vars(array_merge(array( +				'USERNAME'						=> $user['username'], + +				'U_NOTIFICATION_SETTINGS'		=> generate_board_url() . '/ucp.' . $this->php_ext . '?i=ucp_notifications', +			), $notification->get_email_template_variables())); + +			$messenger->send($this->notify_method); +		} + +		// Save the queue in the messenger class (has to be called or these emails could be lost?) +		$messenger->save_queue(); + +		// We're done, empty the queue +		$this->empty_queue(); +	} +} diff --git a/phpBB/includes/notification/method/interface.php b/phpBB/includes/notification/method/interface.php new file mode 100644 index 0000000000..ef875942cc --- /dev/null +++ b/phpBB/includes/notification/method/interface.php @@ -0,0 +1,48 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Base notifications method interface +* @package notifications +*/ +interface phpbb_notification_method_interface +{ +	/** +	* Get notification method name +	* +	* @return string +	*/ +	public function get_type(); + +	/** +	* Is this method available for the user? +	* This is checked on the notifications options +	*/ +	public function is_available(); + +	/** +	* Add a notification to the queue +	* +	* @param phpbb_notification_type_interface $notification +	*/ +	public function add_to_queue(phpbb_notification_type_interface $notification); + +	/** +	* Parse the queue and notify the users +	*/ +	public function notify(); +} diff --git a/phpBB/includes/notification/method/jabber.php b/phpBB/includes/notification/method/jabber.php new file mode 100644 index 0000000000..e3eb571fbc --- /dev/null +++ b/phpBB/includes/notification/method/jabber.php @@ -0,0 +1,77 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Jabber notification method class +* This class handles sending Jabber messages for notifications +* +* @package notifications +*/ +class phpbb_notification_method_jabber extends phpbb_notification_method_email +{ +	/** +	* Get notification method name +	* +	* @return string +	*/ +	public function get_type() +	{ +		return 'jabber'; +	} + +	/** +	* Notify method (since jabber gets sent through the same messenger, we let the jabber class inherit from this to reduce code duplication) +	* +	* @var mixed +	*/ +	protected $notify_method = NOTIFY_IM; + +	/** +	* Base directory to prepend to the email template name +	* +	* @var string +	*/ +	protected $email_template_base_dir = 'short/'; + +	/** +	* Is this method available for the user? +	* This is checked on the notifications options +	*/ +	public function is_available() +	{ +		return ($this->global_available() && $this->user->data['jabber']); +	} + +	/** +	* Is this method available at all? +	* This is checked before notifications are sent +	*/ +	public function global_available() +	{ +		return ($this->config['jab_enable'] && @extension_loaded('xml')); +	} + +	public function notify() +	{ +		if (!$this->global_available()) +		{ +			return; +		} + +		return parent::notify(); +	} +} diff --git a/phpBB/includes/notification/type/approve_post.php b/phpBB/includes/notification/type/approve_post.php new file mode 100644 index 0000000000..1a30781c35 --- /dev/null +++ b/phpBB/includes/notification/type/approve_post.php @@ -0,0 +1,140 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Post approved notifications class +* This class handles notifications for posts when they are approved (to their authors) +* +* @package notifications +*/ +class phpbb_notification_type_approve_post extends phpbb_notification_type_post +{ +	/** +	* Get notification type name +	* +	* @return string +	*/ +	public function get_type() +	{ +		return 'approve_post'; +	} + +	/** +	* Language key used to output the text +	* +	* @var string +	*/ +	protected $language_key = 'NOTIFICATION_POST_APPROVED'; + +	/** +	* Notification option data (for outputting to the user) +	* +	* @var bool|array False if the service should use it's default data +	* 					Array of data (including keys 'id', 'lang', and 'group') +	*/ +	public static $notification_option = array( +		'id'	=> 'moderation_queue', +		'lang'	=> 'NOTIFICATION_TYPE_MODERATION_QUEUE', +		'group'	=> 'NOTIFICATION_GROUP_POSTING', +	); + +	/** +	* Is available +	*/ +	public function is_available() +	{ +		return !$this->auth->acl_get('m_approve'); +	} + +	/** +	* Find the users who want to receive notifications +	* +	* @param array $post Data from +	* +	* @return array +	*/ +	public function find_users_for_notification($post, $options = array()) +	{ +		$options = array_merge(array( +			'ignore_users'		=> array(), +		), $options); + +		$users = array(); +		$users[$post['poster_id']] = array(''); + +		$auth_read = $this->auth->acl_get_list(array_keys($users), 'f_read', $post['forum_id']); + +		if (empty($auth_read)) +		{ +			return array(); +		} + +		return $this->check_user_notification_options($auth_read[$post['forum_id']]['f_read'], array_merge($options, array( +			'item_type'		=> self::$notification_option['id'], +		))); +	} + +	/** +	* Pre create insert array function +	* This allows you to perform certain actions, like run a query +	* and load data, before create_insert_array() is run. The data +	* returned from this function will be sent to create_insert_array(). +	* +	* @param array $post Post data from submit_post +	* @param array $notify_users Notify users list +	* 		Formated from find_users_for_notification() +	* @return array Whatever you want to send to create_insert_array(). +	*/ +	public function pre_create_insert_array($post, $notify_users) +	{ +		// In the parent class, this is used to check if the post is already +		// read by a user and marks the notification read if it was marked read. +		// Returning an empty array in effect, forces it to be marked as unread +		// (and also saves a query) +		return array(); +	} + +	/** +	* Function for preparing the data for insertion in an SQL query +	* (The service handles insertion) +	* +	* @param array $post Data from submit_post +	* @param array $pre_create_data Data from pre_create_insert_array() +	* +	* @return array Array of data ready to be inserted into the database +	*/ +	public function create_insert_array($post, $pre_create_data = array()) +	{ +		$this->set_data('post_subject', $post['post_subject']); + +		$data = parent::create_insert_array($post, $pre_create_data); + +		$this->notification_time = $data['notification_time'] = time(); + +		return $data; +	} + +	/** +	* Get email template +	* +	* @return string|bool +	*/ +	public function get_email_template() +	{ +		return 'post_approved'; +	} +} diff --git a/phpBB/includes/notification/type/approve_topic.php b/phpBB/includes/notification/type/approve_topic.php new file mode 100644 index 0000000000..e728e9ac30 --- /dev/null +++ b/phpBB/includes/notification/type/approve_topic.php @@ -0,0 +1,138 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Topic approved notifications class +* This class handles notifications for topics when they are approved (for authors) +* +* @package notifications +*/ +class phpbb_notification_type_approve_topic extends phpbb_notification_type_topic +{ +	/** +	* Get notification type name +	* +	* @return string +	*/ +	public function get_type() +	{ +		return 'approve_topic'; +	} +	 +	/** +	* Language key used to output the text +	* +	* @var string +	*/ +	protected $language_key = 'NOTIFICATION_TOPIC_APPROVED'; + +	/** +	* Notification option data (for outputting to the user) +	* +	* @var bool|array False if the service should use it's default data +	* 					Array of data (including keys 'id', 'lang', and 'group') +	*/ +	public static $notification_option = array( +		'id'	=> 'moderation_queue', +		'lang'	=> 'NOTIFICATION_TYPE_MODERATION_QUEUE', +		'group'	=> 'NOTIFICATION_GROUP_POSTING', +	); + +	/** +	* Is available +	*/ +	public function is_available() +	{ +		return !$this->auth->acl_get('m_approve'); +	} + +	/** +	* Find the users who want to receive notifications +	* +	* @param array $post Data from +	* +	* @return array +	*/ +	public function find_users_for_notification($post, $options = array()) +	{ +		$options = array_merge(array( +			'ignore_users'		=> array(), +		), $options); + +		$users = array(); +		$users[$post['poster_id']] = array(''); + +		$auth_read = $this->auth->acl_get_list(array_keys($users), 'f_read', $post['forum_id']); + +		if (empty($auth_read)) +		{ +			return array(); +		} + +		return $this->check_user_notification_options($auth_read[$post['forum_id']]['f_read'], array_merge($options, array( +			'item_type'		=> self::$notification_option['id'], +		))); +	} + +	/** +	* Pre create insert array function +	* This allows you to perform certain actions, like run a query +	* and load data, before create_insert_array() is run. The data +	* returned from this function will be sent to create_insert_array(). +	* +	* @param array $post Post data from submit_post +	* @param array $notify_users Notify users list +	* 		Formated from find_users_for_notification() +	* @return array Whatever you want to send to create_insert_array(). +	*/ +	public function pre_create_insert_array($post, $notify_users) +	{ +		// In the parent class, this is used to check if the post is already +		// read by a user and marks the notification read if it was marked read. +		// Returning an empty array in effect, forces it to be marked as unread +		// (and also saves a query) +		return array(); +	} + +	/** +	* Function for preparing the data for insertion in an SQL query +	* (The service handles insertion) +	* +	* @param array $post Data from submit_post +	* @param array $pre_create_data Data from pre_create_insert_array() +	* +	* @return array Array of data ready to be inserted into the database +	*/ +	public function create_insert_array($post, $pre_create_data = array()) +	{ +		$data = parent::create_insert_array($post, $pre_create_data); + +		$this->notification_time = $data['notification_time'] = time(); + +		return $data; +	} + +	/** +	* Get email template +	* +	* @return string|bool +	*/ +	public function get_email_template() +	{ +		return 'topic_approved'; +	} +} diff --git a/phpBB/includes/notification/type/base.php b/phpBB/includes/notification/type/base.php new file mode 100644 index 0000000000..600ef7c965 --- /dev/null +++ b/phpBB/includes/notification/type/base.php @@ -0,0 +1,479 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Base notifications class +* @package notifications +*/ +abstract class phpbb_notification_type_base implements phpbb_notification_type_interface +{ +	/** @var phpbb_notification_manager */ +	protected $notification_manager; + +	/** @var phpbb_user_loader */ +	protected $user_loader; + +	/** @var phpbb_db_driver */ +	protected $db; + +	/** @var phpbb_cache_service */ +	protected $cache; + +	/** @var phpbb_template */ +	protected $template; + +	/** @var phpbb_user */ +	protected $user; + +	/** @var phpbb_auth */ +	protected $auth; + +	/** @var phpbb_config */ +	protected $config; + +	/** @var string */ +	protected $phpbb_root_path; + +	/** @var string */ +	protected $php_ext; + +	/** @var string */ +	protected $notification_types_table; + +	/** @var string */ +	protected $notifications_table; + +	/** @var string */ +	protected $user_notifications_table; + +	/** +	* Notification option data (for outputting to the user) +	* +	* @var bool|array False if the service should use its default data +	* 					Array of data (including keys 'id', 'lang', and 'group') +	*/ +	public static $notification_option = false; + +	/** +	* Indentification data +	* item_type			- Type of the item (translates to the notification type) +	* item_id			- ID of the item (e.g. post_id, msg_id) +	* item_parent_id	- Parent item id (ex: for topic => forum_id, for post => topic_id, etc) +	* user_id +	* notification_read +	* notification_time +	* notification_data (special serialized field that each notification type can use to store stuff) +	* +	* @var array $data Notification row from the database +	* 		This must be private, all interaction should use __get(), __set(), get_data(), set_data() +	*/ +	private $data = array(); + +	/** +	* Notification Type Base Constructor +	*  +	* @param phpbb_user_loader $user_loader +	* @param phpbb_db_driver $db +	* @param phpbb_cache_driver_interface $cache +	* @param phpbb_user $user +	* @param phpbb_auth $auth +	* @param phpbb_config $config +	* @param string $phpbb_root_path +	* @param string $php_ext +	* @param string $notification_types_table +	* @param string $notifications_table +	* @param string $user_notifications_table +	* @return phpbb_notification_type_base +	*/ +	public function __construct(phpbb_user_loader $user_loader, phpbb_db_driver $db, phpbb_cache_driver_interface $cache, $user, phpbb_auth $auth, phpbb_config $config, $phpbb_root_path, $php_ext, $notification_types_table, $notifications_table, $user_notifications_table) +	{ +		$this->user_loader = $user_loader; +		$this->db = $db; +		$this->cache = $cache; +		$this->user = $user; +		$this->auth = $auth; +		$this->config = $config; + +		$this->phpbb_root_path = $phpbb_root_path; +		$this->php_ext = $php_ext; + +		$this->notification_types_table = $notification_types_table; +		$this->notifications_table = $notifications_table; +		$this->user_notifications_table = $user_notifications_table; +	} + +	/** +	* Set notification manager (required) +	*  +	* @param phpbb_notification_manager $notification_manager +	*/ +	public function set_notification_manager(phpbb_notification_manager $notification_manager) +	{ +		$this->notification_manager = $notification_manager; +	} + +	/** +	* Set initial data from the database +	* +	* @param array $data Row directly from the database +	*/ +	public function set_initial_data($data = array()) +	{ +		// The row from the database (unless this is a new notification we're going to add) +		$this->data = $data; +		$this->data['notification_data'] = (isset($this->data['notification_data'])) ? unserialize($this->data['notification_data']) : array(); +	} + +	/** +	* Magic method to get data from this notification +	*  +	* @param mixed $name +	* @return mixed +	*/ +	public function __get($name) +	{ +		return (!isset($this->data[$name])) ? null : $this->data[$name]; +	} + + +	/** +	* Magic method to set data on this notification +	*  +	* @param mixed $name +	* @return null +	*/ +	public function __set($name, $value) +	{ +		$this->data[$name] = $value; +	} + + +	/** +	* Magic method to get a string of this notification +	*  +	* Primarily for testing +	*  +	* @param string $name +	* @return mixed +	*/ +	public function __toString() +	{ +		return (!empty($this->data)) ? var_export($this->data, true) : $this->get_type(); +	} + +	/** +	* Get special data (only important for the classes that extend this) +	* +	* @param string $name Name of the variable to get +	* @return mixed +	*/ +	protected function get_data($name) +	{ +		return ($name === false) ? $this->data['notification_data'] : ((isset($this->data['notification_data'][$name])) ? $this->data['notification_data'][$name] : null); +	} + +	/** +	* Set special data (only important for the classes that extend this) +	* +	* @param string $name Name of the variable to set +	* @param mixed $value Value to set to the variable +	* @return mixed +	*/ +	protected function set_data($name, $value) +	{ +		$this->data['notification_data'][$name] = $value; +	} + +	/** +	* Function for preparing the data for insertion in an SQL query +	* (The service handles insertion) +	* +	* @param array $type_data Data unique to this notification type +	* @param array $pre_create_data Data from pre_create_insert_array() +	* @return array Array of data ready to be inserted into the database +	*/ +	public function create_insert_array($type_data, $pre_create_data = array()) +	{ +		// Defaults +		$this->data = array_merge(array( +			'item_id'				=> static::get_item_id($type_data), +			'item_type'	   			=> $this->get_type(), +			'item_parent_id'		=> static::get_item_parent_id($type_data), + +			'notification_time'		=> time(), +			'notification_read'		=> false, + +			'notification_data'					=> array(), +		), $this->data); + +		$data = $this->data; + +		$data['notification_data'] = serialize($data['notification_data']); + +		return $data; +	} + +	/** +	* Function for preparing the data for update in an SQL query +	* (The service handles insertion) +	* +	* @param array $type_data Data unique to this notification type +	* @return array Array of data ready to be updated in the database +	*/ +	public function create_update_array($type_data) +	{ +		$data = $this->create_insert_array($type_data); + +		// Unset data unique to each row +		unset( +			$data['notification_time'], // Also unsetting time, since it always tries to change the time to current (if you actually need to change the time, over-ride this function) +			$data['notification_id'], +			$data['notification_read'], +			$data['user_id'] +		); + +		return $data; +	} + +	/** +	* Mark this item read +	* +	* @param bool $return True to return a string containing the SQL code to update this item, False to execute it (Default: False) +	* @return string|null If $return is False, nothing will be returned, else the sql code to update this item +	*/ +	public function mark_read($return = false) +	{ +		return $this->mark(false, $return); +	} + +	/** +	* Mark this item unread +	* +	* @param bool $return True to return a string containing the SQL code to update this item, False to execute it (Default: False) +	* @return string|null If $return is False, nothing will be returned, else the sql code to update this item +	*/ +	public function mark_unread($return = false) +	{ +		return $this->mark(true, $return); +	} + +	/** +	* Prepare to output the notification to the template +	*  +	* @return array Template variables +	*/ +	public function prepare_for_display() +	{ +		if ($this->get_url()) +		{ +			$u_mark_read = append_sid($this->phpbb_root_path . 'index.' . $this->php_ext, 'mark_notification=' . $this->notification_id); +		} +		else +		{ +			$redirect = (($this->user->page['page_dir']) ? $this->user->page['page_dir'] . '/' : '') . $this->user->page['page_name'] . (($this->user->page['query_string']) ? '?' . $this->user->page['query_string'] : ''); + +			$u_mark_read = append_sid($this->phpbb_root_path . 'index.' . $this->php_ext, 'mark_notification=' . $this->notification_id . '&redirect=' . urlencode($redirect)); +		} + +		return array( +			'NOTIFICATION_ID'	=> $this->notification_id, + +			'AVATAR'			=> $this->get_avatar(), + +			'FORMATTED_TITLE'	=> $this->get_title(), + +			'URL'				=> $this->get_url(), +			'TIME'	   			=> $this->user->format_date($this->notification_time), + +			'UNREAD'			=> !$this->notification_read, + +			'U_MARK_READ'		=> (!$this->notification_read) ? $u_mark_read : '', +		); +	} + +	/** +	* -------------- Fall back functions ------------------- +	*/ + +	/** +	* URL to unsubscribe to this notification (fall back) +	* +	* @param string|bool $method Method name to unsubscribe from (email|jabber|etc), False to unsubscribe from all notifications for this item +	*/ +	public function get_unsubscribe_url($method = false) +	{ +		return false; +	} + +	/** +	* Get the user's avatar (fall back) +	*  +	* @return string +	*/ +	public function get_avatar() +	{ +		return ''; +	} + +	/** +	* Get the special items to load (fall back) +	*  +	* @return array +	*/ +	public function get_load_special() +	{ +		return array(); +	} + +	/** +	* Load the special items (fall back) +	*/ +	public function load_special($data, $notifications) +	{ +		return; +	} + +	/** +	* Is available (fall back) +	*  +	* @return bool +	*/ +	public function is_available() +	{ +		return true; +	} + +	/** +	* Pre create insert array function (fall back) +	*  +	* @return array +	*/ +	public function pre_create_insert_array($type_data, $notify_users) +	{ +		return array(); +	} + +	/** +	* -------------- Helper functions ------------------- +	*/ + +	/** +	* Find the users who want to receive notifications (helper) +	* +	* @param array $user_ids User IDs to check if they want to receive notifications +	* 		(Bool False to check all users besides anonymous and bots (USER_IGNORE)) +	* +	* @return array +	*/ +	protected function check_user_notification_options($user_ids = false, $options = array()) +	{ +		$options = array_merge(array( +			'ignore_users'		=> array(), +			'item_type'			=> $this->get_type(), +			'item_id'			=> 0, // Global by default +		), $options); + +		if ($user_ids === false) +		{ +			$user_ids = array(); + +			$sql = 'SELECT user_id +				FROM ' . USERS_TABLE . ' +				WHERE user_id <> ' . ANONYMOUS . ' +					AND user_type <> ' . USER_IGNORE; +			$result = $this->db->sql_query($sql); +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				$user_ids[] = $row['user_id']; +			} +			$this->db->sql_freeresult($result); +		} + +		if (empty($user_ids)) +		{ +			return array(); +		} + +		$rowset = $resulting_user_ids = array(); + +		$sql = 'SELECT user_id, method, notify +			FROM ' . $this->user_notifications_table . ' +			WHERE ' . $this->db->sql_in_set('user_id', $user_ids) . " +				AND item_type = '" . $this->db->sql_escape($options['item_type']) . "' +				AND item_id = " . (int) $options['item_id']; +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$resulting_user_ids[] = $row['user_id']; + +			if (!$row['notify'] || (isset($options['ignore_users'][$row['user_id']]) && in_array($row['method'], $options['ignore_users'][$row['user_id']]))) +			{ +				continue; +			} + +			if (!isset($rowset[$row['user_id']])) +			{ +				$rowset[$row['user_id']] = array(); +			} + +			$rowset[$row['user_id']][] = $row['method']; +		} + +		$this->db->sql_freeresult($result); + +		foreach ($user_ids as $user_id) +		{ +			if (!in_array($user_id, $resulting_user_ids) && !isset($options['ignore_users'][$user_id])) +			{ +				// No rows at all for this user, default to '' +				$rowset[$user_id] = array(''); +			} +		} + +		return $rowset; +	} + +	/** +	* Mark this item read/unread helper +	* +	* @param bool $unread Unread (True/False) (Default: False) +	* @param bool $return True to return a string containing the SQL code to update this item, False to execute it (Default: False) +	* @return string|null If $return is False, nothing will be returned, else the sql code to update this item +	*/ +	protected function mark($unread = true, $return = false) +	{ +		$this->notification_read = (bool) !$unread; + +		$where = array( +			"item_type = '" . $this->db->sql_escape($this->item_type) . "'", +			'item_id = ' . (int) $this->item_id, +			'user_id = ' . (int) $this->user_id, +		); +		$where = implode(' AND ', $where); + +		if ($return) +		{ +			return $where; +		} + +		$sql = 'UPDATE ' . $this->notifications_table . ' +			SET notification_read = ' . (int) $this->notification_read . ' +			WHERE ' . $where; +		$this->db->sql_query($sql); +	} +} diff --git a/phpBB/includes/notification/type/bookmark.php b/phpBB/includes/notification/type/bookmark.php new file mode 100644 index 0000000000..4e48a967d0 --- /dev/null +++ b/phpBB/includes/notification/type/bookmark.php @@ -0,0 +1,137 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Bookmark updating notifications class +* This class handles notifications for replies to a bookmarked topic +* +* @package notifications +*/ +class phpbb_notification_type_bookmark extends phpbb_notification_type_post +{ +	/** +	* Get notification type name +	* +	* @return string +	*/ +	public function get_type() +	{ +		return 'bookmark'; +	} + +	/** +	* Language key used to output the text +	* +	* @var string +	*/ +	protected $language_key = 'NOTIFICATION_BOOKMARK'; + +	/** +	* Notification option data (for outputting to the user) +	* +	* @var bool|array False if the service should use it's default data +	* 					Array of data (including keys 'id', 'lang', and 'group') +	*/ +	public static $notification_option = array( +		'lang'	=> 'NOTIFICATION_TYPE_BOOKMARK', +		'group'	=> 'NOTIFICATION_GROUP_POSTING', +	); + +	/** +	* Is available +	*/ +	public function is_available() +	{ +		return $this->config['allow_bookmarks']; +	} + +	/** +	* Find the users who want to receive notifications +	* +	* @param array $post Data from +	* +	* @return array +	*/ +	public function find_users_for_notification($post, $options = array()) +	{ +		$options = array_merge(array( +			'ignore_users'		=> array(), +		), $options); + +		$users = array(); + +		$sql = 'SELECT user_id +			FROM ' . BOOKMARKS_TABLE . ' +			WHERE ' . $this->db->sql_in_set('topic_id', $post['topic_id']) . ' +				AND user_id <> ' . (int) $post['poster_id']; +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$users[] = $row['user_id']; +		} +		$this->db->sql_freeresult($result); + +		if (empty($users)) +		{ +			return array(); +		} + +		$auth_read = $this->auth->acl_get_list($users, 'f_read', $post['forum_id']); + +		if (empty($auth_read)) +		{ +			return array(); +		} + +		$notify_users = $this->check_user_notification_options($auth_read[$post['forum_id']]['f_read'], $options); + +		// Try to find the users who already have been notified about replies and have not read the topic since and just update their notifications +		$update_notifications = array(); +		$sql = 'SELECT n.* +			FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . " nt +			WHERE n.item_type = '" . $this->get_type() . "' +				AND n.item_parent_id = " . (int) self::get_item_parent_id($post) . ' +				AND n.notification_read = 0 +				AND nt.notification_type = n.item_type +				AND nt.notification_type_enabled = 1'; +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			// Do not create a new notification +			unset($notify_users[$row['user_id']]); + +			$notification = $this->notification_manager->get_item_type_class($this->get_type(), $row); +			$sql = 'UPDATE ' . $this->notifications_table . ' +				SET ' . $this->db->sql_build_array('UPDATE', $notification->add_responders($post)) . ' +				WHERE notification_id = ' . $row['notification_id']; +			$this->db->sql_query($sql); +		} +		$this->db->sql_freeresult($result); + +		return $notify_users; +	} + +	/** +	* Get email template +	* +	* @return string|bool +	*/ +	public function get_email_template() +	{ +		return 'bookmark'; +	} +} diff --git a/phpBB/includes/notification/type/disapprove_post.php b/phpBB/includes/notification/type/disapprove_post.php new file mode 100644 index 0000000000..951c7e0254 --- /dev/null +++ b/phpBB/includes/notification/type/disapprove_post.php @@ -0,0 +1,120 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Post disapproved notifications class +* This class handles notifications for posts when they are disapproved (for authors) +* +* @package notifications +*/ +class phpbb_notification_type_disapprove_post extends phpbb_notification_type_approve_post +{ +	/** +	* Get notification type name +	* +	* @return string +	*/ +	public function get_type() +	{ +		return 'disapprove_post'; +	} + +	/** +	* Language key used to output the text +	* +	* @var string +	*/ +	protected $language_key = 'NOTIFICATION_POST_DISAPPROVED'; + +	/** +	* Notification option data (for outputting to the user) +	* +	* @var bool|array False if the service should use it's default data +	* 					Array of data (including keys 'id', 'lang', and 'group') +	*/ +	public static $notification_option = array( +		'id'	=> 'moderation_queue', +		'lang'	=> 'NOTIFICATION_TYPE_MODERATION_QUEUE', +		'group'	=> 'NOTIFICATION_GROUP_POSTING', +	); + +	/** +	* Get the HTML formatted title of this notification +	* +	* @return string +	*/ +	public function get_title() +	{ +		return $this->user->lang( +			$this->language_key, +			censor_text($this->get_data('topic_title')), +			$this->get_data('disapprove_reason') +		); +	} + +	/** +	* Get the url to this item +	* +	* @return string URL +	*/ +	public function get_url() +	{ +		return ''; +	} + +	/** +	* Get email template variables +	* +	* @return array +	*/ +	public function get_email_template_variables() +	{ +		return array_merge(parent::get_email_template_variables(), array( +			'REASON'	=> htmlspecialchars_decode($this->get_data('disapprove_reason')), +		)); +	} + +	/** +	* Function for preparing the data for insertion in an SQL query +	* (The service handles insertion) +	* +	* @param array $post Data from submit_post +	* @param array $pre_create_data Data from pre_create_insert_array() +	* +	* @return array Array of data ready to be inserted into the database +	*/ +	public function create_insert_array($post, $pre_create_data = array()) +	{ +		$this->set_data('disapprove_reason', $post['disapprove_reason']); + +		$data = parent::create_insert_array($post); + +		$this->notification_time = $data['notification_time'] = time(); + +		return $data; +	} + +	/** +	* Get email template +	* +	* @return string|bool +	*/ +	public function get_email_template() +	{ +		return 'post_disapproved'; +	} +} diff --git a/phpBB/includes/notification/type/disapprove_topic.php b/phpBB/includes/notification/type/disapprove_topic.php new file mode 100644 index 0000000000..038e528797 --- /dev/null +++ b/phpBB/includes/notification/type/disapprove_topic.php @@ -0,0 +1,120 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Topic disapproved notifications class +* This class handles notifications for topics when they are disapproved (for authors) +* +* @package notifications +*/ +class phpbb_notification_type_disapprove_topic extends phpbb_notification_type_approve_topic +{ +	/** +	* Get notification type name +	* +	* @return string +	*/ +	public function get_type() +	{ +		return 'disapprove_topic'; +	} + +	/** +	* Language key used to output the text +	* +	* @var string +	*/ +	protected $language_key = 'NOTIFICATION_TOPIC_DISAPPROVED'; + +	/** +	* Notification option data (for outputting to the user) +	* +	* @var bool|array False if the service should use it's default data +	* 					Array of data (including keys 'id', 'lang', and 'group') +	*/ +	public static $notification_option = array( +		'id'	=> 'moderation_queue', +		'lang'	=> 'NOTIFICATION_TYPE_MODERATION_QUEUE', +		'group'	=> 'NOTIFICATION_GROUP_POSTING', +	); + +	/** +	* Get the HTML formatted title of this notification +	* +	* @return string +	*/ +	public function get_title() +	{ +		return $this->user->lang( +			$this->language_key, +			censor_text($this->get_data('topic_title')), +			$this->get_data('disapprove_reason') +		); +	} + +	/** +	* Get the url to this item +	* +	* @return string URL +	*/ +	public function get_url() +	{ +		return ''; +	} + +	/** +	* Get email template variables +	* +	* @return array +	*/ +	public function get_email_template_variables() +	{ +		return array_merge(parent::get_email_template_variables(), array( +			'REASON'			=> htmlspecialchars_decode($this->get_data('disapprove_reason')), +		)); +	} + +	/** +	* Function for preparing the data for insertion in an SQL query +	* (The service handles insertion) +	* +	* @param array $post Data from submit_post +	* @param array $pre_create_data Data from pre_create_insert_array() +	* +	* @return array Array of data ready to be inserted into the database +	*/ +	public function create_insert_array($post, $pre_create_data = array()) +	{ +		$this->set_data('disapprove_reason', $post['disapprove_reason']); + +		$data = parent::create_insert_array($post, $pre_create_data); + +		$this->notification_time = $data['notification_time'] = time(); + +		return $data; +	} + +	/** +	* Get email template +	* +	* @return string|bool +	*/ +	public function get_email_template() +	{ +		return 'topic_disapproved'; +	} +} diff --git a/phpBB/includes/notification/type/interface.php b/phpBB/includes/notification/type/interface.php new file mode 100644 index 0000000000..a40fdafd09 --- /dev/null +++ b/phpBB/includes/notification/type/interface.php @@ -0,0 +1,189 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Base notifications interface +* @package notifications +*/ +interface phpbb_notification_type_interface +{ +	/** +	* Get notification type name +	* +	* @return string +	*/ +	public function get_type(); +	 +	/** +	* Set initial data from the database +	* +	* @param array $data Row directly from the database +	*/ +	public function set_initial_data($data); + +	/** +	* Get the id of the item +	* +	* @param array $type_data The type specific data +	*/ +	public static function get_item_id($type_data); + +	/** +	* Get the id of the parent +	* +	* @param array $type_data The type specific data +	*/ +	public static function get_item_parent_id($type_data); + +	/** +	* Is this type available to the current user (defines whether or not it will be shown in the UCP Edit notification options) +	* +	* @return bool True/False whether or not this is available to the user +	*/ +	public function is_available(); + +	/** +	* Find the users who want to receive notifications +	* +	* @param array $type_data The type specific data +	* @param array $options Options for finding users for notification +	* 		ignore_users => array of users and user types that should not receive notifications from this type because they've already been notified +	* 						e.g.: array(2 => array(''), 3 => array('', 'email'), ...) +	* +	* @return array +	*/ +	public function find_users_for_notification($type_data, $options); + +	/** +	* Users needed to query before this notification can be displayed +	* +	* @return array Array of user_ids +	*/ +	public function users_to_query(); + +	/** +	* Get the special items to load +	* +	* @return array Data will be combined sent to load_special() so you can run a single query and get data required for this notification type +	*/ +	public function get_load_special(); + +	/** +	* Load the special items +	* +	* @param array $data Data from get_load_special() +	* @param array $notifications Array of notifications (key is notification_id, value is the notification objects) +	*/ +	public function load_special($data, $notifications); + +	/** +	* Get the HTML formatted title of this notification +	* +	* @return string +	*/ +	public function get_title(); + +	/** +	* Get the url to this item +	* +	* @return string URL +	*/ +	public function get_url(); + +	/** +	* URL to unsubscribe to this notification +	* +	* @param string|bool $method Method name to unsubscribe from (email|jabber|etc), False to unsubscribe from all notifications for this item +	*/ +	public function get_unsubscribe_url($method); + +	/** +	* Get the user's avatar (the user who caused the notification typically) +	* +	* @return string +	*/ +	public function get_avatar(); + +	/** +	* Prepare to output the notification to the template +	*/ +	public function prepare_for_display(); + +	/** +	* Get email template +	* +	* @return string|bool +	*/ +	public function get_email_template(); + +	/** +	* Get email template variables +	* +	* @return array +	*/ +	public function get_email_template_variables(); + +	/** +	* Pre create insert array function +	* This allows you to perform certain actions, like run a query +	* and load data, before create_insert_array() is run. The data +	* returned from this function will be sent to create_insert_array(). +	* +	* @param array $type_data The type specific data +	* @param array $notify_users Notify users list +	* 		Formated from find_users_for_notification() +	* @return array Whatever you want to send to create_insert_array(). +	*/ +	public function pre_create_insert_array($type_data, $notify_users); + +	/** +	* Function for preparing the data for insertion in an SQL query +	* (The service handles insertion) +	* +	* @param array $type_data The type specific data +	* @param array $pre_create_data Data from pre_create_insert_array() +	* +	* @return array Array of data ready to be inserted into the database +	*/ +	public function create_insert_array($type_data, $pre_create_data); + +	/** +	* Function for preparing the data for update in an SQL query +	* (The service handles insertion) +	* +	* @param array $type_data Data unique to this notification type +	* +	* @return array Array of data ready to be updated in the database +	*/ +	public function create_update_array($type_data); + +	/** +	* Mark this item read +	* +	* @param bool $return True to return a string containing the SQL code to update this item, False to execute it (Default: False) +	* @return string +	*/ +	public function mark_read($return); + +	/** +	* Mark this item unread +	* +	* @param bool $return True to return a string containing the SQL code to update this item, False to execute it (Default: False) +	* @return string +	*/ +	public function mark_unread($return); +} diff --git a/phpBB/includes/notification/type/pm.php b/phpBB/includes/notification/type/pm.php new file mode 100644 index 0000000000..b3db7ad5ad --- /dev/null +++ b/phpBB/includes/notification/type/pm.php @@ -0,0 +1,184 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Private message notifications class +* This class handles notifications for private messages +* +* @package notifications +*/ +class phpbb_notification_type_pm extends phpbb_notification_type_base +{ +	/** +	* Get notification type name +	* +	* @return string +	*/ +	public function get_type() +	{ +		return 'pm'; +	} + +	/** +	* Notification option data (for outputting to the user) +	* +	* @var bool|array False if the service should use it's default data +	* 					Array of data (including keys 'id', 'lang', and 'group') +	*/ +	public static $notification_option = array( +		'lang'	=> 'NOTIFICATION_TYPE_PM', +	); + +	/** +	* Is available +	*/ +	public function is_available() +	{ +		return ($this->config['allow_privmsg'] && $this->auth->acl_get('u_readpm')); +	} + +	/** +	* Get the id of the +	* +	* @param array $pm The data from the private message +	*/ +	public static function get_item_id($pm) +	{ +		return (int) $pm['msg_id']; +	} + +	/** +	* Get the id of the parent +	* +	* @param array $pm The data from the pm +	*/ +	public static function get_item_parent_id($pm) +	{ +		// No parent +		return 0; +	} + +	/** +	* Find the users who want to receive notifications +	* +	* @param array $pm Data from +	* +	* @return array +	*/ +	public function find_users_for_notification($pm, $options = array()) +	{ +		$options = array_merge(array( +			'ignore_users'		=> array(), +		), $options); + +		if (!sizeof($pm['recipients'])) +		{ +			return array(); +		} + +		unset($pm['recipients'][$pm['from_user_id']]); + +		$this->user_loader->load_users(array_keys($pm['recipients'])); + +		return $this->check_user_notification_options(array_keys($pm['recipients']), $options); +	} + +	/** +	* Get the user's avatar +	*/ +	public function get_avatar() +	{ +		return $this->user_loader->get_avatar($this->get_data('from_user_id')); +	} + +	/** +	* Get the HTML formatted title of this notification +	* +	* @return string +	*/ +	public function get_title() +	{ +		$username = $this->user_loader->get_username($this->get_data('from_user_id'), 'no_profile'); + +		return $this->user->lang('NOTIFICATION_PM', $username, $this->get_data('message_subject')); +	} + +	/** +	* Get email template +	* +	* @return string|bool +	*/ +	public function get_email_template() +	{ +		return 'privmsg_notify'; +	} + +	/** +	* Get email template variables +	* +	* @return array +	*/ +	public function get_email_template_variables() +	{ +		$user_data = $this->user_loader->get_user($this->get_data('from_user_id')); + +		return array( +			'AUTHOR_NAME'				=> htmlspecialchars_decode($user_data['username']), +			'SUBJECT'					=> htmlspecialchars_decode(censor_text($this->get_data('message_subject'))), + +			'U_VIEW_MESSAGE'			=> generate_board_url() . '/ucp.' . $this->php_ext . "?i=pm&mode=view&p={$this->item_id}", +		); +	} + +	/** +	* Get the url to this item +	* +	* @return string URL +	*/ +	public function get_url() +	{ +		return append_sid($this->phpbb_root_path . 'ucp.' . $this->php_ext, "i=pm&mode=view&p={$this->item_id}"); +	} + +	/** +	* Users needed to query before this notification can be displayed +	* +	* @return array Array of user_ids +	*/ +	public function users_to_query() +	{ +		return array($this->get_data('from_user_id')); +	} + +	/** +	* Function for preparing the data for insertion in an SQL query +	* (The service handles insertion) +	* +	* @param array $post Data from submit_post +	* @param array $pre_create_data Data from pre_create_insert_array() +	* +	* @return array Array of data ready to be inserted into the database +	*/ +	public function create_insert_array($pm, $pre_create_data = array()) +	{ +		$this->set_data('from_user_id', $pm['from_user_id']); + +		$this->set_data('message_subject', $pm['message_subject']); + +		return parent::create_insert_array($pm, $pre_create_data); +	} +} diff --git a/phpBB/includes/notification/type/post.php b/phpBB/includes/notification/type/post.php new file mode 100644 index 0000000000..ddfa720e5e --- /dev/null +++ b/phpBB/includes/notification/type/post.php @@ -0,0 +1,370 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Post notifications class +* This class handles notifications for replies to a topic +* +* @package notifications +*/ +class phpbb_notification_type_post extends phpbb_notification_type_base +{ +	/** +	* Get notification type name +	* +	* @return string +	*/ +	public function get_type() +	{ +		return 'post'; +	} + +	/** +	* Language key used to output the text +	* +	* @var string +	*/ +	protected $language_key = 'NOTIFICATION_POST'; + +	/** +	* Notification option data (for outputting to the user) +	* +	* @var bool|array False if the service should use it's default data +	* 					Array of data (including keys 'id', 'lang', and 'group') +	*/ +	public static $notification_option = array( +		'lang'	=> 'NOTIFICATION_TYPE_POST', +		'group'	=> 'NOTIFICATION_GROUP_POSTING', +	); + +	/** +	* Is available +	*/ +	public function is_available() +	{ +		return $this->config['allow_topic_notify']; +	} + +	/** +	* Get the id of the item +	* +	* @param array $post The data from the post +	*/ +	public static function get_item_id($post) +	{ +		return (int) $post['post_id']; +	} + +	/** +	* Get the id of the parent +	* +	* @param array $post The data from the post +	*/ +	public static function get_item_parent_id($post) +	{ +		return (int) $post['topic_id']; +	} + +	/** +	* Find the users who want to receive notifications +	* +	* @param array $post Data from +	* +	* @return array +	*/ +	public function find_users_for_notification($post, $options = array()) +	{ +		$options = array_merge(array( +			'ignore_users'		=> array(), +		), $options); + +		$users = array(); + +		$sql = 'SELECT user_id +			FROM ' . TOPICS_WATCH_TABLE . ' +			WHERE topic_id = ' . (int) $post['topic_id'] . ' +				AND notify_status = ' . NOTIFY_YES . ' +				AND user_id <> ' . (int) $post['poster_id']; +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$users[] = $row['user_id']; +		} +		$this->db->sql_freeresult($result); + +		if (empty($users)) +		{ +			return array(); +		} + +		$auth_read = $this->auth->acl_get_list($users, 'f_read', $post['forum_id']); + +		if (empty($auth_read)) +		{ +			return array(); +		} + +		$notify_users = $this->check_user_notification_options($auth_read[$post['forum_id']]['f_read'], $options); + +		// Try to find the users who already have been notified about replies and have not read the topic since and just update their notifications +		$update_notifications = array(); +		$sql = 'SELECT n.* +			FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . " nt +			WHERE n.item_type = '" . $this->get_type() . "' +				AND n.item_parent_id = " . (int) self::get_item_parent_id($post) . ' +				AND n.notification_read = 0 +				AND nt.notification_type = n.item_type +				AND nt.notification_type_enabled = 1'; +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			// Do not create a new notification +			unset($notify_users[$row['user_id']]); + +			$notification = $this->notification_manager->get_item_type_class($this->get_type(), $row); +			$sql = 'UPDATE ' . $this->notifications_table . ' +				SET ' . $this->db->sql_build_array('UPDATE', $notification->add_responders($post)) . ' +				WHERE notification_id = ' . $row['notification_id']; +			$this->db->sql_query($sql); +		} +		$this->db->sql_freeresult($result); + +		return $notify_users; +	} + +	/** +	* Get the user's avatar +	*/ +	public function get_avatar() +	{ +		return $this->user_loader->get_avatar($this->get_data('poster_id')); +	} + +	/** +	* Get the HTML formatted title of this notification +	* +	* @return string +	*/ +	public function get_title() +	{ +		$responders = $this->get_data('responders'); +		$usernames = array(); + +		if (!is_array($responders)) +		{ +			$responders = array(); +		} + +		$responders = array_merge(array(array( +			'poster_id'		=> $this->get_data('poster_id'), +			'username'		=> $this->get_data('post_username'), +		)), $responders); + +		foreach ($responders as $responder) +		{ +			if ($responder['username']) +			{ +				$usernames[] = $responder['username']; +			} +			else +			{ +				$usernames[] = $this->user_loader->get_username($responder['poster_id'], 'no_profile'); +			} +		} + +		return $this->user->lang( +			$this->language_key, +			implode(', ', $usernames), +			censor_text($this->get_data('topic_title')) +		); +	} + +	/** +	* Get email template +	* +	* @return string|bool +	*/ +	public function get_email_template() +	{ +		return 'topic_notify'; +	} + +	/** +	* Get email template variables +	* +	* @return array +	*/ +	public function get_email_template_variables() +	{ +		if ($this->get_data('post_username')) +		{ +			$username = $this->get_data('post_username'); +		} +		else +		{ +			$username = $this->user_loader->get_username($this->get_data('poster_id'), 'no_profile'); +		} + +		return array( +			'AUTHOR_NAME'				=> htmlspecialchars_decode($username), +			'POST_SUBJECT'				=> htmlspecialchars_decode(censor_text($this->get_data('post_subject'))), +			'TOPIC_TITLE'				=> htmlspecialchars_decode(censor_text($this->get_data('topic_title'))), + +			'U_VIEW_POST'				=> generate_board_url() . "/viewtopic.{$this->php_ext}?p={$this->item_id}#p{$this->item_id}", +			'U_NEWEST_POST'				=> generate_board_url() . "/viewtopic.{$this->php_ext}?f={$this->get_data('forum_id')}&t={$this->item_parent_id}&view=unread#unread", +			'U_TOPIC'					=> generate_board_url() . "/viewtopic.{$this->php_ext}?f={$this->get_data('forum_id')}&t={$this->item_parent_id}", +			'U_VIEW_TOPIC'				=> generate_board_url() . "/viewtopic.{$this->php_ext}?f={$this->get_data('forum_id')}&t={$this->item_parent_id}", +			'U_FORUM'					=> generate_board_url() . "/viewforum.{$this->php_ext}?f={$this->get_data('forum_id')}", +			'U_STOP_WATCHING_TOPIC'		=> generate_board_url() . "/viewtopic.{$this->php_ext}?uid={$this->user_id}&f={$this->get_data('forum_id')}&t={$this->item_parent_id}&unwatch=topic", +		); +	} + +	/** +	* Get the url to this item +	* +	* @return string URL +	*/ +	public function get_url() +	{ +		return append_sid($this->phpbb_root_path . 'viewtopic.' . $this->php_ext, "p={$this->item_id}#p{$this->item_id}"); +	} + +	/** +	* Users needed to query before this notification can be displayed +	* +	* @return array Array of user_ids +	*/ +	public function users_to_query() +	{ +		$responders = $this->get_data('responders'); +		$users = array( +			$this->get_data('poster_id'), +		); + +		if (is_array($responders)) +		{ +			foreach ($responders as $responder) +			{ +				$users[] = $responder['poster_id']; +			} +		} + +		return $users; +	} + +	/** +	* Pre create insert array function +	* This allows you to perform certain actions, like run a query +	* and load data, before create_insert_array() is run. The data +	* returned from this function will be sent to create_insert_array(). +	* +	* @param array $post Post data from submit_post +	* @param array $notify_users Notify users list +	* 		Formated from find_users_for_notification() +	* @return array Whatever you want to send to create_insert_array(). +	*/ +	public function pre_create_insert_array($post, $notify_users) +	{ +		if (!sizeof($notify_users)) +		{ +			return array(); +		} + +		$tracking_data = array(); +		$sql = 'SELECT user_id, mark_time FROM ' . TOPICS_TRACK_TABLE . ' +			WHERE topic_id = ' . (int) $post['topic_id'] . ' +				AND ' . $this->db->sql_in_set('user_id', array_keys($notify_users)); +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$tracking_data[$row['user_id']] = $row['mark_time']; +		} + +		return $tracking_data; +	} + +	/** +	* Function for preparing the data for insertion in an SQL query +	* (The service handles insertion) +	* +	* @param array $post Data from submit_post +	* @param array $pre_create_data Data from pre_create_insert_array() +	* +	* @return array Array of data ready to be inserted into the database +	*/ +	public function create_insert_array($post, $pre_create_data = array()) +	{ +		$this->set_data('poster_id', $post['poster_id']); + +		$this->set_data('topic_title', $post['topic_title']); + +		$this->set_data('post_subject', $post['post_subject']); + +		$this->set_data('post_username', (($post['poster_id'] == ANONYMOUS) ? $post['post_username'] : '')); + +		$this->set_data('forum_id', $post['forum_id']); + +		$this->set_data('forum_name', $post['forum_name']); + +		$this->notification_time = $post['post_time']; + +		// Topics can be "read" before they are public (while awaiting approval). +		// Make sure that if the user has read the topic, it's marked as read in the notification +		if (isset($pre_create_data[$this->user_id]) && $pre_create_data[$this->user_id] >= $this->notification_time) +		{ +			$this->notification_read = true; +		} + +		return parent::create_insert_array($post, $pre_create_data); +	} + +	/** +	* Add responders to the notification +	* +	* @param mixed $post +	*/ +	public function add_responders($post) +	{ +		// Do not add them as a responder if they were the original poster that created the notification +		if ($this->get_data('poster_id') == $post['poster_id']) +		{ +			return array('notification_data' => serialize($this->get_data(false))); +		} + +		$responders = $this->get_data('responders'); + +		$responders = ($responders === null) ? array() : $responders; + +		foreach ($responders as $responder) +		{ +			// Do not add them as a responder multiple times +			if ($responder['poster_id'] == $post['poster_id']) +			{ +				return array('notification_data' => serialize($this->get_data(false))); +			} +		} + +		$responders[] = array( +			'poster_id'		=> $post['poster_id'], +			'username'		=> (($post['poster_id'] == ANONYMOUS) ? $post['post_username'] : ''), +		); + +		$this->set_data('responders', $responders); + +		return array('notification_data' => serialize($this->get_data(false))); +	} +} diff --git a/phpBB/includes/notification/type/post_in_queue.php b/phpBB/includes/notification/type/post_in_queue.php new file mode 100644 index 0000000000..1c29bee3cd --- /dev/null +++ b/phpBB/includes/notification/type/post_in_queue.php @@ -0,0 +1,137 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Post in queue notifications class +* This class handles notifications for posts that are put in the moderation queue (for moderators) +* +* @package notifications +*/ +class phpbb_notification_type_post_in_queue extends phpbb_notification_type_post +{ +	/** +	* Get notification type name +	* +	* @return string +	*/ +	public function get_type() +	{ +		return 'post_in_queue'; +	} + +	/** +	* Language key used to output the text +	* +	* @var string +	*/ +	protected $language_key = 'NOTIFICATION_POST_IN_QUEUE'; + +	/** +	* Notification option data (for outputting to the user) +	* +	* @var bool|array False if the service should use it's default data +	* 					Array of data (including keys 'id', 'lang', and 'group') +	*/ +	public static $notification_option = array( +		'id'	=> 'needs_approval', +		'lang'	=> 'NOTIFICATION_TYPE_IN_MODERATION_QUEUE', +		'group'	=> 'NOTIFICATION_GROUP_MODERATION', +	); + +	/** +	* Permission to check for (in find_users_for_notification) +	* +	* @var string Permission name +	*/ +	protected $permission = 'm_approve'; + +	/** +	* Is available +	*/ +	public function is_available() +	{ +		$m_approve = $this->auth->acl_getf($this->permission, true); + +		return (!empty($m_approve)); +	} + +	/** +	* Find the users who want to receive notifications +	* +	* @param array $post Data from the post +	* +	* @return array +	*/ +	public function find_users_for_notification($post, $options = array()) +	{ +		$options = array_merge(array( +			'ignore_users'		=> array(), +		), $options); + +		// 0 is for global +		$auth_approve = $this->auth->acl_get_list(false, $this->permission, array($post['forum_id'], 0)); + +		if (empty($auth_approve)) +		{ +			return array(); +		} + +		$auth_approve[$post['forum_id']] = array_unique(array_merge($auth_approve[$post['forum_id']], $auth_approve[0])); + +		return $this->check_user_notification_options($auth_approve[$post['forum_id']][$this->permission], array_merge($options, array( +			'item_type'		=> self::$notification_option['id'], +		))); +	} + +	/** +	* Get the url to this item +	* +	* @return string URL +	*/ +	public function get_url() +	{ +		return append_sid($this->phpbb_root_path . 'mcp.' . $this->php_ext, "i=queue&mode=approve_details&f={$this->get_data('forum_id')}&p={$this->item_id}"); +	} + +	/** +	* Function for preparing the data for insertion in an SQL query +	* (The service handles insertion) +	* +	* @param array $post Data from submit_post +	* @param array $pre_create_data Data from pre_create_insert_array() +	* +	* @return array Array of data ready to be inserted into the database +	*/ +	public function create_insert_array($post, $pre_create_data = array()) +	{ +		$data = parent::create_insert_array($post, $pre_create_data); + +		$this->notification_time = $data['notification_time'] = time(); + +		return $data; +	} + +	/** +	* Get email template +	* +	* @return string|bool +	*/ +	public function get_email_template() +	{ +		return 'post_in_queue'; +	} +} diff --git a/phpBB/includes/notification/type/quote.php b/phpBB/includes/notification/type/quote.php new file mode 100644 index 0000000000..5453b267c8 --- /dev/null +++ b/phpBB/includes/notification/type/quote.php @@ -0,0 +1,221 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Post quoting notifications class +* This class handles notifications for quoting users in a post +* +* @package notifications +*/ +class phpbb_notification_type_quote extends phpbb_notification_type_post +{ +	/** +	* Get notification type name +	* +	* @return string +	*/ +	public function get_type() +	{ +		return 'quote'; +	} + +	/** +	* regular expression to match to find usernames +	* +	* @var string +	*/ +	protected static $regular_expression_match = '#\[quote="(.+?)"#'; + +	/** +	* Language key used to output the text +	* +	* @var string +	*/ +	protected $language_key = 'NOTIFICATION_QUOTE'; + +	/** +	* Notification option data (for outputting to the user) +	* +	* @var bool|array False if the service should use it's default data +	* 					Array of data (including keys 'id', 'lang', and 'group') +	*/ +	public static $notification_option = array( +		'lang'	=> 'NOTIFICATION_TYPE_QUOTE', +		'group'	=> 'NOTIFICATION_GROUP_POSTING', +	); + +	/** +	* Is available +	*/ +	public function is_available() +	{ +		return true; +	} + +	/** +	* Find the users who want to receive notifications +	* +	* @param array $post Data from +	* +	* @return array +	*/ +	public function find_users_for_notification($post, $options = array()) +	{ +		$options = array_merge(array( +			'ignore_users'		=> array(), +		), $options); + +		$usernames = false; +		preg_match_all(self::$regular_expression_match, $post['post_text'], $usernames); + +		if (empty($usernames[1])) +		{ +			return array(); +		} + +		$usernames[1] = array_unique($usernames[1]); + +		$usernames = array_map('utf8_clean_string', $usernames[1]); + +		$users = array(); + +		$sql = 'SELECT user_id +			FROM ' . USERS_TABLE . ' +			WHERE ' . $this->db->sql_in_set('username_clean', $usernames) . ' +				AND user_id <> ' . (int) $post['poster_id']; +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$users[] = $row['user_id']; +		} +		$this->db->sql_freeresult($result); + +		if (empty($users)) +		{ +			return array(); +		} + +		$auth_read = $this->auth->acl_get_list($users, 'f_read', $post['forum_id']); + +		if (empty($auth_read)) +		{ +			return array(); +		} + +		$notify_users = $this->check_user_notification_options($auth_read[$post['forum_id']]['f_read'], $options); + +		// Try to find the users who already have been notified about replies and have not read the topic since and just update their notifications +		$update_notifications = array(); +		$sql = 'SELECT n.* +			FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . " nt +			WHERE n.item_type = '" . $this->get_type() . "' +				AND n.item_parent_id = " . (int) self::get_item_parent_id($post) . ' +				AND n.notification_read = 0 +				AND nt.notification_type = n.item_type +				AND nt.notification_type_enabled = 1'; +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			// Do not create a new notification +			unset($notify_users[$row['user_id']]); + +			$notification = $this->notification_manager->get_item_type_class($this->get_type(), $row); +			$sql = 'UPDATE ' . $this->notifications_table . ' +				SET ' . $this->db->sql_build_array('UPDATE', $notification->add_responders($post)) . ' +				WHERE notification_id = ' . $row['notification_id']; +			$this->db->sql_query($sql); +		} +		$this->db->sql_freeresult($result); + +		return $notify_users; +	} + +	/** +	* Update a notification +	* +	* @param array $data Data specific for this type that will be updated +	*/ +	public function update_notifications($post) +	{ +		$old_notifications = array(); +		$sql = 'SELECT n.user_id +			FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . " nt +			WHERE n.item_type = '" . $this->get_type() . "' +				AND n.item_id = " . self::get_item_id($post) . ' +				AND nt.notification_type = n.item_type +				AND nt.notification_type_enabled = 1'; +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$old_notifications[] = $row['user_id']; +		} +		$this->db->sql_freeresult($result); + +		// Find the new users to notify +		$notifications = $this->find_users_for_notification($post); + +		// Find the notifications we must delete +		$remove_notifications = array_diff($old_notifications, array_keys($notifications)); + +		// Find the notifications we must add +		$add_notifications = array(); +		foreach (array_diff(array_keys($notifications), $old_notifications) as $user_id) +		{ +			$add_notifications[$user_id] = $notifications[$user_id]; +		} + +		// Add the necessary notifications +		$this->notification_manager->add_notifications_for_users($this->get_type(), $post, $add_notifications); + +		// Remove the necessary notifications +		if (!empty($remove_notifications)) +		{ +			$sql = 'DELETE FROM ' . $this->notifications_table . " +				WHERE item_type = '" . $this->get_type() . "' +					AND item_id = " . self::get_item_id($post) . ' +					AND ' . $this->db->sql_in_set('user_id', $remove_notifications); +			$this->db->sql_query($sql); +		} + +		// return true to continue with the update code in the notifications service (this will update the rest of the notifications) +		return true; +	} + +	/** +	* Get email template +	* +	* @return string|bool +	*/ +	public function get_email_template() +	{ +		return 'quote'; +	} + +	/** +	* Get email template variables +	* +	* @return array +	*/ +	public function get_email_template_variables() +	{ +		$user_data = $this->user_loader->get_user($this->get_data('poster_id')); + +		return array_merge(parent::get_email_template_variables(), array( +			'AUTHOR_NAME'		=> htmlspecialchars_decode($user_data['username']), +		)); +	} +} diff --git a/phpBB/includes/notification/type/report_pm.php b/phpBB/includes/notification/type/report_pm.php new file mode 100644 index 0000000000..3fa73bab41 --- /dev/null +++ b/phpBB/includes/notification/type/report_pm.php @@ -0,0 +1,229 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Private message reproted notifications class +* This class handles notifications for private messages when they are reported +* +* @package notifications +*/ +class phpbb_notification_type_report_pm extends phpbb_notification_type_pm +{ +	/** +	* Get notification type name +	* +	* @return string +	*/ +	public function get_type() +	{ +		return 'report_pm'; +	} + +	/** +	* Language key used to output the text +	* +	* @var string +	*/ +	protected $language_key = 'NOTIFICATION_REPORT_PM'; + +	/** +	* Permission to check for (in find_users_for_notification) +	* +	* @var string Permission name +	*/ +	protected $permission = 'm_report'; + +	/** +	* Notification option data (for outputting to the user) +	* +	* @var bool|array False if the service should use it's default data +	* 					Array of data (including keys 'id', 'lang', and 'group') +	*/ +	public static $notification_option = array( +		'id'	=> 'report', +		'lang'	=> 'NOTIFICATION_TYPE_REPORT', +		'group'	=> 'NOTIFICATION_GROUP_MODERATION', +	); + +	/** +	* Get the id of the parent +	* +	* @param array $pm The data from the pm +	*/ +	public static function get_item_parent_id($pm) +	{ +		return (int) $pm['report_id']; +	} + +	/** +	* Is this type available to the current user (defines whether or not it will be shown in the UCP Edit notification options) +	* +	* @return bool True/False whether or not this is available to the user +	*/ +	public function is_available() +	{ +		$m_approve = $this->auth->acl_getf($this->permission, true); + +		return (!empty($m_approve)); +	} + + +	/** +	* Find the users who want to receive notifications +	*  (copied from post_in_queue) +	* +	* @param array $post Data from the post +	* +	* @return array +	*/ +	public function find_users_for_notification($post, $options = array()) +	{ +		$options = array_merge(array( +			'ignore_users'		=> array(), +		), $options); + +		// Global +		$post['forum_id'] = 0; + +		$auth_approve = $this->auth->acl_get_list(false, $this->permission, $post['forum_id']); + +		if (empty($auth_approve)) +		{ +			return array(); +		} + +		if (($key = array_search($this->user->data['user_id'], $auth_approve[$post['forum_id']][$this->permission]))) +		{ +			unset($auth_approve[$post['forum_id']][$this->permission][$key]); +		} + +		return $this->check_user_notification_options($auth_approve[$post['forum_id']][$this->permission], array_merge($options, array( +			'item_type'		=> self::$notification_option['id'], +		))); +	} + +	/** +	* Get email template +	* +	* @return string|bool +	*/ +	public function get_email_template() +	{ +		return 'report_pm'; +	} + +	/** +	* Get email template variables +	* +	* @return array +	*/ +	public function get_email_template_variables() +	{ +		return array( +			'AUTHOR_NAME'				=> htmlspecialchars_decode($user_data['username']), +			'SUBJECT'					=> htmlspecialchars_decode(censor_text($this->get_data('message_subject'))), + +			'U_VIEW_REPORT'				=> generate_board_url() . "mcp.{$this->php_ext}?r={$this->item_parent_id}&i=pm_reports&mode=pm_report_details", +		); +	} + +	/** +	* Get the url to this item +	* +	* @return string URL +	*/ +	public function get_url() +	{ +		return append_sid($this->phpbb_root_path . 'mcp.' . $this->php_ext, "r={$this->item_parent_id}&i=pm_reports&mode=pm_report_details"); +	} + +	/** +	* Get the HTML formatted title of this notification +	* +	* @return string +	*/ +	public function get_title() +	{ +		$this->user->add_lang('mcp'); + +		$username = $this->user_loader->get_username($this->get_data('reporter_id'), 'no_profile'); + +		if ($this->get_data('report_text')) +		{ +			return $this->user->lang( +				$this->language_key, +				$username, +				censor_text($this->get_data('message_subject')), +				$this->get_data('report_text') +			); +		} + +		if (isset($this->user->lang[$this->get_data('reason_title')])) +		{ +			return $this->user->lang( +				$this->language_key, +				$username, +				censor_text($this->get_data('message_subject')), +				$this->user->lang[$this->get_data('reason_title')] +			); +		} + +		return $this->user->lang( +			$this->language_key, +			$username, +			censor_text($this->get_data('message_subject')), +			$this->get_data('reason_description') +		); +	} + +	/** +	* Get the user's avatar +	*/ +	public function get_avatar() +	{ +		return $this->user_loader->get_avatar($this->get_data('reporter_id')); +	} + +	/** +	* Users needed to query before this notification can be displayed +	* +	* @return array Array of user_ids +	*/ +	public function users_to_query() +	{ +		return array($this->get_data('reporter_id')); +	} + +	/** +	* Function for preparing the data for insertion in an SQL query +	* (The service handles insertion) +	* +	* @param array $post Data from submit_post +	* @param array $pre_create_data Data from pre_create_insert_array() +	* +	* @return array Array of data ready to be inserted into the database +	*/ +	public function create_insert_array($post, $pre_create_data = array()) +	{ +		$this->set_data('reporter_id', $this->user->data['user_id']); +		$this->set_data('reason_title', strtoupper($post['reason_title'])); +		$this->set_data('reason_description', $post['reason_description']); +		$this->set_data('report_text', $post['report_text']); + +		return parent::create_insert_array($post, $pre_create_data); +	} +} diff --git a/phpBB/includes/notification/type/report_pm_closed.php b/phpBB/includes/notification/type/report_pm_closed.php new file mode 100644 index 0000000000..63dfa92064 --- /dev/null +++ b/phpBB/includes/notification/type/report_pm_closed.php @@ -0,0 +1,155 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* PM report closed notifications class +* This class handles notifications for when reports are closed on PMs (for the one who reported the PM) +* +* @package notifications +*/ +class phpbb_notification_type_report_pm_closed extends phpbb_notification_type_pm +{ +	/** +	* Get notification type name +	* +	* @return string +	*/ +	public function get_type() +	{ +		return 'report_pm_closed'; +	} + +	/** +	* Email template to use to send notifications +	* +	* @var string +	*/ +	public $email_template = ''; + +	/** +	* Language key used to output the text +	* +	* @var string +	*/ +	protected $language_key = 'NOTIFICATION_REPORT_CLOSED'; + +	public function is_available() +	{ +		return false; +	} + +	/** +	* Find the users who want to receive notifications +	* +	* @param array $pm Data from +	* +	* @return array +	*/ +	public function find_users_for_notification($pm, $options = array()) +	{ +		if ($pm['reporter'] == $this->user->data['user_id']) +		{ +			return array(); +		} + +		return array($pm['reporter'] => array('')); +	} + +	/** +	* Get email template +	* +	* @return string|bool +	*/ +	public function get_email_template() +	{ +		return false; +	} + +	/** +	* Get email template variables +	* +	* @return array +	*/ +	public function get_email_template_variables() +	{ +		return array(); +	} + +	/** +	* Get the url to this item +	* +	* @return string URL +	*/ +	public function get_url() +	{ +		return ''; +	} + +	/** +	* Get the HTML formatted title of this notification +	* +	* @return string +	*/ +	public function get_title() +	{ +		$username = $this->user_loader->get_username($this->get_data('closer_id'), 'no_profile'); + +		return $this->user->lang( +			$this->language_key, +			$username, +			censor_text($this->get_data('message_subject')) +		); +	} + +	/** +	* Get the user's avatar +	*/ +	public function get_avatar() +	{ +		return $this->get_user_avatar($this->get_data('closer_id')); +	} + +	/** +	* Users needed to query before this notification can be displayed +	* +	* @return array Array of user_ids +	*/ +	public function users_to_query() +	{ +		return array($this->get_data('closer_id')); +	} + +	/** +	* Function for preparing the data for insertion in an SQL query +	* (The service handles insertion) +	* +	* @param array $pm PM Data +	* @param array $pre_create_data Data from pre_create_insert_array() +	* +	* @return array Array of data ready to be inserted into the database +	*/ +	public function create_insert_array($pm, $pre_create_data = array()) +	{ +		$this->set_data('closer_id', $pm['closer_id']); + +		$data = parent::create_insert_array($pm, $pre_create_data); + +		$this->notification_time = $data['notification_time'] = time(); + +		return $data; +	} +} diff --git a/phpBB/includes/notification/type/report_post.php b/phpBB/includes/notification/type/report_post.php new file mode 100644 index 0000000000..de5c54a291 --- /dev/null +++ b/phpBB/includes/notification/type/report_post.php @@ -0,0 +1,196 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Reported post notifications class +* This class handles notifications for reported posts +* +* @package notifications +*/ +class phpbb_notification_type_report_post extends phpbb_notification_type_post_in_queue +{ +	/** +	* Get notification type name +	* +	* @return string +	*/ +	public function get_type() +	{ +		return 'report_post'; +	} + +	/** +	* Language key used to output the text +	* +	* @var string +	*/ +	protected $language_key = 'NOTIFICATION_REPORT_POST'; + +	/** +	* Permission to check for (in find_users_for_notification) +	* +	* @var string Permission name +	*/ +	protected $permission = 'm_report'; + +	/** +	* Notification option data (for outputting to the user) +	* +	* @var bool|array False if the service should use it's default data +	* 					Array of data (including keys 'id' and 'lang') +	*/ +	public static $notification_option = array( +		'id'	=> 'report', +		'lang'	=> 'NOTIFICATION_TYPE_REPORT', +		'group'	=> 'NOTIFICATION_GROUP_MODERATION', +	); + +	/** +	* Find the users who want to receive notifications +	* +	* @param array $post Data from the post +	* +	* @return array +	*/ +	public function find_users_for_notification($post, $options = array()) +	{ +		$notify_users = parent::find_users_for_notification($post, $options); + +		// never notify reporter +		unset($notify_users[$this->user->data['user_id']]); + +		return $notify_users; +	} + +	/** +	* Get email template +	* +	* @return string|bool +	*/ +	public function get_email_template() +	{ +		return 'report_post'; +	} + +	/** +	* Get email template variables +	* +	* @return array +	*/ +	public function get_email_template_variables() +	{ +		$board_url = generate_board_url(); + +		return array( +			'POST_SUBJECT'				=> htmlspecialchars_decode(censor_text($this->get_data('post_subject'))), +			'TOPIC_TITLE'				=> htmlspecialchars_decode(censor_text($this->get_data('topic_title'))), + +			'U_VIEW_REPORT'				=> "{$board_url}/mcp.{$this->php_ext}?f={$this->get_data('forum_id')}&p={$this->item_id}&i=reports&mode=report_details#reports", +			'U_VIEW_POST'				=> "{$board_url}/viewtopic.{$this->php_ext}?p={$this->item_id}#p{$this->item_id}", +			'U_NEWEST_POST'				=> "{$board_url}/viewtopic.{$this->php_ext}?f={$this->get_data('forum_id')}&t={$this->item_parent_id}&view=unread#unread", +			'U_TOPIC'					=> "{$board_url}/viewtopic.{$this->php_ext}?f={$this->get_data('forum_id')}&t={$this->item_parent_id}", +			'U_VIEW_TOPIC'				=> "{$board_url}/viewtopic.{$this->php_ext}?f={$this->get_data('forum_id')}&t={$this->item_parent_id}", +			'U_FORUM'					=> "{$board_url}/viewforum.{$this->php_ext}?f={$this->get_data('forum_id')}", +		); +	} + +	/** +	* Get the url to this item +	* +	* @return string URL +	*/ +	public function get_url() +	{ +		return append_sid($this->phpbb_root_path . 'mcp.' . $this->php_ext, "f={$this->get_data('forum_id')}&p={$this->item_id}&i=reports&mode=report_details#reports"); +	} + +	/** +	* Get the HTML formatted title of this notification +	* +	* @return string +	*/ +	public function get_title() +	{ +		$this->user->add_lang('mcp'); + +		$username = $this->user_loader->get_username($this->get_data('reporter_id'), 'no_profile'); + +		if ($this->get_data('report_text')) +		{ +			return $this->user->lang( +				$this->language_key, +				$username, +				censor_text($this->get_data('post_subject')), +				$this->get_data('report_text') +			); +		} + +		if (isset($this->user->lang[$this->get_data('reason_title')])) +		{ +			return $this->user->lang( +				$this->language_key, +				$username, +				censor_text($this->get_data('post_subject')), +				$this->user->lang[$this->get_data('reason_title')] +			); +		} + +		return $this->user->lang( +			$this->language_key, +			$username, +			censor_text($this->get_data('post_subject')), +			$this->get_data('reason_description') +		); +	} + +	/** +	* Get the user's avatar +	*/ +	public function get_avatar() +	{ +		return $this->user_loader->get_avatar($this->get_data('reporter_id')); +	} + +	/** +	* Users needed to query before this notification can be displayed +	* +	* @return array Array of user_ids +	*/ +	public function users_to_query() +	{ +		return array($this->get_data('reporter_id')); +	} + +	/** +	* Function for preparing the data for insertion in an SQL query +	* (The service handles insertion) +	* +	* @param array $post Data from submit_post +	* @param array $pre_create_data Data from pre_create_insert_array() +	* +	* @return array Array of data ready to be inserted into the database +	*/ +	public function create_insert_array($post, $pre_create_data = array()) +	{ +		$this->set_data('reporter_id', $this->user->data['user_id']); +		$this->set_data('reason_title', strtoupper($post['reason_title'])); +		$this->set_data('reason_description', $post['reason_description']); +		$this->set_data('report_text', $post['report_text']); + +		return parent::create_insert_array($post, $pre_create_data); +	} +} diff --git a/phpBB/includes/notification/type/report_post_closed.php b/phpBB/includes/notification/type/report_post_closed.php new file mode 100644 index 0000000000..3916cd8db7 --- /dev/null +++ b/phpBB/includes/notification/type/report_post_closed.php @@ -0,0 +1,155 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Post report closed notifications class +* This class handles notifications for when reports are closed on posts (for the one who reported the post) +* +* @package notifications +*/ +class phpbb_notification_type_report_post_closed extends phpbb_notification_type_post +{ +	/** +	* Get notification type name +	* +	* @return string +	*/ +	public function get_type() +	{ +		return 'report_post_closed'; +	} + +	/** +	* Email template to use to send notifications +	* +	* @var string +	*/ +	public $email_template = ''; + +	/** +	* Language key used to output the text +	* +	* @var string +	*/ +	protected $language_key = 'NOTIFICATION_REPORT_CLOSED'; + +	public function is_available() +	{ +		return false; +	} + +	/** +	* Find the users who want to receive notifications +	* +	* @param array $post Data from +	* +	* @return array +	*/ +	public function find_users_for_notification($post, $options = array()) +	{ +		if ($post['reporter'] == $this->user->data['user_id']) +		{ +			return array(); +		} + +		return array($post['reporter'] => array('')); +	} + +	/** +	* Get email template +	* +	* @return string|bool +	*/ +	public function get_email_template() +	{ +		return false; +	} + +	/** +	* Get email template variables +	* +	* @return array +	*/ +	public function get_email_template_variables() +	{ +		return array(); +	} + +	/** +	* Get the url to this item +	* +	* @return string URL +	*/ +	public function get_url() +	{ +		return ''; +	} + +	/** +	* Get the HTML formatted title of this notification +	* +	* @return string +	*/ +	public function get_title() +	{ +		$username = $this->user_loader->get_username($this->get_data('closer_id'), 'no_profile'); + +		return $this->user->lang( +			$this->language_key, +			$username, +			censor_text($this->get_data('post_subject')) +		); +	} + +	/** +	* Get the user's avatar +	*/ +	public function get_avatar() +	{ +		return $this->user_loader->get_avatar($this->get_data('closer_id')); +	} + +	/** +	* Users needed to query before this notification can be displayed +	* +	* @return array Array of user_ids +	*/ +	public function users_to_query() +	{ +		return array($this->get_data('closer_id')); +	} + +	/** +	* Function for preparing the data for insertion in an SQL query +	* (The service handles insertion) +	* +	* @param array $post Data from submit_post +	* @param array $pre_create_data Data from pre_create_insert_array() +	* +	* @return array Array of data ready to be inserted into the database +	*/ +	public function create_insert_array($post, $pre_create_data = array()) +	{ +		$this->set_data('closer_id', $post['closer_id']); + +		$data = parent::create_insert_array($post, $pre_create_data); + +		$this->notification_time = $data['notification_time'] = time(); + +		return $data; +	} +} diff --git a/phpBB/includes/notification/type/topic.php b/phpBB/includes/notification/type/topic.php new file mode 100644 index 0000000000..2549b29409 --- /dev/null +++ b/phpBB/includes/notification/type/topic.php @@ -0,0 +1,277 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Topic notifications class +* This class handles notifications for new topics +* +* @package notifications +*/ +class phpbb_notification_type_topic extends phpbb_notification_type_base +{ +	/** +	* Get notification type name +	* +	* @return string +	*/ +	public function get_type() +	{ +		return 'topic'; +	} + +	/** +	* Language key used to output the text +	* +	* @var string +	*/ +	protected $language_key = 'NOTIFICATION_TOPIC'; + +	/** +	* Notification option data (for outputting to the user) +	* +	* @var bool|array False if the service should use it's default data +	* 					Array of data (including keys 'id', 'lang', and 'group') +	*/ +	public static $notification_option = array( +		'lang'	=> 'NOTIFICATION_TYPE_TOPIC', +		'group'	=> 'NOTIFICATION_GROUP_POSTING', +	); + +	/** +	* Is available +	*/ +	public function is_available() +	{ +		return $this->config['allow_forum_notify']; +	} + +	/** +	* Get the id of the item +	* +	* @param array $post The data from the post +	*/ +	public static function get_item_id($post) +	{ +		return (int) $post['topic_id']; +	} + +	/** +	* Get the id of the parent +	* +	* @param array $post The data from the post +	*/ +	public static function get_item_parent_id($post) +	{ +		return (int) $post['forum_id']; +	} + +	/** +	* Find the users who want to receive notifications +	* +	* @param array $topic Data from the topic +	* +	* @return array +	*/ +	public function find_users_for_notification($topic, $options = array()) +	{ +		$options = array_merge(array( +			'ignore_users'			=> array(), +		), $options); + +		$users = array(); + +		$sql = 'SELECT user_id +			FROM ' . FORUMS_WATCH_TABLE . ' +			WHERE forum_id = ' . (int) $topic['forum_id'] . ' +				AND notify_status = ' . NOTIFY_YES . ' +				AND user_id <> ' . (int) $topic['poster_id']; +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$users[] = $row['user_id']; +		} +		$this->db->sql_freeresult($result); + +		if (empty($users)) +		{ +			return array(); +		} + +		$auth_read = $this->auth->acl_get_list($users, 'f_read', $topic['forum_id']); + +		if (empty($auth_read)) +		{ +			return array(); +		} + +		return $this->check_user_notification_options($auth_read[$topic['forum_id']]['f_read'], $options); +	} + +	/** +	* Get the user's avatar +	*/ +	public function get_avatar() +	{ +		return $this->user_loader->get_avatar($this->get_data('poster_id')); +	} + +	/** +	* Get the HTML formatted title of this notification +	* +	* @return string +	*/ +	public function get_title() +	{ +		if ($this->get_data('post_username')) +		{ +			$username = $this->get_data('post_username'); +		} +		else +		{ +			$username = $this->user_loader->get_username($this->get_data('poster_id'), 'no_profile'); +		} + +		return $this->user->lang( +			$this->language_key, +			$username, +			censor_text($this->get_data('topic_title')), +			$this->get_data('forum_name') +		); +	} + +	/** +	* Get email template +	* +	* @return string|bool +	*/ +	public function get_email_template() +	{ +		return 'newtopic_notify'; +	} + +	/** +	* Get email template variables +	* +	* @return array +	*/ +	public function get_email_template_variables() +	{ +		$board_url = generate_board_url(); + +		if ($this->get_data('post_username')) +		{ +			$username = $this->get_data('post_username'); +		} +		else +		{ +			$username = $this->user_loader->get_username($this->get_data('poster_id'), 'no_profile'); +		} + +		return array( +			'AUTHOR_NAME'				=> htmlspecialchars_decode($username), +			'FORUM_NAME'				=> htmlspecialchars_decode($this->get_data('forum_name')), +			'TOPIC_TITLE'				=> htmlspecialchars_decode(censor_text($this->get_data('topic_title'))), + +			'U_TOPIC'					=> "{$board_url}/viewtopic.{$this->php_ext}?f={$this->item_parent_id}&t={$this->item_id}", +			'U_VIEW_TOPIC'				=> "{$board_url}/viewtopic.{$this->php_ext}?f={$this->item_parent_id}&t={$this->item_id}", +			'U_FORUM'					=> "{$board_url}/viewforum.{$this->php_ext}?f={$this->item_parent_id}", +			'U_STOP_WATCHING_FORUM'		=> "{$board_url}/viewforum.{$this->php_ext}?uid={$this->user_id}&f={$this->item_parent_id}&unwatch=forum", +		); +	} + +	/** +	* Get the url to this item +	* +	* @return string URL +	*/ +	public function get_url() +	{ +		return append_sid($this->phpbb_root_path . 'viewtopic.' . $this->php_ext, "f={$this->item_parent_id}&t={$this->item_id}"); +	} + +	/** +	* Users needed to query before this notification can be displayed +	* +	* @return array Array of user_ids +	*/ +	public function users_to_query() +	{ +		return array($this->get_data('poster_id')); +	} + +	/** +	* Pre create insert array function +	* This allows you to perform certain actions, like run a query +	* and load data, before create_insert_array() is run. The data +	* returned from this function will be sent to create_insert_array(). +	* +	* @param array $post Post data from submit_post +	* @param array $notify_users Notify users list +	* 		Formated from find_users_for_notification() +	* @return array Whatever you want to send to create_insert_array(). +	*/ +	public function pre_create_insert_array($post, $notify_users) +	{ +		if (!sizeof($notify_users)) +		{ +			return array(); +		} + +		$tracking_data = array(); +		$sql = 'SELECT user_id, mark_time FROM ' . TOPICS_TRACK_TABLE . ' +			WHERE topic_id = ' . (int) $post['topic_id'] . ' +				AND ' . $this->db->sql_in_set('user_id', array_keys($notify_users)); +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$tracking_data[$row['user_id']] = $row['mark_time']; +		} + +		return $tracking_data; +	} + +	/** +	* Function for preparing the data for insertion in an SQL query +	* (The service handles insertion) +	* +	* @param array $post Data from submit_post +	* @param array $pre_create_data Data from pre_create_insert_array() +	* +	* @return array Array of data ready to be inserted into the database +	*/ +	public function create_insert_array($post, $pre_create_data = array()) +	{ +		$this->set_data('poster_id', $post['poster_id']); + +		$this->set_data('topic_title', $post['topic_title']); + +		$this->set_data('post_username', (($post['poster_id'] == ANONYMOUS) ? $post['post_username'] : '')); + +		$this->set_data('forum_name', $post['forum_name']); + +		$this->notification_time = $post['post_time']; + +		// Topics can be "read" before they are public (while awaiting approval). +		// Make sure that if the user has read the topic, it's marked as read in the notification +		if (isset($pre_create_data[$this->user_id]) && $pre_create_data[$this->user_id] >= $this->notification_time) +		{ +			$this->notification_read = true; +		} + +		return parent::create_insert_array($post, $pre_create_data); +	} +} diff --git a/phpBB/includes/notification/type/topic_in_queue.php b/phpBB/includes/notification/type/topic_in_queue.php new file mode 100644 index 0000000000..dc0b9f9869 --- /dev/null +++ b/phpBB/includes/notification/type/topic_in_queue.php @@ -0,0 +1,130 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Topic in queue notifications class +* This class handles notifications for topics when they are put in the moderation queue (for moderators) +* +* @package notifications +*/ +class phpbb_notification_type_topic_in_queue extends phpbb_notification_type_topic +{ +	/** +	* Get notification type name +	* +	* @return string +	*/ +	public function get_type() +	{ +		return 'topic_in_queue'; +	} + +	/** +	* Language key used to output the text +	* +	* @var string +	*/ +	protected $language_key = 'NOTIFICATION_TOPIC_IN_QUEUE'; + +	/** +	* Notification option data (for outputting to the user) +	* +	* @var bool|array False if the service should use it's default data +	* 					Array of data (including keys 'id', 'lang', and 'group') +	*/ +	public static $notification_option = array( +		'id'	=> 'needs_approval', +		'lang'	=> 'NOTIFICATION_TYPE_IN_MODERATION_QUEUE', +		'group'	=> 'NOTIFICATION_GROUP_MODERATION', +	); + +	/** +	* Is available +	*/ +	public function is_available() +	{ +		$m_approve = $this->auth->acl_getf('m_approve', true); + +		return (!empty($m_approve)); +	} + +	/** +	* Find the users who want to receive notifications +	* +	* @param array $topic Data from the topic +	* +	* @return array +	*/ +	public function find_users_for_notification($topic, $options = array()) +	{ +		$options = array_merge(array( +			'ignore_users'		=> array(), +		), $options); + +		// 0 is for global +		$auth_approve = $this->auth->acl_get_list(false, 'm_approve', array($topic['forum_id'], 0)); + +		if (empty($auth_approve)) +		{ +			return array(); +		} + +		$auth_approve[$topic['forum_id']] = array_unique(array_merge($auth_approve[$topic['forum_id']], $auth_approve[0])); + +		return $this->check_user_notification_options($auth_approve[$topic['forum_id']]['m_approve'], array_merge($options, array( +			'item_type'		=> self::$notification_option['id'], +		))); +	} + +	/** +	* Get the url to this item +	* +	* @return string URL +	*/ +	public function get_url() +	{ +		return append_sid($this->phpbb_root_path . 'mcp.' . $this->php_ext, "i=queue&mode=approve_details&f={$this->item_parent_id}&t={$this->item_id}"); +	} + +	/** +	* Function for preparing the data for insertion in an SQL query +	* (The service handles insertion) +	* +	* @param array $topic Data from submit_post +	* @param array $pre_create_data Data from pre_create_insert_array() +	* +	* @return array Array of data ready to be inserted into the database +	*/ +	public function create_insert_array($topic, $pre_create_data = array()) +	{ +		$data = parent::create_insert_array($topic, $pre_create_data); + +		$this->notification_time = $data['notification_time'] = time(); + +		return $data; +	} + +	/** +	* Get email template +	* +	* @return string|bool +	*/ +	public function get_email_template() +	{ +		return 'topic_in_queue'; +	} +} diff --git a/phpBB/includes/search/base.php b/phpBB/includes/search/base.php index b364dead9a..914cef9167 100644 --- a/phpBB/includes/search/base.php +++ b/phpBB/includes/search/base.php @@ -94,7 +94,7 @@ class phpbb_search_base  	*  	* @return int SEARCH_RESULT_NOT_IN_CACHE or SEARCH_RESULT_IN_CACHE or SEARCH_RESULT_INCOMPLETE  	*/ -	function obtain_ids($search_key, &$result_count, &$id_ary, $start, $per_page, $sort_dir) +	function obtain_ids($search_key, &$result_count, &$id_ary, &$start, $per_page, $sort_dir)  	{  		global $cache; @@ -109,6 +109,19 @@ class phpbb_search_base  			$reverse_ids = ($stored_ids[-2] != $sort_dir) ? true : false;  			$complete = true; +			// Change start parameter in case out of bounds +			if ($result_count) +			{ +				if ($start < 0) +				{ +					$start = 0; +				} +				else if ($start >= $result_count) +				{ +					$start = floor(($result_count - 1) / $per_page) * $per_page; +				} +			} +  			// change the start to the actual end of the current request if the sort direction differs  			// from the dirction in the cache and reverse the ids later  			if ($reverse_ids) diff --git a/phpBB/includes/search/fulltext_mysql.php b/phpBB/includes/search/fulltext_mysql.php index 324c214e91..adaf025730 100644 --- a/phpBB/includes/search/fulltext_mysql.php +++ b/phpBB/includes/search/fulltext_mysql.php @@ -353,7 +353,7 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base  	* @param	int			$per_page			number of ids each page is supposed to contain  	* @return	boolean|int						total number of results  	*/ -	public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page) +	public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)  	{  		// No keywords? No posts  		if (!$this->search_query) @@ -375,6 +375,11 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base  			implode(',', $author_ary)  		))); +		if ($start < 0) +		{ +			$start = 0; +		} +  		// try reading the results from cache  		$result_count = 0;  		if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE) @@ -488,16 +493,11 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base  		$id_ary = array_unique($id_ary); -		if (!sizeof($id_ary)) -		{ -			return false; -		} -  		// if the total result count is not cached yet, retrieve it from the db  		if (!$result_count)  		{ -			$sql = 'SELECT FOUND_ROWS() as result_count'; -			$result = $this->db->sql_query($sql); +			$sql_found_rows = 'SELECT FOUND_ROWS() as result_count'; +			$result = $this->db->sql_query($sql_found_rows);  			$result_count = (int) $this->db->sql_fetchfield('result_count');  			$this->db->sql_freeresult($result); @@ -507,6 +507,21 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base  			}  		} +		if ($start >= $result_count) +		{ +			$start = floor(($result_count - 1) / $per_page) * $per_page; + +			$result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start); + +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				$id_ary[] = (int) $row[$field]; +			} +			$this->db->sql_freeresult($result); + +			$id_ary = array_unique($id_ary); +		} +  		// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page  		$this->save_ids($search_key, implode(' ', $this->split_words), $author_ary, $result_count, $id_ary, $start, $sort_dir);  		$id_ary = array_slice($id_ary, 0, (int) $per_page); @@ -533,7 +548,7 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base  	* @param	int			$per_page			number of ids each page is supposed to contain  	* @return	boolean|int						total number of results  	*/ -	public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page) +	public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)  	{  		// No author? No posts  		if (!sizeof($author_ary)) @@ -557,6 +572,11 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base  			$author_name,  		))); +		if ($start < 0) +		{ +			$start = 0; +		} +  		// try reading the results from cache  		$result_count = 0;  		if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE) @@ -662,8 +682,8 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base  		// retrieve the total result count if needed  		if (!$result_count)  		{ -			$sql = 'SELECT FOUND_ROWS() as result_count'; -			$result = $this->db->sql_query($sql); +			$sql_found_rows = 'SELECT FOUND_ROWS() as result_count'; +			$result = $this->db->sql_query($sql_found_rows);  			$result_count = (int) $this->db->sql_fetchfield('result_count');  			$this->db->sql_freeresult($result); @@ -673,6 +693,20 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base  			}  		} +		if ($start >= $result_count) +		{ +			$start = floor(($result_count - 1) / $per_page) * $per_page; + +			$result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start); +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				$id_ary[] = (int) $row[$field]; +			} +			$this->db->sql_freeresult($result); + +			$id_ary = array_unique($id_ary); +		} +  		if (sizeof($id_ary))  		{  			$this->save_ids($search_key, '', $author_ary, $result_count, $id_ary, $start, $sort_dir); diff --git a/phpBB/includes/search/fulltext_native.php b/phpBB/includes/search/fulltext_native.php index 53df8348ae..c9f33054fc 100644 --- a/phpBB/includes/search/fulltext_native.php +++ b/phpBB/includes/search/fulltext_native.php @@ -516,7 +516,7 @@ class phpbb_search_fulltext_native extends phpbb_search_base  	* @param	int			$per_page			number of ids each page is supposed to contain  	* @return	boolean|int						total number of results  	*/ -	public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page) +	public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)  	{  		// No keywords? No posts.  		if (empty($this->search_query)) @@ -855,10 +855,6 @@ class phpbb_search_fulltext_native extends phpbb_search_base  		}  		$this->db->sql_freeresult($result); -		if (!sizeof($id_ary)) -		{ -			return false; -		}  		// if we use mysql and the total result count is not cached yet, retrieve it from the db  		if (!$total_results && $is_mysql) @@ -867,14 +863,14 @@ class phpbb_search_fulltext_native extends phpbb_search_base  			$sql_array_copy = $sql_array;  			$sql_array_copy['SELECT'] = 'SQL_CALC_FOUND_ROWS p.post_id '; -			$sql = $this->db->sql_build_query('SELECT', $sql_array_copy); +			$sql_calc = $this->db->sql_build_query('SELECT', $sql_array_copy);  			unset($sql_array_copy); -			$this->db->sql_query($sql); +			$this->db->sql_query($sql_calc);  			$this->db->sql_freeresult($result); -			$sql = 'SELECT FOUND_ROWS() as total_results'; -			$result = $this->db->sql_query($sql); +			$sql_count = 'SELECT FOUND_ROWS() as total_results'; +			$result = $this->db->sql_query($sql_count);  			$total_results = (int) $this->db->sql_fetchfield('total_results');  			$this->db->sql_freeresult($result); @@ -884,6 +880,20 @@ class phpbb_search_fulltext_native extends phpbb_search_base  			}  		} +		if ($start >= $total_results) +		{ +			$start = floor(($total_results - 1) / $per_page) * $per_page; + +			$result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start); + +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				$id_ary[] = (int) $row[(($type == 'posts') ? 'post_id' : 'topic_id')]; +			} +			$this->db->sql_freeresult($result); + +		} +  		// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page  		$this->save_ids($search_key, $this->search_query, $author_ary, $total_results, $id_ary, $start, $sort_dir);  		$id_ary = array_slice($id_ary, 0, (int) $per_page); @@ -910,7 +920,7 @@ class phpbb_search_fulltext_native extends phpbb_search_base  	* @param	int			$per_page			number of ids each page is supposed to contain  	* @return	boolean|int						total number of results  	*/ -	public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page) +	public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)  	{  		// No author? No posts  		if (!sizeof($author_ary)) @@ -1096,13 +1106,13 @@ class phpbb_search_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 = str_replace('SELECT ' . $select, 'SELECT DISTINCT SQL_CALC_FOUND_ROWS p.post_id', $sql); +			$sql_calc = str_replace('SELECT ' . $select, 'SELECT DISTINCT SQL_CALC_FOUND_ROWS p.post_id', $sql); -			$this->db->sql_query($sql); +			$this->db->sql_query($sql_calc);  			$this->db->sql_freeresult($result); -			$sql = 'SELECT FOUND_ROWS() as total_results'; -			$result = $this->db->sql_query($sql); +			$sql_count = 'SELECT FOUND_ROWS() as total_results'; +			$result = $this->db->sql_query($sql_count);  			$total_results = (int) $this->db->sql_fetchfield('total_results');  			$this->db->sql_freeresult($result); @@ -1112,6 +1122,19 @@ class phpbb_search_fulltext_native extends phpbb_search_base  			}  		} +		if ($start >= $total_results) +		{ +			$start = floor(($total_results - 1) / $per_page) * $per_page; + +			$result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start); + +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				$id_ary[] = (int) $row[$field]; +			} +			$this->db->sql_freeresult($result); +		} +  		if (sizeof($id_ary))  		{  			$this->save_ids($search_key, '', $author_ary, $total_results, $id_ary, $start, $sort_dir); diff --git a/phpBB/includes/search/fulltext_postgres.php b/phpBB/includes/search/fulltext_postgres.php index 1475cc31d0..eeb628b18f 100644 --- a/phpBB/includes/search/fulltext_postgres.php +++ b/phpBB/includes/search/fulltext_postgres.php @@ -343,7 +343,7 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base  	* @param	int			$per_page			number of ids each page is supposed to contain  	* @return	boolean|int						total number of results  	*/ -	public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page) +	public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)  	{  		// No keywords? No posts  		if (!$this->search_query) @@ -371,6 +371,11 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base  			implode(',', $author_ary)  		))); +		if ($start < 0) +		{ +			$start = 0; +		} +  		// try reading the results from cache  		$result_count = 0;  		if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE) @@ -495,11 +500,6 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base  		$id_ary = array_unique($id_ary); -		if (!sizeof($id_ary)) -		{ -			return false; -		} -  		// if the total result count is not cached yet, retrieve it from the db  		if (!$result_count)  		{ @@ -518,6 +518,21 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base  		$this->db->sql_transaction('commit'); +		if ($start >= $result_count) +		{ +			$start = floor(($result_count - 1) / $per_page) * $per_page; + +			$result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start); + +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				$id_ary[] = $row[$field]; +			} +			$this->db->sql_freeresult($result); + +			$id_ary = array_unique($id_ary); +		} +  		// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page  		$this->save_ids($search_key, implode(' ', $this->split_words), $author_ary, $result_count, $id_ary, $start, $sort_dir);  		$id_ary = array_slice($id_ary, 0, (int) $per_page); @@ -544,7 +559,7 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base  	* @param	int			$per_page			number of ids each page is supposed to contain  	* @return	boolean|int						total number of results  	*/ -	public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page) +	public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)  	{  		// No author? No posts  		if (!sizeof($author_ary)) @@ -568,6 +583,11 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base  			$author_name,  		))); +		if ($start < 0) +		{ +			$start = 0; +		} +  		// try reading the results from cache  		$result_count = 0;  		if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE) @@ -710,6 +730,20 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base  		$this->db->sql_transaction('commit'); +		if ($start >= $result_count) +		{ +			$start = floor(($result_count - 1) / $per_page) * $per_page; + +			$result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start); +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				$id_ary[] = (int) $row[$field]; +			} +			$this->db->sql_freeresult($result); + +			$id_ary = array_unique($id_ary); +		} +  		if (sizeof($id_ary))  		{  			$this->save_ids($search_key, '', $author_ary, $result_count, $id_ary, $start, $sort_dir); diff --git a/phpBB/includes/search/fulltext_sphinx.php b/phpBB/includes/search/fulltext_sphinx.php index 4bacf74f93..48445d0794 100644 --- a/phpBB/includes/search/fulltext_sphinx.php +++ b/phpBB/includes/search/fulltext_sphinx.php @@ -454,7 +454,7 @@ class phpbb_search_fulltext_sphinx  	* @param	int			$per_page			number of ids each page is supposed to contain  	* @return	boolean|int						total number of results  	*/ -	public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page) +	public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)  	{  		// No keywords? No posts.  		if (!strlen($this->search_query) && !sizeof($author_ary)) @@ -609,6 +609,25 @@ class phpbb_search_fulltext_sphinx  			}  		} +		$result_count = $result['total_found']; + +		if ($start >= $result_count) +		{ +			$start = floor(($result_count - 1) / $per_page) * $per_page; + +			$this->sphinx->SetLimits((int) $start, (int) $per_page, SPHINX_MAX_MATCHES); +			$result = $this->sphinx->Query($search_query_prefix . str_replace('"', '"', $this->search_query), $this->indexes); + +			// Could be connection to localhost:9312 failed (errno=111, +			// msg=Connection refused) during rotate, retry if so +			$retries = SPHINX_CONNECT_RETRIES; +			while (!$result && (strpos($this->sphinx->GetLastError(), "errno=111,") !== false) && $retries--) +			{ +				usleep(SPHINX_CONNECT_WAIT_TIME); +				$result = $this->sphinx->Query($search_query_prefix . str_replace('"', '"', $this->search_query), $this->indexes); +			} +		} +  		$id_ary = array();  		if (isset($result['matches']))  		{ @@ -629,8 +648,6 @@ class phpbb_search_fulltext_sphinx  			return false;  		} -		$result_count = $result['total_found']; -  		$id_ary = array_slice($id_ary, 0, (int) $per_page);  		return $result_count; @@ -878,8 +895,8 @@ class phpbb_search_fulltext_sphinx  			<dd><input id="fulltext_sphinx_indexer_mem_limit" type="text" size="4" maxlength="10" name="config[fulltext_sphinx_indexer_mem_limit]" value="' . $this->config['fulltext_sphinx_indexer_mem_limit'] . '" /> ' . $this->user->lang['MIB'] . '</dd>  		</dl>  		<dl> -			<dt><label for="fulltext_sphinx_config_file">' . $this->user->lang['FULLTEXT_SPHINX_CONFIG_FILE'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['FULLTEXT_SPHINX_CONFIG_FILE_EXPLAIN'] . '</dt> -			<dd>' . (($this->config_generate()) ? '<textarea readonly="readonly" rows="6">' . $this->config_file_data . '</textarea>' : $this->config_file_data) . '</dd> +			<dt><label for="fulltext_sphinx_config_file">' . $this->user->lang['FULLTEXT_SPHINX_CONFIG_FILE'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['FULLTEXT_SPHINX_CONFIG_FILE_EXPLAIN'] . '</span></dt> +			<dd>' . (($this->config_generate()) ? '<textarea readonly="readonly" rows="6" id="sphinx_config_data">' . htmlspecialchars($this->config_file_data) . '</textarea>' : $this->config_file_data) . '</dd>  		<dl>  		'; diff --git a/phpBB/includes/session.php b/phpBB/includes/session.php index ee8a4094c7..6bc71da0c1 100644 --- a/phpBB/includes/session.php +++ b/phpBB/includes/session.php @@ -346,7 +346,7 @@ class phpbb_session  		$session_id = $request->variable('sid', '');  		if (defined('NEED_SID') && (empty($session_id) || $this->session_id !== $session_id))  		{ -			send_status_line(401, 'Not authorized'); +			send_status_line(401, 'Unauthorized');  			redirect(append_sid("{$phpbb_root_path}index.$phpEx"));  		} diff --git a/phpBB/includes/style/extension_path_provider.php b/phpBB/includes/style/extension_path_provider.php index 4eac300424..6976a45ed0 100644 --- a/phpBB/includes/style/extension_path_provider.php +++ b/phpBB/includes/style/extension_path_provider.php @@ -92,7 +92,7 @@ class phpbb_style_extension_path_provider extends phpbb_extension_provider imple  					if ($path && !phpbb_is_absolute($path))  					{  						$result = $finder->directory('/' . $this->ext_dir_prefix . $path) -							->get_directories(true, true); +							->get_directories(true, false, true);  						foreach ($result as $ext => $ext_path)  						{  							$directories[$ext][] = $ext_path; diff --git a/phpBB/includes/ucp/info/ucp_notifications.php b/phpBB/includes/ucp/info/ucp_notifications.php new file mode 100644 index 0000000000..98d8b9db61 --- /dev/null +++ b/phpBB/includes/ucp/info/ucp_notifications.php @@ -0,0 +1,35 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @package module_install +*/ +class ucp_notifications_info +{ +	function module() +	{ +		return array( +			'filename'	=> 'ucp_notifications', +			'title'		=> 'UCP_NOTIFICATION_OPTIONS', +			'version'	=> '1.0.0', +			'modes'		=> array( +				'notification_options'		=> array('title' => 'UCP_NOTIFICATION_OPTIONS', 'auth' => '', 'cat' => array('UCP_PREFS')), +				'notification_list'			=> array('title' => 'UCP_NOTIFICATION_LIST', 'auth' => '', 'cat' => array('UCP_MAIN')), +			), +		); +	} + +	function install() +	{ +	} + +	function uninstall() +	{ +	} +} diff --git a/phpBB/includes/ucp/ucp_notifications.php b/phpBB/includes/ucp/ucp_notifications.php new file mode 100644 index 0000000000..338c921e94 --- /dev/null +++ b/phpBB/includes/ucp/ucp_notifications.php @@ -0,0 +1,226 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +class ucp_notifications +{ +	public $u_action; + +	public function main($id, $mode) +	{ +		global $config, $template, $user, $request, $phpbb_container; +		global $phpbb_root_path, $phpEx; + +		add_form_key('ucp_notification'); + +		$start = $request->variable('start', 0); +		$form_time = min($request->variable('form_time', 0), time()); + +		$phpbb_notifications = $phpbb_container->get('notification_manager'); + +		switch ($mode) +		{ +			case 'notification_options': +				$subscriptions = $phpbb_notifications->get_global_subscriptions(false); + +				// Add/remove subscriptions +				if ($request->is_set_post('submit')) +				{ +					if (!check_form_key('ucp_notification')) +					{ +						trigger_error('FORM_INVALID'); +					} + +					$notification_methods = $phpbb_notifications->get_subscription_methods(); + +					foreach($phpbb_notifications->get_subscription_types() as $group => $subscription_types) +					{ +						foreach($subscription_types as $type => $data) +						{ +							foreach($notification_methods as $method => $method_data) +							{ +								if ($request->is_set_post($type . '_' . $method_data['id']) && (!isset($subscriptions[$type]) || !in_array($method_data['id'], $subscriptions[$type]))) +								{ +									$phpbb_notifications->add_subscription($type, 0, $method_data['id']); +								} +								else if (!$request->is_set_post($type . '_' . $method_data['id']) && isset($subscriptions[$type]) && in_array($method_data['id'], $subscriptions[$type])) +								{ +									$phpbb_notifications->delete_subscription($type, 0, $method_data['id']); +								} +							} + +							if ($request->is_set_post($type . '_notification') && !isset($subscriptions[$type])) +							{ +								$phpbb_notifications->add_subscription($type); +							} +							else if (!$request->is_set_post($type . '_notification') && isset($subscriptions[$type])) +							{ +								$phpbb_notifications->delete_subscription($type); +							} +						} +					} + +					meta_refresh(3, $this->u_action); +					$message = $user->lang['PREFERENCES_UPDATED'] . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $this->u_action . '">', '</a>'); +					trigger_error($message); +				} + +				$this->output_notification_methods('notification_methods', $phpbb_notifications, $template, $user); + +				$this->output_notification_types($subscriptions, 'notification_types', $phpbb_notifications, $template, $user); + +				$this->tpl_name = 'ucp_notifications'; +				$this->page_title = 'UCP_NOTIFICATION_OPTIONS'; +			break; + +			case 'notification_list': +			default: +				// Mark all items read +				if ($request->variable('mark', '') == 'all' && (confirm_box(true) || check_link_hash($request->variable('token', ''), 'mark_all_notifications_read'))) +				{ +					if (confirm_box(true)) +					{ +						$phpbb_notifications->mark_notifications_read(false, false, $user->data['user_id'], $form_time); + +						meta_refresh(3, $this->u_action); +						$message = $user->lang['NOTIFICATIONS_MARK_ALL_READ_SUCCESS'] . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $this->u_action . '">', '</a>'); +						trigger_error($message); +					} +					else +					{ +						confirm_box(false, 'NOTIFICATIONS_MARK_ALL_READ', build_hidden_fields(array( +							'mark'		=> 'all', +							'form_time'	=> $form_time, +						))); +					} +				} + +				// Mark specific notifications read +				if ($request->is_set_post('submit')) +				{ +					if (!check_form_key('ucp_notification')) +					{ +						trigger_error('FORM_INVALID'); +					} + +					$mark_read = $request->variable('mark', array(0)); + +					if (!empty($mark_read)) +					{ +						$phpbb_notifications->mark_notifications_read_by_id($mark_read, $form_time); +					} +				} + +				$notifications = $phpbb_notifications->load_notifications(array( +					'start'			=> $start, +					'limit'			=> $config['topics_per_page'], +					'count_total'	=> true, +				)); + +				foreach ($notifications['notifications'] as $notification) +				{ +					$template->assign_block_vars('notification_list', $notification->prepare_for_display()); +				} + +				$base_url = append_sid("{$phpbb_root_path}ucp.$phpEx", "i=ucp_notifications&mode=notification_list"); +				phpbb_generate_template_pagination($template, $base_url, 'pagination', 'start', $notifications['total_count'], $config['topics_per_page'], $start); + +				$template->assign_vars(array( +					'PAGE_NUMBER'	=> phpbb_on_page($template, $user, $base_url, $notifications['total_count'], $config['topics_per_page'], $start), +					'TOTAL_COUNT'	=> $user->lang('NOTIFICATIONS_COUNT', $notifications['total_count']), +					'U_MARK_ALL'	=> $base_url . '&mark=all&token=' . generate_link_hash('mark_all_notifications_read'), +				)); + +				$this->tpl_name = 'ucp_notifications'; +				$this->page_title = 'UCP_NOTIFICATION_LIST'; +			break; +		} + +		$template->assign_vars(array( +			'TITLE'				=> $user->lang($this->page_title), +			'TITLE_EXPLAIN'		=> $user->lang($this->page_title . '_EXPLAIN'), + +			'MODE'				=> $mode, + +			'FORM_TIME'			=> time(), +		)); +	} + +	/** +	* Output all the notification types to the template +	* +	* @param string $block +	* @param phpbb_notification_manager $phpbb_notifications +	* @param phpbb_template $template +	* @param phpbb_user $user +	*/ +	public function output_notification_types($subscriptions, $block = 'notification_types', phpbb_notification_manager $phpbb_notifications, phpbb_template $template, phpbb_user $user) +	{ +		$notification_methods = $phpbb_notifications->get_subscription_methods(); + +		foreach($phpbb_notifications->get_subscription_types() as $group => $subscription_types) +		{ +			$template->assign_block_vars($block, array( +				'GROUP_NAME'	=> $user->lang($group), +			)); + +			foreach($subscription_types as $type => $data) +			{ +				$template->assign_block_vars($block, array( +					'TYPE'				=> $type, + +					'NAME'				=> $user->lang($data['lang']), +					'EXPLAIN'			=> (isset($user->lang[$data['lang'] . '_EXPLAIN'])) ? $user->lang($data['lang'] . '_EXPLAIN') : '', + +					'SUBSCRIBED'		=> (isset($subscriptions[$type])) ? true : false, +				)); + +				foreach($notification_methods as $method => $method_data) +				{ +					$template->assign_block_vars($block . '.notification_methods', array( +						'METHOD'			=> $method_data['id'], + +						'NAME'				=> $user->lang($method_data['lang']), + +						'SUBSCRIBED'		=> (isset($subscriptions[$type]) && in_array($method_data['id'], $subscriptions[$type])) ? true : false, +					)); +				} +			} +		} +	} + +	/** +	* Output all the notification methods to the template +	* +	* @param string $block +	* @param phpbb_notification_manager $phpbb_notifications +	* @param phpbb_template $template +	* @param phpbb_user $user +	*/ +	public function output_notification_methods($block = 'notification_methods', phpbb_notification_manager $phpbb_notifications, phpbb_template $template, phpbb_user $user) +	{ +		$notification_methods = $phpbb_notifications->get_subscription_methods(); + +		foreach($notification_methods as $method => $method_data) +		{ +			$template->assign_block_vars($block, array( +				'METHOD'			=> $method_data['id'], + +				'NAME'				=> $user->lang($method_data['lang']), +			)); +		} +	} +} diff --git a/phpBB/includes/ucp/ucp_prefs.php b/phpBB/includes/ucp/ucp_prefs.php index 23892c2c8c..7c3286c1d1 100644 --- a/phpBB/includes/ucp/ucp_prefs.php +++ b/phpBB/includes/ucp/ucp_prefs.php @@ -46,8 +46,6 @@ class ucp_prefs  					'viewemail'		=> request_var('viewemail', (bool) $user->data['user_allow_viewemail']),  					'massemail'		=> request_var('massemail', (bool) $user->data['user_allow_massemail']),  					'hideonline'	=> request_var('hideonline', (bool) !$user->data['user_allow_viewonline']), -					'notifypm'		=> request_var('notifypm', (bool) $user->data['user_notify_pm']), -					'popuppm'		=> request_var('popuppm', (bool) $user->optionget('popuppm')),  					'allowpm'		=> request_var('allowpm', (bool) $user->data['user_allow_pm']),  				); @@ -81,15 +79,12 @@ class ucp_prefs  					if (!sizeof($error))  					{ -						$user->optionset('popuppm', $data['popuppm']); -  						$sql_ary = array(  							'user_allow_pm'			=> $data['allowpm'],  							'user_allow_viewemail'	=> $data['viewemail'],  							'user_allow_massemail'	=> $data['massemail'],  							'user_allow_viewonline'	=> ($auth->acl_get('u_hideonline')) ? !$data['hideonline'] : $user->data['user_allow_viewonline'],  							'user_notify_type'		=> $data['notifymethod'], -							'user_notify_pm'		=> $data['notifypm'],  							'user_options'			=> $user->data['user_options'],  							'user_dateformat'		=> $data['dateformat'], @@ -172,8 +167,6 @@ class ucp_prefs  					'S_MASS_EMAIL'		=> $data['massemail'],  					'S_ALLOW_PM'		=> $data['allowpm'],  					'S_HIDE_ONLINE'		=> $data['hideonline'], -					'S_NOTIFY_PM'		=> $data['notifypm'], -					'S_POPUP_PM'		=> $data['popuppm'],  					'DATE_FORMAT'			=> $data['dateformat'],  					'A_DATE_FORMAT'			=> addslashes($data['dateformat']), diff --git a/phpBB/includes/user_loader.php b/phpBB/includes/user_loader.php new file mode 100644 index 0000000000..77128d6570 --- /dev/null +++ b/phpBB/includes/user_loader.php @@ -0,0 +1,231 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* User loader class +* +* This handles loading users from the database and +* storing in them in a temporary cache so we do not +* have to query the same user multiple times in +* different services. +*/ +class phpbb_user_loader +{ +	/** @var phpbb_db_driver */ +	protected $db = null; + +	/** @var string */ +	protected $phpbb_root_path = null; + +	/** @var string */ +	protected $php_ext = null; + +	/** @var string */ +	protected $users_table = null; + +	/** +	* Users loaded from the DB +	* +	* @var array Array of user data that we've loaded from the DB +	*/ +	protected $users = array(); + +	/** +	* User loader constructor +	* +	* @param phpbb_db_driver $db A database connection +	* @param string $phpbb_root_path Path to the phpbb includes directory. +	* @param string $php_ext php file extension +	* @param string $users_table The name of the database table (phpbb_users) +	*/ +	public function __construct(phpbb_db_driver $db, $phpbb_root_path, $php_ext, $users_table) +	{ +		$this->db = $db; + +		$this->phpbb_root_path = $phpbb_root_path; +		$this->php_ext = $php_ext; + +		$this->users_table = $users_table; +	} + +	/** +	* Load user helper +	* +	* @param array $user_ids +	*/ +	public function load_users(array $user_ids) +	{ +		$user_ids[] = ANONYMOUS; + +		// Load the users +		$user_ids = array_unique($user_ids); + +		// Do not load users we already have in $this->users +		$user_ids = array_diff($user_ids, array_keys($this->users)); + +		if (sizeof($user_ids)) +		{ +			$sql = 'SELECT * +				FROM ' . $this->users_table . ' +				WHERE ' . $this->db->sql_in_set('user_id', $user_ids); +			$result = $this->db->sql_query($sql); + +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				$this->users[$row['user_id']] = $row; +			} +			$this->db->sql_freeresult($result); +		} +	} + +	/** +	* Load a user by username +	* +	* Stores the full data in the user cache so they do not need to be loaded again +	* Returns the user id so you may use get_user() from the returned value +	* +	* @param string $username Raw username to load (will be cleaned) +	* @return int User ID for the username +	*/ +	public function load_user_by_username($username) +	{ +		$sql = 'SELECT * +			FROM ' . $this->users_table . " +			WHERE username_clean = '" . $this->db->sql_escape(utf8_clean_string($username)) . "'"; +		$result = $this->db->sql_query($sql); +		$row = $this->db->sql_fetchrow($result); +		$this->db->sql_freeresult($result); + +		if ($row) +		{ +			$this->users[$row['user_id']] = $row; + +			return $row['user_id']; +		} + +		return ANONYMOUS; +	} + +	/** +	* Get a user row from our users cache +	* +	* @param int $user_id User ID of the user you want to retreive +	* @param bool $query Should we query the database if this user has not yet been loaded? +	* 						Typically this should be left as false and you should make sure +	* 						you load users ahead of time with load_users() +	* @return array|bool Row from the database of the user or Anonymous if the user wasn't loaded/does not exist +	* 						or bool False if the anonymous user was not loaded +	*/ +	public function get_user($user_id, $query = false) +	{ +		if (isset($this->users[$user_id])) +		{ +			return $this->users[$user_id]; +		} +		// Query them if we must (if ANONYMOUS is sent as the user_id and we have not loaded Anonymous yet, we must load Anonymous as a last resort) +		else if ($query || $user_id == ANONYMOUS) +		{ +			$this->load_users(array($user_id)); + +			return $this->get_user($user_id); +		} + +		return $this->get_user(ANONYMOUS); +	} + +	/** +	* Get username +	* +	* @param int $user_id User ID of the user you want to retreive the username for +	* @param string $mode The mode to load (same as get_username_string). One of the following: +	* 			profile (for getting an url to the profile) +	* 			username (for obtaining the username) +	* 			colour (for obtaining the user colour) +	* 			full (for obtaining a html string representing a coloured link to the users profile) +	* 			no_profile (the same as full but forcing no profile link) +	* @param string $guest_username Optional parameter to specify the guest username. It will be used in favor of the GUEST language variable then. +	* @param string $custom_profile_url Optional parameter to specify a profile url. The user id get appended to this url as &u={user_id} +	* @param bool $query Should we query the database if this user has not yet been loaded? +	* 						Typically this should be left as false and you should make sure +	* 						you load users ahead of time with load_users() +	* @return string +	*/ +	public function get_username($user_id, $mode, $guest_username = false, $custom_profile_url = false, $query = false) +	{ +		if (!($user = $this->get_user($user_id, $query))) +		{ +			return ''; +		} + +		return get_username_string($mode, $user['user_id'], $user['username'], $user['user_colour'], $guest_username, $custom_profile_url); +	} + +	/** +	* Get avatar +	* +	* @param int $user_id User ID of the user you want to retreive the avatar for +	* @param bool $query Should we query the database if this user has not yet been loaded? +	* 						Typically this should be left as false and you should make sure +	* 						you load users ahead of time with load_users() +	* @return string +	*/ +	public function get_avatar($user_id, $query = false) +	{ +		if (!($user = $this->get_user($user_id, $query))) +		{ +			return ''; +		} + +		if (!function_exists('get_user_avatar')) +		{ +			include($this->phpbb_root_path . 'includes/functions_display.' . $this->php_ext); +		} + +		return get_user_avatar($user['user_avatar'], $user['user_avatar_type'], $user['user_avatar_width'], $user['user_avatar_height']); +	} + +	/** +	* Get rank +	* +	* @param int $user_id User ID of the user you want to retreive the rank for +	* @param bool $query Should we query the database if this user has not yet been loaded? +	* 						Typically this should be left as false and you should make sure +	* 						you load users ahead of time with load_users() +	* @return array Array with keys 'rank_title', 'rank_img', and 'rank_img_src' +	*/ +	public function get_rank($user_id, $query = false) +	{ +		if (!($user = $this->get_user($user_id, $query))) +		{ +			return ''; +		} + +		if (!function_exists('get_user_rank')) +		{ +			include($this->phpbb_root_path . 'includes/functions_display.' . $this->php_ext); +		} + +		$rank = array( +			'rank_title', +			'rank_img', +			'rank_img_src', +		); + +		get_user_rank($user['user_rank'], (($user['user_id'] == ANONYMOUS) ? false : $user['user_posts']), $rank['rank_title'], $rank['rank_img'], $rank['rank_img_src']); + +		return $rank; +	} +}  | 
