diff options
author | Joas Schilling <nickvergessen@gmx.de> | 2013-07-11 11:41:48 +0200 |
---|---|---|
committer | Joas Schilling <nickvergessen@gmx.de> | 2013-07-11 11:41:48 +0200 |
commit | bdb7ec0ceb3210955b2457bfe6fe469b1781d8d6 (patch) | |
tree | 4a6cd814385ae839f577866d3c45175f9ea4928e /phpBB/includes | |
parent | d41cf293e1609be9d0cc08e5ccd37947481e61ca (diff) | |
parent | 2fcae1ca16d096d2839b487e8c1bcbe0f313d91f (diff) | |
download | forums-bdb7ec0ceb3210955b2457bfe6fe469b1781d8d6.tar forums-bdb7ec0ceb3210955b2457bfe6fe469b1781d8d6.tar.gz forums-bdb7ec0ceb3210955b2457bfe6fe469b1781d8d6.tar.bz2 forums-bdb7ec0ceb3210955b2457bfe6fe469b1781d8d6.tar.xz forums-bdb7ec0ceb3210955b2457bfe6fe469b1781d8d6.zip |
Merge remote-tracking branch 'phpbb/develop' into feature/softdelete-1-permission
* phpbb/develop: (704 commits)
[ticket/11630] Improvements to the PHP lint pre-commit hook
[feature/auth-refactor] Move auth providers to separate directory
[ticket/11619] Use HTTP/1.0 because of lack of chunked-encoding handling.
[ticket/11619] Some tests for get_remote_file().
[ticket/11617] Remove spaces and tabs from empty lines
[ticket/11617] Missing U_ACTION in acp_captcha.php
[feature/auth-refactor] Fix code style issue
[feature/auth-refactor] Fix comment grammar
[feature/auth-refactor] Fix the actual cause of test failures
[ticket/10838] Fix URL for wiki and remove irrelevant line
[ticket/10838] Remove php 5.4 and builtin server references
[ticket/10838] Fix missing data
[ticket/10838] separate database used mentioned in unit tests
[ticket/11585] Make $auth_admin class property
[feature/auth-refactor] A possible fix for the functional test failures
[ticket/11566] Subsilver template error displayed after table headers
[ticket/11566] Remove extra pair of brackets from conditional statement
[ticket/11566] Check that guest doesn't have reporting permission by default
[ticket/11566] Add captcha to report post template in subsilver
[ticket/11566] Use the new constant CONFIRM_REPORT for captcha init
...
Conflicts:
phpBB/docs/sphinx.sample.conf
phpBB/feed.php
phpBB/styles/prosilver/template/search_results.html
phpBB/styles/prosilver/template/viewforum_body.html
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)); |