diff options
62 files changed, 840 insertions, 205 deletions
diff --git a/.travis.yml b/.travis.yml index b7b17f2f19..e68ba5f501 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,9 +22,12 @@ matrix: env: DB=mysqli - php: 5.6 env: DB=mysqli + - php: 7.0 + env: DB=mysqli - php: hhvm env: DB=mysqli allow_failures: + - php: 7.0 - php: hhvm fast_finish: true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..6996ca22d3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,6 @@ +## CONTRIBUTE + +1. [Create an account on phpBB.com](http://www.phpbb.com/community/ucp.php?mode=register) +2. [Create a ticket (unless there already is one)](http://tracker.phpbb.com/secure/CreateIssue!default.jspa) +3. Read our [Coding guidelines](https://wiki.phpbb.com/Coding_guidelines) and [Git Contribution Guidelines](http://wiki.phpbb.com/Git); if you're new to git, also read [the introduction guide](http://wiki.phpbb.com/display/DEV/Working+with+Git) +4. Send us a pull request diff --git a/build/sami-all.conf.php b/build/sami-all.conf.php index 68350fee8f..fb1a269206 100644 --- a/build/sami-all.conf.php +++ b/build/sami-all.conf.php @@ -18,13 +18,13 @@ $config['versions'] = Sami\Version\GitVersionCollection::create(__DIR__ . '/../' This would be nice, but currently causes various problems that need debugging. ->addFromTags('release-3.0.*') - ->add('develop-olympus', '3.0-next (olympus)') + ->add('3.0.x', '3.0-next (olympus)') ->addFromTags('release-3.1.*') - ->add('develop-ascraeus', '3.1-next (ascraeus)') - ->add('develop') + ->add('3.1.x', '3.1-next (ascraeus)') + ->add('master') */ - ->add('develop-olympus') - ->add('develop-ascraeus') + ->add('3.0.x') + ->add('3.1.x') ; return new Sami\Sami($iterator, $config); diff --git a/composer.phar b/composer.phar Binary files differindex b00eef5a3e..3481b599b7 100755 --- a/composer.phar +++ b/composer.phar diff --git a/phpBB/adm/style/acp_users_profile.html b/phpBB/adm/style/acp_users_profile.html index d32348ff1c..573534fc95 100644 --- a/phpBB/adm/style/acp_users_profile.html +++ b/phpBB/adm/style/acp_users_profile.html @@ -2,6 +2,7 @@ <fieldset> <legend>{L_USER_PROFILE}</legend> + <!-- EVENT acp_users_profile_before --> <dl> <dt><label for="jabber">{L_UCP_JABBER}{L_COLON}</label></dt> <dd><input type="email" id="jabber" name="jabber" value="{JABBER}" /></dd> @@ -10,6 +11,7 @@ <dt><label for="birthday">{L_BIRTHDAY}{L_COLON}</label><br /><span>{L_BIRTHDAY_EXPLAIN}</span></dt> <dd>{L_DAY}{L_COLON} <select id="birthday" name="bday_day">{S_BIRTHDAY_DAY_OPTIONS}</select> {L_MONTH}{L_COLON} <select name="bday_month">{S_BIRTHDAY_MONTH_OPTIONS}</select> {L_YEAR}{L_COLON} <select name="bday_year">{S_BIRTHDAY_YEAR_OPTIONS}</select></dd> </dl> + <!-- EVENT acp_users_profile_after --> </fieldset> <!-- IF .profile_fields --> @@ -26,7 +28,7 @@ <!-- END profile_fields --> </fieldset> <!-- ENDIF --> - + <!-- EVENT acp_users_profile_custom_after --> <fieldset class="quick"> <input class="button1" type="submit" name="update" value="{L_SUBMIT}" /> {S_FORM_TOKEN} diff --git a/phpBB/adm/style/install_header.html b/phpBB/adm/style/install_header.html index c818a4fc6d..6f7f129d39 100644 --- a/phpBB/adm/style/install_header.html +++ b/phpBB/adm/style/install_header.html @@ -2,7 +2,7 @@ <html dir="{S_CONTENT_DIRECTION}" lang="{S_USER_LANG}"> <head> <meta charset="utf-8"> -<meta name="viewport" content="width=device-width" /> +<meta name="viewport" content="width=device-width, initial-scale=1" /> <!-- IF META -->{META}<!-- ENDIF --> <title>{PAGE_TITLE}</title> diff --git a/phpBB/adm/style/install_update_diff.html b/phpBB/adm/style/install_update_diff.html index 150ef37e0e..5f80084705 100644 --- a/phpBB/adm/style/install_update_diff.html +++ b/phpBB/adm/style/install_update_diff.html @@ -2,7 +2,7 @@ <html dir="{S_CONTENT_DIRECTION}" lang="{S_USER_LANG}"> <head> <meta charset="utf-8"> -<meta name="viewport" content="width=device-width" /> +<meta name="viewport" content="width=device-width, initial-scale=1" /> <!-- IF META -->{META}<!-- ENDIF --> <title>{PAGE_TITLE}</title> diff --git a/phpBB/adm/style/overall_header.html b/phpBB/adm/style/overall_header.html index f1f7eee282..ada88edff2 100644 --- a/phpBB/adm/style/overall_header.html +++ b/phpBB/adm/style/overall_header.html @@ -2,7 +2,7 @@ <html dir="{S_CONTENT_DIRECTION}" lang="{S_USER_LANG}"> <head> <meta charset="utf-8"> -<meta name="viewport" content="width=device-width" /> +<meta name="viewport" content="width=device-width, initial-scale=1" /> <!-- IF META -->{META}<!-- ENDIF --> <title>{PAGE_TITLE}</title> diff --git a/phpBB/adm/style/simple_header.html b/phpBB/adm/style/simple_header.html index d0b9bf62ed..f62a7a900e 100644 --- a/phpBB/adm/style/simple_header.html +++ b/phpBB/adm/style/simple_header.html @@ -2,6 +2,7 @@ <html dir="{S_CONTENT_DIRECTION}" lang="{S_USER_LANG}"> <head> <meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1" /> <!-- IF META -->{META}<!-- ENDIF --> <title>{PAGE_TITLE}</title> diff --git a/phpBB/assets/javascript/core.js b/phpBB/assets/javascript/core.js index 6481a2e113..806db7d35f 100644 --- a/phpBB/assets/javascript/core.js +++ b/phpBB/assets/javascript/core.js @@ -408,7 +408,9 @@ phpbb.ajaxify = function(options) { $elements.find('input:submit').click(function () { var $this = $(this); - $this.siblings('[data-clicked]').removeAttr('data-clicked'); + // Remove data-clicked attribute from any submit button of form + $this.parents('form:first').find('input:submit[data-clicked]').removeAttr('data-clicked'); + $this.attr('data-clicked', 'true'); }); } diff --git a/phpBB/docs/CREDITS.txt b/phpBB/docs/CREDITS.txt index 5c17787495..e3f2b325e8 100644 --- a/phpBB/docs/CREDITS.txt +++ b/phpBB/docs/CREDITS.txt @@ -24,9 +24,10 @@ phpBB Lead Developer: naderman (Nils Adermann) phpBB Developers: bantu (Andreas Fischer) dhruv.goel92 (Dhruv Goel) + Elsensee (Oliver Schramm) marc1706 (Marc Alexander) nickvergessen (Joas Schilling) - nicofuma (Tristan Darricau) + Nicofuma (Tristan Darricau) prototech (Cesar Gallegos) For a list of phpBB Team members, please see: diff --git a/phpBB/docs/events.md b/phpBB/docs/events.md index 2926598aec..a76da95f7b 100644 --- a/phpBB/docs/events.md +++ b/phpBB/docs/events.md @@ -177,6 +177,27 @@ acp_ranks_list_header_before * Purpose: Add content after the last header-column (but before the action column) in the ranks list in the ACP +acp_users_profile_before +=== +* Locations: + + adm/style/acp_users_profile.html +* Since: 3.1.4-RC1 +* Purpose: Add content before the profile details when editing a user in the ACP + +acp_users_profile_after +=== +* Locations: + + adm/style/acp_users_profile.html +* Since: 3.1.4-RC1 +* Purpose: Add content after the profile details but before the custom profile fields when editing a user in the ACP + +acp_users_profile_custom_after +=== +* Locations: + + adm/style/acp_users_profile.html +* Since: 3.1.4-RC1 +* Purpose: Add content after the the custom profile fields when editing a user in the ACP + acp_simple_footer_after === * Location: adm/style/simple_footer.html @@ -819,6 +840,14 @@ overall_header_head_append * Since: 3.1.0-a1 * Purpose: Add asset calls directly before the `</head>` tag +overall_header_navbar_before +=== +* Locations: + + styles/prosilver/template/overall_header.html + + styles/subsilver2/template/overall_header.html +* Since: 3.1.4-RC1 +* Purpose: Add content before the navigation bar + overall_header_navigation_append === * Locations: @@ -859,6 +888,13 @@ overall_header_page_body_before * Since: 3.1.0-b3 * Purpose: Add content after the page-header, but before the page-body +overall_header_searchbox_before +=== +* Locations: + + styles/prosilver/template/overall_header.html +* Since: 3.1.4-RC1 +* Purpose: Add content before the search box in the header + overall_header_stylesheets_after === * Locations: diff --git a/phpBB/includes/acp/acp_attachments.php b/phpBB/includes/acp/acp_attachments.php index 2873b48fa4..67fba1094d 100644 --- a/phpBB/includes/acp/acp_attachments.php +++ b/phpBB/includes/acp/acp_attachments.php @@ -153,7 +153,7 @@ class acp_attachments '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: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' => 'path', 'type' => 'text:20:200', 'explain' => true, 'append' => ' <span>[ <a href="' . $this->u_action . '&action=imgmagick">' . $user->lang['SEARCH_IMAGICK'] . '</a> ]</span>'), + 'img_imagick' => array('lang' => 'IMAGICK_PATH', 'validate' => 'absolute_path', '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: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']), ) diff --git a/phpBB/includes/acp/acp_extensions.php b/phpBB/includes/acp/acp_extensions.php index 89fdc8b863..0c9bc0deab 100644 --- a/phpBB/includes/acp/acp_extensions.php +++ b/phpBB/includes/acp/acp_extensions.php @@ -174,11 +174,6 @@ class acp_extensions trigger_error($user->lang['EXTENSION_NOT_ENABLEABLE'] . adm_back_link($this->u_action), E_USER_WARNING); } - if ($phpbb_extension_manager->is_enabled($ext_name)) - { - redirect($this->u_action); - } - try { while ($phpbb_extension_manager->enable_step($ext_name)) diff --git a/phpBB/includes/acp/acp_search.php b/phpBB/includes/acp/acp_search.php index eddc6871f8..9ff999567a 100644 --- a/phpBB/includes/acp/acp_search.php +++ b/phpBB/includes/acp/acp_search.php @@ -449,7 +449,6 @@ class acp_search $search = null; $error = false; - $search_options = ''; foreach ($search_types as $type) { if ($this->init_search($type, $search, $error) || !method_exists($search, 'index_created')) diff --git a/phpBB/includes/acp/acp_users.php b/phpBB/includes/acp/acp_users.php index 3c957a7093..8c17fb6311 100644 --- a/phpBB/includes/acp/acp_users.php +++ b/phpBB/includes/acp/acp_users.php @@ -1414,6 +1414,18 @@ class acp_users $error[] = 'FORM_INVALID'; } + /** + * Validate profile data in ACP before submitting to the database + * + * @event core.acp_users_profile_validate + * @var bool submit Flag indicating if submit button has been pressed + * @var array data Array with user profile data + * @var array error Array with the form errors + * @since 3.1.4-RC1 + */ + $vars = array('submit', 'data', 'error'); + extract($phpbb_dispatcher->trigger_event('core.acp_users_profile_validate', compact($vars))); + if (!sizeof($error)) { $sql_ary = array( @@ -1429,9 +1441,10 @@ class acp_users * @var array data Array with user profile data * @var int user_id The user id * @var array user_row Array with the full user data + * @var array sql_ary Array with sql data * @since 3.1.4-RC1 */ - $vars = array('cp_data', 'data', 'user_id', 'user_row'); + $vars = array('cp_data', 'data', 'user_id', 'user_row', 'sql_ary'); extract($phpbb_dispatcher->trigger_event('core.acp_users_profile_modify_sql_ary', compact($vars))); $sql = 'UPDATE ' . USERS_TABLE . ' diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php index acb4690a48..573df9e55d 100644 --- a/phpBB/includes/functions.php +++ b/phpBB/includes/functions.php @@ -2920,19 +2920,6 @@ function login_box($redirect = '', $l_explain = '', $l_success = '', $admin = fa // Special cases... determine switch ($result['status']) { - case LOGIN_ERROR_ATTEMPTS: - - $captcha = $phpbb_container->get('captcha.factory')->get_instance($config['captcha_plugin']); - $captcha->init(CONFIRM_LOGIN); - // $captcha->reset(); - - $template->assign_vars(array( - 'CAPTCHA_TEMPLATE' => $captcha->get_template(), - )); - - $err = $user->lang[$result['error_msg']]; - break; - case LOGIN_ERROR_PASSWORD_CONVERT: $err = sprintf( $user->lang[$result['error_msg']], @@ -2943,6 +2930,17 @@ function login_box($redirect = '', $l_explain = '', $l_success = '', $admin = fa ); break; + case LOGIN_ERROR_ATTEMPTS: + + $captcha = $phpbb_container->get('captcha.factory')->get_instance($config['captcha_plugin']); + $captcha->init(CONFIRM_LOGIN); + // $captcha->reset(); + + $template->assign_vars(array( + 'CAPTCHA_TEMPLATE' => $captcha->get_template(), + )); + // no break; + // Username, password, etc... default: $err = $user->lang[$result['error_msg']]; diff --git a/phpBB/includes/functions_acp.php b/phpBB/includes/functions_acp.php index e30c6da505..a53a54368e 100644 --- a/phpBB/includes/functions_acp.php +++ b/phpBB/includes/functions_acp.php @@ -550,6 +550,9 @@ function validate_config_vars($config_vars, &$cfg_array, &$error) $cfg_array[$config_name] = trim($destination); + // Absolute file path + case 'absolute_path': + case 'absolute_path_writable': // Path being relative (still prefixed by phpbb_root_path), but with the ability to escape the root dir... case 'path': case 'wpath': @@ -568,20 +571,22 @@ function validate_config_vars($config_vars, &$cfg_array, &$error) break; } - if (!file_exists($phpbb_root_path . $cfg_array[$config_name])) + $path = in_array($config_definition['validate'], array('wpath', 'path', 'rpath', 'rwpath')) ? $phpbb_root_path . $cfg_array[$config_name] : $cfg_array[$config_name]; + + if (!file_exists($path)) { $error[] = sprintf($user->lang['DIRECTORY_DOES_NOT_EXIST'], $cfg_array[$config_name]); } - if (file_exists($phpbb_root_path . $cfg_array[$config_name]) && !is_dir($phpbb_root_path . $cfg_array[$config_name])) + if (file_exists($path) && !is_dir($path)) { $error[] = sprintf($user->lang['DIRECTORY_NOT_DIR'], $cfg_array[$config_name]); } // Check if the path is writable - if ($config_definition['validate'] == 'wpath' || $config_definition['validate'] == 'rwpath') + if ($config_definition['validate'] == 'wpath' || $config_definition['validate'] == 'rwpath' || $config_definition['validate'] === 'absolute_path_writable') { - if (file_exists($phpbb_root_path . $cfg_array[$config_name]) && !phpbb_is_writable($phpbb_root_path . $cfg_array[$config_name])) + if (file_exists($path) && !phpbb_is_writable($path)) { $error[] = sprintf($user->lang['DIRECTORY_NOT_WRITABLE'], $cfg_array[$config_name]); } diff --git a/phpBB/includes/functions_admin.php b/phpBB/includes/functions_admin.php index b016659541..79f9db2f3f 100644 --- a/phpBB/includes/functions_admin.php +++ b/phpBB/includes/functions_admin.php @@ -618,7 +618,7 @@ function move_posts($post_ids, $topic_id, $auto_sync = true) */ function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_sync = true, $call_delete_posts = true) { - global $db, $config, $phpbb_container; + global $db, $config, $phpbb_container, $phpbb_dispatcher; $approved_topics = 0; $forum_ids = $topic_ids = array(); @@ -672,6 +672,20 @@ function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_s $table_ary = array(BOOKMARKS_TABLE, TOPICS_TRACK_TABLE, TOPICS_POSTED_TABLE, POLL_VOTES_TABLE, POLL_OPTIONS_TABLE, TOPICS_WATCH_TABLE, TOPICS_TABLE); + /** + * Perform additional actions before topic(s) deletion + * + * @event core.delete_topics_before_query + * @var array table_ary Array of tables from which all rows will be deleted that hold a topic_id occuring in topic_ids + * @var array topic_ids Array of topic ids to delete + * @since 3.1.4-RC1 + */ + $vars = array( + 'table_ary', + 'topic_ids', + ); + extract($phpbb_dispatcher->trigger_event('core.delete_topics_before_query', compact($vars))); + foreach ($table_ary as $table) { $sql = "DELETE FROM $table @@ -680,6 +694,18 @@ function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_s } unset($table_ary); + /** + * Perform additional actions after topic(s) deletion + * + * @event core.delete_topics_after_query + * @var array topic_ids Array of topic ids that were deleted + * @since 3.1.4-RC1 + */ + $vars = array( + 'topic_ids', + ); + extract($phpbb_dispatcher->trigger_event('core.delete_topics_after_query', compact($vars))); + $moved_topic_ids = array(); // update the other forums diff --git a/phpBB/includes/functions_mcp.php b/phpBB/includes/functions_mcp.php index eb4fc6d44b..ed96dcf338 100644 --- a/phpBB/includes/functions_mcp.php +++ b/phpBB/includes/functions_mcp.php @@ -423,28 +423,6 @@ function phpbb_mcp_sorting($mode, &$sort_days, &$sort_key, &$sort_dir, &$sort_by AND t.topic_id = p.topic_id AND t.topic_visibility <> p.post_visibility'; - /** - * This event allows you to control the SQL query to retrieve the list of unapproved and deleted posts - * - * @event core.mcp_sorting_unapproved_deleted_posts_query_before - * @var string sql The current SQL search string - * @var int forum_id The forum id of the posts the user is trying to access - * @var int topic_id The topic id of the posts the user is trying to access - * @var int min_time Integer with the minimum post time that the user is searching for - * @var int visibility_const Integer with one of the possible ITEM_* constant values - * @var string where_sql Extra information included in the WHERE clause. It must end with "WHERE" or "AND" or "OR" - * @since 3.1.4-RC1 - */ - $vars = array( - 'sql', - 'forum_id', - 'topic_id', - 'min_time', - 'visibility_const', - 'where_sql', - ); - extract($phpbb_dispatcher->trigger_event('core.mcp_sorting_unapproved_deleted_posts_query_before', compact($vars))); - if ($min_time) { $sql .= ' AND post_time >= ' . $min_time; @@ -575,6 +553,56 @@ function phpbb_mcp_sorting($mode, &$sort_days, &$sort_key, &$sort_dir, &$sort_by break; } + // Default total to -1 to allow editing by the event + $total = -1; + + /** + * This event allows you to control the SQL query used to get the total number + * of reports the user can access. + * + * This total is used for the pagination and for displaying the total number + * of reports to the user + * + * + * @event core.mcp_sorting_query_before + * @var string sql The current SQL search string + * @var string mode An id related to the module(s) the user is viewing + * @var string type Which kind of information is this being used for displaying. Posts, topics, etc... + * @var int forum_id The forum id of the posts the user is trying to access, if not 0 + * @var int topic_id The topic id of the posts the user is trying to access, if not 0 + * @var int sort_days The max age of the oldest report to be shown, in days + * @var string sort_key The way the user has decided to sort the data. + * The valid values must be in the keys of the sort_by_* variables + * @var string sort_dir Either 'd' for "DESC" or 'a' for 'ASC' in the SQL query + * @var int limit_days The possible max ages of the oldest report for the user to choose, in days. + * @var array sort_by_sql SQL text (values) for the possible names of the ways of sorting data (keys). + * @var array sort_by_text Language text (values) for the possible names of the ways of sorting data (keys). + * @var int min_time Integer with the minimum post time that the user is searching for + * @var int limit_time_sql Time limiting options used in the SQL query. + * @var int total The total number of reports that exist. Only set if you want to override the result + * @var string where_sql Extra information included in the WHERE clause. It must end with "WHERE" or "AND" or "OR". + * Set to "WHERE" and set total above -1 to override the total value + * @since 3.1.4-RC1 + */ + $vars = array( + 'sql', + 'mode', + 'type', + 'forum_id', + 'topic_id', + 'sort_days', + 'sort_key', + 'sort_dir', + 'limit_days', + 'sort_by_sql', + 'sort_by_text', + 'min_time', + 'limit_time_sql', + 'total', + 'where_sql', + ); + extract($phpbb_dispatcher->trigger_event('core.mcp_sorting_query_before', compact($vars))); + if (!isset($sort_by_sql[$sort_key])) { $sort_key = $default_key; @@ -606,7 +634,7 @@ function phpbb_mcp_sorting($mode, &$sort_days, &$sort_key, &$sort_dir, &$sort_by $total = (int) $db->sql_fetchfield('total'); $db->sql_freeresult($result); } - else + else if ($total < -1) { $total = -1; } diff --git a/phpBB/includes/mcp/mcp_main.php b/phpBB/includes/mcp/mcp_main.php index 1241b8bd0e..10f1a5b8c1 100644 --- a/phpBB/includes/mcp/mcp_main.php +++ b/phpBB/includes/mcp/mcp_main.php @@ -226,6 +226,31 @@ class mcp_main break; default: + if ($quickmod) + { + switch ($action) + { + case 'lock': + case 'unlock': + case 'make_announce': + case 'make_sticky': + case 'make_global': + case 'make_normal': + case 'make_onindex': + case 'move': + case 'fork': + case 'delete_topic': + trigger_error('TOPIC_NOT_EXIST'); + break; + + case 'lock_post': + case 'unlock_post': + case 'delete_post': + trigger_error('POST_NOT_EXIST'); + break; + } + } + trigger_error('NO_MODE', E_USER_ERROR); break; } diff --git a/phpBB/includes/mcp/mcp_topic.php b/phpBB/includes/mcp/mcp_topic.php index a3cffe51e6..8347830d0f 100644 --- a/phpBB/includes/mcp/mcp_topic.php +++ b/phpBB/includes/mcp/mcp_topic.php @@ -259,6 +259,8 @@ function mcp_topic_view($id, $mode, $action) * @var int current_row_number Number of the post on this page * @var array post_row Template block array of the current post * @var array row Array with original post and user data + * @var array topic_info Array with topic data + * @var int total Total posts count * @since 3.1.4-RC1 */ $vars = array( @@ -270,6 +272,8 @@ function mcp_topic_view($id, $mode, $action) 'current_row_number', 'post_row', 'row', + 'topic_info', + 'total', ); extract($phpbb_dispatcher->trigger_event('core.mcp_topic_review_modify_row', compact($vars))); diff --git a/phpBB/includes/message_parser.php b/phpBB/includes/message_parser.php index 04a2726d22..63e027cd66 100644 --- a/phpBB/includes/message_parser.php +++ b/phpBB/includes/message_parser.php @@ -21,6 +21,19 @@ if (!defined('IN_PHPBB')) if (!class_exists('bbcode')) { + // The following lines are for extensions which include message_parser.php + // while $phpbb_root_path and $phpEx are out of the script scope + // which may lead to the 'Undefined variable' and 'failed to open stream' errors + if (!isset($phpbb_root_path)) + { + global $phpbb_root_path; + } + + if (!isset($phpEx)) + { + global $phpEx; + } + include($phpbb_root_path . 'includes/bbcode.' . $phpEx); } diff --git a/phpBB/includes/ucp/ucp_pm_compose.php b/phpBB/includes/ucp/ucp_pm_compose.php index f3b59186a6..51018e3a5d 100644 --- a/phpBB/includes/ucp/ucp_pm_compose.php +++ b/phpBB/includes/ucp/ucp_pm_compose.php @@ -90,6 +90,32 @@ function compose_pm($id, $mode, $action, $user_folders = array()) // we include the language file here $user->add_lang('viewtopic'); + /** + * Modify the default vars before composing a PM + * + * @event core.ucp_pm_compose_modify_data + * @var int msg_id post_id in the page request + * @var int to_user_id The id of whom the message is to + * @var int to_group_id The id of the group the message is to + * @var bool submit Whether the form has been submitted + * @var bool preview Whether the user is previewing the PM or not + * @var string action One of: post, reply, quote, forward, quotepost, edit, delete, smilies + * @var bool delete Whether the user is deleting the PM + * @var int reply_to_all Value of reply_to_all request variable. + * @since 3.1.4-RC1 + */ + $vars = array( + 'msg_id', + 'to_user_id', + 'to_group_id', + 'submit', + 'preview', + 'action', + 'delete', + 'reply_to_all', + ); + extract($phpbb_dispatcher->trigger_event('core.ucp_pm_compose_modify_data', compact($vars))); + // Output PM_TO box if message composing if ($action != 'edit') { diff --git a/phpBB/includes/ucp/ucp_profile.php b/phpBB/includes/ucp/ucp_profile.php index 2c786a1d39..8d8d42e742 100644 --- a/phpBB/includes/ucp/ucp_profile.php +++ b/phpBB/includes/ucp/ucp_profile.php @@ -115,16 +115,16 @@ class ucp_profile } /** - * Validate user data on editing profile in UCP + * Validate user data on editing registration data in UCP * - * @event core.ucp_profile_info_validate_data + * @event core.ucp_profile_reg_details_validate * @var array data Array with user profile data * @var bool submit Flag indicating if submit button has been pressed * @var array error Array of any generated errors * @since 3.1.4-RC1 */ $vars = array('data', 'submit', 'error'); - extract($phpbb_dispatcher->trigger_event('core.ucp_profile_info_validate_data', compact($vars))); + extract($phpbb_dispatcher->trigger_event('core.ucp_profile_reg_details_validate', compact($vars))); if (!sizeof($error)) { @@ -365,6 +365,18 @@ class ucp_profile $error[] = 'FORM_INVALID'; } + /** + * Validate user data on editing profile in UCP + * + * @event core.ucp_profile_validate_profile_info + * @var array data Array with user profile data + * @var bool submit Flag indicating if submit button has been pressed + * @var array error Array of any generated errors + * @since 3.1.4-RC1 + */ + $vars = array('data', 'submit', 'error'); + extract($phpbb_dispatcher->trigger_event('core.ucp_profile_validate_profile_info', compact($vars))); + if (!sizeof($error)) { $data['notify'] = $user->data['user_notify_type']; diff --git a/phpBB/phpbb/avatar/driver/local.php b/phpBB/phpbb/avatar/driver/local.php index 8888686b2d..36087f8ba0 100644 --- a/phpBB/phpbb/avatar/driver/local.php +++ b/phpBB/phpbb/avatar/driver/local.php @@ -23,8 +23,10 @@ class local extends \phpbb\avatar\driver\driver */ public function get_data($row) { + $root_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? generate_board_url() . '/' : $this->path_helper->get_web_root_path(); + return array( - 'src' => $this->path_helper->get_web_root_path() . $this->config['avatar_gallery_path'] . '/' . $row['avatar'], + 'src' => $root_path . $this->config['avatar_gallery_path'] . '/' . $row['avatar'], 'width' => $row['avatar_width'], 'height' => $row['avatar_height'], ); diff --git a/phpBB/phpbb/avatar/driver/upload.php b/phpBB/phpbb/avatar/driver/upload.php index 003b23659f..ee36243844 100644 --- a/phpBB/phpbb/avatar/driver/upload.php +++ b/phpBB/phpbb/avatar/driver/upload.php @@ -48,8 +48,10 @@ class upload extends \phpbb\avatar\driver\driver */ public function get_data($row, $ignore_config = false) { + $root_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? generate_board_url() . '/' : $this->path_helper->get_web_root_path(); + return array( - 'src' => $this->path_helper->get_web_root_path() . 'download/file.' . $this->php_ext . '?avatar=' . $row['avatar'], + 'src' => $root_path . 'download/file.' . $this->php_ext . '?avatar=' . $row['avatar'], 'width' => $row['avatar_width'], 'height' => $row['avatar_height'], ); diff --git a/phpBB/phpbb/cache/driver/file.php b/phpBB/phpbb/cache/driver/file.php index fd5bce4515..9a7c4aec7f 100644 --- a/phpBB/phpbb/cache/driver/file.php +++ b/phpBB/phpbb/cache/driver/file.php @@ -279,6 +279,7 @@ class file extends \phpbb\cache\driver\base if ($var_name[0] == '_') { global $phpEx; + $var_name = $this->clean_varname($var_name); return file_exists($this->cache_dir . 'data' . $var_name . ".$phpEx"); } else @@ -334,6 +335,7 @@ class file extends \phpbb\cache\driver\base { global $phpEx; + $filename = $this->clean_varname($filename); $file = "{$this->cache_dir}$filename.$phpEx"; $type = substr($filename, 0, strpos($filename, '_')); @@ -516,6 +518,7 @@ class file extends \phpbb\cache\driver\base { global $phpEx; + $filename = $this->clean_varname($filename); $file = "{$this->cache_dir}$filename.$phpEx"; $lock = new \phpbb\lock\flock($file); @@ -584,4 +587,15 @@ class file extends \phpbb\cache\driver\base return $return_value; } + + /** + * Replace slashes in the file name + * + * @param string $varname name of a cache variable + * @return string $varname name that is safe to use as a filename + */ + protected function clean_varname($varname) + { + return str_replace('/', '-', $varname); + } } diff --git a/phpBB/phpbb/captcha/plugins/qa.php b/phpBB/phpbb/captcha/plugins/qa.php index a7ba994cc3..04052b3406 100644 --- a/phpBB/phpbb/captcha/plugins/qa.php +++ b/phpBB/phpbb/captcha/plugins/qa.php @@ -125,7 +125,7 @@ class qa */ public function is_available() { - global $config, $db, $phpbb_root_path, $phpEx, $user; + global $config, $db, $user; // load language file for pretty display in the ACP dropdown $user->add_lang('captcha_qa'); @@ -263,7 +263,7 @@ class qa */ function garbage_collect($type = 0) { - global $db, $config; + global $db; $sql = 'SELECT c.confirm_id FROM ' . $this->table_qa_confirm . ' c @@ -310,8 +310,6 @@ class qa $db_tool = new \phpbb\db\tools($db); - $tables = array($this->table_captcha_questions, $this->table_captcha_answers, $this->table_qa_confirm); - $schemas = array( $this->table_captcha_questions => array ( 'COLUMNS' => array( @@ -366,7 +364,7 @@ class qa */ function validate() { - global $config, $db, $user; + global $user; $error = ''; @@ -414,7 +412,7 @@ class qa if (!sizeof($this->question_ids)) { - return false; + return; } $this->confirm_id = md5(unique_id($user->ip)); $this->question = (int) array_rand($this->question_ids); @@ -440,7 +438,7 @@ class qa if (!sizeof($this->question_ids)) { - return false; + return; } $this->question = (int) array_rand($this->question_ids); @@ -611,8 +609,8 @@ class qa */ function acp_page($id, &$module) { - global $db, $user, $auth, $template; - global $config, $phpbb_root_path, $phpbb_admin_path, $phpEx; + global $user, $template; + global $config; $user->add_lang('acp/board'); $user->add_lang('captcha_qa'); @@ -674,11 +672,7 @@ class qa else { // okay, show the editor - $error = false; - $input_question = request_var('question_text', '', true); - $input_answers = request_var('answers', '', true); - $input_lang = request_var('lang_iso', '', true); - $input_strict = request_var('strict', false); + $question_input = $this->acp_get_question_input(); $langs = $this->get_languages(); foreach ($langs as $lang => $entry) @@ -697,13 +691,11 @@ class qa { if ($question = $this->acp_get_question_data($question_id)) { - $answers = (isset($input_answers[$lang])) ? $input_answers[$lang] : implode("\n", $question['answers']); - $template->assign_vars(array( - 'QUESTION_TEXT' => ($input_question) ? $input_question : $question['question_text'], - 'LANG_ISO' => ($input_lang) ? $input_lang : $question['lang_iso'], - 'STRICT' => (isset($_REQUEST['strict'])) ? $input_strict : $question['strict'], - 'ANSWERS' => $answers, + 'QUESTION_TEXT' => ($question_input['question_text']) ? $question_input['question_text'] : $question['question_text'], + 'LANG_ISO' => ($question_input['lang_iso']) ? $question_input['lang_iso'] : $question['lang_iso'], + 'STRICT' => (isset($_REQUEST['strict'])) ? $question_input['strict'] : $question['strict'], + 'ANSWERS' => implode("\n", $question['answers']), )); } else @@ -714,18 +706,16 @@ class qa else { $template->assign_vars(array( - 'QUESTION_TEXT' => $input_question, - 'LANG_ISO' => $input_lang, - 'STRICT' => $input_strict, - 'ANSWERS' => $input_answers, + 'QUESTION_TEXT' => $question_input['question_text'], + 'LANG_ISO' => $question_input['lang_iso'], + 'STRICT' => $question_input['strict'], + 'ANSWERS' => (is_array($question_input['answers'])) ? implode("\n", $question_input['answers']) : '', )); } if ($submit && check_form_key($form_key)) { - $data = $this->acp_get_question_input(); - - if (!$this->validate_input($data)) + if (!$this->validate_input($question_input)) { $template->assign_vars(array( 'S_ERROR' => true, @@ -735,11 +725,11 @@ class qa { if ($question_id) { - $this->acp_update_question($data, $question_id); + $this->acp_update_question($question_input, $question_id); } else { - $this->acp_add_question($data); + $this->acp_add_question($question_input); } add_log('admin', 'LOG_CONFIG_VISUAL'); @@ -819,6 +809,8 @@ class qa return $question; } + + return false; } /** @@ -827,13 +819,21 @@ class qa function acp_get_question_input() { $answers = utf8_normalize_nfc(request_var('answers', '', true)); + + // Convert answers into array and filter if answers are set + if (strlen($answers)) + { + $answers = array_filter(array_map('trim', explode("\n", $answers)), function ($value) { + return $value !== ''; + }); + } + $question = array( 'question_text' => request_var('question_text', '', true), 'strict' => request_var('strict', false), 'lang_iso' => request_var('lang_iso', ''), - 'answers' => (strlen($answers)) ? explode("\n", $answers) : '', + 'answers' => $answers, ); - return $question; } diff --git a/phpBB/phpbb/content_visibility.php b/phpBB/phpbb/content_visibility.php index 700009da6a..0ba0489cb7 100644 --- a/phpBB/phpbb/content_visibility.php +++ b/phpBB/phpbb/content_visibility.php @@ -237,7 +237,7 @@ class content_visibility if (!sizeof($forum_ids)) { // The user can see all posts/topics in all specified forums - return $this->db->sql_in_set($table_alias . 'forum_id', $approve_forums); + return $where_sql . $this->db->sql_in_set($table_alias . 'forum_id', $approve_forums) . ')'; } else { @@ -248,8 +248,8 @@ class content_visibility else { // The user is just a normal user - return $table_alias . $mode . '_visibility = ' . ITEM_APPROVED . ' - AND ' . $this->db->sql_in_set($table_alias . 'forum_id', $forum_ids, false, true); + return $where_sql . $table_alias . $mode . '_visibility = ' . ITEM_APPROVED . ' + AND ' . $this->db->sql_in_set($table_alias . 'forum_id', $forum_ids, false, true) . ')'; } $where_sql .= '(' . $table_alias . $mode . '_visibility = ' . ITEM_APPROVED . ' diff --git a/phpBB/phpbb/db/migration/data/v310/avatars.php b/phpBB/phpbb/db/migration/data/v310/avatars.php index 2698adeed5..9b03a8fa94 100644 --- a/phpBB/phpbb/db/migration/data/v310/avatars.php +++ b/phpBB/phpbb/db/migration/data/v310/avatars.php @@ -17,7 +17,29 @@ class avatars extends \phpbb\db\migration\migration { public function effectively_installed() { - return isset($this->config['allow_avatar_gravatar']); + // Get current avatar type of guest user + $sql = 'SELECT user_avatar_type + FROM ' . $this->table_prefix . 'users + WHERE user_id = ' . ANONYMOUS; + $result = $this->db->sql_query($sql); + $backup_type = $this->db->sql_fetchfield('user_avatar_type'); + $this->db->sql_freeresult($result); + + // Try to set avatar type to string + $sql = 'UPDATE ' . $this->table_prefix . "users + SET user_avatar_type = 'avatar.driver.upload' + WHERE user_id = " . ANONYMOUS; + $this->db->sql_return_on_error(true); + $effectively_installed = $this->db->sql_query($sql); + $this->db->sql_return_on_error(); + + // Restore avatar type of guest user to previous state + $sql = 'UPDATE ' . $this->table_prefix . "users + SET user_avatar_type = '{$backup_type}' + WHERE user_id = " . ANONYMOUS; + $this->db->sql_query($sql); + + return $effectively_installed !== false; } static public function depends_on() diff --git a/phpBB/phpbb/plupload/plupload.php b/phpBB/phpbb/plupload/plupload.php index 3c686a552f..fcce5b3bd8 100644 --- a/phpBB/phpbb/plupload/plupload.php +++ b/phpBB/phpbb/plupload/plupload.php @@ -326,7 +326,7 @@ class plupload $tmp_file = $this->temporary_filepath($upload['tmp_name']); - if (!move_uploaded_file($upload['tmp_name'], $tmp_file)) + if (!phpbb_is_writable($this->temporary_directory) || !move_uploaded_file($upload['tmp_name'], $tmp_file)) { $this->emit_error(103, 'PLUPLOAD_ERR_MOVE_UPLOADED'); } diff --git a/phpBB/phpbb/search/fulltext_native.php b/phpBB/phpbb/search/fulltext_native.php index 93ea46ca60..4d02dd1cbf 100644 --- a/phpBB/phpbb/search/fulltext_native.php +++ b/phpBB/phpbb/search/fulltext_native.php @@ -823,6 +823,13 @@ class fulltext_native extends \phpbb\search\base ); } + // if using mysql and the total result count is not calculated yet, get it from the db + if (!$total_results && $is_mysql) + { + // Also count rows for the query as if there was not LIMIT. Add SQL_CALC_FOUND_ROWS to SQL + $sql_array['SELECT'] = 'SQL_CALC_FOUND_ROWS ' . $sql_array['SELECT']; + } + $sql_array['WHERE'] = implode(' AND ', $sql_where); $sql_array['GROUP_BY'] = ($group_by) ? (($type == 'posts') ? 'p.post_id' : 'p.topic_id') . ', ' . $sort_by_sql[$sort_key] : ''; $sql_array['ORDER_BY'] = $sql_sort; @@ -838,19 +845,9 @@ class fulltext_native extends \phpbb\search\base } $this->db->sql_freeresult($result); - // if we use mysql and the total result count is not cached yet, retrieve it from the db if (!$total_results && $is_mysql) { - // Count rows for the executed queries. Replace $select within $sql with SQL_CALC_FOUND_ROWS, and run it - $sql_array_copy = $sql_array; - $sql_array_copy['SELECT'] = 'SQL_CALC_FOUND_ROWS p.post_id '; - - $sql_calc = $this->db->sql_build_query('SELECT', $sql_array_copy); - unset($sql_array_copy); - - $this->db->sql_query($sql_calc); - $this->db->sql_freeresult($result); - + // Get the number of results as calculated by MySQL $sql_count = 'SELECT FOUND_ROWS() as total_results'; $result = $this->db->sql_query($sql_count); $total_results = (int) $this->db->sql_fetchfield('total_results'); diff --git a/phpBB/posting.php b/phpBB/posting.php index ac412c0c73..f0446cf2db 100644 --- a/phpBB/posting.php +++ b/phpBB/posting.php @@ -330,14 +330,17 @@ switch ($mode) { $is_authed = true; } - break; + + // no break; case 'soft_delete': - if ($user->data['is_registered'] && $phpbb_content_visibility->can_soft_delete($forum_id, $post_data['poster_id'], $post_data['post_edit_locked'])) + if (!$is_authed && $user->data['is_registered'] && $phpbb_content_visibility->can_soft_delete($forum_id, $post_data['poster_id'], $post_data['post_edit_locked'])) { + // Fall back to soft_delete if we have no permissions to delete posts but to soft delete them $is_authed = true; + $mode = 'soft_delete'; } - else + else if (!$is_authed) { // Display the same error message for softdelete we use for delete $mode = 'delete'; @@ -1517,9 +1520,13 @@ if (!sizeof($error) && $preview) 'L_MAX_VOTES' => $user->lang('MAX_OPTIONS_SELECT', (int) $post_data['poll_max_options']), )); - $parse_poll->message = implode("\n", $post_data['poll_options']); - $parse_poll->format_display($post_data['enable_bbcode'], $post_data['enable_urls'], $post_data['enable_smilies']); - $preview_poll_options = explode('<br />', $parse_poll->message); + $preview_poll_options = array(); + foreach ($post_data['poll_options'] as $poll_option) + { + $parse_poll->message = $poll_option; + $parse_poll->format_display($post_data['enable_bbcode'], $post_data['enable_urls'], $post_data['enable_smilies']); + $preview_poll_options[] = $parse_poll->message; + } unset($parse_poll); foreach ($preview_poll_options as $key => $option) diff --git a/phpBB/styles/prosilver/template/forum_fn.js b/phpBB/styles/prosilver/template/forum_fn.js index aabc5679f6..7a377a4973 100644 --- a/phpBB/styles/prosilver/template/forum_fn.js +++ b/phpBB/styles/prosilver/template/forum_fn.js @@ -376,12 +376,19 @@ function parseDocument($container) { function resize() { var width = 0, - diff = $left.outerWidth(true) - $left.width(); + diff = $left.outerWidth(true) - $left.width(), + minWidth = Math.max($this.width() / 3, 240), + maxWidth; $right.each(function() { - width += $(this).outerWidth(true); + var $this = $(this); + if ($this.is(':visible')) { + width += $this.outerWidth(true); + } }); - $left.css('max-width', Math.floor($this.width() - width - diff) + 'px'); + + maxWidth = $this.width() - width - diff; + $left.css('max-width', Math.floor(Math.max(maxWidth, minWidth)) + 'px'); } resize(); @@ -410,7 +417,13 @@ function parseDocument($container) { // Function that checks breadcrumbs function check() { var height = $this.height(), - width = $body.width(); + width; + + // Test max-width set in code for .navlinks above + width = parseInt($this.css('max-width')); + if (!width) { + width = $body.width(); + } maxHeight = parseInt($this.css('line-height')); $links.each(function() { diff --git a/phpBB/styles/prosilver/template/mcp_logs.html b/phpBB/styles/prosilver/template/mcp_logs.html index eaa3838f17..4f74085968 100644 --- a/phpBB/styles/prosilver/template/mcp_logs.html +++ b/phpBB/styles/prosilver/template/mcp_logs.html @@ -22,10 +22,10 @@ <table class="table1"> <thead> <tr> - <th>{L_USERNAME}</th> - <th style="text-align: center">{L_IP}</th> - <th style="text-align: center">{L_TIME}</th> - <th>{L_ACTION}</th> + <th class="name">{L_USERNAME}</th> + <th class="center">{L_IP}</th> + <th class="center">{L_TIME}</th> + <th class="name">{L_ACTION}</th> <!-- IF S_CLEAR_ALLOWED --><th>{L_MARK}</th><!-- ENDIF --> </tr> </thead> @@ -34,8 +34,8 @@ <!-- BEGIN log --> <!-- IF log.S_ROW_COUNT is even --><tr class="bg1"><!-- ELSE --><tr class="bg2"><!-- ENDIF --> <td>{log.USERNAME}</td> - <td style="text-align: center">{log.IP}</td> - <td style="text-align: center">{log.DATE}</td> + <td class="center">{log.IP}</td> + <td class="center">{log.DATE}</td> <td>{log.ACTION}<br /> {log.DATA} </td> diff --git a/phpBB/styles/prosilver/template/mcp_notes_user.html b/phpBB/styles/prosilver/template/mcp_notes_user.html index ec317b141a..3e8d47eb9d 100644 --- a/phpBB/styles/prosilver/template/mcp_notes_user.html +++ b/phpBB/styles/prosilver/template/mcp_notes_user.html @@ -65,9 +65,9 @@ <table class="table1"> <thead> <tr> - <th>{L_REPORT_BY}</th> - <th style="text-align: center">{L_IP}</th> - <th style="text-align: center">{L_TIME}</th> + <th class="name reportby">{L_REPORT_BY}</th> + <th class="center">{L_IP}</th> + <th class="center">{L_TIME}</th> <th>{L_ACTION_NOTE}</th> <!-- IF S_CLEAR_ALLOWED --><th>{L_MARK}</th><!-- ENDIF --> </tr> @@ -76,11 +76,11 @@ <!-- BEGIN usernotes --> <!-- IF usernotes.S_ROW_COUNT is even --><tr class="bg1"><!-- ELSE --><tr class="bg2"><!-- ENDIF --> <td>{usernotes.REPORT_BY}</td> - <td style="text-align: center">{usernotes.IP}</td> - <td style="text-align: center">{usernotes.REPORT_AT}</td> + <td class="center">{usernotes.IP}</td> + <td class="center">{usernotes.REPORT_AT}</td> <td>{usernotes.ACTION}</td> - <!-- IF S_CLEAR_ALLOWED --><td style="width: 5%; text-align: center;"><input type="checkbox" name="marknote[]" id="note-{usernotes.ID}" value="{usernotes.ID}" /></td><!-- ENDIF --> + <!-- IF S_CLEAR_ALLOWED --><td class="center" style="width: 5%;"><input type="checkbox" name="marknote[]" id="note-{usernotes.ID}" value="{usernotes.ID}" /></td><!-- ENDIF --> </tr> <!-- BEGINELSE --> <tr> diff --git a/phpBB/styles/prosilver/template/memberlist_email.html b/phpBB/styles/prosilver/template/memberlist_email.html index 1bfd83e3a1..4a9f764d07 100644 --- a/phpBB/styles/prosilver/template/memberlist_email.html +++ b/phpBB/styles/prosilver/template/memberlist_email.html @@ -40,21 +40,21 @@ </dl> <!-- ELSEIF S_CONTACT_ADMIN--> <dl> - <dt><label>{L_RECIPIENT}:</label></dt> + <dt><label>{L_RECIPIENT}{L_COLON}</label></dt> <dd><strong>{L_ADMINISTRATOR}</strong></dd> </dl> <!-- IF not S_IS_REGISTERED --> <dl> - <dt><label for="email">{L_SENDER_EMAIL_ADDRESS}:</label></dt> + <dt><label for="email">{L_SENDER_EMAIL_ADDRESS}{L_COLON}</label></dt> <dd><input class="inputbox autowidth" type="text" name="email" id="email" size="50" maxlength="100" tabindex="1" value="{EMAIL}" /></dd> </dl> <dl> - <dt><label for="name">{L_SENDER_NAME}:</label></dt> + <dt><label for="name">{L_SENDER_NAME}{L_COLON}</label></dt> <dd><input class="inputbox autowidth" type="text" name="name" id="name" size="50" tabindex="2" value="{NAME}" /></dd> </dl> <!-- ENDIF --> <dl> - <dt><label for="subject">{L_SUBJECT}:</label></dt> + <dt><label for="subject">{L_SUBJECT}{L_COLON}</label></dt> <dd><input class="inputbox autowidth" type="text" name="subject" id="subject" size="50" tabindex="3" value="{SUBJECT}" /></dd> </dl> <!-- ELSE --> diff --git a/phpBB/styles/prosilver/template/navbar_footer.html b/phpBB/styles/prosilver/template/navbar_footer.html index 4a9275c898..b5a705d567 100644 --- a/phpBB/styles/prosilver/template/navbar_footer.html +++ b/phpBB/styles/prosilver/template/navbar_footer.html @@ -1,4 +1,4 @@ -<div class="navbar"> +<div class="navbar" role="navigation"> <div class="inner"> <ul id="nav-footer" class="linklist bulletin" role="menubar"> diff --git a/phpBB/styles/prosilver/template/overall_footer.html b/phpBB/styles/prosilver/template/overall_footer.html index 6f35d0e80b..8749ce60e8 100644 --- a/phpBB/styles/prosilver/template/overall_footer.html +++ b/phpBB/styles/prosilver/template/overall_footer.html @@ -3,7 +3,7 @@ <!-- EVENT overall_footer_page_body_after --> -<div id="page-footer"> +<div id="page-footer" role="contentinfo"> <!-- INCLUDE navbar_footer.html --> <div class="copyright"> diff --git a/phpBB/styles/prosilver/template/overall_header.html b/phpBB/styles/prosilver/template/overall_header.html index 121094f6e0..e0c8e51d25 100644 --- a/phpBB/styles/prosilver/template/overall_header.html +++ b/phpBB/styles/prosilver/template/overall_header.html @@ -2,7 +2,7 @@ <html dir="{S_CONTENT_DIRECTION}" lang="{S_USER_LANG}"> <head> <meta charset="utf-8" /> -<meta name="viewport" content="width=device-width" /> +<meta name="viewport" content="width=device-width, initial-scale=1" /> {META} <title><!-- IF UNREAD_NOTIFICATIONS_COUNT -->({UNREAD_NOTIFICATIONS_COUNT}) <!-- ENDIF --><!-- IF not S_VIEWTOPIC and not S_VIEWFORUM -->{SITENAME} - <!-- ENDIF --><!-- IF S_IN_MCP -->{L_MCP} - <!-- ELSEIF S_IN_UCP -->{L_UCP} - <!-- ENDIF -->{PAGE_TITLE}<!-- IF S_VIEWTOPIC or S_VIEWFORUM --> - {SITENAME}<!-- ENDIF --></title> @@ -59,7 +59,7 @@ <div id="wrap"> <a id="top" class="anchor" accesskey="t"></a> <div id="page-header"> - <div class="headerbar"> + <div class="headerbar" role="banner"> <div class="inner"> <div id="site-description"> @@ -69,8 +69,9 @@ <p class="skiplink"><a href="#start_here">{L_SKIP}</a></p> </div> + <!-- EVENT overall_header_searchbox_before --> <!-- IF S_DISPLAY_SEARCH and not S_IN_SEARCH --> - <div id="search-box" class="search-box search-header"> + <div id="search-box" class="search-box search-header" role="search"> <form action="{U_SEARCH}" method="get" id="search"> <fieldset> <input name="keywords" id="keywords" type="search" maxlength="128" title="{L_SEARCH_KEYWORDS}" class="inputbox search tiny" size="20" value="{SEARCH_WORDS}" placeholder="{L_SEARCH_MINI}" /> @@ -84,14 +85,14 @@ </div> </div> - + <!-- EVENT overall_header_navbar_before --> <!-- INCLUDE navbar_header.html --> </div> <!-- EVENT overall_header_page_body_before --> <a id="start_here" class="anchor"></a> - <div id="page-body"> + <div id="page-body" role="main"> <!-- IF S_BOARD_DISABLED and S_USER_LOGGED_IN and (U_MCP or U_ACP) --> <div id="information" class="rules"> <div class="inner"> diff --git a/phpBB/styles/prosilver/template/search_results.html b/phpBB/styles/prosilver/template/search_results.html index 2767eb85a3..f76afe3767 100644 --- a/phpBB/styles/prosilver/template/search_results.html +++ b/phpBB/styles/prosilver/template/search_results.html @@ -19,7 +19,7 @@ <div class="action-bar top"> <!-- IF TOTAL_MATCHES > 0 --> - <div class="search-box"> + <div class="search-box" role="search"> <form method="post" action="{S_SEARCH_ACTION}"> <fieldset> <input class="inputbox search tiny" type="search" name="add_keywords" id="add_keywords" value="" placeholder="{L_SEARCH_IN_RESULTS}" /> diff --git a/phpBB/styles/prosilver/template/simple_footer.html b/phpBB/styles/prosilver/template/simple_footer.html index d5d0f45fa9..123f8992f2 100644 --- a/phpBB/styles/prosilver/template/simple_footer.html +++ b/phpBB/styles/prosilver/template/simple_footer.html @@ -1,6 +1,6 @@ </div> - <div class="copyright">{CREDIT_LINE} + <div class="copyright" role="contentinfo">{CREDIT_LINE} <!-- IF TRANSLATION_INFO --><br />{TRANSLATION_INFO}<!-- ENDIF --> <!-- IF DEBUG_OUTPUT --><br />{DEBUG_OUTPUT}<!-- ENDIF --> </div> diff --git a/phpBB/styles/prosilver/template/simple_header.html b/phpBB/styles/prosilver/template/simple_header.html index a0c7bc68bb..ab4f855a97 100644 --- a/phpBB/styles/prosilver/template/simple_header.html +++ b/phpBB/styles/prosilver/template/simple_header.html @@ -2,7 +2,7 @@ <html dir="{S_CONTENT_DIRECTION}" lang="{S_USER_LANG}"> <head> <meta charset="utf-8" /> -<meta name="viewport" content="width=device-width" /> +<meta name="viewport" content="width=device-width, initial-scale=1" /> {META} <title>{SITENAME} • <!-- IF S_IN_MCP -->{L_MCP} • <!-- ELSEIF S_IN_UCP -->{L_UCP} • <!-- ENDIF -->{PAGE_TITLE}</title> @@ -36,4 +36,4 @@ <div id="wrap"> <a id="top" class="anchor" accesskey="t"></a> - <div id="page-body"> + <div id="page-body" role="main"> diff --git a/phpBB/styles/prosilver/template/ucp_profile_autologin_keys.html b/phpBB/styles/prosilver/template/ucp_profile_autologin_keys.html index 6fec0b8aed..65909b7068 100644 --- a/phpBB/styles/prosilver/template/ucp_profile_autologin_keys.html +++ b/phpBB/styles/prosilver/template/ucp_profile_autologin_keys.html @@ -10,19 +10,19 @@ <table class="table1"> <thead> <tr> - <th>{L_LOGIN_KEY}</th> - <th>{L_IP}</th> - <th>{L_LOGIN_TIME}</th> - <th class="mark">{L_MARK}</th> + <th class="name">{L_LOGIN_KEY}</th> + <th class="center">{L_IP}</th> + <th class="center">{L_LOGIN_TIME}</th> + <th class="center mark">{L_MARK}</th> </tr> </thead> <tbody> <!-- BEGIN sessions --> <!-- IF sessions.S_ROW_COUNT is even --><tr class="bg1"><!-- ELSE --><tr class="bg2"><!-- ENDIF --> <td><label for="{sessions.KEY}">{sessions.KEY}</label></td> - <td style="text-align: center">{sessions.IP}</td> - <td style="text-align: center">{sessions.LOGIN_TIME}</td> - <td style="text-align: center" class="mark"><input type="checkbox" name="keys[]" value="{sessions.KEY}" id="{sessions.KEY}" /></td> + <td class="center">{sessions.IP}</td> + <td class="center">{sessions.LOGIN_TIME}</td> + <td class="center mark"><input type="checkbox" name="keys[]" value="{sessions.KEY}" id="{sessions.KEY}" /></td> </tr> <!-- BEGINELSE --> <tr><td colspan="4" class="bg1" style="text-align: center">{L_PROFILE_NO_AUTOLOGIN_KEYS}</td></tr> diff --git a/phpBB/styles/prosilver/template/viewforum_body.html b/phpBB/styles/prosilver/template/viewforum_body.html index 43e994f1f9..a0a0cd547a 100644 --- a/phpBB/styles/prosilver/template/viewforum_body.html +++ b/phpBB/styles/prosilver/template/viewforum_body.html @@ -50,7 +50,7 @@ <!-- ENDIF --> <!-- IF S_DISPLAY_SEARCHBOX --> - <div class="search-box"> + <div class="search-box" role="search"> <form method="get" id="forum-search" action="{S_SEARCHBOX_ACTION}"> <fieldset> <input class="inputbox search tiny" type="search" name="keywords" id="search_keywords" size="20" placeholder="{L_SEARCH_FORUM}" /> diff --git a/phpBB/styles/prosilver/template/viewtopic_body.html b/phpBB/styles/prosilver/template/viewtopic_body.html index d39645cbe2..5b8078877e 100644 --- a/phpBB/styles/prosilver/template/viewtopic_body.html +++ b/phpBB/styles/prosilver/template/viewtopic_body.html @@ -42,7 +42,7 @@ <!-- INCLUDE viewtopic_topic_tools.html --> <!-- IF S_DISPLAY_SEARCHBOX --> - <div class="search-box"> + <div class="search-box" role="search"> <form method="get" id="topic-search" action="{S_SEARCHBOX_ACTION}"> <fieldset> <input class="inputbox search tiny" type="search" name="keywords" id="search_keywords" size="20" placeholder="{L_SEARCH_TOPIC}" /> diff --git a/phpBB/styles/prosilver/theme/bidi.css b/phpBB/styles/prosilver/theme/bidi.css index 889110e3fc..f3468ebcf2 100644 --- a/phpBB/styles/prosilver/theme/bidi.css +++ b/phpBB/styles/prosilver/theme/bidi.css @@ -182,8 +182,7 @@ /* Misc layout styles ---------------------------------------- */ -/* column[1-2] styles are containers for two column layouts - Also see tweaks.css */ +/* column[1-2] styles are containers for two column layouts */ .rtl .column1 { float: right; clear: right; @@ -313,6 +312,13 @@ li.breadcrumbs span:first-child > a { padding-right: 19px; } +/* Notification mark read link */ +.rtl .dropdown-extended a.mark_read { + border-radius: 0 3px 3px 0; + left: 0; + right: auto; +} + .rtl a.top { float: left; } @@ -596,9 +602,6 @@ li.breadcrumbs span:first-child > a { /** * buttons.css */ -/* Rollover buttons - Based on: http://wellstyled.com/css-nopreload-rollovers.html -----------------------------------------*/ .rtl .dropdown-select { padding-left: 24px; padding-right: 8px; @@ -976,10 +979,6 @@ li.breadcrumbs span:first-child > a { padding-left: 0; } -/** -* tweaks.css -*/ - /* Form button styles ---------------------------------------- */ diff --git a/phpBB/styles/prosilver/theme/buttons.css b/phpBB/styles/prosilver/theme/buttons.css index d600e76b44..f9a520369e 100644 --- a/phpBB/styles/prosilver/theme/buttons.css +++ b/phpBB/styles/prosilver/theme/buttons.css @@ -1,13 +1,11 @@ /* Button Styles ---------------------------------------- */ -/* Rollover buttons - Based on: http://wellstyled.com/css-nopreload-rollovers.html -----------------------------------------*/ .button { cursor: pointer; display: inline-block; height: 18px; + line-height: 18px; font-size: 13px; white-space: nowrap; border: 1px solid transparent; diff --git a/phpBB/styles/prosilver/theme/common.css b/phpBB/styles/prosilver/theme/common.css index 9da24b6ef9..c0cc2bb2dd 100644 --- a/phpBB/styles/prosilver/theme/common.css +++ b/phpBB/styles/prosilver/theme/common.css @@ -53,9 +53,7 @@ html { } body { - /* Text-Sizing with ems: http://www.clagnut.com/blog/348/ */ font-family: Verdana, Helvetica, Arial, sans-serif; - /*font-size: 62.5%; This sets the default font size to be equivalent to 10px */ font-size: 10px; line-height: normal; margin: 0; @@ -113,7 +111,6 @@ img { } hr { - /* Also see tweaks.css */ border: 0 solid transparent; border-top-width: 1px; height: 1px; @@ -671,6 +668,8 @@ table.table1 tbody th { /* Specific column styles */ table.table1 .name { text-align: left; } +table.table1 .center { text-align: center; } +table.table1 .reportby { width: 15%; } table.table1 .posts { text-align: center; width: 7%; } table.table1 .joined { text-align: left; width: 15%; } table.table1 .active { text-align: left; width: 15%; } @@ -711,8 +710,7 @@ table.info tbody th { /* Misc layout styles ---------------------------------------- */ -/* column[1-2] styles are containers for two column layouts - Also see tweaks.css */ +/* column[1-2] styles are containers for two column layouts */ .column1 { float: left; clear: left; diff --git a/phpBB/styles/prosilver/theme/content.css b/phpBB/styles/prosilver/theme/content.css index e73f8c9d54..380b285b83 100644 --- a/phpBB/styles/prosilver/theme/content.css +++ b/phpBB/styles/prosilver/theme/content.css @@ -259,7 +259,6 @@ dd.option { } .postbody h3 img { - /* Also see tweaks.css */ vertical-align: bottom; } @@ -510,7 +509,6 @@ blockquote .codebox { } .codebox code { - /* Also see tweaks.css */ overflow: auto; display: block; height: auto; @@ -693,7 +691,6 @@ fieldset.polls dd div { /* Poster profile block ----------------------------------------*/ .postprofile { - /* Also see tweaks.css */ margin: 5px 0 10px 0; min-height: 80px; border: 1px solid transparent; diff --git a/phpBB/styles/subsilver2/template/overall_header.html b/phpBB/styles/subsilver2/template/overall_header.html index 225a7d85ff..f3693eb865 100644 --- a/phpBB/styles/subsilver2/template/overall_header.html +++ b/phpBB/styles/subsilver2/template/overall_header.html @@ -152,7 +152,7 @@ function marklist(id, name, state) </tr> </table> </div> - + <!-- EVENT overall_header_navbar_before --> <div id="menubar"> <table width="100%" cellspacing="0"> <tr> diff --git a/phpBB/styles/subsilver2/template/ucp_profile_autologin_keys.html b/phpBB/styles/subsilver2/template/ucp_profile_autologin_keys.html index 3f63319b59..087ae89743 100644 --- a/phpBB/styles/subsilver2/template/ucp_profile_autologin_keys.html +++ b/phpBB/styles/subsilver2/template/ucp_profile_autologin_keys.html @@ -16,9 +16,9 @@ </tr> <tr> <th>{L_LOGIN_KEY}</th> - <th>{L_IP}</th> - <th>{L_LOGIN_TIME}</th> - <th>{L_MARK}</th> + <th class="center">{L_IP}</th> + <th class="center">{L_LOGIN_TIME}</th> + <th class="center">{L_MARK}</th> </tr> <!-- BEGIN sessions --> <!-- IF sessions.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF --> diff --git a/phpBB/styles/subsilver2/theme/stylesheet.css b/phpBB/styles/subsilver2/theme/stylesheet.css index 3c7ff23db5..7764020567 100644 --- a/phpBB/styles/subsilver2/theme/stylesheet.css +++ b/phpBB/styles/subsilver2/theme/stylesheet.css @@ -322,6 +322,10 @@ th { padding: 7px 5px; } +th.center { + text-align: center; +} + td { padding: 2px; } diff --git a/phpBB/viewforum.php b/phpBB/viewforum.php index 92ac9171cb..cd55114bad 100644 --- a/phpBB/viewforum.php +++ b/phpBB/viewforum.php @@ -391,15 +391,29 @@ $sql_array = array( /** * Event to modify the SQL query before the topic data is retrieved * +* It may also be used to override the above assigned template vars +* * @event core.viewforum_get_topic_data * @var array forum_data Array with forum data * @var array sql_array The SQL array to get the data of all topics +* @var array forum_id The forum_id whose topics are being listed +* @var array topics_count The total number of topics for display +* @var array sort_days The oldest topic displayable in elapsed days +* @var array sort_key The sorting by. It is one of the first character of (in low case): +* Author, Post time, Replies, Subject, Views +* @var array sort_dir Either "a" for ascending or "d" for descending * @since 3.1.0-a1 * @change 3.1.0-RC4 Added forum_data var +* @change 3.1.4-RC1 Added forum_id, topics_count, sort_days, sort_key and sort_dir vars */ $vars = array( 'forum_data', 'sql_array', + 'forum_id', + 'topics_count', + 'sort_days', + 'sort_key', + 'sort_dir', ); extract($phpbb_dispatcher->trigger_event('core.viewforum_get_topic_data', compact($vars))); diff --git a/phpBB/web.config b/phpBB/web.config index a73c328626..99a1fe6023 100644 --- a/phpBB/web.config +++ b/phpBB/web.config @@ -1,6 +1,18 @@ <?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> + <rewrite> + <rules> + <rule name="Extension Routes" stopProcessing="true"> + <match url="^(.*)$" ignoreCase="true" /> + <conditions> + <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" /> + <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" /> + </conditions> + <action type="Rewrite" url="app.php" appendQueryString="true" /> + </rule> + </rules> + </rewrite> <security> <requestFiltering> <hiddenSegments> diff --git a/tests/captcha/qa_test.php b/tests/captcha/qa_test.php new file mode 100644 index 0000000000..1f2f9f3070 --- /dev/null +++ b/tests/captcha/qa_test.php @@ -0,0 +1,94 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; + +class phpbb_captcha_qa_test extends \phpbb_database_test_case +{ + protected $request; + + /** @var \phpbb\captcha\plugins\qa */ + protected $qa; + + public function getDataSet() + { + return $this->createXMLDataSet(dirname(__FILE__) . '/../fixtures/empty.xml'); + } + + public function setUp() + { + global $db; + + $db = $this->new_dbal(); + + parent::setUp(); + + $this->request = new \phpbb_mock_request(); + request_var(false, false, false, false, $this->request); + $this->qa = new \phpbb\captcha\plugins\qa('phpbb_captcha_questions', 'phpbb_captcha_answers', 'phpbb_qa_confirm'); + } + + public function test_is_installed() + { + $this->assertFalse($this->qa->is_installed()); + + $this->qa->install(); + + $this->assertTrue($this->qa->is_installed()); + } + + public function test_set_get_name() + { + $this->assertNull($this->qa->get_service_name()); + $this->qa->set_name('foobar'); + $this->assertSame('foobar', $this->qa->get_service_name()); + } + + public function data_acp_get_question_input() + { + return array( + array("foobar\ntest\nyes", array( + 'question_text' => '', + 'strict' => false, + 'lang_iso' => '', + 'answers' => array('foobar', 'test', 'yes') + )), + array("foobar\ntest\n \nyes", array( + 'question_text' => '', + 'strict' => false, + 'lang_iso' => '', + 'answers' => array( + 0 => 'foobar', + 1 => 'test', + 3 => 'yes', + ) + )), + array('', array( + 'question_text' => '', + 'strict' => false, + 'lang_iso' => '', + 'answers' => '', + )), + ); + } + + /** + * @dataProvider data_acp_get_question_input + */ + public function test_acp_get_question_input($value, $expected) + { + $this->request->overwrite('answers', $value); + + $this->assertEquals($expected, $this->qa->acp_get_question_input()); + } +} diff --git a/tests/functional/acp_attachments_test.php b/tests/functional/acp_attachments_test.php new file mode 100644 index 0000000000..8e810a508a --- /dev/null +++ b/tests/functional/acp_attachments_test.php @@ -0,0 +1,78 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +/** + * @group functional + */ +class phpbb_functional_acp_attachments_test extends phpbb_functional_test_case +{ + public function data_imagick_path_linux() + { + return array( + array('/usr/bin', 'Configuration updated successfully'), + array('/usr/foobar', 'The entered path “/usr/foobar” does not exist.'), + array('/usr/bin/which', 'The entered path “/usr/bin/which” is not a directory.'), + ); + } + + /** + * @dataProvider data_imagick_path_linux + */ + public function test_imagick_path_linux($imagick_path, $expected) + { + if (strtolower(substr(PHP_OS, 0, 5)) !== 'linux') + { + $this->markTestSkipped('Unable to test linux specific paths on other OS.'); + } + + $this->login(); + $this->admin_login(); + + $crawler = self::request('GET', 'adm/index.php?i=attachments&mode=attach&sid=' . $this->sid); + + $form = $crawler->selectButton('Submit')->form(array('config[img_imagick]' => $imagick_path)); + + $crawler = self::submit($form); + $this->assertContains($expected, $crawler->filter('#main')->text()); + } + + public function data_imagick_path_windows() + { + return array( + array('C:\Windows', 'Configuration updated successfully'), + array('C:\Windows\foobar1', 'The entered path “C:\Windows\foobar1” does not exist.'), + array('C:\Windows\explorer.exe', 'The entered path “C:\Windows\explorer.exe” is not a directory.'), + ); + } + + /** + * @dataProvider data_imagick_path_windows + */ + public function test_imagick_path_windows($imagick_path, $expected) + { + if (strtolower(substr(PHP_OS, 0, 3)) !== 'win') + { + $this->markTestSkipped('Unable to test windows specific paths on other OS.'); + } + + $this->login(); + $this->admin_login(); + + $crawler = self::request('GET', 'adm/index.php?i=attachments&mode=attach&sid=' . $this->sid); + + $form = $crawler->selectButton('Submit')->form(array('config[img_imagick]' => $imagick_path)); + + $crawler = self::submit($form); + $this->assertContains($expected, $crawler->filter('#main')->text()); + } +} diff --git a/tests/functional/common_avatar_test_case.php b/tests/functional/common_avatar_test_case.php index 7278f23bcc..924eb1273c 100644 --- a/tests/functional/common_avatar_test_case.php +++ b/tests/functional/common_avatar_test_case.php @@ -62,7 +62,7 @@ abstract class phpbb_functional_common_avatar_test_case extends phpbb_functional { if (is_array($value)) { - $form[$key]->$value[0]($value[1]); + $form[$key]->{$value[0]}($value[1]); } else { diff --git a/tests/functional/visibility_softdelete_test.php b/tests/functional/visibility_softdelete_test.php index 794f0cde68..39efc99a35 100644 --- a/tests/functional/visibility_softdelete_test.php +++ b/tests/functional/visibility_softdelete_test.php @@ -42,6 +42,19 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ 'forum_perm_from' => 2, )); $crawler = self::submit($form); + + // Create second user which does not have m_delete permission + $this->add_lang('acp/permissions'); + + $second_user = $this->create_user('no m_delete moderator'); + $this->add_user_group("GLOBAL_MODERATORS", 'no m_delete moderator', true); + + // Set m_delete to never + $crawler = self::request('GET', "adm/index.php?i=acp_permissions&icat=16&mode=setting_user_global&user_id[0]=$second_user&type=m_&sid={$this->sid}"); + $form = $crawler->selectButton($this->lang('APPLY_PERMISSIONS'))->form(); + $data = array("setting[$second_user][0][m_delete]" => ACL_NEVER); + $form->setValues($data); + $crawler = self::submit($form); } public function test_create_post() @@ -98,6 +111,23 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ 'forum_topics_softdeleted' => 0, 'forum_last_post_id' => $this->data['posts']['Re: Soft Delete Topic #1-#2'], ), 'after replying'); + + // Test creating another reply + $post3 = $this->create_post($this->data['forums']['Soft Delete #1'], $post['topic_id'], 'Re: Soft Delete Topic #1-#3', 'This is another test post posted by the testing framework.'); + $crawler = self::request('GET', "viewtopic.php?t={$post3['topic_id']}&sid={$this->sid}"); + + $this->assertContains('Re: Soft Delete Topic #1-#3', $crawler->filter('html')->text()); + $this->data['posts']['Re: Soft Delete Topic #1-#3'] = (int) $post3['post_id']; + + $this->assert_forum_details($this->data['forums']['Soft Delete #1'], array( + 'forum_posts_approved' => 3, + 'forum_posts_unapproved' => 0, + 'forum_posts_softdeleted' => 0, + 'forum_topics_approved' => 1, + 'forum_topics_unapproved' => 0, + 'forum_topics_softdeleted' => 0, + 'forum_last_post_id' => $this->data['posts']['Re: Soft Delete Topic #1-#3'], + ), 'after replying a second time'); } public function test_softdelete_post() @@ -114,21 +144,22 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ 'posts' => array( 'Soft Delete Topic #1', 'Re: Soft Delete Topic #1-#2', + 'Re: Soft Delete Topic #1-#3', ), )); $this->assert_forum_details($this->data['forums']['Soft Delete #1'], array( - 'forum_posts_approved' => 2, + 'forum_posts_approved' => 3, 'forum_posts_unapproved' => 0, 'forum_posts_softdeleted' => 0, 'forum_topics_approved' => 1, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 0, - 'forum_last_post_id' => $this->data['posts']['Re: Soft Delete Topic #1-#2'], + 'forum_last_post_id' => $this->data['posts']['Re: Soft Delete Topic #1-#3'], ), 'before softdelete'); $this->add_lang('posting'); - $crawler = self::request('GET', "posting.php?mode=delete&f={$this->data['forums']['Soft Delete #1']}&p={$this->data['posts']['Re: Soft Delete Topic #1-#2']}&sid={$this->sid}"); + $crawler = self::request('GET', "posting.php?mode=delete&f={$this->data['forums']['Soft Delete #1']}&p={$this->data['posts']['Re: Soft Delete Topic #1-#3']}&sid={$this->sid}"); $this->assertContainsLang('DELETE_PERMANENTLY', $crawler->text()); $form = $crawler->selectButton('Yes')->form(); @@ -136,19 +167,69 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ $this->assertContainsLang('POST_DELETED', $crawler->text()); $this->assert_forum_details($this->data['forums']['Soft Delete #1'], array( - 'forum_posts_approved' => 1, + 'forum_posts_approved' => 2, 'forum_posts_unapproved' => 0, 'forum_posts_softdeleted' => 1, 'forum_topics_approved' => 1, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 0, - 'forum_last_post_id' => $this->data['posts']['Soft Delete Topic #1'], + 'forum_last_post_id' => $this->data['posts']['Re: Soft Delete Topic #1-#2'], ), 'after softdelete'); $crawler = self::request('GET', "viewtopic.php?t={$this->data['topics']['Soft Delete Topic #1']}&sid={$this->sid}"); $this->assertContains($this->lang('POST_DISPLAY', '', ''), $crawler->text()); } + public function test_softdelete_post_no_m_delete() + { + $this->login('no m_delete moderator'); + $this->load_ids(array( + 'forums' => array( + 'Soft Delete #1', + 'Soft Delete #2', + ), + 'topics' => array( + 'Soft Delete Topic #1', + ), + 'posts' => array( + 'Soft Delete Topic #1', + 'Re: Soft Delete Topic #1-#2', + 'Re: Soft Delete Topic #1-#3', + ), + )); + + $this->assert_forum_details($this->data['forums']['Soft Delete #1'], array( + 'forum_posts_approved' => 2, + 'forum_posts_unapproved' => 0, + 'forum_posts_softdeleted' => 1, + 'forum_topics_approved' => 1, + 'forum_topics_unapproved' => 0, + 'forum_topics_softdeleted' => 0, + 'forum_last_post_id' => $this->data['posts']['Re: Soft Delete Topic #1-#2'], + ), 'before softdelete without m_delete'); + + $this->add_lang('posting'); + $crawler = self::request('GET', "posting.php?mode=delete&f={$this->data['forums']['Soft Delete #1']}&p={$this->data['posts']['Re: Soft Delete Topic #1-#2']}&sid={$this->sid}"); + $this->assertNotContainsLang('DELETE_PERMANENTLY', $crawler->text()); + + $form = $crawler->selectButton('Yes')->form(); + $crawler = self::submit($form); + $this->assertContainsLang('POST_DELETED', $crawler->text()); + + $this->assert_forum_details($this->data['forums']['Soft Delete #1'], array( + 'forum_posts_approved' => 1, + 'forum_posts_unapproved' => 0, + 'forum_posts_softdeleted' => 2, + 'forum_topics_approved' => 1, + 'forum_topics_unapproved' => 0, + 'forum_topics_softdeleted' => 0, + 'forum_last_post_id' => $this->data['posts']['Soft Delete Topic #1'], + ), 'after softdelete without m_delete'); + + $crawler = self::request('GET', "viewtopic.php?t={$this->data['topics']['Soft Delete Topic #1']}&sid={$this->sid}"); + $this->assertContains($this->lang('POST_DISPLAY', '', ''), $crawler->text()); + } + public function test_move_softdeleted_post() { $this->login(); @@ -163,13 +244,14 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ 'posts' => array( 'Soft Delete Topic #1', 'Re: Soft Delete Topic #1-#2', + 'Re: Soft Delete Topic #1-#3', ), )); $this->assert_forum_details($this->data['forums']['Soft Delete #1'], array( 'forum_posts_approved' => 1, 'forum_posts_unapproved' => 0, - 'forum_posts_softdeleted' => 1, + 'forum_posts_softdeleted' => 2, 'forum_topics_approved' => 1, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 0, @@ -212,7 +294,7 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ $this->assert_forum_details($this->data['forums']['Soft Delete #2'], array( 'forum_posts_approved' => 1, 'forum_posts_unapproved' => 0, - 'forum_posts_softdeleted' => 1, + 'forum_posts_softdeleted' => 2, 'forum_topics_approved' => 1, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 0, @@ -234,6 +316,7 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ 'posts' => array( 'Soft Delete Topic #1', 'Re: Soft Delete Topic #1-#2', + 'Re: Soft Delete Topic #1-#3' ), )); @@ -250,7 +333,7 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ $this->assert_forum_details($this->data['forums']['Soft Delete #2'], array( 'forum_posts_approved' => 1, 'forum_posts_unapproved' => 0, - 'forum_posts_softdeleted' => 1, + 'forum_posts_softdeleted' => 2, 'forum_topics_approved' => 1, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 0, @@ -283,7 +366,7 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ $this->assert_forum_details($this->data['forums']['Soft Delete #2'], array( 'forum_posts_approved' => 0, 'forum_posts_unapproved' => 0, - 'forum_posts_softdeleted' => 2, + 'forum_posts_softdeleted' => 3, 'forum_topics_approved' => 0, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 1, @@ -305,6 +388,7 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ 'posts' => array( 'Soft Delete Topic #1', 'Re: Soft Delete Topic #1-#2', + 'Re: Soft Delete Topic #1-#3' ), )); @@ -321,7 +405,7 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ $this->assert_forum_details($this->data['forums']['Soft Delete #2'], array( 'forum_posts_approved' => 0, 'forum_posts_unapproved' => 0, - 'forum_posts_softdeleted' => 2, + 'forum_posts_softdeleted' => 3, 'forum_topics_approved' => 0, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 1, @@ -344,7 +428,7 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ $this->assert_forum_details($this->data['forums']['Soft Delete #1'], array( 'forum_posts_approved' => 0, 'forum_posts_unapproved' => 0, - 'forum_posts_softdeleted' => 2, + 'forum_posts_softdeleted' => 3, 'forum_topics_approved' => 0, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 1, @@ -376,13 +460,14 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ 'posts' => array( 'Soft Delete Topic #1', 'Re: Soft Delete Topic #1-#2', + 'Re: Soft Delete Topic #1-#3' ), )); $this->assert_forum_details($this->data['forums']['Soft Delete #1'], array( 'forum_posts_approved' => 0, 'forum_posts_unapproved' => 0, - 'forum_posts_softdeleted' => 2, + 'forum_posts_softdeleted' => 3, 'forum_topics_approved' => 0, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 1, @@ -417,7 +502,7 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ $this->assert_forum_details($this->data['forums']['Soft Delete #1'], array( 'forum_posts_approved' => 1, 'forum_posts_unapproved' => 0, - 'forum_posts_softdeleted' => 1, + 'forum_posts_softdeleted' => 2, 'forum_topics_approved' => 1, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 0, @@ -449,13 +534,14 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ 'posts' => array( 'Soft Delete Topic #1', 'Re: Soft Delete Topic #1-#2', + 'Re: Soft Delete Topic #1-#3' ), )); $this->assert_forum_details($this->data['forums']['Soft Delete #1'], array( 'forum_posts_approved' => 1, 'forum_posts_unapproved' => 0, - 'forum_posts_softdeleted' => 1, + 'forum_posts_softdeleted' => 2, 'forum_topics_approved' => 1, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 0, @@ -495,7 +581,7 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ $this->assert_forum_details($this->data['forums']['Soft Delete #1'], array( 'forum_posts_approved' => 1, 'forum_posts_unapproved' => 0, - 'forum_posts_softdeleted' => 0, + 'forum_posts_softdeleted' => 1, 'forum_topics_approved' => 1, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 0, @@ -528,6 +614,7 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ 'posts' => array( 'Soft Delete Topic #1', 'Re: Soft Delete Topic #1-#2', + 'Re: Soft Delete Topic #1-#3' ), )); @@ -539,7 +626,7 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ $this->assert_forum_details($this->data['forums']['Soft Delete #1'], array( 'forum_posts_approved' => 1, 'forum_posts_unapproved' => 0, - 'forum_posts_softdeleted' => 1, + 'forum_posts_softdeleted' => 2, 'forum_topics_approved' => 1, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 1, @@ -562,13 +649,14 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ 'posts' => array( 'Soft Delete Topic #1', 'Re: Soft Delete Topic #1-#2', + 'Re: Soft Delete Topic #1-#3' ), )); $this->assert_forum_details($this->data['forums']['Soft Delete #1'], array( 'forum_posts_approved' => 1, 'forum_posts_unapproved' => 0, - 'forum_posts_softdeleted' => 1, + 'forum_posts_softdeleted' => 2, 'forum_topics_approved' => 1, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 1, @@ -603,7 +691,7 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ $this->assert_forum_details($this->data['forums']['Soft Delete #1'], array( 'forum_posts_approved' => 1, 'forum_posts_unapproved' => 0, - 'forum_posts_softdeleted' => 1, + 'forum_posts_softdeleted' => 2, 'forum_topics_approved' => 1, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 0, @@ -625,13 +713,14 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ 'posts' => array( 'Soft Delete Topic #1', 'Re: Soft Delete Topic #1-#2', + 'Re: Soft Delete Topic #1-#3' ), )); $this->assert_forum_details($this->data['forums']['Soft Delete #1'], array( 'forum_posts_approved' => 1, 'forum_posts_unapproved' => 0, - 'forum_posts_softdeleted' => 1, + 'forum_posts_softdeleted' => 2, 'forum_topics_approved' => 1, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 0, @@ -660,7 +749,7 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ $this->assert_forum_details($this->data['forums']['Soft Delete #1'], array( 'forum_posts_approved' => 1, 'forum_posts_unapproved' => 0, - 'forum_posts_softdeleted' => 1, + 'forum_posts_softdeleted' => 2, 'forum_topics_approved' => 1, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 0, @@ -670,11 +759,11 @@ class phpbb_functional_visibility_softdelete_test extends phpbb_functional_test_ $this->assert_forum_details($this->data['forums']['Soft Delete #2'], array( 'forum_posts_approved' => 1, 'forum_posts_unapproved' => 0, - 'forum_posts_softdeleted' => 1, + 'forum_posts_softdeleted' => 2, 'forum_topics_approved' => 1, 'forum_topics_unapproved' => 0, 'forum_topics_softdeleted' => 0, - 'forum_last_post_id' => $this->data['posts']['Soft Delete Topic #1'] + 2, + 'forum_last_post_id' => $this->data['posts']['Soft Delete Topic #1'] + 3, ), 'after forking #2'); } diff --git a/tests/functions_acp/validate_config_vars_test.php b/tests/functions_acp/validate_config_vars_test.php index 3c9af4a889..4bf6ba3984 100644 --- a/tests/functions_acp/validate_config_vars_test.php +++ b/tests/functions_acp/validate_config_vars_test.php @@ -162,4 +162,91 @@ class phpbb_functions_acp_validate_config_vars_test extends phpbb_test_case $this->assertEquals($expected, $phpbb_error); } + + public function data_validate_path_linux() + { + return array( + array('/usr/bin', 'absolute_path', true), + array('/usr/bin/', 'absolute_path:50:200', true), + array('/usr/bin/which', 'absolute_path', 'DIRECTORY_NOT_DIR'), + array('/foo/bar', 'absolute_path', 'DIRECTORY_DOES_NOT_EXIST'), + array('C:\Windows', 'absolute_path', 'DIRECTORY_DOES_NOT_EXIST'), + array('.', 'absolute_path', true), + array('', 'absolute_path', true), + array('mkdir /foo/bar', 'absolute_path', 'DIRECTORY_DOES_NOT_EXIST'), + // Make sure above command didn't do anything + array('/foo/bar', 'absolute_path', 'DIRECTORY_DOES_NOT_EXIST'), + ); + } + + /** + * @dataProvider data_validate_path_linux + */ + public function test_validate_path_linux($path, $validation_type, $expected) + { + if (strtolower(substr(PHP_OS, 0, 5)) !== 'linux') + { + $this->markTestSkipped('Unable to test linux specific paths on other OS.'); + } + + $error = array(); + $config_ary = array( + 'path' => $path, + ); + + validate_config_vars(array( + 'path' => array('lang' => 'FOOBAR', 'validate' => $validation_type), + ), + $config_ary, + $error + ); + } + + public function data_validate_path_windows() + { + return array( + array('C:\Windows', 'absolute_path', true), + array('C:\Windows\\', 'absolute_path:50:200', true), + array('C:\Windows\explorer.exe', 'absolute_path', 'DIRECTORY_NOT_DIR'), + array('C:\foobar', 'absolute_path', 'DIRECTORY_DOES_NOT_EXIST'), + array('/usr/bin', 'absolute_path', 'DIRECTORY_DOES_NOT_EXIST'), + array('.', 'absolute_path', true), + array('', 'absolute_path', true), + array('mkdir C:\Windows\foobar', 'absolute_path', 'DIRECTORY_DOES_NOT_EXIST'), + // Make sure above command didn't do anything + array('C:\Windows\foobar', 'absolute_path', 'DIRECTORY_DOES_NOT_EXIST'), + ); + } + + /** + * @dataProvider data_validate_path_windows + */ + public function test_validate_path_windows($path, $validation_type, $expected) + { + if (strtolower(substr(PHP_OS, 0, 3)) !== 'win') + { + $this->markTestSkipped('Unable to test windows specific paths on other OS.'); + } + + $error = array(); + $config_ary = array( + 'path' => $path, + ); + + validate_config_vars(array( + 'path' => array('lang' => 'FOOBAR', 'validate' => $validation_type), + ), + $config_ary, + $error + ); + + if ($expected === true) + { + $this->assertEmpty($error); + } + else + { + $this->assertEquals(array($expected), $error); + } + } } diff --git a/tests/test_framework/phpbb_functional_test_case.php b/tests/test_framework/phpbb_functional_test_case.php index b6769f08d0..844caa8f54 100644 --- a/tests/test_framework/phpbb_functional_test_case.php +++ b/tests/test_framework/phpbb_functional_test_case.php @@ -410,6 +410,18 @@ class phpbb_functional_test_case extends phpbb_test_case $form = $crawler->selectButton('Enable')->form(); $crawler = self::submit($form); $this->add_lang('acp/extensions'); + + $meta_refresh = $crawler->filter('meta[http-equiv="refresh"]'); + + // Wait for extension to be fully enabled + while (sizeof($meta_refresh)) + { + preg_match('#url=.+/(adm+.+)#', $meta_refresh->attr('content'), $match); + $url = $match[1]; + $crawler = self::request('POST', $url); + $meta_refresh = $crawler->filter('meta[http-equiv="refresh"]'); + } + $this->assertContainsLang('EXTENSION_ENABLE_SUCCESS', $crawler->filter('div.successbox')->text()); $this->logout(); |