diff options
Diffstat (limited to 'phpBB/includes')
111 files changed, 5566 insertions, 2114 deletions
diff --git a/phpBB/includes/acp/acp_attachments.php b/phpBB/includes/acp/acp_attachments.php index 9d6c2d5de1..d0e8ff3882 100644 --- a/phpBB/includes/acp/acp_attachments.php +++ b/phpBB/includes/acp/acp_attachments.php @@ -117,8 +117,8 @@ class acp_attachments  						'attachment_quota'		=> array('lang' => 'ATTACH_QUOTA',			'validate' => 'string',	'type' => 'custom', 'method' => 'max_filesize', 'explain' => true),  						'max_filesize'			=> array('lang' => 'ATTACH_MAX_FILESIZE',	'validate' => 'string',	'type' => 'custom', 'method' => 'max_filesize', 'explain' => true),  						'max_filesize_pm'		=> array('lang' => 'ATTACH_MAX_PM_FILESIZE','validate' => 'string',	'type' => 'custom', 'method' => 'max_filesize', 'explain' => true), -						'max_attachments'		=> array('lang' => 'MAX_ATTACHMENTS',		'validate' => 'int',	'type' => 'text:3:3', 'explain' => false), -						'max_attachments_pm'	=> array('lang' => 'MAX_ATTACHMENTS_PM',	'validate' => 'int',	'type' => 'text:3:3', 'explain' => false), +						'max_attachments'		=> array('lang' => 'MAX_ATTACHMENTS',		'validate' => 'int:0:999',	'type' => 'number:0:999', 'explain' => false), +						'max_attachments_pm'	=> array('lang' => 'MAX_ATTACHMENTS_PM',	'validate' => 'int:0:999',	'type' => 'number:0:999', 'explain' => false),  						'secure_downloads'		=> array('lang' => 'SECURE_DOWNLOADS',		'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true),  						'secure_allow_deny'		=> array('lang' => 'SECURE_ALLOW_DENY',		'validate' => 'int',	'type' => 'custom', 'method' => 'select_allow_deny', 'explain' => true),  						'secure_allow_empty_referer'	=> array('lang' => 'SECURE_EMPTY_REFERRER', 'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true), @@ -128,11 +128,11 @@ class acp_attachments  						'legend2'					=> $l_legend_cat_images,  						'img_display_inlined'		=> array('lang' => 'DISPLAY_INLINED',		'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true),  						'img_create_thumbnail'		=> array('lang' => 'CREATE_THUMBNAIL',		'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true), -						'img_max_thumb_width'		=> array('lang' => 'MAX_THUMB_WIDTH',		'validate' => 'int',	'type' => 'text:7:15', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']), -						'img_min_thumb_filesize'	=> array('lang' => 'MIN_THUMB_FILESIZE',	'validate' => 'int',	'type' => 'text:7:15', 'explain' => true, 'append' => ' ' . $user->lang['BYTES']), +						'img_max_thumb_width'		=> array('lang' => 'MAX_THUMB_WIDTH',		'validate' => 'int:0:999999999999999',	'type' => 'number:0:999999999999999', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']), +						'img_min_thumb_filesize'	=> array('lang' => 'MIN_THUMB_FILESIZE',	'validate' => 'int:0:999999999999999',	'type' => 'number:0:999999999999999', 'explain' => true, 'append' => ' ' . $user->lang['BYTES']),  						'img_imagick'				=> array('lang' => 'IMAGICK_PATH',			'validate' => 'string',	'type' => 'text:20:200', 'explain' => true, 'append' => '  <span>[ <a href="' . $this->u_action . '&action=imgmagick">' . $user->lang['SEARCH_IMAGICK'] . '</a> ]</span>'), -						'img_max'					=> array('lang' => 'MAX_IMAGE_SIZE',		'validate' => 'int',	'type' => 'dimension:3:4', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']), -						'img_link'					=> array('lang' => 'IMAGE_LINK_SIZE',		'validate' => 'int',	'type' => 'dimension:3:4', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']), +						'img_max'					=> array('lang' => 'MAX_IMAGE_SIZE',		'validate' => 'int:0:9999',	'type' => 'dimension:0:9999', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']), +						'img_link'					=> array('lang' => 'IMAGE_LINK_SIZE',		'validate' => 'int:0:9999',	'type' => 'dimension:0:9999', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']),  					)  				); @@ -1203,8 +1203,8 @@ class acp_attachments  				// Just get the files  				$sql = 'SELECT a.*, u.username, u.user_colour, t.topic_title -					FROM ' . ATTACHMENTS_TABLE . ' a  -					LEFT JOIN ' . USERS_TABLE . ' u ON (u.user_id = a.poster_id)  +					FROM ' . ATTACHMENTS_TABLE . ' a +					LEFT JOIN ' . USERS_TABLE . ' u ON (u.user_id = a.poster_id)  					LEFT JOIN ' . TOPICS_TABLE . " t ON (a.topic_id = t.topic_id)  					WHERE a.is_orphan = 0  						$limit_filetime @@ -1670,7 +1670,8 @@ class acp_attachments  		$size_var = $filesize['si_identifier'];  		$value = $filesize['value']; -		return '<input type="text" id="' . $key . '" size="8" maxlength="15" name="config[' . $key . ']" value="' . $value . '" /> <select name="' . $key . '">' . size_select_options($size_var) . '</select>'; +		// size="8" and maxlength="15" attributes as a fallback for browsers that do not support type="number" yet. +		return '<input type="number" id="' . $key . '" size="8" maxlength="15" min="0" name="config[' . $key . ']" value="' . $value . '" /> <select name="' . $key . '">' . size_select_options($size_var) . '</select>';  	}  	/** diff --git a/phpBB/includes/acp/acp_board.php b/phpBB/includes/acp/acp_board.php index 6543427677..24b913260b 100644 --- a/phpBB/includes/acp/acp_board.php +++ b/phpBB/includes/acp/acp_board.php @@ -53,8 +53,9 @@ class acp_board  						'legend1'				=> 'ACP_BOARD_SETTINGS',  						'sitename'				=> array('lang' => 'SITE_NAME',				'validate' => 'string',	'type' => 'text:40:255', 'explain' => false),  						'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_url'			=> array('lang' => 'SITE_HOME_URL',			'validate' => 'string',	'type' => 'url: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), @@ -64,7 +65,7 @@ class acp_board  						'override_user_style'	=> array('lang' => 'OVERRIDE_STYLE',		'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true),  						'legend2'				=> 'WARNINGS', -						'warnings_expire_days'	=> array('lang' => 'WARNINGS_EXPIRE',		'validate' => 'int',	'type' => 'text:3:4', 'explain' => true, 'append' => ' ' . $user->lang['DAYS']), +						'warnings_expire_days'	=> array('lang' => 'WARNINGS_EXPIRE',		'validate' => 'int:0:9999',	'type' => 'number:0:9999', 'explain' => true, 'append' => ' ' . $user->lang['DAYS']),  						'legend3'					=> 'ACP_SUBMIT_CHANGES',  					) @@ -135,8 +136,8 @@ class acp_board  						'avatar_max_height'		=> array('lang' => 'MAX_AVATAR_SIZE', 'validate' => 'int:0', 'type' => false, 'method' => false, 'explain' => false),  						'allow_avatar'			=> array('lang' => 'ALLOW_AVATARS',			'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true), -						'avatar_min'			=> array('lang' => 'MIN_AVATAR_SIZE',		'validate' => 'int:0',	'type' => 'dimension:3:4', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']), -						'avatar_max'			=> array('lang' => 'MAX_AVATAR_SIZE',		'validate' => 'int:0',	'type' => 'dimension:3:4', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']), +						'avatar_min'			=> array('lang' => 'MIN_AVATAR_SIZE',		'validate' => 'int:0',	'type' => 'dimension:0', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']), +						'avatar_max'			=> array('lang' => 'MAX_AVATAR_SIZE',		'validate' => 'int:0',	'type' => 'dimension:0', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']),  					)  				); @@ -153,11 +154,11 @@ class acp_board  					'vars'	=> array(  						'legend1'				=> 'GENERAL_SETTINGS',  						'allow_privmsg'			=> array('lang' => 'BOARD_PM',				'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true), -						'pm_max_boxes'			=> array('lang' => 'BOXES_MAX',				'validate' => 'int:0',	'type' => 'text:4:4', 'explain' => true), -						'pm_max_msgs'			=> array('lang' => 'BOXES_LIMIT',			'validate' => 'int:0',	'type' => 'text:4:4', 'explain' => true), +						'pm_max_boxes'			=> array('lang' => 'BOXES_MAX',				'validate' => 'int:0:9999',	'type' => 'number:0:9999', 'explain' => true), +						'pm_max_msgs'			=> array('lang' => 'BOXES_LIMIT',			'validate' => 'int:0:9999',	'type' => 'number:0:9999', 'explain' => true),  						'full_folder_action'	=> array('lang' => 'FULL_FOLDER_ACTION',	'validate' => 'int',	'type' => 'select', 'method' => 'full_folder_select', 'explain' => true), -						'pm_edit_time'			=> array('lang' => 'PM_EDIT_TIME',			'validate' => 'int:0',	'type' => 'text:5:5', 'explain' => true, 'append' => ' ' . $user->lang['MINUTES']), -						'pm_max_recipients'		=> array('lang' => 'PM_MAX_RECIPIENTS',		'validate' => 'int:0',	'type' => 'text:5:5', 'explain' => true), +						'pm_edit_time'			=> array('lang' => 'PM_EDIT_TIME',			'validate' => 'int:0:99999',	'type' => 'number:0:99999', 'explain' => true, 'append' => ' ' . $user->lang['MINUTES']), +						'pm_max_recipients'		=> array('lang' => 'PM_MAX_RECIPIENTS',		'validate' => 'int:0:99999',	'type' => 'number:0:99999', 'explain' => true),  						'legend2'				=> 'GENERAL_OPTIONS',  						'allow_mass_pm'			=> array('lang' => 'ALLOW_MASS_PM',			'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => false), @@ -194,24 +195,24 @@ class acp_board  						'legend2'				=> 'POSTING',  						'bump_type'				=> false, -						'edit_time'				=> array('lang' => 'EDIT_TIME',				'validate' => 'int:0',		'type' => 'text:5:5', 'explain' => true, 'append' => ' ' . $user->lang['MINUTES']), -						'delete_time'			=> array('lang' => 'DELETE_TIME',			'validate' => 'int:0',		'type' => 'text:5:5', 'explain' => true, 'append' => ' ' . $user->lang['MINUTES']), +						'edit_time'				=> array('lang' => 'EDIT_TIME',				'validate' => 'int:0:99999',		'type' => 'number:0:99999', 'explain' => true, 'append' => ' ' . $user->lang['MINUTES']), +						'delete_time'			=> array('lang' => 'DELETE_TIME',			'validate' => 'int:0:99999',		'type' => 'number:0:99999', 'explain' => true, 'append' => ' ' . $user->lang['MINUTES']),  						'display_last_edited'	=> array('lang' => 'DISPLAY_LAST_EDITED',	'validate' => 'bool',		'type' => 'radio:yes_no', 'explain' => true), -						'flood_interval'		=> array('lang' => 'FLOOD_INTERVAL',		'validate' => 'int:0',		'type' => 'text:3:10', 'explain' => true, 'append' => ' ' . $user->lang['SECONDS']), +						'flood_interval'		=> array('lang' => 'FLOOD_INTERVAL',		'validate' => 'int:0:9999999999',		'type' => 'number:0:9999999999', 'explain' => true, 'append' => ' ' . $user->lang['SECONDS']),  						'bump_interval'			=> array('lang' => 'BUMP_INTERVAL',			'validate' => 'int:0',		'type' => 'custom', 'method' => 'bump_interval', 'explain' => true), -						'topics_per_page'		=> array('lang' => 'TOPICS_PER_PAGE',		'validate' => 'int:1',		'type' => 'text:3:4', 'explain' => false), -						'posts_per_page'		=> array('lang' => 'POSTS_PER_PAGE',		'validate' => 'int:1',		'type' => 'text:3:4', 'explain' => false), -						'smilies_per_page'		=> array('lang' => 'SMILIES_PER_PAGE',		'validate' => 'int:1',		'type' => 'text:3:4', 'explain' => false), -						'hot_threshold'			=> array('lang' => 'HOT_THRESHOLD',			'validate' => 'int:0',		'type' => 'text:3:4', 'explain' => true), -						'max_poll_options'		=> array('lang' => 'MAX_POLL_OPTIONS',		'validate' => 'int:2:127',	'type' => 'text:4:4', 'explain' => false), -						'max_post_chars'		=> array('lang' => 'CHAR_LIMIT',			'validate' => 'int:0',		'type' => 'text:4:6', 'explain' => true), -						'min_post_chars'		=> array('lang' => 'MIN_CHAR_LIMIT',		'validate' => 'int:1',		'type' => 'text:4:6', 'explain' => true), -						'max_post_smilies'		=> array('lang' => 'SMILIES_LIMIT',			'validate' => 'int:0',		'type' => 'text:4:4', 'explain' => true), -						'max_post_urls'			=> array('lang' => 'MAX_POST_URLS',			'validate' => 'int:0',		'type' => 'text:5:4', 'explain' => true), -						'max_post_font_size'	=> array('lang' => 'MAX_POST_FONT_SIZE',	'validate' => 'int:0',		'type' => 'text:5:4', 'explain' => true, 'append' => ' %'), -						'max_quote_depth'		=> array('lang' => 'QUOTE_DEPTH_LIMIT',		'validate' => 'int:0',		'type' => 'text:4:4', 'explain' => true), -						'max_post_img_width'	=> array('lang' => 'MAX_POST_IMG_WIDTH',	'validate' => 'int:0',		'type' => 'text:5:4', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']), -						'max_post_img_height'	=> array('lang' => 'MAX_POST_IMG_HEIGHT',	'validate' => 'int:0',		'type' => 'text:5:4', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']), +						'topics_per_page'		=> array('lang' => 'TOPICS_PER_PAGE',		'validate' => 'int:1:9999',		'type' => 'number:1:9999', 'explain' => false), +						'posts_per_page'		=> array('lang' => 'POSTS_PER_PAGE',		'validate' => 'int:1:9999',		'type' => 'number:1:9999', 'explain' => false), +						'smilies_per_page'		=> array('lang' => 'SMILIES_PER_PAGE',		'validate' => 'int:1:9999',		'type' => 'number:1:9999', 'explain' => false), +						'hot_threshold'			=> array('lang' => 'HOT_THRESHOLD',			'validate' => 'int:0:9999',		'type' => 'number:0:9999', 'explain' => true), +						'max_poll_options'		=> array('lang' => 'MAX_POLL_OPTIONS',		'validate' => 'int:2:127',	'type' => 'number:2:127', 'explain' => false), +						'max_post_chars'		=> array('lang' => 'CHAR_LIMIT',			'validate' => 'int:0:999999',		'type' => 'number:0:999999', 'explain' => true), +						'min_post_chars'		=> array('lang' => 'MIN_CHAR_LIMIT',		'validate' => 'int:1:999999',		'type' => 'number:1:999999', 'explain' => true), +						'max_post_smilies'		=> array('lang' => 'SMILIES_LIMIT',			'validate' => 'int:0:9999',		'type' => 'number:0:9999', 'explain' => true), +						'max_post_urls'			=> array('lang' => 'MAX_POST_URLS',			'validate' => 'int:0:9999',		'type' => 'number:0:9999', 'explain' => true), +						'max_post_font_size'	=> array('lang' => 'MAX_POST_FONT_SIZE',	'validate' => 'int:0:9999',		'type' => 'number:0:9999', 'explain' => true, 'append' => ' %'), +						'max_quote_depth'		=> array('lang' => 'QUOTE_DEPTH_LIMIT',		'validate' => 'int:0:9999',		'type' => 'number:0:9999', 'explain' => true), +						'max_post_img_width'	=> array('lang' => 'MAX_POST_IMG_WIDTH',	'validate' => 'int:0:9999',		'type' => 'number:0:9999', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']), +						'max_post_img_height'	=> array('lang' => 'MAX_POST_IMG_HEIGHT',	'validate' => 'int:0:9999',		'type' => 'number:0:9999', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']),  						'legend3'					=> 'ACP_SUBMIT_CHANGES',  					) @@ -231,12 +232,12 @@ class acp_board  						'allow_sig_links'		=> array('lang' => 'ALLOW_SIG_LINKS',		'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true),  						'legend2'				=> 'GENERAL_SETTINGS', -						'max_sig_chars'			=> array('lang' => 'MAX_SIG_LENGTH',		'validate' => 'int:0',	'type' => 'text:5:4', 'explain' => true), -						'max_sig_urls'			=> array('lang' => 'MAX_SIG_URLS',			'validate' => 'int:0',	'type' => 'text:5:4', 'explain' => true), -						'max_sig_font_size'		=> array('lang' => 'MAX_SIG_FONT_SIZE',		'validate' => 'int:0',	'type' => 'text:5:4', 'explain' => true, 'append' => ' %'), -						'max_sig_smilies'		=> array('lang' => 'MAX_SIG_SMILIES',		'validate' => 'int:0',	'type' => 'text:5:4', 'explain' => true), -						'max_sig_img_width'		=> array('lang' => 'MAX_SIG_IMG_WIDTH',		'validate' => 'int:0',	'type' => 'text:5:4', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']), -						'max_sig_img_height'	=> array('lang' => 'MAX_SIG_IMG_HEIGHT',	'validate' => 'int:0',	'type' => 'text:5:4', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']), +						'max_sig_chars'			=> array('lang' => 'MAX_SIG_LENGTH',		'validate' => 'int:0:9999',	'type' => 'number:0:9999', 'explain' => true), +						'max_sig_urls'			=> array('lang' => 'MAX_SIG_URLS',			'validate' => 'int:0:9999',	'type' => 'number:0:9999', 'explain' => true), +						'max_sig_font_size'		=> array('lang' => 'MAX_SIG_FONT_SIZE',		'validate' => 'int:0:9999',	'type' => 'number:0:9999', 'explain' => true, 'append' => ' %'), +						'max_sig_smilies'		=> array('lang' => 'MAX_SIG_SMILIES',		'validate' => 'int:0:9999',	'type' => 'number:0:9999', 'explain' => true), +						'max_sig_img_width'		=> array('lang' => 'MAX_SIG_IMG_WIDTH',		'validate' => 'int:0:9999',	'type' => 'number:0:9999', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']), +						'max_sig_img_height'	=> array('lang' => 'MAX_SIG_IMG_HEIGHT',	'validate' => 'int:0:9999',	'type' => 'number:0:9999', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']),  						'legend3'					=> 'ACP_SUBMIT_CHANGES',  					) @@ -252,20 +253,20 @@ class acp_board  						'max_pass_chars'		=> array('lang' => 'PASSWORD_LENGTH', 'validate' => 'int:8:255', 'type' => false, 'method' => false, 'explain' => false,),  						'require_activation'	=> array('lang' => 'ACC_ACTIVATION',	'validate' => 'int',	'type' => 'select', 'method' => 'select_acc_activation', 'explain' => true), -						'new_member_post_limit'	=> array('lang' => 'NEW_MEMBER_POST_LIMIT', 'validate' => 'int:0:255', 'type' => 'text:4:4', 'explain' => true, 'append' => ' ' . $user->lang['POSTS']), +						'new_member_post_limit'	=> array('lang' => 'NEW_MEMBER_POST_LIMIT', 'validate' => 'int:0:255', 'type' => 'number:0:255', 'explain' => true, 'append' => ' ' . $user->lang['POSTS']),  						'new_member_group_default'=> array('lang' => 'NEW_MEMBER_GROUP_DEFAULT', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true),  						'min_name_chars'		=> array('lang' => 'USERNAME_LENGTH',	'validate' => 'int:1',	'type' => 'custom:5:180', 'method' => 'username_length', 'explain' => true),  						'min_pass_chars'		=> array('lang' => 'PASSWORD_LENGTH',	'validate' => 'int:1',	'type' => 'custom', 'method' => 'password_length', 'explain' => true),  						'allow_name_chars'		=> array('lang' => 'USERNAME_CHARS',	'validate' => 'string',	'type' => 'select', 'method' => 'select_username_chars', 'explain' => true),  						'pass_complex'			=> array('lang' => 'PASSWORD_TYPE',		'validate' => 'string',	'type' => 'select', 'method' => 'select_password_chars', 'explain' => true), -						'chg_passforce'			=> array('lang' => 'FORCE_PASS_CHANGE',	'validate' => 'int:0',	'type' => 'text:3:3', 'explain' => true, 'append' => ' ' . $user->lang['DAYS']), +						'chg_passforce'			=> array('lang' => 'FORCE_PASS_CHANGE',	'validate' => 'int:0:999',	'type' => 'number:0:999', 'explain' => true, 'append' => ' ' . $user->lang['DAYS']),  						'legend2'				=> 'GENERAL_OPTIONS',  						'allow_namechange'		=> array('lang' => 'ALLOW_NAME_CHANGE',		'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => false),  						'allow_emailreuse'		=> array('lang' => 'ALLOW_EMAIL_REUSE',		'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true),  						'enable_confirm'		=> array('lang' => 'VISUAL_CONFIRM_REG',	'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true), -						'max_login_attempts'	=> array('lang' => 'MAX_LOGIN_ATTEMPTS',	'validate' => 'int:0',	'type' => 'text:3:3', 'explain' => true), -						'max_reg_attempts'		=> array('lang' => 'REG_LIMIT',				'validate' => 'int:0',	'type' => 'text:4:4', 'explain' => true), +						'max_login_attempts'	=> array('lang' => 'MAX_LOGIN_ATTEMPTS',	'validate' => 'int:0:999',	'type' => 'number:0:999', 'explain' => true), +						'max_reg_attempts'		=> array('lang' => 'REG_LIMIT',				'validate' => 'int:0:9999',	'type' => 'number:0:9999', 'explain' => true),  						'legend3'			=> 'COPPA',  						'coppa_enable'		=> array('lang' => 'ENABLE_COPPA',		'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true), @@ -287,13 +288,13 @@ class acp_board  						'feed_http_auth'			=> array('lang' => 'ACP_FEED_HTTP_AUTH',			'validate' => 'bool',	'type' => 'radio:enabled_disabled',	'explain' => true),  						'legend2'					=> 'ACP_FEED_POST_BASED', -						'feed_limit_post'			=> array('lang' => 'ACP_FEED_LIMIT',				'validate' => 'int:5',	'type' => 'text:3:4',				'explain' => true), +						'feed_limit_post'			=> array('lang' => 'ACP_FEED_LIMIT',				'validate' => 'int:5:9999',	'type' => 'number:5:9999',				'explain' => true),  						'feed_overall'				=> array('lang' => 'ACP_FEED_OVERALL',				'validate' => 'bool',	'type' => 'radio:enabled_disabled',	'explain' => true ),  						'feed_forum'				=> array('lang' => 'ACP_FEED_FORUM',				'validate' => 'bool',	'type' => 'radio:enabled_disabled',	'explain' => true ),  						'feed_topic'				=> array('lang' => 'ACP_FEED_TOPIC',				'validate' => 'bool',	'type' => 'radio:enabled_disabled',	'explain' => true ),  						'legend3'					=> 'ACP_FEED_TOPIC_BASED', -						'feed_limit_topic'			=> array('lang' => 'ACP_FEED_LIMIT',				'validate' => 'int:5',	'type' => 'text:3:4',				'explain' => true), +						'feed_limit_topic'			=> array('lang' => 'ACP_FEED_LIMIT',				'validate' => 'int:5:9999',	'type' => 'number:5:9999',				'explain' => true),  						'feed_topics_new'			=> array('lang' => 'ACP_FEED_TOPICS_NEW',			'validate' => 'bool',	'type' => 'radio:enabled_disabled',	'explain' => true ),  						'feed_topics_active'		=> array('lang' => 'ACP_FEED_TOPICS_ACTIVE',		'validate' => 'bool',	'type' => 'radio:enabled_disabled',	'explain' => true ),  						'feed_news_id'				=> array('lang' => 'ACP_FEED_NEWS',					'validate' => 'string',	'type' => 'custom', 'method' => 'select_news_forums', 'explain' => true), @@ -323,10 +324,10 @@ class acp_board  					'title'	=> 'ACP_LOAD_SETTINGS',  					'vars'	=> array(  						'legend1'			=> 'GENERAL_SETTINGS', -						'limit_load'		=> array('lang' => 'LIMIT_LOAD',		'validate' => 'string',	'type' => 'text:4:4', 'explain' => true), -						'session_length'	=> array('lang' => 'SESSION_LENGTH',	'validate' => 'int:60',	'type' => 'text:5:10', 'explain' => true, 'append' => ' ' . $user->lang['SECONDS']), -						'active_sessions'	=> array('lang' => 'LIMIT_SESSIONS',	'validate' => 'int:0',	'type' => 'text:4:4', 'explain' => true), -						'load_online_time'	=> array('lang' => 'ONLINE_LENGTH',		'validate' => 'int:0',	'type' => 'text:4:3', 'explain' => true, 'append' => ' ' . $user->lang['MINUTES']), +						'limit_load'		=> array('lang' => 'LIMIT_LOAD',		'validate' => 'int:0:9999',	'type' => 'number:0:9999', 'explain' => true), +						'session_length'	=> array('lang' => 'SESSION_LENGTH',	'validate' => 'int:60:9999999999',	'type' => 'number:60:9999999999', 'explain' => true, 'append' => ' ' . $user->lang['SECONDS']), +						'active_sessions'	=> array('lang' => 'LIMIT_SESSIONS',	'validate' => 'int:0:9999',	'type' => 'number:0:9999', 'explain' => true), +						'load_online_time'	=> array('lang' => 'ONLINE_LENGTH',		'validate' => 'int:0:999',	'type' => 'number:0:999', 'explain' => true, 'append' => ' ' . $user->lang['MINUTES']),  						'legend2'				=> 'GENERAL_OPTIONS',  						'load_notifications'	=> array('lang' => 'LOAD_NOTIFICATIONS',	'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true), @@ -383,7 +384,7 @@ class acp_board  						'force_server_vars'		=> array('lang' => 'FORCE_SERVER_VARS',	'validate' => 'bool',			'type' => 'radio:yes_no', 'explain' => true),  						'server_protocol'		=> array('lang' => 'SERVER_PROTOCOL',	'validate' => 'string',			'type' => 'text:10:10', 'explain' => true),  						'server_name'			=> array('lang' => 'SERVER_NAME',		'validate' => 'string',			'type' => 'text:40:255', 'explain' => true), -						'server_port'			=> array('lang' => 'SERVER_PORT',		'validate' => 'int:0',			'type' => 'text:5:5', 'explain' => true), +						'server_port'			=> array('lang' => 'SERVER_PORT',		'validate' => 'int:0:99999',			'type' => 'number:0:99999', 'explain' => true),  						'script_path'			=> array('lang' => 'SCRIPT_PATH',		'validate' => 'script_path',	'type' => 'text::255', 'explain' => true),  						'legend4'					=> 'ACP_SUBMIT_CHANGES', @@ -397,7 +398,8 @@ class acp_board  					'vars'	=> array(  						'legend1'				=> 'ACP_SECURITY_SETTINGS',  						'allow_autologin'		=> array('lang' => 'ALLOW_AUTOLOGIN',		'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']), +						'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:99999',	'type' => 'number:0:99999',	'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),  						'forwarded_for_check'	=> array('lang' => 'FORWARDED_FOR_VALID',	'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true), @@ -407,13 +409,13 @@ class acp_board  						'max_pass_chars'		=> array('lang' => 'PASSWORD_LENGTH', 'validate' => 'int:8:255', 'type' => false, 'method' => false, 'explain' => false,),  						'min_pass_chars'		=> array('lang' => 'PASSWORD_LENGTH',	'validate' => 'int:1',	'type' => 'custom', 'method' => 'password_length', 'explain' => true),  						'pass_complex'			=> array('lang' => 'PASSWORD_TYPE',			'validate' => 'string',	'type' => 'select', 'method' => 'select_password_chars', 'explain' => true), -						'chg_passforce'			=> array('lang' => 'FORCE_PASS_CHANGE',		'validate' => 'int:0',	'type' => 'text:3:3', 'explain' => true, 'append' => ' ' . $user->lang['DAYS']), -						'max_login_attempts'	=> array('lang' => 'MAX_LOGIN_ATTEMPTS',	'validate' => 'int:0',	'type' => 'text:3:3', 'explain' => true), -						'ip_login_limit_max'	=> array('lang' => 'IP_LOGIN_LIMIT_MAX',	'validate' => 'int:0',	'type' => 'text:3:3', 'explain' => true), -						'ip_login_limit_time'	=> array('lang' => 'IP_LOGIN_LIMIT_TIME',	'validate' => 'int:0',	'type' => 'text:5:5', 'explain' => true, 'append' => ' ' . $user->lang['SECONDS']), +						'chg_passforce'			=> array('lang' => 'FORCE_PASS_CHANGE',		'validate' => 'int:0:999',	'type' => 'number:0:999', 'explain' => true, 'append' => ' ' . $user->lang['DAYS']), +						'max_login_attempts'	=> array('lang' => 'MAX_LOGIN_ATTEMPTS',	'validate' => 'int:0:999',	'type' => 'number:0:999', 'explain' => true), +						'ip_login_limit_max'	=> array('lang' => 'IP_LOGIN_LIMIT_MAX',	'validate' => 'int:0:999',	'type' => 'number:0:999', 'explain' => true), +						'ip_login_limit_time'	=> array('lang' => 'IP_LOGIN_LIMIT_TIME',	'validate' => 'int:0:99999',	'type' => 'number:0:99999', 'explain' => true, 'append' => ' ' . $user->lang['SECONDS']),  						'ip_login_limit_use_forwarded'	=> array('lang' => 'IP_LOGIN_LIMIT_USE_FORWARDED',	'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true),  						'tpl_allow_php'			=> array('lang' => 'TPL_ALLOW_PHP',			'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true), -						'form_token_lifetime'	=> array('lang' => 'FORM_TIME_MAX',			'validate' => 'int:-1',	'type' => 'text:5:5', 'explain' => true, 'append' => ' ' . $user->lang['SECONDS']), +						'form_token_lifetime'	=> array('lang' => 'FORM_TIME_MAX',			'validate' => 'int:-1:99999',	'type' => 'number:-1:99999', 'explain' => true, 'append' => ' ' . $user->lang['SECONDS']),  						'form_token_sid_guests'	=> array('lang' => 'FORM_SID_GUESTS',		'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true),  					) @@ -428,16 +430,16 @@ class acp_board  						'email_enable'			=> array('lang' => 'ENABLE_EMAIL',			'validate' => 'bool',	'type' => 'radio:enabled_disabled', 'explain' => true),  						'board_email_form'		=> array('lang' => 'BOARD_EMAIL_FORM',		'validate' => 'bool',	'type' => 'radio:enabled_disabled', 'explain' => true),  						'email_function_name'	=> array('lang' => 'EMAIL_FUNCTION_NAME',	'validate' => 'string',	'type' => 'text:20:50', 'explain' => true), -						'email_package_size'	=> array('lang' => 'EMAIL_PACKAGE_SIZE',	'validate' => 'int:0',	'type' => 'text:5:5', 'explain' => true), -						'board_contact'			=> array('lang' => 'CONTACT_EMAIL',			'validate' => 'email',	'type' => 'text:25:100', 'explain' => true), -						'board_email'			=> array('lang' => 'ADMIN_EMAIL',			'validate' => 'email',	'type' => 'text:25:100', 'explain' => true), +						'email_package_size'	=> array('lang' => 'EMAIL_PACKAGE_SIZE',	'validate' => 'int:0',	'type' => 'number:0:99999', 'explain' => true), +						'board_contact'			=> array('lang' => 'CONTACT_EMAIL',			'validate' => 'email',	'type' => 'email:25:100', 'explain' => true), +						'board_email'			=> array('lang' => 'ADMIN_EMAIL',			'validate' => 'email',	'type' => 'email:25:100', 'explain' => true),  						'board_email_sig'		=> array('lang' => 'EMAIL_SIG',				'validate' => 'string',	'type' => 'textarea:5:30', 'explain' => true),  						'board_hide_emails'		=> array('lang' => 'BOARD_HIDE_EMAILS',		'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true),  						'legend2'				=> 'SMTP_SETTINGS',  						'smtp_delivery'			=> array('lang' => 'USE_SMTP',				'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true),  						'smtp_host'				=> array('lang' => 'SMTP_SERVER',			'validate' => 'string',	'type' => 'text:25:50', 'explain' => false), -						'smtp_port'				=> array('lang' => 'SMTP_PORT',				'validate' => 'int:0',	'type' => 'text:4:5', 'explain' => true), +						'smtp_port'				=> array('lang' => 'SMTP_PORT',				'validate' => 'int:0:99999',	'type' => 'number:0:99999', 'explain' => true),  						'smtp_auth_method'		=> array('lang' => 'SMTP_AUTH_METHOD',		'validate' => 'string',	'type' => 'select', 'method' => 'mail_auth_select', 'explain' => true),  						'smtp_username'			=> array('lang' => 'SMTP_USERNAME',			'validate' => 'string',	'type' => 'text:25:255', 'explain' => true),  						'smtp_password'			=> array('lang' => 'SMTP_PASSWORD',			'validate' => 'string',	'type' => 'password:25:255', 'explain' => true), @@ -520,84 +522,54 @@ class acp_board  		if ($mode == 'auth')  		{  			// Retrieve a list of auth plugins and check their config values -			$auth_plugins = array(); +			$auth_providers = $phpbb_container->get('auth.provider_collection'); -			$dp = @opendir($phpbb_root_path . 'includes/auth'); - -			if ($dp) +			$updated_auth_settings = false; +			$old_auth_config = array(); +			foreach ($auth_providers as $provider)  			{ -				while (($file = readdir($dp)) !== false) +				if ($fields = $provider->acp($this->new_config))  				{ -					if (preg_match('#^auth_(.*?)\.' . $phpEx . '$#', $file)) +					// Check if we need to create config fields for this plugin and save config when submit was pressed +					foreach ($fields['config'] as $field)  					{ -						$auth_plugins[] = basename(preg_replace('#^auth_(.*?)\.' . $phpEx . '$#', '\1', $file)); -					} -				} -				closedir($dp); +						if (!isset($config[$field])) +						{ +							set_config($field, ''); +						} -				sort($auth_plugins); -			} +						if (!isset($cfg_array[$field]) || strpos($field, 'legend') !== false) +						{ +							continue; +						} -			$updated_auth_settings = false; -			$old_auth_config = array(); -			foreach ($auth_plugins as $method) -			{ -				if ($method && file_exists($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx)) -				{ -					include_once($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx); +						$old_auth_config[$field] = $this->new_config[$field]; +						$config_value = $cfg_array[$field]; +						$this->new_config[$field] = $config_value; -					$method = 'acp_' . $method; -					if (function_exists($method)) -					{ -						if ($fields = $method($this->new_config)) +						if ($submit)  						{ -							// Check if we need to create config fields for this plugin and save config when submit was pressed -							foreach ($fields['config'] as $field) -							{ -								if (!isset($config[$field])) -								{ -									set_config($field, ''); -								} - -								if (!isset($cfg_array[$field]) || strpos($field, 'legend') !== false) -								{ -									continue; -								} - -								$old_auth_config[$field] = $this->new_config[$field]; -								$config_value = $cfg_array[$field]; -								$this->new_config[$field] = $config_value; - -								if ($submit) -								{ -									$updated_auth_settings = true; -									set_config($field, $config_value); -								} -							} +							$updated_auth_settings = true; +							set_config($field, $config_value);  						} -						unset($fields);  					}  				} +				unset($fields);  			}  			if ($submit && (($cfg_array['auth_method'] != $this->new_config['auth_method']) || $updated_auth_settings))  			{  				$method = basename($cfg_array['auth_method']); -				if ($method && in_array($method, $auth_plugins)) +				if (array_key_exists('auth.provider.' . $method, $auth_providers))  				{ -					include_once($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx); - -					$method = 'init_' . $method; -					if (function_exists($method)) +					$provider = $auth_providers['auth.provider.' . $method]; +					if ($error = $provider->init())  					{ -						if ($error = $method()) +						foreach ($old_auth_config as $config_name => $config_value)  						{ -							foreach ($old_auth_config as $config_name => $config_value) -							{ -								set_config($config_name, $config_value); -							} -							trigger_error($error . adm_back_link($this->u_action), E_USER_WARNING); +							set_config($config_name, $config_value);  						} +						trigger_error($error . adm_back_link($this->u_action), E_USER_WARNING);  					}  					set_config('auth_method', basename($cfg_array['auth_method']));  				} @@ -681,24 +653,17 @@ class acp_board  		{  			$template->assign_var('S_AUTH', true); -			foreach ($auth_plugins as $method) +			foreach ($auth_providers as $provider)  			{ -				if ($method && file_exists($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx)) -				{ -					$method = 'acp_' . $method; -					if (function_exists($method)) -					{ -						$fields = $method($this->new_config); +				$fields = $provider->acp($this->new_config); -						if ($fields['tpl']) -						{ -							$template->assign_block_vars('auth_tpl', array( -								'TPL'	=> $fields['tpl']) -							); -						} -						unset($fields); -					} +				if ($fields['tpl']) +				{ +					$template->assign_block_vars('auth_tpl', array( +						'TPL'	=> $fields['tpl'], +					));  				} +				unset($fields);  			}  		}  	} @@ -708,26 +673,16 @@ class acp_board  	*/  	function select_auth_method($selected_method, $key = '')  	{ -		global $phpbb_root_path, $phpEx; +		global $phpbb_root_path, $phpEx, $phpbb_container;  		$auth_plugins = array(); +		$auth_providers = $phpbb_container->get('auth.provider_collection'); -		$dp = @opendir($phpbb_root_path . 'includes/auth'); - -		if (!$dp) +		foreach($auth_providers as $key => $value)  		{ -			return ''; +			$auth_plugins[] = str_replace('auth.provider.', '', $key);  		} -		while (($file = readdir($dp)) !== false) -		{ -			if (preg_match('#^auth_(.*?)\.' . $phpEx . '$#', $file)) -			{ -				$auth_plugins[] = preg_replace('#^auth_(.*?)\.' . $phpEx . '$#', '\1', $file); -			} -		} -		closedir($dp); -  		sort($auth_plugins);  		$auth_select = ''; @@ -803,7 +758,7 @@ class acp_board  		{  			$act_ary['ACC_USER'] = USER_ACTIVATION_SELF;  			$act_ary['ACC_ADMIN'] = USER_ACTIVATION_ADMIN; -		}		 +		}  		$act_options = '';  		foreach ($act_ary as $key => $value) @@ -822,7 +777,7 @@ class acp_board  	{  		global $user; -		return '<input id="' . $key . '" type="text" size="3" maxlength="3" name="config[min_name_chars]" value="' . $value . '" /> ' . $user->lang['MIN_CHARS'] . '  <input type="text" size="3" maxlength="3" name="config[max_name_chars]" value="' . $this->new_config['max_name_chars'] . '" /> ' . $user->lang['MAX_CHARS']; +		return '<input id="' . $key . '" type="number" size="3" maxlength="3" min="1" max="999" name="config[min_name_chars]" value="' . $value . '" /> ' . $user->lang['MIN_CHARS'] . '  <input type="number" size="3" maxlength="3" min="8" max="180" name="config[max_name_chars]" value="' . $this->new_config['max_name_chars'] . '" /> ' . $user->lang['MAX_CHARS'];  	}  	/** @@ -850,7 +805,7 @@ class acp_board  	{  		global $user; -		return '<input id="' . $key . '" type="text" size="3" maxlength="3" name="config[min_pass_chars]" value="' . $value . '" /> ' . $user->lang['MIN_CHARS'] . '  <input type="text" size="3" maxlength="3" name="config[max_pass_chars]" value="' . $this->new_config['max_pass_chars'] . '" /> ' . $user->lang['MAX_CHARS']; +		return '<input id="' . $key . '" type="number" size="3" maxlength="3" min="1" max="999" name="config[min_pass_chars]" value="' . $value . '" /> ' . $user->lang['MIN_CHARS'] . '  <input type="number" size="3" maxlength="3" min="8" max="255" name="config[max_pass_chars]" value="' . $this->new_config['max_pass_chars'] . '" /> ' . $user->lang['MAX_CHARS'];  	}  	/** diff --git a/phpBB/includes/acp/acp_captcha.php b/phpBB/includes/acp/acp_captcha.php index c7c64ae56b..1a083c20ac 100644 --- a/phpBB/includes/acp/acp_captcha.php +++ b/phpBB/includes/acp/acp_captcha.php @@ -124,6 +124,8 @@ class acp_captcha  					'CAPTCHA_PREVIEW_TPL'	=> $demo_captcha->get_demo_template($id),  					'S_CAPTCHA_HAS_CONFIG'	=> $demo_captcha->has_config(),  					'CAPTCHA_SELECT'		=> $captcha_select, + +					'U_ACTION'				=> $this->u_action,  				));  			}  		} diff --git a/phpBB/includes/acp/acp_extensions.php b/phpBB/includes/acp/acp_extensions.php index 24211196bd..379e779c2c 100644 --- a/phpBB/includes/acp/acp_extensions.php +++ b/phpBB/includes/acp/acp_extensions.php @@ -44,6 +44,10 @@ class acp_extensions  		$action = $request->variable('action', 'list');  		$ext_name = $request->variable('ext_name', ''); +		// What is a safe limit of execution time? Half the max execution time should be safe. +		$safe_time_limit = (ini_get('max_execution_time') / 2); +		$start_time = time(); +  		// Cancel action  		if ($request->is_set_post('cancel'))  		{ @@ -54,7 +58,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 +85,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,16 +104,20 @@ 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  				{ -					if ($phpbb_extension_manager->enable_step($ext_name)) +					while ($phpbb_extension_manager->enable_step($ext_name))  					{ -						$template->assign_var('S_NEXT_STEP', true); +						// Are we approaching the time limit? If so we want to pause the update and continue after refreshing +						if ((time() - $start_time) >= $safe_time_limit) +						{ +							$template->assign_var('S_NEXT_STEP', true); -						meta_refresh(0, $this->u_action . '&action=enable&ext_name=' . urlencode($ext_name)); +							meta_refresh(0, $this->u_action . '&action=enable&ext_name=' . urlencode($ext_name)); +						}  					}  				}  				catch (phpbb_db_migration_exception $e) @@ -139,11 +147,15 @@ class acp_extensions  			break;  			case 'disable': -				if ($phpbb_extension_manager->disable_step($ext_name)) +				while ($phpbb_extension_manager->disable_step($ext_name))  				{ -					$template->assign_var('S_NEXT_STEP', true); +					// Are we approaching the time limit? If so we want to pause the update and continue after refreshing +					if ((time() - $start_time) >= $safe_time_limit) +					{ +						$template->assign_var('S_NEXT_STEP', true); -					meta_refresh(0, $this->u_action . '&action=disable&ext_name=' . urlencode($ext_name)); +						meta_refresh(0, $this->u_action . '&action=disable&ext_name=' . urlencode($ext_name)); +					}  				}  				$this->tpl_name = 'acp_ext_disable'; @@ -165,11 +177,15 @@ class acp_extensions  			case 'purge':  				try  				{ -					if ($phpbb_extension_manager->purge_step($ext_name)) +					while ($phpbb_extension_manager->purge_step($ext_name))  					{ -						$template->assign_var('S_NEXT_STEP', true); +						// Are we approaching the time limit? If so we want to pause the update and continue after refreshing +						if ((time() - $start_time) >= $safe_time_limit) +						{ +							$template->assign_var('S_NEXT_STEP', true); -						meta_refresh(0, $this->u_action . '&action=purge&ext_name=' . urlencode($ext_name)); +							meta_refresh(0, $this->u_action . '&action=purge&ext_name=' . urlencode($ext_name)); +						}  					}  				}  				catch (phpbb_db_migration_exception $e) diff --git a/phpBB/includes/acp/acp_groups.php b/phpBB/includes/acp/acp_groups.php index 8cae0151c8..c79699d465 100644 --- a/phpBB/includes/acp/acp_groups.php +++ b/phpBB/includes/acp/acp_groups.php @@ -87,6 +87,11 @@ class acp_groups  			case 'approve':  			case 'demote':  			case 'promote': +				if (!check_form_key($form_key)) +				{ +					trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING); +				} +  				if (!$group_id)  				{  					trigger_error($user->lang['NO_GROUP'] . adm_back_link($this->u_action), E_USER_WARNING); @@ -148,57 +153,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']; -						do +					$start = 0; + +					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))  				{ @@ -258,6 +264,11 @@ class acp_groups  			break;  			case 'addusers': +				if (!check_form_key($form_key)) +				{ +					trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING); +				} +  				if (!$group_id)  				{  					trigger_error($user->lang['NO_GROUP'] . adm_back_link($this->u_action), E_USER_WARNING); @@ -380,15 +391,26 @@ class acp_groups  							$submit_ary['avatar_width'] = 0;  							$submit_ary['avatar_height'] = 0;  						} + +						// Merge any avatar errors into the primary error array +						$error = array_merge($error, $phpbb_avatar_manager->localize_errors($user, $avatar_error));  					} -					// Validate the length of "Maximum number of allowed recipients per private message" setting. -					// We use 16777215 as a maximum because it matches MySQL unsigned mediumint maximum value -					// which is the lowest amongst DBMSes supported by phpBB3 -					if ($max_recipients_error = validate_data($submit_ary, array('max_recipients' => array('num', false, 0, 16777215)))) +					/* +					* Validate the length of "Maximum number of allowed recipients per +					* private message" setting. We use 16777215 as a maximum because it matches +					* MySQL unsigned mediumint maximum value which is the lowest amongst DBMSes +					* supported by phpBB3. Also validate the submitted colour value. +					*/ +					$validation_checks = array( +						'max_recipients' => array('num', false, 0, 16777215), +						'colour'	=> array('hex_colour', true), +					); + +					if ($validation_error = validate_data($submit_ary, $validation_checks))  					{  						// Replace "error" string with its real, localised form -						$error = array_merge($error, array_map(array(&$user, 'lang'), $max_recipients_error)); +						$error = array_merge($error, array_map(array(&$user, 'lang'), $validation_error));  					}  					if (!sizeof($error)) @@ -569,8 +591,11 @@ class acp_groups  				$avatar = phpbb_get_group_avatar($group_row, 'GROUP_AVATAR', true); -				// Merge any avatar errors into the primary error array -				$error = array_merge($error, $phpbb_avatar_manager->localize_errors($user, $avatar_error)); +				if (isset($phpbb_avatar_manager) && !$update) +				{ +					// Merge any avatar errors into the primary error array +					$error = array_merge($error, $phpbb_avatar_manager->localize_errors($user, $avatar_error)); +				}  				$back_link = request_var('back_link', ''); @@ -905,10 +930,12 @@ class acp_groups  				case 'set_config_teampage':  					$config->set('teampage_forums', $request->variable('teampage_forums', 0));  					$config->set('teampage_memberships', $request->variable('teampage_memberships', 0)); +					trigger_error($user->lang['CONFIG_UPDATED'] . adm_back_link($this->u_action));  				break;  				case 'set_config_legend':  					$config->set('legend_sort_groupname', $request->variable('legend_sort_groupname', 0)); +					trigger_error($user->lang['CONFIG_UPDATED'] . adm_back_link($this->u_action));  				break;  			}  		} 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_permission_roles.php b/phpBB/includes/acp/acp_permission_roles.php index 004187af84..e830479389 100644 --- a/phpBB/includes/acp/acp_permission_roles.php +++ b/phpBB/includes/acp/acp_permission_roles.php @@ -21,6 +21,7 @@ if (!defined('IN_PHPBB'))  class acp_permission_roles  {  	var $u_action; +	protected $auth_admin;  	function main($id, $mode)  	{ @@ -30,7 +31,7 @@ class acp_permission_roles  		include_once($phpbb_root_path . 'includes/functions_user.' . $phpEx);  		include_once($phpbb_root_path . 'includes/acp/auth.' . $phpEx); -		$auth_admin = new auth_admin(); +		$this->auth_admin = new auth_admin();  		$user->add_lang('acp/permissions');  		add_permission_language(); @@ -210,7 +211,7 @@ class acp_permission_roles  					}  					// Now add the auth settings -					$auth_admin->acl_set_role($role_id, $auth_settings); +					$this->auth_admin->acl_set_role($role_id, $auth_settings);  					$role_name = (!empty($user->lang[$role_name])) ? $user->lang[$role_name] : $role_name;  					add_log('admin', 'LOG_' . strtoupper($permission_type) . 'ROLE_' . strtoupper($action), $role_name); @@ -343,7 +344,7 @@ class acp_permission_roles  				// Get users/groups/forums using this preset...  				if ($action == 'edit')  				{ -					$hold_ary = $auth_admin->get_role_mask($role_id); +					$hold_ary = $this->auth_admin->get_role_mask($role_id);  					if (sizeof($hold_ary))  					{ @@ -354,7 +355,7 @@ class acp_permission_roles  							'L_ROLE_ASSIGNED_TO'	=> sprintf($user->lang['ROLE_ASSIGNED_TO'], $role_name))  						); -						$auth_admin->display_role_mask($hold_ary); +						$this->auth_admin->display_role_mask($hold_ary);  					}  				} @@ -445,8 +446,8 @@ class acp_permission_roles  				'S_DISPLAY_ROLE_MASK'	=> true)  			); -			$hold_ary = $auth_admin->get_role_mask($display_item); -			$auth_admin->display_role_mask($hold_ary); +			$hold_ary = $this->auth_admin->get_role_mask($display_item); +			$this->auth_admin->display_role_mask($hold_ary);  		}  	} @@ -462,7 +463,7 @@ class acp_permission_roles  		$auth_options = array(0 => $auth_options);  		// Making use of auth_admin method here (we do not really want to change two similar code fragments) -		auth_admin::build_permission_array($auth_options, $content_array, $categories, $key_sort_array); +		$this->auth_admin->build_permission_array($auth_options, $content_array, $categories, $key_sort_array);  		$content_array = $content_array[0]; @@ -500,8 +501,6 @@ class acp_permission_roles  	{  		global $db; -		$auth_admin = new auth_admin(); -  		// Get complete auth array  		$sql = 'SELECT auth_option, auth_option_id  			FROM ' . ACL_OPTIONS_TABLE . " @@ -529,19 +528,19 @@ class acp_permission_roles  		$db->sql_freeresult($result);  		// Get role assignments -		$hold_ary = $auth_admin->get_role_mask($role_id); +		$hold_ary = $this->auth_admin->get_role_mask($role_id);  		// Re-assign permissions  		foreach ($hold_ary as $forum_id => $forum_ary)  		{  			if (isset($forum_ary['users']))  			{ -				$auth_admin->acl_set('user', $forum_id, $forum_ary['users'], $auth_settings, 0, false); +				$this->auth_admin->acl_set('user', $forum_id, $forum_ary['users'], $auth_settings, 0, false);  			}  			if (isset($forum_ary['groups']))  			{ -				$auth_admin->acl_set('group', $forum_id, $forum_ary['groups'], $auth_settings, 0, false); +				$this->auth_admin->acl_set('group', $forum_id, $forum_ary['groups'], $auth_settings, 0, false);  			}  		} @@ -563,6 +562,6 @@ class acp_permission_roles  			WHERE role_id = ' . $role_id;  		$db->sql_query($sql); -		$auth_admin->acl_clear_prefetch(); +		$this->auth_admin->acl_clear_prefetch();  	}  } 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_styles.php b/phpBB/includes/acp/acp_styles.php index 266495972b..094d84de40 100644 --- a/phpBB/includes/acp/acp_styles.php +++ b/phpBB/includes/acp/acp_styles.php @@ -68,13 +68,20 @@ class acp_styles  		$action = $this->request->variable('action', '');  		$post_actions = array('install', 'activate', 'deactivate', 'uninstall'); + +		if ($action && in_array($action, $post_actions) && !check_link_hash($request->variable('hash', ''), $action)) +		{ +			trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING); +		} +  		foreach ($post_actions as $key)  		{ -			if (isset($_POST[$key])) +			if ($this->request->is_set_post($key))  			{  				$action = $key;  			}  		} +  		if ($action != '')  		{  			$this->s_hidden_fields['action'] = $action; @@ -921,21 +928,23 @@ class acp_styles  				'L_ACTION'	=> $this->user->lang['DETAILS']  			); -			// Activate +			// Activate/Deactive +			$action_name = ($style['style_active'] ? 'de' : '') . 'activate'; +  			$actions[] = array( -				'U_ACTION'	=> $this->u_action . '&action=' . ($style['style_active'] ? 'de' : '') . 'activate&id=' . $style['style_id'], +				'U_ACTION'	=> $this->u_action . '&action=' . $action_name . '&hash=' . generate_link_hash($action_name) . '&id=' . $style['style_id'],  				'L_ACTION'	=> $this->user->lang['STYLE_' . ($style['style_active'] ? 'DE' : '') . 'ACTIVATE']  			);  /*			// Export  			$actions[] = array( -				'U_ACTION'	=> $this->u_action . '&action=export&id=' . $style['style_id'], +				'U_ACTION'	=> $this->u_action . '&action=export&hash=' . generate_link_hash('export') . '&id=' . $style['style_id'],  				'L_ACTION'	=> $this->user->lang['EXPORT']  			); */  			// Uninstall  			$actions[] = array( -				'U_ACTION'	=> $this->u_action . '&action=uninstall&id=' . $style['style_id'], +				'U_ACTION'	=> $this->u_action . '&action=uninstall&hash=' . generate_link_hash('uninstall') . '&id=' . $style['style_id'],  				'L_ACTION'	=> $this->user->lang['STYLE_UNINSTALL']  			); @@ -957,7 +966,7 @@ class acp_styles  			else  			{  				$actions[] = array( -					'U_ACTION'	=> $this->u_action . '&action=install&dir=' . urlencode($style['style_path']), +					'U_ACTION'	=> $this->u_action . '&action=install&hash=' . generate_link_hash('install') . '&dir=' . urlencode($style['style_path']),  					'L_ACTION'	=> $this->user->lang['INSTALL_STYLE']  				);  			} diff --git a/phpBB/includes/acp/acp_update.php b/phpBB/includes/acp/acp_update.php index f7f003781d..6b5407067d 100644 --- a/phpBB/includes/acp/acp_update.php +++ b/phpBB/includes/acp/acp_update.php @@ -38,7 +38,7 @@ class acp_update  		$info = obtain_latest_version_info(request_var('versioncheck_force', false)); -		if ($info === false) +		if (empty($info))  		{  			trigger_error('VERSIONCHECK_FAIL', E_USER_WARNING);  		} diff --git a/phpBB/includes/acp/acp_users.php b/phpBB/includes/acp/acp_users.php index 9fcff665b1..cbfd578d87 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/auth/auth.php b/phpBB/includes/auth/auth.php index 2535247571..279959974d 100644 --- a/phpBB/includes/auth/auth.php +++ b/phpBB/includes/auth/auth.php @@ -927,15 +927,14 @@ class phpbb_auth  	*/  	function login($username, $password, $autologin = false, $viewonline = 1, $admin = 0)  	{ -		global $config, $db, $user, $phpbb_root_path, $phpEx; +		global $config, $db, $user, $phpbb_root_path, $phpEx, $phpbb_container;  		$method = trim(basename($config['auth_method'])); -		include_once($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx); -		$method = 'login_' . $method; -		if (function_exists($method)) +		$provider = $phpbb_container->get('auth.provider.' . $method); +		if ($provider)  		{ -			$login = $method($username, $password, $user->ip, $user->browser, $user->forwarded_for); +			$login = $provider->login($username, $password);  			// If the auth module wants us to create an empty profile do so and then treat the status as LOGIN_SUCCESS  			if ($login['status'] == LOGIN_SUCCESS_CREATE_PROFILE) diff --git a/phpBB/includes/auth/auth_apache.php b/phpBB/includes/auth/auth_apache.php deleted file mode 100644 index 10b288aa09..0000000000 --- a/phpBB/includes/auth/auth_apache.php +++ /dev/null @@ -1,247 +0,0 @@ -<?php -/** -* Apache auth plug-in for phpBB3 -* -* Authentication plug-ins is largely down to Sergey Kanareykin, our thanks to him. -* -* @package login -* @copyright (c) 2005 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 -* -*/ - -/** -* @ignore -*/ -if (!defined('IN_PHPBB')) -{ -	exit; -} - -/** -* Checks whether the user is identified to apache -* Only allow changing authentication to apache if the user is identified -* Called in acp_board while setting authentication plugins -* -* @return boolean|string false if the user is identified and else an error message -*/ -function init_apache() -{ -	global $user, $request; - -	if (!$request->is_set('PHP_AUTH_USER', phpbb_request_interface::SERVER) || $user->data['username'] !== htmlspecialchars_decode($request->server('PHP_AUTH_USER'))) -	{ -		return $user->lang['APACHE_SETUP_BEFORE_USE']; -	} -	return false; -} - -/** -* Login function -*/ -function login_apache(&$username, &$password) -{ -	global $db, $request; - -	// do not allow empty password -	if (!$password) -	{ -		return array( -			'status'	=> LOGIN_ERROR_PASSWORD, -			'error_msg'	=> 'NO_PASSWORD_SUPPLIED', -			'user_row'	=> array('user_id' => ANONYMOUS), -		); -	} - -	if (!$username) -	{ -		return array( -			'status'	=> LOGIN_ERROR_USERNAME, -			'error_msg'	=> 'LOGIN_ERROR_USERNAME', -			'user_row'	=> array('user_id' => ANONYMOUS), -		); -	} - -	if (!$request->is_set('PHP_AUTH_USER', phpbb_request_interface::SERVER)) -	{ -		return array( -			'status'		=> LOGIN_ERROR_EXTERNAL_AUTH, -			'error_msg'		=> 'LOGIN_ERROR_EXTERNAL_AUTH_APACHE', -			'user_row'		=> array('user_id' => ANONYMOUS), -		); -	} - -	$php_auth_user = htmlspecialchars_decode($request->server('PHP_AUTH_USER')); -	$php_auth_pw = htmlspecialchars_decode($request->server('PHP_AUTH_PW')); - -	if (!empty($php_auth_user) && !empty($php_auth_pw)) -	{ -		if ($php_auth_user !== $username) -		{ -			return array( -				'status'	=> LOGIN_ERROR_USERNAME, -				'error_msg'	=> 'LOGIN_ERROR_USERNAME', -				'user_row'	=> array('user_id' => ANONYMOUS), -			); -		} - -		$sql = 'SELECT user_id, username, user_password, user_passchg, user_email, user_type -			FROM ' . USERS_TABLE . " -			WHERE username = '" . $db->sql_escape($php_auth_user) . "'"; -		$result = $db->sql_query($sql); -		$row = $db->sql_fetchrow($result); -		$db->sql_freeresult($result); - -		if ($row) -		{ -			// User inactive... -			if ($row['user_type'] == USER_INACTIVE || $row['user_type'] == USER_IGNORE) -			{ -				return array( -					'status'		=> LOGIN_ERROR_ACTIVE, -					'error_msg'		=> 'ACTIVE_ERROR', -					'user_row'		=> $row, -				); -			} - -			// Successful login... -			return array( -				'status'		=> LOGIN_SUCCESS, -				'error_msg'		=> false, -				'user_row'		=> $row, -			); -		} - -		// this is the user's first login so create an empty profile -		return array( -			'status'		=> LOGIN_SUCCESS_CREATE_PROFILE, -			'error_msg'		=> false, -			'user_row'		=> user_row_apache($php_auth_user, $php_auth_pw), -		); -	} - -	// Not logged into apache -	return array( -		'status'		=> LOGIN_ERROR_EXTERNAL_AUTH, -		'error_msg'		=> 'LOGIN_ERROR_EXTERNAL_AUTH_APACHE', -		'user_row'		=> array('user_id' => ANONYMOUS), -	); -} - -/** -* Autologin function -* -* @return array containing the user row or empty if no auto login should take place -*/ -function autologin_apache() -{ -	global $db, $request; - -	if (!$request->is_set('PHP_AUTH_USER', phpbb_request_interface::SERVER)) -	{ -		return array(); -	} - -	$php_auth_user = htmlspecialchars_decode($request->server('PHP_AUTH_USER')); -	$php_auth_pw = htmlspecialchars_decode($request->server('PHP_AUTH_PW')); - -	if (!empty($php_auth_user) && !empty($php_auth_pw)) -	{ -		set_var($php_auth_user, $php_auth_user, 'string', true); -		set_var($php_auth_pw, $php_auth_pw, 'string', true); - -		$sql = 'SELECT * -			FROM ' . USERS_TABLE . " -			WHERE username = '" . $db->sql_escape($php_auth_user) . "'"; -		$result = $db->sql_query($sql); -		$row = $db->sql_fetchrow($result); -		$db->sql_freeresult($result); - -		if ($row) -		{ -			return ($row['user_type'] == USER_INACTIVE || $row['user_type'] == USER_IGNORE) ? array() : $row; -		} - -		if (!function_exists('user_add')) -		{ -			global $phpbb_root_path, $phpEx; - -			include($phpbb_root_path . 'includes/functions_user.' . $phpEx); -		} - -		// create the user if he does not exist yet -		user_add(user_row_apache($php_auth_user, $php_auth_pw)); - -		$sql = 'SELECT * -			FROM ' . USERS_TABLE . " -			WHERE username_clean = '" . $db->sql_escape(utf8_clean_string($php_auth_user)) . "'"; -		$result = $db->sql_query($sql); -		$row = $db->sql_fetchrow($result); -		$db->sql_freeresult($result); - -		if ($row) -		{ -			return $row; -		} -	} - -	return array(); -} - -/** -* This function generates an array which can be passed to the user_add function in order to create a user -*/ -function user_row_apache($username, $password) -{ -	global $db, $config, $user; -	// first retrieve default group id -	$sql = 'SELECT group_id -		FROM ' . GROUPS_TABLE . " -		WHERE group_name = '" . $db->sql_escape('REGISTERED') . "' -			AND group_type = " . GROUP_SPECIAL; -	$result = $db->sql_query($sql); -	$row = $db->sql_fetchrow($result); -	$db->sql_freeresult($result); - -	if (!$row) -	{ -		trigger_error('NO_GROUP'); -	} - -	// generate user account data -	return array( -		'username'		=> $username, -		'user_password'	=> phpbb_hash($password), -		'user_email'	=> '', -		'group_id'		=> (int) $row['group_id'], -		'user_type'		=> USER_NORMAL, -		'user_ip'		=> $user->ip, -		'user_new'		=> ($config['new_member_post_limit']) ? 1 : 0, -	); -} - -/** -* The session validation function checks whether the user is still logged in -* -* @return boolean true if the given user is authenticated or false if the session should be closed -*/ -function validate_session_apache(&$user) -{ -	global $request; - -	// Check if PHP_AUTH_USER is set and handle this case -	if ($request->is_set('PHP_AUTH_USER', phpbb_request_interface::SERVER)) -	{ -		$php_auth_user = $request->server('PHP_AUTH_USER'); - -		return ($php_auth_user === $user['username']) ? true : false; -	} - -	// PHP_AUTH_USER is not set. A valid session is now determined by the user type (anonymous/bot or not) -	if ($user['user_type'] == USER_IGNORE) -	{ -		return true; -	} - -	return false; -} diff --git a/phpBB/includes/auth/auth_db.php b/phpBB/includes/auth/auth_db.php deleted file mode 100644 index ac944532a5..0000000000 --- a/phpBB/includes/auth/auth_db.php +++ /dev/null @@ -1,289 +0,0 @@ -<?php -/** -* Database auth plug-in for phpBB3 -* -* Authentication plug-ins is largely down to Sergey Kanareykin, our thanks to him. -* -* This is for authentication via the integrated user table -* -* @package login -* @copyright (c) 2005 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 -* -*/ - -/** -* @ignore -*/ -if (!defined('IN_PHPBB')) -{ -	exit; -} - -/** -* Login function -* -* @param string $username -* @param string $password -* @param string $ip			IP address the login is taking place from. Used to -*							limit the number of login attempts per IP address. -* @param string $browser	The user agent used to login -* @param string $forwarded_for X_FORWARDED_FOR header sent with login request -* @return array				A associative array of the format -*							array( -*								'status' => status constant -*								'error_msg' => string -*								'user_row' => array -*							) -*/ -function login_db($username, $password, $ip = '', $browser = '', $forwarded_for = '') -{ -	global $db, $config; -	global $request; - -	// Auth plugins get the password untrimmed. -	// For compatibility we trim() here. -	$password = trim($password); - -	// do not allow empty password -	if (!$password) -	{ -		return array( -			'status'	=> LOGIN_ERROR_PASSWORD, -			'error_msg'	=> 'NO_PASSWORD_SUPPLIED', -			'user_row'	=> array('user_id' => ANONYMOUS), -		); -	} - -	if (!$username) -	{ -		return array( -			'status'	=> LOGIN_ERROR_USERNAME, -			'error_msg'	=> 'LOGIN_ERROR_USERNAME', -			'user_row'	=> array('user_id' => ANONYMOUS), -		); -	} - -	$username_clean = utf8_clean_string($username); - -	$sql = 'SELECT user_id, username, user_password, user_passchg, user_pass_convert, user_email, user_type, user_login_attempts -		FROM ' . USERS_TABLE . " -		WHERE username_clean = '" . $db->sql_escape($username_clean) . "'"; -	$result = $db->sql_query($sql); -	$row = $db->sql_fetchrow($result); -	$db->sql_freeresult($result); - -	if (($ip && !$config['ip_login_limit_use_forwarded']) || -		($forwarded_for && $config['ip_login_limit_use_forwarded'])) -	{ -		$sql = 'SELECT COUNT(*) AS attempts -			FROM ' . LOGIN_ATTEMPT_TABLE . ' -			WHERE attempt_time > ' . (time() - (int) $config['ip_login_limit_time']); -		if ($config['ip_login_limit_use_forwarded']) -		{ -			$sql .= " AND attempt_forwarded_for = '" . $db->sql_escape($forwarded_for) . "'"; -		} -		else -		{ -			$sql .= " AND attempt_ip = '" . $db->sql_escape($ip) . "' "; -		} - -		$result = $db->sql_query($sql); -		$attempts = (int) $db->sql_fetchfield('attempts'); -		$db->sql_freeresult($result); - -		$attempt_data = array( -			'attempt_ip'			=> $ip, -			'attempt_browser'		=> trim(substr($browser, 0, 149)), -			'attempt_forwarded_for'	=> $forwarded_for, -			'attempt_time'			=> time(), -			'user_id'				=> ($row) ? (int) $row['user_id'] : 0, -			'username'				=> $username, -			'username_clean'		=> $username_clean, -		); -		$sql = 'INSERT INTO ' . LOGIN_ATTEMPT_TABLE . $db->sql_build_array('INSERT', $attempt_data); -		$result = $db->sql_query($sql); -	} -	else -	{ -		$attempts = 0; -	} - -	if (!$row) -	{ -		if ($config['ip_login_limit_max'] && $attempts >= $config['ip_login_limit_max']) -		{ -			return array( -				'status'		=> LOGIN_ERROR_ATTEMPTS, -				'error_msg'		=> 'LOGIN_ERROR_ATTEMPTS', -				'user_row'		=> array('user_id' => ANONYMOUS), -			); -		} - -		return array( -			'status'	=> LOGIN_ERROR_USERNAME, -			'error_msg'	=> 'LOGIN_ERROR_USERNAME', -			'user_row'	=> array('user_id' => ANONYMOUS), -		); -	} - -	$show_captcha = ($config['max_login_attempts'] && $row['user_login_attempts'] >= $config['max_login_attempts']) || -		($config['ip_login_limit_max'] && $attempts >= $config['ip_login_limit_max']); - -	// If there are too much login attempts, we need to check for an confirm image -	// Every auth module is able to define what to do by itself... -	if ($show_captcha) -	{ -		// Visual Confirmation handling -		if (!class_exists('phpbb_captcha_factory', false)) -		{ -			global $phpbb_root_path, $phpEx; -			include ($phpbb_root_path . 'includes/captcha/captcha_factory.' . $phpEx); -		} - -		$captcha = phpbb_captcha_factory::get_instance($config['captcha_plugin']); -		$captcha->init(CONFIRM_LOGIN); -		$vc_response = $captcha->validate($row); -		if ($vc_response) -		{ -			return array( -				'status'		=> LOGIN_ERROR_ATTEMPTS, -				'error_msg'		=> 'LOGIN_ERROR_ATTEMPTS', -				'user_row'		=> $row, -			); -		} -		else -		{ -			$captcha->reset(); -		} - -	} - -	// If the password convert flag is set we need to convert it -	if ($row['user_pass_convert']) -	{ -		// enable super globals to get literal value -		// this is needed to prevent unicode normalization -		$super_globals_disabled = $request->super_globals_disabled(); -		if ($super_globals_disabled) -		{ -			$request->enable_super_globals(); -		} - -		// in phpBB2 passwords were used exactly as they were sent, with addslashes applied -		$password_old_format = isset($_REQUEST['password']) ? (string) $_REQUEST['password'] : ''; -		$password_old_format = (!STRIP) ? addslashes($password_old_format) : $password_old_format; -		$password_new_format = $request->variable('password', '', true); - -		if ($super_globals_disabled) -		{ -			$request->disable_super_globals(); -		} - -		if ($password == $password_new_format) -		{ -			if (!function_exists('utf8_to_cp1252')) -			{ -				global $phpbb_root_path, $phpEx; -				include($phpbb_root_path . 'includes/utf/data/recode_basic.' . $phpEx); -			} - -			// cp1252 is phpBB2's default encoding, characters outside ASCII range might work when converted into that encoding -			// plain md5 support left in for conversions from other systems. -			if ((strlen($row['user_password']) == 34 && (phpbb_check_hash(md5($password_old_format), $row['user_password']) || phpbb_check_hash(md5(utf8_to_cp1252($password_old_format)), $row['user_password']))) -				|| (strlen($row['user_password']) == 32  && (md5($password_old_format) == $row['user_password'] || md5(utf8_to_cp1252($password_old_format)) == $row['user_password']))) -			{ -				$hash = phpbb_hash($password_new_format); - -				// Update the password in the users table to the new format and remove user_pass_convert flag -				$sql = 'UPDATE ' . USERS_TABLE . ' -					SET user_password = \'' . $db->sql_escape($hash) . '\', -						user_pass_convert = 0 -					WHERE user_id = ' . $row['user_id']; -				$db->sql_query($sql); - -				$row['user_pass_convert'] = 0; -				$row['user_password'] = $hash; -			} -			else -			{ -				// Although we weren't able to convert this password we have to -				// increase login attempt count to make sure this cannot be exploited -				$sql = 'UPDATE ' . USERS_TABLE . ' -					SET user_login_attempts = user_login_attempts + 1 -					WHERE user_id = ' . (int) $row['user_id'] . ' -						AND user_login_attempts < ' . LOGIN_ATTEMPTS_MAX; -				$db->sql_query($sql); - -				return array( -					'status'		=> LOGIN_ERROR_PASSWORD_CONVERT, -					'error_msg'		=> 'LOGIN_ERROR_PASSWORD_CONVERT', -					'user_row'		=> $row, -				); -			} -		} -	} - -	// Check password ... -	if (!$row['user_pass_convert'] && phpbb_check_hash($password, $row['user_password'])) -	{ -		// Check for old password hash... -		if (strlen($row['user_password']) == 32) -		{ -			$hash = phpbb_hash($password); - -			// Update the password in the users table to the new format -			$sql = 'UPDATE ' . USERS_TABLE . " -				SET user_password = '" . $db->sql_escape($hash) . "', -					user_pass_convert = 0 -				WHERE user_id = {$row['user_id']}"; -			$db->sql_query($sql); - -			$row['user_password'] = $hash; -		} - -		$sql = 'DELETE FROM ' . LOGIN_ATTEMPT_TABLE . ' -			WHERE user_id = ' . $row['user_id']; -		$db->sql_query($sql); - -		if ($row['user_login_attempts'] != 0) -		{ -			// Successful, reset login attempts (the user passed all stages) -			$sql = 'UPDATE ' . USERS_TABLE . ' -				SET user_login_attempts = 0 -				WHERE user_id = ' . $row['user_id']; -			$db->sql_query($sql); -		} - -		// User inactive... -		if ($row['user_type'] == USER_INACTIVE || $row['user_type'] == USER_IGNORE) -		{ -			return array( -				'status'		=> LOGIN_ERROR_ACTIVE, -				'error_msg'		=> 'ACTIVE_ERROR', -				'user_row'		=> $row, -			); -		} - -		// Successful login... set user_login_attempts to zero... -		return array( -			'status'		=> LOGIN_SUCCESS, -			'error_msg'		=> false, -			'user_row'		=> $row, -		); -	} - -	// Password incorrect - increase login attempts -	$sql = 'UPDATE ' . USERS_TABLE . ' -		SET user_login_attempts = user_login_attempts + 1 -		WHERE user_id = ' . (int) $row['user_id'] . ' -			AND user_login_attempts < ' . LOGIN_ATTEMPTS_MAX; -	$db->sql_query($sql); - -	// Give status about wrong password... -	return array( -		'status'		=> ($show_captcha) ? LOGIN_ERROR_ATTEMPTS : LOGIN_ERROR_PASSWORD, -		'error_msg'		=> ($show_captcha) ? 'LOGIN_ERROR_ATTEMPTS' : 'LOGIN_ERROR_PASSWORD', -		'user_row'		=> $row, -	); -} diff --git a/phpBB/includes/auth/auth_ldap.php b/phpBB/includes/auth/auth_ldap.php deleted file mode 100644 index 24823f9ce7..0000000000 --- a/phpBB/includes/auth/auth_ldap.php +++ /dev/null @@ -1,350 +0,0 @@ -<?php -/** -* -* LDAP auth plug-in for phpBB3 -* -* Authentication plug-ins is largely down to Sergey Kanareykin, our thanks to him. -* -* @package login -* @copyright (c) 2005 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 -* -*/ - -/** -* @ignore -*/ -if (!defined('IN_PHPBB')) -{ -	exit; -} - -/** -* Connect to ldap server -* Only allow changing authentication to ldap if we can connect to the ldap server -* Called in acp_board while setting authentication plugins -*/ -function init_ldap() -{ -	global $config, $user; - -	if (!@extension_loaded('ldap')) -	{ -		return $user->lang['LDAP_NO_LDAP_EXTENSION']; -	} - -	$config['ldap_port'] = (int) $config['ldap_port']; -	if ($config['ldap_port']) -	{ -		$ldap = @ldap_connect($config['ldap_server'], $config['ldap_port']); -	} -	else -	{ -		$ldap = @ldap_connect($config['ldap_server']); -	} - -	if (!$ldap) -	{ -		return $user->lang['LDAP_NO_SERVER_CONNECTION']; -	} - -	@ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3); -	@ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0); - -	if ($config['ldap_user'] || $config['ldap_password']) -	{ -		if (!@ldap_bind($ldap, htmlspecialchars_decode($config['ldap_user']), htmlspecialchars_decode($config['ldap_password']))) -		{ -			return $user->lang['LDAP_INCORRECT_USER_PASSWORD']; -		} -	} - -	// ldap_connect only checks whether the specified server is valid, so the connection might still fail -	$search = @ldap_search( -		$ldap, -		htmlspecialchars_decode($config['ldap_base_dn']), -		ldap_user_filter($user->data['username']), -		(empty($config['ldap_email'])) ? -			array(htmlspecialchars_decode($config['ldap_uid'])) : -			array(htmlspecialchars_decode($config['ldap_uid']), htmlspecialchars_decode($config['ldap_email'])), -		0, -		1 -	); - -	if ($search === false) -	{ -		return $user->lang['LDAP_SEARCH_FAILED']; -	} - -	$result = @ldap_get_entries($ldap, $search); - -	@ldap_close($ldap); - - -	if (!is_array($result) || sizeof($result) < 2) -	{ -		return sprintf($user->lang['LDAP_NO_IDENTITY'], $user->data['username']); -	} - -	if (!empty($config['ldap_email']) && !isset($result[0][htmlspecialchars_decode($config['ldap_email'])])) -	{ -		return $user->lang['LDAP_NO_EMAIL']; -	} - -	return false; -} - -/** -* Login function -*/ -function login_ldap(&$username, &$password) -{ -	global $db, $config, $user; - -	// do not allow empty password -	if (!$password) -	{ -		return array( -			'status'	=> LOGIN_ERROR_PASSWORD, -			'error_msg'	=> 'NO_PASSWORD_SUPPLIED', -			'user_row'	=> array('user_id' => ANONYMOUS), -		); -	} - -	if (!$username) -	{ -		return array( -			'status'	=> LOGIN_ERROR_USERNAME, -			'error_msg'	=> 'LOGIN_ERROR_USERNAME', -			'user_row'	=> array('user_id' => ANONYMOUS), -		); -	} - -	if (!@extension_loaded('ldap')) -	{ -		return array( -			'status'		=> LOGIN_ERROR_EXTERNAL_AUTH, -			'error_msg'		=> 'LDAP_NO_LDAP_EXTENSION', -			'user_row'		=> array('user_id' => ANONYMOUS), -		); -	} - -	$config['ldap_port'] = (int) $config['ldap_port']; -	if ($config['ldap_port']) -	{ -		$ldap = @ldap_connect($config['ldap_server'], $config['ldap_port']); -	} -	else -	{ -		$ldap = @ldap_connect($config['ldap_server']); -	} - -	if (!$ldap) -	{ -		return array( -			'status'		=> LOGIN_ERROR_EXTERNAL_AUTH, -			'error_msg'		=> 'LDAP_NO_SERVER_CONNECTION', -			'user_row'		=> array('user_id' => ANONYMOUS), -		); -	} - -	@ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3); -	@ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0); - -	if ($config['ldap_user'] || $config['ldap_password']) -	{ -		if (!@ldap_bind($ldap, htmlspecialchars_decode($config['ldap_user']), htmlspecialchars_decode($config['ldap_password']))) -		{ -			return array( -				'status'		=> LOGIN_ERROR_EXTERNAL_AUTH, -				'error_msg'		=> 'LDAP_NO_SERVER_CONNECTION', -				'user_row'		=> array('user_id' => ANONYMOUS), -			); -		} -	} - -	$search = @ldap_search( -		$ldap, -		htmlspecialchars_decode($config['ldap_base_dn']), -		ldap_user_filter($username), -		(empty($config['ldap_email'])) ? -			array(htmlspecialchars_decode($config['ldap_uid'])) : -			array(htmlspecialchars_decode($config['ldap_uid']), htmlspecialchars_decode($config['ldap_email'])), -		0, -		1 -	); - -	$ldap_result = @ldap_get_entries($ldap, $search); - -	if (is_array($ldap_result) && sizeof($ldap_result) > 1) -	{ -		if (@ldap_bind($ldap, $ldap_result[0]['dn'], htmlspecialchars_decode($password))) -		{ -			@ldap_close($ldap); - -			$sql ='SELECT user_id, username, user_password, user_passchg, user_email, user_type -				FROM ' . USERS_TABLE . " -				WHERE username_clean = '" . $db->sql_escape(utf8_clean_string($username)) . "'"; -			$result = $db->sql_query($sql); -			$row = $db->sql_fetchrow($result); -			$db->sql_freeresult($result); - -			if ($row) -			{ -				unset($ldap_result); - -				// User inactive... -				if ($row['user_type'] == USER_INACTIVE || $row['user_type'] == USER_IGNORE) -				{ -					return array( -						'status'		=> LOGIN_ERROR_ACTIVE, -						'error_msg'		=> 'ACTIVE_ERROR', -						'user_row'		=> $row, -					); -				} - -				// Successful login... set user_login_attempts to zero... -				return array( -					'status'		=> LOGIN_SUCCESS, -					'error_msg'		=> false, -					'user_row'		=> $row, -				); -			} -			else -			{ -				// retrieve default group id -				$sql = 'SELECT group_id -					FROM ' . GROUPS_TABLE . " -					WHERE group_name = '" . $db->sql_escape('REGISTERED') . "' -						AND group_type = " . GROUP_SPECIAL; -				$result = $db->sql_query($sql); -				$row = $db->sql_fetchrow($result); -				$db->sql_freeresult($result); - -				if (!$row) -				{ -					trigger_error('NO_GROUP'); -				} - -				// generate user account data -				$ldap_user_row = array( -					'username'		=> $username, -					'user_password'	=> phpbb_hash($password), -					'user_email'	=> (!empty($config['ldap_email'])) ? utf8_htmlspecialchars($ldap_result[0][htmlspecialchars_decode($config['ldap_email'])][0]) : '', -					'group_id'		=> (int) $row['group_id'], -					'user_type'		=> USER_NORMAL, -					'user_ip'		=> $user->ip, -					'user_new'		=> ($config['new_member_post_limit']) ? 1 : 0, -				); - -				unset($ldap_result); - -				// this is the user's first login so create an empty profile -				return array( -					'status'		=> LOGIN_SUCCESS_CREATE_PROFILE, -					'error_msg'		=> false, -					'user_row'		=> $ldap_user_row, -				); -			} -		} -		else -		{ -			unset($ldap_result); -			@ldap_close($ldap); - -			// Give status about wrong password... -			return array( -				'status'		=> LOGIN_ERROR_PASSWORD, -				'error_msg'		=> 'LOGIN_ERROR_PASSWORD', -				'user_row'		=> array('user_id' => ANONYMOUS), -			); -		} -	} - -	@ldap_close($ldap); - -	return array( -		'status'	=> LOGIN_ERROR_USERNAME, -		'error_msg'	=> 'LOGIN_ERROR_USERNAME', -		'user_row'	=> array('user_id' => ANONYMOUS), -	); -} - -/** -* Generates a filter string for ldap_search to find a user -* -* @param	$username	string	Username identifying the searched user -* -* @return				string	A filter string for ldap_search -*/ -function ldap_user_filter($username) -{ -	global $config; - -	$filter = '(' . $config['ldap_uid'] . '=' . ldap_escape(htmlspecialchars_decode($username)) . ')'; -	if ($config['ldap_user_filter']) -	{ -		$_filter = ($config['ldap_user_filter'][0] == '(' && substr($config['ldap_user_filter'], -1) == ')') ? $config['ldap_user_filter'] : "({$config['ldap_user_filter']})"; -		$filter = "(&{$filter}{$_filter})"; -	} -	return $filter; -} - -/** -* Escapes an LDAP AttributeValue -*/ -function ldap_escape($string) -{ -	return str_replace(array('*', '\\', '(', ')'), array('\\*', '\\\\', '\\(', '\\)'), $string); -} - -/** -* This function is used to output any required fields in the authentication -* admin panel. It also defines any required configuration table fields. -*/ -function acp_ldap(&$new) -{ -	global $user; - -	$tpl = ' - -	<dl> -		<dt><label for="ldap_server">' . $user->lang['LDAP_SERVER'] . $user->lang['COLON'] . '</label><br /><span>' . $user->lang['LDAP_SERVER_EXPLAIN'] . '</span></dt> -		<dd><input type="text" id="ldap_server" size="40" name="config[ldap_server]" value="' . $new['ldap_server'] . '" /></dd> -	</dl> -	<dl> -		<dt><label for="ldap_port">' . $user->lang['LDAP_PORT'] . $user->lang['COLON'] . '</label><br /><span>' . $user->lang['LDAP_PORT_EXPLAIN'] . '</span></dt> -		<dd><input type="text" id="ldap_port" size="40" name="config[ldap_port]" value="' . $new['ldap_port'] . '" /></dd> -	</dl> -	<dl> -		<dt><label for="ldap_dn">' . $user->lang['LDAP_DN'] . $user->lang['COLON'] . '</label><br /><span>' . $user->lang['LDAP_DN_EXPLAIN'] . '</span></dt> -		<dd><input type="text" id="ldap_dn" size="40" name="config[ldap_base_dn]" value="' . $new['ldap_base_dn'] . '" /></dd> -	</dl> -	<dl> -		<dt><label for="ldap_uid">' . $user->lang['LDAP_UID'] . $user->lang['COLON'] . '</label><br /><span>' . $user->lang['LDAP_UID_EXPLAIN'] . '</span></dt> -		<dd><input type="text" id="ldap_uid" size="40" name="config[ldap_uid]" value="' . $new['ldap_uid'] . '" /></dd> -	</dl> -	<dl> -		<dt><label for="ldap_user_filter">' . $user->lang['LDAP_USER_FILTER'] . $user->lang['COLON'] . '</label><br /><span>' . $user->lang['LDAP_USER_FILTER_EXPLAIN'] . '</span></dt> -		<dd><input type="text" id="ldap_user_filter" size="40" name="config[ldap_user_filter]" value="' . $new['ldap_user_filter'] . '" /></dd> -	</dl> -	<dl> -		<dt><label for="ldap_email">' . $user->lang['LDAP_EMAIL'] . $user->lang['COLON'] . '</label><br /><span>' . $user->lang['LDAP_EMAIL_EXPLAIN'] . '</span></dt> -		<dd><input type="text" id="ldap_email" size="40" name="config[ldap_email]" value="' . $new['ldap_email'] . '" /></dd> -	</dl> -	<dl> -		<dt><label for="ldap_user">' . $user->lang['LDAP_USER'] . $user->lang['COLON'] . '</label><br /><span>' . $user->lang['LDAP_USER_EXPLAIN'] . '</span></dt> -		<dd><input type="text" id="ldap_user" size="40" name="config[ldap_user]" value="' . $new['ldap_user'] . '" /></dd> -	</dl> -	<dl> -		<dt><label for="ldap_password">' . $user->lang['LDAP_PASSWORD'] . $user->lang['COLON'] . '</label><br /><span>' . $user->lang['LDAP_PASSWORD_EXPLAIN'] . '</span></dt> -		<dd><input type="password" id="ldap_password" size="40" name="config[ldap_password]" value="' . $new['ldap_password'] . '" autocomplete="off" /></dd> -	</dl> -	'; - -	// These are fields required in the config table -	return array( -		'tpl'		=> $tpl, -		'config'	=> array('ldap_server', 'ldap_port', 'ldap_base_dn', 'ldap_uid', 'ldap_user_filter', 'ldap_email', 'ldap_user', 'ldap_password') -	); -} diff --git a/phpBB/includes/auth/provider/apache.php b/phpBB/includes/auth/provider/apache.php new file mode 100644 index 0000000000..5f6f2862b6 --- /dev/null +++ b/phpBB/includes/auth/provider/apache.php @@ -0,0 +1,275 @@ +<?php +/** +* +* @package auth +* @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; +} + +/** + * Apache authentication provider for phpBB3 + * + * @package auth + */ +class phpbb_auth_provider_apache implements phpbb_auth_provider_interface +{ +	/** +	 * Apache Authentication Constructor +	 * +	 * @param 	phpbb_db_driver 	$db +	 * @param 	phpbb_config 		$config +	 * @param 	phpbb_request 		$request +	 * @param 	phpbb_user 			$user +	 * @param 	string 				$phpbb_root_path +	 * @param 	string 				$php_ext +	 */ +	public function __construct(phpbb_db_driver $db, phpbb_config $config, phpbb_request $request, phpbb_user $user, $phpbb_root_path, $php_ext) +	{ +		$this->db = $db; +		$this->config = $config; +		$this->request = $request; +		$this->user = $user; +		$this->phpbb_root_path = $phpbb_root_path; +		$this->php_ext = $php_ext; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function init() +	{ +		if (!$this->request->is_set('PHP_AUTH_USER', phpbb_request_interface::SERVER) || $this->user->data['username'] !== htmlspecialchars_decode($this->request->server('PHP_AUTH_USER'))) +		{ +			return $this->user->lang['APACHE_SETUP_BEFORE_USE']; +		} +		return false; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function login($username, $password) +	{ +		// do not allow empty password +		if (!$password) +		{ +			return array( +				'status'	=> LOGIN_ERROR_PASSWORD, +				'error_msg'	=> 'NO_PASSWORD_SUPPLIED', +				'user_row'	=> array('user_id' => ANONYMOUS), +			); +		} + +		if (!$username) +		{ +			return array( +				'status'	=> LOGIN_ERROR_USERNAME, +				'error_msg'	=> 'LOGIN_ERROR_USERNAME', +				'user_row'	=> array('user_id' => ANONYMOUS), +			); +		} + +		if (!$this->request->is_set('PHP_AUTH_USER', phpbb_request_interface::SERVER)) +		{ +			return array( +				'status'		=> LOGIN_ERROR_EXTERNAL_AUTH, +				'error_msg'		=> 'LOGIN_ERROR_EXTERNAL_AUTH_APACHE', +				'user_row'		=> array('user_id' => ANONYMOUS), +			); +		} + +		$php_auth_user = htmlspecialchars_decode($this->request->server('PHP_AUTH_USER')); +		$php_auth_pw = htmlspecialchars_decode($this->request->server('PHP_AUTH_PW')); + +		if (!empty($php_auth_user) && !empty($php_auth_pw)) +		{ +			if ($php_auth_user !== $username) +			{ +				return array( +					'status'	=> LOGIN_ERROR_USERNAME, +					'error_msg'	=> 'LOGIN_ERROR_USERNAME', +					'user_row'	=> array('user_id' => ANONYMOUS), +				); +			} + +			$sql = 'SELECT user_id, username, user_password, user_passchg, user_email, user_type +				FROM ' . USERS_TABLE . " +				WHERE username = '" . $this->db->sql_escape($php_auth_user) . "'"; +			$result = $this->db->sql_query($sql); +			$row = $this->db->sql_fetchrow($result); +			$this->db->sql_freeresult($result); + +			if ($row) +			{ +				// User inactive... +				if ($row['user_type'] == USER_INACTIVE || $row['user_type'] == USER_IGNORE) +				{ +					return array( +						'status'		=> LOGIN_ERROR_ACTIVE, +						'error_msg'		=> 'ACTIVE_ERROR', +						'user_row'		=> $row, +					); +				} + +				// Successful login... +				return array( +					'status'		=> LOGIN_SUCCESS, +					'error_msg'		=> false, +					'user_row'		=> $row, +				); +			} + +			// this is the user's first login so create an empty profile +			return array( +				'status'		=> LOGIN_SUCCESS_CREATE_PROFILE, +				'error_msg'		=> false, +				'user_row'		=> user_row_apache($php_auth_user, $php_auth_pw), +			); +		} + +		// Not logged into apache +		return array( +			'status'		=> LOGIN_ERROR_EXTERNAL_AUTH, +			'error_msg'		=> 'LOGIN_ERROR_EXTERNAL_AUTH_APACHE', +			'user_row'		=> array('user_id' => ANONYMOUS), +		); +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function autologin() +	{ +		if (!$this->request->is_set('PHP_AUTH_USER', phpbb_request_interface::SERVER)) +		{ +			return array(); +		} + +		$php_auth_user = htmlspecialchars_decode($this->request->server('PHP_AUTH_USER')); +		$php_auth_pw = htmlspecialchars_decode($this->request->server('PHP_AUTH_PW')); + +		if (!empty($php_auth_user) && !empty($php_auth_pw)) +		{ +			set_var($php_auth_user, $php_auth_user, 'string', true); +			set_var($php_auth_pw, $php_auth_pw, 'string', true); + +			$sql = 'SELECT * +				FROM ' . USERS_TABLE . " +				WHERE username = '" . $this->db->sql_escape($php_auth_user) . "'"; +			$result = $this->db->sql_query($sql); +			$row = $this->db->sql_fetchrow($result); +			$this->db->sql_freeresult($result); + +			if ($row) +			{ +				return ($row['user_type'] == USER_INACTIVE || $row['user_type'] == USER_IGNORE) ? array() : $row; +			} + +			if (!function_exists('user_add')) +			{ +				include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext); +			} + +			// create the user if he does not exist yet +			user_add(user_row_apache($php_auth_user, $php_auth_pw)); + +			$sql = 'SELECT * +				FROM ' . USERS_TABLE . " +				WHERE username_clean = '" . $this->db->sql_escape(utf8_clean_string($php_auth_user)) . "'"; +			$result = $this->db->sql_query($sql); +			$row = $this->db->sql_fetchrow($result); +			$this->db->sql_freeresult($result); + +			if ($row) +			{ +				return $row; +			} +		} + +		return array(); +	} + +	/** +	 * This function generates an array which can be passed to the user_add +	 * function in order to create a user +	 * +	 * @param 	string	$username 	The username of the new user. +	 * @param 	string	$password 	The password of the new user. +	 * @return 	array 				Contains data that can be passed directly to +	 *								the user_add function. +	 */ +	private function user_row($username, $password) +	{ +		// first retrieve default group id +		$sql = 'SELECT group_id +			FROM ' . GROUPS_TABLE . " +			WHERE group_name = '" . $this->db->sql_escape('REGISTERED') . "' +				AND group_type = " . GROUP_SPECIAL; +		$result = $this->db->sql_query($sql); +		$row = $this->db->sql_fetchrow($result); +		$this->db->sql_freeresult($result); + +		if (!$row) +		{ +			trigger_error('NO_GROUP'); +		} + +		// generate user account data +		return array( +			'username'		=> $username, +			'user_password'	=> phpbb_hash($password), +			'user_email'	=> '', +			'group_id'		=> (int) $row['group_id'], +			'user_type'		=> USER_NORMAL, +			'user_ip'		=> $this->user->ip, +			'user_new'		=> ($this->config['new_member_post_limit']) ? 1 : 0, +		); +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function validate_session($user) +	{ +		// Check if PHP_AUTH_USER is set and handle this case +		if ($this->request->is_set('PHP_AUTH_USER', phpbb_request_interface::SERVER)) +		{ +			$php_auth_user = $this->request->server('PHP_AUTH_USER'); + +			return ($php_auth_user === $user['username']) ? true : false; +		} + +		// PHP_AUTH_USER is not set. A valid session is now determined by the user type (anonymous/bot or not) +		if ($user['user_type'] == USER_IGNORE) +		{ +			return true; +		} + +		return false; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function acp($new) +	{ +		return; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function logout($data, $new_session) +	{ +		return; +	} +} diff --git a/phpBB/includes/auth/provider/db.php b/phpBB/includes/auth/provider/db.php new file mode 100644 index 0000000000..894041c9cf --- /dev/null +++ b/phpBB/includes/auth/provider/db.php @@ -0,0 +1,337 @@ +<?php +/** +* +* @package auth +* @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; +} + +/** + * Database authentication provider for phpBB3 + * + * This is for authentication via the integrated user table + * + * @package auth + */ +class phpbb_auth_provider_db implements phpbb_auth_provider_interface +{ + +	/** +	 * Database Authentication Constructor +	 * +	 * @param	phpbb_db_driver	$db +	 * @param	phpbb_config 	$config +	 * @param	phpbb_request	$request +	 * @param	phpbb_user		$user +	 * @param	string			$phpbb_root_path +	 * @param	string			$php_ext +	 */ +	public function __construct(phpbb_db_driver $db, phpbb_config $config, phpbb_request $request, phpbb_user $user, $phpbb_root_path, $php_ext) +	{ +		$this->db = $db; +		$this->config = $config; +		$this->request = $request; +		$this->user = $user; +		$this->phpbb_root_path = $phpbb_root_path; +		$this->php_ext = $php_ext; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function init() +	{ +		return; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function login($username, $password) +	{ +		// Auth plugins get the password untrimmed. +		// For compatibility we trim() here. +		$password = trim($password); + +		// do not allow empty password +		if (!$password) +		{ +			return array( +				'status'	=> LOGIN_ERROR_PASSWORD, +				'error_msg'	=> 'NO_PASSWORD_SUPPLIED', +				'user_row'	=> array('user_id' => ANONYMOUS), +			); +		} + +		if (!$username) +		{ +			return array( +				'status'	=> LOGIN_ERROR_USERNAME, +				'error_msg'	=> 'LOGIN_ERROR_USERNAME', +				'user_row'	=> array('user_id' => ANONYMOUS), +			); +		} + +		$username_clean = utf8_clean_string($username); + +		$sql = 'SELECT user_id, username, user_password, user_passchg, user_pass_convert, user_email, user_type, user_login_attempts +			FROM ' . USERS_TABLE . " +			WHERE username_clean = '" . $this->db->sql_escape($username_clean) . "'"; +		$result = $this->db->sql_query($sql); +		$row = $this->db->sql_fetchrow($result); +		$this->db->sql_freeresult($result); + +		if (($this->user->ip && !$this->config['ip_login_limit_use_forwarded']) || +			($this->user->forwarded_for && $this->config['ip_login_limit_use_forwarded'])) +		{ +			$sql = 'SELECT COUNT(*) AS attempts +				FROM ' . LOGIN_ATTEMPT_TABLE . ' +				WHERE attempt_time > ' . (time() - (int) $this->config['ip_login_limit_time']); +			if ($this->config['ip_login_limit_use_forwarded']) +			{ +				$sql .= " AND attempt_forwarded_for = '" . $this->db->sql_escape($this->user->forwarded_for) . "'"; +			} +			else +			{ +				$sql .= " AND attempt_ip = '" . $this->db->sql_escape($this->user->ip) . "' "; +			} + +			$result = $this->db->sql_query($sql); +			$attempts = (int) $this->db->sql_fetchfield('attempts'); +			$this->db->sql_freeresult($result); + +			$attempt_data = array( +				'attempt_ip'			=> $this->user->ip, +				'attempt_browser'		=> trim(substr($this->user->browser, 0, 149)), +				'attempt_forwarded_for'	=> $this->user->forwarded_for, +				'attempt_time'			=> time(), +				'user_id'				=> ($row) ? (int) $row['user_id'] : 0, +				'username'				=> $username, +				'username_clean'		=> $username_clean, +			); +			$sql = 'INSERT INTO ' . LOGIN_ATTEMPT_TABLE . $this->db->sql_build_array('INSERT', $attempt_data); +			$result = $this->db->sql_query($sql); +		} +		else +		{ +			$attempts = 0; +		} + +		if (!$row) +		{ +			if ($this->config['ip_login_limit_max'] && $attempts >= $this->config['ip_login_limit_max']) +			{ +				return array( +					'status'		=> LOGIN_ERROR_ATTEMPTS, +					'error_msg'		=> 'LOGIN_ERROR_ATTEMPTS', +					'user_row'		=> array('user_id' => ANONYMOUS), +				); +			} + +			return array( +				'status'	=> LOGIN_ERROR_USERNAME, +				'error_msg'	=> 'LOGIN_ERROR_USERNAME', +				'user_row'	=> array('user_id' => ANONYMOUS), +			); +		} + +		$show_captcha = ($this->config['max_login_attempts'] && $row['user_login_attempts'] >= $this->config['max_login_attempts']) || +			($this->config['ip_login_limit_max'] && $attempts >= $this->config['ip_login_limit_max']); + +		// If there are too many login attempts, we need to check for a confirm image +		// Every auth module is able to define what to do by itself... +		if ($show_captcha) +		{ +			// Visual Confirmation handling +			if (!class_exists('phpbb_captcha_factory', false)) +			{ +				include ($this->phpbb_root_path . 'includes/captcha/captcha_factory.' . $this->php_ext); +			} + +			$captcha = phpbb_captcha_factory::get_instance($this->config['captcha_plugin']); +			$captcha->init(CONFIRM_LOGIN); +			$vc_response = $captcha->validate($row); +			if ($vc_response) +			{ +				return array( +					'status'		=> LOGIN_ERROR_ATTEMPTS, +					'error_msg'		=> 'LOGIN_ERROR_ATTEMPTS', +					'user_row'		=> $row, +				); +			} +			else +			{ +				$captcha->reset(); +			} + +		} + +		// If the password convert flag is set we need to convert it +		if ($row['user_pass_convert']) +		{ +			// enable super globals to get literal value +			// this is needed to prevent unicode normalization +			$super_globals_disabled = $this->request->super_globals_disabled(); +			if ($super_globals_disabled) +			{ +				$this->request->enable_super_globals(); +			} + +			// in phpBB2 passwords were used exactly as they were sent, with addslashes applied +			$password_old_format = isset($_REQUEST['password']) ? (string) $_REQUEST['password'] : ''; +			$password_old_format = (!STRIP) ? addslashes($password_old_format) : $password_old_format; +			$password_new_format = $this->request->variable('password', '', true); + +			if ($super_globals_disabled) +			{ +				$this->request->disable_super_globals(); +			} + +			if ($password == $password_new_format) +			{ +				if (!function_exists('utf8_to_cp1252')) +				{ +					include($this->phpbb_root_path . 'includes/utf/data/recode_basic.' . $this->php_ext); +				} + +				// cp1252 is phpBB2's default encoding, characters outside ASCII range might work when converted into that encoding +				// plain md5 support left in for conversions from other systems. +				if ((strlen($row['user_password']) == 34 && (phpbb_check_hash(md5($password_old_format), $row['user_password']) || phpbb_check_hash(md5(utf8_to_cp1252($password_old_format)), $row['user_password']))) +					|| (strlen($row['user_password']) == 32  && (md5($password_old_format) == $row['user_password'] || md5(utf8_to_cp1252($password_old_format)) == $row['user_password']))) +				{ +					$hash = phpbb_hash($password_new_format); + +					// Update the password in the users table to the new format and remove user_pass_convert flag +					$sql = 'UPDATE ' . USERS_TABLE . ' +						SET user_password = \'' . $this->db->sql_escape($hash) . '\', +							user_pass_convert = 0 +						WHERE user_id = ' . $row['user_id']; +					$this->db->sql_query($sql); + +					$row['user_pass_convert'] = 0; +					$row['user_password'] = $hash; +				} +				else +				{ +					// Although we weren't able to convert this password we have to +					// increase login attempt count to make sure this cannot be exploited +					$sql = 'UPDATE ' . USERS_TABLE . ' +						SET user_login_attempts = user_login_attempts + 1 +						WHERE user_id = ' . (int) $row['user_id'] . ' +							AND user_login_attempts < ' . LOGIN_ATTEMPTS_MAX; +					$this->db->sql_query($sql); + +					return array( +						'status'		=> LOGIN_ERROR_PASSWORD_CONVERT, +						'error_msg'		=> 'LOGIN_ERROR_PASSWORD_CONVERT', +						'user_row'		=> $row, +					); +				} +			} +		} + +		// Check password ... +		if (!$row['user_pass_convert'] && phpbb_check_hash($password, $row['user_password'])) +		{ +			// Check for old password hash... +			if (strlen($row['user_password']) == 32) +			{ +				$hash = phpbb_hash($password); + +				// Update the password in the users table to the new format +				$sql = 'UPDATE ' . USERS_TABLE . " +					SET user_password = '" . $this->db->sql_escape($hash) . "', +						user_pass_convert = 0 +					WHERE user_id = {$row['user_id']}"; +				$this->db->sql_query($sql); + +				$row['user_password'] = $hash; +			} + +			$sql = 'DELETE FROM ' . LOGIN_ATTEMPT_TABLE . ' +				WHERE user_id = ' . $row['user_id']; +			$this->db->sql_query($sql); + +			if ($row['user_login_attempts'] != 0) +			{ +				// Successful, reset login attempts (the user passed all stages) +				$sql = 'UPDATE ' . USERS_TABLE . ' +					SET user_login_attempts = 0 +					WHERE user_id = ' . $row['user_id']; +				$this->db->sql_query($sql); +			} + +			// User inactive... +			if ($row['user_type'] == USER_INACTIVE || $row['user_type'] == USER_IGNORE) +			{ +				return array( +					'status'		=> LOGIN_ERROR_ACTIVE, +					'error_msg'		=> 'ACTIVE_ERROR', +					'user_row'		=> $row, +				); +			} + +			// Successful login... set user_login_attempts to zero... +			return array( +				'status'		=> LOGIN_SUCCESS, +				'error_msg'		=> false, +				'user_row'		=> $row, +			); +		} + +		// Password incorrect - increase login attempts +		$sql = 'UPDATE ' . USERS_TABLE . ' +			SET user_login_attempts = user_login_attempts + 1 +			WHERE user_id = ' . (int) $row['user_id'] . ' +				AND user_login_attempts < ' . LOGIN_ATTEMPTS_MAX; +		$this->db->sql_query($sql); + +		// Give status about wrong password... +		return array( +			'status'		=> ($show_captcha) ? LOGIN_ERROR_ATTEMPTS : LOGIN_ERROR_PASSWORD, +			'error_msg'		=> ($show_captcha) ? 'LOGIN_ERROR_ATTEMPTS' : 'LOGIN_ERROR_PASSWORD', +			'user_row'		=> $row, +		); +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function autologin() +	{ +		return; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function acp($new) +	{ +		return; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function logout($data, $new_session) +	{ +		return; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function validate_session($user) +	{ +		return; +	} +} diff --git a/phpBB/includes/auth/provider/index.htm b/phpBB/includes/auth/provider/index.htm new file mode 100644 index 0000000000..ee1f723a7d --- /dev/null +++ b/phpBB/includes/auth/provider/index.htm @@ -0,0 +1,10 @@ +<html> +<head> +<title></title> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> +</head> + +<body bgcolor="#FFFFFF" text="#000000"> + +</body> +</html> diff --git a/phpBB/includes/auth/provider/interface.php b/phpBB/includes/auth/provider/interface.php new file mode 100644 index 0000000000..2d1935f8f0 --- /dev/null +++ b/phpBB/includes/auth/provider/interface.php @@ -0,0 +1,93 @@ +<?php +/** +* +* @package auth +* @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; +} + +/** + * The interface authentication provider classes have to implement. + * + * @package auth + */ +interface phpbb_auth_provider_interface +{ +	/** +	 * Checks whether the user is currently identified to the authentication +	 * provider. +	 * Called in acp_board while setting authentication plugins. +	 * Changing to an authentication provider will not be permitted in acp_board +	 * if there is an error. +	 * +	 * @return 	boolean|string 	False if the user is identified, otherwise an +	 *							error message, or null if not implemented. +	 */ +	public function init(); + +	/** +	 * Performs login. +	 * +	 * @param	string	$username 	The name of the user being authenticated. +	 * @param	string	$password	The password of the user. +	 * @return	array	An associative array of the format: +	 *						array( +	 *							'status' => status constant +	 *							'error_msg' => string +	 *							'user_row' => array +	 *						) +	 */ +	public function login($username, $password); + +	/** +	 * Autologin function +	 * +	 * @return 	array|null	containing the user row, empty if no auto login +	 * 						should take place, or null if not impletmented. +	 */ +	public function autologin(); + +	/** +	 * This function is used to output any required fields in the authentication +	 * admin panel. It also defines any required configuration table fields. +	 * +	 * @param 	array 	$new 	Contains the new configuration values that have +	 * 							been set in acp_board. +	 * @return	array|null	Returns null if not implemented or an array of the +	 *						form: +	 *							array( +	 *								'tpl'		=> string +	 *								'config' 	=> array +	 *							) +	 */ +	public function acp($new); + +	/** +	 * Performs additional actions during logout. +	 * +	 * @param 	array	$data			An array corresponding to +	 *									phpbb_session::data +	 * @param 	boolean	$new_session	True for a new session, false for no new +	 *									session. +	 */ +	public function logout($data, $new_session); + +	/** +	 * The session validation function checks whether the user is still logged +	 * into phpBB. +	 * +	 * @param 	array 	$user +	 * @return 	boolean	true if the given user is authenticated, false if the  +	 * 					session should be closed, or null if not implemented. +	 */ +	public function validate_session($user); +} diff --git a/phpBB/includes/auth/provider/ldap.php b/phpBB/includes/auth/provider/ldap.php new file mode 100644 index 0000000000..f67c1e9247 --- /dev/null +++ b/phpBB/includes/auth/provider/ldap.php @@ -0,0 +1,386 @@ +<?php +/** +* +* @package auth +* @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; +} + +/** + * Database authentication provider for phpBB3 + * + * This is for authentication via the integrated user table + * + * @package auth + */ +class phpbb_auth_provider_ldap implements phpbb_auth_provider_interface +{ +	/** +	 * LDAP Authentication Constructor +	 * +	 * @param 	phpbb_db_driver	$db +	 * @param 	phpbb_config	$config +	 * @param 	phpbb_user		$user +	 */ +	public function __construct(phpbb_db_driver $db, phpbb_config $config, phpbb_user $user) +	{ +		$this->db = $db; +		$this->config = $config; +		$this->user = $user; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function init() +	{ +		if (!@extension_loaded('ldap')) +		{ +			return $this->user->lang['LDAP_NO_LDAP_EXTENSION']; +		} + +		$this->config['ldap_port'] = (int) $this->config['ldap_port']; +		if ($this->config['ldap_port']) +		{ +			$ldap = @ldap_connect($this->config['ldap_server'], $this->config['ldap_port']); +		} +		else +		{ +			$ldap = @ldap_connect($this->config['ldap_server']); +		} + +		if (!$ldap) +		{ +			return $this->user->lang['LDAP_NO_SERVER_CONNECTION']; +		} + +		@ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3); +		@ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0); + +		if ($this->config['ldap_user'] || $this->config['ldap_password']) +		{ +			if (!@ldap_bind($ldap, htmlspecialchars_decode($this->config['ldap_user']), htmlspecialchars_decode($this->config['ldap_password']))) +			{ +				return $this->user->lang['LDAP_INCORRECT_USER_PASSWORD']; +			} +		} + +		// ldap_connect only checks whether the specified server is valid, so the connection might still fail +		$search = @ldap_search( +			$ldap, +			htmlspecialchars_decode($this->config['ldap_base_dn']), +			$this->ldap_user_filter($this->user->data['username']), +			(empty($this->config['ldap_email'])) ? +				array(htmlspecialchars_decode($this->config['ldap_uid'])) : +				array(htmlspecialchars_decode($this->config['ldap_uid']), htmlspecialchars_decode($this->config['ldap_email'])), +			0, +			1 +		); + +		if ($search === false) +		{ +			return $this->user->lang['LDAP_SEARCH_FAILED']; +		} + +		$result = @ldap_get_entries($ldap, $search); + +		@ldap_close($ldap); + + +		if (!is_array($result) || sizeof($result) < 2) +		{ +			return sprintf($this->user->lang['LDAP_NO_IDENTITY'], $this->user->data['username']); +		} + +		if (!empty($this->config['ldap_email']) && !isset($result[0][htmlspecialchars_decode($this->config['ldap_email'])])) +		{ +			return $this->user->lang['LDAP_NO_EMAIL']; +		} + +		return false; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function login($username, $password) +	{ +		// do not allow empty password +		if (!$password) +		{ +			return array( +				'status'	=> LOGIN_ERROR_PASSWORD, +				'error_msg'	=> 'NO_PASSWORD_SUPPLIED', +				'user_row'	=> array('user_id' => ANONYMOUS), +			); +		} + +		if (!$username) +		{ +			return array( +				'status'	=> LOGIN_ERROR_USERNAME, +				'error_msg'	=> 'LOGIN_ERROR_USERNAME', +				'user_row'	=> array('user_id' => ANONYMOUS), +			); +		} + +		if (!@extension_loaded('ldap')) +		{ +			return array( +				'status'		=> LOGIN_ERROR_EXTERNAL_AUTH, +				'error_msg'		=> 'LDAP_NO_LDAP_EXTENSION', +				'user_row'		=> array('user_id' => ANONYMOUS), +			); +		} + +		$this->config['ldap_port'] = (int) $this->config['ldap_port']; +		if ($this->config['ldap_port']) +		{ +			$ldap = @ldap_connect($this->config['ldap_server'], $this->config['ldap_port']); +		} +		else +		{ +			$ldap = @ldap_connect($this->config['ldap_server']); +		} + +		if (!$ldap) +		{ +			return array( +				'status'		=> LOGIN_ERROR_EXTERNAL_AUTH, +				'error_msg'		=> 'LDAP_NO_SERVER_CONNECTION', +				'user_row'		=> array('user_id' => ANONYMOUS), +			); +		} + +		@ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3); +		@ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0); + +		if ($this->config['ldap_user'] || $this->config['ldap_password']) +		{ +			if (!@ldap_bind($ldap, htmlspecialchars_decode($this->config['ldap_user']), htmlspecialchars_decode($this->config['ldap_password']))) +			{ +				return array( +					'status'		=> LOGIN_ERROR_EXTERNAL_AUTH, +					'error_msg'		=> 'LDAP_NO_SERVER_CONNECTION', +					'user_row'		=> array('user_id' => ANONYMOUS), +				); +			} +		} + +		$search = @ldap_search( +			$ldap, +			htmlspecialchars_decode($this->config['ldap_base_dn']), +			$this->ldap_user_filter($username), +			(empty($this->config['ldap_email'])) ? +				array(htmlspecialchars_decode($this->config['ldap_uid'])) : +				array(htmlspecialchars_decode($this->config['ldap_uid']), htmlspecialchars_decode($this->config['ldap_email'])), +			0, +			1 +		); + +		$ldap_result = @ldap_get_entries($ldap, $search); + +		if (is_array($ldap_result) && sizeof($ldap_result) > 1) +		{ +			if (@ldap_bind($ldap, $ldap_result[0]['dn'], htmlspecialchars_decode($password))) +			{ +				@ldap_close($ldap); + +				$sql ='SELECT user_id, username, user_password, user_passchg, user_email, user_type +					FROM ' . USERS_TABLE . " +					WHERE username_clean = '" . $this->db->sql_escape(utf8_clean_string($username)) . "'"; +				$result = $this->db->sql_query($sql); +				$row = $this->db->sql_fetchrow($result); +				$this->db->sql_freeresult($result); + +				if ($row) +				{ +					unset($ldap_result); + +					// User inactive... +					if ($row['user_type'] == USER_INACTIVE || $row['user_type'] == USER_IGNORE) +					{ +						return array( +							'status'		=> LOGIN_ERROR_ACTIVE, +							'error_msg'		=> 'ACTIVE_ERROR', +							'user_row'		=> $row, +						); +					} + +					// Successful login... set user_login_attempts to zero... +					return array( +						'status'		=> LOGIN_SUCCESS, +						'error_msg'		=> false, +						'user_row'		=> $row, +					); +				} +				else +				{ +					// retrieve default group id +					$sql = 'SELECT group_id +						FROM ' . GROUPS_TABLE . " +						WHERE group_name = '" . $this->db->sql_escape('REGISTERED') . "' +							AND group_type = " . GROUP_SPECIAL; +					$result = $this->db->sql_query($sql); +					$row = $this->db->sql_fetchrow($result); +					$this->db->sql_freeresult($result); + +					if (!$row) +					{ +						trigger_error('NO_GROUP'); +					} + +					// generate user account data +					$ldap_user_row = array( +						'username'		=> $username, +						'user_password'	=> phpbb_hash($password), +						'user_email'	=> (!empty($this->config['ldap_email'])) ? utf8_htmlspecialchars($ldap_result[0][htmlspecialchars_decode($this->config['ldap_email'])][0]) : '', +						'group_id'		=> (int) $row['group_id'], +						'user_type'		=> USER_NORMAL, +						'user_ip'		=> $this->user->ip, +						'user_new'		=> ($this->config['new_member_post_limit']) ? 1 : 0, +					); + +					unset($ldap_result); + +					// this is the user's first login so create an empty profile +					return array( +						'status'		=> LOGIN_SUCCESS_CREATE_PROFILE, +						'error_msg'		=> false, +						'user_row'		=> $ldap_user_row, +					); +				} +			} +			else +			{ +				unset($ldap_result); +				@ldap_close($ldap); + +				// Give status about wrong password... +				return array( +					'status'		=> LOGIN_ERROR_PASSWORD, +					'error_msg'		=> 'LOGIN_ERROR_PASSWORD', +					'user_row'		=> array('user_id' => ANONYMOUS), +				); +			} +		} + +		@ldap_close($ldap); + +		return array( +			'status'	=> LOGIN_ERROR_USERNAME, +			'error_msg'	=> 'LOGIN_ERROR_USERNAME', +			'user_row'	=> array('user_id' => ANONYMOUS), +		); +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function autologin() +	{ +		return; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function acp($new) +	{ +		$tpl = ' + +		<dl> +			<dt><label for="ldap_server">' . $this->user->lang['LDAP_SERVER'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['LDAP_SERVER_EXPLAIN'] . '</span></dt> +			<dd><input type="text" id="ldap_server" size="40" name="config[ldap_server]" value="' . $new['ldap_server'] . '" /></dd> +		</dl> +		<dl> +			<dt><label for="ldap_port">' . $this->user->lang['LDAP_PORT'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['LDAP_PORT_EXPLAIN'] . '</span></dt> +			<dd><input type="text" id="ldap_port" size="40" name="config[ldap_port]" value="' . $new['ldap_port'] . '" /></dd> +		</dl> +		<dl> +			<dt><label for="ldap_dn">' . $this->user->lang['LDAP_DN'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['LDAP_DN_EXPLAIN'] . '</span></dt> +			<dd><input type="text" id="ldap_dn" size="40" name="config[ldap_base_dn]" value="' . $new['ldap_base_dn'] . '" /></dd> +		</dl> +		<dl> +			<dt><label for="ldap_uid">' . $this->user->lang['LDAP_UID'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['LDAP_UID_EXPLAIN'] . '</span></dt> +			<dd><input type="text" id="ldap_uid" size="40" name="config[ldap_uid]" value="' . $new['ldap_uid'] . '" /></dd> +		</dl> +		<dl> +			<dt><label for="ldap_user_filter">' . $this->user->lang['LDAP_USER_FILTER'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['LDAP_USER_FILTER_EXPLAIN'] . '</span></dt> +			<dd><input type="text" id="ldap_user_filter" size="40" name="config[ldap_user_filter]" value="' . $new['ldap_user_filter'] . '" /></dd> +		</dl> +		<dl> +			<dt><label for="ldap_email">' . $this->user->lang['LDAP_EMAIL'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['LDAP_EMAIL_EXPLAIN'] . '</span></dt> +			<dd><input type="email" id="ldap_email" size="40" name="config[ldap_email]" value="' . $new['ldap_email'] . '" /></dd> +		</dl> +		<dl> +			<dt><label for="ldap_user">' . $this->user->lang['LDAP_USER'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['LDAP_USER_EXPLAIN'] . '</span></dt> +			<dd><input type="text" id="ldap_user" size="40" name="config[ldap_user]" value="' . $new['ldap_user'] . '" /></dd> +		</dl> +		<dl> +			<dt><label for="ldap_password">' . $this->user->lang['LDAP_PASSWORD'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['LDAP_PASSWORD_EXPLAIN'] . '</span></dt> +			<dd><input type="password" id="ldap_password" size="40" name="config[ldap_password]" value="' . $new['ldap_password'] . '" autocomplete="off" /></dd> +		</dl> +		'; + +		// These are fields required in the config table +		return array( +			'tpl'		=> $tpl, +			'config'	=> array('ldap_server', 'ldap_port', 'ldap_base_dn', 'ldap_uid', 'ldap_user_filter', 'ldap_email', 'ldap_user', 'ldap_password') +		); +	} + +	/** +	 * Generates a filter string for ldap_search to find a user +	 * +	 * @param	$username	string	Username identifying the searched user +	 * +	 * @return				string	A filter string for ldap_search +	 */ +	private function ldap_user_filter($username) +	{ +		$filter = '(' . $this->config['ldap_uid'] . '=' . $this->ldap_escape(htmlspecialchars_decode($username)) . ')'; +		if ($this->config['ldap_user_filter']) +		{ +			$_filter = ($this->config['ldap_user_filter'][0] == '(' && substr($this->config['ldap_user_filter'], -1) == ')') ? $this->config['ldap_user_filter'] : "({$this->config['ldap_user_filter']})"; +			$filter = "(&{$filter}{$_filter})"; +		} +		return $filter; +	} + +	/** +	 * Escapes an LDAP AttributeValue +	 * +	 * @param	string	$string	The string to be escaped +	 * @return	string	The escaped string +	 */ +	private function ldap_escape($string) +	{ +		return str_replace(array('*', '\\', '(', ')'), array('\\*', '\\\\', '\\(', '\\)'), $string); +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function logout($data, $new_session) +	{ +		return; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function validate_session($user) +	{ +		return; +	} +} 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..baf51f61c1 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)); @@ -126,7 +126,7 @@ class phpbb_avatar_driver_upload extends phpbb_avatar_driver  	{  		return array(  			'allow_avatar_remote_upload'=> array('lang' => 'ALLOW_REMOTE_UPLOAD', 'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true), -			'avatar_filesize'		=> array('lang' => 'MAX_FILESIZE',			'validate' => 'int:0',	'type' => 'text:4:10', 'explain' => true, 'append' => ' ' . $user->lang['BYTES']), +			'avatar_filesize'		=> array('lang' => 'MAX_FILESIZE',			'validate' => 'int:0',	'type' => 'number:0', 'explain' => true, 'append' => ' ' . $user->lang['BYTES']),  			'avatar_path'			=> array('lang' => 'AVATAR_STORAGE_PATH',	'validate' => 'rwpath',	'type' => 'text:20:255', 'explain' => true),  		);  	} diff --git a/phpBB/includes/avatar/manager.php b/phpBB/includes/avatar/manager.php index 9c60436de8..58d994c3c0 100644 --- a/phpBB/includes/avatar/manager.php +++ b/phpBB/includes/avatar/manager.php @@ -46,6 +46,17 @@ class phpbb_avatar_manager  	protected $container;  	/** +	* Default avatar data row +	* @var array +	*/ +	static protected $default_row = array( +		'avatar'		=> '', +		'avatar_type'	=> '', +		'avatar_width'	=> '', +		'avatar_height'	=> '', +	); + +	/**  	* Construct an avatar manager object  	*  	* @param phpbb_config $config phpBB configuration @@ -174,6 +185,12 @@ class phpbb_avatar_manager  	*/  	static public function clean_row($row)  	{ +		// Upon creation of a user/group $row might be empty +		if (empty($row)) +		{ +			return self::$default_row; +		} +  		$keys = array_keys($row);  		$values = array_values($row); 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/constants.php b/phpBB/includes/constants.php index 5c4c5f4327..c1f4c6ac0e 100644 --- a/phpBB/includes/constants.php +++ b/phpBB/includes/constants.php @@ -160,6 +160,7 @@ define('PHYSICAL_LINK', 2);  define('CONFIRM_REG', 1);  define('CONFIRM_LOGIN', 2);  define('CONFIRM_POST', 3); +define('CONFIRM_REPORT', 4);  // Categories - Attachments  define('ATTACHMENT_CATEGORY_NONE', 0); 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..a1d1a5d5dd 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  	*/ @@ -261,7 +253,7 @@ class phpbb_db_driver_mssql_odbc extends phpbb_db_driver  	* Fetch current row  	* @note number of bytes returned depends on odbc.defaultlrl php.ini setting. If it is limited to 4K for example only 4K of data is returned max.  	*/ -	function sql_fetchrow($query_id = false, $debug = false) +	function sql_fetchrow($query_id = false)  	{  		global $cache; @@ -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..28fc88298a 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; @@ -334,7 +326,7 @@ class phpbb_db_driver_mssqlnative extends phpbb_db_driver  					$this->sql_report('stop', $query);  				} -				if ($cache_ttl) +				if ($cache && $cache_ttl)  				{  					$this->open_queries[(int) $this->query_result] = $this->query_result;  					$this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl); @@ -402,7 +394,7 @@ class phpbb_db_driver_mssqlnative extends phpbb_db_driver  	*/  	function sql_affectedrows()  	{ -		return (!empty($this->query_result)) ? @sqlsrv_rows_affected($this->query_result) : false; +		return ($this->db_connect_id) ? @sqlsrv_rows_affected($this->query_result) : false;  	}  	/** @@ -417,7 +409,7 @@ class phpbb_db_driver_mssqlnative extends phpbb_db_driver  			$query_id = $this->query_result;  		} -		if ($cache->sql_exists($query_id)) +		if ($cache && $cache->sql_exists($query_id))  		{  			return $cache->sql_fetchrow($query_id);  		} @@ -482,40 +474,15 @@ class phpbb_db_driver_mssqlnative extends phpbb_db_driver  			return $cache->sql_freeresult($query_id);  		} -		if (isset($this->open_queries[$query_id])) +		if (isset($this->open_queries[(int) $query_id]))  		{ -			unset($this->open_queries[$query_id]); +			unset($this->open_queries[(int) $query_id]);  			return @sqlsrv_free_stmt($query_id);  		}  		return false;  	}  	/** -	* 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/notifications_schema_fix.php b/phpBB/includes/db/migration/data/310/notifications_schema_fix.php new file mode 100644 index 0000000000..27e63e10d0 --- /dev/null +++ b/phpBB/includes/db/migration/data/310/notifications_schema_fix.php @@ -0,0 +1,92 @@ +<?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_notifications_schema_fix extends phpbb_db_migration +{ +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_310_notifications'); +	} + +	public function update_schema() +	{ +		return array( +			'drop_tables'		=> array( +				$this->table_prefix . 'notification_types', +				$this->table_prefix . 'notifications', +			), +			'add_tables'		=> array( +				$this->table_prefix . 'notification_types'	=> array( +					'COLUMNS'			=> array( +						'notification_type_id'		=> array('USINT', NULL, 'auto_increment'), +						'notification_type_name'	=> array('VCHAR:255', ''), +						'notification_type_enabled'	=> array('BOOL', 1), +					), +					'PRIMARY_KEY'		=> array('notification_type_id'), +					'KEYS'				=> array( +						'type'			=> array('UNIQUE', array('notification_type_name')), +					), +				), +				$this->table_prefix . 'notifications'		=> array( +					'COLUMNS'			=> array( +						'notification_id'				=> array('UINT:10', NULL, 'auto_increment'), +						'notification_type_id'			=> array('USINT', 0), +						'item_id'						=> array('UINT', 0), +						'item_parent_id'				=> array('UINT', 0), +						'user_id'						=> array('UINT', 0), +						'notification_read'				=> array('BOOL', 0), +						'notification_time'				=> array('TIMESTAMP', 1), +						'notification_data'				=> array('TEXT_UNI', ''), +					), +					'PRIMARY_KEY'		=> 'notification_id', +					'KEYS'				=> array( +						'item_ident'		=> array('INDEX', array('notification_type_id', 'item_id')), +						'user'				=> array('INDEX', array('user_id', 'notification_read')), +					), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_tables'	=> array( +				$this->table_prefix . 'notification_types', +				$this->table_prefix . 'notifications', +			), +			'add_tables'		=> array( +				$this->table_prefix . 'notification_types'	=> array( +					'COLUMNS'			=> array( +						'notification_type'			=> array('VCHAR:255', ''), +						'notification_type_enabled'	=> array('BOOL', 1), +					), +					'PRIMARY_KEY'		=> array('notification_type', 'notification_type_enabled'), +				), +				$this->table_prefix . 'notifications'		=> array( +					'COLUMNS'			=> array( +						'notification_id'  				=> array('UINT', NULL, 'auto_increment'), +						'item_type'			   			=> array('VCHAR:255', ''), +						'item_id'		  				=> array('UINT', 0), +						'item_parent_id'   				=> array('UINT', 0), +						'user_id'						=> array('UINT', 0), +						'notification_read'				=> array('BOOL', 0), +						'notification_time'				=> array('TIMESTAMP', 1), +						'notification_data'			   	=> array('TEXT_UNI', ''), +					), +					'PRIMARY_KEY'		=> 'notification_id', +					'KEYS'				=> array( +						'item_ident'		=> array('INDEX', array('item_type', 'item_id')), +						'user'				=> array('INDEX', array('user_id', 'notification_read')), +					), +				), +			), +		); +	} +} diff --git a/phpBB/includes/db/migration/data/310/signature_module_auth.php b/phpBB/includes/db/migration/data/310/signature_module_auth.php new file mode 100644 index 0000000000..e4fbb27bcb --- /dev/null +++ b/phpBB/includes/db/migration/data/310/signature_module_auth.php @@ -0,0 +1,51 @@ +<?php +/** +* +* @package migration +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License v2 +* +*/ + +class phpbb_db_migration_data_310_signature_module_auth extends phpbb_db_migration +{ +	public function effectively_installed() +	{ +		$sql = 'SELECT module_auth +			FROM ' . MODULES_TABLE . " +			WHERE module_class = 'ucp' +				AND module_basename = 'ucp_profile' +				AND module_mode = 'signature'"; +		$result = $this->db->sql_query($sql); +		$module_auth = $this->db_sql_fetchfield('module_auth'); +		$this->db->sql_freeresult($result); + +		return $module_auth === 'acl_u_sig' || $module_auth === false; +	} + +	static public function depends_on() +	{ +		return array('phpbb_db_migration_data_31x_dev'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array( +					array($this, 'update_signature_module_auth'), +				), +			), +		); +	} + +	public function update_signature_module_auth() +	{ +		$sql = 'UPDATE ' . MODULES_TABLE . " +			SET module_auth = 'acl_u_sig' +			WHERE module_class = 'ucp' +				AND module_basename = 'ucp_profile' +				AND module_mode = 'signature' +				AND module_auth = ''"; +		$this->db->sql_query($sql); +	} +} 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/migration/tool/module.php b/phpBB/includes/db/migration/tool/module.php index ec683d36af..ac4d2c9bd7 100644 --- a/phpBB/includes/db/migration/tool/module.php +++ b/phpBB/includes/db/migration/tool/module.php @@ -209,9 +209,6 @@ class phpbb_db_migration_tool_module implements phpbb_db_migration_tool_interfac  		}  		// The "manual" way -		$module_log_name = ((isset($this->user->lang[$data['module_langname']])) ? $this->user->lang[$data['module_langname']] : $data['module_langname']); -		add_log('admin', 'LOG_MODULE_ADD', $module_log_name); -  		if (!is_numeric($parent))  		{  			$sql = 'SELECT module_id @@ -267,6 +264,8 @@ class phpbb_db_migration_tool_module implements phpbb_db_migration_tool_interfac  		else  		{  			// Success +			$module_log_name = ((isset($this->user->lang[$data['module_langname']])) ? $this->user->lang[$data['module_langname']] : $data['module_langname']); +			add_log('admin', 'LOG_MODULE_ADD', $module_log_name);  			// Move the module if requested above/below an existing one  			if (isset($data['before']) && $data['before']) diff --git a/phpBB/includes/db/migrator.php b/phpBB/includes/db/migrator.php index 9d7cede95b..ca3ffc8043 100644 --- a/phpBB/includes/db/migrator.php +++ b/phpBB/includes/db/migrator.php @@ -362,6 +362,12 @@ class phpbb_db_migrator  	{  		$state = ($state) ? unserialize($state) : false; +		// reverse order of steps if reverting +		if ($revert === true) +		{ +			$steps = array_reverse($steps); +		} +  		foreach ($steps as $step_identifier => $step)  		{  			$last_result = false; 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/base.php b/phpBB/includes/extension/base.php index d51589d719..c4462b64d8 100644 --- a/phpBB/includes/extension/base.php +++ b/phpBB/includes/extension/base.php @@ -27,25 +27,51 @@ class phpbb_extension_base implements phpbb_extension_interface  	/** @var ContainerInterface */  	protected $container; +	/** @var phpbb_extension_finder */ +	protected $finder; + +	/** @var phpbb_db_migrator */ +	protected $migrator; + +	/** @var string */ +	protected $extension_name; + +	/** @var string */ +	protected $extension_path; +  	/**  	* Constructor  	*  	* @param ContainerInterface $container Container object +	* @param phpbb_extension_finder $extension_finder +	* @param string $extension_name Name of this extension (from ext.manager) +	* @param string $extension_path Relative path to this extension  	*/ -	public function __construct(ContainerInterface $container) +	public function __construct(ContainerInterface $container, phpbb_extension_finder $extension_finder, phpbb_db_migrator $migrator, $extension_name, $extension_path)  	{  		$this->container = $container; +		$this->extension_finder = $extension_finder; +		$this->migrator = $migrator; + +		$this->extension_name = $extension_name; +		$this->extension_path = $extension_path;  	}  	/** -	* Single enable step that does nothing +	* Single enable step that installs any included migrations  	*  	* @param mixed $old_state State returned by previous call of this method  	* @return false Indicates no further steps are required  	*/  	public function enable_step($old_state)  	{ -		return false; +		$migrations = $this->get_migration_file_list(); + +		$this->migrator->set_migrations($migrations); + +		$this->migrator->update(); + +		return !$this->migrator->finished();  	}  	/** @@ -60,13 +86,50 @@ class phpbb_extension_base implements phpbb_extension_interface  	}  	/** -	* Single purge step that does nothing +	* Single purge step that reverts any included and installed migrations  	*  	* @param mixed $old_state State returned by previous call of this method  	* @return false Indicates no further steps are required  	*/  	public function purge_step($old_state)  	{ +		$migrations = $this->get_migration_file_list(); + +		$this->migrator->set_migrations($migrations); + +		foreach ($migrations as $migration) +		{ +			while ($this->migrator->migration_state($migration) !== false) +			{ +				$this->migrator->revert($migration); + +				return true; +			} +		} +  		return false;  	} + +	/** +	* Get the list of migration files from this extension +	* +	* @return array +	*/ +	protected function get_migration_file_list() +	{ +		static $migrations = false; + +		if ($migrations !== false) +		{ +			return $migrations; +		} + +		// Only have the finder search in this extension path directory +		$migrations = $this->extension_finder +			->extension_directory('/migrations') +			->find_from_extension($this->extension_name, $this->extension_path); +		$migrations = $this->extension_finder->get_classes_from_files($migrations); + +		return $migrations; +	}  } diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php index f71e32bc8d..49bb2a514f 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;  	} @@ -374,6 +377,34 @@ class phpbb_extension_finder  		return $files;  	} +	 +	/** +	* Finds all file system entries matching the configured options for one +	* specific extension +	* +	* @param string $extension_name Name of the extension +	* @param string $extension_path Relative path to the extension root directory +	* @param bool $cache Whether the result should be cached +	* @param bool $is_dir Directories will be returned when true, only files +	*                     otherwise +	* @return array An array of paths to found items +	*/ +	public function find_from_extension($extension_name, $extension_path, $cache = true, $is_dir = false) +	{ +		$extensions = array( +			$extension_name => $extension_path, +		); + +		$files = array(); +		$file_list = $this->find_from_paths($extensions, $cache, $is_dir); + +		foreach ($file_list as $file) +		{ +			$files[$file['named_path']] = $file['ext_name']; +		} + +		return $files; +	}  	/**  	* Finds all file system entries matching the configured options from diff --git a/phpBB/includes/extension/manager.php b/phpBB/includes/extension/manager.php index 44a30c6280..4451049d04 100644 --- a/phpBB/includes/extension/manager.php +++ b/phpBB/includes/extension/manager.php @@ -29,7 +29,6 @@ class phpbb_extension_manager  	protected $db;  	protected $config; -	protected $migrator;  	protected $cache;  	protected $php_ext;  	protected $extensions; @@ -43,21 +42,21 @@ class phpbb_extension_manager  	* @param ContainerInterface $container A container  	* @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_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;  		$this->db = $db;  		$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; @@ -134,13 +133,15 @@ class phpbb_extension_manager  	{  		$extension_class_name = 'phpbb_ext_' . str_replace('/', '_', $name) . '_ext'; +		$migrator = $this->container->get('migrator'); +  		if (class_exists($extension_class_name))  		{ -			return new $extension_class_name($this->container); +			return new $extension_class_name($this->container, $this->get_finder(), $migrator, $name, $this->get_extension_path($name, true));  		}  		else  		{ -			return new phpbb_extension_base($this->container); +			return new phpbb_extension_base($this->container, $this->get_finder(), $migrator, $name, $this->get_extension_path($name, true));  		}  	} @@ -153,7 +154,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);  	}  	/** @@ -176,12 +177,6 @@ class phpbb_extension_manager  		$old_state = (isset($this->extensions[$name]['ext_state'])) ? unserialize($this->extensions[$name]['ext_state']) : false; -		// Returns false if not completed -		if (!$this->handle_migrations($name, 'enable')) -		{ -			return true; -		} -  		$extension = $this->get_extension($name);  		$state = $extension->enable_step($old_state); @@ -197,12 +192,21 @@ class phpbb_extension_manager  		$this->extensions[$name]['ext_path'] = $this->get_extension_path($extension_data['ext_name']);  		ksort($this->extensions); -		$sql = 'UPDATE ' . $this->extension_table . ' -			SET ' . $this->db->sql_build_array('UPDATE', $extension_data) . " +		$sql = 'SELECT COUNT(ext_name) as row_count +			FROM ' . $this->extension_table . "  			WHERE ext_name = '" . $this->db->sql_escape($name) . "'"; -		$this->db->sql_query($sql); +		$result = $this->db->sql_query($sql); +		$count = $this->db->sql_fetchfield('row_count'); +		$this->db->sql_freeresult($result); -		if (!$this->db->sql_affectedrows()) +		if ($count) +		{ +			$sql = 'UPDATE ' . $this->extension_table . ' +				SET ' . $this->db->sql_build_array('UPDATE', $extension_data) . " +				WHERE ext_name = '" . $this->db->sql_escape($name) . "'"; +			$this->db->sql_query($sql); +		} +		else  		{  			$sql = 'INSERT INTO ' . $this->extension_table . '  				' . $this->db->sql_build_array('INSERT', $extension_data); @@ -333,12 +337,6 @@ class phpbb_extension_manager  		$old_state = unserialize($this->extensions[$name]['ext_state']); -		// Returns false if not completed -		if (!$this->handle_migrations($name, 'purge')) -		{ -			return true; -		} -  		$extension = $this->get_extension($name);  		$state = $extension->purge_step($old_state); @@ -410,7 +408,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,74 +508,6 @@ 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'); -	} - -	/** -	* Handle installing/reverting migrations -	* -	* @param string $extension_name Name of the extension -	* @param string $mode enable or purge -	* @return bool True if completed, False if not completed -	*/ -	protected function handle_migrations($extension_name, $mode) -	{ -		$extensions = array( -			$extension_name => $this->phpbb_root_path . $this->get_extension_path($extension_name), -		); - -		$finder = $this->get_finder(); -		$migrations = array(); -		$file_list = $finder -			->extension_directory('/migrations') -			->find_from_paths($extensions); - -		if (empty($file_list)) -		{ -			return true; -		} - -		foreach ($file_list as $file) -		{ -			$migrations[$file['named_path']] = $file['ext_name']; -		} -		$migrations = $finder->get_classes_from_files($migrations); -		$this->migrator->set_migrations($migrations); - -		// What is a safe limit of execution time? Half the max execution time should be safe. -		$safe_time_limit = (ini_get('max_execution_time') / 2); -		$start_time = time(); - -		if ($mode == 'enable') -		{ -			while (!$this->migrator->finished()) -			{ -				$this->migrator->update(); - -				// Are we approaching the time limit? If so we want to pause the update and continue after refreshing -				if ((time() - $start_time) >= $safe_time_limit) -				{ -					return false; -				} -			} -		} -		else if ($mode == 'purge') -		{ -			foreach ($migrations as $migration) -			{ -				while ($this->migrator->migration_state($migration) !== false) -				{ -					$this->migrator->revert($migration); - -					// Are we approaching the time limit? If so we want to pause the update and continue after refreshing -					if ((time() - $start_time) >= $safe_time_limit) -					{ -						return false; -					} -				} -			} -		} - -		return true; +		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/feed/base.php b/phpBB/includes/feed/base.php new file mode 100644 index 0000000000..af28ee8dc8 --- /dev/null +++ b/phpBB/includes/feed/base.php @@ -0,0 +1,259 @@ +<?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; +} + +/** +* Base class with some generic functions and settings. +* +* @package phpBB3 +*/ +abstract class phpbb_feed_base +{ +	/** +	* Feed helper object +	* @var phpbb_feed_helper +	*/ +	protected $helper; + +	/** @var phpbb_config */ +	protected $config; + +	/** @var phpbb_db_driver */ +	protected $db; + +	/** @var phpbb_cache_driver_interface */ +	protected $cache; + +	/** @var phpbb_user */ +	protected $user; + +	/** @var phpbb_auth */ +	protected $auth; + +	/** @var string */ +	protected $phpEx; + +	/** +	* SQL Query to be executed to get feed items +	*/ +	var $sql = array(); + +	/** +	* Keys specified for retrieval of title, content, etc. +	*/ +	var $keys = array(); + +	/** +	* Number of items to fetch. Usually overwritten by $config['feed_something'] +	*/ +	var $num_items = 15; + +	/** +	* Separator for title elements to separate items (for example forum / topic) +	*/ +	var $separator = "\xE2\x80\xA2"; // • + +	/** +	* Separator for the statistics row (Posted by, post date, replies, etc.) +	*/ +	var $separator_stats = "\xE2\x80\x94"; // — + +	/** +	* Constructor +	* +	* @param phpbb_feed_helper $helper Feed helper +	* @param phpbb_config		$config		Config object +	* @param phpbb_db_driver	$db			Database connection +	* @param phpbb_cache_driver_interface	$cache			Cache object +	* @param phpbb_user			$user		User object +	* @param phpbb_auth			$auth		Auth object +	* @param string				$phpEx		php file extension +	* @return	null +	*/ +	function __construct(phpbb_feed_helper $helper, phpbb_config $config, phpbb_db_driver $db, phpbb_cache_driver_interface $cache, phpbb_user $user, phpbb_auth $auth, $phpEx) +	{ +		$this->config = $config; +		$this->helper = $helper; +		$this->db = $db; +		$this->cache = $cache; +		$this->user = $user; +		$this->auth = $auth; +		$this->phpEx = $phpEx; + +		$this->set_keys(); + +		// Allow num_items to be string +		if (is_string($this->num_items)) +		{ +			$this->num_items = (int) $this->config[$this->num_items]; + +			// A precaution +			if (!$this->num_items) +			{ +				$this->num_items = 10; +			} +		} +	} + +	/** +	* Set keys. +	*/ +	function set_keys() +	{ +	} + +	/** +	* Open feed +	*/ +	function open() +	{ +	} + +	/** +	* Close feed +	*/ +	function close() +	{ +		if (!empty($this->result)) +		{ +			$this->db->sql_freeresult($this->result); +		} +	} + +	/** +	* Set key +	*/ +	function set($key, $value) +	{ +		$this->keys[$key] = $value; +	} + +	/** +	* Get key +	*/ +	function get($key) +	{ +		return (isset($this->keys[$key])) ? $this->keys[$key] : NULL; +	} + +	function get_readable_forums() +	{ +		static $forum_ids; + +		if (!isset($forum_ids)) +		{ +			$forum_ids = array_keys($this->auth->acl_getf('f_read', true)); +		} + +		return $forum_ids; +	} + +	function get_moderator_approve_forums() +	{ +		static $forum_ids; + +		if (!isset($forum_ids)) +		{ +			$forum_ids = array_keys($this->auth->acl_getf('m_approve', true)); +		} + +		return $forum_ids; +	} + +	function is_moderator_approve_forum($forum_id) +	{ +		static $forum_ids; + +		if (!isset($forum_ids)) +		{ +			$forum_ids = array_flip($this->get_moderator_approve_forums()); +		} + +		return (isset($forum_ids[$forum_id])) ? true : false; +	} + +	function get_excluded_forums() +	{ +		static $forum_ids; + +		// Matches acp/acp_board.php +		$cache_name	= 'feed_excluded_forum_ids'; + +		if (!isset($forum_ids) && ($forum_ids = $this->cache->get('_' . $cache_name)) === false) +		{ +			$sql = 'SELECT forum_id +				FROM ' . FORUMS_TABLE . ' +				WHERE ' . $this->db->sql_bit_and('forum_options', FORUM_OPTION_FEED_EXCLUDE, '<> 0'); +			$result = $this->db->sql_query($sql); + +			$forum_ids = array(); +			while ($forum_id = (int) $this->db->sql_fetchfield('forum_id')) +			{ +				$forum_ids[$forum_id] = $forum_id; +			} +			$this->db->sql_freeresult($result); + +			$this->cache->put('_' . $cache_name, $forum_ids); +		} + +		return $forum_ids; +	} + +	function is_excluded_forum($forum_id) +	{ +		$forum_ids = $this->get_excluded_forums(); + +		return isset($forum_ids[$forum_id]) ? true : false; +	} + +	function get_passworded_forums() +	{ +		return $this->user->get_passworded_forums(); +	} + +	function get_item() +	{ +		static $result; + +		if (!isset($result)) +		{ +			if (!$this->get_sql()) +			{ +				return false; +			} + +			// Query database +			$sql = $this->db->sql_build_query('SELECT', $this->sql); +			$result = $this->db->sql_query_limit($sql, $this->num_items); +		} + +		return $this->db->sql_fetchrow($result); +	} + +	function user_viewprofile($row) +	{ +		$author_id = (int) $row[$this->get('author_id')]; + +		if ($author_id == ANONYMOUS) +		{ +			// Since we cannot link to a profile, we just return GUEST +			// instead of $row['username'] +			return $this->user->lang['GUEST']; +		} + +		return '<a href="' . $this->helper->append_sid('memberlist.' . $this->phpEx, 'mode=viewprofile&u=' . $author_id) . '">' . $row[$this->get('creator')] . '</a>'; +	} +} diff --git a/phpBB/includes/feed/factory.php b/phpBB/includes/feed/factory.php new file mode 100644 index 0000000000..63a1eb8ef0 --- /dev/null +++ b/phpBB/includes/feed/factory.php @@ -0,0 +1,129 @@ +<?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; +} + +/** +* Factory class to return correct object +* @package phpBB3 +*/ +class phpbb_feed_factory +{ +	/** +	* Service container object +	* @var object +	*/ +	protected $container; + +	/** @var phpbb_config */ +	protected $config; + +	/** @var phpbb_db_driver */ +	protected $db; + +	/** +	* Constructor +	* +	* @param objec				$container	Container object +	* @param phpbb_config		$config		Config object +	* @param phpbb_db_driver	$db			Database connection +	* @return	null +	*/ +	public function __construct($container, phpbb_config $config, phpbb_db_driver $db) +	{ +		$this->container = $container; +		$this->config = $config; +		$this->db = $db; +	} + +	/** +	* Return correct object for specified mode +	* +	* @param string	$mode		The feeds mode. +	* @param int	$forum_id	Forum id specified by the script if forum feed provided. +	* @param int	$topic_id	Topic id specified by the script if topic feed provided. +	* +	* @return object	Returns correct feeds object for specified mode. +	*/ +	function get_feed($mode, $forum_id, $topic_id) +	{ +		switch ($mode) +		{ +			case 'forums': +				if (!$this->config['feed_overall_forums']) +				{ +					return false; +				} + +				return $this->container->get('feed.forums'); +			break; + +			case 'topics': +			case 'topics_new': +				if (!$this->config['feed_topics_new']) +				{ +					return false; +				} + +				return $this->container->get('feed.topics'); +			break; + +			case 'topics_active': +				if (!$this->config['feed_topics_active']) +				{ +					return false; +				} + +				return $this->container->get('feed.topics_active'); +			break; + +			case 'news': +				// Get at least one news forum +				$sql = 'SELECT forum_id +					FROM ' . FORUMS_TABLE . ' +					WHERE ' . $this->db->sql_bit_and('forum_options', FORUM_OPTION_FEED_NEWS, '<> 0'); +				$result = $this->db->sql_query_limit($sql, 1, 0, 600); +				$s_feed_news = (int) $this->db->sql_fetchfield('forum_id'); +				$this->db->sql_freeresult($result); + +				if (!$s_feed_news) +				{ +					return false; +				} + +				return $this->container->get('feed.news'); +			break; + +			default: +				if ($topic_id && $this->config['feed_topic']) +				{ +					return $this->container->get('feed.topic') +								->set_topic_id($topic_id); +				} +				else if ($forum_id && $this->config['feed_forum']) +				{ +					return $this->container->get('feed.forum') +								->set_forum_id($forum_id); +				} +				else if ($this->config['feed_overall']) +				{ +				return $this->container->get('feed.overall'); +				} + +				return false; +			break; +		} +	} +} diff --git a/phpBB/includes/feed/forum.php b/phpBB/includes/feed/forum.php new file mode 100644 index 0000000000..83b836b81d --- /dev/null +++ b/phpBB/includes/feed/forum.php @@ -0,0 +1,149 @@ +<?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; +} + +/** +* Forum feed +* +* This will give you the last {$this->num_items} posts made +* within a specific forum. +* +* @package phpBB3 +*/ +class phpbb_feed_forum extends phpbb_feed_post_base +{ +	var $forum_id		= 0; +	var $forum_data		= array(); + +	/** +	* Set the Forum ID +	* +	* @param int	$forum_id			Forum ID +	* @return	phpbb_feed_forum +	*/ +	public function set_forum_id($topic_id) +	{ +		$this->forum_id = (int) $forum_id; + +		return $this; +	} + +	function open() +	{ +		// Check if forum exists +		$sql = 'SELECT forum_id, forum_name, forum_password, forum_type, forum_options +			FROM ' . FORUMS_TABLE . ' +			WHERE forum_id = ' . $this->forum_id; +		$result = $this->db->sql_query($sql); +		$this->forum_data = $this->db->sql_fetchrow($result); +		$this->db->sql_freeresult($result); + +		if (empty($this->forum_data)) +		{ +			trigger_error('NO_FORUM'); +		} + +		// Forum needs to be postable +		if ($this->forum_data['forum_type'] != FORUM_POST) +		{ +			trigger_error('NO_FEED'); +		} + +		// Make sure forum is not excluded from feed +		if (phpbb_optionget(FORUM_OPTION_FEED_EXCLUDE, $this->forum_data['forum_options'])) +		{ +			trigger_error('NO_FEED'); +		} + +		// Make sure we can read this forum +		if (!$this->auth->acl_get('f_read', $this->forum_id)) +		{ +			trigger_error('SORRY_AUTH_READ'); +		} + +		// Make sure forum is not passworded or user is authed +		if ($this->forum_data['forum_password']) +		{ +			$forum_ids_passworded = $this->get_passworded_forums(); + +			if (isset($forum_ids_passworded[$this->forum_id])) +			{ +				trigger_error('SORRY_AUTH_READ'); +			} + +			unset($forum_ids_passworded); +		} +	} + +	function get_sql() +	{ +		$sql_visibility = phpbb_content_visibility::get_visibility_sql('topic', $this->forum_id); + +		// Determine topics with recent activity +		$sql = 'SELECT topic_id, topic_last_post_time +			FROM ' . TOPICS_TABLE . ' +			WHERE forum_id = ' . $this->forum_id . ' +				AND topic_moved_id = 0 +				' . (($sql_visibility) ? ' AND ' . $sql_visibility : '') . ' +			ORDER BY topic_last_post_time DESC'; +		$result = $this->db->sql_query_limit($sql, $this->num_items); + +		$topic_ids = array(); +		$min_post_time = 0; +		while ($row = $this->db->sql_fetchrow()) +		{ +			$topic_ids[] = (int) $row['topic_id']; + +			$min_post_time = (int) $row['topic_last_post_time']; +		} +		$this->db->sql_freeresult($result); + +		if (empty($topic_ids)) +		{ +			return false; +		} + +		$sql_visibility = phpbb_content_visibility::get_visibility_sql('post', $this->forum_id, 'p.');  + +		$this->sql = array( +			'SELECT'	=>	'p.post_id, p.topic_id, p.post_time, p.post_edit_time, p.post_visibility, p.post_subject, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, ' . +							'u.username, u.user_id', +			'FROM'		=> array( +				POSTS_TABLE		=> 'p', +				USERS_TABLE		=> 'u', +			), +			'WHERE'		=> $this->db->sql_in_set('p.topic_id', $topic_ids) . ' +							' . (($sql_visibility) ? ' AND ' . $sql_visibility : '') . ' +							AND p.post_time >= ' . $min_post_time . ' +							AND p.poster_id = u.user_id', +			'ORDER_BY'	=> 'p.post_time DESC', +		); + +		return true; +	} + +	function adjust_item(&$item_row, &$row) +	{ +		parent::adjust_item($item_row, $row); + +		$item_row['title'] = (isset($row['forum_name']) && $row['forum_name'] !== '') ? $row['forum_name'] . ' ' . $this->separator . ' ' . $item_row['title'] : $item_row['title']; +	} + +	function get_item() +	{ +		return ($row = parent::get_item()) ? array_merge($this->forum_data, $row) : $row; +	} +} diff --git a/phpBB/includes/feed/forums.php b/phpBB/includes/feed/forums.php new file mode 100644 index 0000000000..409097a9f3 --- /dev/null +++ b/phpBB/includes/feed/forums.php @@ -0,0 +1,72 @@ +<?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; +} + +/** +* 'All Forums' feed +* +* This will give you a list of all postable forums where feeds are enabled +* including forum description, topic stats and post stats +* +* @package phpBB3 +*/ +class phpbb_feed_forums extends phpbb_feed_base +{ +	var $num_items	= 0; + +	function set_keys() +	{ +		$this->set('title',		'forum_name'); +		$this->set('text',		'forum_desc'); +		$this->set('bitfield',	'forum_desc_bitfield'); +		$this->set('bbcode_uid','forum_desc_uid'); +		$this->set('updated',	'forum_last_post_time'); +		$this->set('options',	'forum_desc_options'); +	} + +	function get_sql() +	{ +		$in_fid_ary = array_diff($this->get_readable_forums(), $this->get_excluded_forums()); +		if (empty($in_fid_ary)) +		{ +			return false; +		} + +		// Build SQL Query +		$this->sql = array( +			'SELECT'	=> 'f.forum_id, f.left_id, f.forum_name, f.forum_last_post_time, +							f.forum_desc, f.forum_desc_bitfield, f.forum_desc_uid, f.forum_desc_options, +							f.forum_topics_approved, f.forum_posts_approved', +			'FROM'		=> array(FORUMS_TABLE => 'f'), +			'WHERE'		=> 'f.forum_type = ' . FORUM_POST . ' +							AND ' . $this->db->sql_in_set('f.forum_id', $in_fid_ary), +			'ORDER_BY'	=> 'f.left_id ASC', +		); + +		return true; +	} + +	function adjust_item(&$item_row, &$row) +	{ +		$item_row['link'] = $this->helper->append_sid('viewforum.' . $this->phpEx, 'f=' . $row['forum_id']); + +		if ($this->config['feed_item_statistics']) +		{ +			$item_row['statistics'] = $this->user->lang('TOTAL_TOPICS', (int) $row['forum_topics_approved']) +				. ' ' . $this->separator_stats . ' ' . $this->user->lang('TOTAL_POSTS_COUNT', (int) $row['forum_posts_approved']); +		} +	} +} diff --git a/phpBB/includes/feed/helper.php b/phpBB/includes/feed/helper.php new file mode 100644 index 0000000000..93330aa2ad --- /dev/null +++ b/phpBB/includes/feed/helper.php @@ -0,0 +1,159 @@ +<?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; +} + +/** +* Class with some helpful functions used in feeds +* @package phpBB3 +*/ +class phpbb_feed_helper +{ +	/** @var phpbb_config */ +	protected $config; + +	/** @var phpbb_user */ +	protected $user; + +	/** @var string */ +	protected $phpbb_root_path; + +	/** +	* Constructor +	* +	* @param	phpbb_config	$config		Config object +	* @param	phpbb_user		$user		User object +	* @param	string	$phpbb_root_path	Root path +	* @return	null +	*/ +	public function __construct(phpbb_config $config, phpbb_user $user, $phpbb_root_path) +	{ +		$this->config = $config; +		$this->user = $user; +		$this->phpbb_root_path = $phpbb_root_path; +	} + +	/** +	* Run links through append_sid(), prepend generate_board_url() and remove session id +	*/ +	public function get_board_url() +	{ +		static $board_url; + +		if (empty($board_url)) +		{ +			$board_url = generate_board_url(); +		} + +		return $board_url; +	} + +	/** +	* Run links through append_sid(), prepend generate_board_url() and remove session id +	*/ +	public function append_sid($url, $params) +	{ +		return append_sid($this->get_board_url() . '/' . $url, $params, true, ''); +	} + +	/** +	* Generate ISO 8601 date string (RFC 3339) +	*/ +	public function format_date($time) +	{ +		static $zone_offset; +		static $offset_string; + +		if (empty($offset_string)) +		{ +			$zone_offset = $this->user->create_datetime()->getOffset(); +			$offset_string = phpbb_format_timezone_offset($zone_offset); +		} + +		return gmdate("Y-m-d\TH:i:s", $time + $zone_offset) . $offset_string; +	} + +	/** +	* Generate text content +	*/ +	public function generate_content($content, $uid, $bitfield, $options) +	{ +		if (empty($content)) +		{ +			return ''; +		} + +		// Prepare some bbcodes for better parsing +		$content	= preg_replace("#\[quote(=".*?")?:$uid\]\s*(.*?)\s*\[/quote:$uid\]#si", "[quote$1:$uid]<br />$2<br />[/quote:$uid]", $content); + +		$content = generate_text_for_display($content, $uid, $bitfield, $options); + +		// Add newlines +		$content = str_replace('<br />', '<br />' . "\n", $content); + +		// Convert smiley Relative paths to Absolute path, Windows style +		$content = str_replace($this->phpbb_root_path . $this->config['smilies_path'], $this->get_board_url() . '/' . $this->config['smilies_path'], $content); + +		// Remove "Select all" link and mouse events +		$content = str_replace('<a href="#" onclick="selectCode(this); return false;">' . $this->user->lang['SELECT_ALL_CODE'] . '</a>', '', $content); +		$content = preg_replace('#(onkeypress|onclick)="(.*?)"#si', '', $content); + +		// Firefox does not support CSS for feeds, though + +		// Remove font sizes +	//	$content = preg_replace('#<span style="font-size: [0-9]+%; line-height: [0-9]+%;">([^>]+)</span>#iU', '\1', $content); + +		// Make text strong :P +	//	$content = preg_replace('#<span style="font-weight: bold?">(.*?)</span>#iU', '<strong>\1</strong>', $content); + +		// Italic +	//	$content = preg_replace('#<span style="font-style: italic?">([^<]+)</span>#iU', '<em>\1</em>', $content); + +		// Underline +	//	$content = preg_replace('#<span style="text-decoration: underline?">([^<]+)</span>#iU', '<u>\1</u>', $content); + +		// Remove embed Windows Media Streams +		$content	= preg_replace( '#<\!--\[if \!IE\]>-->([^[]+)<\!--<!\[endif\]-->#si', '', $content); + +		// Do not use < and >, because we want to retain code contained in [code][/code] + +		// Remove embed and objects +		$content	= preg_replace( '#<(object|embed)(.*?) (value|src)=(.*?) ([^[]+)(object|embed)>#si',' <a href=$4 target="_blank"><strong>$1</strong></a> ',$content); + +		// Remove some specials html tag, because somewhere there are a mod to allow html tags ;) +		$content	= preg_replace( '#<(script|iframe)([^[]+)\1>#siU', ' <strong>$1</strong> ', $content); + +		// Remove Comments from inline attachments [ia] +		$content	= preg_replace('#<div class="(inline-attachment|attachtitle)">(.*?)<!-- ia(.*?) -->(.*?)<!-- ia(.*?) -->(.*?)</div>#si','$4',$content); + +		// Replace some entities with their unicode counterpart +		$entities = array( +			' '	=> "\xC2\xA0", +			'•'	=> "\xE2\x80\xA2", +			'·'	=> "\xC2\xB7", +			'©'	=> "\xC2\xA9", +		); + +		$content = str_replace(array_keys($entities), array_values($entities), $content); + +		// Remove CDATA blocks. ;) +		$content = preg_replace('#\<\!\[CDATA\[(.*?)\]\]\>#s', '', $content); + +		// Other control characters +		$content = preg_replace('#(?:[\x00-\x1F\x7F]+|(?:\xC2[\x80-\x9F])+)#', '', $content); + +		return $content; +	} +} diff --git a/phpBB/includes/feed/news.php b/phpBB/includes/feed/news.php new file mode 100644 index 0000000000..f2d45b5165 --- /dev/null +++ b/phpBB/includes/feed/news.php @@ -0,0 +1,112 @@ +<?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; +} + +/** +* News feed +* +* This will give you {$this->num_items} first posts +* of all topics in the selected news forums. +* +* @package phpBB3 +*/ +class phpbb_feed_news extends phpbb_feed_topic_base +{ +	function get_news_forums() +	{ +		static $forum_ids; + +		// Matches acp/acp_board.php +		$cache_name	= 'feed_news_forum_ids'; + +		if (!isset($forum_ids) && ($forum_ids = $this->cache->get('_' . $cache_name)) === false) +		{ +			$sql = 'SELECT forum_id +				FROM ' . FORUMS_TABLE . ' +				WHERE ' . $this->db->sql_bit_and('forum_options', FORUM_OPTION_FEED_NEWS, '<> 0'); +			$result = $this->db->sql_query($sql); + +			$forum_ids = array(); +			while ($forum_id = (int) $this->db->sql_fetchfield('forum_id')) +			{ +				$forum_ids[$forum_id] = $forum_id; +			} +			$this->db->sql_freeresult($result); + +			$this->cache->put('_' . $cache_name, $forum_ids); +		} + +		return $forum_ids; +	} + +	function get_sql() +	{ +		// Determine forum ids +		$in_fid_ary = array_intersect($this->get_news_forums(), $this->get_readable_forums()); +		if (empty($in_fid_ary)) +		{ +			return false; +		} + +		$in_fid_ary = array_diff($in_fid_ary, $this->get_passworded_forums()); +		if (empty($in_fid_ary)) +		{ +			return false; +		} + +		// We really have to get the post ids first! +		$sql = 'SELECT topic_first_post_id, topic_time +			FROM ' . TOPICS_TABLE . ' +			WHERE ' . $this->db->sql_in_set('forum_id', $in_fid_ary) . ' +				AND topic_moved_id = 0 +				AND topic_visibility = ' . ITEM_APPROVED . ' +			ORDER BY topic_time DESC'; +		$result = $this->db->sql_query_limit($sql, $this->num_items); + +		$post_ids = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$post_ids[] = (int) $row['topic_first_post_id']; +		} +		$this->db->sql_freeresult($result); + +		if (empty($post_ids)) +		{ +			return false; +		} + +		$this->sql = array( +			'SELECT'	=> 'f.forum_id, f.forum_name, +							t.topic_id, t.topic_title, t.topic_poster, t.topic_first_poster_name, t.topic_posts_approved, t.topic_posts_unapproved, t.topic_posts_softdeleted, t.topic_views, t.topic_time, t.topic_last_post_time, +							p.post_id, p.post_time, p.post_edit_time, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url', +			'FROM'		=> array( +				TOPICS_TABLE	=> 't', +				POSTS_TABLE		=> 'p', +			), +			'LEFT_JOIN'	=> array( +				array( +					'FROM'	=> array(FORUMS_TABLE => 'f'), +					'ON'	=> 'p.forum_id = f.forum_id', +				), +			), +			'WHERE'		=> 'p.topic_id = t.topic_id +							AND ' . $this->db->sql_in_set('p.post_id', $post_ids), +			'ORDER_BY'	=> 'p.post_time DESC', +		); + +		return true; +	} +} diff --git a/phpBB/includes/feed/overall.php b/phpBB/includes/feed/overall.php new file mode 100644 index 0000000000..7e48120b97 --- /dev/null +++ b/phpBB/includes/feed/overall.php @@ -0,0 +1,92 @@ +<?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; +} + +/** +* Board wide feed (aka overall feed) +* +* This will give you the newest {$this->num_items} posts +* from the whole board. +* +* @package phpBB3 +*/ +class phpbb_feed_overall extends phpbb_feed_post_base +{ +	function get_sql() +	{ +		$forum_ids = array_diff($this->get_readable_forums(), $this->get_excluded_forums(), $this->get_passworded_forums()); +		if (empty($forum_ids)) +		{ +			return false; +		} + +		// Determine topics with recent activity +		$sql = 'SELECT topic_id, topic_last_post_time +			FROM ' . TOPICS_TABLE . ' +			WHERE topic_moved_id = 0 +				AND ' . phpbb_content_visibility::get_forums_visibility_sql('topic', $forum_ids) . ' +			ORDER BY topic_last_post_time DESC'; +		$result = $this->db->sql_query_limit($sql, $this->num_items); + +		$topic_ids = array(); +		$min_post_time = 0; +		while ($row = $this->db->sql_fetchrow()) +		{ +			$topic_ids[] = (int) $row['topic_id']; + +			$min_post_time = (int) $row['topic_last_post_time']; +		} +		$this->db->sql_freeresult($result); + +		if (empty($topic_ids)) +		{ +			return false; +		} + +		$sql_visibility = phpbb_content_visibility::get_visibility_sql('post', array(), 'p.'); + +		// Get the actual data +		$this->sql = array( +			'SELECT'	=>	'f.forum_id, f.forum_name, ' . +							'p.post_id, p.topic_id, p.post_time, p.post_edit_time, p.post_visibility, p.post_subject, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, ' . +							'u.username, u.user_id', +			'FROM'		=> array( +				USERS_TABLE		=> 'u', +				POSTS_TABLE		=> 'p', +			), +			'LEFT_JOIN'	=> array( +				array( +					'FROM'	=> array(FORUMS_TABLE	=> 'f'), +					'ON'	=> 'f.forum_id = p.forum_id', +				), +			), +			'WHERE'		=> $this->db->sql_in_set('p.topic_id', $topic_ids) . ' +							' . (($sql_visibility) ? ' AND ' . $sql_visibility : '') . ' +							AND p.post_time >= ' . $min_post_time . ' +							AND u.user_id = p.poster_id', +			'ORDER_BY'	=> 'p.post_time DESC', +		); + +		return true; +	} + +	function adjust_item(&$item_row, &$row) +	{ +		parent::adjust_item($item_row, $row); + +		$item_row['title'] = (isset($row['forum_name']) && $row['forum_name'] !== '') ? $row['forum_name'] . ' ' . $this->separator . ' ' . $item_row['title'] : $item_row['title']; +	} +} diff --git a/phpBB/includes/feed/post_base.php b/phpBB/includes/feed/post_base.php new file mode 100644 index 0000000000..1f4cb4b5ef --- /dev/null +++ b/phpBB/includes/feed/post_base.php @@ -0,0 +1,57 @@ +<?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; +} + +/** +* Abstract class for post based feeds +* +* @package phpBB3 +*/ +abstract class phpbb_feed_post_base extends phpbb_feed_base +{ +	var $num_items = 'feed_limit_post'; + +	function set_keys() +	{ +		$this->set('title',		'post_subject'); +		$this->set('title2',	'topic_title'); + +		$this->set('author_id',	'user_id'); +		$this->set('creator',	'username'); +		$this->set('published',	'post_time'); +		$this->set('updated',	'post_edit_time'); +		$this->set('text',		'post_text'); + +		$this->set('bitfield',	'bbcode_bitfield'); +		$this->set('bbcode_uid','bbcode_uid'); + +		$this->set('enable_bbcode',		'enable_bbcode'); +		$this->set('enable_smilies',	'enable_smilies'); +		$this->set('enable_magic_url',	'enable_magic_url'); +	} + +	function adjust_item(&$item_row, &$row) +	{ +		$item_row['link'] = $this->helper->append_sid('viewtopic.' . $this->phpEx, "t={$row['topic_id']}&p={$row['post_id']}#p{$row['post_id']}"); + +		if ($this->config['feed_item_statistics']) +		{ +			$item_row['statistics'] = $this->user->lang['POSTED'] . ' ' . $this->user->lang['POST_BY_AUTHOR'] . ' ' . $this->user_viewprofile($row) +				. ' ' . $this->separator_stats . ' ' . $this->user->format_date($row[$this->get('published')]) +				. (($this->is_moderator_approve_forum($row['forum_id']) && $row['post_visibility'] !== ITEM_APPROVED) ? ' ' . $this->separator_stats . ' ' . $this->user->lang['POST_UNAPPROVED'] : ''); +		} +	} +} diff --git a/phpBB/includes/feed/topic.php b/phpBB/includes/feed/topic.php new file mode 100644 index 0000000000..42bd291343 --- /dev/null +++ b/phpBB/includes/feed/topic.php @@ -0,0 +1,118 @@ +<?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; +} + +/** +* Topic feed for a specific topic +* +* This will give you the last {$this->num_items} posts made within this topic. +* +* @package phpBB3 +*/ +class phpbb_feed_topic extends phpbb_feed_post_base +{ +	var $topic_id		= 0; +	var $forum_id		= 0; +	var $topic_data		= array(); + +	/** +	* Set the Topic ID +	* +	* @param int	$topic_id			Topic ID +	* @return	phpbb_feed_topic +	*/ +	public function set_topic_id($topic_id) +	{ +		$this->topic_id = (int) $topic_id; + +		return $this; +	} + +	function open() +	{ +		$sql = 'SELECT f.forum_options, f.forum_password, t.topic_id, t.forum_id, t.topic_visibility, t.topic_title, t.topic_time, t.topic_views, t.topic_replies, t.topic_type +			FROM ' . TOPICS_TABLE . ' t +			LEFT JOIN ' . FORUMS_TABLE . ' f +				ON (f.forum_id = t.forum_id) +			WHERE t.topic_id = ' . $this->topic_id; +		$result = $this->db->sql_query($sql); +		$this->topic_data = $this->db->sql_fetchrow($result); +		$this->db->sql_freeresult($result); + +		if (empty($this->topic_data)) +		{ +			trigger_error('NO_TOPIC'); +		} + +		$this->forum_id = (int) $this->topic_data['forum_id']; + +		// Make sure topic is either approved or user authed +		if (!$this->topic_data['topic_approved'] && !$this->auth->acl_get('m_approve', $this->forum_id)) +		{ +			trigger_error('SORRY_AUTH_READ'); +		} + +		// Make sure forum is not excluded from feed +		if (phpbb_optionget(FORUM_OPTION_FEED_EXCLUDE, $this->topic_data['forum_options'])) +		{ +			trigger_error('NO_FEED'); +		} + +		// Make sure we can read this forum +		if (!$this->auth->acl_get('f_read', $this->forum_id)) +		{ +			trigger_error('SORRY_AUTH_READ'); +		} + +		// Make sure forum is not passworded or user is authed +		if ($this->topic_data['forum_password']) +		{ +			$forum_ids_passworded = $this->get_passworded_forums(); + +			if (isset($forum_ids_passworded[$this->forum_id])) +			{ +				trigger_error('SORRY_AUTH_READ'); +			} + +			unset($forum_ids_passworded); +		} +	} + +	function get_sql() +	{ +		$sql_visibility = phpbb_content_visibility::get_visibility_sql('post', $this->forum_id, 'p.');  + +		$this->sql = array( +			'SELECT'	=>	'p.post_id, p.post_time, p.post_edit_time, p.post_visibility, p.post_subject, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, ' . +							'u.username, u.user_id', +			'FROM'		=> array( +				POSTS_TABLE		=> 'p', +				USERS_TABLE		=> 'u', +			), +			'WHERE'		=> 'p.topic_id = ' . $this->topic_id . ' +								' . (($sql_visibility) ? ' AND ' . $sql_visibility : '') . ' +								AND p.poster_id = u.user_id', +			'ORDER_BY'	=> 'p.post_time DESC', +		); + +		return true; +	} + +	function get_item() +	{ +		return ($row = parent::get_item()) ? array_merge($this->topic_data, $row) : $row; +	} +} diff --git a/phpBB/includes/feed/topic_base.php b/phpBB/includes/feed/topic_base.php new file mode 100644 index 0000000000..a2f5a56f1d --- /dev/null +++ b/phpBB/includes/feed/topic_base.php @@ -0,0 +1,59 @@ +<?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; +} + +/** +* Abstract class for topic based feeds +* +* @package phpBB3 +*/ +abstract class phpbb_feed_topic_base extends phpbb_feed_base +{ +	var $num_items = 'feed_limit_topic'; + +	function set_keys() +	{ +		$this->set('title',		'topic_title'); +		$this->set('title2',	'forum_name'); + +		$this->set('author_id',	'topic_poster'); +		$this->set('creator',	'topic_first_poster_name'); +		$this->set('published',	'post_time'); +		$this->set('updated',	'post_edit_time'); +		$this->set('text',		'post_text'); + +		$this->set('bitfield',	'bbcode_bitfield'); +		$this->set('bbcode_uid','bbcode_uid'); + +		$this->set('enable_bbcode',		'enable_bbcode'); +		$this->set('enable_smilies',	'enable_smilies'); +		$this->set('enable_magic_url',	'enable_magic_url'); +	} + +	function adjust_item(&$item_row, &$row) +	{ +		$item_row['link'] = $this->helper->append_sid('viewtopic.' . $this->phpEx, 't=' . $row['topic_id'] . '&p=' . $row['post_id'] . '#p' . $row['post_id']); + +		if ($this->config['feed_item_statistics']) +		{ +			$item_row['statistics'] = $this->user->lang['POSTED'] . ' ' . $this->user->lang['POST_BY_AUTHOR'] . ' ' . $this->user_viewprofile($row) +				. ' ' . $this->separator_stats . ' ' . $this->user->format_date($row[$this->get('published')]) +				. ' ' . $this->separator_stats . ' ' . $this->user->lang['REPLIES'] . ' ' . phpbb_content_visibility::get_count('topic_posts', $row, $row['forum_id']) - 1 +				. ' ' . $this->separator_stats . ' ' . $this->user->lang['VIEWS'] . ' ' . $row['topic_views'] +				. (($this->is_moderator_approve_forum($row['forum_id']) && $row['topic_posts_unapproved']) ? ' ' . $this->separator_stats . ' ' . $this->user->lang['POSTS_UNAPPROVED'] : ''); +		} +	} +} diff --git a/phpBB/includes/feed/topics.php b/phpBB/includes/feed/topics.php new file mode 100644 index 0000000000..31f5177773 --- /dev/null +++ b/phpBB/includes/feed/topics.php @@ -0,0 +1,91 @@ +<?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; +} + +/** +* New Topics feed +* +* This will give you the last {$this->num_items} created topics +* including the first post. +* +* @package phpBB3 +*/ +class phpbb_feed_topics extends phpbb_feed_topic_base +{ +	function get_sql() +	{ +		$forum_ids_read = $this->get_readable_forums(); +		if (empty($forum_ids_read)) +		{ +			return false; +		} + +		$in_fid_ary = array_diff($forum_ids_read, $this->get_excluded_forums(), $this->get_passworded_forums()); +		if (empty($in_fid_ary)) +		{ +			return false; +		} + +		// We really have to get the post ids first! +		$sql = 'SELECT topic_first_post_id, topic_time +			FROM ' . TOPICS_TABLE . ' +			WHERE ' . $this->db->sql_in_set('forum_id', $in_fid_ary) . ' +				AND topic_moved_id = 0 +				AND topic_visibility = ' . ITEM_APPROVED . ' +			ORDER BY topic_time DESC'; +		$result = $this->db->sql_query_limit($sql, $this->num_items); + +		$post_ids = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$post_ids[] = (int) $row['topic_first_post_id']; +		} +		$this->db->sql_freeresult($result); + +		if (empty($post_ids)) +		{ +			return false; +		} + +		$this->sql = array( +			'SELECT'	=> 'f.forum_id, f.forum_name, +							t.topic_id, t.topic_title, t.topic_poster, t.topic_first_poster_name, t.topic_posts_approved, t.topic_posts_unapproved, t.topic_posts_softdeleted, t.topic_views, t.topic_time, t.topic_last_post_time, +							p.post_id, p.post_time, p.post_edit_time, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url', +			'FROM'		=> array( +				TOPICS_TABLE	=> 't', +				POSTS_TABLE		=> 'p', +			), +			'LEFT_JOIN'	=> array( +				array( +					'FROM'	=> array(FORUMS_TABLE => 'f'), +					'ON'	=> 'p.forum_id = f.forum_id', +				), +			), +			'WHERE'		=> 'p.topic_id = t.topic_id +							AND ' . $this->db->sql_in_set('p.post_id', $post_ids), +			'ORDER_BY'	=> 'p.post_time DESC', +		); + +		return true; +	} + +	function adjust_item(&$item_row, &$row) +	{ +		parent::adjust_item($item_row, $row); + +		$item_row['title'] = (isset($row['forum_name']) && $row['forum_name'] !== '') ? $row['forum_name'] . ' ' . $this->separator . ' ' . $item_row['title'] : $item_row['title']; +	} +} diff --git a/phpBB/includes/feed/topics_active.php b/phpBB/includes/feed/topics_active.php new file mode 100644 index 0000000000..249dd1d66a --- /dev/null +++ b/phpBB/includes/feed/topics_active.php @@ -0,0 +1,136 @@ +<?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; +} + +/** +* Active Topics feed +* +* This will give you the last {$this->num_items} topics +* with replies made withing the last {$this->sort_days} days +* including the last post. +* +* @package phpBB3 +*/ +class phpbb_feed_topics_active extends phpbb_feed_topic_base +{ +	var $sort_days = 7; + +	function set_keys() +	{ +		parent::set_keys(); + +		$this->set('author_id',	'topic_last_poster_id'); +		$this->set('creator',	'topic_last_poster_name'); +	} + +	function get_sql() +	{ +		$forum_ids_read = $this->get_readable_forums(); +		if (empty($forum_ids_read)) +		{ +			return false; +		} + +		$in_fid_ary = array_intersect($forum_ids_read, $this->get_forum_ids()); +		$in_fid_ary = array_diff($in_fid_ary, $this->get_passworded_forums()); +		if (empty($in_fid_ary)) +		{ +			return false; +		} + +		// Search for topics in last X days +		$last_post_time_sql = ($this->sort_days) ? ' AND topic_last_post_time > ' . (time() - ($this->sort_days * 24 * 3600)) : ''; + +		// We really have to get the post ids first! +		$sql = 'SELECT topic_last_post_id, topic_last_post_time +			FROM ' . TOPICS_TABLE . ' +			WHERE ' . $this->db->sql_in_set('forum_id', $in_fid_ary) . ' +				AND topic_moved_id = 0 +				AND topic_visibility = ' . ITEM_APPROVED . ' +				' . $last_post_time_sql . ' +			ORDER BY topic_last_post_time DESC'; +		$result = $this->db->sql_query_limit($sql, $this->num_items); + +		$post_ids = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$post_ids[] = (int) $row['topic_last_post_id']; +		} +		$this->db->sql_freeresult($result); + +		if (empty($post_ids)) +		{ +			return false; +		} + +		$this->sql = array( +			'SELECT'	=> 'f.forum_id, f.forum_name, +							t.topic_id, t.topic_title, t.topic_posts_approved, t.topic_posts_unapproved, t.topic_posts_softdeleted, t.topic_views, +							t.topic_last_poster_id, t.topic_last_poster_name, t.topic_last_post_time, +							p.post_id, p.post_time, p.post_edit_time, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url', +			'FROM'		=> array( +				TOPICS_TABLE	=> 't', +				POSTS_TABLE		=> 'p', +			), +			'LEFT_JOIN'	=> array( +				array( +					'FROM'	=> array(FORUMS_TABLE => 'f'), +					'ON'	=> 'p.forum_id = f.forum_id', +				), +			), +			'WHERE'		=> 'p.topic_id = t.topic_id +							AND ' . $this->db->sql_in_set('p.post_id', $post_ids), +			'ORDER_BY'	=> 'p.post_time DESC', +		); + +		return true; +	} + +	function get_forum_ids() +	{ +		static $forum_ids; + +		$cache_name	= 'feed_topic_active_forum_ids'; + +		if (!isset($forum_ids) && ($forum_ids = $this->cache->get('_' . $cache_name)) === false) +		{ +			$sql = 'SELECT forum_id +				FROM ' . FORUMS_TABLE . ' +				WHERE forum_type = ' . FORUM_POST . ' +					AND ' . $this->db->sql_bit_and('forum_options', FORUM_OPTION_FEED_EXCLUDE, '= 0') . ' +					AND ' . $this->db->sql_bit_and('forum_flags', log(FORUM_FLAG_ACTIVE_TOPICS, 2), '<> 0'); +			$result = $this->db->sql_query($sql); + +			$forum_ids = array(); +			while ($forum_id = (int) $this->db->sql_fetchfield('forum_id')) +			{ +				$forum_ids[$forum_id] = $forum_id; +			} +			$this->db->sql_freeresult($result); + +			$this->cache->put('_' . $cache_name, $forum_ids, 180); +		} + +		return $forum_ids; +	} + +	function adjust_item(&$item_row, &$row) +	{ +		parent::adjust_item($item_row, $row); + +		$item_row['title'] = (isset($row['forum_name']) && $row['forum_name'] !== '') ? $row['forum_name'] . ' ' . $this->separator . ' ' . $item_row['title'] : $item_row['title']; +	} +} 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 7af962eca7..e884a2f94c 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 @@ -2344,9 +2346,8 @@ function phpbb_generate_template_pagination($template, $base_url, $block_var_nam  		$tpl_prefix . 'BASE_URL'		=> $base_url,  		'A_' . $tpl_prefix . 'BASE_URL'		=> addslashes($base_url),  		$tpl_prefix . 'PER_PAGE'		=> $per_page, -		$tpl_prefix . 'PREVIOUS_PAGE'	=> $previous_page, -		$tpl_prefix . 'PREV_PAGE'		=> $previous_page, -		$tpl_prefix . 'NEXT_PAGE'		=> ($on_page != $total_pages) ? $base_url . $url_delim . $start_name . '=' . ($on_page * $per_page) : '', +		'U_' . $tpl_prefix . 'PREVIOUS_PAGE'	=> $previous_page, +		'U_' . $tpl_prefix . 'NEXT_PAGE'		=> ($on_page != $total_pages) ? $base_url . $url_delim . $start_name . '=' . ($on_page * $per_page) : '',  		$tpl_prefix . 'TOTAL_PAGES'		=> $total_pages,  		$tpl_prefix . 'CURRENT_PAGE'	=> $on_page,  	); @@ -2732,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... @@ -2741,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) @@ -2906,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 . '" />')  		);  	} @@ -3466,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'])))  	); @@ -4183,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)  { @@ -5293,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_acp.php b/phpBB/includes/functions_acp.php index d6bd9e35dd..ff0e2a1ac1 100644 --- a/phpBB/includes/functions_acp.php +++ b/phpBB/includes/functions_acp.php @@ -249,17 +249,49 @@ function build_cfg_template($tpl_type, $key, &$new, $config_key, $vars)  	{  		case 'text':  		case 'password': +		case 'url': +		case 'email': +		case 'color': +		case 'date': +		case 'time': +		case 'datetime': +		case 'datetime-local': +		case 'month': +		case 'range': +		case 'search': +		case 'tel': +		case 'url': +		case 'week':  			$size = (int) $tpl_type[1];  			$maxlength = (int) $tpl_type[2];  			$tpl = '<input id="' . $key . '" type="' . $tpl_type[0] . '"' . (($size) ? ' size="' . $size . '"' : '') . ' maxlength="' . (($maxlength) ? $maxlength : 255) . '" name="' . $name . '" value="' . $new[$config_key] . '"' . (($tpl_type[0] === 'password') ?  ' autocomplete="off"' : '') . ' />';  		break; +		case 'number': +			$min = $max = $maxlength = ''; +			$min = ( isset($tpl_type[1]) ) ? (int) $tpl_type[1] : false; +			if ( isset($tpl_type[2]) ) +			{ +				$max = (int) $tpl_type[2]; +				$maxlength = strlen( (string) $max ); +			} + +			$tpl = '<input id="' . $key . '" type="number" maxlength="' . (( $maxlength != '' ) ? $maxlength : 255) . '"' . (( $min != '' ) ? ' min="' . $min . '"' : '') . (( $max != '' ) ? ' max="' . $max . '"' : '') . ' name="' . $name . '" value="' . $new[$config_key] . '" />'; +		break; +  		case 'dimension': -			$size = (int) $tpl_type[1]; -			$maxlength = (int) $tpl_type[2]; +			$min = $max = $maxlength = $size = ''; + +			$min = (int) $tpl_type[1]; + +			if ( isset($tpl_type[2]) ) +			{ +				$max = (int) $tpl_type[2]; +				$size = $maxlength = strlen( (string) $max ); +			} -			$tpl = '<input id="' . $key . '" type="text"' . (($size) ? ' size="' . $size . '"' : '') . ' maxlength="' . (($maxlength) ? $maxlength : 255) . '" name="config[' . $config_key . '_width]" value="' . $new[$config_key . '_width'] . '" /> x <input type="text"' . (($size) ? ' size="' . $size . '"' : '') . ' maxlength="' . (($maxlength) ? $maxlength : 255) . '" name="config[' . $config_key . '_height]" value="' . $new[$config_key . '_height'] . '" />'; +			$tpl = '<input id="' . $key . '" type="number"' . (( $size != '' ) ? ' size="' . $size . '"' : '') . ' maxlength="' . (($maxlength != '') ? $maxlength : 255) . '"' . (( $min !== '' ) ? ' min="' . $min . '"' : '') . (( $max != '' ) ? ' max="' . $max . '"' : '') . ' name="config[' . $config_key . '_width]" value="' . $new[$config_key . '_width'] . '" /> x <input type="number"' . (( $size != '' ) ? ' size="' . $size . '"' : '') . ' maxlength="' . (($maxlength != '') ? $maxlength : 255) . '"' . (( $min !== '' ) ? ' min="' . $min . '"' : '') . (( $max != '' ) ? ' max="' . $max . '"' : '') . ' name="config[' . $config_key . '_height]" value="' . $new[$config_key . '_height'] . '" />';  		break;  		case 'textarea': diff --git a/phpBB/includes/functions_admin.php b/phpBB/includes/functions_admin.php index 2103362651..fc29492ac1 100644 --- a/phpBB/includes/functions_admin.php +++ b/phpBB/includes/functions_admin.php @@ -2933,7 +2933,7 @@ function get_remote_file($host, $directory, $filename, &$errstr, &$errno, $port  	if ($fsock = @fsockopen($host, $port, $errno, $errstr, $timeout))  	{ -		@fputs($fsock, "GET $directory/$filename HTTP/1.1\r\n"); +		@fputs($fsock, "GET $directory/$filename HTTP/1.0\r\n");  		@fputs($fsock, "HOST: $host\r\n");  		@fputs($fsock, "Connection: close\r\n\r\n"); @@ -3074,38 +3074,29 @@ function tidy_database()  */  function add_permission_language()  { -	global $user, $phpEx; +	global $user, $phpEx, $phpbb_extension_manager; -	// First of all, our own file. We need to include it as the first file because it presets all relevant variables. -	$user->add_lang('acp/permissions_phpbb'); +	// add permission language files from extensions +	$finder = $phpbb_extension_manager->get_finder(); -	$files_to_add = array(); +	$lang_files = $finder +		->prefix('permissions_') +		->suffix(".$phpEx") +		->core_path('language/' . $user->lang_name . '/') +		->extension_directory('/language/' . $user->lang_name) +		->find(); -	// Now search in acp and mods folder for permissions_ files. -	foreach (array('acp/', 'mods/') as $path) +	foreach ($lang_files as $lang_file => $ext_name)  	{ -		$dh = @opendir($user->lang_path . $user->lang_name . '/' . $path); - -		if ($dh) +		if ($ext_name === '/')  		{ -			while (($file = readdir($dh)) !== false) -			{ -				if ($file !== 'permissions_phpbb.' . $phpEx && strpos($file, 'permissions_') === 0 && substr($file, -(strlen($phpEx) + 1)) === '.' . $phpEx) -				{ -					$files_to_add[] = $path . substr($file, 0, -(strlen($phpEx) + 1)); -				} -			} -			closedir($dh); +			$user->add_lang($lang_file); +		} +		else +		{ +			$user->add_lang_ext($ext_name, $lang_file);  		}  	} - -	if (!sizeof($files_to_add)) -	{ -		return false; -	} - -	$user->add_lang($files_to_add); -	return true;  }  /** @@ -3131,7 +3122,7 @@ function obtain_latest_version_info($force_update = false, $warn_fail = false, $  		$info = get_remote_file('version.phpbb.com', '/phpbb',  				((defined('PHPBB_QA')) ? '30x_qa.txt' : '30x.txt'), $errstr, $errno); -		if ($info === false) +		if (empty($info))  		{  			$cache->destroy('versioncheck');  			if ($warn_fail) diff --git a/phpBB/includes/functions_download.php b/phpBB/includes/functions_download.php index 9ae647d806..0a8000ea3d 100644 --- a/phpBB/includes/functions_download.php +++ b/phpBB/includes/functions_download.php @@ -46,7 +46,7 @@ function send_avatar_to_browser($file, $browser)  		$image_data = @getimagesize($file_path);  		header('Content-Type: ' . image_type_to_mime_type($image_data[2])); -		if (strpos(strtolower($browser), 'msie') !== false && strpos(strtolower($browser), 'msie 8.0') === false) +		if ((strpos(strtolower($user->browser), 'msie') !== false) && !phpbb_is_greater_ie_version($browser, 7))  		{  			header('Content-Disposition: attachment; ' . header_filename($file)); @@ -174,10 +174,9 @@ function send_file_to_browser($attachment, $upload_dir, $category)  	header('Pragma: public');  	// Send out the Headers. Do not set Content-Disposition to inline please, it is a security measure for users using the Internet Explorer. -	$is_ie8 = (strpos(strtolower($user->browser), 'msie 8.0') !== false);  	header('Content-Type: ' . $attachment['mimetype']); -	if ($is_ie8) +	if (phpbb_is_greater_ie_version($user->browser, 7))  	{  		header('X-Content-Type-Options: nosniff');  	} @@ -189,7 +188,7 @@ function send_file_to_browser($attachment, $upload_dir, $category)  	}  	else  	{ -		if (empty($user->browser) || (!$is_ie8 && (strpos(strtolower($user->browser), 'msie') !== false))) +		if (empty($user->browser) || ((strpos(strtolower($user->browser), 'msie') !== false) && !phpbb_is_greater_ie_version($user->browser, 7)))  		{  			header('Content-Disposition: attachment; ' . header_filename(htmlspecialchars_decode($attachment['real_filename'])));  			if (empty($user->browser) || (strpos(strtolower($user->browser), 'msie 6.0') !== false)) @@ -200,7 +199,7 @@ function send_file_to_browser($attachment, $upload_dir, $category)  		else  		{  			header('Content-Disposition: ' . ((strpos($attachment['mimetype'], 'image') === 0) ? 'inline' : 'attachment') . '; ' . header_filename(htmlspecialchars_decode($attachment['real_filename']))); -			if ($is_ie8 && (strpos($attachment['mimetype'], 'image') !== 0)) +			if (phpbb_is_greater_ie_version($user->browser, 7) && (strpos($attachment['mimetype'], 'image') !== 0))  			{  				header('X-Download-Options: noopen');  			} @@ -410,7 +409,8 @@ function set_modified_headers($stamp, $browser)  	// let's see if we have to send the file at all  	$last_load 	=  $request->header('Modified-Since') ? strtotime(trim($request->header('Modified-Since'))) : false; -	if ((strpos(strtolower($browser), 'msie 6.0') === false) && (strpos(strtolower($browser), 'msie 8.0') === false)) + +	if (strpos(strtolower($browser), 'msie 6.0') === false && !phpbb_is_greater_ie_version($browser, 7))  	{  		if ($last_load !== false && $last_load >= $stamp)  		{ @@ -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"; @@ -721,3 +721,24 @@ function phpbb_download_clean_filename($filename)  	return $filename;  } + +/** +* Check if the browser is internet explorer version 7+ +* +* @param string $user_agent	User agent HTTP header +* @param int $version IE version to check against +* +* @return bool true if internet explorer version is greater than $version +*/ +function phpbb_is_greater_ie_version($user_agent, $version) +{ +	if (preg_match('/msie (\d+)/', strtolower($user_agent), $matches)) +	{ +		$ie_version = (int) $matches[1]; +		return ($ie_version > $version); +	} +	else +	{ +		return false; +	} +} 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_posting.php b/phpBB/includes/functions_posting.php index d2ff095e25..de88f7cc98 100644 --- a/phpBB/includes/functions_posting.php +++ b/phpBB/includes/functions_posting.php @@ -403,14 +403,7 @@ function upload_attachment($form_name, $forum_id, $local = false, $local_storage  		$upload->set_disallowed_content(explode('|', $config['mime_triggers']));  	} -	if (!$local) -	{ -		$filedata['post_attach'] = ($upload->is_valid($form_name)) ? true : false; -	} -	else -	{ -		$filedata['post_attach'] = true; -	} +	$filedata['post_attach'] = $local || $upload->is_valid($form_name);  	if (!$filedata['post_attach'])  	{ @@ -429,30 +422,18 @@ function upload_attachment($form_name, $forum_id, $local = false, $local_storage  		return $filedata;  	} -	$cat_id = (isset($extensions[$file->get('extension')]['display_cat'])) ? $extensions[$file->get('extension')]['display_cat'] : ATTACHMENT_CATEGORY_NONE; - -	// Make sure the image category only holds valid images... -	if ($cat_id == ATTACHMENT_CATEGORY_IMAGE && !$file->is_image()) -	{ -		$file->remove(); - -		// If this error occurs a user tried to exploit an IE Bug by renaming extensions -		// Since the image category is displaying content inline we need to catch this. -		trigger_error($user->lang['ATTACHED_IMAGE_NOT_IMAGE']); -	} - -	// Do we have to create a thumbnail? -	$filedata['thumbnail'] = ($cat_id == ATTACHMENT_CATEGORY_IMAGE && $config['img_create_thumbnail']) ? 1 : 0; - -	// Check Image Size, if it is an image -	if (!$auth->acl_get('a_') && !$auth->acl_get('m_', $forum_id) && $cat_id == ATTACHMENT_CATEGORY_IMAGE) -	{ -		$file->upload->set_allowed_dimensions(0, 0, $config['img_max_width'], $config['img_max_height']); -	} +	// Whether the uploaded file is in the image category +	$is_image = (isset($extensions[$file->get('extension')]['display_cat'])) ? $extensions[$file->get('extension')]['display_cat'] == ATTACHMENT_CATEGORY_IMAGE : false; -	// Admins and mods are allowed to exceed the allowed filesize  	if (!$auth->acl_get('a_') && !$auth->acl_get('m_', $forum_id))  	{ +		// Check Image Size, if it is an image +		if ($is_image) +		{ +			$file->upload->set_allowed_dimensions(0, 0, $config['img_max_width'], $config['img_max_height']); +		} + +		// Admins and mods are allowed to exceed the allowed filesize  		if (!empty($extensions[$file->get('extension')]['max_filesize']))  		{  			$allowed_filesize = $extensions[$file->get('extension')]['max_filesize']; @@ -467,10 +448,12 @@ function upload_attachment($form_name, $forum_id, $local = false, $local_storage  	$file->clean_filename('unique', $user->data['user_id'] . '_'); -	// Are we uploading an image *and* this image being within the image category? Only then perform additional image checks. -	$no_image = ($cat_id == ATTACHMENT_CATEGORY_IMAGE) ? false : true; +	// Are we uploading an image *and* this image being within the image category? +	// Only then perform additional image checks. +	$file->move_file($config['upload_path'], false, !$is_image); -	$file->move_file($config['upload_path'], false, $no_image); +	// Do we have to create a thumbnail? +	$filedata['thumbnail'] = ($is_image && $config['img_create_thumbnail']) ? 1 : 0;  	if (sizeof($file->error))  	{ @@ -481,6 +464,16 @@ function upload_attachment($form_name, $forum_id, $local = false, $local_storage  		return $filedata;  	} +	// Make sure the image category only holds valid images... +	if ($is_image && !$file->is_image()) +	{ +		$file->remove(); + +		// If this error occurs a user tried to exploit an IE Bug by renaming extensions +		// Since the image category is displaying content inline we need to catch this. +		trigger_error($user->lang['ATTACHED_IMAGE_NOT_IMAGE']); +	} +  	$filedata['filesize'] = $file->get('filesize');  	$filedata['mimetype'] = $file->get('mimetype');  	$filedata['extension'] = $file->get('extension'); diff --git a/phpBB/includes/functions_profile_fields.php b/phpBB/includes/functions_profile_fields.php index 10af997bff..7dd0b0e87d 100644 --- a/phpBB/includes/functions_profile_fields.php +++ b/phpBB/includes/functions_profile_fields.php @@ -1040,9 +1040,9 @@ class custom_profile_admin extends custom_profile  		global $user;  		$options = array( -			0 => array('TITLE' => $user->lang['FIELD_LENGTH'],		'FIELD' => '<input type="text" name="field_length" size="5" value="' . $this->vars['field_length'] . '" />'), -			1 => array('TITLE' => $user->lang['MIN_FIELD_CHARS'],	'FIELD' => '<input type="text" name="field_minlen" size="5" value="' . $this->vars['field_minlen'] . '" />'), -			2 => array('TITLE' => $user->lang['MAX_FIELD_CHARS'],	'FIELD' => '<input type="text" name="field_maxlen" size="5" value="' . $this->vars['field_maxlen'] . '" />'), +			0 => array('TITLE' => $user->lang['FIELD_LENGTH'],		'FIELD' => '<input type="number" min="0" name="field_length" size="5" value="' . $this->vars['field_length'] . '" />'), +			1 => array('TITLE' => $user->lang['MIN_FIELD_CHARS'],	'FIELD' => '<input type="number" min="0" name="field_minlen" size="5" value="' . $this->vars['field_minlen'] . '" />'), +			2 => array('TITLE' => $user->lang['MAX_FIELD_CHARS'],	'FIELD' => '<input type="number" min="0" size="5" value="' . $this->vars['field_maxlen'] . '" />'),  			3 => array('TITLE' => $user->lang['FIELD_VALIDATION'],	'FIELD' => '<select name="field_validation">' . $this->validate_options() . '</select>')  		); @@ -1057,9 +1057,9 @@ class custom_profile_admin extends custom_profile  		global $user;  		$options = array( -			0 => array('TITLE' => $user->lang['FIELD_LENGTH'],		'FIELD' => '<input name="rows" size="5" value="' . $this->vars['rows'] . '" /> ' . $user->lang['ROWS'] . '</dd><dd><input name="columns" size="5" value="' . $this->vars['columns'] . '" /> ' . $user->lang['COLUMNS'] . ' <input type="hidden" name="field_length" value="' . $this->vars['field_length'] . '" />'), -			1 => array('TITLE' => $user->lang['MIN_FIELD_CHARS'],	'FIELD' => '<input type="text" name="field_minlen" size="10" value="' . $this->vars['field_minlen'] . '" />'), -			2 => array('TITLE' => $user->lang['MAX_FIELD_CHARS'],	'FIELD' => '<input type="text" name="field_maxlen" size="10" value="' . $this->vars['field_maxlen'] . '" />'), +			0 => array('TITLE' => $user->lang['FIELD_LENGTH'],		'FIELD' => '<input type="number" min="0" max="99999" name="rows" size="5" value="' . $this->vars['rows'] . '" /> ' . $user->lang['ROWS'] . '</dd><dd><input type="number" min="0" max="99999" name="columns" size="5" value="' . $this->vars['columns'] . '" /> ' . $user->lang['COLUMNS'] . ' <input type="hidden" name="field_length" value="' . $this->vars['field_length'] . '" />'), +			1 => array('TITLE' => $user->lang['MIN_FIELD_CHARS'],	'FIELD' => '<input type="number" min="0" max="9999999999" name="field_minlen" size="10" value="' . $this->vars['field_minlen'] . '" />'), +			2 => array('TITLE' => $user->lang['MAX_FIELD_CHARS'],	'FIELD' => '<input type="number" min="0" max="9999999999" name="field_maxlen" size="10" value="' . $this->vars['field_maxlen'] . '" />'),  			3 => array('TITLE' => $user->lang['FIELD_VALIDATION'],	'FIELD' => '<select name="field_validation">' . $this->validate_options() . '</select>')  		); @@ -1074,9 +1074,9 @@ class custom_profile_admin extends custom_profile  		global $user;  		$options = array( -			0 => array('TITLE' => $user->lang['FIELD_LENGTH'],		'FIELD' => '<input type="text" name="field_length" size="5" value="' . $this->vars['field_length'] . '" />'), -			1 => array('TITLE' => $user->lang['MIN_FIELD_NUMBER'],	'FIELD' => '<input type="text" name="field_minlen" size="5" value="' . $this->vars['field_minlen'] . '" />'), -			2 => array('TITLE' => $user->lang['MAX_FIELD_NUMBER'],	'FIELD' => '<input type="text" name="field_maxlen" size="5" value="' . $this->vars['field_maxlen'] . '" />'), +			0 => array('TITLE' => $user->lang['FIELD_LENGTH'],		'FIELD' => '<input type="number" min="0" max="99999" name="field_length" size="5" value="' . $this->vars['field_length'] . '" />'), +			1 => array('TITLE' => $user->lang['MIN_FIELD_NUMBER'],	'FIELD' => '<input type="number" min="0" max="99999" name="field_minlen" size="5" value="' . $this->vars['field_minlen'] . '" />'), +			2 => array('TITLE' => $user->lang['MAX_FIELD_NUMBER'],	'FIELD' => '<input type="number" min="0" max="99999" name="field_maxlen" size="5" value="' . $this->vars['field_maxlen'] . '" />'),  			3 => array('TITLE' => $user->lang['DEFAULT_VALUE'],		'FIELD' => '<input type="post" name="field_default_value" value="' . $this->vars['field_default_value'] . '" />')  		); 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..1b598f7bf7 100644 --- a/phpBB/includes/functions_user.php +++ b/phpBB/includes/functions_user.php @@ -1330,22 +1330,12 @@ function validate_data($data, $val_ary)  		{  			$function = array_shift($validate);  			array_unshift($validate, $data[$var]); +			$function_prefix = (function_exists('phpbb_validate_' . $function)) ? 'phpbb_validate_' : 'validate_'; -			if (function_exists('phpbb_validate_' . $function)) +			if ($result = call_user_func_array($function_prefix . $function, $validate))  			{ -				if ($result = call_user_func_array('phpbb_validate_' . $function, $validate)) -				{ -					// Since errors are checked later for their language file existence, we need to make sure custom errors are not adjusted. -					$error[] = (empty($user->lang[$result . '_' . strtoupper($var)])) ? $result : $result . '_' . strtoupper($var); -				} -			} -			else -			{ -				if ($result = call_user_func_array('validate_' . $function, $validate)) -				{ -					// Since errors are checked later for their language file existence, we need to make sure custom errors are not adjusted. -					$error[] = (empty($user->lang[$result . '_' . strtoupper($var)])) ? $result : $result . '_' . strtoupper($var); -				} +				// Since errors are checked later for their language file existence, we need to make sure custom errors are not adjusted. +				$error[] = (empty($user->lang[$result . '_' . strtoupper($var)])) ? $result : $result . '_' . strtoupper($var);  			}  		}  	} @@ -1663,7 +1653,7 @@ function validate_username($username, $allowed_username = false)  */  function validate_password($password)  { -	global $config, $db, $user; +	global $config;  	if ($password === '' || $config['pass_complex'] === 'PASS_TYPE_ANY')  	{ @@ -2009,6 +1999,30 @@ function validate_jabber($jid)  }  /** +* Validate hex colour value +* +* @param string $colour The hex colour value +* @param bool $optional Whether the colour value is optional. True if an empty +*			string will be accepted as correct input, false if not. +* @return bool|string Error message if colour value is incorrect, false if it +*			fits the hex colour code +*/ +function phpbb_validate_hex_colour($colour, $optional = false) +{ +	if ($colour === '') +	{ +		return (($optional) ? false : 'WRONG_DATA'); +	} + +	if (!preg_match('/^([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/', $colour)) +	{ +		return 'WRONG_DATA'; +	} + +	return false; +} + +/**  * Verifies whether a style ID corresponds to an active style.  *  * @param int $style_id The style_id of a style which should be checked if activated or not. @@ -2924,8 +2938,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/exception.php b/phpBB/includes/notification/exception.php new file mode 100644 index 0000000000..a52d6fdc57 --- /dev/null +++ b/phpBB/includes/notification/exception.php @@ -0,0 +1,29 @@ +<?php +/** +* +* @package notifications +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Notifications exception +* +* @package notifications +*/ +class phpbb_notification_exception extends \Exception +{ +	public function __toString() +	{ +		return $this->getMessage(); +	} +} diff --git a/phpBB/includes/notification/manager.php b/phpBB/includes/notification/manager.php index ff83d4bb37..97833710c0 100644 --- a/phpBB/includes/notification/manager.php +++ b/phpBB/includes/notification/manager.php @@ -36,6 +36,9 @@ class phpbb_notification_manager  	/** @var phpbb_db_driver */  	protected $db; +	/** @var phpbb_cache_service */ +	protected $cache; +  	/** @var phpbb_user */  	protected $user; @@ -70,7 +73,7 @@ class phpbb_notification_manager  	* @param string $user_notifications_table  	* @return phpbb_notification_manager  	*/ -	public function __construct($notification_types, $notification_methods, $phpbb_container, phpbb_user_loader $user_loader, phpbb_db_driver $db, $user, $phpbb_root_path, $php_ext, $notification_types_table, $notifications_table, $user_notifications_table) +	public function __construct($notification_types, $notification_methods, $phpbb_container, phpbb_user_loader $user_loader, phpbb_db_driver $db, phpbb_cache_service $cache, $user, $phpbb_root_path, $php_ext, $notification_types_table, $notifications_table, $user_notifications_table)  	{  		$this->notification_types = $notification_types;  		$this->notification_methods = $notification_methods; @@ -78,6 +81,7 @@ class phpbb_notification_manager  		$this->user_loader = $user_loader;  		$this->db = $db; +		$this->cache = $cache;  		$this->user = $user;  		$this->phpbb_root_path = $phpbb_root_path; @@ -145,7 +149,7 @@ class phpbb_notification_manager  				FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt  				WHERE n.user_id = ' . (int) $options['user_id'] . '  					AND n.notification_read = 0 -					AND nt.notification_type = n.item_type +					AND nt.notification_type_id = n.notification_type_id  					AND nt.notification_type_enabled = 1';  			$result = $this->db->sql_query($sql);  			$unread_count = (int) $this->db->sql_fetchfield('unread_count', $result); @@ -158,7 +162,7 @@ class phpbb_notification_manager  			$sql = 'SELECT COUNT(n.notification_id) AS total_count  				FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt  				WHERE n.user_id = ' . (int) $options['user_id'] . ' -					AND nt.notification_type = n.item_type +					AND nt.notification_type_id = n.notification_type_id  					AND nt.notification_type_enabled = 1';  			$result = $this->db->sql_query($sql);  			$total_count = (int) $this->db->sql_fetchfield('total_count', $result); @@ -170,11 +174,11 @@ class phpbb_notification_manager  			$rowset = array();  			// Get the main notifications -			$sql = 'SELECT n.* +			$sql = 'SELECT n.*, nt.notification_type_name  				FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt  				WHERE n.user_id = ' . (int) $options['user_id'] .  					(($options['notification_id']) ? ((is_array($options['notification_id'])) ? ' AND ' . $this->db->sql_in_set('n.notification_id', $options['notification_id']) : ' AND n.notification_id = ' . (int) $options['notification_id']) : '') . ' -					AND nt.notification_type = n.item_type +					AND nt.notification_type_id = n.notification_type_id  					AND nt.notification_type_enabled = 1  				ORDER BY n.' . $this->db->sql_escape($options['order_by']) . ' ' . $this->db->sql_escape($options['order_dir']);  			$result = $this->db->sql_query_limit($sql, $options['limit'], $options['start']); @@ -188,12 +192,12 @@ class phpbb_notification_manager  			// Get all unread notifications  			if ($unread_count && $options['all_unread'] && !empty($rowset))  			{ -				$sql = 'SELECT n.* +				$sql = 'SELECT n.*, nt.notification_type_name  				FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt  					WHERE n.user_id = ' . (int) $options['user_id'] . '  						AND n.notification_read = 0  						AND ' . $this->db->sql_in_set('n.notification_id', array_keys($rowset), true) . ' -						AND nt.notification_type = n.item_type +						AND nt.notification_type_id = n.notification_type_id  						AND nt.notification_type_enabled = 1  					ORDER BY n.' . $this->db->sql_escape($options['order_by']) . ' ' . $this->db->sql_escape($options['order_dir']);  				$result = $this->db->sql_query_limit($sql, $options['limit'], $options['start']); @@ -207,17 +211,17 @@ class phpbb_notification_manager  			foreach ($rowset as $row)  			{ -				$notification = $this->get_item_type_class($row['item_type'], $row); +				$notification = $this->get_item_type_class($row['notification_type_name'], $row);  				// Array of user_ids to query all at once  				$user_ids = array_merge($user_ids, $notification->users_to_query());  				// Some notification types also require querying additional tables themselves -				if (!isset($load_special[$row['item_type']])) +				if (!isset($load_special[$row['notification_type_name']]))  				{ -					$load_special[$row['item_type']] = array(); +					$load_special[$row['notification_type_name']] = array();  				} -				$load_special[$row['item_type']] = array_merge($load_special[$row['item_type']], $notification->get_load_special()); +				$load_special[$row['notification_type_name']] = array_merge($load_special[$row['notification_type_name']], $notification->get_load_special());  				$notifications[$row['notification_id']] = $notification;  			} @@ -243,19 +247,22 @@ class phpbb_notification_manager  	/**  	* Mark notifications read  	* -	* @param bool|string|array $item_type Type identifier or array of item types (only acceptable if the $data is identical for the specified types). False to mark read for all item types +	* @param bool|string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types). False to mark read for all item types  	* @param bool|int|array $item_id Item id or array of item ids. False to mark read for all item ids  	* @param bool|int|array $user_id User id or array of user ids. False to mark read for all user ids  	* @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False)  	*/ -	public function mark_notifications_read($item_type, $item_id, $user_id, $time = false) +	public function mark_notifications_read($notification_type_name, $item_id, $user_id, $time = false)  	{  		$time = ($time !== false) ? $time : time();  		$sql = 'UPDATE ' . $this->notifications_table . "  			SET notification_read = 1  			WHERE notification_time <= " . (int) $time . -				(($item_type !== false) ? ' AND ' . (is_array($item_type) ? $this->db->sql_in_set('item_type', $item_type) : " item_type = '" . $this->db->sql_escape($item_type) . "'") : '') . +				(($notification_type_name !== false) ? ' AND ' . +					(is_array($notification_type_name) ? $this->db->sql_in_set('notification_type_id', $this->get_notification_type_ids($notification_type_name)) : 'notification_type_id = ' . $this->get_notification_type_id($notification_type_name)) +					 : '') . +				(($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);  	} @@ -263,29 +270,21 @@ class phpbb_notification_manager  	/**  	* Mark notifications read from a parent identifier  	* -	* @param string|array $item_type Type identifier or array of item types (only acceptable if the $data is identical for the specified types) +	* @param string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types)  	* @param bool|int|array $item_parent_id Item parent id or array of item parent ids. False to mark read for all item parent ids  	* @param bool|int|array $user_id User id or array of user ids. False to mark read for all user ids  	* @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False)  	*/ -	public function mark_notifications_read_by_parent($item_type, $item_parent_id, $user_id, $time = false) +	public function mark_notifications_read_by_parent($notification_type_name, $item_parent_id, $user_id, $time = false)  	{ -		if (is_array($item_type)) -		{ -			foreach ($item_type as $type) -			{ -				$this->mark_notifications_read_by_parent($type, $item_parent_id, $user_id, $time); -			} - -			return; -		} -  		$time = ($time !== false) ? $time : time();  		$sql = 'UPDATE ' . $this->notifications_table . "  			SET notification_read = 1 -			WHERE item_type = '" . $this->db->sql_escape($item_type) . "' -				AND notification_time <= " . (int) $time . +			WHERE notification_time <= " . (int) $time . +				(($notification_type_name !== false) ? ' AND ' . +					(is_array($notification_type_name) ? $this->db->sql_in_set('notification_type_id', $this->get_notification_type_ids($notification_type_name)) : 'notification_type_id = ' . $this->get_notification_type_id($notification_type_name)) +					 : '') .  				(($item_parent_id !== false) ? ' AND ' . (is_array($item_parent_id) ? $this->db->sql_in_set('item_parent_id', $item_parent_id) : 'item_parent_id = ' . (int) $item_parent_id) : '') .  				(($user_id !== false) ? ' AND ' . (is_array($user_id) ? $this->db->sql_in_set('user_id', $user_id) : 'user_id = ' . (int) $user_id) : '');  		$this->db->sql_query($sql); @@ -311,7 +310,7 @@ class phpbb_notification_manager  	/**  	* Add a notification  	* -	* @param string|array $item_type Type identifier or array of item types (only acceptable if the $data is identical for the specified types) +	* @param string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types)  	*			Note: If you send an array of types, any user who could receive multiple notifications from this single item will only receive  	* 			a single notification. If they MUST receive multiple notifications, call this function multiple times instead of sending an array  	* @param array $data Data specific for this type that will be inserted @@ -319,18 +318,18 @@ class phpbb_notification_manager  	* 			ignore_users	array of data to specify which users should not receive certain types of notifications  	* @return array Information about what users were notified and how they were notified  	*/ -	public function add_notifications($item_type, $data, array $options = array()) +	public function add_notifications($notification_type_name, $data, array $options = array())  	{  		$options = array_merge(array(  			'ignore_users'		=> array(),  		), $options); -		if (is_array($item_type)) +		if (is_array($notification_type_name))  		{  			$notified_users = array();  			$temp_options = $options; -			foreach ($item_type as $type) +			foreach ($notification_type_name as $type)  			{  				$temp_options['ignore_users'] = $options['ignore_users'] + $notified_users;  				$notified_users += $this->add_notifications($type, $data, $temp_options); @@ -339,12 +338,12 @@ class phpbb_notification_manager  			return $notified_users;  		} -		$item_id = $this->get_item_type_class($item_type)->get_item_id($data); +		$item_id = $this->get_item_type_class($notification_type_name)->get_item_id($data);  		// find out which users want to receive this type of notification -		$notify_users = $this->get_item_type_class($item_type)->find_users_for_notification($data, $options); +		$notify_users = $this->get_item_type_class($notification_type_name)->find_users_for_notification($data, $options); -		$this->add_notifications_for_users($item_type, $data, $notify_users); +		$this->add_notifications_for_users($notification_type_name, $data, $notify_users);  		return $notify_users;  	} @@ -352,15 +351,15 @@ class phpbb_notification_manager  	/**  	* Add a notification for specific users  	* -	* @param string|array $item_type Type identifier or array of item types (only acceptable if the $data is identical for the specified types) +	* @param string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types)  	* @param array $data Data specific for this type that will be inserted  	* @param array $notify_users User list to notify  	*/ -	public function add_notifications_for_users($item_type, $data, $notify_users) +	public function add_notifications_for_users($notification_type_name, $data, $notify_users)  	{ -		if (is_array($item_type)) +		if (is_array($notification_type_name))  		{ -			foreach ($item_type as $type) +			foreach ($notification_type_name as $type)  			{  				$this->add_notifications_for_users($type, $data, $notify_users);  			} @@ -368,28 +367,12 @@ class phpbb_notification_manager  			return;  		} -		$sql = 'SELECT notification_type -			FROM ' . $this->notification_types_table . " -			WHERE notification_type = '" . $this->db->sql_escape($item_type) . "'"; -		$result = $this->db->sql_query($sql); +		$notification_type_id = $this->get_notification_type_id($notification_type_name); -		if ($this->db->sql_fetchrow($result) === false) -		{ -			// Does not exist in the database, must add the item type -			$sql = 'INSERT INTO ' . $this->notification_types_table . ' ' . $this->db->sql_build_array('INSERT', array( -				'notification_type'				=> $item_type, -				'notification_type_enabled'		=> 1, -			)); -			$this->db->sql_query($sql); -		} - -		$this->db->sql_freeresult($result); - -		$item_id = $this->get_item_type_class($item_type)->get_item_id($data); +		$item_id = $this->get_item_type_class($notification_type_name)->get_item_id($data);  		$user_ids = array();  		$notification_objects = $notification_methods = array(); -		$new_rows = array();  		// Never send notifications to the anonymous user!  		unset($notify_users[ANONYMOUS]); @@ -397,10 +380,10 @@ class phpbb_notification_manager  		// Make sure not to send new notifications to users who've already been notified about this item  		// This may happen when an item was added, but now new users are able to see the item  		$sql = 'SELECT n.user_id -			FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . " nt -			WHERE n.item_type = '" . $this->db->sql_escape($item_type) . "' -				AND n.item_id = " . (int) $item_id . ' -				AND nt.notification_type = n.item_type +			FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt +			WHERE n.notification_type_id = ' . (int) $notification_type_id . ' +				AND n.item_id = ' . (int) $item_id . ' +				AND nt.notification_type_id = n.notification_type_id  				AND nt.notification_type_enabled = 1';  		$result = $this->db->sql_query($sql);  		while ($row = $this->db->sql_fetchrow($result)) @@ -415,19 +398,21 @@ class phpbb_notification_manager  		}  		// Allow notifications to perform actions before creating the insert array (such as run a query to cache some data needed for all notifications) -		$notification = $this->get_item_type_class($item_type); +		$notification = $this->get_item_type_class($notification_type_name);  		$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)  		{ -			$notification = $this->get_item_type_class($item_type); +			$notification = $this->get_item_type_class($notification_type_name);  			$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 +432,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); @@ -463,14 +447,14 @@ class phpbb_notification_manager  	/**  	* Update a notification  	* -	* @param string|array $item_type Type identifier or array of item types (only acceptable if the $data is identical for the specified types) +	* @param string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types)  	* @param array $data Data specific for this type that will be updated  	*/ -	public function update_notifications($item_type, $data) +	public function update_notifications($notification_type_name, $data)  	{ -		if (is_array($item_type)) +		if (is_array($notification_type_name))  		{ -			foreach ($item_type as $type) +			foreach ($notification_type_name as $type)  			{  				$this->update_notifications($type, $data);  			} @@ -478,7 +462,7 @@ class phpbb_notification_manager  			return;  		} -		$notification = $this->get_item_type_class($item_type); +		$notification = $this->get_item_type_class($notification_type_name);  		// Allow the notifications class to over-ride the update_notifications functionality  		if (method_exists($notification, 'update_notifications')) @@ -490,28 +474,29 @@ class phpbb_notification_manager  			}  		} +		$notification_type_id = $this->get_notification_type_id($notification_type_name);  		$item_id = $notification->get_item_id($data);  		$update_array = $notification->create_update_array($data);  		$sql = 'UPDATE ' . $this->notifications_table . ' -			SET ' . $this->db->sql_build_array('UPDATE', $update_array) . " -			WHERE item_type = '" . $this->db->sql_escape($item_type) . "' -				AND item_id = " . (int) $item_id; +			SET ' . $this->db->sql_build_array('UPDATE', $update_array) . ' +			WHERE notification_type_id = ' . (int) $notification_type_id . ' +				AND item_id = ' . (int) $item_id;  		$this->db->sql_query($sql);  	}  	/**  	* Delete a notification  	* -	* @param string|array $item_type Type identifier or array of item types (only acceptable if the $item_id is identical for the specified types) +	* @param string|array $notification_type_name Type identifier or array of item types (only acceptable if the $item_id is identical for the specified types)  	* @param int|array $item_id Identifier within the type (or array of ids)  	* @param array $data Data specific for this type that will be updated  	*/ -	public function delete_notifications($item_type, $item_id) +	public function delete_notifications($notification_type_name, $item_id)  	{ -		if (is_array($item_type)) +		if (is_array($notification_type_name))  		{ -			foreach ($item_type as $type) +			foreach ($notification_type_name as $type)  			{  				$this->delete_notifications($type, $item_id);  			} @@ -519,9 +504,11 @@ class phpbb_notification_manager  			return;  		} -		$sql = 'DELETE FROM ' . $this->notifications_table . " -			WHERE item_type = '" . $this->db->sql_escape($item_type) . "' -				AND " . (is_array($item_id) ? $this->db->sql_in_set('item_id', $item_id) : 'item_id = ' . (int) $item_id); +		$notification_type_id = $this->get_notification_type_id($notification_type_name); + +		$sql = 'DELETE FROM ' . $this->notifications_table . ' +			WHERE notification_type_id = ' . (int) $notification_type_id . ' +				AND ' . (is_array($item_id) ? $this->db->sql_in_set('item_id', $item_id) : 'item_id = ' . (int) $item_id);  		$this->db->sql_query($sql);  	} @@ -654,7 +641,8 @@ class phpbb_notification_manager  	{  		if ($method !== '')  		{ -			$this->add_subscription($item_type, $item_type, '', $user_id); +			// Make sure to subscribe them to the base subscription +			$this->add_subscription($item_type, $item_id, '', $user_id);  		}  		$user_id = ($user_id === false) ? $this->user->data['user_id'] : $user_id; @@ -754,13 +742,13 @@ class phpbb_notification_manager  	* is disabled so that all those notifications are hidden and do not  	* cause errors  	* -	* @param string $item_type Type identifier of the subscription +	* @param string $notification_type_name Type identifier of the subscription  	*/ -	public function disable_notifications($item_type) +	public function disable_notifications($notification_type_name)  	{  		$sql = 'UPDATE ' . $this->notification_types_table . "  			SET notification_type_enabled = 0 -			WHERE notification_type = '" . $this->db->sql_escape($item_type) . "'"; +			WHERE notification_type_name = '" . $this->db->sql_escape($notification_type_name) . "'";  		$this->db->sql_query($sql);  	} @@ -770,17 +758,21 @@ class phpbb_notification_manager  	* This should be called when an extension which has notification types  	* is purged so that all those notifications are removed  	* -	* @param string $item_type Type identifier of the subscription +	* @param string $notification_type_name Type identifier of the subscription  	*/ -	public function purge_notifications($item_type) +	public function purge_notifications($notification_type_name)  	{ -		$sql = 'DELETE FROM ' . $this->notifications_table . " -			WHERE item_type = '" . $this->db->sql_escape($item_type) . "'"; +		$notification_type_id = $this->get_notification_type_id($notification_type_name); + +		$sql = 'DELETE FROM ' . $this->notifications_table . ' +			WHERE notification_type_id = ' . (int) $notification_type_id;  		$this->db->sql_query($sql); -		$sql = 'DELETE FROM ' . $this->notification_types_table . " -			WHERE notification_type = '" . $this->db->sql_escape($item_type) . "'"; +		$sql = 'DELETE FROM ' . $this->notification_types_table . ' +			WHERE notification_type_id = ' . (int) $notification_type_id;  		$this->db->sql_query($sql); + +		$this->cache->destroy('notification_type_ids');  	}  	/** @@ -790,13 +782,13 @@ class phpbb_notification_manager  	* that was disabled is re-enabled so that all those notifications that  	* were hidden are shown again  	* -	* @param string $item_type Type identifier of the subscription +	* @param string $notification_type_name Type identifier of the subscription  	*/ -	public function enable_notifications($item_type) +	public function enable_notifications($notification_type_name)  	{  		$sql = 'UPDATE ' . $this->notification_types_table . "  			SET notification_type_enabled = 1 -			WHERE notification_type = '" . $this->db->sql_escape($item_type) . "'"; +			WHERE notification_type_name = '" . $this->db->sql_escape($notification_type_name) . "'";  		$this->db->sql_query($sql);  	} @@ -815,11 +807,11 @@ class phpbb_notification_manager  	/**  	* Helper to get the notifications item type class and set it up  	*/ -	public function get_item_type_class($item_type, $data = array()) +	public function get_item_type_class($notification_type_name, $data = array())  	{ -		$item_type = (strpos($item_type, 'notification.type.') === 0) ? $item_type : 'notification.type.' . $item_type; +		$notification_type_name = (strpos($notification_type_name, 'notification.type.') === 0) ? $notification_type_name : 'notification.type.' . $notification_type_name; -		$item = $this->load_object($item_type); +		$item = $this->load_object($notification_type_name);  		$item->set_initial_data($data); @@ -850,4 +842,69 @@ class phpbb_notification_manager  		return $object;  	} + +	/** +	* Get the notification type id from the name +	* +	* @param string $notification_type_name The name +	* @return int the notification_type_id +	*/ +	public function get_notification_type_id($notification_type_name) +	{ +		$notification_type_ids = $this->cache->get('notification_type_ids'); + +		if ($notification_type_ids === false) +		{ +			$notification_type_ids = array(); + +			$sql = 'SELECT notification_type_id, notification_type_name +				FROM ' . $this->notification_types_table; +			$result = $this->db->sql_query($sql); +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				$notification_type_ids[$row['notification_type_name']] = (int) $row['notification_type_id']; +			} +			$this->db->sql_freeresult($result); + +			$this->cache->put('notification_type_ids', $notification_type_ids); +		} + +		if (!isset($notification_type_ids[$notification_type_name])) +		{ +			if (!isset($this->notification_types[$notification_type_name]) && !isset($this->notification_types['notification.type.' . $notification_type_name])) +			{ +				throw new phpbb_notification_exception($this->user->lang('NOTIFICATION_TYPE_NOT_EXIST', $notification_type_name)); +			} + +			$sql = 'INSERT INTO ' . $this->notification_types_table . ' ' . $this->db->sql_build_array('INSERT', array( +				'notification_type_name'		=> $notification_type_name, +				'notification_type_enabled'		=> 1, +			)); +			$this->db->sql_query($sql); + +			$notification_type_ids[$notification_type_name] = (int) $this->db->sql_nextid(); + +			$this->cache->put('notification_type_ids', $notification_type_ids); +		} + +		return $notification_type_ids[$notification_type_name]; +	} + +	/** +	* Get notification type ids (as an array) +	* +	* @param array $notification_type_names Array of strings +	* @return array Array of integers +	*/ +	public function get_notification_type_ids(array $notification_type_names) +	{ +		$notification_type_ids = array(); + +		foreach ($notification_type_names as $name) +		{ +			$notification_type_ids[$name] = $this->get_notification_type_id($name); +		} + +		return $notification_type_ids; +	}  } diff --git a/phpBB/includes/notification/method/base.php b/phpBB/includes/notification/method/base.php index 22418c9be8..b633956d01 100644 --- a/phpBB/includes/notification/method/base.php +++ b/phpBB/includes/notification/method/base.php @@ -30,7 +30,7 @@ abstract class phpbb_notification_method_base implements phpbb_notification_meth  	/** @var phpbb_db_driver */  	protected $db; -	/** @var phpbb_cache_service */ +	/** @var phpbb_cache_driver_interface */  	protected $cache;  	/** @var phpbb_template */ 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/base.php b/phpBB/includes/notification/type/base.php index 600ef7c965..46517f1c9b 100644 --- a/phpBB/includes/notification/type/base.php +++ b/phpBB/includes/notification/type/base.php @@ -30,7 +30,7 @@ abstract class phpbb_notification_type_base implements phpbb_notification_type_i  	/** @var phpbb_db_driver */  	protected $db; -	/** @var phpbb_cache_service */ +	/** @var phpbb_cache_driver_interface */  	protected $cache;  	/** @var phpbb_template */ @@ -69,10 +69,18 @@ abstract class phpbb_notification_type_base implements phpbb_notification_type_i  	public static $notification_option = false;  	/** +	* The notification_type_id, set upon creation of the class +	* This is the notification_type_id from the notification_types table +	* +	* @var int +	*/ +	protected $notification_type_id; + +	/**  	* Indentification data -	* item_type			- Type of the item (translates to the notification type) -	* item_id			- ID of the item (e.g. post_id, msg_id) -	* item_parent_id	- Parent item id (ex: for topic => forum_id, for post => topic_id, etc) +	* notification_type_id	- ID of the item type (auto generated, from notification types table) +	* item_id				- ID of the item (e.g. post_id, msg_id) +	* item_parent_id		- Parent item id (ex: for topic => forum_id, for post => topic_id, etc)  	* user_id  	* notification_read  	* notification_time @@ -124,6 +132,8 @@ abstract class phpbb_notification_type_base implements phpbb_notification_type_i  	public function set_notification_manager(phpbb_notification_manager $notification_manager)  	{  		$this->notification_manager = $notification_manager; + +		$this->notification_type_id = $this->notification_manager->get_notification_type_id($this->get_type());  	}  	/** @@ -211,7 +221,7 @@ abstract class phpbb_notification_type_base implements phpbb_notification_type_i  		// Defaults  		$this->data = array_merge(array(  			'item_id'				=> static::get_item_id($type_data), -			'item_type'	   			=> $this->get_type(), +			'notification_type_id'	=> $this->notification_type_id,  			'item_parent_id'		=> static::get_item_parent_id($type_data),  			'notification_time'		=> time(), @@ -460,7 +470,7 @@ abstract class phpbb_notification_type_base implements phpbb_notification_type_i  		$this->notification_read = (bool) !$unread;  		$where = array( -			"item_type = '" . $this->db->sql_escape($this->item_type) . "'", +			'notification_type_id = ' . (int) $this->notification_type_id,  			'item_id = ' . (int) $this->item_id,  			'user_id = ' . (int) $this->user_id,  		); diff --git a/phpBB/includes/notification/type/bookmark.php b/phpBB/includes/notification/type/bookmark.php index 4e48a967d0..ae2e75d3eb 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']); @@ -102,11 +103,11 @@ class phpbb_notification_type_bookmark extends phpbb_notification_type_post  		// Try to find the users who already have been notified about replies and have not read the topic since and just update their notifications  		$update_notifications = array();  		$sql = 'SELECT n.* -			FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . " nt -			WHERE n.item_type = '" . $this->get_type() . "' -				AND n.item_parent_id = " . (int) self::get_item_parent_id($post) . ' +			FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt +			WHERE n.notification_type_id = ' . (int) $this->notification_type_id . ' +				AND n.item_parent_id = ' . (int) self::get_item_parent_id($post) . '  				AND n.notification_read = 0 -				AND nt.notification_type = n.item_type +				AND nt.notification_type_id = n.notification_type_id  				AND nt.notification_type_enabled = 1';  		$result = $this->db->sql_query($sql);  		while ($row = $this->db->sql_fetchrow($result)) diff --git a/phpBB/includes/notification/type/post.php b/phpBB/includes/notification/type/post.php index ddfa720e5e..9207fd866e 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)) @@ -123,11 +138,11 @@ class phpbb_notification_type_post extends phpbb_notification_type_base  		// Try to find the users who already have been notified about replies and have not read the topic since and just update their notifications  		$update_notifications = array();  		$sql = 'SELECT n.* -			FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . " nt -			WHERE n.item_type = '" . $this->get_type() . "' -				AND n.item_parent_id = " . (int) self::get_item_parent_id($post) . ' +			FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt +			WHERE n.notification_type_id = ' . (int) $this->notification_type_id . ' +				AND n.item_parent_id = ' . (int) self::get_item_parent_id($post) . '  				AND n.notification_read = 0 -				AND nt.notification_type = n.item_type +				AND nt.notification_type_id = n.notification_type_id  				AND nt.notification_type_enabled = 1';  		$result = $this->db->sql_query($sql);  		while ($row = $this->db->sql_fetchrow($result)) @@ -216,7 +231,7 @@ class phpbb_notification_type_post extends phpbb_notification_type_base  		}  		else  		{ -			$username = $this->user_loader->get_username($this->get_data('poster_id'), 'no_profile'); +			$username = $this->user_loader->get_username($this->get_data('poster_id'), 'username');  		}  		return array( 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..0ed13f36fb 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']); @@ -121,11 +122,11 @@ class phpbb_notification_type_quote extends phpbb_notification_type_post  		// Try to find the users who already have been notified about replies and have not read the topic since and just update their notifications  		$update_notifications = array();  		$sql = 'SELECT n.* -			FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . " nt -			WHERE n.item_type = '" . $this->get_type() . "' -				AND n.item_parent_id = " . (int) self::get_item_parent_id($post) . ' +			FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt +			WHERE n.notification_type_id = ' . (int) $this->notification_type_id . ' +				AND n.item_parent_id = ' . (int) self::get_item_parent_id($post) . '  				AND n.notification_read = 0 -				AND nt.notification_type = n.item_type +				AND nt.notification_type_id = n.notification_type_id  				AND nt.notification_type_enabled = 1';  		$result = $this->db->sql_query($sql);  		while ($row = $this->db->sql_fetchrow($result)) @@ -153,10 +154,10 @@ class phpbb_notification_type_quote extends phpbb_notification_type_post  	{  		$old_notifications = array();  		$sql = 'SELECT n.user_id -			FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . " nt -			WHERE n.item_type = '" . $this->get_type() . "' -				AND n.item_id = " . self::get_item_id($post) . ' -				AND nt.notification_type = n.item_type +			FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt +			WHERE n.notification_type_id = ' . (int) $this->notification_type_id . ' +				AND n.item_id = ' . self::get_item_id($post) . ' +				AND nt.notification_type_id = n.notification_type_id  				AND nt.notification_type_enabled = 1';  		$result = $this->db->sql_query($sql);  		while ($row = $this->db->sql_fetchrow($result)) @@ -184,9 +185,9 @@ class phpbb_notification_type_quote extends phpbb_notification_type_post  		// Remove the necessary notifications  		if (!empty($remove_notifications))  		{ -			$sql = 'DELETE FROM ' . $this->notifications_table . " -				WHERE item_type = '" . $this->get_type() . "' -					AND item_id = " . self::get_item_id($post) . ' +			$sql = 'DELETE FROM ' . $this->notifications_table . ' +				WHERE notification_type_id = ' . (int) $this->notification_type_id . ' +					AND item_id = ' . self::get_item_id($post) . '  					AND ' . $this->db->sql_in_set('user_id', $remove_notifications);  			$this->db->sql_query($sql);  		} diff --git a/phpBB/includes/notification/type/topic.php b/phpBB/includes/notification/type/topic.php index 2549b29409..22436d3fb1 100644 --- a/phpBB/includes/notification/type/topic.php +++ b/phpBB/includes/notification/type/topic.php @@ -178,7 +178,7 @@ class phpbb_notification_type_topic extends phpbb_notification_type_base  		}  		else  		{ -			$username = $this->user_loader->get_username($this->get_data('poster_id'), 'no_profile'); +			$username = $this->user_loader->get_username($this->get_data('poster_id'), 'username');  		}  		return array( 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_mysql.php b/phpBB/includes/search/fulltext_mysql.php index e3ec56423e..2dd95a9680 100644 --- a/phpBB/includes/search/fulltext_mysql.php +++ b/phpBB/includes/search/fulltext_mysql.php @@ -163,9 +163,16 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base  			$engine = $info['Type'];  		} -		if ($engine != 'MyISAM') +		$fulltext_supported = +			$engine === 'MyISAM' || +			// FULLTEXT is supported on InnoDB since MySQL 5.6.4 according to +			// http://dev.mysql.com/doc/refman/5.6/en/innodb-storage-engine.html +			$engine === 'InnoDB' && +			phpbb_version_compare($this->db->sql_server_info(true), '5.6.4', '>='); + +		if (!$fulltext_supported)  		{ -			return $this->user->lang['FULLTEXT_MYSQL_NOT_MYISAM']; +			return $this->user->lang['FULLTEXT_MYSQL_NOT_SUPPORTED'];  		}  		$sql = 'SHOW VARIABLES diff --git a/phpBB/includes/search/fulltext_native.php b/phpBB/includes/search/fulltext_native.php index 2a9b552928..730c3a6c2d 100644 --- a/phpBB/includes/search/fulltext_native.php +++ b/phpBB/includes/search/fulltext_native.php @@ -1800,11 +1800,11 @@ class phpbb_search_fulltext_native extends phpbb_search_base  		</dl>  		<dl>  			<dt><label for="fulltext_native_min_chars">' . $this->user->lang['MIN_SEARCH_CHARS'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['MIN_SEARCH_CHARS_EXPLAIN'] . '</span></dt> -			<dd><input id="fulltext_native_min_chars" type="text" size="3" maxlength="3" name="config[fulltext_native_min_chars]" value="' . (int) $this->config['fulltext_native_min_chars'] . '" /></dd> +			<dd><input id="fulltext_native_min_chars" type="number" size="3" maxlength="3" min="0" max="255" name="config[fulltext_native_min_chars]" value="' . (int) $this->config['fulltext_native_min_chars'] . '" /></dd>  		</dl>  		<dl>  			<dt><label for="fulltext_native_max_chars">' . $this->user->lang['MAX_SEARCH_CHARS'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['MAX_SEARCH_CHARS_EXPLAIN'] . '</span></dt> -			<dd><input id="fulltext_native_max_chars" type="text" size="3" maxlength="3" name="config[fulltext_native_max_chars]" value="' . (int) $this->config['fulltext_native_max_chars'] . '" /></dd> +			<dd><input id="fulltext_native_max_chars" type="number" size="3" maxlength="3" min="0" max="255" name="config[fulltext_native_max_chars]" value="' . (int) $this->config['fulltext_native_max_chars'] . '" /></dd>  		</dl>  		<dl>  			<dt><label for="fulltext_native_common_thres">' . $this->user->lang['COMMON_WORD_THRESHOLD'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['COMMON_WORD_THRESHOLD_EXPLAIN'] . '</span></dt> diff --git a/phpBB/includes/search/fulltext_postgres.php b/phpBB/includes/search/fulltext_postgres.php index 8ccd27f43a..16369c3d95 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); @@ -946,11 +946,11 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base  		</dl>                  <dl>                          <dt><label for="fulltext_postgres_min_word_len">' . $this->user->lang['FULLTEXT_POSTGRES_MIN_WORD_LEN'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['FULLTEXT_POSTGRES_MIN_WORD_LEN_EXPLAIN'] . '</span></dt> -                        <dd><input id="fulltext_postgres_min_word_len" type="text" size="3" maxlength="3" name="config[fulltext_postgres_min_word_len]" value="' . (int) $this->config['fulltext_postgres_min_word_len'] . '" /></dd> +                        <dd><input id="fulltext_postgres_min_word_len" type="number" size="3" maxlength="3" min="0" max="255" name="config[fulltext_postgres_min_word_len]" value="' . (int) $this->config['fulltext_postgres_min_word_len'] . '" /></dd>                  </dl>                  <dl>                          <dt><label for="fulltext_postgres_max_word_len">' . $this->user->lang['FULLTEXT_POSTGRES_MAX_WORD_LEN'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['FULLTEXT_POSTGRES_MAX_WORD_LEN_EXPLAIN'] . '</span></dt> -                        <dd><input id="fulltext_postgres_max_word_len" type="text" size="3" maxlength="3" name="config[fulltext_postgres_max_word_len]" value="' . (int) $this->config['fulltext_postgres_max_word_len'] . '" /></dd> +                        <dd><input id="fulltext_postgres_max_word_len" type="number" size="3" maxlength="3" min="0" max="255" name="config[fulltext_postgres_max_word_len]" value="' . (int) $this->config['fulltext_postgres_max_word_len'] . '" /></dd>                  </dl>  		'; diff --git a/phpBB/includes/search/fulltext_sphinx.php b/phpBB/includes/search/fulltext_sphinx.php index 5d5e0ab8f9..2f7b236c78 100644 --- a/phpBB/includes/search/fulltext_sphinx.php +++ b/phpBB/includes/search/fulltext_sphinx.php @@ -258,13 +258,13 @@ class phpbb_search_fulltext_sphinx  		$config_object = new phpbb_search_sphinx_config($this->config_file_data);  		$config_data = array(  			'source source_phpbb_' . $this->id . '_main' => array( -				array('type',						$this->dbtype), +				array('type',						$this->dbtype . ' # mysql or pgsql'),  				// This config value sql_host needs to be changed incase sphinx and sql are on different servers -				array('sql_host',					$dbhost), +				array('sql_host',					$dbhost . ' # SQL server host sphinx connects to'),  				array('sql_user',					$dbuser),  				array('sql_pass',					$dbpasswd),  				array('sql_db',						$dbname), -				array('sql_port',					$dbport), +				array('sql_port',					$dbport . ' # optional, default is 3306 for mysql and 5432 for pgsql'),  				array('sql_query_pre',				'SET NAMES \'utf8\''),  				array('sql_query_pre',				'UPDATE ' . SPHINX_TABLE . ' SET max_doc_id = (SELECT MAX(post_id) FROM ' . POSTS_TABLE . ') WHERE counter_id = 1'),  				array('sql_query_range',			'SELECT MIN(post_id), MAX(post_id) FROM ' . POSTS_TABLE . ''), @@ -619,7 +619,7 @@ class phpbb_search_fulltext_sphinx  		$result_count = $result['total_found']; -		if ($start >= $result_count) +		if ($result_count && $start >= $result_count)  		{  			$start = floor(($result_count - 1) / $per_page) * $per_page; @@ -896,11 +896,11 @@ class phpbb_search_fulltext_sphinx  		</dl>  		<dl>  			<dt><label for="fulltext_sphinx_port">' . $this->user->lang['FULLTEXT_SPHINX_PORT'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['FULLTEXT_SPHINX_PORT_EXPLAIN'] . '</span></dt> -			<dd><input id="fulltext_sphinx_port" type="text" size="4" maxlength="10" name="config[fulltext_sphinx_port]" value="' . $this->config['fulltext_sphinx_port'] . '" /></dd> +			<dd><input id="fulltext_sphinx_port" type="number" size="4" maxlength="10" name="config[fulltext_sphinx_port]" value="' . $this->config['fulltext_sphinx_port'] . '" /></dd>  		</dl>  		<dl>  			<dt><label for="fulltext_sphinx_indexer_mem_limit">' . $this->user->lang['FULLTEXT_SPHINX_INDEXER_MEM_LIMIT'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['FULLTEXT_SPHINX_INDEXER_MEM_LIMIT_EXPLAIN'] . '</span></dt> -			<dd><input id="fulltext_sphinx_indexer_mem_limit" type="text" size="4" maxlength="10" name="config[fulltext_sphinx_indexer_mem_limit]" value="' . $this->config['fulltext_sphinx_indexer_mem_limit'] . '" /> ' . $this->user->lang['MIB'] . '</dd> +			<dd><input id="fulltext_sphinx_indexer_mem_limit" type="number" size="4" maxlength="10" name="config[fulltext_sphinx_indexer_mem_limit]" value="' . $this->config['fulltext_sphinx_indexer_mem_limit'] . '" /> ' . $this->user->lang['MIB'] . '</dd>  		</dl>  		<dl>  			<dt><label for="fulltext_sphinx_config_file">' . $this->user->lang['FULLTEXT_SPHINX_CONFIG_FILE'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['FULLTEXT_SPHINX_CONFIG_FILE_EXPLAIN'] . '</span></dt> diff --git a/phpBB/includes/search/sphinx/config_variable.php b/phpBB/includes/search/sphinx/config_variable.php index 35abe281cb..2c1d35a49c 100644 --- a/phpBB/includes/search/sphinx/config_variable.php +++ b/phpBB/includes/search/sphinx/config_variable.php @@ -75,6 +75,6 @@ class phpbb_search_sphinx_config_variable  	*/  	function to_string()  	{ -		return "\t" . $this->name . ' = ' . str_replace("\n", "\\\n", $this->value) . ' ' . $this->comment . "\n"; +		return "\t" . $this->name . ' = ' . str_replace("\n", " \\\n", $this->value) . ' ' . $this->comment . "\n";  	}  } diff --git a/phpBB/includes/session.php b/phpBB/includes/session.php index 6bc71da0c1..66bf053f7d 100644 --- a/phpBB/includes/session.php +++ b/phpBB/includes/session.php @@ -207,7 +207,7 @@ class phpbb_session  	function session_begin($update_session_page = true)  	{  		global $phpEx, $SID, $_SID, $_EXTRA_URL, $db, $config, $phpbb_root_path; -		global $request; +		global $request, $phpbb_container;  		// Give us some basic information  		$this->time_now				= time(); @@ -402,15 +402,12 @@ class phpbb_session  					// Check whether the session is still valid if we have one  					$method = basename(trim($config['auth_method'])); -					include_once($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx); -					$method = 'validate_session_' . $method; -					if (function_exists($method)) +					$provider = $phpbb_container->get('auth.provider.' . $method); +					$ret = $provider->validate_session($this->data); +					if ($ret !== null && !$ret)  					{ -						if (!$method($this->data)) -						{ -							$session_expired = true; -						} +						$session_expired = true;  					}  					if (!$session_expired) @@ -504,7 +501,7 @@ class phpbb_session  	*/  	function session_create($user_id = false, $set_admin = false, $persist_login = false, $viewonline = true)  	{ -		global $SID, $_SID, $db, $config, $cache, $phpbb_root_path, $phpEx; +		global $SID, $_SID, $db, $config, $cache, $phpbb_root_path, $phpEx, $phpbb_container;  		$this->data = array(); @@ -568,18 +565,14 @@ class phpbb_session  		}  		$method = basename(trim($config['auth_method'])); -		include_once($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx); -		$method = 'autologin_' . $method; -		if (function_exists($method)) -		{ -			$this->data = $method(); +		$provider = $phpbb_container->get('auth.provider.' . $method); +		$this->data = $provider->autologin(); -			if (sizeof($this->data)) -			{ -				$this->cookie_data['k'] = ''; -				$this->cookie_data['u'] = $this->data['user_id']; -			} +		if (sizeof($this->data)) +		{ +			$this->cookie_data['k'] = ''; +			$this->cookie_data['u'] = $this->data['user_id'];  		}  		// If we're presented with an autologin key we'll join against it. @@ -884,7 +877,7 @@ class phpbb_session  	*/  	function session_kill($new_session = true)  	{ -		global $SID, $_SID, $db, $config, $phpbb_root_path, $phpEx; +		global $SID, $_SID, $db, $config, $phpbb_root_path, $phpEx, $phpbb_container;  		$sql = 'DELETE FROM ' . SESSIONS_TABLE . "  			WHERE session_id = '" . $db->sql_escape($this->session_id) . "' @@ -893,13 +886,9 @@ class phpbb_session  		// Allow connecting logout with external auth method logout  		$method = basename(trim($config['auth_method'])); -		include_once($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx); -		$method = 'logout_' . $method; -		if (function_exists($method)) -		{ -			$method($this->data, $new_session); -		} +		$provider = $phpbb_container->get('auth.provider.' . $method); +		$provider->logout($this->data, $new_session);  		if ($this->data['user_id'] != ANONYMOUS)  		{ 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/compile.php b/phpBB/includes/template/compile.php index fcdaf7abda..76cb3011df 100644 --- a/phpBB/includes/template/compile.php +++ b/phpBB/includes/template/compile.php @@ -33,6 +33,13 @@ class phpbb_template_compile  	private $filter_params;  	/** +	* Array of default parameters +	* +	* @var array +	*/ +	private $default_filter_params; + +	/**  	* Constructor.  	*  	* @param bool $allow_php Whether PHP code will be allowed in templates (inline PHP code, PHP tag and INCLUDEPHP tag) @@ -44,18 +51,40 @@ class phpbb_template_compile  	*/  	public function __construct($allow_php, $style_names, $locator, $phpbb_root_path, $extension_manager = null, $user = null)  	{ -		$this->filter_params = array( -			'allow_php'	=> $allow_php, -			'style_names'	=> $style_names, -			'locator'	=> $locator, +		$this->filter_params = $this->default_filter_params = array( +			'allow_php'			=> $allow_php, +			'style_names'		=> $style_names, +			'locator'			=> $locator,  			'phpbb_root_path'	=> $phpbb_root_path,  			'extension_manager'	=> $extension_manager, -			'user'          => $user, +			'user'          	=> $user,  			'template_compile'	=> $this, +			'cleanup'			=> true,  		);  	}  	/** +	* Set filter parameters +	* +	* @param array $params Array of parameters (will be merged onto $this->filter_params) +	*/ +	public function set_filter_params($params) +	{ +		$this->filter_params = array_merge( +			$this->filter_params, +			$params +		); +	} + +	/** +	* Reset filter parameters to their default settings +	*/ +	public function reset_filter_params() +	{ +		$this->filter_params = $this->default_filter_params; +	} + +	/**  	* Compiles template in $source_file and writes compiled template to  	* cache directory  	* diff --git a/phpBB/includes/template/filter.php b/phpBB/includes/template/filter.php index f73ad28ba1..1c0a56c9f5 100644 --- a/phpBB/includes/template/filter.php +++ b/phpBB/includes/template/filter.php @@ -76,6 +76,14 @@ class phpbb_template_filter extends php_user_filter  	private $allow_php;  	/** +	* Whether cleanup will be performed on resulting code, see compile() +	* (Preserve whitespace) +	* +	* @var bool +	*/ +	private $cleanup = true; + +	/**  	* Resource locator.  	*  	* @var phpbb_template_locator @@ -183,6 +191,7 @@ class phpbb_template_filter extends php_user_filter  		$this->phpbb_root_path = $this->params['phpbb_root_path'];  		$this->style_names = $this->params['style_names'];  		$this->extension_manager = $this->params['extension_manager']; +		$this->cleanup = $this->params['cleanup'];  		if (isset($this->params['user']))  		{  			$this->user = $this->params['user']; @@ -223,9 +232,16 @@ class phpbb_template_filter extends php_user_filter  			$data = preg_replace('~<!-- ENDPHP -->.*?$~', '', $data);  		} -		/* +		if ($this->cleanup) +		{ +			/* +  			Preserve whitespace. -			PHP removes a newline after the closing tag (if it's there). This is by design. +			PHP removes a newline after the closing tag (if it's there). +			This is by design: + +			http://www.php.net/manual/en/language.basic-syntax.phpmode.php +			http://www.php.net/manual/en/language.basic-syntax.instruction-separation.php  			Consider the following template: @@ -234,24 +250,27 @@ class phpbb_template_filter extends php_user_filter  				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. +			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 aren't the only statement on a line. -			It will NOT preserve newlines at the end of statements in the above examle. +			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 --> +			*/ -		*/ +			$data = preg_replace('~(?<!^)(<\?php.+(?<!/\*\*/)\?>)$~m', "$1\n", $data); +			$data = str_replace('/**/?>', "?>\n", $data); +			$data = str_replace('?><?php', '', $data); +		} -		$data = preg_replace('~(?<!^)(<\?php.+(?<!/\*\*/)\?>)$~m', "$1\n", $data); -		$data = str_replace('/**/?>', "?>\n", $data); -		$data = str_replace('?><?php', '', $data);  		return $data;  	} @@ -329,6 +348,10 @@ class phpbb_template_filter extends php_user_filter  				return '<?php ' . $this->compile_tag_define($matches[2], false) . ' ?>';  			break; +			case 'ENDDEFINE': +				return '<?php ' . $this->compile_tag_enddefine() . ' ?>'; +			break; +  			case 'INCLUDE':  				return '<?php ' . $this->compile_tag_include($matches[2]) . ' ?>';  			break; @@ -452,6 +475,7 @@ class phpbb_template_filter extends php_user_filter  	*/  	private function compile_var_tags(&$text_blocks)  	{ +		$is_expr = null;  		$text_blocks = $this->get_varref($text_blocks, $is_expr);  		$lang_replaced = $this->compile_language_tags($text_blocks); @@ -833,6 +857,16 @@ class phpbb_template_filter extends php_user_filter  		$match = array();  		preg_match('#^((?:' . self::REGEX_NS . '\.)+)?\$(?=[A-Z])([A-Z0-9_\-]*)(?: = (.*?))?$#', $tag_args, $match); +		if (!empty($match[2]) && !isset($match[3]) && $op) +		{ +			// DEFINE tag with ENDDEFINE +			$array = "\$_tpldata['DEFINE']['.vars']"; +			$code = 'ob_start(); '; +			$code .= "if (!isset($array)) { $array = array(); } "; +			$code .= "{$array}[] = '{$match[2]}'"; +			return $code; +		} +  		if (empty($match[2]) || (!isset($match[3]) && $op))  		{  			return ''; @@ -843,12 +877,37 @@ class phpbb_template_filter extends php_user_filter  			return 'unset(' . (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ');';  		} -		$parsed_statement = implode(' ', $this->compile_expression($match[3])); +		/* +		* Define tags that contain template variables (enclosed in curly brackets) +		* need to be treated differently. +		*/ +		if (substr($match[3], 1, 1) == '{' && substr($match[3], -2, 1) == '}') +		{ +			$parsed_statement = implode(' ', $this->compile_expression(substr($match[3], 2, -2))); +		} +		else +		{ +			$parsed_statement = implode(' ', $this->compile_expression($match[3])); +		}  		return (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ' = ' . $parsed_statement . ';';  	}  	/** +	* Compile ENDDEFINE tag +	* +	* @return string compiled template code +	*/ +	private function compile_tag_enddefine() +	{ +		$array = "\$_tpldata['DEFINE']['.vars']"; +		$code = "if (!isset($array) || !sizeof($array)) { trigger_error('ENDDEFINE tag without DEFINE in ' . basename(__FILE__), E_USER_ERROR); }"; +		$code .= "\$define_var = array_pop($array); "; +		$code .= "\$_tpldata['DEFINE']['.'][\$define_var] = ob_get_clean();"; +		return $code; +	} + +	/**  	* Compile INCLUDE tag  	*  	* @param string $tag_args Expression given with INCLUDE in source template @@ -950,8 +1009,14 @@ class phpbb_template_filter extends php_user_filter  			$all_compiled = '';  			foreach ($files as $file)  			{ +				$this->template_compile->set_filter_params(array( +					'cleanup'	=> false, +				)); +  				$compiled = $this->template_compile->compile_file($file); +				$this->template_compile->reset_filter_params(); +  				if ($compiled === false)  				{  					if ($this->user) 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..aada0525a8 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']), @@ -417,9 +416,11 @@ class ucp_groups  				if ($group_id)  				{ -					$sql = 'SELECT * -						FROM ' . GROUPS_TABLE . " -						WHERE group_id = $group_id"; +					$sql = 'SELECT g.*, t.teampage_position AS group_teampage +						FROM ' . GROUPS_TABLE . ' g +						LEFT JOIN ' . TEAMPAGE_TABLE . ' t +							ON (t.group_id = g.group_id) +						WHERE g.group_id = ' . $group_id;  					$result = $db->sql_query($sql);  					$group_row = $db->sql_fetchrow($result);  					$db->sql_freeresult($result); @@ -515,6 +516,8 @@ class ucp_groups  								'receive_pm'	=> isset($_REQUEST['group_receive_pm']) ? 1 : 0,  								'message_limit'	=> request_var('group_message_limit', 0),  								'max_recipients'=> request_var('group_max_recipients', 0), +								'legend'	=> $group_row['group_legend'], +								'teampage'	=> $group_row['group_teampage'],  							);  							if ($config['allow_avatar']) @@ -548,6 +551,9 @@ class ucp_groups  									$submit_ary['avatar_width'] = 0;  									$submit_ary['avatar_height'] = 0;  								} + +								// Merge any avatars errors into the primary error array +								$error = array_merge($error, $phpbb_avatar_manager->localize_errors($user, $avatar_error));  							}  							if (!check_form_key('ucp_groups')) @@ -555,11 +561,21 @@ class ucp_groups  								$error[] = $user->lang['FORM_INVALID'];  							} +							// Validate submitted colour value +							if ($colour_error = validate_data($submit_ary, array('colour'	=> array('hex_colour', true)))) +							{ +								// Replace "error" string with its real, localised form +								$error = array_merge($error, array_map(array(&$user, 'lang'), $colour_error)); +							} +  							if (!sizeof($error))  							{  								// Only set the rank, colour, etc. if it's changed or if we're adding a new  								// group. This prevents existing group members being updated if no changes  								// were made. +								// However there are some attributes that need to be set everytime, +								// otherwise the group gets removed from the feature. +								$set_attributes = array('legend', 'teampage');  								$group_attributes = array();  								$test_variables = array( @@ -571,13 +587,14 @@ class ucp_groups  									'avatar_height'	=> 'int',  									'receive_pm'	=> 'int',  									'legend'		=> 'int', +									'teampage'		=> 'int',  									'message_limit'	=> 'int',  									'max_recipients'=> 'int',  								);  								foreach ($test_variables as $test => $type)  								{ -									if (isset($submit_ary[$test]) && ($action == 'add' || $group_row['group_' . $test] != $submit_ary[$test] || isset($group_attributes['group_avatar']) && strpos($test, 'avatar') === 0)) +									if (isset($submit_ary[$test]) && ($action == 'add' || $group_row['group_' . $test] != $submit_ary[$test] || isset($group_attributes['group_avatar']) && strpos($test, 'avatar') === 0 || in_array($test, $set_attributes)))  									{  										settype($submit_ary[$test], $type);  										$group_attributes['group_' . $test] = $group_row['group_' . $test] = $submit_ary[$test]; @@ -587,6 +604,7 @@ class ucp_groups  								if (!($error = group_create($group_id, $group_type, $group_name, $group_desc, $group_attributes, $allow_desc_bbcode, $allow_desc_urls, $allow_desc_smilies)))  								{  									$cache->destroy('sql', GROUPS_TABLE); +									$cache->destroy('sql', TEAMPAGE_TABLE);  									$message = ($action == 'edit') ? 'GROUP_UPDATED' : 'GROUP_CREATED';  									trigger_error($user->lang[$message] . $return_page); @@ -673,8 +691,11 @@ class ucp_groups  							}  						} -						// Merge any avatars errors into the primary error array -						$error = array_merge($error, $phpbb_avatar_manager->localize_errors($user, $avatar_error)); +						if (isset($phpbb_avatar_manager) && !$update) +						{ +							// Merge any avatars errors into the primary error array +							$error = array_merge($error, $phpbb_avatar_manager->localize_errors($user, $avatar_error)); +						}  						$template->assign_vars(array(  							'S_EDIT'			=> true, 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 c57aec00a0..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']), @@ -457,6 +456,7 @@ class ucp_register  			'S_LANG_OPTIONS'	=> language_select($data['lang']),  			'S_TZ_OPTIONS'			=> $timezone_selects['tz_select'],  			'S_TZ_DATE_OPTIONS'		=> $timezone_selects['tz_dates'], +			'S_TZ_PRESELECT'	=> !$submit,  			'S_CONFIRM_REFRESH'	=> ($config['enable_confirm'] && $config['confirm_refresh']) ? true : false,  			'S_REGISTRATION'	=> true,  			'S_COPPA'			=> $coppa, 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));  | 
