diff options
Diffstat (limited to 'phpBB/includes')
64 files changed, 2031 insertions, 669 deletions
diff --git a/phpBB/includes/acp/acp_board.php b/phpBB/includes/acp/acp_board.php index 6543427677..ab44920f0a 100644 --- a/phpBB/includes/acp/acp_board.php +++ b/phpBB/includes/acp/acp_board.php @@ -55,6 +55,7 @@ class acp_board  						'site_desc'				=> array('lang' => 'SITE_DESC',				'validate' => 'string',	'type' => 'text:40:255', 'explain' => false),  						'site_home_url'			=> array('lang' => 'SITE_HOME_URL',			'validate' => 'string',	'type' => 'text:40:255', 'explain' => true),  						'site_home_text'		=> array('lang' => 'SITE_HOME_TEXT',		'validate' => 'string',	'type' => 'text:40:255', 'explain' => true), +						'board_index_text'		=> array('lang' => 'BOARD_INDEX_TEXT',		'validate' => 'string',	'type' => 'text:40:255', 'explain' => true),  						'board_disable'			=> array('lang' => 'DISABLE_BOARD',			'validate' => 'bool',	'type' => 'custom', 'method' => 'board_disable', 'explain' => true),  						'board_disable_msg'		=> false,  						'default_lang'			=> array('lang' => 'DEFAULT_LANGUAGE',		'validate' => 'lang',	'type' => 'select', 'function' => 'language_select', 'params' => array('{CONFIG_VALUE}'), 'explain' => false), @@ -397,6 +398,7 @@ class acp_board  					'vars'	=> array(  						'legend1'				=> 'ACP_SECURITY_SETTINGS',  						'allow_autologin'		=> array('lang' => 'ALLOW_AUTOLOGIN',		'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true), +						'allow_password_reset'	=> array('lang' => 'ALLOW_PASSWORD_RESET',	'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true),  						'max_autologin_time'	=> array('lang' => 'AUTOLOGIN_LENGTH',		'validate' => 'int:0',	'type' => 'text:5:5', 'explain' => true, 'append' => ' ' . $user->lang['DAYS']),  						'ip_check'				=> array('lang' => 'IP_VALID',				'validate' => 'int',	'type' => 'custom', 'method' => 'select_ip_check', 'explain' => true),  						'browser_check'			=> array('lang' => 'BROWSER_VALID',			'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 24211196bd..c52e4e0473 100644 --- a/phpBB/includes/acp/acp_extensions.php +++ b/phpBB/includes/acp/acp_extensions.php @@ -54,7 +54,7 @@ class acp_extensions  		// If they've specified an extension, let's load the metadata manager and validate it.  		if ($ext_name)  		{ -			$md_manager = new phpbb_extension_metadata_manager($ext_name, $db, $phpbb_extension_manager, $phpbb_root_path, ".$phpEx", $template, $config); +			$md_manager = new phpbb_extension_metadata_manager($ext_name, $config, $phpbb_extension_manager, $template, $phpbb_root_path);  			try  			{ @@ -81,7 +81,7 @@ class acp_extensions  			case 'enable_pre':  				if (!$md_manager->validate_enable())  				{ -					trigger_error($user->lang['EXTENSION_NOT_AVAILABLE'] . adm_back_link($this->u_action)); +					trigger_error($user->lang['EXTENSION_NOT_AVAILABLE'] . adm_back_link($this->u_action), E_USER_WARNING);  				}  				if ($phpbb_extension_manager->enabled($ext_name)) @@ -100,7 +100,7 @@ class acp_extensions  			case 'enable':  				if (!$md_manager->validate_enable())  				{ -					trigger_error($user->lang['EXTENSION_NOT_AVAILABLE'] . adm_back_link($this->u_action)); +					trigger_error($user->lang['EXTENSION_NOT_AVAILABLE'] . adm_back_link($this->u_action), E_USER_WARNING);  				}  				try diff --git a/phpBB/includes/acp/acp_groups.php b/phpBB/includes/acp/acp_groups.php index 8cae0151c8..865810687b 100644 --- a/phpBB/includes/acp/acp_groups.php +++ b/phpBB/includes/acp/acp_groups.php @@ -148,57 +148,58 @@ class acp_groups  						'action'	=> $action))  					);  				} +			break; -				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; +				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 +					do +					{ +						$sql = 'SELECT user_id +							FROM ' . USER_GROUP_TABLE . " +							WHERE group_id = $group_id +							ORDER BY user_id"; +						$result = $db->sql_query_limit($sql, 200, $start); + +						$mark_ary = array(); +						if ($row = $db->sql_fetchrow($result))  						{ -							$sql = 'SELECT user_id -								FROM ' . USER_GROUP_TABLE . " -								WHERE group_id = $group_id -								ORDER BY user_id"; -							$result = $db->sql_query_limit($sql, 200, $start); - -							$mark_ary = array(); -							if ($row = $db->sql_fetchrow($result)) +							do  							{ -								do -								{ -									$mark_ary[] = $row['user_id']; -								} -								while ($row = $db->sql_fetchrow($result)); +								$mark_ary[] = $row['user_id']; +							} +							while ($row = $db->sql_fetchrow($result)); -								group_user_attributes('default', $group_id, $mark_ary, false, $group_name, $group_row); +							group_user_attributes('default', $group_id, $mark_ary, false, $group_name, $group_row); -								$start = (sizeof($mark_ary) < 200) ? 0 : $start + 200; -							} -							else -							{ -								$start = 0; -							} -							$db->sql_freeresult($result); +							$start = (sizeof($mark_ary) < 200) ? 0 : $start + 200;  						} -						while ($start); -							 -						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)) -						); +						else +						{ +							$start = 0; +						} +						$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 +				{ +					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))  				{ diff --git a/phpBB/includes/acp/acp_inactive.php b/phpBB/includes/acp/acp_inactive.php index e61115f681..de4679b58d 100644 --- a/phpBB/includes/acp/acp_inactive.php +++ b/phpBB/includes/acp/acp_inactive.php @@ -115,7 +115,7 @@ class acp_inactive  							{  								$messenger->template('admin_welcome_activated', $row['user_lang']); -								$messenger->to($row['user_email'], $row['username']); +								$messenger->set_addresses($row);  								$messenger->anti_abuse_headers($config, $user); @@ -203,8 +203,7 @@ class acp_inactive  						{  							$messenger->template('user_remind_inactive', $row['user_lang']); -							$messenger->to($row['user_email'], $row['username']); -							$messenger->im($row['user_jabber'], $row['username']); +							$messenger->set_addresses($row);  							$messenger->anti_abuse_headers($config, $user); diff --git a/phpBB/includes/acp/acp_modules.php b/phpBB/includes/acp/acp_modules.php index 7c2ea86122..ab416fb406 100644 --- a/phpBB/includes/acp/acp_modules.php +++ b/phpBB/includes/acp/acp_modules.php @@ -544,81 +544,60 @@ class acp_modules  	*/  	function get_module_infos($module = '', $module_class = false, $use_all_available = false)  	{ -		global $phpbb_root_path, $phpEx; +		global $phpbb_extension_manager, $phpbb_root_path, $phpEx;  		$module_class = ($module_class === false) ? $this->module_class : $module_class;  		$directory = $phpbb_root_path . 'includes/' . $module_class . '/info/';  		$fileinfo = array(); -		if (!$module) -		{ -			global $phpbb_extension_manager; - -			$finder = $phpbb_extension_manager->get_finder(); +		$finder = $phpbb_extension_manager->get_finder(); -			$modules = $finder -				->extension_suffix('_module') -				->extension_directory("/$module_class") -				->core_path("includes/$module_class/info/") -				->core_prefix($module_class . '_') -				->get_classes(true, $use_all_available); +		$modules = $finder +			->extension_suffix('_module') +			->extension_directory("/$module_class") +			->core_path("includes/$module_class/info/") +			->core_prefix($module_class . '_') +			->get_classes(true, $use_all_available); -			foreach ($modules as $module) +		foreach ($modules as $cur_module) +		{ +			// Skip entries we do not need if we know the module we are +			// looking for +			if ($module && strpos($cur_module, $module) === false)  			{ -				$info_class = preg_replace('/_module$/', '_info', $module); - -				// If the class does not exist it might be following the old -				// format. phpbb_acp_info_acp_foo needs to be turned into -				// acp_foo_info and the respective file has to be included -				// manually because it does not support auto loading -				if (!class_exists($info_class)) -				{ -					$info_class = str_replace("phpbb_{$module_class}_info_", '', $module) . '_info'; -					if (file_exists($directory . $info_class . '.' . $phpEx)) -					{ -						include($directory . $info_class . '.' . $phpEx); -					} -				} - -				if (class_exists($info_class)) -				{ -					$info = new $info_class(); -					$module_info = $info->module(); - -					$main_class = (isset($module_info['filename'])) ? $module_info['filename'] : $module; - -					$fileinfo[$main_class] = $module_info; -				} +				continue;  			} -			ksort($fileinfo); -		} -		else -		{ -			$info_class = preg_replace('/_module$/', '_info', $module); +			$info_class = preg_replace('/_module$/', '_info', $cur_module); +			// If the class does not exist it might be following the old +			// format. phpbb_acp_info_acp_foo needs to be turned into +			// acp_foo_info and the respective file has to be included +			// manually because it does not support auto loading  			if (!class_exists($info_class))  			{ -				$info_class = $module . '_info'; -				if (!class_exists($info_class) && file_exists($directory . $module . '.' . $phpEx)) +				$info_class_file = str_replace("phpbb_{$module_class}_info_", '', $cur_module); +				$info_class = $info_class_file . '_info'; +				if (!class_exists($info_class) && file_exists($directory . $info_class_file . '.' . $phpEx))  				{ -					include($directory . $module . '.' . $phpEx); +					include($directory . $info_class_file . '.' . $phpEx);  				}  			} -			// Get module title tag  			if (class_exists($info_class))  			{  				$info = new $info_class();  				$module_info = $info->module(); -				$main_class = (isset($module_info['filename'])) ? $module_info['filename'] : $module; +				$main_class = (isset($module_info['filename'])) ? $module_info['filename'] : $cur_module;  				$fileinfo[$main_class] = $module_info;  			}  		} +		ksort($fileinfo); +  		return $fileinfo;  	} diff --git a/phpBB/includes/acp/acp_prune.php b/phpBB/includes/acp/acp_prune.php index a5dc02849a..4234ec1505 100644 --- a/phpBB/includes/acp/acp_prune.php +++ b/phpBB/includes/acp/acp_prune.php @@ -331,7 +331,7 @@ class acp_prune  			$s_find_active_time .= '<option value="' . $key . '">' . $value . '</option>';  		} -		$s_group_list = ''; +		$s_group_list = '<option value="0"></option>';  		$sql = 'SELECT group_id, group_name  			FROM ' . GROUPS_TABLE . '  			WHERE group_type <> ' . GROUP_SPECIAL . ' @@ -340,7 +340,7 @@ class acp_prune  		while ($row = $db->sql_fetchrow($result))  		{ -			$s_group_list .= '<option value="' . $row['group_id'] . '">' . $row['group_name'] . '</select>'; +			$s_group_list .= '<option value="' . $row['group_id'] . '">' . $row['group_name'] . '</option>';  		}  		$db->sql_freeresult($result); @@ -491,11 +491,12 @@ class acp_prune  		if ($group_id)  		{ -			$sql = 'SELECT user_id -				FROM ' . USER_GROUP_TABLE . ' -				WHERE group_id = ' . (int) $group_id . ' -					AND user_pending = 0 -					AND ' . $db->sql_in_set('user_id', $user_ids, false, true); +			$sql = 'SELECT u.user_id, u.username +				FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . ' u +				WHERE ug.group_id = ' . (int) $group_id . ' +					AND ug.user_pending = 0 +					AND ' . $db->sql_in_set('ug.user_id', $user_ids, false, true) . ' +					AND u.user_id = ug.user_id';  			$result = $db->sql_query($sql);  			// we're performing an intersection operation, so all the relevant users @@ -504,24 +505,19 @@ class acp_prune  			$user_ids = $usernames = array();  			while ($row = $db->sql_fetchrow($result))  			{ -				$user_ids[] = $row['poster_id']; +				$user_ids[] = $row['user_id']; +				$usernames[$row['user_id']] = $row['username'];  			}  			$db->sql_freeresult($result); - -			// only get usernames if they are needed (not part of some later query) -			if (!$posts_on_queue) -			{ -				// this is an additional query aginst the users table -				user_get_id_name($user_ids, $usernames); -			}  		}  		if ($posts_on_queue)  		{ -			$sql = 'SELECT poster_id, COUNT(post_id) AS queue_posts -				FROM ' . POSTS_TABLE . ' -				WHERE ' . $db->sql_in_set('poster_id', $user_ids, false, true) . ' -				GROUP BY poster_id +			$sql = 'SELECT u.user_id, u.username, COUNT(p.post_id) AS queue_posts +				FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u +				WHERE ' . $db->sql_in_set('p.poster_id', $user_ids, false, true) . ' +					AND u.user_id = p.poster_id +				GROUP BY p.poster_id  				HAVING queue_posts ' . $key_match[$queue_select] . ' ' . $posts_on_queue;  			$result = $db->sql_query($result); @@ -529,12 +525,10 @@ class acp_prune  			$user_ids = $usernames = array();  			while ($row = $db->sql_fetchrow($result))  			{ -				$user_ids[] = $row['poster_id']; +				$user_ids[] = $row['user_id']; +				$usernames[$row['user_id']] = $row['username'];  			}  			$db->sql_freeresult($result); - -			// do an additional query to get the correct set of usernames -			user_get_id_name($user_ids, $usernames);  		}  	}  } diff --git a/phpBB/includes/acp/acp_users.php b/phpBB/includes/acp/acp_users.php index 8f4a22b61f..c8542ddbe7 100644 --- a/phpBB/includes/acp/acp_users.php +++ b/phpBB/includes/acp/acp_users.php @@ -347,7 +347,7 @@ class acp_users  								$messenger->template($email_template, $user_row['user_lang']); -								$messenger->to($user_row['user_email'], $user_row['username']); +								$messenger->set_addresses($user_row);  								$messenger->anti_abuse_headers($config, $user); @@ -402,7 +402,7 @@ class acp_users  									$messenger->template('admin_welcome_activated', $user_row['user_lang']); -									$messenger->to($user_row['user_email'], $user_row['username']); +									$messenger->set_addresses($user_row);  									$messenger->anti_abuse_headers($config, $user); diff --git a/phpBB/includes/avatar/driver/gravatar.php b/phpBB/includes/avatar/driver/gravatar.php index 2e2ae2071f..d559da1c0d 100644 --- a/phpBB/includes/avatar/driver/gravatar.php +++ b/phpBB/includes/avatar/driver/gravatar.php @@ -74,7 +74,7 @@ class phpbb_avatar_driver_gravatar extends phpbb_avatar_driver  		if (!function_exists('validate_data'))  		{ -			require($this->phpbb_root_path . 'includes/functions_user' . $this->php_ext); +			require($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);  		}  		$validate_array = validate_data( diff --git a/phpBB/includes/avatar/driver/remote.php b/phpBB/includes/avatar/driver/remote.php index 3661e16160..7da58107a1 100644 --- a/phpBB/includes/avatar/driver/remote.php +++ b/phpBB/includes/avatar/driver/remote.php @@ -63,7 +63,7 @@ class phpbb_avatar_driver_remote extends phpbb_avatar_driver  		if (!function_exists('validate_data'))  		{ -			require($this->phpbb_root_path . 'includes/functions_user' . $this->php_ext); +			require($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);  		}  		$validate_array = validate_data( @@ -117,7 +117,7 @@ class phpbb_avatar_driver_remote extends phpbb_avatar_driver  		if (!class_exists('fileupload'))  		{ -			include($this->phpbb_root_path . 'includes/functions_upload' . $this->php_ext); +			include($this->phpbb_root_path . 'includes/functions_upload.' . $this->php_ext);  		}  		$types = fileupload::image_types(); diff --git a/phpBB/includes/avatar/driver/upload.php b/phpBB/includes/avatar/driver/upload.php index f91d170d7c..19737693fd 100644 --- a/phpBB/includes/avatar/driver/upload.php +++ b/phpBB/includes/avatar/driver/upload.php @@ -27,7 +27,7 @@ class phpbb_avatar_driver_upload extends phpbb_avatar_driver  	public function get_data($row, $ignore_config = false)  	{  		return array( -			'src' => $this->phpbb_root_path . 'download/file' . $this->php_ext . '?avatar=' . $row['avatar'], +			'src' => $this->phpbb_root_path . 'download/file.' . $this->php_ext . '?avatar=' . $row['avatar'],  			'width' => $row['avatar_width'],  			'height' => $row['avatar_height'],  		); @@ -63,7 +63,7 @@ class phpbb_avatar_driver_upload extends phpbb_avatar_driver  		if (!class_exists('fileupload'))  		{ -			include($this->phpbb_root_path . 'includes/functions_upload' . $this->php_ext); +			include($this->phpbb_root_path . 'includes/functions_upload.' . $this->php_ext);  		}  		$upload = new fileupload('AVATAR_', $this->allowed_extensions, $this->config['avatar_filesize'], $this->config['avatar_min_width'], $this->config['avatar_min_height'], $this->config['avatar_max_width'], $this->config['avatar_max_height'], (isset($this->config['mime_triggers']) ? explode('|', $this->config['mime_triggers']) : false)); diff --git a/phpBB/includes/bbcode.php b/phpBB/includes/bbcode.php index e8681420d4..c198abeb54 100644 --- a/phpBB/includes/bbcode.php +++ b/phpBB/includes/bbcode.php @@ -133,7 +133,7 @@ class bbcode  			$this->template_bitfield = new bitfield($user->style['bbcode_bitfield']);  			$style_resource_locator = new phpbb_style_resource_locator(); -			$style_path_provider = new phpbb_style_extension_path_provider($phpbb_extension_manager, new phpbb_style_path_provider()); +			$style_path_provider = new phpbb_style_extension_path_provider($phpbb_extension_manager, new phpbb_style_path_provider(), $phpbb_root_path);  			$template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, new phpbb_template_context(), $phpbb_extension_manager);  			$style = new phpbb_style($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, $style_path_provider, $template);  			$style->set_style(); diff --git a/phpBB/includes/captcha/plugins/phpbb_recaptcha_plugin.php b/phpBB/includes/captcha/plugins/phpbb_recaptcha_plugin.php index 83d40bbba7..cb21b04ec5 100644 --- a/phpBB/includes/captcha/plugins/phpbb_recaptcha_plugin.php +++ b/phpBB/includes/captcha/plugins/phpbb_recaptcha_plugin.php @@ -270,7 +270,7 @@ class phpbb_recaptcha extends phpbb_default_captcha  		$response = '';  		if (false == ($fs = @fsockopen($host, $port, $errno, $errstr, 10)))  		{ -			trigger_error('Could not open socket', E_USER_ERROR); +			trigger_error('RECAPTCHA_SOCKET_ERROR', E_USER_ERROR);  		}  		fwrite($fs, $http_request); diff --git a/phpBB/includes/class_loader.php b/phpBB/includes/class_loader.php index 6082800908..02a2d584dc 100644 --- a/phpBB/includes/class_loader.php +++ b/phpBB/includes/class_loader.php @@ -52,7 +52,7 @@ class phpbb_class_loader  	* @param string $php_ext The file extension for PHP files  	* @param phpbb_cache_driver_interface $cache An implementation of the phpBB cache interface.  	*/ -	public function __construct($prefix, $path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null) +	public function __construct($prefix, $path, $php_ext = 'php', phpbb_cache_driver_interface $cache = null)  	{  		$this->prefix = $prefix;  		$this->path = $path; @@ -111,7 +111,7 @@ class phpbb_class_loader  	{  		if (isset($this->cached_paths[$class]))  		{ -			return $this->path . $this->cached_paths[$class] . $this->php_ext; +			return $this->path . $this->cached_paths[$class] . '.' . $this->php_ext;  		}  		if (!preg_match('/^' . $this->prefix . '[a-zA-Z0-9_]+$/', $class)) @@ -136,7 +136,7 @@ class phpbb_class_loader  		$relative_path = $dirs . implode(array_slice($parts, $i, sizeof($parts) - $i), '_'); -		if (!file_exists($this->path . $relative_path . $this->php_ext)) +		if (!file_exists($this->path . $relative_path . '.' . $this->php_ext))  		{  			return false;  		} @@ -147,7 +147,7 @@ class phpbb_class_loader  			$this->cache->put('class_loader_' . $this->prefix, $this->cached_paths);  		} -		return $this->path . $relative_path . $this->php_ext; +		return $this->path . $relative_path . '.' . $this->php_ext;  	}  	/** diff --git a/phpBB/includes/controller/helper.php b/phpBB/includes/controller/helper.php index 6cacc8fefa..74410ddfd1 100644 --- a/phpBB/includes/controller/helper.php +++ b/phpBB/includes/controller/helper.php @@ -85,17 +85,39 @@ class phpbb_controller_helper  	}  	/** -	* Easily generate a URL +	* Generate a URL  	* -	* @param array $url_parts Each array element is a 'folder' -	* 		i.e. array('my', 'ext') maps to ./app.php/my/ext -	* @param mixed $query The Query string, passed directly into the second -	*		argument of append_sid() -	* @return string A URL that has already been run through append_sid() +	* @param string	$route		The route to travel +	* @param mixed	$params		String or array of additional url parameters +	* @param bool	$is_amp		Is url using & (true) or & (false) +	* @param string	$session_id	Possibility to use a custom session id instead of the global one +	* @return string The URL already passed through append_sid()  	*/ -	public function url(array $url_parts, $query = '') +	public function url($route, $params = false, $is_amp = true, $session_id = false)  	{ -		return append_sid($this->phpbb_root_path . implode('/', $url_parts), $query); +		$route_params = ''; +		if (($route_delim = strpos($route, '?')) !== false) +		{ +			$route_params = substr($route, $route_delim); +			$route = substr($route, 0, $route_delim); +		} + +		if (is_array($params) && !empty($params)) +		{ +			$params = array_merge(array( +				'controller' => $route, +			), $params); +		} +		else if (is_string($params) && $params) +		{ +			$params = 'controller=' . $route . (($is_amp) ? '&' : '&') . $params; +		} +		else +		{ +			$params = array('controller' => $route); +		} + +		return append_sid($this->phpbb_root_path . 'app.' . $this->php_ext . $route_params, $params, $is_amp, $session_id);  	}  	/** diff --git a/phpBB/includes/db/driver/driver.php b/phpBB/includes/db/driver/driver.php index 8dda94bc2c..b915ee081b 100644 --- a/phpBB/includes/db/driver/driver.php +++ b/phpBB/includes/db/driver/driver.php @@ -568,12 +568,12 @@ class phpbb_db_driver  	* Run more than one insert statement.  	*  	* @param string $table table name to run the statements on -	* @param array &$sql_ary multi-dimensional array holding the statement data. +	* @param array $sql_ary multi-dimensional array holding the statement data.  	*  	* @return bool false if no statements were executed.  	* @access public  	*/ -	function sql_multi_insert($table, &$sql_ary) +	function sql_multi_insert($table, $sql_ary)  	{  		if (!sizeof($sql_ary))  		{ diff --git a/phpBB/includes/db/driver/mssql_base.php b/phpBB/includes/db/driver/mssql_base.php new file mode 100644 index 0000000000..56c111c871 --- /dev/null +++ b/phpBB/includes/db/driver/mssql_base.php @@ -0,0 +1,65 @@ +<?php +/** +* +* @package dbal +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* MSSQL Database Base Abstraction Layer +* @package dbal + */ +abstract class phpbb_db_driver_mssql_base extends phpbb_db_driver +{ +	/** +	* {@inheritDoc} +	*/ +	public function sql_concatenate($expr1, $expr2) +	{ +		return $expr1 . ' + ' . $expr2; +	} + +	/** +	* Escape string used in sql query +	*/ +	function sql_escape($msg) +	{ +		return str_replace(array("'", "\0"), array("''", ''), $msg); +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_lower_text($column_name) +	{ +		return "LOWER(SUBSTRING($column_name, 1, DATALENGTH($column_name)))"; +	} + +	/** +	* Build LIKE expression +	* @access private +	*/ +	function _sql_like_expression($expression) +	{ +		return $expression . " ESCAPE '\\'"; +	} + +	/** +	* Build db-specific query data +	* @access private +	*/ +	function _sql_custom_build($stage, $data) +	{ +		return $data; +	} +} diff --git a/phpBB/includes/db/driver/mssql_odbc.php b/phpBB/includes/db/driver/mssql_odbc.php index f7834443eb..cde9d332ba 100644 --- a/phpBB/includes/db/driver/mssql_odbc.php +++ b/phpBB/includes/db/driver/mssql_odbc.php @@ -26,7 +26,7 @@ if (!defined('IN_PHPBB'))  *  * @package dbal  */ -class phpbb_db_driver_mssql_odbc extends phpbb_db_driver +class phpbb_db_driver_mssql_odbc extends phpbb_db_driver_mssql_base  {  	var $last_query_text = '';  	var $connect_error = ''; @@ -126,14 +126,6 @@ class phpbb_db_driver_mssql_odbc extends phpbb_db_driver  	}  	/** -	* {@inheritDoc} -	*/ -	public function sql_concatenate($expr1, $expr2) -	{ -		return $expr1 . ' + ' . $expr2; -	} - -	/**  	* SQL Transaction  	* @access private  	*/ @@ -326,40 +318,6 @@ class phpbb_db_driver_mssql_odbc extends phpbb_db_driver  	}  	/** -	* Escape string used in sql query -	*/ -	function sql_escape($msg) -	{ -		return str_replace(array("'", "\0"), array("''", ''), $msg); -	} - -	/** -	* {@inheritDoc} -	*/ -	function sql_lower_text($column_name) -	{ -		return "LOWER(SUBSTRING($column_name, 1, DATALENGTH($column_name)))"; -	} - -	/** -	* Build LIKE expression -	* @access private -	*/ -	function _sql_like_expression($expression) -	{ -		return $expression . " ESCAPE '\\'"; -	} - -	/** -	* Build db-specific query data -	* @access private -	*/ -	function _sql_custom_build($stage, $data) -	{ -		return $data; -	} - -	/**  	* return sql error array  	* @access private  	*/ diff --git a/phpBB/includes/db/driver/mssqlnative.php b/phpBB/includes/db/driver/mssqlnative.php index 656cbd2437..6f433e10cf 100644 --- a/phpBB/includes/db/driver/mssqlnative.php +++ b/phpBB/includes/db/driver/mssqlnative.php @@ -191,7 +191,7 @@ class result_mssqlnative  /**  * @package dbal  */ -class phpbb_db_driver_mssqlnative extends phpbb_db_driver +class phpbb_db_driver_mssqlnative extends phpbb_db_driver_mssql_base  {  	var $m_insert_id = NULL;  	var $last_query_text = ''; @@ -259,14 +259,6 @@ class phpbb_db_driver_mssqlnative extends phpbb_db_driver  	/**  	* {@inheritDoc}  	*/ -	public function sql_concatenate($expr1, $expr2) -	{ -		return $expr1 . ' + ' . $expr2; -	} - -	/** -	* {@inheritDoc} -	*/  	function sql_buffer_nested_transactions()  	{  		return true; @@ -491,31 +483,6 @@ class phpbb_db_driver_mssqlnative extends phpbb_db_driver  	}  	/** -	* Escape string used in sql query -	*/ -	function sql_escape($msg) -	{ -		return str_replace(array("'", "\0"), array("''", ''), $msg); -	} - -	/** -	* {@inheritDoc} -	*/ -	function sql_lower_text($column_name) -	{ -		return "LOWER(SUBSTRING($column_name, 1, DATALENGTH($column_name)))"; -	} - -	/** -	* Build LIKE expression -	* @access private -	*/ -	function _sql_like_expression($expression) -	{ -		return $expression . " ESCAPE '\\'"; -	} - -	/**  	* return sql error array  	* @access private  	*/ @@ -561,15 +528,6 @@ class phpbb_db_driver_mssqlnative extends phpbb_db_driver  	}  	/** -	* Build db-specific query data -	* @access private -	*/ -	function _sql_custom_build($stage, $data) -	{ -		return $data; -	} - -	/**  	* Close sql connection  	* @access private  	*/ diff --git a/phpBB/includes/db/driver/mysql.php b/phpBB/includes/db/driver/mysql.php index 9de7283a42..f3744ac09d 100644 --- a/phpBB/includes/db/driver/mysql.php +++ b/phpBB/includes/db/driver/mysql.php @@ -24,7 +24,7 @@ if (!defined('IN_PHPBB'))  * MySQL 5.0+  * @package dbal  */ -class phpbb_db_driver_mysql extends phpbb_db_driver +class phpbb_db_driver_mysql extends phpbb_db_driver_mysql_base  {  	var $multi_insert = true;  	var $connect_error = ''; @@ -136,14 +136,6 @@ class phpbb_db_driver_mysql extends phpbb_db_driver  	}  	/** -	* {@inheritDoc} -	*/ -	public function sql_concatenate($expr1, $expr2) -	{ -		return 'CONCAT(' . $expr1 . ', ' . $expr2 . ')'; -	} - -	/**  	* SQL Transaction  	* @access private  	*/ @@ -227,25 +219,6 @@ class phpbb_db_driver_mysql extends phpbb_db_driver  	}  	/** -	* Build LIMIT query -	*/ -	function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) -	{ -		$this->query_result = false; - -		// if $total is set to 0 we do not want to limit the number of rows -		if ($total == 0) -		{ -			// Having a value of -1 was always a bug -			$total = '18446744073709551615'; -		} - -		$query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total); - -		return $this->sql_query($query, $cache_ttl); -	} - -	/**  	* Return number of affected rows  	*/  	function sql_affectedrows() @@ -342,101 +315,6 @@ class phpbb_db_driver_mysql extends phpbb_db_driver  	}  	/** -	* Gets the estimated number of rows in a specified table. -	* -	* @param string $table_name		Table name -	* -	* @return string				Number of rows in $table_name. -	*								Prefixed with ~ if estimated (otherwise exact). -	* -	* @access public -	*/ -	function get_estimated_row_count($table_name) -	{ -		$table_status = $this->get_table_status($table_name); - -		if (isset($table_status['Engine'])) -		{ -			if ($table_status['Engine'] === 'MyISAM') -			{ -				return $table_status['Rows']; -			} -			else if ($table_status['Engine'] === 'InnoDB' && $table_status['Rows'] > 100000) -			{ -				return '~' . $table_status['Rows']; -			} -		} - -		return parent::get_row_count($table_name); -	} - -	/** -	* Gets the exact number of rows in a specified table. -	* -	* @param string $table_name		Table name -	* -	* @return string				Exact number of rows in $table_name. -	* -	* @access public -	*/ -	function get_row_count($table_name) -	{ -		$table_status = $this->get_table_status($table_name); - -		if (isset($table_status['Engine']) && $table_status['Engine'] === 'MyISAM') -		{ -			return $table_status['Rows']; -		} - -		return parent::get_row_count($table_name); -	} - -	/** -	* Gets some information about the specified table. -	* -	* @param string $table_name		Table name -	* -	* @return array -	* -	* @access protected -	*/ -	function get_table_status($table_name) -	{ -		$sql = "SHOW TABLE STATUS -			LIKE '" . $this->sql_escape($table_name) . "'"; -		$result = $this->sql_query($sql); -		$table_status = $this->sql_fetchrow($result); -		$this->sql_freeresult($result); - -		return $table_status; -	} - -	/** -	* Build LIKE expression -	* @access private -	*/ -	function _sql_like_expression($expression) -	{ -		return $expression; -	} - -	/** -	* Build db-specific query data -	* @access private -	*/ -	function _sql_custom_build($stage, $data) -	{ -		switch ($stage) -		{ -			case 'FROM': -				$data = '(' . $data . ')'; -			break; -		} - -		return $data; -	} - -	/**  	* return sql error array  	* @access private  	*/ diff --git a/phpBB/includes/db/driver/mysql_base.php b/phpBB/includes/db/driver/mysql_base.php new file mode 100644 index 0000000000..ba44ea61aa --- /dev/null +++ b/phpBB/includes/db/driver/mysql_base.php @@ -0,0 +1,145 @@ +<?php +/** +* +* @package dbal +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Abstract MySQL Database Base Abstraction Layer +* @package dbal +*/ +abstract class phpbb_db_driver_mysql_base extends phpbb_db_driver +{ +	/** +	* {@inheritDoc} +	*/ +	public function sql_concatenate($expr1, $expr2) +	{ +		return 'CONCAT(' . $expr1 . ', ' . $expr2 . ')'; +	} + +	/** +	* Build LIMIT query +	*/ +	function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) +	{ +		$this->query_result = false; + +		// if $total is set to 0 we do not want to limit the number of rows +		if ($total == 0) +		{ +			// MySQL 4.1+ no longer supports -1 in limit queries +			$total = '18446744073709551615'; +		} + +		$query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total); + +		return $this->sql_query($query, $cache_ttl); +	} + +	/** +	* Gets the estimated number of rows in a specified table. +	* +	* @param string $table_name		Table name +	* +	* @return string				Number of rows in $table_name. +	*								Prefixed with ~ if estimated (otherwise exact). +	* +	* @access public +	*/ +	function get_estimated_row_count($table_name) +	{ +		$table_status = $this->get_table_status($table_name); + +		if (isset($table_status['Engine'])) +		{ +			if ($table_status['Engine'] === 'MyISAM') +			{ +				return $table_status['Rows']; +			} +			else if ($table_status['Engine'] === 'InnoDB' && $table_status['Rows'] > 100000) +			{ +				return '~' . $table_status['Rows']; +			} +		} + +		return parent::get_row_count($table_name); +	} + +	/** +	* Gets the exact number of rows in a specified table. +	* +	* @param string $table_name		Table name +	* +	* @return string				Exact number of rows in $table_name. +	* +	* @access public +	*/ +	function get_row_count($table_name) +	{ +		$table_status = $this->get_table_status($table_name); + +		if (isset($table_status['Engine']) && $table_status['Engine'] === 'MyISAM') +		{ +			return $table_status['Rows']; +		} + +		return parent::get_row_count($table_name); +	} + +	/** +	* Gets some information about the specified table. +	* +	* @param string $table_name		Table name +	* +	* @return array +	* +	* @access protected +	*/ +	function get_table_status($table_name) +	{ +		$sql = "SHOW TABLE STATUS +			LIKE '" . $this->sql_escape($table_name) . "'"; +		$result = $this->sql_query($sql); +		$table_status = $this->sql_fetchrow($result); +		$this->sql_freeresult($result); + +		return $table_status; +	} + +	/** +	* Build LIKE expression +	* @access private +	*/ +	function _sql_like_expression($expression) +	{ +		return $expression; +	} + +	/** +	* Build db-specific query data +	* @access private +	*/ +	function _sql_custom_build($stage, $data) +	{ +		switch ($stage) +		{ +			case 'FROM': +				$data = '(' . $data . ')'; +			break; +		} + +		return $data; +	} +} diff --git a/phpBB/includes/db/driver/mysqli.php b/phpBB/includes/db/driver/mysqli.php index 7448bf1670..0f7a73ee6e 100644 --- a/phpBB/includes/db/driver/mysqli.php +++ b/phpBB/includes/db/driver/mysqli.php @@ -21,7 +21,7 @@ if (!defined('IN_PHPBB'))  * MySQL 4.1+ or MySQL 5.0+  * @package dbal  */ -class phpbb_db_driver_mysqli extends phpbb_db_driver +class phpbb_db_driver_mysqli extends phpbb_db_driver_mysql_base  {  	var $multi_insert = true;  	var $connect_error = ''; @@ -103,6 +103,7 @@ class phpbb_db_driver_mysqli extends phpbb_db_driver  	/**  	* Version information about used database +	* @param bool $raw if true, only return the fetched sql_server_version  	* @param bool $use_cache If true, it is safe to retrieve the value from the cache  	* @return string sql server version  	*/ @@ -128,14 +129,6 @@ class phpbb_db_driver_mysqli extends phpbb_db_driver  	}  	/** -	* {@inheritDoc} -	*/ -	public function sql_concatenate($expr1, $expr2) -	{ -		return 'CONCAT(' . $expr1 . ', ' . $expr2 . ')'; -	} - -	/**  	* SQL Transaction  	* @access private  	*/ @@ -218,25 +211,6 @@ class phpbb_db_driver_mysqli extends phpbb_db_driver  	}  	/** -	* Build LIMIT query -	*/ -	function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) -	{ -		$this->query_result = false; - -		// if $total is set to 0 we do not want to limit the number of rows -		if ($total == 0) -		{ -			// MySQL 4.1+ no longer supports -1 in limit queries -			$total = '18446744073709551615'; -		} - -		$query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total); - -		return $this->sql_query($query, $cache_ttl); -	} - -	/**  	* Return number of affected rows  	*/  	function sql_affectedrows() @@ -328,101 +302,6 @@ class phpbb_db_driver_mysqli extends phpbb_db_driver  	}  	/** -	* Gets the estimated number of rows in a specified table. -	* -	* @param string $table_name		Table name -	* -	* @return string				Number of rows in $table_name. -	*								Prefixed with ~ if estimated (otherwise exact). -	* -	* @access public -	*/ -	function get_estimated_row_count($table_name) -	{ -		$table_status = $this->get_table_status($table_name); - -		if (isset($table_status['Engine'])) -		{ -			if ($table_status['Engine'] === 'MyISAM') -			{ -				return $table_status['Rows']; -			} -			else if ($table_status['Engine'] === 'InnoDB' && $table_status['Rows'] > 100000) -			{ -				return '~' . $table_status['Rows']; -			} -		} - -		return parent::get_row_count($table_name); -	} - -	/** -	* Gets the exact number of rows in a specified table. -	* -	* @param string $table_name		Table name -	* -	* @return string				Exact number of rows in $table_name. -	* -	* @access public -	*/ -	function get_row_count($table_name) -	{ -		$table_status = $this->get_table_status($table_name); - -		if (isset($table_status['Engine']) && $table_status['Engine'] === 'MyISAM') -		{ -			return $table_status['Rows']; -		} - -		return parent::get_row_count($table_name); -	} - -	/** -	* Gets some information about the specified table. -	* -	* @param string $table_name		Table name -	* -	* @return array -	* -	* @access protected -	*/ -	function get_table_status($table_name) -	{ -		$sql = "SHOW TABLE STATUS -			LIKE '" . $this->sql_escape($table_name) . "'"; -		$result = $this->sql_query($sql); -		$table_status = $this->sql_fetchrow($result); -		$this->sql_freeresult($result); - -		return $table_status; -	} - -	/** -	* Build LIKE expression -	* @access private -	*/ -	function _sql_like_expression($expression) -	{ -		return $expression; -	} - -	/** -	* Build db-specific query data -	* @access private -	*/ -	function _sql_custom_build($stage, $data) -	{ -		switch ($stage) -		{ -			case 'FROM': -				$data = '(' . $data . ')'; -			break; -		} - -		return $data; -	} - -	/**  	* return sql error array  	* @access private  	*/ diff --git a/phpBB/includes/db/migration/data/310/boardindex.php b/phpBB/includes/db/migration/data/310/boardindex.php new file mode 100644 index 0000000000..965e32c15c --- /dev/null +++ b/phpBB/includes/db/migration/data/310/boardindex.php @@ -0,0 +1,23 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_310_boardindex extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return isset($this->config['board_index_text']); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('board_index_text', '')), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/310/forgot_password.php b/phpBB/includes/db/migration/data/310/forgot_password.php new file mode 100644 index 0000000000..a553e51f35 --- /dev/null +++ b/phpBB/includes/db/migration/data/310/forgot_password.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_310_forgot_password extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return isset($this->config['allow_password_reset']); +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_30x_3_0_11'); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('allow_password_reset', 1)), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/310/jquery_update.php b/phpBB/includes/db/migration/data/310/jquery_update.php new file mode 100644 index 0000000000..dc49f74fcb --- /dev/null +++ b/phpBB/includes/db/migration/data/310/jquery_update.php @@ -0,0 +1,31 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_310_jquery_update extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		return $this->config['load_jquery_url'] !== '//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js'; +	} + +	static public function depends_on() +	{ +		return array( +			'phpbb_db_migration_data_310_dev', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('load_jquery_url', '//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js')), +		); +	} + +} diff --git a/phpBB/includes/db/migration/data/310/style_update_p1.php b/phpBB/includes/db/migration/data/310/style_update_p1.php index e324ce7f24..d43537559d 100644 --- a/phpBB/includes/db/migration/data/310/style_update_p1.php +++ b/phpBB/includes/db/migration/data/310/style_update_p1.php @@ -19,6 +19,34 @@ class phpbb_db_migration_data_310_style_update_p1 extends phpbb_db_migration  		return array('phpbb_db_migration_data_30x_3_0_11');  	} +	public function update_schema() +	{ +		return array( +			'add_columns'	=> array( +				$this->table_prefix . 'styles'		=> array( +					'style_path'			=> array('VCHAR:100', ''), +					'bbcode_bitfield'		=> array('VCHAR:255', 'kNg='), +					'style_parent_id'		=> array('UINT', 0), +					'style_parent_tree'		=> array('TEXT', ''), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns'	=> array( +				$this->table_prefix . 'styles'		=> array( +					'style_path', +					'bbcode_bitfield', +					'style_parent_id', +					'style_parent_tree', +				), +			), +		); +	} +  	public function update_data()  	{  		return array( diff --git a/phpBB/includes/db/sql_insert_buffer.php b/phpBB/includes/db/sql_insert_buffer.php new file mode 100644 index 0000000000..c18f908429 --- /dev/null +++ b/phpBB/includes/db/sql_insert_buffer.php @@ -0,0 +1,150 @@ +<?php +/** +* +* @package dbal +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Collects rows for insert into a database until the buffer size is reached. +* Then flushes the buffer to the database and starts over again. +* +* Benefits over collecting a (possibly huge) insert array and then using +* $db->sql_multi_insert() include: +* +*  - Going over max packet size of the database connection is usually prevented +*    because the data is submitted in batches. +* +*  - Reaching database connection timeout is usually prevented because +*    submission of batches talks to the database every now and then. +* +*  - Usage of less PHP memory because data no longer needed is discarded on +*    buffer flush. +* +* Attention: +* Please note that users of this class have to call flush() to flush the +* remaining rows to the database after their batch insert operation is +* finished. +* +* Usage: +* <code> +*	$buffer = new phpbb_db_sql_insert_buffer($db, 'test_table', 1234); +* +*	while (do_stuff()) +*	{ +*		$buffer->insert(array( +*			'column1' => 'value1', +*			'column2' => 'value2', +*		)); +*	} +* +*	$buffer->flush(); +* </code> +* +* @package dbal +*/ +class phpbb_db_sql_insert_buffer +{ +	/** @var phpbb_db_driver */ +	protected $db; + +	/** @var string */ +	protected $table_name; + +	/** @var int */ +	protected $max_buffered_rows; + +	/** @var array */ +	protected $buffer = array(); + +	/** +	* @param phpbb_db_driver $db +	* @param string          $table_name +	* @param int             $max_buffered_rows +	*/ +	public function __construct(phpbb_db_driver $db, $table_name, $max_buffered_rows = 500) +	{ +		$this->db = $db; +		$this->table_name = $table_name; +		$this->max_buffered_rows = $max_buffered_rows; +	} + +	/** +	* Inserts a single row into the buffer if multi insert is supported by the +	* database (otherwise an insert query is sent immediately). Then flushes +	* the buffer if the number of rows in the buffer is now greater than or +	* equal to $max_buffered_rows. +	* +	* @param array $row +	* +	* @return bool		True when some data was flushed to the database. +	*					False otherwise. +	*/ +	public function insert(array $row) +	{ +		$this->buffer[] = $row; + +		// Flush buffer if it is full or when DB does not support multi inserts. +		// In the later case, the buffer will always only contain one row. +		if (!$this->db->multi_insert || sizeof($this->buffer) >= $this->max_buffered_rows) +		{ +			return $this->flush(); +		} + +		return false; +	} + +	/** +	* Inserts a row set, i.e. an array of rows, by calling insert(). +	* +	* Please note that it is in most cases better to use insert() instead of +	* first building a huge rowset. Or at least sizeof($rows) should be kept +	* small. +	* +	* @param array $rows  +	* +	* @return bool		True when some data was flushed to the database. +	*					False otherwise. +	*/ +	public function insert_all(array $rows) +	{ +		// Using bitwise |= because PHP does not have logical ||= +		$result = 0; + +		foreach ($rows as $row) +		{ +			$result |= (int) $this->insert($row); +		} + +		return (bool) $result; +	} + +	/** +	* Flushes the buffer content to the DB and clears the buffer. +	* +	* @return bool		True when some data was flushed to the database. +	*					False otherwise. +	*/ +	public function flush() +	{ +		if (!empty($this->buffer)) +		{ +			$this->db->sql_multi_insert($this->table_name, $this->buffer); +			$this->buffer = array(); + +			return true; +		} + +		return false; +	} +} diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php index f71e32bc8d..766b9e9b63 100644 --- a/phpBB/includes/extension/finder.php +++ b/phpBB/includes/extension/finder.php @@ -23,6 +23,7 @@ if (!defined('IN_PHPBB'))  class phpbb_extension_finder  {  	protected $extension_manager; +	protected $filesystem;  	protected $phpbb_root_path;  	protected $cache;  	protected $php_ext; @@ -54,15 +55,17 @@ class phpbb_extension_finder  	* @param phpbb_extension_manager $extension_manager An extension manager  	*            instance that provides the finder with a list of active  	*            extensions and their locations +	* @param phpbb_filesystem $filesystem Filesystem instance  	* @param string $phpbb_root_path Path to the phpbb root directory  	* @param phpbb_cache_driver_interface $cache A cache instance or null  	* @param string $php_ext php file extension  	* @param string $cache_name The name of the cache variable, defaults to  	*                           _ext_finder  	*/ -	public function __construct(phpbb_extension_manager $extension_manager, $phpbb_root_path = '', phpbb_cache_driver_interface $cache = null, $php_ext = '.php', $cache_name = '_ext_finder') +	public function __construct(phpbb_extension_manager $extension_manager, phpbb_filesystem $filesystem, $phpbb_root_path = '', phpbb_cache_driver_interface $cache = null, $php_ext = 'php', $cache_name = '_ext_finder')  	{  		$this->extension_manager = $extension_manager; +		$this->filesystem = $filesystem;  		$this->phpbb_root_path = $phpbb_root_path;  		$this->cache = $cache;  		$this->php_ext = $php_ext; @@ -227,7 +230,7 @@ class phpbb_extension_finder  	*/  	protected function sanitise_directory($directory)  	{ -		$directory = preg_replace('#(?:^|/)\./#', '/', $directory); +		$directory = $this->filesystem->clean_path($directory);  		$dir_len = strlen($directory);  		if ($dir_len > 1 && $directory[$dir_len - 1] === '/') @@ -253,8 +256,8 @@ class phpbb_extension_finder  	*/  	public function get_classes($cache = true, $use_all_available = false)  	{ -		$this->query['extension_suffix'] .= $this->php_ext; -		$this->query['core_suffix'] .= $this->php_ext; +		$this->query['extension_suffix'] .= '.' . $this->php_ext; +		$this->query['core_suffix'] .= '.' . $this->php_ext;  		$files = $this->find($cache, false, $use_all_available); @@ -274,7 +277,7 @@ class phpbb_extension_finder  		{  			$file = preg_replace('#^includes/#', '', $file); -			$classes[] = 'phpbb_' . str_replace('/', '_', substr($file, 0, -strlen($this->php_ext))); +			$classes[] = 'phpbb_' . str_replace('/', '_', substr($file, 0, -strlen('.' . $this->php_ext)));  		}  		return $classes;  	} diff --git a/phpBB/includes/extension/manager.php b/phpBB/includes/extension/manager.php index 44a30c6280..653117adfa 100644 --- a/phpBB/includes/extension/manager.php +++ b/phpBB/includes/extension/manager.php @@ -44,13 +44,14 @@ class phpbb_extension_manager  	* @param phpbb_db_driver $db A database connection  	* @param phpbb_config $config phpbb_config  	* @param phpbb_db_migrator $migrator +	* @param phpbb_filesystem $filesystem  	* @param string $extension_table The name of the table holding extensions  	* @param string $phpbb_root_path Path to the phpbb includes directory.  	* @param string $php_ext php file extension  	* @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(ContainerInterface $container, phpbb_db_driver $db, phpbb_config $config, phpbb_db_migrator $migrator, $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, phpbb_db_migrator $migrator, phpbb_filesystem $filesystem, $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; @@ -58,6 +59,7 @@ class phpbb_extension_manager  		$this->config = $config;  		$this->migrator = $migrator;  		$this->cache = $cache; +		$this->filesystem = $filesystem;  		$this->php_ext = $php_ext;  		$this->extension_table = $extension_table;  		$this->cache_name = $cache_name; @@ -153,7 +155,7 @@ class phpbb_extension_manager  	*/  	public function create_extension_metadata_manager($name, phpbb_template $template)  	{ -		return new phpbb_extension_metadata_manager($name, $this->db, $this, $this->phpbb_root_path, $this->php_ext, $template, $this->config); +		return new phpbb_extension_metadata_manager($name, $this->config, $this, $template, $this->phpbb_root_path);  	}  	/** @@ -410,7 +412,7 @@ class phpbb_extension_manager  			RecursiveIteratorIterator::SELF_FIRST);  		foreach ($iterator as $file_info)  		{ -			if ($file_info->isFile() && $file_info->getFilename() == 'ext' . $this->php_ext) +			if ($file_info->isFile() && $file_info->getFilename() == 'ext.' . $this->php_ext)  			{  				$ext_name = $iterator->getInnerIterator()->getSubPath(); @@ -510,7 +512,7 @@ class phpbb_extension_manager  	*/  	public function get_finder()  	{ -		return new phpbb_extension_finder($this, $this->phpbb_root_path, $this->cache, $this->php_ext, $this->cache_name . '_finder'); +		return new phpbb_extension_finder($this, $this->filesystem, $this->phpbb_root_path, $this->cache, $this->php_ext, $this->cache_name . '_finder');  	}  	/** diff --git a/phpBB/includes/extension/metadata_manager.php b/phpBB/includes/extension/metadata_manager.php index 36b0f8b184..14b77c085b 100644 --- a/phpBB/includes/extension/metadata_manager.php +++ b/phpBB/includes/extension/metadata_manager.php @@ -22,31 +22,64 @@ if (!defined('IN_PHPBB'))  */  class phpbb_extension_metadata_manager  { -	protected $phpEx; +	/** +	* phpBB Config instance +	* @var phpbb_config +	*/ +	protected $config; + +	/** +	* phpBB Extension Manager +	* @var phpbb_extension_manager +	*/  	protected $extension_manager; -	protected $db; -	protected $phpbb_root_path; + +	/** +	* phpBB Template instance +	* @var phpbb_template +	*/  	protected $template; + +	/** +	* phpBB root path +	* @var string +	*/ +	protected $phpbb_root_path; + +	/** +	* Name (including vendor) of the extension +	* @var string +	*/  	protected $ext_name; + +	/** +	* Metadata from the composer.json file +	* @var array +	*/  	protected $metadata; + +	/** +	* Link (including root path) to the metadata file +	* @var string +	*/  	protected $metadata_file;  	/**  	* Creates the metadata manager  	* -	* @param phpbb_db_driver $db A database connection -	* @param string $extension_manager An instance of the phpbb extension manager -	* @param string $phpbb_root_path Path to the phpbb includes directory. -	* @param string $phpEx php file extension +	* @param string				$ext_name			Name (including vendor) of the extension +	* @param phpbb_config		$config				phpBB Config instance +	* @param phpbb_extension_manager	$extension_manager An instance of the phpBBb extension manager +	* @param phpbb_template		$template			phpBB Template instance +	* @param string				$phpbb_root_path	Path to the phpbb includes directory.  	*/ -	public function __construct($ext_name, phpbb_db_driver $db, phpbb_extension_manager $extension_manager, $phpbb_root_path, $phpEx = '.php', phpbb_template $template, phpbb_config $config) +	public function __construct($ext_name, phpbb_config $config, phpbb_extension_manager $extension_manager, phpbb_template $template, $phpbb_root_path)  	{ -		$this->phpbb_root_path = $phpbb_root_path; -		$this->db = $db;  		$this->config = $config; -		$this->phpEx = $phpEx; -		$this->template = $template;  		$this->extension_manager = $extension_manager; +		$this->template = $template; +		$this->phpbb_root_path = $phpbb_root_path; +  		$this->ext_name = $ext_name;  		$this->metadata = array();  		$this->metadata_file = ''; diff --git a/phpBB/includes/filesystem.php b/phpBB/includes/filesystem.php new file mode 100644 index 0000000000..27cab48fb0 --- /dev/null +++ b/phpBB/includes/filesystem.php @@ -0,0 +1,52 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* A class with various functions that are related to paths, files and the filesystem +* @package phpBB3 +*/ +class phpbb_filesystem +{ +	/** +	* Eliminates useless . and .. components from specified path. +	* +	* @param string $path Path to clean +	* @return string Cleaned path +	*/ +	public function clean_path($path) +	{ +		$exploded = explode('/', $path); +		$filtered = array(); +		foreach ($exploded as $part) +		{ +			if ($part === '.' && !empty($filtered)) +			{ +				continue; +			} + +			if ($part === '..' && !empty($filtered) && $filtered[sizeof($filtered) - 1] !== '..') +			{ +				array_pop($filtered); +			} +			else +			{ +				$filtered[] = $part; +			} +		} +		$path = implode('/', $filtered); +		return $path; +	} +} diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php index 98a2c0db79..b087e1298b 100644 --- a/phpBB/includes/functions.php +++ b/phpBB/includes/functions.php @@ -846,7 +846,7 @@ function phpbb_is_writable($file)  */  function phpbb_is_absolute($path)  { -	return ($path[0] == '/' || (DIRECTORY_SEPARATOR == '\\' && preg_match('#^[a-z]:[/\\\]#i', $path))) ? true : false; +	return (isset($path[0]) && $path[0] == '/' || preg_match('#^[a-z]:[/\\\]#i', $path)) ? true : false;  }  /** @@ -1049,31 +1049,33 @@ else  /**  * Eliminates useless . and .. components from specified path.  * +* Deprecated, use filesystem class instead +*  * @param string $path Path to clean  * @return string Cleaned path +* +* @deprecated  */  function phpbb_clean_path($path)  { -	$exploded = explode('/', $path); -	$filtered = array(); -	foreach ($exploded as $part) -	{ -		if ($part === '.' && !empty($filtered)) -		{ -			continue; -		} +	global $phpbb_container; -		if ($part === '..' && !empty($filtered) && $filtered[sizeof($filtered) - 1] !== '..') -		{ -			array_pop($filtered); -		} -		else +	if ($phpbb_container) +	{ +		$phpbb_filesystem = $phpbb_container->get('filesystem'); +	} +	else +	{ +		// The container is not yet loaded, use a new instance +		if (!class_exists('phpbb_filesystem'))  		{ -			$filtered[] = $part; +			global $phpbb_root_path, $phpEx; +			require($phpbb_root_path . 'includes/filesystem.' . $phpEx);  		} +		$phpbb_filesystem = new phpbb_filesystem();  	} -	$path = implode('/', $filtered); -	return $path; + +	return $phpbb_filesystem->clean_path($path);  }  // functions used for building option fields @@ -2731,7 +2733,7 @@ function redirect($url, $return = false, $disable_cd_check = false)  	// Make sure no linebreaks are there... to prevent http response splitting for PHP < 4.4.2  	if (strpos(urldecode($url), "\n") !== false || strpos(urldecode($url), "\r") !== false || strpos($url, ';') !== false)  	{ -		trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR); +		trigger_error('INSECURE_REDIRECT', E_USER_ERROR);  	}  	// Now, also check the protocol and for a valid url the last time... @@ -2740,7 +2742,7 @@ function redirect($url, $return = false, $disable_cd_check = false)  	if ($url_parts === false || empty($url_parts['scheme']) || !in_array($url_parts['scheme'], $allowed_protocols))  	{ -		trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR); +		trigger_error('INSECURE_REDIRECT', E_USER_ERROR);  	}  	if ($return) @@ -2905,7 +2907,7 @@ function meta_refresh($time, $url, $disable_cd_check = false)  		// For XHTML compatibility we change back & to &  		$template->assign_vars(array( -			'META' => '<meta http-equiv="refresh" content="' . $time . ';url=' . $url . '" />') +			'META' => '<meta http-equiv="refresh" content="' . $time . '; url=' . $url . '" />')  		);  	} @@ -3465,6 +3467,7 @@ function login_forum_box($forum_data)  	page_header($user->lang['LOGIN'], false);  	$template->assign_vars(array( +		'FORUM_NAME'			=> isset($forum_data['forum_name']) ? $forum_data['forum_name'] : '',  		'S_LOGIN_ACTION'		=> build_url(array('f')),  		'S_HIDDEN_FIELDS'		=> build_hidden_fields(array('f' => $forum_data['forum_id'])))  	); @@ -4182,7 +4185,7 @@ function phpbb_checkdnsrr($host, $type = 'MX')  // Handler, header and footer  /** -* Error and message handler, call with trigger_error if reqd +* Error and message handler, call with trigger_error if read  */  function msg_handler($errno, $msg_text, $errfile, $errline)  { @@ -5292,7 +5295,7 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0  		'BOARD_URL'			=> $board_url,  		'L_LOGIN_LOGOUT'	=> $l_login_logout, -		'L_INDEX'			=> $user->lang['FORUM_INDEX'], +		'L_INDEX'			=> ($config['board_index_text'] !== '') ? $config['board_index_text'] : $user->lang['FORUM_INDEX'],  		'L_SITE_HOME'		=> ($config['site_home_text'] !== '') ? $config['site_home_text'] : $user->lang['HOME'],  		'L_ONLINE_EXPLAIN'	=> $l_online_time, diff --git a/phpBB/includes/functions_download.php b/phpBB/includes/functions_download.php index 9ae647d806..ee4e2f5135 100644 --- a/phpBB/includes/functions_download.php +++ b/phpBB/includes/functions_download.php @@ -625,7 +625,7 @@ function phpbb_increment_downloads($db, $ids)  */  function phpbb_download_handle_forum_auth($db, $auth, $topic_id)  { -	$sql = 'SELECT t.forum_id, f.forum_password, f.parent_id +	$sql = 'SELECT t.forum_id, f.forum_name, f.forum_password, f.parent_id  		FROM ' . TOPICS_TABLE . ' t, ' . FORUMS_TABLE . " f  		WHERE t.topic_id = " . (int) $topic_id . "  			AND t.forum_id = f.forum_id"; diff --git a/phpBB/includes/functions_messenger.php b/phpBB/includes/functions_messenger.php index 821f0d970d..a646f35fdd 100644 --- a/phpBB/includes/functions_messenger.php +++ b/phpBB/includes/functions_messenger.php @@ -55,6 +55,24 @@ class messenger  		$this->vars = $this->msg = $this->replyto = $this->from = '';  		$this->mail_priority = MAIL_NORMAL_PRIORITY;  	} +	 +	/** +	* Set addresses for to/im as available +	*  +	* @param array $user User row +	*/ +	function set_addresses($user) +	{ +		if (isset($user['user_email']) && $user['user_email']) +		{ +			$this->to($user['user_email'], (isset($user['username']) ? $user['username'] : '')); +		} + +		if (isset($user['user_jabber']) && $user['user_jabber']) +		{ +			$this->im($user['user_jabber'], (isset($user['username']) ? $user['username'] : '')); +		} +	}  	/**  	* Sets an email address to send to @@ -209,7 +227,7 @@ class messenger  		if (!isset($this->tpl_msg[$template_lang . $template_file]))  		{  			$style_resource_locator = new phpbb_style_resource_locator(); -			$style_path_provider = new phpbb_style_extension_path_provider($phpbb_extension_manager, new phpbb_style_path_provider()); +			$style_path_provider = new phpbb_style_extension_path_provider($phpbb_extension_manager, new phpbb_style_path_provider(), $phpbb_root_path);  			$tpl = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, new phpbb_template_context(), $phpbb_extension_manager);  			$style = new phpbb_style($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, $style_path_provider, $tpl); diff --git a/phpBB/includes/functions_url_matcher.php b/phpBB/includes/functions_url_matcher.php index 7280cb74eb..a89ab7b126 100644 --- a/phpBB/includes/functions_url_matcher.php +++ b/phpBB/includes/functions_url_matcher.php @@ -60,7 +60,7 @@ function phpbb_create_dumped_url_matcher(phpbb_extension_finder $finder, $root_p  		'class'			=> 'phpbb_url_matcher',  	)); -	file_put_contents($root_path . 'cache/url_matcher' . $php_ext, $cached_url_matcher_dump); +	file_put_contents($root_path . 'cache/url_matcher.' . $php_ext, $cached_url_matcher_dump);  }  /** @@ -87,7 +87,7 @@ function phpbb_create_url_matcher(phpbb_extension_finder $finder, RequestContext  */  function phpbb_load_url_matcher(RequestContext $context, $root_path, $php_ext)  { -	require($root_path . 'cache/url_matcher' . $php_ext); +	require($root_path . 'cache/url_matcher.' . $php_ext);  	return new phpbb_url_matcher($context);  } @@ -102,5 +102,5 @@ function phpbb_load_url_matcher(RequestContext $context, $root_path, $php_ext)  */  function phpbb_url_matcher_dumped($root_path, $php_ext)  { -	return file_exists($root_path . 'cache/url_matcher' . $php_ext); +	return file_exists($root_path . 'cache/url_matcher.' . $php_ext);  } diff --git a/phpBB/includes/functions_user.php b/phpBB/includes/functions_user.php index bc636acabb..599cb24f75 100644 --- a/phpBB/includes/functions_user.php +++ b/phpBB/includes/functions_user.php @@ -2924,8 +2924,7 @@ function group_user_attributes($action, $group_id, $user_id_ary = false, $userna  			{  				$messenger->template('group_approved', $row['user_lang']); -				$messenger->to($row['user_email'], $row['username']); -				$messenger->im($row['user_jabber'], $row['username']); +				$messenger->set_addresses($row);  				$messenger->assign_vars(array(  					'USERNAME'		=> htmlspecialchars_decode($row['username']), diff --git a/phpBB/includes/hook/finder.php b/phpBB/includes/hook/finder.php index 065e685514..7b0412f733 100644 --- a/phpBB/includes/hook/finder.php +++ b/phpBB/includes/hook/finder.php @@ -66,7 +66,7 @@ class phpbb_hook_finder  		{  			while (($file = readdir($dh)) !== false)  			{ -				if (strpos($file, 'hook_') === 0 && substr($file, -(strlen($this->php_ext) + 1)) === '.' . $this->php_ext) +				if (strpos($file, 'hook_') === 0 && substr($file, -strlen('.' . $this->php_ext)) === '.' . $this->php_ext)  				{  					$hook_files[] = substr($file, 0, -(strlen($this->php_ext) + 1));  				} diff --git a/phpBB/includes/lock/db.php b/phpBB/includes/lock/db.php index ccdaed0b28..5cc0821aa0 100644 --- a/phpBB/includes/lock/db.php +++ b/phpBB/includes/lock/db.php @@ -117,6 +117,17 @@ class phpbb_lock_db  	}  	/** +	* Does this process own the lock? +	* +	* @return	bool			true if lock is owned +	*							false otherwise +	*/ +	public function owns_lock() +	{ +		return (bool) $this->locked; +	} + +	/**  	* Releases the lock.  	*  	* The lock must have been previously obtained, that is, acquire() call diff --git a/phpBB/includes/lock/flock.php b/phpBB/includes/lock/flock.php index 97bc7dd2b9..17de0847c0 100644 --- a/phpBB/includes/lock/flock.php +++ b/phpBB/includes/lock/flock.php @@ -111,6 +111,17 @@ class phpbb_lock_flock  	}  	/** +	* Does this process own the lock? +	* +	* @return	bool			true if lock is owned +	*							false otherwise +	*/ +	public function owns_lock() +	{ +		return (bool) $this->lock_fp; +	} + +	/**  	* Releases the lock.  	*  	* The lock must have been previously obtained, that is, acquire() call diff --git a/phpBB/includes/notification/manager.php b/phpBB/includes/notification/manager.php index ff83d4bb37..9eceeb753a 100644 --- a/phpBB/includes/notification/manager.php +++ b/phpBB/includes/notification/manager.php @@ -256,6 +256,7 @@ class phpbb_notification_manager  			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) . "'") : '') . +				(($user_id !== false) ? ' AND ' . (is_array($user_id) ? $this->db->sql_in_set('user_id', $user_id) : 'user_id = ' . (int) $user_id) : '') .  				(($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);  	} @@ -389,7 +390,6 @@ class phpbb_notification_manager  		$user_ids = array();  		$notification_objects = $notification_methods = array(); -		$new_rows = array();  		// Never send notifications to the anonymous user!  		unset($notify_users[ANONYMOUS]); @@ -419,6 +419,8 @@ class phpbb_notification_manager  		$pre_create_data = $notification->pre_create_insert_array($data, $notify_users);  		unset($notification); +		$insert_buffer = new phpbb_db_sql_insert_buffer($this->db, $this->notifications_table); +  		// 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)  		{ @@ -426,8 +428,8 @@ class phpbb_notification_manager  			$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); +			// Insert notification row using buffer. +			$insert_buffer->insert($notification->create_insert_array($data, $pre_create_data));  			// Users are needed to send notifications  			$user_ids = array_merge($user_ids, $notification->users_to_query()); @@ -447,8 +449,7 @@ class phpbb_notification_manager  			}  		} -		// insert into the db -		$this->db->sql_multi_insert($this->notifications_table, $new_rows); +		$insert_buffer->flush();  		// We need to load all of the users to send notifications  		$this->user_loader->load_users($user_ids); diff --git a/phpBB/includes/notification/method/email.php b/phpBB/includes/notification/method/email.php index 4a7fea6df3..571b0ec656 100644 --- a/phpBB/includes/notification/method/email.php +++ b/phpBB/includes/notification/method/email.php @@ -21,7 +21,7 @@ if (!defined('IN_PHPBB'))  *  * @package notifications  */ -class phpbb_notification_method_email extends phpbb_notification_method_base +class phpbb_notification_method_email extends phpbb_notification_method_messenger_base  {  	/**  	* Get notification method name @@ -34,26 +34,12 @@ class phpbb_notification_method_email extends phpbb_notification_method_base  	}  	/** -	* 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()  	{ -		return (bool) $this->config['email_enable']; +		return $this->config['email_enable'] && $this->user->data['user_email'];  	}  	/** @@ -61,68 +47,6 @@ class phpbb_notification_method_email extends phpbb_notification_method_base  	*/  	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(); +		return $this->notify_using_messenger(NOTIFY_EMAIL);  	}  } diff --git a/phpBB/includes/notification/method/jabber.php b/phpBB/includes/notification/method/jabber.php index 863846b8a5..d3b756d020 100644 --- a/phpBB/includes/notification/method/jabber.php +++ b/phpBB/includes/notification/method/jabber.php @@ -21,7 +21,7 @@ if (!defined('IN_PHPBB'))  *  * @package notifications  */ -class phpbb_notification_method_jabber extends phpbb_notification_method_email +class phpbb_notification_method_jabber extends phpbb_notification_method_messenger_base  {  	/**  	* Get notification method name @@ -34,20 +34,6 @@ class phpbb_notification_method_jabber extends phpbb_notification_method_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_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  	*/ @@ -62,7 +48,13 @@ class phpbb_notification_method_jabber extends phpbb_notification_method_email  	*/  	public function global_available()  	{ -		return ($this->config['jab_enable'] && @extension_loaded('xml')); +		return !( +			empty($this->config['jab_enable']) || +			empty($this->config['jab_host']) || +			empty($this->config['jab_username']) || +			empty($this->config['jab_password']) || +			!@extension_loaded('xml') +		);  	}  	public function notify() @@ -72,6 +64,6 @@ class phpbb_notification_method_jabber extends phpbb_notification_method_email  			return;  		} -		return parent::notify(); +		return $this->notify_using_messenger(NOTIFY_IM, 'short/');  	}  } diff --git a/phpBB/includes/notification/method/messenger_base.php b/phpBB/includes/notification/method/messenger_base.php new file mode 100644 index 0000000000..4966aa94bc --- /dev/null +++ b/phpBB/includes/notification/method/messenger_base.php @@ -0,0 +1,100 @@ +<?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; +} + +/** +* Abstract notification method handling email and jabber notifications +* using the phpBB messenger. +* +* @package notifications +*/ +abstract class phpbb_notification_method_messenger_base extends phpbb_notification_method_base +{ +	/** +	* Notify using phpBB messenger +	* +	* @param int $notify_method				Notify method for messenger (e.g. NOTIFY_IM) +	* @param string $template_dir_prefix	Base directory to prepend to the email template name +	* +	* @return null +	*/ +	protected function notify_using_messenger($notify_method, $template_dir_prefix = '') +	{ +		if (empty($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($template_dir_prefix . $notification->get_email_template(), $user['user_lang']); + +			$messenger->set_addresses($user); + +			$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($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/type/bookmark.php b/phpBB/includes/notification/type/bookmark.php index 4e48a967d0..946cb9b4ed 100644 --- a/phpBB/includes/notification/type/bookmark.php +++ b/phpBB/includes/notification/type/bookmark.php @@ -89,6 +89,7 @@ class phpbb_notification_type_bookmark extends phpbb_notification_type_post  		{  			return array();  		} +		sort($users);  		$auth_read = $this->auth->acl_get_list($users, 'f_read', $post['forum_id']); diff --git a/phpBB/includes/notification/type/post.php b/phpBB/includes/notification/type/post.php index d8ffdea81d..626c13b7fd 100644 --- a/phpBB/includes/notification/type/post.php +++ b/phpBB/includes/notification/type/post.php @@ -106,11 +106,26 @@ class phpbb_notification_type_post extends phpbb_notification_type_base  		}  		$this->db->sql_freeresult($result); +		$sql = 'SELECT user_id +			FROM ' . FORUMS_WATCH_TABLE . ' +			WHERE forum_id = ' . (int) $post['forum_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();  		} +		$users = array_unique($users); +		sort($users); +  		$auth_read = $this->auth->acl_get_list($users, 'f_read', $post['forum_id']);  		if (empty($auth_read)) diff --git a/phpBB/includes/notification/type/post_in_queue.php b/phpBB/includes/notification/type/post_in_queue.php index 9c719205e6..bc4b15cdc3 100644 --- a/phpBB/includes/notification/type/post_in_queue.php +++ b/phpBB/includes/notification/type/post_in_queue.php @@ -82,7 +82,7 @@ class phpbb_notification_type_post_in_queue extends phpbb_notification_type_post  			'ignore_users'		=> array(),  		), $options); -		// 0 is for global +		// 0 is for global moderator permissions  		$auth_approve = $this->auth->acl_get_list(false, $this->permission, array($post['forum_id'], 0));  		if (empty($auth_approve)) @@ -101,8 +101,15 @@ class phpbb_notification_type_post_in_queue extends phpbb_notification_type_post  		{  			$has_permission = array_unique(array_merge($has_permission, $auth_approve[0][$this->permission]));  		} +		sort($has_permission); -		return $this->check_user_notification_options($has_permission, array_merge($options, array( +		$auth_read = $this->auth->acl_get_list($has_permission, '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'],  		)));  	} diff --git a/phpBB/includes/notification/type/quote.php b/phpBB/includes/notification/type/quote.php index 5453b267c8..e9eb7bea21 100644 --- a/phpBB/includes/notification/type/quote.php +++ b/phpBB/includes/notification/type/quote.php @@ -108,6 +108,7 @@ class phpbb_notification_type_quote extends phpbb_notification_type_post  		{  			return array();  		} +		sort($users);  		$auth_read = $this->auth->acl_get_list($users, 'f_read', $post['forum_id']); diff --git a/phpBB/includes/notification/type/topic_in_queue.php b/phpBB/includes/notification/type/topic_in_queue.php index c501434c43..f735e10c00 100644 --- a/phpBB/includes/notification/type/topic_in_queue.php +++ b/phpBB/includes/notification/type/topic_in_queue.php @@ -82,7 +82,7 @@ class phpbb_notification_type_topic_in_queue extends phpbb_notification_type_top  			'ignore_users'		=> array(),  		), $options); -		// 0 is for global +		// 0 is for global moderator permissions  		$auth_approve = $this->auth->acl_get_list(false, 'm_approve', array($topic['forum_id'], 0));  		if (empty($auth_approve)) @@ -101,8 +101,15 @@ class phpbb_notification_type_topic_in_queue extends phpbb_notification_type_top  		{  			$has_permission = array_unique(array_merge($has_permission, $auth_approve[0][$this->permission]));  		} +		sort($has_permission); -		return $this->check_user_notification_options($has_permission, array_merge($options, array( +		$auth_read = $this->auth->acl_get_list($has_permission, 'f_read', $topic['forum_id']); +		if (empty($auth_read)) +		{ +			return array(); +		} + +		return $this->check_user_notification_options($auth_read[$topic['forum_id']]['f_read'], array_merge($options, array(  			'item_type'		=> self::$notification_option['id'],  		)));  	} diff --git a/phpBB/includes/search/fulltext_postgres.php b/phpBB/includes/search/fulltext_postgres.php index eeb628b18f..5080587681 100644 --- a/phpBB/includes/search/fulltext_postgres.php +++ b/phpBB/includes/search/fulltext_postgres.php @@ -214,7 +214,7 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base  	{  		if ($terms == 'all')  		{ -			$match		= array('#\sand\s#iu', '#\sor\s#iu', '#\snot\s#iu', '#\+#', '#-#', '#\|#'); +			$match		= array('#\sand\s#iu', '#\sor\s#iu', '#\snot\s#iu', '#(^|\s)\+#', '#(^|\s)-#', '#(^|\s)\|#');  			$replace	= array(' +', ' |', ' -', ' +', ' -', ' |');  			$keywords = preg_replace($match, $replace, $keywords); diff --git a/phpBB/includes/style/extension_path_provider.php b/phpBB/includes/style/extension_path_provider.php index 6976a45ed0..ec1d85f821 100644 --- a/phpBB/includes/style/extension_path_provider.php +++ b/phpBB/includes/style/extension_path_provider.php @@ -40,17 +40,22 @@ class phpbb_style_extension_path_provider extends phpbb_extension_provider imple  	*/  	protected $base_path_provider; +	/** @var string */ +	protected $phpbb_root_path; +  	/**  	* Constructor stores extension manager  	*  	* @param phpbb_extension_manager $extension_manager phpBB extension manager  	* @param phpbb_style_path_provider $base_path_provider A simple path provider  	*            to provide paths to be located in extensions +	* @param string		$phpbb_root_path	phpBB root path  	*/ -	public function __construct(phpbb_extension_manager $extension_manager, phpbb_style_path_provider $base_path_provider) +	public function __construct(phpbb_extension_manager $extension_manager, phpbb_style_path_provider $base_path_provider, $phpbb_root_path)  	{  		parent::__construct($extension_manager);  		$this->base_path_provider = $base_path_provider; +		$this->phpbb_root_path = $phpbb_root_path;  	}  	/** @@ -91,10 +96,23 @@ class phpbb_style_extension_path_provider extends phpbb_extension_provider imple  					$directories['style'][] = $path;  					if ($path && !phpbb_is_absolute($path))  					{ +						// Remove phpBB root path from the style path, +						// so the finder is able to find extension styles, +						// when the root path is not ./ +						if (strpos($path, $this->phpbb_root_path) === 0) +						{ +							$path = substr($path, strlen($this->phpbb_root_path)); +						} +  						$result = $finder->directory('/' . $this->ext_dir_prefix . $path)  							->get_directories(true, false, true);  						foreach ($result as $ext => $ext_path)  						{ +							// Make sure $ext_path has no ending slash +							if (substr($ext_path, -1) === '/') +							{ +								$ext_path = substr($ext_path, 0, -1); +							}  							$directories[$ext][] = $ext_path;  						}  					} diff --git a/phpBB/includes/template/filter.php b/phpBB/includes/template/filter.php index 9e8ad2fef0..c2c100e93e 100644 --- a/phpBB/includes/template/filter.php +++ b/phpBB/includes/template/filter.php @@ -224,28 +224,34 @@ class phpbb_template_filter extends php_user_filter  		}  		/* -			Preserve whitespace. -			PHP removes a newline after the closing tag (if it's there). This is by design. +		Preserve whitespace. +		PHP removes a newline after the closing tag (if it's there). +		This is by design: -			Consider the following template: +		http://www.php.net/manual/en/language.basic-syntax.phpmode.php +		http://www.php.net/manual/en/language.basic-syntax.instruction-separation.php -				<!-- IF condition --> -				some content -				<!-- ENDIF --> -			If we were to simply preserve all whitespace, we could simply replace all "?>" tags -			with "?>\n". -			Doing that, would add additional newlines to the compiled tempalte in place of the -			IF and ENDIF statements. These newlines are unwanted (and one is conditional). -			The IF and ENDIF are usually on their own line for ease of reading. +		Consider the following template: -			This replacement preserves newlines only for statements that aren't the only statement on a line. -			It will NOT preserve newlines at the end of statements in the above examle. -			It will preserve newlines in situations like: +			<!-- IF condition --> +			some content +			<!-- ENDIF --> -				<!-- IF condition -->inline content<!-- ENDIF --> +		If we were to simply preserve all whitespace, we could simply +		replace all "?>" tags with "?>\n". +		Doing that, would add additional newlines to the compiled +		template in place of the IF and ENDIF statements. These +		newlines are unwanted (and one is conditional). The IF and +		ENDIF are usually on their own line for ease of reading. +		This replacement preserves newlines only for statements that +		are not the only statement on a line. It will NOT preserve +		newlines at the end of statements in the above example. +		It will preserve newlines in situations like: + +			<!-- IF condition -->inline content<!-- ENDIF -->  		*/ diff --git a/phpBB/includes/tree/interface.php b/phpBB/includes/tree/interface.php new file mode 100644 index 0000000000..cc8aab2115 --- /dev/null +++ b/phpBB/includes/tree/interface.php @@ -0,0 +1,122 @@ +<?php +/** +* +* @package tree +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +interface phpbb_tree_interface +{ +	/** +	* Inserts an item into the database table and into the tree. +	* +	* @param array	$item	The item to be added +	* @return array Array with item data as set in the database +	*/ +	public function insert(array $additional_data); + +	/** +	* Delete an item from the tree and from the database table +	* +	* Also deletes the subtree from the tree and from the database table +	* +	* @param int	$item_id	The item to be deleted +	* @return array		Item ids that have been deleted +	*/ +	public function delete($item_id); + +	/** +	* Move an item by a given delta +	* +	* An item is only moved up/down within the same parent. If the delta is +	* larger then the number of children, the item is moved to the top/bottom +	* of the list of children within this parent. +	* +	* @param int	$item_id	The item to be moved +	* @param int	$delta		Number of steps to move this item, < 0 => down, > 0 => up +	* @return bool True if the item was moved +	*/ +	public function move($item_id, $delta); + +	/** +	* Move an item down by 1 +	* +	* @param int	$item_id	The item to be moved +	* @return bool True if the item was moved +	*/ +	public function move_down($item_id); + +	/** +	* Move an item up by 1 +	* +	* @param int	$item_id	The item to be moved +	* @return bool True if the item was moved +	*/ +	public function move_up($item_id); + +	/** +	* Moves all children of one item to another item +	* +	* If the new parent already has children, the new children are appended +	* to the list. +	* +	* @param int	$current_parent_id	The current parent item +	* @param int	$new_parent_id		The new parent item +	* @return bool True if any items where moved +	*/ +	public function move_children($current_parent_id, $new_parent_id); + +	/** +	* Change parent item +	* +	* Moves the item to the bottom of the new parent's list of children +	* +	* @param int	$item_id			The item to be moved +	* @param int	$new_parent_id		The new parent item +	* @return bool True if the parent was set successfully +	*/ +	public function change_parent($item_id, $new_parent_id); + +	/** +	* Get all items that are either ancestors or descendants of the item +	* +	* @param int		$item_id		Id of the item to retrieve the ancestors/descendants from +	* @param bool		$order_asc		Order the items ascendingly (most outer ancestor first) +	* @param bool		$include_item	Should the item matching the given item id be included in the list as well +	* @return array			Array of items (containing all columns from the item table) +	*							ID => Item data +	*/ +	public function get_path_and_subtree_data($item_id, $order_asc, $include_item); + +	/** +	* Get all of the item's ancestors +	* +	* @param int		$item_id		Id of the item to retrieve the ancestors from +	* @param bool		$order_asc		Order the items ascendingly (most outer ancestor first) +	* @param bool		$include_item	Should the item matching the given item id be included in the list as well +	* @return array			Array of items (containing all columns from the item table) +	*							ID => Item data +	*/ +	public function get_path_data($item_id, $order_asc, $include_item); + +	/** +	* Get all of the item's descendants +	* +	* @param int		$item_id		Id of the item to retrieve the descendants from +	* @param bool		$order_asc		Order the items ascendingly +	* @param bool		$include_item	Should the item matching the given item id be included in the list as well +	* @return array			Array of items (containing all columns from the item table) +	*							ID => Item data +	*/ +	public function get_subtree_data($item_id, $order_asc, $include_item); +} diff --git a/phpBB/includes/tree/nestedset.php b/phpBB/includes/tree/nestedset.php new file mode 100644 index 0000000000..4d851a87a8 --- /dev/null +++ b/phpBB/includes/tree/nestedset.php @@ -0,0 +1,850 @@ +<?php +/** +* +* @package tree +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +abstract class phpbb_tree_nestedset implements phpbb_tree_interface +{ +	/** @var phpbb_db_driver */ +	protected $db; + +	/** @var phpbb_lock_db */ +	protected $lock; + +	/** @var string */ +	protected $table_name; + +	/** +	* Prefix for the language keys returned by exceptions +	* @var string +	*/ +	protected $message_prefix = ''; + +	/** +	* Column names in the table +	* @var string +	*/ +	protected $column_item_id = 'item_id'; +	protected $column_left_id = 'left_id'; +	protected $column_right_id = 'right_id'; +	protected $column_parent_id = 'parent_id'; +	protected $column_item_parents = 'item_parents'; + +	/** +	* Additional SQL restrictions +	* Allows to have multiple nested sets in one table +	* @var string +	*/ +	protected $sql_where = ''; + +	/** +	* List of item properties to be cached in the item_parents column +	* @var array +	*/ +	protected $item_basic_data = array('*'); + +	/** +	* Construct +	* +	* @param phpbb_db_driver	$db		Database connection +	* @param phpbb_lock_db		$lock	Lock class used to lock the table when moving forums around +	* @param string			$table_name			Table name +	* @param string			$message_prefix		Prefix for the messages thrown by exceptions +	* @param string			$sql_where			Additional SQL restrictions for the queries +	* @param array			$item_basic_data	Array with basic item data that is stored in item_parents +	* @param array			$columns			Array with column names to overwrite +	*/ +	public function __construct(phpbb_db_driver $db, phpbb_lock_db $lock, $table_name, $message_prefix = '', $sql_where = '', $item_basic_data = array(), $columns = array()) +	{ +		$this->db = $db; +		$this->lock = $lock; + +		$this->table_name = $table_name; +		$this->message_prefix = $message_prefix; +		$this->sql_where = $sql_where; +		$this->item_basic_data = (!empty($item_basic_data)) ? $item_basic_data : array('*'); + +		if (!empty($columns)) +		{ +			foreach ($columns as $column => $name) +			{ +				$column_name = 'column_' . $column; +				$this->$column_name = $name; +			} +		} +	} + +	/** +	* Returns additional sql where restrictions +	* +	* @param string		$operator		SQL operator that needs to be prepended to sql_where, +	*									if it is not empty. +	* @param string		$column_prefix	Prefix that needs to be prepended to column names +	* @return string		Returns additional where statements to narrow down the tree, +	*						prefixed with operator and prepended column_prefix to column names +	*/ +	public function get_sql_where($operator = 'AND', $column_prefix = '') +	{ +		return (!$this->sql_where) ? '' : $operator . ' ' . sprintf($this->sql_where, $column_prefix); +	} + +	/** +	* Acquires a lock on the item table +	* +	* @return bool	True if the lock was acquired, false if it has been acquired previously +	* +	* @throws RuntimeException If the lock could not be acquired +	*/ +	protected function acquire_lock() +	{ +		if ($this->lock->owns_lock()) +		{ +			return false; +		} + +		if (!$this->lock->acquire()) +		{ +			throw new RuntimeException($this->message_prefix . 'LOCK_FAILED_ACQUIRE'); +		} + +		return true; +	} + +	/** +	* @inheritdoc +	*/ +	public function insert(array $additional_data) +	{ +		$item_data = $this->reset_nestedset_values($additional_data); + +		$sql = 'INSERT INTO ' . $this->table_name . ' ' . $this->db->sql_build_array('INSERT', $item_data); +		$this->db->sql_query($sql); + +		$item_data[$this->column_item_id] = (int) $this->db->sql_nextid(); + +		return array_merge($item_data, $this->add_item_to_nestedset($item_data[$this->column_item_id])); +	} + +	/** +	* Add an item which already has a database row at the end of the tree +	* +	* @param int	$item_id	The item to be added +	* @return array		Array with updated data, if the item was added successfully +	*					Empty array otherwise +	*/ +	protected function add_item_to_nestedset($item_id) +	{ +		$sql = 'SELECT MAX(' . $this->column_right_id . ') AS ' . $this->column_right_id . ' +			FROM ' . $this->table_name . ' +			' . $this->get_sql_where('WHERE'); +		$result = $this->db->sql_query($sql); +		$current_max_right_id = (int) $this->db->sql_fetchfield($this->column_right_id); +		$this->db->sql_freeresult($result); + +		$update_item_data = array( +			$this->column_parent_id		=> 0, +			$this->column_left_id		=> $current_max_right_id + 1, +			$this->column_right_id		=> $current_max_right_id + 2, +			$this->column_item_parents	=> '', +		); + +		$sql = 'UPDATE ' . $this->table_name . ' +			SET ' . $this->db->sql_build_array('UPDATE', $update_item_data) . ' +			WHERE ' . $this->column_item_id . ' = ' . (int) $item_id . ' +				AND ' . $this->column_parent_id . ' = 0 +				AND ' . $this->column_left_id . ' = 0 +				AND ' . $this->column_right_id . ' = 0'; +		$this->db->sql_query($sql); + +		return ($this->db->sql_affectedrows() == 1) ? $update_item_data : array(); +	} + +	/** +	* Remove an item from the tree without deleting it from the database +	* +	* Also removes all subitems from the tree without deleting them from the database either +	* +	* @param int	$item_id	The item to be deleted +	* @return array		Item ids that have been removed +	*/ +	protected function remove_item_from_nestedset($item_id) +	{ +		$item_id = (int) $item_id; +		if (!$item_id) +		{ +			throw new OutOfBoundsException($this->message_prefix . 'INVALID_ITEM'); +		} + +		$items = $this->get_subtree_data($item_id); +		$item_ids = array_keys($items); + +		if (empty($items) || !isset($items[$item_id])) +		{ +			throw new OutOfBoundsException($this->message_prefix . 'INVALID_ITEM'); +		} + +		$this->remove_subset($item_ids, $items[$item_id]); + +		return $item_ids; +	} + +	/** +	* @inheritdoc +	*/ +	public function delete($item_id) +	{ +		$removed_items = $this->remove_item_from_nestedset($item_id); + +		$sql = 'DELETE FROM ' . $this->table_name . ' +			WHERE ' . $this->db->sql_in_set($this->column_item_id, $removed_items) . ' +			' . $this->get_sql_where('AND'); +		$this->db->sql_query($sql); + +		return $removed_items; +	} + +	/** +	* @inheritdoc +	*/ +	public function move($item_id, $delta) +	{ +		if ($delta == 0) +		{ +			return false; +		} + +		$this->acquire_lock(); + +		$action = ($delta > 0) ? 'move_up' : 'move_down'; +		$delta = abs($delta); + +		// Keep $this->get_sql_where() here, to ensure we are in the right tree. +		$sql = 'SELECT * +			FROM ' . $this->table_name . ' +			WHERE ' . $this->column_item_id . ' = ' . (int) $item_id . ' +				' . $this->get_sql_where(); +		$result = $this->db->sql_query_limit($sql, $delta); +		$item = $this->db->sql_fetchrow($result); +		$this->db->sql_freeresult($result); + +		if (!$item) +		{ +			$this->lock->release(); +			throw new OutOfBoundsException($this->message_prefix . 'INVALID_ITEM'); +		} + +		/** +		* Fetch all the siblings between the item's current spot +		* and where we want to move it to. If there are less than $delta +		* siblings between the current spot and the target then the +		* item will move as far as possible +		*/ +		$sql = "SELECT {$this->column_item_id}, {$this->column_parent_id}, {$this->column_left_id}, {$this->column_right_id}, {$this->column_item_parents} +			FROM " . $this->table_name . ' +			WHERE ' . $this->column_parent_id . ' = ' . (int) $item[$this->column_parent_id] . ' +				' . $this->get_sql_where() . ' +				AND '; + +		if ($action == 'move_up') +		{ +			$sql .= $this->column_right_id . ' < ' . (int) $item[$this->column_right_id] . ' ORDER BY ' . $this->column_right_id . ' DESC'; +		} +		else +		{ +			$sql .= $this->column_left_id . ' > ' . (int) $item[$this->column_left_id] . ' ORDER BY ' . $this->column_left_id . ' ASC'; +		} + +		$result = $this->db->sql_query_limit($sql, $delta); + +		$target = false; +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$target = $row; +		} +		$this->db->sql_freeresult($result); + +		if (!$target) +		{ +			$this->lock->release(); +			// The item is already on top or bottom +			return false; +		} + +		/** +		* $left_id and $right_id define the scope of the items that are affected by the move. +		* $diff_up and $diff_down are the values to substract or add to each item's left_id +		* and right_id in order to move them up or down. +		* $move_up_left and $move_up_right define the scope of the items that are moving +		* up. Other items in the scope of ($left_id, $right_id) are considered to move down. +		*/ +		if ($action == 'move_up') +		{ +			$left_id = (int) $target[$this->column_left_id]; +			$right_id = (int) $item[$this->column_right_id]; + +			$diff_up = (int) $item[$this->column_left_id] - (int) $target[$this->column_left_id]; +			$diff_down = (int) $item[$this->column_right_id] + 1 - (int) $item[$this->column_left_id]; + +			$move_up_left = (int) $item[$this->column_left_id]; +			$move_up_right = (int) $item[$this->column_right_id]; +		} +		else +		{ +			$left_id = (int) $item[$this->column_left_id]; +			$right_id = (int) $target[$this->column_right_id]; + +			$diff_up = (int) $item[$this->column_right_id] + 1 - (int) $item[$this->column_left_id]; +			$diff_down = (int) $target[$this->column_right_id] - (int) $item[$this->column_right_id]; + +			$move_up_left = (int) $item[$this->column_right_id] + 1; +			$move_up_right = (int) $target[$this->column_right_id]; +		} + +		// Now do the dirty job +		$sql = 'UPDATE ' . $this->table_name . ' +			SET ' . $this->column_left_id . ' = ' . $this->column_left_id . ' + CASE +				WHEN ' . $this->column_left_id . " BETWEEN {$move_up_left} AND {$move_up_right} THEN -{$diff_up} +				ELSE {$diff_down} +			END, +			" . $this->column_right_id . ' = ' . $this->column_right_id . ' + CASE +				WHEN ' . $this->column_right_id . " BETWEEN {$move_up_left} AND {$move_up_right} THEN -{$diff_up} +				ELSE {$diff_down} +			END +			WHERE +				" . $this->column_left_id . " BETWEEN {$left_id} AND {$right_id} +				AND " . $this->column_right_id . " BETWEEN {$left_id} AND {$right_id} +				" . $this->get_sql_where(); +		$this->db->sql_query($sql); + +		$this->lock->release(); + +		return true; +	} + +	/** +	* @inheritdoc +	*/ +	public function move_down($item_id) +	{ +		return $this->move($item_id, -1); +	} + +	/** +	* @inheritdoc +	*/ +	public function move_up($item_id) +	{ +		return $this->move($item_id, 1); +	} + +	/** +	* @inheritdoc +	*/ +	public function move_children($current_parent_id, $new_parent_id) +	{ +		$current_parent_id = (int) $current_parent_id; +		$new_parent_id = (int) $new_parent_id; + +		if ($current_parent_id == $new_parent_id) +		{ +			return false; +		} + +		if (!$current_parent_id) +		{ +			throw new OutOfBoundsException($this->message_prefix . 'INVALID_ITEM'); +		} + +		$this->acquire_lock(); + +		$item_data = $this->get_subtree_data($current_parent_id); +		if (!isset($item_data[$current_parent_id])) +		{ +			$this->lock->release(); +			throw new OutOfBoundsException($this->message_prefix . 'INVALID_ITEM'); +		} + +		$current_parent = $item_data[$current_parent_id]; +		unset($item_data[$current_parent_id]); +		$move_items = array_keys($item_data); + +		if (($current_parent[$this->column_right_id] - $current_parent[$this->column_left_id]) <= 1) +		{ +			$this->lock->release(); +			return false; +		} + +		if (in_array($new_parent_id, $move_items)) +		{ +			$this->lock->release(); +			throw new OutOfBoundsException($this->message_prefix . 'INVALID_PARENT'); +		} + +		$diff = sizeof($move_items) * 2; +		$sql_exclude_moved_items = $this->db->sql_in_set($this->column_item_id, $move_items, true); + +		$this->db->sql_transaction('begin'); + +		$this->remove_subset($move_items, $current_parent, false, true); + +		if ($new_parent_id) +		{ +			// Retrieve new-parent again, it may have been changed... +			$sql = 'SELECT * +				FROM ' . $this->table_name . ' +				WHERE ' . $this->column_item_id . ' = ' . $new_parent_id; +			$result = $this->db->sql_query($sql); +			$new_parent = $this->db->sql_fetchrow($result); +			$this->db->sql_freeresult($result); + +			if (!$new_parent) +			{ +				$this->db->sql_transaction('rollback'); +				$this->lock->release(); +				throw new OutOfBoundsException($this->message_prefix . 'INVALID_PARENT'); +			} + +			$new_right_id = $this->prepare_adding_subset($move_items, $new_parent, true); + +			if ($new_right_id > $current_parent[$this->column_right_id]) +			{ +				$diff = ' + ' . ($new_right_id - $current_parent[$this->column_right_id]); +			} +			else +			{ +				$diff = ' - ' . abs($new_right_id - $current_parent[$this->column_right_id]); +			} +		} +		else +		{ +			$sql = 'SELECT MAX(' . $this->column_right_id . ') AS ' . $this->column_right_id . ' +				FROM ' . $this->table_name . ' +				WHERE ' . $sql_exclude_moved_items . ' +					' . $this->get_sql_where('AND'); +			$result = $this->db->sql_query($sql); +			$row = $this->db->sql_fetchrow($result); +			$this->db->sql_freeresult($result); + +			$diff = ' + ' . ($row[$this->column_right_id] - $current_parent[$this->column_left_id]); +		} + +		$sql = 'UPDATE ' . $this->table_name . ' +			SET ' . $this->column_left_id . ' = ' . $this->column_left_id . $diff . ', +				' . $this->column_right_id . ' = ' . $this->column_right_id . $diff . ', +				' . $this->column_parent_id . ' = ' . $this->db->sql_case($this->column_parent_id . ' = ' . $current_parent_id, $new_parent_id, $this->column_parent_id) . ', +				' . $this->column_item_parents . " = '' +			WHERE " . $this->db->sql_in_set($this->column_item_id, $move_items) . ' +				' . $this->get_sql_where('AND'); +		$this->db->sql_query($sql); + +		$this->db->sql_transaction('commit'); +		$this->lock->release(); + +		return true; +	} + +	/** +	* @inheritdoc +	*/ +	public function change_parent($item_id, $new_parent_id) +	{ +		$item_id = (int) $item_id; +		$new_parent_id = (int) $new_parent_id; + +		if ($item_id == $new_parent_id) +		{ +			return false; +		} + +		if (!$item_id) +		{ +			throw new OutOfBoundsException($this->message_prefix . 'INVALID_ITEM'); +		} + +		$this->acquire_lock(); + +		$item_data = $this->get_subtree_data($item_id); +		if (!isset($item_data[$item_id])) +		{ +			$this->lock->release(); +			throw new OutOfBoundsException($this->message_prefix . 'INVALID_ITEM'); +		} + +		$item = $item_data[$item_id]; +		$move_items = array_keys($item_data); + +		if (in_array($new_parent_id, $move_items)) +		{ +			$this->lock->release(); +			throw new OutOfBoundsException($this->message_prefix . 'INVALID_PARENT'); +		} + +		$diff = sizeof($move_items) * 2; +		$sql_exclude_moved_items = $this->db->sql_in_set($this->column_item_id, $move_items, true); + +		$this->db->sql_transaction('begin'); + +		$this->remove_subset($move_items, $item, false, true); + +		if ($new_parent_id) +		{ +			// Retrieve new-parent again, it may have been changed... +			$sql = 'SELECT * +				FROM ' . $this->table_name . ' +				WHERE ' . $this->column_item_id . ' = ' . $new_parent_id; +			$result = $this->db->sql_query($sql); +			$new_parent = $this->db->sql_fetchrow($result); +			$this->db->sql_freeresult($result); + +			if (!$new_parent) +			{ +				$this->db->sql_transaction('rollback'); +				$this->lock->release(); +				throw new OutOfBoundsException($this->message_prefix . 'INVALID_PARENT'); +			} + +			$new_right_id = $this->prepare_adding_subset($move_items, $new_parent, true); + +			if ($new_right_id > (int) $item[$this->column_right_id]) +			{ +				$diff = ' + ' . ($new_right_id - (int) $item[$this->column_right_id] - 1); +			} +			else +			{ +				$diff = ' - ' . abs($new_right_id - (int) $item[$this->column_right_id] - 1); +			} +		} +		else +		{ +			$sql = 'SELECT MAX(' . $this->column_right_id . ') AS ' . $this->column_right_id . ' +				FROM ' . $this->table_name . ' +				WHERE ' . $sql_exclude_moved_items . ' +					' . $this->get_sql_where('AND'); +			$result = $this->db->sql_query($sql); +			$row = $this->db->sql_fetchrow($result); +			$this->db->sql_freeresult($result); + +			$diff = ' + ' . ($row[$this->column_right_id] - (int) $item[$this->column_left_id] + 1); +		} + +		$sql = 'UPDATE ' . $this->table_name . ' +			SET ' . $this->column_left_id . ' = ' . $this->column_left_id . $diff . ', +				' . $this->column_right_id . ' = ' . $this->column_right_id . $diff . ', +				' . $this->column_parent_id . ' = ' . $this->db->sql_case($this->column_item_id . ' = ' . $item_id, $new_parent_id, $this->column_parent_id) . ', +				' . $this->column_item_parents . " = '' +			WHERE " . $this->db->sql_in_set($this->column_item_id, $move_items) . ' +				' . $this->get_sql_where('AND'); +		$this->db->sql_query($sql); + +		$this->db->sql_transaction('commit'); +		$this->lock->release(); + +		return true; +	} + +	/** +	* @inheritdoc +	*/ +	public function get_path_and_subtree_data($item_id, $order_asc = true, $include_item = true) +	{ +		$condition = 'i2.' . $this->column_left_id . ' BETWEEN i1.' . $this->column_left_id . ' AND i1.' . $this->column_right_id . ' +			OR i1.' . $this->column_left_id . ' BETWEEN i2.' . $this->column_left_id . ' AND i2.' . $this->column_right_id; + +		return $this->get_set_of_nodes_data($item_id, $condition, $order_asc, $include_item); +	} + +	/** +	* @inheritdoc +	*/ +	public function get_path_data($item_id, $order_asc = true, $include_item = true) +	{ +		$condition = 'i1.' . $this->column_left_id . ' BETWEEN i2.' . $this->column_left_id . ' AND i2.' . $this->column_right_id . ''; + +		return $this->get_set_of_nodes_data($item_id, $condition, $order_asc, $include_item); +	} + +	/** +	* @inheritdoc +	*/ +	public function get_subtree_data($item_id, $order_asc = true, $include_item = true) +	{ +		$condition = 'i2.' . $this->column_left_id . ' BETWEEN i1.' . $this->column_left_id . ' AND i1.' . $this->column_right_id . ''; + +		return $this->get_set_of_nodes_data($item_id, $condition, $order_asc, $include_item); +	} + +	/** +	* Get items that are related to the given item by the condition +	* +	* @param int		$item_id		Id of the item to retrieve the node set from +	* @param string		$condition		Query string restricting the item list +	* @param bool		$order_asc		Order the items ascending by their left_id +	* @param bool		$include_item	Should the item matching the given item id be included in the list as well +	* @return array			Array of items (containing all columns from the item table) +	*							ID => Item data +	*/ +	protected function get_set_of_nodes_data($item_id, $condition, $order_asc = true, $include_item = true) +	{ +		$rows = array(); + +		$sql = 'SELECT i2.* +			FROM ' . $this->table_name . ' i1 +			LEFT JOIN ' . $this->table_name . " i2 +				ON (($condition) " . $this->get_sql_where('AND', 'i2.') . ') +			WHERE i1.' . $this->column_item_id . ' = ' . (int) $item_id . ' +				' . $this->get_sql_where('AND', 'i1.') . ' +			ORDER BY i2.' . $this->column_left_id . ' ' . ($order_asc ? 'ASC' : 'DESC'); +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			if (!$include_item && $item_id == $row[$this->column_item_id]) +			{ +				continue; +			} + +			$rows[(int) $row[$this->column_item_id]] = $row; +		} +		$this->db->sql_freeresult($result); + +		return $rows; +	} + +	/** +	* Get basic data of all parent items +	* +	* Basic data is defined in the $item_basic_data property. +	* Data is cached in the item_parents column in the item table +	* +	* @param array	$item		The item to get the path from +	* @return array			Array of items (containing basic columns from the item table) +	*							ID => Item data +	*/ +	public function get_path_basic_data(array $item) +	{ +		$parents = array(); +		if ($item[$this->column_parent_id]) +		{ +			if (!$item[$this->column_item_parents]) +			{ +				$sql = 'SELECT ' . implode(', ', $this->item_basic_data) . ' +					FROM ' . $this->table_name . ' +					WHERE ' . $this->column_left_id . ' < ' . (int) $item[$this->column_left_id] . ' +						AND ' . $this->column_right_id . ' > ' . (int) $item[$this->column_right_id] . ' +						' . $this->get_sql_where('AND') . ' +					ORDER BY ' . $this->column_left_id . ' ASC'; +				$result = $this->db->sql_query($sql); + +				while ($row = $this->db->sql_fetchrow($result)) +				{ +					$parents[$row[$this->column_item_id]] = $row; +				} +				$this->db->sql_freeresult($result); + +				$item_parents = serialize($parents); + +				$sql = 'UPDATE ' . $this->table_name . ' +					SET ' . $this->column_item_parents . " = '" . $this->db->sql_escape($item_parents) . "' +					WHERE " . $this->column_parent_id . ' = ' . (int) $item[$this->column_parent_id]; +				$this->db->sql_query($sql); +			} +			else +			{ +				$parents = unserialize($item[$this->column_item_parents]); +			} +		} + +		return $parents; +	} + +	/** +	* Remove a subset from the nested set +	* +	* @param array	$subset_items		Subset of items to remove +	* @param array	$bounding_item		Item containing the right bound of the subset +	* @param bool	$set_subset_zero	Should the parent, left and right id of the items be set to 0, or kept unchanged? +	*									In case of removing an item from the tree, we should the values to 0 +	*									In case of moving an item, we shouldkeep the original values, in order to allow "+ diff" later +	* @return	null +	*/ +	protected function remove_subset(array $subset_items, array $bounding_item, $set_subset_zero = true) +	{ +		$acquired_new_lock = $this->acquire_lock(); + +		$diff = sizeof($subset_items) * 2; +		$sql_subset_items = $this->db->sql_in_set($this->column_item_id, $subset_items); +		$sql_not_subset_items = $this->db->sql_in_set($this->column_item_id, $subset_items, true); + +		$sql_is_parent = $this->column_left_id . ' <= ' . (int) $bounding_item[$this->column_right_id] . ' +			AND ' . $this->column_right_id . ' >= ' . (int) $bounding_item[$this->column_right_id]; + +		$sql_is_right = $this->column_left_id . ' > ' . (int) $bounding_item[$this->column_right_id]; + +		$set_left_id = $this->db->sql_case($sql_is_right, $this->column_left_id . ' - ' . $diff, $this->column_left_id); +		$set_right_id = $this->db->sql_case($sql_is_parent . ' OR ' . $sql_is_right, $this->column_right_id . ' - ' . $diff, $this->column_right_id); + +		if ($set_subset_zero) +		{ +			$set_left_id = $this->db->sql_case($sql_subset_items, 0, $set_left_id); +			$set_right_id = $this->db->sql_case($sql_subset_items, 0, $set_right_id); +		} + +		$sql = 'UPDATE ' . $this->table_name . ' +			SET ' . (($set_subset_zero) ? $this->column_parent_id . ' = ' . $this->db->sql_case($sql_subset_items, 0, $this->column_parent_id) . ',' : '') . ' +				' . $this->column_left_id . ' = ' . $set_left_id . ', +				' . $this->column_right_id . ' = ' . $set_right_id . ' +			' . ((!$set_subset_zero) ? ' WHERE ' . $sql_not_subset_items . ' ' . $this->get_sql_where('AND') : $this->get_sql_where('WHERE')); +		$this->db->sql_query($sql); + +		if ($acquired_new_lock) +		{ +			$this->lock->release(); +		} +	} + +	/** +	* Prepare adding a subset to the nested set +	* +	* @param array	$subset_items		Subset of items to add +	* @param array	$new_parent	Item containing the right bound of the new parent +	* @return	int		New right id of the parent item +	*/ +	protected function prepare_adding_subset(array $subset_items, array $new_parent) +	{ +		$diff = sizeof($subset_items) * 2; +		$sql_not_subset_items = $this->db->sql_in_set($this->column_item_id, $subset_items, true); + +		$set_left_id = $this->db->sql_case($this->column_left_id . ' > ' . (int) $new_parent[$this->column_right_id], $this->column_left_id . ' + ' . $diff, $this->column_left_id); +		$set_right_id = $this->db->sql_case($this->column_right_id . ' >= ' . (int) $new_parent[$this->column_right_id], $this->column_right_id . ' + ' . $diff, $this->column_right_id); + +		$sql = 'UPDATE ' . $this->table_name . ' +			SET ' . $this->column_left_id . ' = ' . $set_left_id . ', +				' . $this->column_right_id . ' = ' . $set_right_id . ' +			WHERE ' . $sql_not_subset_items . ' +				' . $this->get_sql_where('AND'); +		$this->db->sql_query($sql); + +		return $new_parent[$this->column_right_id] + $diff; +	} + +	/** +	* Resets values required for the nested set system +	* +	* @param array	$item		Original item data +	* @return	array		Original item data + nested set defaults +	*/ +	protected function reset_nestedset_values(array $item) +	{ +		$item_data = array_merge($item, array( +			$this->column_parent_id		=> 0, +			$this->column_left_id		=> 0, +			$this->column_right_id		=> 0, +			$this->column_item_parents	=> '', +		)); + +		unset($item_data[$this->column_item_id]); + +		return $item_data; +	} + +	/** +	* Regenerate left/right ids from parent/child relationship +	* +	* This method regenerates the left/right ids for the tree based on +	* the parent/child relations. This function executes three queries per +	* item, so it should only be called, when the set has one of the following +	* problems: +	*	- The set has a duplicated value inside the left/right id chain +	*	- The set has a missing value inside the left/right id chain +	*	- The set has items that do not have a left/right id set +	* +	* When regenerating the items, the items are sorted by parent id and their +	* current left id, so the current child/parent relationships are kept +	* and running the function on a working set will not change the order. +	* +	* @param int	$new_id		First left_id to be used (should start with 1) +	* @param int	$parent_id	parent_id of the current set (default = 0) +	* @param bool	$reset_ids	Should we reset all left_id/right_id on the first call? +	* @return	int		$new_id		The next left_id/right_id that should be used +	*/ +	public function regenerate_left_right_ids($new_id, $parent_id = 0, $reset_ids = false) +	{ +		if ($acquired_new_lock = $this->acquire_lock()) +		{ +			$this->db->sql_transaction('begin'); + +			if (!$reset_ids) +			{ +				$sql = 'UPDATE ' . $this->table_name . ' +					SET ' . $this->column_item_parents . " = '' +					" . $this->get_sql_where('WHERE'); +				$this->db->sql_query($sql); +			} +		} + +		if ($reset_ids) +		{ +			$sql = 'UPDATE ' . $this->table_name . ' +				SET ' . $this->db->sql_build_array('UPDATE', array( +					$this->column_left_id		=> 0, +					$this->column_right_id		=> 0, +					$this->column_item_parents	=> '', +				)) . ' +				' . $this->get_sql_where('WHERE'); +			$this->db->sql_query($sql); +		} + +		$sql = 'SELECT * +			FROM ' . $this->table_name . ' +			WHERE ' . $this->column_parent_id . ' = ' . (int) $parent_id . ' +				' . $this->get_sql_where('AND') . ' +			ORDER BY ' . $this->column_left_id . ', ' . $this->column_item_id . ' ASC'; +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			// First we update the left_id for this module +			if ($row[$this->column_left_id] != $new_id) +			{ +				$sql = 'UPDATE ' . $this->table_name . ' +					SET ' . $this->db->sql_build_array('UPDATE', array($this->column_left_id => $new_id)) . ' +					WHERE ' . $this->column_item_id . ' = ' . (int) $row[$this->column_item_id]; +				$this->db->sql_query($sql); +			} +			$new_id++; + +			// Then we go through any children and update their left/right id's +			$new_id = $this->regenerate_left_right_ids($new_id, $row[$this->column_item_id]); + +			// Then we come back and update the right_id for this module +			if ($row[$this->column_right_id] != $new_id) +			{ +				$sql = 'UPDATE ' . $this->table_name . ' +					SET ' . $this->db->sql_build_array('UPDATE', array($this->column_right_id => $new_id)) . ' +					WHERE ' . $this->column_item_id . ' = ' . (int) $row[$this->column_item_id]; +				$this->db->sql_query($sql); +			} +			$new_id++; +		} +		$this->db->sql_freeresult($result); + +		if ($acquired_new_lock) +		{ +			$this->db->sql_transaction('commit'); +			$this->lock->release(); +		} + +		return $new_id; +	} +} diff --git a/phpBB/includes/tree/nestedset_forum.php b/phpBB/includes/tree/nestedset_forum.php new file mode 100644 index 0000000000..ff09ef55d0 --- /dev/null +++ b/phpBB/includes/tree/nestedset_forum.php @@ -0,0 +1,46 @@ +<?php +/** +* +* @package tree +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +class phpbb_tree_nestedset_forum extends phpbb_tree_nestedset +{ +	/** +	* Construct +	* +	* @param phpbb_db_driver	$db		Database connection +	* @param phpbb_lock_db		$lock	Lock class used to lock the table when moving forums around +	* @param string				$table_name		Table name +	*/ +	public function __construct(phpbb_db_driver $db, phpbb_lock_db $lock, $table_name) +	{ +		parent::__construct( +			$db, +			$lock, +			$table_name, +			'FORUM_NESTEDSET_', +			'', +			array( +				'forum_id', +				'forum_name', +				'forum_type', +			), +			array( +				'item_id'		=> 'forum_id', +				'item_parents'	=> 'forum_parents', +			) +		); +	} +} diff --git a/phpBB/includes/ucp/ucp_activate.php b/phpBB/includes/ucp/ucp_activate.php index 577761dfde..898dacd831 100644 --- a/phpBB/includes/ucp/ucp_activate.php +++ b/phpBB/includes/ucp/ucp_activate.php @@ -114,7 +114,7 @@ class ucp_activate  			$messenger->template('admin_welcome_activated', $user_row['user_lang']); -			$messenger->to($user_row['user_email'], $user_row['username']); +			$messenger->set_addresses($user_row);  			$messenger->anti_abuse_headers($config, $user); diff --git a/phpBB/includes/ucp/ucp_groups.php b/phpBB/includes/ucp/ucp_groups.php index 8516682633..50d13e00b1 100644 --- a/phpBB/includes/ucp/ucp_groups.php +++ b/phpBB/includes/ucp/ucp_groups.php @@ -212,8 +212,7 @@ class ucp_groups  									{  										$messenger->template('group_request', $row['user_lang']); -										$messenger->to($row['user_email'], $row['username']); -										$messenger->im($row['user_jabber'], $row['username']); +										$messenger->set_addresses($row);  										$messenger->assign_vars(array(  											'USERNAME'			=> htmlspecialchars_decode($row['username']), diff --git a/phpBB/includes/ucp/ucp_notifications.php b/phpBB/includes/ucp/ucp_notifications.php index 338c921e94..72c41776b3 100644 --- a/phpBB/includes/ucp/ucp_notifications.php +++ b/phpBB/includes/ucp/ucp_notifications.php @@ -200,6 +200,10 @@ class ucp_notifications  				}  			}  		} + +		$template->assign_vars(array( +			strtoupper($block) . '_COLS' => sizeof($notification_methods) + 2, +		));  	}  	/** diff --git a/phpBB/includes/ucp/ucp_pm_compose.php b/phpBB/includes/ucp/ucp_pm_compose.php index c2d12d17c2..e0e7a46494 100644 --- a/phpBB/includes/ucp/ucp_pm_compose.php +++ b/phpBB/includes/ucp/ucp_pm_compose.php @@ -265,19 +265,16 @@ function compose_pm($id, $mode, $action, $user_folders = array())  			// Passworded forum?  			if ($post['forum_id'])  			{ -				$sql = 'SELECT forum_password +				$sql = 'SELECT forum_id, forum_name, forum_password  					FROM ' . FORUMS_TABLE . '  					WHERE forum_id = ' . (int) $post['forum_id'];  				$result = $db->sql_query($sql); -				$forum_password = (string) $db->sql_fetchfield('forum_password'); +				$forum_data = $db->sql_fetchrow($result);  				$db->sql_freeresult($result); -				if ($forum_password) +				if (!empty($forum_data['forum_password']))  				{ -					login_forum_box(array( -						'forum_id'			=> $post['forum_id'], -						'forum_password'	=> $forum_password, -					)); +					login_forum_box($forum_data);  				}  			}  		} diff --git a/phpBB/includes/ucp/ucp_pm_viewmessage.php b/phpBB/includes/ucp/ucp_pm_viewmessage.php index 712032463f..b7d2dd6821 100644 --- a/phpBB/includes/ucp/ucp_pm_viewmessage.php +++ b/phpBB/includes/ucp/ucp_pm_viewmessage.php @@ -94,8 +94,7 @@ function view_message($id, $mode, $folder_id, $msg_id, $folder, $message_row)  	// Editing information  	if ($message_row['message_edit_count'] && $config['display_last_edited'])  	{ -		$l_edit_time_total = ($message_row['message_edit_count'] == 1) ? $user->lang['EDITED_TIME_TOTAL'] : $user->lang['EDITED_TIMES_TOTAL']; -		$l_edited_by = '<br /><br />' . sprintf($l_edit_time_total, (!$message_row['message_edit_user']) ? $message_row['username'] : $message_row['message_edit_user'], $user->format_date($message_row['message_edit_time'], false, true), $message_row['message_edit_count']); +		$l_edited_by = '<br /><br />' . $user->lang('EDITED_TIMES_TOTAL', (int) $message_row['message_edit_count'], (!$message_row['message_edit_user']) ? $message_row['username'] : $message_row['message_edit_user'], $user->format_date($message_row['message_edit_time'], false, true));  	}  	else  	{ diff --git a/phpBB/includes/ucp/ucp_profile.php b/phpBB/includes/ucp/ucp_profile.php index d2507e5dbd..55df5f610c 100644 --- a/phpBB/includes/ucp/ucp_profile.php +++ b/phpBB/includes/ucp/ucp_profile.php @@ -175,8 +175,7 @@ class ucp_profile  								while ($row = $db->sql_fetchrow($result))  								{  									$messenger->template('admin_activate', $row['user_lang']); -									$messenger->to($row['user_email'], $row['username']); -									$messenger->im($row['user_jabber'], $row['username']); +									$messenger->set_addresses($row);  									$messenger->assign_vars(array(  										'USERNAME'			=> htmlspecialchars_decode($data['username']), diff --git a/phpBB/includes/ucp/ucp_register.php b/phpBB/includes/ucp/ucp_register.php index 1de38fddb7..70fbfe46fb 100644 --- a/phpBB/includes/ucp/ucp_register.php +++ b/phpBB/includes/ucp/ucp_register.php @@ -384,8 +384,7 @@ class ucp_register  						while ($row = $db->sql_fetchrow($result))  						{  							$messenger->template('admin_activate', $row['user_lang']); -							$messenger->to($row['user_email'], $row['username']); -							$messenger->im($row['user_jabber'], $row['username']); +							$messenger->set_addresses($row);  							$messenger->assign_vars(array(  								'USERNAME'			=> htmlspecialchars_decode($data['username']), diff --git a/phpBB/includes/ucp/ucp_remind.php b/phpBB/includes/ucp/ucp_remind.php index 4f65ed1866..ff7ab53736 100644 --- a/phpBB/includes/ucp/ucp_remind.php +++ b/phpBB/includes/ucp/ucp_remind.php @@ -29,6 +29,11 @@ class ucp_remind  		global $config, $phpbb_root_path, $phpEx;  		global $db, $user, $auth, $template; +		if (!$config['allow_password_reset']) +		{ +			trigger_error($user->lang('UCP_PASSWORD_RESET_DISABLED', '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>')); +		} +  		$username	= request_var('username', '', true);  		$email		= strtolower(request_var('email', ''));  		$submit		= (isset($_POST['submit'])) ? true : false; @@ -94,8 +99,7 @@ class ucp_remind  			$messenger->template('user_activate_passwd', $user_row['user_lang']); -			$messenger->to($user_row['user_email'], $user_row['username']); -			$messenger->im($user_row['user_jabber'], $user_row['username']); +			$messenger->set_addresses($user_row);  			$messenger->assign_vars(array(  				'USERNAME'		=> htmlspecialchars_decode($user_row['username']), diff --git a/phpBB/includes/ucp/ucp_resend.php b/phpBB/includes/ucp/ucp_resend.php index 5f1e3a92c3..ab396cdec9 100644 --- a/phpBB/includes/ucp/ucp_resend.php +++ b/phpBB/includes/ucp/ucp_resend.php @@ -91,7 +91,7 @@ class ucp_resend  			if ($config['require_activation'] == USER_ACTIVATION_SELF || $coppa)  			{  				$messenger->template(($coppa) ? 'coppa_resend_inactive' : 'user_resend_inactive', $user_row['user_lang']); -				$messenger->to($user_row['user_email'], $user_row['username']); +				$messenger->set_addresses($user_row);  				$messenger->anti_abuse_headers($config, $user); @@ -126,8 +126,7 @@ class ucp_resend  				while ($row = $db->sql_fetchrow($result))  				{  					$messenger->template('admin_activate', $row['user_lang']); -					$messenger->to($row['user_email'], $row['username']); -					$messenger->im($row['user_jabber'], $row['username']); +					$messenger->set_addresses($row);  					$messenger->anti_abuse_headers($config, $user); diff --git a/phpBB/includes/user.php b/phpBB/includes/user.php index 9ddd806b27..5530fe3f03 100644 --- a/phpBB/includes/user.php +++ b/phpBB/includes/user.php @@ -215,7 +215,7 @@ class phpbb_user extends phpbb_session  		if (!$this->style)  		{ -			trigger_error('Could not get style data', E_USER_ERROR); +			trigger_error('NO_STYLE_DATA', E_USER_ERROR);  		}  		// Now parse the cfg file and cache it diff --git a/phpBB/includes/user_loader.php b/phpBB/includes/user_loader.php index 77128d6570..37bf9648c1 100644 --- a/phpBB/includes/user_loader.php +++ b/phpBB/includes/user_loader.php @@ -70,8 +70,8 @@ class phpbb_user_loader  	{  		$user_ids[] = ANONYMOUS; -		// Load the users -		$user_ids = array_unique($user_ids); +		// Make user_ids unique and convert to integer. +		$user_ids = array_map('intval', array_unique($user_ids));  		// Do not load users we already have in $this->users  		$user_ids = array_diff($user_ids, array_keys($this->users));  | 
