Compare commits

...

51 commits

Author SHA1 Message Date
rxu
497d0a1521
Merge 3a5247d01b into 9adb7eb9fe 2025-05-20 10:36:39 +00:00
rxu
3a5247d01b
[ticket/15214] Get event dispatcher from environment rather than as dependency
Also this will allow to significantly reduce unrelated tests changes.

PHPBB3-15214
2025-05-20 17:36:15 +07:00
rxu
5e0dc9ef2e
[ticket/15214] Fix rebasing and some other issues
PHPBB3-15214
2025-05-20 16:24:38 +07:00
rxu
8338ff9e56
[ticket/15214] Fix Windows tests
PHPBB3-15214
2025-05-20 16:24:37 +07:00
toxyy
84e7e34a66
[ticket/15214] Fix tests again
Adding per rxu's recommendation

PHPBB3-15214
2025-05-20 16:24:37 +07:00
rxu
18bae795f0
[ticket/15214] Fix Windows tests
Purge Twig compiled cache in Windows.
Set appropriate folder access control options to do that.

PHPBB3-15214
2025-05-20 16:24:36 +07:00
rxu
09fd86ffb0
[ticket/15214] Fix test foo/foo extension listener
PHPBB3-15214
2025-05-20 16:24:34 +07:00
rxu
4a00212f2d
[ticket/15214] Optimize event node code and add template event order tests
PHPBB3-15214
2025-05-20 16:24:34 +07:00
toxyy
cb47d78d26
[ticket/15214] Update block, restart tests
Make docblock look a bit cleaner and restart the tests

PHPBB3-15214
2025-05-20 16:24:33 +07:00
toxyy
0eb98d51e2
[ticket/15214] Provide usage example within event docblock
Adds similar usage examples like the event core.permissions has

PHPBB3-15214
2025-05-20 16:24:32 +07:00
toxyy
82a5e20f3e
[ticket/15214] Replace arrow functions with anonymous functions
Arrow functions aren't added until PHP 7.4, so we can't use them yet.
Anonymous functions have been added since PHP 5.3

PHPBB3-15214
2025-05-20 16:24:32 +07:00
toxyy
71fe9d60c4
[ticket/15214] Add fixes for various other tests
Add new dispatch parameter to the template\twig\extension calls

PHPBB3-15214
2025-05-20 16:24:31 +07:00
toxyy
59b482a222
[ticket/15214] Test fix for test_bbcode_firstpass
Add new dispatch parameter to the template\twig\extension call

PHPBB3-15214
2025-05-20 16:24:30 +07:00
toxyy
d934c8c4b7
[ticket/15214] Test fix for test_helper_url_no_rewrite
Add new dispatch parameter to the template\twig\extension call

PHPBB3-15214
2025-05-20 16:24:29 +07:00
toxyy
d07aeb00d8
[ticket/15214] Add event & functionality for assigning template event priority
Event added to allow template events to be assigned priority per extension,
event location chosen so that it only fires once.
Twig node event class refactored to allow template event priority assignment,
compile calls are deferred until all locations are processed
per extension namespace.
Priority precedence mirrors Symfony priority, with higher numbers
being placed at the beginning of the array.
Duplicate priority assignment will currently have the later events
compiled before the others.

PHPBB3-15214
2025-05-20 16:24:24 +07:00
Marc Alexander
9adb7eb9fe Merge branch '3.3.x' 2025-05-19 16:19:02 +00:00
Marc Alexander
29a74db9ec
Merge pull request #6815 from rxu/ticket/17512
[ticket/17512] Add PHP Sniffer coding standard for union types
2025-05-19 18:01:10 +02:00
Marc Alexander
5e12e8b6e9
Merge pull request #6816 from marc1706/ticket/17510-master
[ticket/17510] Fix Code Sniffer deprecations -- master version
2025-05-19 17:53:21 +02:00
Marc Alexander
478d119d42
Merge pull request #6814 from iMattPro/ticket/17510
[ticket/17510] Fix Code Sniffer deprecations
2025-05-19 17:53:18 +02:00
Marc Alexander
1f6f0a3547
Merge pull request #6806 from marc1706/ticket/17501
[ticket/17501] Adjust navbar and breadcrumbs, improve usability
2025-05-19 17:51:27 +02:00
Marc Alexander
5281807e1f Merge branch '3.3.x' 2025-05-19 15:45:56 +00:00
Marc Alexander
e2554d1fb0
Merge pull request #6812 from rxu/ticket/17506-master
[ticket/17506] Ensure superglobal variables are arrays before applying addition - master
2025-05-19 17:44:50 +02:00
Marc Alexander
b3478d83d1
Merge pull request #6811 from rxu/ticket/17506
[ticket/17506] Ensure superglobal variables are arrays before applying addition - 3.3.x
2025-05-19 17:44:47 +02:00
Marc Alexander
ff6f3b2a8c
Merge branch 'ticket/17510' into ticket/17510-master 2025-05-18 09:37:37 +02:00
rxu
1cd17caf87
[ticket/17512] Fix the code layout error reported
PHPBB-17512
2025-05-15 20:42:24 +07:00
rxu
6a0c949ed1
[ticket/17512] Fix sorting types check logic
PHPBB-17512
2025-05-15 20:34:16 +07:00
rxu
146f917d19
[ticket/17512] Fix sniffer error
The error message was: $stackPtr is not a class member var

PHPBB-17512
2025-05-15 20:17:27 +07:00
rxu
c01d1967dd
[ticket/17512] Fix handling tokens with no/empty type hint
PHPBB-17512
2025-05-15 17:02:03 +07:00
rxu
7a4b3f52ae
[ticket/17512] Refactor PHP Sniffer coding standard logic
PHPBB-17512
2025-05-15 16:50:51 +07:00
rxu
b3ff1d7e34
[ticket/17512] Add PHP Sniffer coding standard for union types
PHPBB-17512
2025-05-15 11:47:47 +07:00
Matt Friedman
7268859226
[ticket/17510] Fix Code Sniffer deprecations
PHPBB-17510
2025-05-13 18:03:10 -07:00
Marc Alexander
61bede748a
Merge pull request #6813 from rxu/ticket/17508
[ticket/17508] Fix PHP warning on custom profile fields edit
2025-05-12 21:33:09 +02:00
rxu
0562984999
[ticket/17508] Fix PHP warning on custom profile fields edit
PHPBB-17508
2025-05-13 00:02:56 +07:00
rxu
83e1886e27
Merge branch 'ticket/17506' into ticket/17506-master 2025-05-09 17:04:57 +07:00
rxu
10947f3d49
[ticket/17506] Ensure superglobal variables are arrays before applying addition
PHPBB-17506
2025-05-09 16:58:36 +07:00
Marc Alexander
f94423d491 Merge branch '3.3.x' 2025-05-07 18:16:11 +00:00
Marc Alexander
b6200d6690
Merge pull request #6800 from rxu/ticket/17491-master
[ticket/17491] Fix caching search results - master
2025-05-07 20:15:57 +02:00
Marc Alexander
04f2141a7d
Merge pull request #6799 from rxu/ticket/17491
[ticket/17491] Fix caching search results - 3.3.x
2025-05-07 20:15:53 +02:00
rxu
4b7d7c2fc7
Merge branch 'ticket/17491' into ticket/17491-master 2025-05-07 00:40:32 +07:00
rxu
3d76a8bd09
[ticket/17491] Fix rows duplication in search results
PHPBB-17491
2025-05-07 00:31:21 +07:00
rxu
1c399dcab7
[ticket/17491] Consistently apply array_unique to search results
PHPBB-17491
2025-05-06 00:11:45 +07:00
rxu
e91c7d42a9
[ticket/17491] Add test
PHPBB-17491
2025-05-05 23:46:09 +07:00
Marc Alexander
3eaf4829bb
[ticket/17501] Adjust notification dropdown color and spacing
PHPBB-17501
2025-04-24 21:03:44 +02:00
Marc Alexander
3b233d9c28
[ticket/17501] Update events docs
PHPBB-17501
2025-04-21 21:44:22 +02:00
Marc Alexander
3801eb0946
[ticket/17501] Remove not needed parentheses
PHPBB-17501
2025-04-21 20:38:59 +02:00
Marc Alexander
3a553f07bc
[ticket/17501] Remove unused CSS properties
PHPBB-17501
2025-04-21 20:36:04 +02:00
Marc Alexander
b666bc9e0a
[ticket/17501] Add to top button to viewtopic and add missing title in FAQ
PHPBB-17501
2025-04-21 20:33:36 +02:00
Marc Alexander
81c49aa6a5
[ticket/17501] Remove max width from subforums
PHPBB-17501
2025-04-21 20:18:40 +02:00
Marc Alexander
6fd9a78872
[ticket/17501] Adjust CSS for moved navbar & breadcrumbs
PHPBB-17501
2025-04-21 17:37:53 +02:00
Marc Alexander
7f3b37560e
[ticket/17501] Move navbar above header and breadcrumbs below
PHPBB-17501
2025-04-21 17:37:38 +02:00
rxu
8d0d6c012c
[ticket/17491] Fix caching search results
PHPBB-17491
2025-04-12 14:35:40 +07:00
46 changed files with 854 additions and 162 deletions

View file

@ -0,0 +1,116 @@
<?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\Sniffs\CodeLayout;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
/**
* Checks that union type declarations follows the coding guidelines.
*/
class UnionTypesCheckSniff implements Sniff
{
/**
* {@inheritdoc}
*/
public function register()
{
return [
T_FUNCTION,
T_CLASS,
];
}
/**
* {@inheritdoc}
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['type'] === 'T_FUNCTION')
{
$method_params = $phpcsFile->getMethodParameters($stackPtr);
$method_params_array = array_column($method_params, 'type_hint', 'token');
foreach ($method_params_array as $stack_pointer => $type_hint)
{
$this->check_union_type($phpcsFile, $stack_pointer, $type_hint);
}
$method_properties = $phpcsFile->getMethodProperties($stackPtr);
$this->check_union_type($phpcsFile, $stackPtr, $method_properties['return_type']);
}
else if ($tokens[$stackPtr]['type'] === 'T_CLASS')
{
$class_token = $tokens[$stackPtr];
$class_closer_pointer = $class_token['scope_closer'];
$first_method_pointer = $phpcsFile->findNext(T_FUNCTION, $stackPtr);
$class_members_declarations_end_pointer = $first_method_pointer ?: $class_closer_pointer;
$stack_pointer = $stackPtr;
while(($class_member_pointer = $phpcsFile->findNext(T_VARIABLE, $stack_pointer)) !== false && ($class_member_pointer < $class_members_declarations_end_pointer))
{
$properties = $phpcsFile->getMemberProperties($class_member_pointer);
$this->check_union_type($phpcsFile, $class_member_pointer, $properties['type']);
$stack_pointer = $class_member_pointer + 1;
}
}
}
public function check_union_type(File $phpcsFile, $stack_pointer, $type_hint)
{
if (empty($type_hint))
{
return;
}
if (!strpos($type_hint, '|') && $type_hint[0] == '?') // Check nullable shortcut syntax
{
$type = substr($type_hint, 1);
$error = 'Nullable shortcut syntax must not be used. Use union type instead: %1$s|null; found %2$s';
$data = [$type, $type_hint];
$phpcsFile->addError($error, $stack_pointer, 'ShortNullableSyntax', $data);
}
else if ((count($types_array = explode('|', $type_hint))) > 1) // Check union type layout
{
$types_array_null_less = $types_array;
// Check 'null' to be the last element
$null_position = array_search('null', $types_array);
if ($null_position !== false && $null_position != array_key_last($types_array))
{
$error = 'The "null" type hint must be the last of the union type elements; found %s';
$data = [implode('|', $types_array)];
$phpcsFile->addError($error, $stack_pointer, 'NullAlwaysLast', $data);
}
// Check types excepting 'null' to follow alphabetical order
if ($null_position !== false)
{
array_splice($types_array_null_less, $null_position, 1);
}
if (count($types_array_null_less) > 1)
{
$types_array_null_less_sorted = $types_array_null_less;
sort($types_array_null_less_sorted);
if (!empty(array_diff_assoc($types_array_null_less, $types_array_null_less_sorted)))
{
$error = 'Union type elements must be sorted alphabetically excepting the "null" type hint must be the last if any; found %s';
$data = [implode('|', $types_array)];
$phpcsFile->addError($error, $stack_pointer, 'AlphabeticalSort', $data);
}
}
}
}
}

View file

@ -11,6 +11,8 @@
*
*/
namespace phpbb\Sniffs\Commenting;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
@ -21,7 +23,7 @@ use PHP_CodeSniffer\Sniffs\Sniff;
* @package code_sniffer
* @author Manuel Pichler <mapi@phpundercontrol.org>
*/
class phpbb_Sniffs_Commenting_FileCommentSniff implements Sniff
class FileCommentSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.

View file

@ -11,6 +11,8 @@
*
*/
namespace phpbb\Sniffs\ControlStructures;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
@ -18,7 +20,7 @@ use PHP_CodeSniffer\Sniffs\Sniff;
* Checks that the opening brace of a control structures is on the line after.
* From Generic_Sniffs_Functions_OpeningFunctionBraceBsdAllmanSniff
*/
class phpbb_Sniffs_ControlStructures_OpeningBraceBsdAllmanSniff implements Sniff
class OpeningBraceBsdAllmanSniff implements Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.

View file

@ -10,6 +10,7 @@
* the docs/CREDITS.txt file.
*
*/
namespace phpbb\Sniffs\ControlStructures;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
@ -18,7 +19,7 @@ use PHP_CodeSniffer\Sniffs\Sniff;
* Checks that there is exactly one space between the keyword and the opening
* parenthesis of a control structures.
*/
class phpbb_Sniffs_ControlStructures_OpeningParenthesisSniff implements Sniff
class OpeningParenthesisSniff implements Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.

View file

@ -11,6 +11,8 @@
*
*/
namespace phpbb\Sniffs\ControlStructures;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
@ -18,7 +20,7 @@ use PHP_CodeSniffer\Sniffs\Sniff;
* Checks that the visibility qualifiers are placed after the static keyword
* according to the coding guidelines
*/
class phpbb_Sniffs_ControlStructures_StaticKeywordSniff implements Sniff
class StaticKeywordSniff implements Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.

View file

@ -11,13 +11,15 @@
*
*/
namespace phpbb\Sniffs\Namespaces;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
/**
* Checks that each use statement is used.
*/
class phpbb_Sniffs_Namespaces_UnusedUseSniff implements Sniff
class UnusedUseSniff implements Sniff
{
const FIND = [
T_NS_SEPARATOR,

View file

@ -17,9 +17,6 @@
<!-- There MUST not be more than one statement per line. -->
<rule ref="Generic.Formatting.DisallowMultipleStatements" />
<!-- Call-time pass-by-reference MUST not be used. -->
<rule ref="Generic.Functions.CallTimePassByReference.NotAllowed" />
<!-- Filenames MUST be lowercase. -->
<rule ref="Generic.Files.LowercasedFilename" />

View file

@ -45,4 +45,8 @@
<!-- There MUST NOT be unused use statements. -->
<rule ref="./phpbb/Sniffs/Namespaces/UnusedUseSniff.php" />
<!-- The null type SHALL be the last union type element, other types SHALL follow alphabetical order.
No nullable type shortcut syntax should be used. -->
<rule ref="./phpbb/Sniffs/CodeLayout/UnionTypesCheckSniff.php" />
</ruleset>

10
phpBB/composer.lock generated
View file

@ -9459,16 +9459,16 @@
},
{
"name": "squizlabs/php_codesniffer",
"version": "3.12.2",
"version": "3.13.0",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
"reference": "6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa"
"reference": "65ff2489553b83b4597e89c3b8b721487011d186"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa",
"reference": "6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa",
"url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/65ff2489553b83b4597e89c3b8b721487011d186",
"reference": "65ff2489553b83b4597e89c3b8b721487011d186",
"shasum": ""
},
"require": {
@ -9539,7 +9539,7 @@
"type": "thanks_dev"
}
],
"time": "2025-04-13T04:10:18+00:00"
"time": "2025-05-11T03:36:00+00:00"
},
{
"name": "symfony/browser-kit",

View file

@ -1746,29 +1746,33 @@ overall_header_body_before
overall_header_breadcrumb_append
===
* Locations:
+ styles/prosilver/template/navbar_header.html
+ styles/prosilver/template/breadcrumbs.html
* Since: 3.1.0-a1
* Changed: 4.0.0-a1 Moved to breadcrumbs.html
* Purpose: Add links to the list of breadcrumbs in the header
overall_header_breadcrumb_prepend
===
* Locations:
+ styles/prosilver/template/navbar_header.html
+ styles/prosilver/template/breadcrumbs.html
* Since: 3.1.0-RC3
* Changed: 4.0.0-a1 Moved to breadcrumbs.html
* Purpose: Add links to the list of breadcrumbs in the header (after site-home, but before board-index)
overall_header_breadcrumbs_after
===
* Locations:
+ styles/prosilver/template/navbar_header.html
+ styles/prosilver/template/breadcrumbs.html
* Since: 3.1.0-RC3
* Changed: 4.0.0-a1 Moved to breadcrumbs.html
* Purpose: Add content after the breadcrumbs (outside of the breadcrumbs container)
overall_header_breadcrumbs_before
===
* Locations:
+ styles/prosilver/template/navbar_header.html
+ styles/prosilver/template/breadcrumbs.html
* Since: 3.1.0-RC3
* Changed: 4.0.0-a1 Moved to breadcrumbs.html
* Purpose: Add content before the breadcrumbs (outside of the breadcrumbs container)
overall_header_content_before
@ -1830,15 +1834,17 @@ overall_header_navigation_prepend
overall_header_navlink_append
===
* Locations:
+ styles/prosilver/template/navbar_header.html
+ styles/prosilver/template/breadcrumbs.html
* Since: 3.1.0-b3
* Changed: 4.0.0-a1 Moved to breadcrumbs.html
* Purpose: Add content after each individual navlink (breadcrumb)
overall_header_navlink_prepend
===
* Locations:
+ styles/prosilver/template/navbar_header.html
+ styles/prosilver/template/breadcrumbs.html
* Since: 3.1.0-b3
* Changed: 4.0.0-a1 Moved to breadcrumbs.html
* Purpose: Add content before each individual navlink (breadcrumb)
overall_header_page_body_before

View file

@ -475,6 +475,41 @@ class acp_profile
$cp->vars[$key] = $var;
}
// step 3 - all arrays
if ($action == 'edit')
{
// Get language entries
$sql = 'SELECT *
FROM ' . PROFILE_FIELDS_LANG_TABLE . '
WHERE lang_id <> ' . $this->edit_lang_id . "
AND field_id = $field_id
ORDER BY option_id ASC";
$result = $db->sql_query($sql);
$l_lang_options = [];
while ($row = $db->sql_fetchrow($result))
{
$l_lang_options[$row['lang_id']][$row['option_id']] = $row['lang_value'];
}
$db->sql_freeresult($result);
$sql = 'SELECT lang_id, lang_name, lang_explain, lang_default_value
FROM ' . PROFILE_LANG_TABLE . '
WHERE lang_id <> ' . $this->edit_lang_id . "
AND field_id = $field_id
ORDER BY lang_id ASC";
$result = $db->sql_query($sql);
$l_lang_name = $l_lang_explain = $l_lang_default_value = [];
while ($row = $db->sql_fetchrow($result))
{
$l_lang_name[$row['lang_id']] = $row['lang_name'];
$l_lang_explain[$row['lang_id']] = $row['lang_explain'];
$l_lang_default_value[$row['lang_id']] = $row['lang_default_value'];
}
$db->sql_freeresult($result);
}
foreach ($exclude[3] as $key)
{
$cp->vars[$key] = $request->variable($key, array(0 => ''), true);

View file

@ -70,12 +70,12 @@ class request implements request_interface
foreach ($this->super_globals as $const => $super_global)
{
$this->input[$const] = isset($GLOBALS[$super_global]) ? $GLOBALS[$super_global] : array();
$this->input[$const] = isset($GLOBALS[$super_global]) ? (array) $GLOBALS[$super_global] : array();
}
// simulate request_order = GP
$this->original_request = $this->input[request_interface::REQUEST];
$this->input[request_interface::REQUEST] = $this->input[request_interface::POST] + $this->input[request_interface::GET];
$this->original_request = (array) $this->input[request_interface::REQUEST];
$this->input[request_interface::REQUEST] = (array) $this->input[request_interface::POST] + (array) $this->input[request_interface::GET];
if ($disable_super_globals)
{

View file

@ -33,7 +33,7 @@ class loader_resolver implements LoaderResolverInterface
/**
* {@inheritdoc}
*/
public function resolve($resource, $type = null): false|\Symfony\Component\Config\Loader\LoaderInterface
public function resolve($resource, $type = null): \Symfony\Component\Config\Loader\LoaderInterface|false
{
/** @var \Symfony\Component\Config\Loader\LoaderInterface $loader */
foreach ($this->loaders as $loader)

View file

@ -112,10 +112,16 @@ abstract class base implements search_backend_interface
}
}
// If the sort direction differs from the direction in the cache, then reverse the ids array
// If the sort direction differs from the direction in the cache, then recalculate array keys
if ($reverse_ids)
{
$stored_ids = array_reverse($stored_ids);
$keys = array_keys($stored_ids);
array_walk($keys, function (&$value, $key) use ($result_count)
{
$value = ($value >= 0) ? $result_count - $value - 1 : $value;
}
);
$stored_ids = array_combine($keys, $stored_ids);
}
for ($i = $start, $n = $start + $per_page; ($i < $n) && ($i < $result_count); $i++)
@ -166,6 +172,8 @@ abstract class base implements search_backend_interface
}
$store_ids = array_slice($id_ary, 0, $length);
$id_range = range($start, $start + $length - 1);
$store_ids = array_combine($id_range, $store_ids);
// create a new resultset if there is none for this search_key yet
// or add the ids to the existing resultset
@ -200,29 +208,26 @@ abstract class base implements search_backend_interface
$this->db->sql_query($sql);
$store = array(-1 => $result_count, -2 => $sort_dir);
$id_range = range($start, $start + $length - 1);
}
else
{
// we use one set of results for both sort directions so we have to calculate the indizes
// for the reversed array and we also have to reverse the ids themselves
// for the reversed array
if ($store[-2] != $sort_dir)
{
$store_ids = array_reverse($store_ids);
$id_range = range($store[-1] - $start - $length, $store[-1] - $start - 1);
}
else
{
$id_range = range($start, $start + $length - 1);
$keys = array_keys($store_ids);
array_walk($keys, function (&$value, $key) use ($store) {
$value = $store[-1] - $value - 1;
});
$store_ids = array_combine($keys, $store_ids);
}
}
$store_ids = array_combine($id_range, $store_ids);
// append the ids
if (is_array($store_ids))
{
$store += $store_ids;
ksort($store);
// if the cache is too big
if (count($store) - 2 > 20 * $this->config['search_block_size'])

View file

@ -550,7 +550,6 @@ class fulltext_mysql extends base implements search_backend_interface
}
$this->db->sql_freeresult($result);
$id_ary = array_unique($id_ary);
// if the total result count is not cached yet, retrieve it from the db
if (!$result_count && count($id_ary))
{
@ -576,10 +575,10 @@ class fulltext_mysql extends base implements search_backend_interface
$id_ary[] = (int) $row[$field];
}
$this->db->sql_freeresult($result);
$id_ary = array_unique($id_ary);
}
$id_ary = array_unique($id_ary);
// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page
$this->save_ids($search_key, implode(' ', $this->split_words), $author_ary, $result_count, $id_ary, $start, $sort_dir);
$id_ary = array_slice($id_ary, 0, (int) $per_page);
@ -758,6 +757,8 @@ class fulltext_mysql extends base implements search_backend_interface
// Build the query for really selecting the post_ids
if ($type == 'posts')
{
// For sorting by non-unique columns, add unique sort key to avoid duplicated rows in results
$sql_sort .= ', p.post_id' . (($sort_dir == 'a') ? ' ASC' : ' DESC');
$sql = "SELECT $sql_select
FROM " . $sql_sort_table . POSTS_TABLE . ' p' . (($firstpost_only) ? ', ' . TOPICS_TABLE . ' t ' : ' ') . "
WHERE $sql_author
@ -821,10 +822,10 @@ class fulltext_mysql extends base implements search_backend_interface
$id_ary[] = (int) $row[$field];
}
$this->db->sql_freeresult($result);
$id_ary = array_unique($id_ary);
}
$id_ary = array_unique($id_ary);
if (count($id_ary))
{
$this->save_ids($search_key, '', $author_ary, $result_count, $id_ary, $start, $sort_dir);

View file

@ -1004,6 +1004,8 @@ class fulltext_native extends base implements search_backend_interface
$this->db->sql_freeresult($result);
}
$id_ary = array_unique($id_ary);
// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page
$this->save_ids($search_key, $this->search_query, $author_ary, $total_results, $id_ary, $start, $sort_dir);
$id_ary = array_slice($id_ary, 0, (int) $per_page);
@ -1225,6 +1227,8 @@ class fulltext_native extends base implements search_backend_interface
// Build the query for really selecting the post_ids
if ($type == 'posts')
{
// For sorting by non-unique columns, add unique sort key to avoid duplicated rows in results
$sql_sort .= ', p.post_id' . (($sort_dir == 'a') ? ' ASC' : ' DESC');
$sql = "SELECT $select
FROM " . $sql_sort_table . POSTS_TABLE . ' p' . (($firstpost_only) ? ', ' . TOPICS_TABLE . ' t' : '') . "
WHERE $sql_author
@ -1289,6 +1293,8 @@ class fulltext_native extends base implements search_backend_interface
$this->db->sql_freeresult($result);
}
$id_ary = array_unique($id_ary);
if (count($id_ary))
{
$this->save_ids($search_key, '', $author_ary, $total_results, $id_ary, $start, $sort_dir);

View file

@ -478,8 +478,6 @@ class fulltext_postgres extends base implements search_backend_interface
}
$this->db->sql_freeresult($result);
$id_ary = array_unique($id_ary);
// if the total result count is not cached yet, retrieve it from the db
if (!$result_count)
{
@ -509,10 +507,10 @@ class fulltext_postgres extends base implements search_backend_interface
$id_ary[] = $row[$field];
}
$this->db->sql_freeresult($result);
$id_ary = array_unique($id_ary);
}
$id_ary = array_unique($id_ary);
// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page
$this->save_ids($search_key, implode(' ', $this->split_words), $author_ary, $result_count, $id_ary, $start, $sort_dir);
$id_ary = array_slice($id_ary, 0, (int) $per_page);
@ -683,6 +681,8 @@ class fulltext_postgres extends base implements search_backend_interface
// Build the query for really selecting the post_ids
if ($type == 'posts')
{
// For sorting by non-unique columns, add unique sort key to avoid duplicated rows in results
$sql_sort .= ', p.post_id' . (($sort_dir == 'a') ? ' ASC' : ' DESC');
$sql = "SELECT p.post_id
FROM " . $sql_sort_table . POSTS_TABLE . ' p' . (($firstpost_only) ? ', ' . TOPICS_TABLE . ' t ' : ' ') . "
WHERE $sql_author
@ -775,10 +775,10 @@ class fulltext_postgres extends base implements search_backend_interface
$id_ary[] = (int) $row[$field];
}
$this->db->sql_freeresult($result);
$id_ary = array_unique($id_ary);
}
$id_ary = array_unique($id_ary);
if (count($id_ary))
{
$this->save_ids($search_key, '', $author_ary, $result_count, $id_ary, $start, $sort_dir);

View file

@ -161,6 +161,16 @@ class environment extends \Twig\Environment
return $this->assets_bag;
}
/**
* Gets the event dispatcher instance
*
* @return dispatcher_interface
*/
public function get_phpbb_dispatcher()
{
return $this->phpbb_dispatcher;
}
/**
* Get the namespace look up order
*

View file

@ -24,9 +24,13 @@ class event extends \Twig\Node\Node
/** @var \phpbb\template\twig\environment */
protected $environment;
public function __construct(\Twig\Node\Expression\AbstractExpression $expr, \phpbb\template\twig\environment $environment, $lineno, $tag = null)
/** @var array */
protected $template_event_priority_array;
public function __construct(\Twig\Node\Expression\AbstractExpression $expr, \phpbb\template\twig\environment $environment, $lineno, $tag = null, $template_event_priority_array = [])
{
$this->environment = $environment;
$this->template_event_priority_array = $template_event_priority_array;
parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
}
@ -42,40 +46,49 @@ class event extends \Twig\Node\Node
$location = $this->listener_directory . $this->getNode('expr')->getAttribute('name');
$template_events = [];
// Group and sort extension template events in according to their priority (0 by default if not set)
foreach ($this->environment->get_phpbb_extensions() as $ext_namespace => $ext_path)
{
$ext_namespace = str_replace('/', '_', $ext_namespace);
if ($this->environment->isDebug())
{
// If debug mode is enabled, lets check for new/removed EVENT
// templates on page load rather than at compile. This is
// slower, but makes developing extensions easier (no need to
// purge the cache when a new event template file is added)
$compiler
->write("if (\$this->env->getLoader()->exists('@{$ext_namespace}/{$location}.html')) {\n")
->indent()
;
}
if ($this->environment->isDebug() || $this->environment->getLoader()->exists('@' . $ext_namespace . '/' . $location . '.html'))
{
$priority_key = $this->template_event_priority_array[$ext_namespace][$location] ?? 0;
$template_events[$priority_key][] = $ext_namespace;
}
}
krsort($template_events);
foreach ($template_events as $events)
{
foreach ($events as $ext_namespace)
{
if ($this->environment->isDebug())
{
// If debug mode is enabled, lets check for new/removed EVENT
// templates on page load rather than at compile. This is
// slower, but makes developing extensions easier (no need to
// purge the cache when a new event template file is added)
$compiler
->write("if (\$this->env->getLoader()->exists('@{$ext_namespace}/{$location}.html')) {\n")
->indent();
}
$compiler
->write("\$previous_look_up_order = \$this->env->getNamespaceLookUpOrder();\n")
// We set the namespace lookup order to be this extension first, then the main path
->write("\$this->env->setNamespaceLookUpOrder(array('{$ext_namespace}', '__main__'));\n")
->write("\$this->env->loadTemplate(\$this->env->getTemplateClass('@{$ext_namespace}/{$location}.html'), '@{$ext_namespace}/{$location}.html')->display(\$context);\n")
->write("\$this->env->setNamespaceLookUpOrder(\$previous_look_up_order);\n")
;
}
->write("\$this->env->setNamespaceLookUpOrder(\$previous_look_up_order);\n");
if ($this->environment->isDebug())
{
$compiler
->outdent()
->write("}\n\n")
;
if ($this->environment->isDebug())
{
$compiler
->outdent()
->write("}\n\n");
}
}
}
}

View file

@ -18,6 +18,12 @@ class event extends \Twig\TokenParser\AbstractTokenParser
/** @var \phpbb\template\twig\environment */
protected $environment;
/** @var \phpbb\event\dispatcher_interface */
protected $phpbb_dispatcher;
/** @var array */
protected $template_event_priority_array;
/**
* Constructor
*
@ -26,6 +32,39 @@ class event extends \Twig\TokenParser\AbstractTokenParser
public function __construct(\phpbb\template\twig\environment $environment)
{
$this->environment = $environment;
$this->phpbb_dispatcher = $this->environment->get_phpbb_dispatcher();
$template_event_priority_array = [];
/**
* Allow assigning priority to template events
*
* The higher number - the higher tempate event listener priority value is.
* In case of equal priority values, corresponding template event listeners will be handled in default compilation order.
* If not set, template event listener priority will be assigned to the value of 0.
*
* @event core.twig_tokenparser_constructor
* @var array template_event_priority_array Array with template event priority assignments per extension namespace
* Usage:
* '<author>_<extension_name>' => [
* 'event/<template_event_name>' => priority_number,
* ],
*
* Example:
* 'phpbb_viglink' => [
* 'event/acp_help_phpbb_stats_after' => 80,
* 'event/overall_footer_after' => 100,
* ],
*
* @since 4.0.0-a1
*/
if ($this->phpbb_dispatcher)
{
$vars = ['template_event_priority_array'];
extract($this->phpbb_dispatcher->trigger_event('core.twig_tokenparser_constructor', compact($vars)));
}
$this->template_event_priority_array = $template_event_priority_array;
unset($template_event_priority_array);
}
/**
@ -42,7 +81,7 @@ class event extends \Twig\TokenParser\AbstractTokenParser
$stream = $this->parser->getStream();
$stream->expect(\Twig\Token::BLOCK_END_TYPE);
return new \phpbb\template\twig\node\event($expr, $this->environment, $token->getLine(), $this->getTag());
return new \phpbb\template\twig\node\event($expr, $this->environment, $token->getLine(), $this->getTag(), $this->template_event_priority_array);
}
/**

View file

@ -0,0 +1,53 @@
<ul id="nav-breadcrumbs" class="nav-breadcrumbs linklist navlinks" role="menubar">
{% set MICRODATA = 'itemtype="https://schema.org/ListItem" itemprop="itemListElement" itemscope' %}
{% set navlink_position = 1 %}
{% EVENT overall_header_breadcrumbs_before %}
<li class="breadcrumbs" itemscope itemtype="https://schema.org/BreadcrumbList">
{% if U_SITE_HOME %}
<span class="crumb" {{ MICRODATA }}>
{% apply spaceless %}
<a itemprop="item" href="{{ U_SITE_HOME }}" data-navbar-reference="home">
{{ Icon('font', 'home', '', true, 'fas o-icon-baseline') }}
<span itemprop="name">{{ L_SITE_HOME }}</span>
</a>
{% endapply %}
<meta itemprop="position" content="{{ navlink_position }}{% set navlink_position = navlink_position + 1 %}">
</span>
{% endif %}
{% EVENT overall_header_breadcrumb_prepend %}
<span class="crumb" {{ MICRODATA }}>
{% apply spaceless %}
<a itemprop="item" href="{{ U_INDEX }}" accesskey="h" data-navbar-reference="index">
{% if not U_SITE_HOME %}{{ Icon('font', 'home', '', true, 'fas o-icon-baseline') }}{% endif %}
<span itemprop="name">{{ L_INDEX }}</span>
</a>
{% endapply %}
<meta itemprop="position" content="{{ navlink_position }}{% set navlink_position = navlink_position + 1 %}">
</span>
{% for navlink in navlinks %}
{% set NAVLINK_NAME = navlink.BREADCRUMB_NAME | default(navlink.FORUM_NAME) %}
{% set NAVLINK_LINK = navlink.U_BREADCRUMB | default(navlink.U_VIEW_FORUM) %}
{% EVENT overall_header_navlink_prepend %}
<span class="crumb" {{ MICRODATA }}{% if navlink.MICRODATA %} {{ navlink.MICRODATA }}{% endif %}>
{% apply spaceless %}
<a itemprop="item" href="{{ NAVLINK_LINK }}">
<span itemprop="name">{{ NAVLINK_NAME }}</span>
</a>
{% endapply %}
<meta itemprop="position" content="{{ navlink_position }}{% set navlink_position = navlink_position + 1 %}">
</span>
{% EVENT overall_header_navlink_append %}
{% endfor %}
{% EVENT overall_header_breadcrumb_append %}
</li>
{% EVENT overall_header_breadcrumbs_after %}
</ul>

View file

@ -44,7 +44,7 @@
<!-- END faq_block -->
<div class="to-top-button">
<a href="#faqlinks">
<a href="#faqlinks" title="{{ lang('BACK_TO_TOP') }}">
{{ Icon('font', 'chevron-up', '', false) }}
</a>
</div>

View file

@ -203,68 +203,5 @@
{% endif %}
</ul>
<ul id="nav-breadcrumbs" class="nav-breadcrumbs linklist navlinks" role="menubar">
{% set MICRODATA = 'itemtype="https://schema.org/ListItem" itemprop="itemListElement" itemscope' %}
{% set navlink_position = 1 %}
{% EVENT overall_header_breadcrumbs_before %}
<li class="breadcrumbs" itemscope itemtype="https://schema.org/BreadcrumbList">
{% if U_SITE_HOME %}
<span class="crumb" {{ MICRODATA }}>
{% apply spaceless %}
<a itemprop="item" href="{{ U_SITE_HOME }}" data-navbar-reference="home">
{{ Icon('font', 'home', '', true, 'fas o-icon-baseline') }}
<span itemprop="name">{{ L_SITE_HOME }}</span>
</a>
{% endapply %}
<meta itemprop="position" content="{{ navlink_position }}{% set navlink_position = navlink_position + 1 %}">
</span>
{% endif %}
{% EVENT overall_header_breadcrumb_prepend %}
<span class="crumb" {{ MICRODATA }}>
{% apply spaceless %}
<a itemprop="item" href="{{ U_INDEX }}" accesskey="h" data-navbar-reference="index">
{% if not U_SITE_HOME %}{{ Icon('font', 'home', '', true, 'fas o-icon-baseline') }}{% endif %}
<span itemprop="name">{{ L_INDEX }}</span>
</a>
{% endapply %}
<meta itemprop="position" content="{{ navlink_position }}{% set navlink_position = navlink_position + 1 %}">
</span>
{% for navlink in navlinks %}
{% set NAVLINK_NAME = navlink.BREADCRUMB_NAME | default(navlink.FORUM_NAME) %}
{% set NAVLINK_LINK = navlink.U_BREADCRUMB | default(navlink.U_VIEW_FORUM) %}
{% EVENT overall_header_navlink_prepend %}
<span class="crumb" {{ MICRODATA }}{% if navlink.MICRODATA %} {{ navlink.MICRODATA }}{% endif %}>
{% apply spaceless %}
<a itemprop="item" href="{{ NAVLINK_LINK }}">
<span itemprop="name">{{ NAVLINK_NAME }}</span>
</a>
{% endapply %}
<meta itemprop="position" content="{{ navlink_position }}{% set navlink_position = navlink_position + 1 %}">
</span>
{% EVENT overall_header_navlink_append %}
{% endfor %}
{% EVENT overall_header_breadcrumb_append %}
</li>
{% EVENT overall_header_breadcrumbs_after %}
{% if S_DISPLAY_SEARCH and not S_IN_SEARCH %}
<li class="rightside responsive-search">
<a href="{{ U_SEARCH }}" title="{{ lang('SEARCH_ADV_EXPLAIN') }}" role="menuitem">
{{ Icon('font', 'search', lang('SEARCH'), true) }}
</a>
</li>
{% endif %}
</ul>
</div>
</div>

View file

@ -68,7 +68,7 @@
<!-- EVENT overall_header_stylesheets_after -->
{% if NOTIFICATIONS_WEBPUSH_ENABLE %}
{% include('ucp_notifications_webpush.html') %}
{% include 'ucp_notifications_webpush.html' %}
{% endif %}
</head>
@ -79,7 +79,11 @@
<div id="wrap" class="wrap">
<a id="top" class="top-anchor" accesskey="t"></a>
<div id="page-header">
<!-- EVENT overall_header_navbar_before -->
<div class="headerbar" role="banner">
{% include 'navbar_header.html' %}
<!-- EVENT overall_header_headerbar_before -->
<div class="inner">
@ -117,10 +121,10 @@
</div>
<!-- EVENT overall_header_headerbar_after -->
</div>
<!-- EVENT overall_header_navbar_before -->
<!-- INCLUDE navbar_header.html -->
</div>
{% include 'breadcrumbs.html' %}
<!-- EVENT overall_header_page_body_before -->
<a id="start_here" class="anchor"></a>

View file

@ -423,9 +423,6 @@
<!-- EVENT viewtopic_body_postrow_back2top_before -->
<div class="back2top">
<!-- EVENT viewtopic_body_postrow_back2top_prepend -->
<a href="#top" class="top" title="{L_BACK_TO_TOP}">
{{ Icon('font', 'circle-chevron-up', lang('BACK_TO_TOP'), false, 'fas c-top-icon') }}
</a>
<!-- EVENT viewtopic_body_postrow_back2top_append -->
</div>
<!-- EVENT viewtopic_body_postrow_back2top_after -->
@ -496,6 +493,12 @@
<!-- ENDIF -->
</div>
<div class="to-top-button">
<a href="#top" title="{{ lang('BACK_TO_TOP') }}">
{{ Icon('font', 'chevron-up', '', false) }}
</a>
</div>
<!-- EVENT viewtopic_body_footer_before -->
<!-- INCLUDE jumpbox.html -->

View file

@ -99,18 +99,19 @@ th a:hover {
}
/* round cornered boxes and backgrounds */
.headerbar {
color: #ffffff;
.headerbar,
.headerbar h1 {
color: #eaf8ff;
}
.headerbar,
.forumbg {
background-color: #13a4ec;
background-color: #4688ce;
background-repeat: repeat-x;
}
.forabg {
background-color: #13a4ec;
background-color: #4688ce;
background-repeat: repeat-x;
}
@ -118,6 +119,18 @@ th a:hover {
background-color: #c9dee8;
}
.headerbar .navbar a {
color: #eaf8ff;
}
.headerbar .navbar .dropdown a {
color: #0f4d8a;
}
.header-profile {
text-shadow: 0 0 1.75rem #eaf8ff;
}
.panel {
background-color: #f0f3f5;
color: #29303d;
@ -814,6 +827,10 @@ dd.profile-warnings {
/* icon images */
.site_logo { background-image: url("./images/site_logo.svg"); }
.c-hero-logo-img g {
fill: #eaf8ff;
}
/* colours and backgrounds for cp.css */
/* main cp box */
@ -1109,6 +1126,7 @@ input.disabled {
.dropdown-extended .dropdown-extended-item {
border-top-color: #bdbdbd;
color: #47536b;
}
.dropdown-extended .dropdown-extended-item:hover {

View file

@ -17,7 +17,7 @@ body {
line-height: normal;
word-wrap: break-word;
margin: 0;
padding: 12px 0;
padding: 0 0 12px;
-webkit-print-color-adjust: exact;
}
@ -151,12 +151,13 @@ a:hover {
/* Main blocks
---------------------------------------- */
.wrap {
border: 1px solid transparent;
border-radius: 8px;
border: solid transparent;
border-width: 0 1px 1px;
border-radius: 0 0 8px 8px;
min-width: 625px;
max-width: 1152px;
margin: 0 auto;
padding: 15px;
padding: 0 15px 15px;
}
.page-body {
@ -200,7 +201,7 @@ a:hover {
/* Round cornered boxes and backgrounds
---------------------------------------- */
.headerbar {
border-radius: 7px;
border-radius: 0 0 7px 7px;
display: flex;
flex-direction: column;
margin-bottom: 0.5rem;
@ -212,6 +213,11 @@ a:hover {
padding: 3px 10px;
}
.headerbar .navbar {
background: none;
padding: calc(var(--ps-line-height) * 0.25) 5px calc(var(--ps-line-height) * 0.5);
}
.forabg {
border-radius: 7px;
clear: both;
@ -340,6 +346,10 @@ ul.linklist .dropdown-up .dropdown {
bottom: 18px;
}
ul.nav-breadcrumbs {
margin: calc(var(--ps-line-height) * 0.75) 0;
}
/* Bulletin icons for list items
---------------------------------------- */
ul.linklist.bulletin > li:before {

View file

@ -465,7 +465,7 @@ p.author {
clear: left;
overflow: hidden;
width: 100%;
margin-top: calc(var(--ps-font-small) * 1.4);
margin: calc(var(--ps-font-small) * 1.4) 0;
padding-top: 2px;
}

View file

@ -328,7 +328,6 @@
vertical-align: bottom;
text-overflow: ellipsis;
overflow: hidden;
max-width: 100px;
}
/* Pagination

View file

@ -69,4 +69,32 @@ class phpbb_functional_acp_profile_field_test extends phpbb_functional_test_case
$this->assertContainsLang('ADDED_PROFILE_FIELD', $crawler->text());
}
public function test_edit_profile_fields()
{
// Custom profile fields page
$crawler = self::request('GET', 'adm/index.php?i=acp_profile&mode=profile&sid=' . $this->sid);
// Get all profile fields edit URLs
$edits = $crawler->filter('td.actions a')
->reduce(
function ($node, $i) {
$url = $node->attr('href');
return ((bool) strpos($url, 'action=edit'));
})
->each(
function ($node, $i) {
$url = $node->attr('href');
return ($url);
});
foreach ($edits as $edit_url)
{
$crawler = self::request('GET', 'adm/' . $edit_url . '&sid=' . $this->sid);
$form = $crawler->selectButton('Save')->form();
$crawler= self::submit($form);
$this->assertContainsLang('CHANGED_PROFILE_FIELD', $crawler->text());
}
}
}

View file

@ -16,8 +16,6 @@
*/
class phpbb_functional_extension_controller_test extends phpbb_functional_test_case
{
protected $phpbb_extension_manager;
private static $helper;
protected static $fixtures = array(

View file

@ -16,8 +16,6 @@
*/
class phpbb_functional_extension_global_lang_test extends phpbb_functional_test_case
{
protected $phpbb_extension_manager;
private static $helper;
protected static $fixtures = array(

View file

@ -17,8 +17,6 @@ require_once __DIR__ . '/../../phpBB/includes/acp/acp_modules.php';
*/
class phpbb_functional_extension_module_test extends phpbb_functional_test_case
{
protected $phpbb_extension_manager;
private static $helper;
protected static $fixtures = array(

View file

@ -16,8 +16,6 @@
*/
class phpbb_functional_extension_permission_lang_test extends phpbb_functional_test_case
{
protected $phpbb_extension_manager;
private static $helper;
protected static $fixtures = array(

View file

@ -0,0 +1,91 @@
<?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_extension_template_event_order_test extends phpbb_functional_test_case
{
static private $helper;
static protected $fixtures = [
'./',
];
static public function setUpBeforeClass(): void
{
parent::setUpBeforeClass();
self::$helper = new phpbb_test_case_helpers(__CLASS__);
self::$helper->copy_ext_fixtures(__DIR__ . '/fixtures/ext/', self::$fixtures);
}
static public function tearDownAfterClass(): void
{
parent::tearDownAfterClass();
self::$helper->restore_original_ext_dir();
}
protected function setUp(): void
{
parent::setUp();
$this->purge_cache();
}
protected function tearDown(): void
{
$this->uninstall_ext('foo/bar');
$this->uninstall_ext('foo/foo');
parent::tearDown();
}
protected static function setup_extensions()
{
return ['foo/bar', 'foo/foo'];
}
/**
* Check a controller for extension foo/bar.
*/
public function test_template_event_order()
{
global $phpbb_root_path;
$crawler = self::request('GET', 'index.php');
$quick_links_menu = $crawler->filter('ul[role="menu"]')->eq(0);
$quick_links_menu_nodes_count = (int) $quick_links_menu->filter('li')->count();
// Ensure foo/foo template event goes before foo/bar one
$this->assertStringContainsString('FOO_FOO_QUICK_LINK', $quick_links_menu->filter('li')->eq($quick_links_menu_nodes_count - 4)->filter('span')->text());
$this->assertStringContainsString('FOO_BAR_QUICK_LINK', $quick_links_menu->filter('li')->eq($quick_links_menu_nodes_count - 3)->filter('span')->text());
// Change template events order to default, put foo/bar event before foo/foo one
$this->disable_ext('foo/bar');
$this->disable_ext('foo/foo');
$this->assertTrue(copy(__DIR__ . '/fixtures/ext/foo/bar/event/template_event_order_higher.php', $phpbb_root_path . 'ext/foo/bar/event/template_event_order.php'));
$this->assertTrue(copy(__DIR__ . '/fixtures/ext/foo/foo/event/template_event_order_lower.php', $phpbb_root_path . 'ext/foo/foo/event/template_event_order.php'));
$this->install_ext('foo/bar');
$this->install_ext('foo/foo');
$crawler = self::request('GET', 'index.php');
$quick_links_menu = $crawler->filter('ul[role="menu"]')->eq(0);
$quick_links_menu_nodes_count = (int) $quick_links_menu->filter('li')->count();
// Ensure foo/foo template event goes before foo/bar one
$this->assertStringContainsString('FOO_BAR_QUICK_LINK', $quick_links_menu->filter('li')->eq($quick_links_menu_nodes_count - 4)->filter('span')->text());
$this->assertStringContainsString('FOO_FOO_QUICK_LINK', $quick_links_menu->filter('li')->eq($quick_links_menu_nodes_count - 3)->filter('span')->text());
}
}

View file

@ -14,7 +14,13 @@ services:
class: foo\bar\event\permission
tags:
- { name: event.listener }
foo_bar.listener.user_setup:
class: foo\bar\event\user_setup
tags:
- { name: event.listener }
foo_bar.listener.template_event_order:
class: foo\bar\event\template_event_order
tags:
- { name: event.listener }

View file

@ -0,0 +1,38 @@
<?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 foo\bar\event;
/**
* Event listener
*/
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class template_event_order implements EventSubscriberInterface
{
static public function getSubscribedEvents()
{
return array(
'core.twig_tokenparser_constructor' => 'set_template_event_priority',
);
}
public function set_template_event_priority($event)
{
$template_event_priority_array = $event['template_event_priority_array'];
$template_event_priority_array['foo_bar'] = [
'event/navbar_header_quick_links_after' => -1,
];
$event['template_event_priority_array'] = $template_event_priority_array;
}
}

View file

@ -0,0 +1,38 @@
<?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 foo\bar\event;
/**
* Event listener
*/
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class template_event_order implements EventSubscriberInterface
{
static public function getSubscribedEvents()
{
return array(
'core.twig_tokenparser_constructor' => 'set_template_event_priority',
);
}
public function set_template_event_priority($event)
{
$template_event_priority_array = $event['template_event_priority_array'];
$template_event_priority_array['foo_bar'] = [
'event/navbar_header_quick_links_after' => 1,
];
$event['template_event_priority_array'] = $template_event_priority_array;
}
}

View file

@ -0,0 +1 @@
<li><span>{{ lang('FOO_BAR_QUICK_LINK') }}</span></li>

View file

@ -1,3 +1,8 @@
services:
foo_foo.controller:
class: foo\foo\controller\controller
foo_foo.listener.template_event_order:
class: foo\foo\event\template_event_order
tags:
- { name: event.listener }

View file

@ -0,0 +1,38 @@
<?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 foo\foo\event;
/**
* Event listener
*/
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class template_event_order implements EventSubscriberInterface
{
static public function getSubscribedEvents()
{
return array(
'core.twig_tokenparser_constructor' => 'set_template_event_priority',
);
}
public function set_template_event_priority($event)
{
$template_event_priority_array = $event['template_event_priority_array'];
$template_event_priority_array['foo_foo'] = [
'event/navbar_header_quick_links_after' => 1,
];
$event['template_event_priority_array'] = $template_event_priority_array;
}
}

View file

@ -0,0 +1,38 @@
<?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 foo\foo\event;
/**
* Event listener
*/
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class template_event_order implements EventSubscriberInterface
{
static public function getSubscribedEvents()
{
return array(
'core.twig_tokenparser_constructor' => 'set_template_event_priority',
);
}
public function set_template_event_priority($event)
{
$template_event_priority_array = $event['template_event_priority_array'];
$template_event_priority_array['foo_foo'] = [
'event/navbar_header_quick_links_after' => -1,
];
$event['template_event_priority_array'] = $template_event_priority_array;
}
}

View file

@ -0,0 +1 @@
<li><span>{{ lang('FOO_FOO_QUICK_LINK') }}</span></li>

View file

@ -16,8 +16,6 @@
*/
class phpbb_functional_metadata_manager_test extends phpbb_functional_test_case
{
protected $phpbb_extension_manager;
private static $helper;
protected static $fixtures = array(

View file

@ -229,6 +229,159 @@ abstract class phpbb_functional_search_base extends phpbb_functional_test_case
$this->delete_topic($topic_multiple_results_count2['topic_id']);
}
public function test_caching_search_results()
{
global $phpbb_root_path;
// Sphinx search doesn't use phpBB search results caching
if (strpos($this->search_backend, 'fulltext_sphinx'))
{
$this->markTestSkipped("Sphinx search doesn't use phpBB search results caching");
}
$this->purge_cache();
$this->login();
$this->admin_login();
$crawler = self::request('GET', 'search.php?author_id=2&sr=posts');
$posts_found_text = $crawler->filter('.searchresults-title')->text();
// Get total user's post count
preg_match('!(\d+)!', $posts_found_text, $matches);
$posts_count = (int) $matches[1];
$this->assertStringContainsString("Search found $posts_count matches", $posts_found_text, $this->search_backend);
// Set this value to cache less results than total count
$sql = 'UPDATE ' . CONFIG_TABLE . '
SET config_value = ' . floor($posts_count / 3) . "
WHERE config_name = '" . $this->db->sql_escape('search_block_size') . "'";
$this->db->sql_query($sql);
// Temporarily set posts_per_page to the value allowing to get several pages (4+)
$crawler = self::request('GET', 'adm/index.php?sid=' . $this->sid . '&i=acp_board&mode=post');
$form = $crawler->selectButton('Submit')->form();
$values = $form->getValues();
$current_posts_per_page = $values['config[posts_per_page]'];
$values['config[posts_per_page]'] = floor($posts_count / 10);
$form->setValues($values);
$crawler = self::submit($form);
$this->assertEquals(1, $crawler->filter('.successbox')->count(), $this->search_backend);
// Now actually test caching search results
$this->purge_cache();
// Default sort direction is 'd' (descending), browse the 1st page
$crawler = self::request('GET', 'search.php?author_id=2&sr=posts');
$pagination = $crawler->filter('.pagination')->eq(0);
$posts_found_text = $pagination->text();
$this->assertStringContainsString("Search found $posts_count matches", $posts_found_text, $this->search_backend);
// Filter all search result page links on the 1st page
$pagination = $pagination->filter('li > a')->reduce(
function ($node, $i)
{
return ($node->attr('class') == 'button');
}
);
// Get last page number
$last_page = (int) $pagination->last()->text();
// Browse the last search page
$crawler = self::$client->click($pagination->selectLink($last_page)->link());
$pagination = $crawler->filter('.pagination')->eq(0);
// Filter all search result page links on the last page
$pagination = $pagination->filter('li > a')->reduce(
function ($node, $i)
{
return ($node->attr('class') == 'button');
}
);
// Now change sort direction to ascending
$form = $crawler->selectButton('sort')->form();
$values = $form->getValues();
$values['sd'] = 'a';
$form->setValues($values);
$crawler = self::submit($form);
$pagination = $crawler->filter('.pagination')->eq(0);
// Filter all search result page links on the 1st page with new sort direction
$pagination = $pagination->filter('li > a')->reduce(
function ($node, $i)
{
return ($node->attr('class') == 'button');
}
);
// Browse the rest of search results pages with new sort direction
$pages = range(2, $last_page);
foreach ($pages as $page_number)
{
$crawler = self::$client->click($pagination->selectLink($page_number)->link());
$pagination = $crawler->filter('.pagination')->eq(0);
$pagination = $pagination->filter('li > a')->reduce(
function ($node, $i)
{
return ($node->attr('class') == 'button');
}
);
}
// Get search results cache varname
$finder = new \Symfony\Component\Finder\Finder();
$finder
->name('data_search_results_*.php')
->files()
->in($phpbb_root_path . 'cache/' . PHPBB_ENVIRONMENT);
$iterator = $finder->getIterator();
$iterator->rewind();
$cache_filename = $iterator->current();
$cache_varname = substr($cache_filename->getBasename('.php'), 4);
// Get cached post ids data
$cache = $this->get_cache_driver();
$post_ids_cached = $cache->get($cache_varname);
$cached_results_count = count($post_ids_cached) - 2; // Don't count '-1' and '-2' indexes
$post_ids_cached_backup = $post_ids_cached;
// Cached data still should have initial 'd' sort direction
$this->assertTrue($post_ids_cached[-2] === 'd', $this->search_backend);
// Cached search results count should be equal to displayed on search results page
$this->assertEquals($posts_count, $post_ids_cached[-1], $this->search_backend);
// Actual cached data array count should be equal to displayed on search results page too
$this->assertEquals($posts_count, $cached_results_count, $this->search_backend);
// Cached data array shouldn't change after removing duplicates. That is, it shouldn't have any duplicates.
unset($post_ids_cached[-2], $post_ids_cached[-1]);
unset($post_ids_cached_backup[-2], $post_ids_cached_backup[-1]);
$post_ids_cached = array_unique($post_ids_cached);
$this->assertEquals($post_ids_cached_backup, $post_ids_cached, $this->search_backend);
// Restore this value to default
$sql = 'UPDATE ' . CONFIG_TABLE . "
SET config_value = 250
WHERE config_name = '" . $this->db->sql_escape('search_block_size') . "'";
$this->db->sql_query($sql);
// Restore posts_per_page value
$crawler = self::request('GET', 'adm/index.php?sid=' . $this->sid . '&i=acp_board&mode=post');
$form = $crawler->selectButton('Submit')->form();
$values = $form->getValues();
$values['config[posts_per_page]'] = $current_posts_per_page;
$form->setValues($values);
$crawler = self::submit($form);
$this->assertEquals(1, $crawler->filter('.successbox')->count(), $this->search_backend);
}
protected function create_search_index($backend = null)
{
$this->add_lang('acp/search');

View file

@ -644,7 +644,7 @@ class phpbb_functional_test_case extends phpbb_test_case
$meta_refresh = $crawler->filter('meta[http-equiv="refresh"]');
// Wait for extension to be fully enabled
// Wait for extension to be fully disabled
while (count($meta_refresh))
{
preg_match('#url=.+/(adm+.+)#', $meta_refresh->attr('content'), $match);