diff options
46 files changed, 839 insertions, 211 deletions
diff --git a/phpBB/adm/style/acp_groups.html b/phpBB/adm/style/acp_groups.html index 23f6e744c0..ffde27a437 100644 --- a/phpBB/adm/style/acp_groups.html +++ b/phpBB/adm/style/acp_groups.html @@ -267,11 +267,12 @@ <!-- EVENT acp_groups_manage_before --> <table class="table1"> - <col class="col1" /><col class="col1" /><col class="col2" /><col class="col2" /><col class="col2" /> + <col class="col1" /><col class="col1" /><col class="col1" /><col class="col2" /><col class="col2" /><col class="col2" /> <thead> <tr> <th style="width: 50%">{L_GROUP}</th> <th>{L_TOTAL_MEMBERS}</th> + <th>{L_PENDING_MEMBERS}</th> <th colspan="2">{L_OPTIONS}</th> <th>{L_ACTION}</th> </tr> @@ -281,7 +282,7 @@ <!-- IF groups.S_SPECIAL --> <!-- IF groups.S_FIRST_ROW --> <tr> - <td colspan="5" class="row3">{L_NO_GROUPS_CREATED}</td> + <td colspan="6" class="row3">{L_NO_GROUPS_CREATED}</td> </tr> <!-- ENDIF --> </tbody> @@ -302,11 +303,12 @@ <p>{L_SPECIAL_GROUPS_EXPLAIN}</p> <table class="table1"> - <col class="col1" /><col class="col1" /><col class="col2" /><col class="col2" /><col class="col2" /> + <col class="col1" /><col class="col1" /><col class="col1" /><col class="col2" /><col class="col2" /> <thead> <tr> <th style="width: 50%">{L_GROUP}</th> <th>{L_TOTAL_MEMBERS}</th> + <th>{L_PENDING_MEMBERS}</th> <th colspan="2">{L_OPTIONS}</th> <th>{L_ACTION}</th> </tr> @@ -316,6 +318,7 @@ <tr> <td><strong>{groups.GROUP_NAME}</strong></td> <td style="text-align: center;">{groups.TOTAL_MEMBERS}</td> + <td style="text-align: center;">{groups.PENDING_MEMBERS}</td> <td style="text-align: center;"><a href="{groups.U_EDIT}">{L_SETTINGS}</a></td> <td style="text-align: center;"><a href="{groups.U_LIST}">{L_MEMBERS}</a></td> <td style="text-align: center;"><!-- IF not groups.S_GROUP_SPECIAL and groups.U_DELETE --><a href="{groups.U_DELETE}" data-ajax="row_delete">{L_DELETE}</a><!-- ELSE -->{L_DELETE}<!-- ENDIF --></td> diff --git a/phpBB/adm/style/admin.css b/phpBB/adm/style/admin.css index e38e1cc3d7..4bb9922d56 100644 --- a/phpBB/adm/style/admin.css +++ b/phpBB/adm/style/admin.css @@ -2554,6 +2554,7 @@ fieldset.permissions .padding { #progress-bar { position: relative; width: 90%; + text-align: center; height: 25px; margin: 20px auto; border: 1px solid #cecece; @@ -2563,10 +2564,7 @@ fieldset.permissions .padding { position: absolute; top: 0; width: 100%; - text-align: center; - line-height: 25px; - font-weight: bold; - color: #fff; + color: #000; } #progress-bar #progress-bar-filler { @@ -2577,4 +2575,11 @@ fieldset.permissions .padding { background-color: #3c84ad; width: 0; height: 25px; + overflow: hidden; + color: #fff; +} + +#progress-bar p { + line-height: 25px; + font-weight: bold; } diff --git a/phpBB/assets/javascript/installer.js b/phpBB/assets/javascript/installer.js index 7bb2bb426d..958450ed57 100644 --- a/phpBB/assets/javascript/installer.js +++ b/phpBB/assets/javascript/installer.js @@ -177,7 +177,7 @@ * @param progressObject */ function setProgress(progressObject) { - var $statusText, $progressBar, $progressText, $progressFiller; + var $statusText, $progressBar, $progressText, $progressFiller, $progressFillerText; if (progressObject.task_name.length) { if (!progressBarTriggered) { @@ -189,18 +189,23 @@ $progressBar.attr('id', 'progress-bar'); $progressText = $('<p />'); $progressText.attr('id', 'progress-bar-text'); - $progressFiller = $('<span />'); + $progressFiller = $('<div />'); $progressFiller.attr('id', 'progress-bar-filler'); - $progressFiller.html($progressText); + $progressFillerText = $('<p />'); + $progressFillerText.attr('id', 'progress-bar-filler-text'); $statusText = $('<p />'); $statusText.attr('id', 'progress-status-text'); + $progressFiller.append($progressFillerText); + $progressBar.append($progressText); $progressBar.append($progressFiller); $progressBarWrapper.append($statusText); $progressBarWrapper.append($progressBar); + $progressFillerText.css('width', $progressBar.width()); + progressBarTriggered = true; } else if (progressObject.hasOwnProperty('restart')) { clearInterval(progressTimer); @@ -210,6 +215,7 @@ $statusText = $('#progress-status-text'); $progressText.text('0%'); + $progressFillerText.text('0%'); $progressFiller.css('width', '0%'); currentProgress = 0; @@ -362,15 +368,20 @@ * * @param $progressText * @param $progressFiller + * @param $progressFillerText * @param progressLimit */ - function incrementFiller($progressText, $progressFiller, progressLimit) { + function incrementFiller($progressText, $progressFiller, $progressFillerText, progressLimit) { if (currentProgress >= progressLimit || currentProgress >= 100) { clearInterval(progressTimer); return; } + var $progressBar = $('#progress-bar'); + currentProgress++; + $progressFillerText.css('width', $progressBar.width()); + $progressFillerText.text(currentProgress + '%'); $progressText.text(currentProgress + '%'); $progressFiller.css('width', currentProgress + '%'); } @@ -382,13 +393,14 @@ */ function incrementProgressBar(progressLimit) { var $progressFiller = $('#progress-bar-filler'); + var $progressFillerText = $('#progress-bar-filler-text'); var $progressText = $('#progress-bar-text'); var progressStart = $progressFiller.width() / $progressFiller.offsetParent().width() * 100; currentProgress = Math.floor(progressStart); clearInterval(progressTimer); progressTimer = setInterval(function() { - incrementFiller($progressText, $progressFiller, progressLimit); + incrementFiller($progressText, $progressFiller, $progressFillerText, progressLimit); }, 10); } diff --git a/phpBB/composer.json b/phpBB/composer.json index 1b2625c593..89bbce4588 100644 --- a/phpBB/composer.json +++ b/phpBB/composer.json @@ -47,8 +47,7 @@ "twig/twig": "1.*" }, "require-dev": { - "fabpot/goutte": "1.0.*", - "guzzle/guzzle": "3.9.*", + "fabpot/goutte": "~2.0", "phing/phing": "2.4.*", "phpunit/dbunit": "1.3.*", "phpunit/phpunit": "4.1.*", diff --git a/phpBB/composer.lock b/phpBB/composer.lock index 079a64c704..6412029b0b 100644 --- a/phpBB/composer.lock +++ b/phpBB/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "b7cd29af73c4846ea28c2c9fbee1067b", - "content-hash": "321f60039c3712a7e8b351aee8f9aca1", + "hash": "f76b5185058599cad6a87ef7c8c35fbf", + "content-hash": "b89d3c18f8d9b3c4dc476f92030a83a1", "packages": [ { "name": "bantu/ini-get-wrapper", @@ -1356,41 +1356,34 @@ "packages-dev": [ { "name": "fabpot/goutte", - "version": "v1.0.7", + "version": "v2.0.4", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/Goutte.git", - "reference": "794b196e76bdd37b5155cdecbad311f0a3b07625" + "reference": "0ad3ee6dc2d0aaa832a80041a1e09bf394e99802" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/794b196e76bdd37b5155cdecbad311f0a3b07625", - "reference": "794b196e76bdd37b5155cdecbad311f0a3b07625", + "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/0ad3ee6dc2d0aaa832a80041a1e09bf394e99802", + "reference": "0ad3ee6dc2d0aaa832a80041a1e09bf394e99802", "shasum": "" }, "require": { - "ext-curl": "*", - "guzzle/http": "~3.1", - "php": ">=5.3.0", + "guzzlehttp/guzzle": ">=4,<6", + "php": ">=5.4.0", "symfony/browser-kit": "~2.1", "symfony/css-selector": "~2.1", - "symfony/dom-crawler": "~2.1", - "symfony/finder": "~2.1", - "symfony/process": "~2.1" - }, - "require-dev": { - "guzzle/plugin-history": "~3.1", - "guzzle/plugin-mock": "~3.1" + "symfony/dom-crawler": "~2.1" }, "type": "application", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { - "psr-0": { - "Goutte": "." + "psr-4": { + "Goutte\\": "Goutte" } }, "notification-url": "https://packagist.org/downloads/", @@ -1404,11 +1397,11 @@ } ], "description": "A simple PHP Web Scraper", - "homepage": "https://github.com/fabpot/Goutte", + "homepage": "https://github.com/FriendsOfPHP/Goutte", "keywords": [ "scraper" ], - "time": "2014-10-09 15:52:51" + "time": "2015-05-05 21:14:57" }, { "name": "guzzle/guzzle", @@ -1506,6 +1499,165 @@ "time": "2015-03-18 18:23:50" }, { + "name": "guzzlehttp/guzzle", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "f3c8c22471cb55475105c14769644a49c3262b93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f3c8c22471cb55475105c14769644a49c3262b93", + "reference": "f3c8c22471cb55475105c14769644a49c3262b93", + "shasum": "" + }, + "require": { + "guzzlehttp/ringphp": "^1.1", + "php": ">=5.4.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.0", + "psr/log": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2015-05-20 03:47:55" + }, + { + "name": "guzzlehttp/ringphp", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/RingPHP.git", + "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/dbbb91d7f6c191e5e405e900e3102ac7f261bc0b", + "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b", + "shasum": "" + }, + "require": { + "guzzlehttp/streams": "~3.0", + "php": ">=5.4.0", + "react/promise": "~2.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "ext-curl": "Guzzle will use specific adapters if cURL is present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Ring\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", + "time": "2015-05-20 03:37:09" + }, + { + "name": "guzzlehttp/streams", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/streams.git", + "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/streams/zipball/47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", + "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Stream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Provides a simple abstraction over streams of data", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "Guzzle", + "stream" + ], + "time": "2014-10-12 19:18:40" + }, + { "name": "michelf/php-markdown", "version": "1.6.0", "source": { @@ -2130,6 +2282,50 @@ "time": "2013-03-08 08:21:40" }, { + "name": "react/promise", + "version": "v2.2.1", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "3b6fca09c7d56321057fa8867c8dbe1abf648627" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/3b6fca09c7d56321057fa8867c8dbe1abf648627", + "reference": "3b6fca09c7d56321057fa8867c8dbe1abf648627", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "time": "2015-07-03 13:48:55" + }, + { "name": "sami/sami", "version": "v1.4.1", "source": { diff --git a/phpBB/docs/events.md b/phpBB/docs/events.md index f1dee8cee8..bb1289b74f 100644 --- a/phpBB/docs/events.md +++ b/phpBB/docs/events.md @@ -1028,6 +1028,20 @@ navbar_header_quick_links_before * Since: 3.1.0-RC2 * Purpose: Add links to the top of the quick-links drop-down menu in the header +navbar_header_user_profile_append +=== +* Locations: + + styles/prosilver/template/navbar_header.html +* Since: 3.1.8-RC1 +* Purpose: Add links to the right of the user drop down area + +navbar_header_user_profile_prepend +=== +* Locations: + + styles/prosilver/template/navbar_header.html +* Since: 3.1.8-RC1 +* Purpose: Add links to the left of the notification area + navbar_header_username_append === * Locations: diff --git a/phpBB/includes/acp/acp_groups.php b/phpBB/includes/acp/acp_groups.php index 1f965b334c..ba39a1a60c 100644 --- a/phpBB/includes/acp/acp_groups.php +++ b/phpBB/includes/acp/acp_groups.php @@ -930,11 +930,12 @@ class acp_groups // used for easy access to the data within a group $cached_group_data[$type][$row['group_id']] = $row; $cached_group_data[$type][$row['group_id']]['total_members'] = 0; + $cached_group_data[$type][$row['group_id']]['pending_members'] = 0; } $db->sql_freeresult($result); // How many people are in which group? - $sql = 'SELECT COUNT(ug.user_id) AS total_members, ug.group_id + $sql = 'SELECT COUNT(ug.user_id) AS total_members, SUM(ug.user_pending) AS pending_members, ug.group_id FROM ' . USER_GROUP_TABLE . ' ug WHERE ' . $db->sql_in_set('ug.group_id', array_keys($lookup)) . ' GROUP BY ug.group_id'; @@ -944,6 +945,7 @@ class acp_groups { $type = $lookup[$row['group_id']]; $cached_group_data[$type][$row['group_id']]['total_members'] = $row['total_members']; + $cached_group_data[$type][$row['group_id']]['pending_members'] = $row['pending_members']; } $db->sql_freeresult($result); @@ -972,6 +974,7 @@ class acp_groups 'GROUP_NAME' => $group_name, 'TOTAL_MEMBERS' => $row['total_members'], + 'PENDING_MEMBERS' => $row['pending_members'] )); } } diff --git a/phpBB/includes/acp/acp_users.php b/phpBB/includes/acp/acp_users.php index 2bec4385c3..0ee6452ada 100644 --- a/phpBB/includes/acp/acp_users.php +++ b/phpBB/includes/acp/acp_users.php @@ -1929,8 +1929,12 @@ class acp_users } } - // Replace "error" strings with their real, localised form - $error = $phpbb_avatar_manager->localize_errors($user, $error); + // Avatar manager is not initialized if avatars are disabled + if (isset($phpbb_avatar_manager)) + { + // Replace "error" strings with their real, localised form + $error = $phpbb_avatar_manager->localize_errors($user, $error); + } $avatar = phpbb_get_user_avatar($user_row, 'USER_AVATAR', true); diff --git a/phpBB/includes/functions_messenger.php b/phpBB/includes/functions_messenger.php index 0aee9dd3cf..b306b9aa79 100644 --- a/phpBB/includes/functions_messenger.php +++ b/phpBB/includes/functions_messenger.php @@ -1733,7 +1733,7 @@ function mail_encode($str, $eol = "\r\n") */ function phpbb_mail($to, $subject, $msg, $headers, $eol, &$err_msg) { - global $phpbb_root_path, $phpEx; + global $config, $phpbb_root_path, $phpEx; // We use the EOL character for the OS here because the PHP mail function does not correctly transform line endings. On Windows SMTP is used (SMTP is \r\n), on UNIX a command is used... // Reference: http://bugs.php.net/bug.php?id=15841 diff --git a/phpBB/language/en/acp/groups.php b/phpBB/language/en/acp/groups.php index 421075ce5e..9d0bb5f8b9 100644 --- a/phpBB/language/en/acp/groups.php +++ b/phpBB/language/en/acp/groups.php @@ -130,6 +130,8 @@ $lang = array_merge($lang, array( 'NO_USERS_ADDED' => 'No users were added to the group.', 'NO_VALID_USERS' => 'You haven’t entered any users eligible for that action.', + 'PENDING_MEMBERS' => 'Pending', + 'SELECT_GROUP' => 'Select a group', 'SPECIAL_GROUPS' => 'Pre-defined groups', 'SPECIAL_GROUPS_EXPLAIN' => 'Pre-defined groups are special groups, they cannot be deleted or directly modified. However you can still add users and alter basic settings.', diff --git a/phpBB/memberlist.php b/phpBB/memberlist.php index c23a409ae7..759a899de1 100644 --- a/phpBB/memberlist.php +++ b/phpBB/memberlist.php @@ -1231,21 +1231,19 @@ switch ($mode) ); extract($phpbb_dispatcher->trigger_event('core.memberlist_modify_sql_query_data', compact($vars))); - // Count the users ... - if ($sql_where) - { - $sql = 'SELECT COUNT(u.user_id) AS total_users - FROM ' . USERS_TABLE . " u$sql_from - WHERE u.user_type IN (" . USER_NORMAL . ', ' . USER_FOUNDER . ") - $sql_where"; - $result = $db->sql_query($sql); - $total_users = (int) $db->sql_fetchfield('total_users'); - $db->sql_freeresult($result); - } - else + $user_types = array(USER_NORMAL, USER_FOUNDER); + if ($auth->acl_get('a_user')) { - $total_users = $config['num_users']; + $user_types[] = USER_INACTIVE; } + // Count the users ... + $sql = 'SELECT COUNT(u.user_id) AS total_users + FROM ' . USERS_TABLE . " u$sql_from + WHERE " . $db->sql_in_set('u.user_type', $user_types) . " + $sql_where"; + $result = $db->sql_query($sql); + $total_users = (int) $db->sql_fetchfield('total_users'); + $db->sql_freeresult($result); // Build a relevant pagination_url $params = $sort_params = array(); @@ -1411,13 +1409,7 @@ switch ($mode) ); } - $user_types = array(USER_NORMAL, USER_FOUNDER); - if ($auth->acl_get('a_user')) - { - $user_types[] = USER_INACTIVE; - } - - $start = $pagination->validate_start($start, $config['topics_per_page'], $config['num_users']); + $start = $pagination->validate_start($start, $config['topics_per_page'], $total_users); // Get us some users :D $sql = "SELECT u.user_id diff --git a/phpBB/phpbb/cache/driver/base.php b/phpBB/phpbb/cache/driver/base.php index 55cd4668de..85762c4d95 100644 --- a/phpBB/phpbb/cache/driver/base.php +++ b/phpBB/phpbb/cache/driver/base.php @@ -49,6 +49,7 @@ abstract class base implements \phpbb\cache\driver\driver_interface $this->remove_dir($fileInfo->getPathname()); } else if (strpos($filename, 'container_') === 0 || + strpos($filename, 'autoload_') === 0 || strpos($filename, 'url_matcher') === 0 || strpos($filename, 'url_generator') === 0 || strpos($filename, 'sql_') === 0 || diff --git a/phpBB/phpbb/console/command/db/migration_command.php b/phpBB/phpbb/console/command/db/migration_command.php index d44ef8c5cb..b951560588 100644 --- a/phpBB/phpbb/console/command/db/migration_command.php +++ b/phpBB/phpbb/console/command/db/migration_command.php @@ -45,7 +45,7 @@ abstract class migration_command extends \phpbb\console\command\command $this->migrator->set_migrations($migrations); - return $migrations; + return $this->migrator->get_migrations(); } protected function finalise_update() diff --git a/phpBB/phpbb/db/migration/data/v320/default_data_type_ids.php b/phpBB/phpbb/db/migration/data/v320/default_data_type_ids.php index ecee09ce77..65e5b3fa73 100644 --- a/phpBB/phpbb/db/migration/data/v320/default_data_type_ids.php +++ b/phpBB/phpbb/db/migration/data/v320/default_data_type_ids.php @@ -17,7 +17,10 @@ class default_data_type_ids extends \phpbb\db\migration\migration { static public function depends_on() { - return array('\phpbb\db\migration\data\v320\v320a2'); + return array( + '\phpbb\db\migration\data\v320\v320a2', + '\phpbb\db\migration\data\v320\oauth_states', + ); } public function update_schema() diff --git a/phpBB/phpbb/db/migration/migration.php b/phpBB/phpbb/db/migration/migration.php index 2304c8e44c..4e218344f4 100644 --- a/phpBB/phpbb/db/migration/migration.php +++ b/phpBB/phpbb/db/migration/migration.php @@ -20,7 +20,7 @@ namespace phpbb\db\migration; * in a subclass. This class provides various utility methods to simplify editing * a phpBB. */ -abstract class migration +abstract class migration implements migration_interface { /** @var \phpbb\config\config */ protected $config; @@ -70,9 +70,7 @@ abstract class migration } /** - * Defines other migrations to be applied first - * - * @return array An array of migration class names + * {@inheritdoc} */ static public function depends_on() { @@ -80,14 +78,7 @@ abstract class migration } /** - * Allows you to check if the migration is effectively installed (entirely optional) - * - * This is checked when a migration is installed. If true is returned, the migration will be set as - * installed without performing the database changes. - * This function is intended to help moving to migrations from a previous database updater, where some - * migrations may have been installed already even though they are not yet listed in the migrations table. - * - * @return bool True if this migration is installed, False if this migration is not installed (checked on install) + * {@inheritdoc} */ public function effectively_installed() { @@ -95,9 +86,7 @@ abstract class migration } /** - * Updates the database schema by providing a set of change instructions - * - * @return array Array of schema changes (compatible with db_tools->perform_schema_changes()) + * {@inheritdoc} */ public function update_schema() { @@ -105,9 +94,7 @@ abstract class migration } /** - * Reverts the database schema by providing a set of change instructions - * - * @return array Array of schema changes (compatible with db_tools->perform_schema_changes()) + * {@inheritdoc} */ public function revert_schema() { @@ -115,9 +102,7 @@ abstract class migration } /** - * Updates data by returning a list of instructions to be executed - * - * @return array Array of data update instructions + * {@inheritdoc} */ public function update_data() { @@ -125,12 +110,7 @@ abstract class migration } /** - * Reverts data by returning a list of instructions to be executed - * - * @return array Array of data instructions that will be performed on revert - * NOTE: calls to tools (such as config.add) are automatically reverted when - * possible, so you should not attempt to revert those, this is mostly for - * otherwise unrevertable calls (custom functions for example) + * {@inheritdoc} */ public function revert_data() { diff --git a/phpBB/phpbb/db/migration/migration_interface.php b/phpBB/phpbb/db/migration/migration_interface.php new file mode 100644 index 0000000000..2aba5ec608 --- /dev/null +++ b/phpBB/phpbb/db/migration/migration_interface.php @@ -0,0 +1,70 @@ +<?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. + * + */ + +namespace phpbb\db\migration; + +/** + * Base class interface for database migrations + */ +interface migration_interface +{ + /** + * Defines other migrations to be applied first + * + * @return array An array of migration class names + */ + static public function depends_on(); + + /** + * Allows you to check if the migration is effectively installed (entirely optional) + * + * This is checked when a migration is installed. If true is returned, the migration will be set as + * installed without performing the database changes. + * This function is intended to help moving to migrations from a previous database updater, where some + * migrations may have been installed already even though they are not yet listed in the migrations table. + * + * @return bool True if this migration is installed, False if this migration is not installed (checked on install) + */ + public function effectively_installed(); + + /** + * Updates the database schema by providing a set of change instructions + * + * @return array Array of schema changes (compatible with db_tools->perform_schema_changes()) + */ + public function update_schema(); + + /** + * Reverts the database schema by providing a set of change instructions + * + * @return array Array of schema changes (compatible with db_tools->perform_schema_changes()) + */ + public function revert_schema(); + + /** + * Updates data by returning a list of instructions to be executed + * + * @return array Array of data update instructions + */ + public function update_data(); + + /** + * Reverts data by returning a list of instructions to be executed + * + * @return array Array of data instructions that will be performed on revert + * NOTE: calls to tools (such as config.add) are automatically reverted when + * possible, so you should not attempt to revert those, this is mostly for + * otherwise unrevertable calls (custom functions for example) + */ + public function revert_data(); +} diff --git a/phpBB/phpbb/db/migration/schema_generator.php b/phpBB/phpbb/db/migration/schema_generator.php index 7003844bc4..c579e25824 100644 --- a/phpBB/phpbb/db/migration/schema_generator.php +++ b/phpBB/phpbb/db/migration/schema_generator.php @@ -77,8 +77,15 @@ class schema_generator $check_dependencies = true; while (!empty($migrations)) { - foreach ($migrations as $migration_class) + foreach ($migrations as $key => $migration_class) { + // Unset classes that are not a valid migration + if (\phpbb\db\migrator::is_migration($migration_class) === false) + { + unset($migrations[$key]); + continue; + } + $open_dependencies = array_diff($migration_class::depends_on(), $tree); if (empty($open_dependencies)) diff --git a/phpBB/phpbb/db/migrator.php b/phpBB/phpbb/db/migrator.php index d91860949a..a1e93942cd 100644 --- a/phpBB/phpbb/db/migrator.php +++ b/phpBB/phpbb/db/migrator.php @@ -170,10 +170,28 @@ class migrator */ public function set_migrations($class_names) { + foreach ($class_names as $key => $class) + { + if (!self::is_migration($class)) + { + unset($class_names[$key]); + } + } + $this->migrations = $class_names; } /** + * Get the list of available migration class names + * + * @return array Array of all migrations available to be run + */ + public function get_migrations() + { + return $this->migrations; + } + + /** * Runs a single update step from the next migration to be applied. * * The update step can either be a schema or a (partial) data update. To @@ -857,4 +875,27 @@ class migrator )); } } + + /** + * Check if a class is a migration. + * + * @param string $migration A migration class name + * @return bool Return true if class is a migration, false otherwise + */ + static public function is_migration($migration) + { + if (class_exists($migration)) + { + // Migration classes should extend the abstract class + // phpbb\db\migration\migration (which implements the + // migration_interface) and be instantiable. + $reflector = new \ReflectionClass($migration); + if ($reflector->implementsInterface('\phpbb\db\migration\migration_interface') && $reflector->isInstantiable()) + { + return true; + } + } + + return false; + } } diff --git a/phpBB/phpbb/di/container_builder.php b/phpBB/phpbb/di/container_builder.php index 433847b285..9583da14f5 100644 --- a/phpBB/phpbb/di/container_builder.php +++ b/phpBB/phpbb/di/container_builder.php @@ -135,6 +135,11 @@ class container_builder $config_cache = new ConfigCache($container_filename, defined('DEBUG')); if ($this->use_cache && $config_cache->isFresh()) { + if ($this->use_extensions) + { + require($this->get_autoload_filename()); + } + require($config_cache->getPath()); $this->container = new \phpbb_cache_container(); } @@ -405,6 +410,15 @@ class container_builder $extensions = $ext_container->get('ext.manager')->all_enabled(); // Load each extension found + $autoloaders = '<?php +/** + * Loads all extensions custom auto-loaders. + * + * This file has been auto-generated + * by phpBB while loading the extensions. + */ + +'; foreach ($extensions as $ext_name => $path) { $extension_class = '\\' . str_replace('/', '\\', $ext_name) . '\\di\\extension'; @@ -420,9 +434,14 @@ class container_builder $filename = $path . 'vendor/autoload.php'; if (file_exists($filename)) { - require $filename; + $autoloaders .= "require('{$filename}');\n"; } } + + $configCache = new ConfigCache($this->get_autoload_filename(), false); + $configCache->write($autoloaders); + + require($this->get_autoload_filename()); } else { @@ -540,6 +559,16 @@ class container_builder } /** + * Get the filename under which the dumped extensions autoloader will be stored. + * + * @return string Path for dumped extensions autoloader + */ + protected function get_autoload_filename() + { + return $this->get_cache_dir() . 'autoload_' . md5($this->phpbb_root_path) . '.' . $this->php_ext; + } + + /** * Return the name of the current environment. * * @return string diff --git a/phpBB/phpbb/extension/base.php b/phpBB/phpbb/extension/base.php index 5bb530bad4..c7778cfed1 100644 --- a/phpBB/phpbb/extension/base.php +++ b/phpBB/phpbb/extension/base.php @@ -24,7 +24,7 @@ class base implements \phpbb\extension\extension_interface protected $container; /** @var \phpbb\finder */ - protected $finder; + protected $extension_finder; /** @var \phpbb\db\migrator */ protected $migrator; @@ -73,9 +73,7 @@ class base implements \phpbb\extension\extension_interface */ public function enable_step($old_state) { - $migrations = $this->get_migration_file_list(); - - $this->migrator->set_migrations($migrations); + $this->get_migration_file_list(); $this->migrator->update(); @@ -103,8 +101,6 @@ class base implements \phpbb\extension\extension_interface { $migrations = $this->get_migration_file_list(); - $this->migrator->set_migrations($migrations); - foreach ($migrations as $migration) { while ($this->migrator->migration_state($migration) !== false) @@ -137,6 +133,10 @@ class base implements \phpbb\extension\extension_interface $migrations = $this->extension_finder->get_classes_from_files($migrations); + $this->migrator->set_migrations($migrations); + + $migrations = $this->migrator->get_migrations(); + return $migrations; } } diff --git a/phpBB/phpbb/install/controller/archive_download.php b/phpBB/phpbb/install/controller/archive_download.php index a0f0ba181d..eabc0a9976 100644 --- a/phpBB/phpbb/install/controller/archive_download.php +++ b/phpBB/phpbb/install/controller/archive_download.php @@ -46,9 +46,9 @@ class archive_download */ public function conflict_archive() { - $filename = $this->installer_config->get('update_file_conflict_archive', false); + $filename = $this->installer_config->get('update_file_conflict_archive', ''); - if (!$filename) + if (empty($filename)) { throw new http_exception(404, 'URL_NOT_FOUND'); } @@ -65,7 +65,7 @@ class archive_download { $filename = $this->installer_config->get('update_file_archive', ''); - if (!$filename) + if (empty($filename)) { throw new http_exception(404, 'URL_NOT_FOUND'); } diff --git a/phpBB/phpbb/install/helper/config.php b/phpBB/phpbb/install/helper/config.php index 0f0840f470..ab5af86320 100644 --- a/phpBB/phpbb/install/helper/config.php +++ b/phpBB/phpbb/install/helper/config.php @@ -157,10 +157,10 @@ class config { if ($this->system_data['max_execution_time'] <= 0) { - return 1; + return PHP_INT_MAX; } - return ($this->system_data['start_time'] + $this->system_data['max_execution_time']) - time(); + return ($this->system_data['start_time'] + $this->system_data['max_execution_time']) - microtime(true); } /** @@ -430,7 +430,7 @@ class config $this->system_data['max_execution_time'] = $execution_time; // Set start time - $this->system_data['start_time'] = time(); + $this->system_data['start_time'] = microtime(true); // Get memory limit $this->system_data['memory_limit'] = $this->php_ini->getBytes('memory_limit'); diff --git a/phpBB/phpbb/install/module/install_database/task/create_schema.php b/phpBB/phpbb/install/module/install_database/task/create_schema.php index cabb78787f..a5635d5dbe 100644 --- a/phpBB/phpbb/install/module/install_database/task/create_schema.php +++ b/phpBB/phpbb/install/module/install_database/task/create_schema.php @@ -13,6 +13,8 @@ namespace phpbb\install\module\install_database\task; +use phpbb\install\exception\resource_limit_reached_exception; + /** * Create database schema */ @@ -106,6 +108,17 @@ class create_schema extends \phpbb\install\task_base */ public function run() { + // As this task may take a large amount of time to complete refreshing the page might be necessary for some + // server configurations with limited resources + if (!$this->config->get('pre_schema_forced_refresh')) + { + if ($this->config->get_time_remaining() < 5) + { + $this->config->set('pre_schema_forced_refresh', true); + throw new resource_limit_reached_exception(); + } + } + $this->db->sql_return_on_error(true); $dbms = $this->config->get('dbms'); diff --git a/phpBB/phpbb/install/module/update_database/task/update.php b/phpBB/phpbb/install/module/update_database/task/update.php index 84ec6f73f5..4b2baf2c23 100644 --- a/phpBB/phpbb/install/module/update_database/task/update.php +++ b/phpBB/phpbb/install/module/update_database/task/update.php @@ -140,7 +140,7 @@ class update extends task_base ->get_classes(); $this->migrator->set_migrations($migrations); - $migration_count = count($migrations); + $migration_count = count($this->migrator->get_migrations()); $this->iohandler->set_task_count($migration_count, true); $progress_count = $this->installer_config->get('database_update_count', 0); diff --git a/phpBB/phpbb/language/language.php b/phpBB/phpbb/language/language.php index 382d4db89e..42429c2c07 100644 --- a/phpBB/phpbb/language/language.php +++ b/phpBB/phpbb/language/language.php @@ -246,14 +246,14 @@ class language } /** - * Act like lang() but takes a key and an array of parameters instead of using variadic + * Returns the raw value associated to a language key or the language key no translation is available. + * No parameter substitution is performed, can be a string or an array. * * @param string|array $key Language key - * @param array $args Parameters * * @return array|string */ - public function lang_array($key, $args = array()) + public function lang_raw($key) { // Load common language files if they not loaded yet if (!$this->common_language_files_loaded) @@ -281,6 +281,26 @@ class language return $key; } + return $lang; + } + + /** + * Act like lang() but takes a key and an array of parameters instead of using variadic + * + * @param string|array $key Language key + * @param array $args Parameters + * + * @return string + */ + public function lang_array($key, $args = array()) + { + $lang = $this->lang_raw($key); + + if ($lang === $key) + { + return $key; + } + // If the language entry is a string, we simply mimic sprintf() behaviour if (is_string($lang)) { diff --git a/phpBB/phpbb/template/assets_bag.php b/phpBB/phpbb/template/assets_bag.php new file mode 100644 index 0000000000..9013061b96 --- /dev/null +++ b/phpBB/phpbb/template/assets_bag.php @@ -0,0 +1,95 @@ +<?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. +* +*/ + +namespace phpbb\template; + +class assets_bag +{ + /** @var asset[] */ + protected $stylesheets = []; + + /** @var asset[] */ + protected $scripts = []; + + /** + * Add a css asset to the bag + * + * @param asset $asset + */ + public function add_stylesheet(asset $asset) + { + $this->stylesheets[] = $asset; + } + + /** + * Add a js script asset to the bag + * + * @param asset $asset + */ + public function add_script(asset $asset) + { + $this->scripts[] = $asset; + } + + /** + * Returns all css assets + * + * @return asset[] + */ + public function get_stylesheets() + { + return $this->stylesheets; + } + + /** + * Returns all js assets + * + * @return asset[] + */ + public function get_scripts() + { + return $this->scripts; + } + + /** + * Returns the HTML code to includes all css assets + * + * @return string + */ + public function get_stylesheets_content() + { + $output = ''; + foreach ($this->stylesheets as $stylesheet) + { + $output .= "<link href=\"{$stylesheet->get_url()}\" rel=\"stylesheet\" type=\"text/css\" media=\"screen\" />\n"; + } + + return $output; + } + + /** + * Returns the HTML code to includes all js assets + * + * @return string + */ + public function get_scripts_content() + { + $output = ''; + foreach ($this->scripts as $script) + { + $output .= "<script type=\"text/javascript\" src=\"{$script->get_url()}\"></script>\n"; + } + + return $output; + } +} diff --git a/phpBB/phpbb/template/twig/definition.php b/phpBB/phpbb/template/twig/definition.php index cb3c953692..205f0e68ee 100644 --- a/phpBB/phpbb/template/twig/definition.php +++ b/phpBB/phpbb/template/twig/definition.php @@ -19,7 +19,10 @@ namespace phpbb\template\twig; class definition { /** @var array **/ - protected $definitions = array(); + protected $definitions = array( + 'SCRIPTS' => '__SCRIPTS_PLACEHOLDER__', + 'STYLESHEETS' => '__STYLESHEETS_PLACEHOLDER__' + ); /** * Get a DEFINE'd variable diff --git a/phpBB/phpbb/template/twig/environment.php b/phpBB/phpbb/template/twig/environment.php index 6e75403159..5660ddc3a4 100644 --- a/phpBB/phpbb/template/twig/environment.php +++ b/phpBB/phpbb/template/twig/environment.php @@ -13,6 +13,8 @@ namespace phpbb\template\twig; +use phpbb\template\assets_bag; + class environment extends \Twig_Environment { /** @var \phpbb\config\config */ @@ -39,6 +41,9 @@ class environment extends \Twig_Environment /** @var array **/ protected $namespace_look_up_order = array('__main__'); + /** @var assets_bag */ + protected $assets_bag; + /** * Constructor * @@ -63,6 +68,8 @@ class environment extends \Twig_Environment $this->phpbb_root_path = $this->phpbb_path_helper->get_phpbb_root_path(); $this->web_root_path = $this->phpbb_path_helper->get_web_root_path(); + $this->assets_bag = new assets_bag(); + $options = array_merge(array( 'cache' => (defined('IN_INSTALL')) ? false : $cache_path, 'debug' => false, @@ -151,6 +158,16 @@ class environment extends \Twig_Environment } /** + * Gets the assets bag + * + * @return assets_bag + */ + public function get_assets_bag() + { + return $this->assets_bag; + } + + /** * Get the namespace look up order * * @return array @@ -174,6 +191,58 @@ class environment extends \Twig_Environment } /** + * {@inheritdoc} + */ + public function render($name, array $context = []) + { + $output = parent::render($name, $context); + + return $this->inject_assets($output); + } + + /** + * {@inheritdoc} + */ + public function display($name, array $context = []) + { + $level = ob_get_level(); + ob_start(); + + try + { + parent::display($name, $context); + } + catch (\Exception $e) + { + while (ob_get_level() > $level) + { + ob_end_clean(); + } + + throw $e; + } + + $output = ob_get_clean(); + + echo $this->inject_assets($output); + } + + /** + * Injects the assets (from INCLUDECSS/JS) in the output. + * + * @param string $output + * + * @return string + */ + private function inject_assets($output) + { + $output = str_replace('__STYLESHEETS_PLACEHOLDER__', $this->assets_bag->get_stylesheets_content(), $output); + $output = str_replace('__SCRIPTS_PLACEHOLDER__', $this->assets_bag->get_scripts_content(), $output); + + return $output; + } + + /** * Loads a template by name. * * @param string $name The template name diff --git a/phpBB/phpbb/template/twig/node/includeasset.php b/phpBB/phpbb/template/twig/node/includeasset.php index 324823b8d7..6d50eafc9d 100644 --- a/phpBB/phpbb/template/twig/node/includeasset.php +++ b/phpBB/phpbb/template/twig/node/includeasset.php @@ -49,33 +49,18 @@ abstract class includeasset extends \Twig_Node ->write("\$local_file = \$this->getEnvironment()->findTemplate(\$asset_path);\n") ->write("\$asset->set_path(\$local_file, true);\n") ->outdent() - ->write("\$asset->add_assets_version('{$config['assets_version']}');\n") - ->write("\$asset_file = \$asset->get_url();\n") ->write("}\n") + ->write("\$asset->add_assets_version('{$config['assets_version']}');\n") ->outdent() ->write("}\n") - ->write("\$context['definition']->append('{$this->get_definition_name()}', '") - ; - - $this->append_asset($compiler); - - $compiler - ->raw("\n');\n") + ->write("\$this->getEnvironment()->get_assets_bag()->add_{$this->get_setters_name()}(\$asset);") ; } /** - * Get the definition name + * Get the name of the assets bag setter * - * @return string (e.g. 'SCRIPTS') - */ - abstract public function get_definition_name(); - - /** - * Append the output code for the asset - * - * @param \Twig_Compiler A Twig_Compiler instance - * @return null + * @return string (e.g. 'script') */ - abstract protected function append_asset(\Twig_Compiler $compiler); + abstract public function get_setters_name(); } diff --git a/phpBB/phpbb/template/twig/node/includecss.php b/phpBB/phpbb/template/twig/node/includecss.php index 2dac154036..2e97d4972d 100644 --- a/phpBB/phpbb/template/twig/node/includecss.php +++ b/phpBB/phpbb/template/twig/node/includecss.php @@ -18,20 +18,8 @@ class includecss extends \phpbb\template\twig\node\includeasset /** * {@inheritdoc} */ - public function get_definition_name() + public function get_setters_name() { - return 'STYLESHEETS'; - } - - /** - * {@inheritdoc} - */ - public function append_asset(\Twig_Compiler $compiler) - { - $compiler - ->raw("<link href=\"' . ") - ->raw("\$asset_file . '\"") - ->raw(' rel="stylesheet" type="text/css" media="screen" />') - ; + return 'stylesheet'; } } diff --git a/phpBB/phpbb/template/twig/node/includejs.php b/phpBB/phpbb/template/twig/node/includejs.php index e77f2afeed..505b49757b 100644 --- a/phpBB/phpbb/template/twig/node/includejs.php +++ b/phpBB/phpbb/template/twig/node/includejs.php @@ -18,20 +18,8 @@ class includejs extends \phpbb\template\twig\node\includeasset /** * {@inheritdoc} */ - public function get_definition_name() + public function get_setters_name() { - return 'SCRIPTS'; - } - - /** - * {@inheritdoc} - */ - protected function append_asset(\Twig_Compiler $compiler) - { - $compiler - ->raw("<script type=\"text/javascript\" src=\"' . ") - ->raw("\$asset_file") - ->raw(". '\"></script>\n") - ; + return 'script'; } } diff --git a/phpBB/posting.php b/phpBB/posting.php index 3174626f6b..6cfb877f75 100644 --- a/phpBB/posting.php +++ b/phpBB/posting.php @@ -482,7 +482,7 @@ if ($mode == 'delete' || $mode == 'soft_delete') } $delete_reason = $request->variable('delete_reason', '', true); - phpbb_handle_post_delete($forum_id, $topic_id, $post_id, $post_data, ($mode == 'soft_delete'), $delete_reason); + phpbb_handle_post_delete($forum_id, $topic_id, $post_id, $post_data, ($mode == 'soft_delete' && !$request->is_set_post('delete_permanent')), $delete_reason); return; } diff --git a/phpBB/styles/prosilver/template/navbar_header.html b/phpBB/styles/prosilver/template/navbar_header.html index a02ec45830..62f32344fb 100644 --- a/phpBB/styles/prosilver/template/navbar_header.html +++ b/phpBB/styles/prosilver/template/navbar_header.html @@ -100,6 +100,7 @@ <!-- ENDIF --> <!-- IF S_REGISTERED_USER --> + <!-- EVENT navbar_header_user_profile_prepend --> <li id="username_logged_in" class="rightside <!-- IF CURRENT_USER_AVATAR --> no-bulletin<!-- ENDIF -->" data-skip-responsive="true"> <!-- EVENT navbar_header_username_prepend --> <div class="header-profile dropdown-container"> @@ -156,6 +157,7 @@ <!-- INCLUDE notification_dropdown.html --> </li> <!-- ENDIF --> + <!-- EVENT navbar_header_user_profile_append --> <!-- ELSE --> <li class="rightside" data-skip-responsive="true"> <a href="{U_LOGIN_LOGOUT}" title="{L_LOGIN_LOGOUT}" accesskey="x" role="menuitem"> diff --git a/phpBB/styles/prosilver/template/search_results.html b/phpBB/styles/prosilver/template/search_results.html index cd8ce66a74..ea558fd7ae 100644 --- a/phpBB/styles/prosilver/template/search_results.html +++ b/phpBB/styles/prosilver/template/search_results.html @@ -78,27 +78,25 @@ <!-- EVENT search_results_topic_before --> <li class="row<!-- IF searchresults.S_ROW_COUNT is even --> bg1<!-- ELSE --> bg2<!-- ENDIF -->"> <dl class="row-item {searchresults.TOPIC_IMG_STYLE}"> - <dt <!-- IF searchresults.TOPIC_ICON_IMG -->style="background-image: url({T_ICONS_PATH}{searchresults.TOPIC_ICON_IMG}); background-repeat: no-repeat;"<!-- ENDIF --> title="{searchresults.TOPIC_FOLDER_IMG_ALT}"> + <dt<!-- IF searchresults.TOPIC_ICON_IMG and S_TOPIC_ICONS --> style="background-image: url({T_ICONS_PATH}{searchresults.TOPIC_ICON_IMG}); background-repeat: no-repeat;"<!-- ENDIF --> title="{searchresults.TOPIC_FOLDER_IMG_ALT}"> <!-- IF searchresults.S_UNREAD_TOPIC and not S_IS_BOT --><a href="{searchresults.U_NEWEST_POST}" class="row-item-link"></a><!-- ENDIF --> <div class="list-inner"> - <!-- EVENT topiclist_row_prepend --> <!-- IF searchresults.S_UNREAD_TOPIC and not S_IS_BOT --> <a class="unread" href="{searchresults.U_NEWEST_POST}"> <i class="icon fa-file fa-fw icon-red icon-md" aria-hidden="true"></i><span class="sr-only">{NEW_POST}</span> - </a> + </a> <!-- ENDIF --> <a href="{searchresults.U_VIEW_TOPIC}" class="topictitle">{searchresults.TOPIC_TITLE}</a> - <!-- IF searchresults.S_TOPIC_UNAPPROVED or searchresults.S_POSTS_UNAPPROVED --> <a href="{searchresults.U_MCP_QUEUE}" title="{TOPIC_UNAPPROVED}"> <i class="icon fa-question fa-fw icon-blue" aria-hidden="true"></i><span class="sr-only">{TOPIC_UNAPPROVED}</span> - </a> + </a> <!-- ENDIF --> <!-- IF searchresults.S_TOPIC_DELETED --> <a href="{searchresults.U_MCP_QUEUE}" title="{TOPIC_DELETED}"> <i class="icon fa-recycle fa-fw icon-green" aria-hidden="true"></i><span class="sr-only">{TOPIC_DELETED}</span> - </a> + </a> <!-- ENDIF --> <!-- IF searchresults.S_TOPIC_REPORTED --> <a href="{searchresults.U_MCP_REPORT}" title="{TOPIC_REPORTED}"> @@ -106,8 +104,24 @@ </a> <!-- ENDIF --> <br /> + + <!-- IF not S_IS_BOT --> + <div class="responsive-show" style="display: none;"> + {L_LAST_POST} {L_POST_BY_AUTHOR} {searchresults.LAST_POST_AUTHOR_FULL} « <a href="{searchresults.U_LAST_POST}" title="{L_GOTO_LAST_POST}">{searchresults.LAST_POST_TIME}</a> + <br />{L_POSTED} {L_IN} <a href="{searchresults.U_VIEW_FORUM}">{searchresults.FORUM_TITLE}</a> + </div> + <!-- IF searchresults.TOPIC_REPLIES --><span class="responsive-show left-box" style="display: none;">{L_REPLIES}{L_COLON} <strong>{searchresults.TOPIC_REPLIES}</strong></span><!-- ENDIF --> + <!-- ENDIF --> + + <div class="responsive-hide"> + <!-- IF searchresults.S_HAS_POLL --><i class="icon fa-bar-chart fa-fw" aria-hidden="true"></i><!-- ENDIF --> + <!-- IF searchresults.ATTACH_ICON_IMG --><i class="icon fa-paperclip fa-fw" aria-hidden="true"></i><!-- ENDIF --> + {L_POST_BY_AUTHOR} {searchresults.TOPIC_AUTHOR_FULL} » {searchresults.FIRST_POST_TIME} » {L_IN} <a href="{searchresults.U_VIEW_FORUM}">{searchresults.FORUM_TITLE}</a> + </div> + <!-- IF .searchresults.pagination --> <div class="pagination"> + <span><i class="icon fa-clone fa-fw" aria-hidden="true"></i></span> <ul> <!-- BEGIN pagination --> <!-- IF searchresults.pagination.S_IS_PREV --> @@ -120,26 +134,20 @@ </ul> </div> <!-- ENDIF --> - <!-- IF searchresults.S_HAS_POLL --><i class="icon fa-bar-chart fa-fw" aria-hidden="true"></i> <!-- ENDIF --> - <!-- IF searchresults.ATTACH_ICON_IMG --><i class="icon fa-paperclip fa-fw" aria-hidden="true"></i><!-- ENDIF --> - {L_POST_BY_AUTHOR} {searchresults.TOPIC_AUTHOR_FULL} » {searchresults.FIRST_POST_TIME} » {L_IN} <a href="{searchresults.U_VIEW_FORUM}">{searchresults.FORUM_TITLE}</a> - <!-- EVENT topiclist_row_append --> + <!-- EVENT topiclist_row_append --> </div> </dt> - <dd class="posts">{searchresults.TOPIC_REPLIES}</dd> - <dd class="views">{searchresults.TOPIC_VIEWS}</dd> + <dd class="posts">{searchresults.TOPIC_REPLIES} <dfn>{L_REPLIES}</dfn></dd> + <dd class="views">{searchresults.TOPIC_VIEWS} <dfn>{L_VIEWS}</dfn></dd> <dd class="lastpost"> - <span> - {L_POST_BY_AUTHOR} {searchresults.LAST_POST_AUTHOR_FULL} + <span><dfn>{L_LAST_POST} </dfn>{L_POST_BY_AUTHOR} {searchresults.LAST_POST_AUTHOR_FULL} <!-- IF not S_IS_BOT --> <a href="{searchresults.U_LAST_POST}" title="{L_GOTO_LAST_POST}"> <i class="icon fa-external-link-square fa-fw icon-lightgray icon-md" aria-hidden="true"></i><span class="sr-only">{VIEW_LATEST_POST}</span> - </a> + </a> <!-- ENDIF --> - <br /> - {searchresults.LAST_POST_TIME} - <br /> + <br />{searchresults.LAST_POST_TIME} </span> </dd> </dl> diff --git a/phpBB/styles/prosilver/template/viewforum_body.html b/phpBB/styles/prosilver/template/viewforum_body.html index c17d99be74..05f57ee8a6 100644 --- a/phpBB/styles/prosilver/template/viewforum_body.html +++ b/phpBB/styles/prosilver/template/viewforum_body.html @@ -161,19 +161,19 @@ <!-- IF topicrow.S_UNREAD_TOPIC and not S_IS_BOT --> <a class="unread" href="{topicrow.U_NEWEST_POST}"> <i class="icon fa-file fa-fw icon-red icon-md" aria-hidden="true"></i><span class="sr-only">{NEW_POST}</span> - </a> + </a> <!-- ENDIF --> <a href="{topicrow.U_VIEW_TOPIC}" class="topictitle">{topicrow.TOPIC_TITLE}</a> <!-- IF topicrow.S_TOPIC_UNAPPROVED or topicrow.S_POSTS_UNAPPROVED --> <a href="{topicrow.U_MCP_QUEUE}" title="{TOPIC_UNAPPROVED}"> <i class="icon fa-question fa-fw icon-blue" aria-hidden="true"></i><span class="sr-only">{TOPIC_UNAPPROVED}</span> - </a> + </a> <!-- ENDIF --> <!-- IF topicrow.S_TOPIC_DELETED --> - <a href="{topicrow.U_MCP_QUEUE}" title="{TOPIC_DELETED"> + <a href="{topicrow.U_MCP_QUEUE}" title="{TOPIC_DELETED}"> <i class="icon fa-recycle fa-fw icon-green" aria-hidden="true"></i><span class="sr-only">{TOPIC_DELETED}</span> - </a> - <!-- ENDIF --> + </a> + <!-- ENDIF --> <!-- IF topicrow.S_TOPIC_REPORTED --> <a href="{topicrow.U_MCP_REPORT}" title="{TOPIC_REPORTED}"> <i class="icon fa-exclamation fa-fw icon-red" aria-hidden="true"></i><span class="sr-only">{TOPIC_REPORTED}</span> @@ -222,7 +222,7 @@ <!-- IF not S_IS_BOT --> <a href="{topicrow.U_LAST_POST}" title="{L_GOTO_LAST_POST}"> <i class="icon fa-external-link-square fa-fw icon-lightgray icon-md" aria-hidden="true"></i><span class="sr-only">{VIEW_LATEST_POST}</span> - </a> + </a> <!-- ENDIF --> <br />{topicrow.LAST_POST_TIME} </span> diff --git a/phpBB/styles/prosilver/template/viewtopic_topic_tools.html b/phpBB/styles/prosilver/template/viewtopic_topic_tools.html index 96c514f1d9..397c807bb3 100644 --- a/phpBB/styles/prosilver/template/viewtopic_topic_tools.html +++ b/phpBB/styles/prosilver/template/viewtopic_topic_tools.html @@ -1,6 +1,6 @@ <!-- IF not S_IS_BOT and (U_WATCH_TOPIC or U_BOOKMARK_TOPIC or U_BUMP_TOPIC or U_EMAIL_TOPIC or U_PRINT_TOPIC or S_DISPLAY_TOPIC_TOOLS) --> <div class="dropdown-container dropdown-button-control topic-tools"> - <span title="{L_PM_TOOLS}" class="button button-secondary dropdown-trigger dropdown-select"> + <span title="{L_TOPIC_TOOLS}" class="button button-secondary dropdown-trigger dropdown-select"> <i class="icon fa-wrench fa-fw" aria-hidden="true"></i> <span class="caret"><i class="icon fa-sort-down fa-fw" aria-hidden="true"></i></span> </span> diff --git a/tests/extension/ext/vendor2/bar/migrations/bar.php b/tests/extension/ext/vendor2/bar/migrations/bar.php new file mode 100644 index 0000000000..ea5ddb6b8b --- /dev/null +++ b/tests/extension/ext/vendor2/bar/migrations/bar.php @@ -0,0 +1,7 @@ +<?php + +namespace vendor2\foo\migrations; + +class bar +{ +} diff --git a/tests/extension/ext/vendor2/bar/migrations/foo.php b/tests/extension/ext/vendor2/bar/migrations/foo.php new file mode 100644 index 0000000000..d727c2f954 --- /dev/null +++ b/tests/extension/ext/vendor2/bar/migrations/foo.php @@ -0,0 +1,54 @@ +<?php + +namespace vendor2\foo\migrations; + +class foo implements \phpbb\db\migration\migration_interface +{ + /** + * {@inheritdoc} + */ + static public function depends_on() + { + return array(); + } + + /** + * {@inheritdoc} + */ + public function effectively_installed() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function update_schema() + { + return array(); + } + + /** + * {@inheritdoc} + */ + public function revert_schema() + { + return array(); + } + + /** + * {@inheritdoc} + */ + public function update_data() + { + return array(); + } + + /** + * {@inheritdoc} + */ + public function revert_data() + { + return array(); + } +} diff --git a/tests/extension/extension_base_test.php b/tests/extension/extension_base_test.php index eee38186db..775a23e198 100644 --- a/tests/extension/extension_base_test.php +++ b/tests/extension/extension_base_test.php @@ -11,6 +11,9 @@ * */ require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; +require_once dirname(__FILE__) . '/ext/vendor2/bar/migrations/bar.php'; +require_once dirname(__FILE__) . '/ext/vendor2/bar/migrations/foo.php'; +require_once dirname(__FILE__) . '/ext/vendor2/bar/migrations/migration.php'; class phpbb_extension_extension_base_test extends phpbb_test_case { @@ -61,9 +64,7 @@ class phpbb_extension_extension_base_test extends phpbb_test_case return array( array( 'vendor2/bar', - array( - '\vendor2\bar\migrations\migration', - ), + array('\vendor2\bar\migrations\migration'), ), ); } @@ -74,6 +75,8 @@ class phpbb_extension_extension_base_test extends phpbb_test_case public function test_suffix_get_classes($extension_name, $expected) { $extension = $this->extension_manager->get_extension($extension_name); - $this->assertEquals($expected, self::$reflection_method_get_migration_file_list->invoke($extension)); + $migration_classes = self::$reflection_method_get_migration_file_list->invoke($extension); + sort($migration_classes); + $this->assertEquals($expected, $migration_classes); } } diff --git a/tests/files/types_remote_test.php b/tests/files/types_remote_test.php index a85844ee78..caed5c9e05 100644 --- a/tests/files/types_remote_test.php +++ b/tests/files/types_remote_test.php @@ -85,8 +85,8 @@ class phpbb_files_types_remote_test extends phpbb_test_case array('500k', 'http://example.com/foo/bar.png'), array('500M', 'http://example.com/foo/bar.png'), array('500m', 'http://example.com/foo/bar.png'), - array('500k', 'http://google.com/.png', 'DISALLOWED_CONTENT'), - array('1', 'http://google.com/.png', 'WRONG_FILESIZE'), + array('500k', 'http://google.com/?.png', array('DISALLOWED_EXTENSION', 'DISALLOWED_CONTENT')), + array('1', 'http://google.com/?.png', array('WRONG_FILESIZE')), array('500g', 'http://example.com/foo/bar.png'), array('foobar', 'http://example.com/foo/bar.png'), array('-5k', 'http://example.com/foo/bar.png'), @@ -96,7 +96,7 @@ class phpbb_files_types_remote_test extends phpbb_test_case /** * @dataProvider data_get_max_file_size */ - public function test_get_max_file_size($max_file_size, $link, $expected = 'URL_NOT_FOUND') + public function test_get_max_file_size($max_file_size, $link, $expected = array('URL_NOT_FOUND')) { $php_ini = $this->getMock('\bantu\IniGetWrapper\IniGetWrapper', array('getString')); $php_ini->expects($this->any()) @@ -109,7 +109,7 @@ class phpbb_files_types_remote_test extends phpbb_test_case $file = $type_remote->upload($link); - $this->assertSame(array($expected), $file->error); + $this->assertSame($expected, $file->error); } public function test_upload_timeout() @@ -120,7 +120,7 @@ class phpbb_files_types_remote_test extends phpbb_test_case $type_remote->set_upload($upload); $upload->upload_timeout = -5; - $file = $type_remote->upload('http://google.com/.png'); + $file = $type_remote->upload('http://google.com/?.png'); $this->assertSame(array('REMOTE_UPLOAD_TIMEOUT'), $file->error); } @@ -133,7 +133,7 @@ class phpbb_files_types_remote_test extends phpbb_test_case $type_remote->set_upload($upload); $type_remote::$tempnam_path = $this->phpbb_root_path . 'cache/wrong/path'; - $file = $type_remote->upload('http://google.com/.png'); + $file = $type_remote->upload('http://google.com/?.png'); $this->assertSame(array('NOT_UPLOADED'), $file->error); $type_remote::$tempnam_path = ''; diff --git a/tests/functional/plupload_test.php b/tests/functional/plupload_test.php index d358681ad1..9d284a7e57 100644 --- a/tests/functional/plupload_test.php +++ b/tests/functional/plupload_test.php @@ -107,11 +107,11 @@ class phpbb_functional_plupload_test extends phpbb_functional_test_case if ($i < self::CHUNKS - 1) { - $this->assertContains('{"jsonrpc":"2.0","id":"id","result":null}', self::$client->getResponse()->getContent()); + $this->assertContains('{"jsonrpc":"2.0","id":"id","result":null}', self::get_content()); } else { - $response = json_decode(self::$client->getResponse()->getContent(), true); + $response = json_decode(self::get_content(), true); $this->assertEquals('valid.jpg', $response['data'][0]['real_filename']); } @@ -134,7 +134,8 @@ class phpbb_functional_plupload_test extends phpbb_functional_test_case 'error' => UPLOAD_ERR_OK, ); - $crawler = self::$client->request( + self::$client->setServerParameter('HTTP_X_PHPBB_USING_PLUPLOAD', '1'); + self::$client->request( 'POST', $url . '&sid=' . $this->sid, array( @@ -144,11 +145,10 @@ class phpbb_functional_plupload_test extends phpbb_functional_test_case 'real_filename' => 'valid.jpg', 'add_file' => $this->lang('ADD_FILE'), ), - array('fileupload' => $file), - array('X-PHPBB-USING-PLUPLOAD' => '1') + array('fileupload' => $file) ); - $response = json_decode(self::$client->getResponse()->getContent(), true); + $response = json_decode(self::get_content(), true); $this->assertEquals('valid.jpg', $response['data'][0]['real_filename']); } } diff --git a/tests/language/language_test.php b/tests/language/language_test.php index 6a814e39dc..29b4873dcb 100644 --- a/tests/language/language_test.php +++ b/tests/language/language_test.php @@ -53,6 +53,25 @@ class phpbb_language_test extends phpbb_test_case $this->assertFalse($this->lang->is_set(array('PHPBB', 'PHP'))); } + public function test_lang_raw() + { + $this->assertEquals($this->lang->lang_raw('FOO'), 'BAR'); + $this->assertEquals($this->lang->lang_raw('VOID'), 'VOID'); + $this->assertEquals($this->lang->lang_raw('ARRY'), array( + 0 => 'No posts', // 0 + 1 => '1 post', // 1 + 2 => '%d posts', // 2+ + )); + } + + public function test_lang_array() + { + $this->assertEquals($this->lang->lang_array('FOO'), 'BAR'); + $this->assertEquals($this->lang->lang_array('VOID'), 'VOID'); + $this->assertEquals($this->lang->lang_array('ARRY', [0]), 'No posts'); + $this->assertEquals($this->lang->lang_array('FOO', [2, 3, 'BARZ']), 'BAR'); + } + public function test_lang() { // No param diff --git a/tests/mock/migrator.php b/tests/mock/migrator.php index 293f115335..4d1aca0a0a 100644 --- a/tests/mock/migrator.php +++ b/tests/mock/migrator.php @@ -21,10 +21,6 @@ class phpbb_mock_migrator extends \phpbb\db\migrator { } - public function set_migrations($class_names) - { - } - public function update() { } diff --git a/tests/mock/phpbb_di_container_builder.php b/tests/mock/phpbb_di_container_builder.php index 59cdf0bb2b..23dc3d1e8b 100644 --- a/tests/mock/phpbb_di_container_builder.php +++ b/tests/mock/phpbb_di_container_builder.php @@ -17,4 +17,14 @@ class phpbb_mock_phpbb_di_container_builder extends \phpbb\di\container_builder { return $this->phpbb_root_path . '../../tmp/container.' . $this->php_ext; } + + /** + * Get the filename under which the dumped extensions autoloader will be stored. + * + * @return string Path for dumped extensions autoloader + */ + protected function get_autoload_filename() + { + return $this->phpbb_root_path . '../../tmp/autoload.' . $this->php_ext; + } } diff --git a/tests/test_framework/phpbb_database_test_connection_manager.php b/tests/test_framework/phpbb_database_test_connection_manager.php index fa50d89a70..27ac64e21d 100644 --- a/tests/test_framework/phpbb_database_test_connection_manager.php +++ b/tests/test_framework/phpbb_database_test_connection_manager.php @@ -84,11 +84,18 @@ class phpbb_database_test_connection_manager break; default: - $dsn .= 'host=' . $this->config['dbhost']; - - if ($this->config['dbport']) + if (!empty($this->config['dbport']) && !is_numeric($this->config['dbport']) && $this->dbms['PDO'] != 'pgsql') + { + $dsn .= 'unix_socket=' . $this->config['dbport']; + } + else { - $dsn .= ';port=' . $this->config['dbport']; + $dsn .= 'host=' . $this->config['dbhost']; + + if ($this->config['dbport']) + { + $dsn .= ';port=' . $this->config['dbport']; + } } if ($use_db) diff --git a/tests/test_framework/phpbb_functional_test_case.php b/tests/test_framework/phpbb_functional_test_case.php index b91894f9c0..34fbcec0e2 100644 --- a/tests/test_framework/phpbb_functional_test_case.php +++ b/tests/test_framework/phpbb_functional_test_case.php @@ -16,6 +16,7 @@ require_once __DIR__ . '/mock/phpbb_mock_null_installer_task.php'; class phpbb_functional_test_case extends phpbb_test_case { + /** @var \Goutte\Client */ static protected $client; static protected $cookieJar; static protected $root_url; @@ -81,9 +82,6 @@ class phpbb_functional_test_case extends phpbb_test_case self::$cookieJar = new CookieJar; self::$client = new Goutte\Client(array(), null, self::$cookieJar); - // Reset the curl handle because it is 0 at this point and not a valid - // resource - self::$client->getClient()->getCurlMulti()->reset(true); // Clear the language array so that things // that were added in other tests are gone @@ -94,6 +92,8 @@ class phpbb_functional_test_case extends phpbb_test_case foreach (static::setup_extensions() as $extension) { + $this->purge_cache(); + $sql = 'SELECT ext_active FROM ' . EXT_TABLE . " WHERE ext_name = '" . $db->sql_escape($extension). "'"; @@ -167,7 +167,7 @@ class phpbb_functional_test_case extends phpbb_test_case */ static public function get_content() { - return self::$client->getResponse()->getContent(); + return (string) self::$client->getResponse()->getContent(); } // bootstrap, called after board is set up @@ -841,7 +841,7 @@ class phpbb_functional_test_case extends phpbb_test_case static public function assert_response_html($status_code = 200) { // Any output before the doc type means there was an error - $content = self::$client->getResponse()->getContent(); + $content = self::get_content(); self::assertNotContains('[phpBB Debug]', $content); self::assertStringStartsWith('<!DOCTYPE', trim($content), 'Output found before DOCTYPE specification.'); @@ -862,7 +862,7 @@ class phpbb_functional_test_case extends phpbb_test_case static public function assert_response_xml($status_code = 200) { // Any output before the xml opening means there was an error - $content = self::$client->getResponse()->getContent(); + $content = self::get_content(); self::assertNotContains('[phpBB Debug]', $content); self::assertStringStartsWith('<?xml', trim($content), 'Output found before XML specification.'); |