1
0
mirror of https://github.com/phpbb/phpbb.git synced 2025-09-17 03:22:07 +02:00

Compare commits

...

154 Commits

Author SHA1 Message Date
Marc Alexander
2973c2f6db [prep-release-3.3.13] Update changelog for 3.3.13-RC1 2024-08-02 19:22:23 +02:00
Marc Alexander
57ef18ecc0 [prep-release-3.3.13] Update stylesheet hashes for 3.3.13-RC1 2024-08-02 19:20:35 +02:00
Marc Alexander
e9e305ee9f [prep-release-3.3.13] Add migration for 3.3.13-RC1 2024-08-02 19:20:35 +02:00
Marc Alexander
1d256fef61 [prep-release-3.3.13] Update version numbers to 3.3.13 2024-08-02 19:20:32 +02:00
Marc Alexander
599073cab2 [prep-release-3.3.13] Update version numbers to 3.3.13-RC1 2024-08-02 19:20:32 +02:00
Marc Alexander
b7ffee0945 Merge pull request #6698 from marc1706/ticket/17375
[ticket/17375] Ensure user last visit does not get updated for active sessions in session gc
2024-08-01 22:18:23 +02:00
Marc Alexander
56c1b9b5bd Merge pull request #6662 from rxu/ticket/17358
[ticket/17358] Properly handle Redis cache expiration time - 3.3.x
2024-08-01 20:24:23 +02:00
Marc Alexander
18c00a902f [ticket/17375] User last active should be updated to current time
PHPBB-17375
2024-07-31 21:25:33 +02:00
Marc Alexander
8497104bdb [ticket/17375] Ensure session garbage collection only happens for expired ones
PHPBB-17375
2024-07-31 21:24:45 +02:00
Marc Alexander
4038bb9dc3 Merge pull request #6691 from marc1706/ticket/13933
[ticket/13933] Extend tokens' definitions in ACP bbcodes
2024-07-28 21:02:11 +02:00
Marc Alexander
935c1a3ea2 Merge pull request #6680 from marc1706/ticket/17366
[ticket/17366] Resolve issues with form submissions and recaptcha
2024-07-28 20:47:14 +02:00
Marc Alexander
6eb49d0907 Merge pull request #6681 from marc1706/ticket/16213
[ticket/16213] Add .htaccess for phpbb and vendor folders
2024-07-28 20:44:56 +02:00
Marc Alexander
7fe0cc7f26 Merge pull request #6686 from marc1706/ticket/15576
[ticket/15576] Handle subject max length same as in form input
2024-07-28 20:43:20 +02:00
Marc Alexander
28cf33eb2f Merge pull request #6688 from marc1706/ticket/14454
[ticket/14454] Handle special case of testing user permissions in ACP modules
2024-07-28 20:37:34 +02:00
Marc Alexander
cf47bed67e Merge pull request #6689 from marc1706/ticket/17370
[ticket/17370] Move delete cookies functionality to controller
2024-07-28 20:35:42 +02:00
Marc Alexander
1e28669407 Merge pull request #6696 from marc1706/ticket/17374
[ticket/17374] Only delete marked logs if entries have been marked
2024-07-28 20:31:52 +02:00
Marc Alexander
58cc1f4ef3 [ticket/17374] Only delete marked logs if entries have been marked
PHPBB-17374
2024-07-23 20:18:28 +02:00
Marc Alexander
45d64a1627 Merge pull request #6660 from marc1706/ticket/17353
[ticket/17353] Get gravatar img src and fix incorrect treatment of height
2024-07-16 21:03:44 +02:00
Marc Alexander
10f92dbb68 [ticket/13933] Extend tokens' definitions in ACP bbcodes
PHPBB-13933
2024-07-16 21:01:57 +02:00
Marc Alexander
bc7bd15312 Merge pull request #5385 from rubencm/ticket/15043
[ticket/15043] Rewrite get_context()
2024-07-15 19:49:59 +02:00
Ruben Calvo
5c40766dc4 [ticket/15403] Remove last element of array only if needed
PHPBB-15403
2024-07-15 12:30:40 +02:00
Marc Alexander
661140c50b [ticket/17370] Redirect from UCP to delete cookies controller
PHPBB-17370
2024-07-14 20:44:01 +02:00
Marc Alexander
294c517256 [ticket/17370] Add controller for deleting cookies
PHPBB-17370
2024-07-14 20:42:03 +02:00
Marc Alexander
de660fe0c8 Merge pull request #6677 from marc1706/ticket/16907
[ticket/16907] Adjust request filtering for phpbb paths
2024-07-14 14:46:20 +02:00
Ruben Calvo
e602889213 [ticket/15403] Add another test
PHPBB-15403
2024-07-14 12:47:06 +02:00
Ruben Calvo
f4b1444248 [ticket/15403] Handle ellipsis at the beginning of context and add tests
PHPBB-15403
2024-07-14 12:45:20 +02:00
Marc Alexander
13e6cd5992 [ticket/14454] Remove extra commas
PHPBB-14454
2024-07-14 08:17:12 +02:00
Ruben Calvo
dd9267b678 [ticket/15403] Use substr over the fragment and not the whole text
PHPBB-15403
2024-07-14 01:10:25 +02:00
Marc Alexander
f646fcdefa [ticket/14454] Handle special case of testing user permissions in ACP modules
PHPBB-14454
2024-07-13 23:46:28 +02:00
Marc Alexander
99c97b951f Merge pull request #6642 from rxu/ticket/17338
[ticket/17338] Prefer user_last_active to display user last activity info
2024-07-13 19:42:48 +02:00
Ruben Calvo
f41c8eef47 [ticket/15403] Refactor get_context
PHPBB-15403
2024-07-13 17:54:17 +02:00
Marc Alexander
17b4838ee3 Merge pull request #6684 from crowjake/ticket/17365
[ticket/17365] Prevent search limit being bypassed with operators
2024-07-10 22:45:09 +02:00
Marc Alexander
a0092bdd18 Merge pull request #6678 from marc1706/ticket/13916
[ticket/13916] Add notify to fields to store during save draft
2024-07-10 21:34:52 +02:00
Marc Alexander
c0b81a1a48 [ticket/17365] Add a test for max search keywords
PHPBB-17365
2024-07-10 21:32:15 +02:00
Marc Alexander
89afa0cb5e [ticket/15043] Small code cleanup
PHPBB-15043
2024-07-09 21:46:37 +02:00
Marc Alexander
a62a303318 [ticket/15043] Add unit test for get_context
PHPBB-15043
2024-07-09 21:20:09 +02:00
Marc Alexander
cacd9375fd Merge pull request #6685 from danieltj27/ticket/17369
[ticket/17369] Add forum id to approve action url for redirection
2024-07-09 19:22:31 +02:00
Ruben Calvo
7763969625 [ticket/15403] Return first fragment of text if there is no coincidences
PHPBB-15403
2024-07-09 17:09:06 +02:00
Ruben Calvo
a2e7205154 [ticket/15403] Fix order of parameters in implode
PHPBB-15403
2024-07-09 00:25:05 +02:00
Rubén Calvo
350c9213ee [ticket/15043] Update regexp
PHPBB3-15043
2024-07-08 21:43:54 +02:00
Rubén Calvo
7365764476 [ticket/15043] New get_context displays more text
PHPBB3-15043
2024-07-08 21:43:52 +02:00
Rubén Calvo
dd4c982792 [ticket/15043] Rewrite get_context()
PHPBB3-15043
2024-07-08 21:42:03 +02:00
Marc Alexander
2088ee5e84 [ticket/15576] Handle subject max length same as in form input
PHPBB-15576
2024-07-08 21:37:05 +02:00
Daniel James
926a0a4e33 [ticket/17369] Add forum id to approve action url for redirection
Added the forum_id to the approve action link which ensures that when
a topic is permanently deleted, the forum_id is present so that it can
be used in the redirection link to go to the previous forum. This addresses
a bug in which the forum_id is not provided and after deleting a topic, the
user will be taken to a 404 page as the forum id fallbacks to 0.

PHPBB-17369
2024-07-08 16:35:41 +01:00
crowjake
30144052da [ticket/17365] Prevent keyword limit being bypassed with the use of +, - and |
Provided `$countable_keywords` wherein the existing `$keywords` value is
modified so that any `-`, `+` and `|` characters without preceding spaces
is replaced with the same but with a space in front of each.

These spaces allow the string to be more accurately split when used instead
of $keywords inside the $num_keywords calculation.

This prevents the word limit being bypassed in search by the use of
operators without whitespace.

PHPBB-17365
2024-07-07 23:04:19 +01:00
Marc Alexander
8298095421 [ticket/16213] Add .htaccess for phpbb and vendor folders
PHPBB-16213
2024-07-07 15:22:23 +02:00
Marc Alexander
55dbe070e3 [ticket/17366] Remove javascript sanity check as this can break with recaptcha
PHPBB-17366
2024-07-07 14:52:00 +02:00
Marc Alexander
8639be4bd4 [ticket/17366] Remember confirm code if recaptcha v3 is solved
PHPBB-17366
2024-07-07 11:31:18 +02:00
Marc Alexander
ffc655a1ba [ticket/13916] Add notify to fields to store during save draft
PHPBB-13916
2024-07-06 21:44:06 +02:00
Marc Alexander
922e7699ed [ticket/16907] Adjust request filtering for phpbb paths
PHPBB-16907
2024-07-06 17:39:49 +02:00
Marc Alexander
c790e81fb6 Merge pull request #6676 from marc1706/ticket/16890
[ticket/16890] Deny access to config file and folder in nginx sample
2024-07-06 13:01:50 +02:00
Marc Alexander
42851d0d9f [ticket/16890] Add rewrite for installer in lighttpd
PHPBB-16890
2024-07-06 11:53:21 +02:00
Marc Alexander
00d1351e55 [ticket/16890] Deny access to config file and folder in nginx sample
PHPBB-16890
2024-07-06 10:47:49 +02:00
Marc Alexander
48a233e415 Merge pull request #6661 from rubencm/ticket/17356
[ticket/17356] Correctly handle errors hidden with at
2024-07-05 21:56:34 +02:00
Marc Alexander
6798c337f8 Merge pull request #6656 from danieltj27/ticket/17350
[ticket/17350] Add IP Address of user during install process
2024-07-05 21:45:16 +02:00
Marc Alexander
99b745982e [ticket/17353] Clean up test code for compatibility with 3.3.x
PHPBB-17353
2024-07-03 21:44:40 +02:00
Marc Alexander
bde7b119b2 [ticket/17355] Update tests after changing gravatar URL
PHPBB-17355
2024-07-03 21:36:51 +02:00
Marc Alexander
5013075af7 [ticket/17355] Change gravatar URL to new format
PHPBB-17355
2024-07-03 21:36:51 +02:00
Marc Alexander
40881b537e [ticket/17353] Return image src for gravatar image
PHPBB-17353
2024-07-03 21:36:51 +02:00
Marc Alexander
efece4136b [ticket/17353] Add test for gravatar driver and fix request var issue
PHPBB-17353
2024-07-03 21:36:51 +02:00
Marc Alexander
38177e9052 Merge pull request #6655 from danieltj27/ticket/17352
[ticket/17352] Fix overflow of long rank titles on profile page
2024-07-01 21:52:47 +02:00
Marc Alexander
a4296a9e6b Merge pull request #6667 from m-ober/ticket/17362
[ticket/17362] Declare property to avoid PHP 8.2 deprecation notice
2024-07-01 19:13:11 +02:00
Micha Ober
d7477760d6 [ticket/17362] Declare property to avoid PHP 8.2 deprecation notice
PHPBB-17362
2024-07-01 13:48:59 +02:00
Marc Alexander
294b3ff922 Merge pull request #6651 from marc1706/ticket/17347
[ticket/17347] Add console command to delete users by user IDs
2024-06-30 17:20:35 +02:00
Marc Alexander
ad77f0d0ab Merge pull request #6658 from rxu/ticket/17351
[ticket/17351] Correctly handle md5 passwords rehashing - 3.3.x
2024-06-30 12:39:48 +02:00
rxu
5a9c01716f [ticket/17358] Properly handle Redis cache expiration time
PHPBB-17358
2024-06-30 12:45:14 +07:00
Ruben Calvo
5b03cd422a [ticket/17356] Correctly handle errors hidden by at
PHPBB-17356
2024-06-28 21:45:33 +02:00
rxu
e5d2e82ef5 [ticket/17351] Correctly handle md5 passwords rehashing
PHPBB-17351
2024-06-28 11:36:14 +07:00
Daniel James
8eacbf8305 [ticket/17350] Add IP Address of user during install process
Adds the IP address of the user completing the installation process
while extensions are being installed or updated. Prior to this change
the IP address field was being left blank however other logs created
during the install process for other actions does record the users IP.

PHPBB-17350
2024-06-25 16:24:58 +01:00
Daniel James
5976dc2141 [ticket/17352] Fix overflow of long rank titles on profile page
Users that had very long rank titles would cause the rest of their
profile details to be pushed down below the avatar and rank details
breaking the layout. This change limits the width of the avatar
and rank details.

PHPBB-17352
2024-06-25 16:09:52 +01:00
Marc Alexander
3647cf2cfe Merge pull request #6637 from rxu/ticket/17337
[ticket/17337] Fix mysqli driver is missing transaction begin statement - 3.3.x
2024-06-24 20:35:00 +02:00
rxu
dd3ebe2b71 [ticket/17337] Correctly handle transaction test on MyISAM (non-transactional)
PHPBB-17337
2024-06-24 21:32:18 +07:00
Marc Alexander
f3460fe9eb [ticket/17337] Extend write tests with simple transaction test
PHPBB-17337
2024-06-23 23:26:13 +02:00
Marc Alexander
15960a7918 Merge pull request #6639 from marc1706/ticket/17301
[ticket/17301] Ensure reading invalid cache files is aborted
2024-06-22 11:56:21 +02:00
Marc Alexander
fa893f092b [ticket/17347] Get rid of not needed code duplication
PHPBB-17347
2024-06-22 07:33:06 +02:00
Marc Alexander
99b07b884b Merge pull request #6647 from marc1706/ticket/17342-3.3.x
[ticket/17342] Add tests for PHP 8.4 to GitHub Actions
2024-06-22 07:28:23 +02:00
Marc Alexander
7f9bcc2743 [ticket/17347] Change command to be called user:delete_id
PHPBB-17347
2024-06-21 21:12:38 +02:00
Marc Alexander
d875eaa405 [ticket/17347] Update tests for delete_id command
PHPBB-17347
2024-06-21 21:12:09 +02:00
Marc Alexander
db40145fd5 [ticket/17347] Use progress bar and test bot deletion only
PHPBB-17347
2024-06-21 20:59:04 +02:00
Marc Alexander
8c77da9c30 [ticket/17347] Add special handling for deletion of bots
PHPBB-17347
2024-06-21 20:48:41 +02:00
Marc Alexander
09f0b417be [ticket/17347] Fix issues noticed in tests
PHPBB-17347
2024-06-20 22:38:24 +02:00
Marc Alexander
8432e506c4 [ticket/17347] Add tests for delete ids
PHPBB-17347
2024-06-20 22:23:38 +02:00
Marc Alexander
77d1010081 [ticket/17347] Add console command to delete users by user IDs
PHPBB-17347
2024-06-20 21:17:48 +02:00
rxu
4003f54d0b [ticket/17338] Do not update user_last_active to outdated session_time value
PHPBB-17338
2024-06-17 11:55:35 +07:00
Marc Alexander
abaef09a14 [ticket/17342] Add tests for PHP 8.4 to GitHub Actions
PHPBB-17342
2024-06-16 20:31:57 +02:00
Marc Alexander
127121f1d2 Merge pull request #6633 from rxu/ticket/17332
[ticket/17332] Fix permission migrator tool to work with copied permissions
2024-06-14 22:28:58 +02:00
Marc Alexander
e53cd79067 Merge pull request #6643 from marc1706/ticket/17340
[ticket/17340] Update composer and dependencies
2024-06-14 21:51:50 +02:00
Marc Alexander
8bdff227d9 [ticket/17340] Update composer dependencies
PHPBB-17340
2024-06-14 17:04:22 +02:00
Marc Alexander
df7dae9600 [ticket/17340] Update composer to 2.7.7
PHPBB-17340
2024-06-14 17:02:36 +02:00
rxu
e21a8e02cd [ticket/17338] Update user_last_active on session removal and login keys reset
PHPBB-17338
2024-06-14 15:22:20 +07:00
rxu
db9874546b [ticket/17338] Add user_last_active to session_gc()
PHPBB-17338
2024-06-14 11:03:02 +07:00
rxu
e125f1f709 [ticket/17338] Prefer user_last_active to display user last activity info
PHPBB-17338
2024-06-13 13:27:03 +07:00
Marc Alexander
9e15802805 [ticket/17301] Do not test unreadable files on windows
PHPBB-17301
2024-06-11 20:29:23 +02:00
Marc Alexander
f98f2c5896 [ticket/17301] Ensure reading invalid cache files is aborted
PHPBB-17301
2024-06-11 20:12:01 +02:00
rxu
c2d91650bd [ticket/17337] Fix mysqli driver is missing transaction begin statement
PHPBB-17337
2024-06-11 16:08:39 +07:00
rxu
51b773e588 [ticket/17332] Add test
PHPBB-17332
2024-06-05 12:50:03 +07:00
rxu
ef4db99709 [ticket/17332] Fix permission migrator tool to work with copied permissions
PHPBB-17332
2024-06-05 00:22:04 +07:00
Marc Alexander
cd0e682984 Merge pull request #6619 from marc1706/ticket/17308
[ticket/17308] Change project key to start with PHPBB-
2024-06-01 08:31:59 +02:00
Marc Alexander
431b399c68 Merge pull request #6628 from marc1706/ticket/17327
[ticket/17327] Use class name from use statement
2024-06-01 08:15:26 +02:00
Marc Alexander
56d8a7e43f [ticket/17327] Use class name from use statement
PHPBB3-17327
2024-05-31 22:30:05 +02:00
Marc Alexander
2976314205 Merge branch 'prep-release-3.3.12' into 3.3.x 2024-05-31 21:41:03 +02:00
Marc Alexander
be6bb09984 [prep-release-3.3.12] Update changelog for 3.3.12 2024-05-31 20:05:39 +02:00
Marc Alexander
8a8232109c Merge pull request #6609 from battye/ticket/17175
[ticket/17175] Fix topic title in the breadcrumbs when emailing a topic
2024-05-31 11:08:06 +02:00
Marc Alexander
f3ead808f3 Merge pull request #6615 from danieltj27/ticket/17316
[ticket/17316] Add template events to ucp_groups_manage
2024-05-31 10:46:06 +02:00
Marc Alexander
276502ce58 Merge pull request #6613 from danieltj27/ticket/17315
[ticket/17315] add template events to acp_groups
2024-05-31 10:41:00 +02:00
Marc Alexander
8899fcfdc2 Merge pull request #6626 from LukeWCS/ticket/17325
[ticket/17325] Show message for "Re-Check version" if phpBB is still up to date
2024-05-31 09:50:54 +02:00
Marc Alexander
2aba9b0e3f [prep-release-3.3.12] Remove columns from user_add
These have default values and do not require setting in user_add().
2024-05-30 20:39:52 +02:00
Marc Alexander
80a12f7108 [prep-release-3.3.12] Update changelog for 3.3.12 2024-05-30 11:03:06 +02:00
Marc Alexander
6cef48af2f [prep-release-3.3.12] Add migration for 3.3.12 2024-05-30 11:00:23 +02:00
Marc Alexander
607fe555af [prep-release-3.3.12] Update version numbers to 3.3.12 2024-05-30 11:00:22 +02:00
Daniel James
166c4eeb7e [ticket/17316] Add template events to ucp_groups_manage
PHPBB3-17316
2024-05-30 10:45:30 +02:00
Marc Alexander
bf55502c62 [ticket/17325] Use not instead of ! in template
PHPBB3-17325
2024-05-30 10:40:25 +02:00
Marc Alexander
df5fcafcb9 Merge pull request #6616 from danieltj27/ticket/17317
[ticket/17317] Change button text for pm recipient to increase readability
2024-05-30 08:54:55 +02:00
Marc Alexander
1fb7e6d2c8 [ticket/security/278] Always release cron lock, even invalid task is passed
SECURITY-278
2024-05-29 22:04:16 +02:00
Marc Alexander
1c1c981b17 [ticket/security/276] Centralise call for token expiration
SECURITY-276
2024-05-29 22:04:15 +02:00
Marc Alexander
7c661746cf [ticket/security/276] Add test for expiration timer
SECURITY-276
2024-05-29 22:04:15 +02:00
Marc Alexander
24dd47adcf [ticket/security/276] Add missing information message
SECURITY-276
2024-05-29 22:04:15 +02:00
Marc Alexander
f853f6523f [ticket/security/276] Prevent sending activation emails multiple times per day
SECURITY-276
2024-05-29 22:04:15 +02:00
Marc Alexander
a63a1913fa [ticket/security-276] Add migration for user_actkey expiration column
SECURITY-276
2024-05-29 22:04:15 +02:00
Marc Alexander
ef593afec5 Merge branch 'prep-release-3.3.12' into 3.3.x 2024-05-29 21:53:47 +02:00
Marc Alexander
51da43f77f [prep-release-3.3.12] Fix since version and undo md exporter change 2024-05-29 21:53:11 +02:00
Marc Alexander
e328ee96d2 Merge pull request #6625 from rxu/ticket/17324
[ticket/17324] Add template event to notification_dropdown.html
2024-05-29 21:42:46 +02:00
Marc Alexander
7f11c1b46d Merge pull request #6624 from battye/ticket/16553
[ticket/16553] Fix for General Error when approving/disapproving a reported post through the MCP
2024-05-28 21:08:15 +02:00
LukeWCS
ae18669fcd [ticket/17325] Show message for "Re-Check version" if phpBB is still up to date
Show explicit message for "Re-Check version" if installed version is still up
to date

PHPBB3-17325
2024-05-28 20:55:49 +02:00
rxu
9ea4aabe9a [ticket/17324] Allow targeting events to prep-releases
PHPBB3-17324
2024-05-28 20:36:40 +02:00
rxu
a88040df85 [ticket/17324] Adjust target version
PHPBB3-17324
2024-05-28 20:36:40 +02:00
rxu
fcf89208cf [ticket/17324] Add template event to notification_dropdown.html
PHPBB3-17324
2024-05-28 20:36:40 +02:00
Marc Alexander
d365f17ebd Merge pull request #6621 from danieltj27/ticket/17320
[ticket/17320] Remove unused js code from prosilver
2024-05-27 20:17:25 +02:00
battye
6f345e43cb [ticket/16553] Change double quotes to single quotes
PHPBB3-16553
2024-05-27 07:55:08 +00:00
battye
874fb7bf8a [ticket/16553] Use env variables in Codespaces for server name
PHPBB3-16553
2024-05-27 07:12:51 +00:00
battye
558b8ae7ed [ticket/16553] Fix approve reported posts bug
Fixes the module not accessible error when trying to approve a reported post.

PHPBB3-16553
2024-05-27 05:18:08 +00:00
Marc Alexander
477f5d9e1d Merge pull request #6617 from battye/ticket/17109
[ticket/17109] Don't show the attach signature option if the user doesn't have permission
2024-05-23 21:34:26 +02:00
Daniel James
92e545fc26 [ticket/17317] Remove unused CSS styling for inputs
PHPBB3-17317
2024-05-23 20:57:36 +02:00
Daniel James
c330500c4d [ticket/17317] Add new icon for groups and remove CSS change
PHPBB3-17317
2024-05-23 20:57:34 +02:00
Daniel James
9fcf956888 [ticket/17317] Replace button with small red fa-icon
Updated the button to a small red X using Font Awesome
rather than using the default phpBB button with text
which adds a better visual distinction in the interface
between the username and action. Credit to @iMattPro for
the suggestion in the ticket comments.

PHPBB3-17317
2024-05-23 20:57:33 +02:00
Daniel James
40ed6c6458 [ticket/17317] Fix padding on button, left and right not top and bottom
PHPBB3-17317
2024-05-23 20:57:31 +02:00
Daniel James
cd34cdedee [ticket/17317] Change button text for pm recipient to increase readability
PHPBB3-17317
2024-05-23 20:57:26 +02:00
Daniel James
e850915190 [ticket/17320] Remove unused js code from prosilver
The forum_fn.js file contained a small block of code
that was commented out so there is no need to keep it
in the codebase, especially as it was a fix for IE8
and older browsers.

PHPBB3-17320
2024-05-23 16:35:28 +01:00
Marc Alexander
d14f98f672 Merge branch 'prep-release-3.3.12' into 3.3.x 2024-05-22 21:54:10 +02:00
Marc Alexander
130c10915e Merge pull request #6618 from marc1706/ticket/17312
[ticket/17312] Add column for tracking user last active time
2024-05-22 21:53:15 +02:00
Marc Alexander
9ecf09dbe8 [ticket/17312] Add and use user_last_active where needed and useful
PHPBB3-17312
2024-05-18 20:58:07 +02:00
Marc Alexander
8954a68953 [ticket/17308] Change project key to start with PHPBB-
PHPBB-17308
2024-05-18 08:27:19 +02:00
Marc Alexander
f986138467 [ticket/17312] Update tests for new column
PHPBB3-17312
2024-05-17 16:41:40 +02:00
battye
611faafaf3 [ticket/17109] Check attach signature permissions
Don't show the attach signature option if the
user doesn't have permission or if the signature
feature is disabled for the board.

PHPBB3-17109
2024-05-17 14:38:18 +00:00
Marc Alexander
ed0b5020a9 [ticket/17312] Use user_last_active instead of user_lastvisit where possible
PHPBB3-17312
2024-05-16 20:44:38 +02:00
Marc Alexander
b2d2216b87 [ticket/17315] Rename events and add them to events.md
PHPBB3-17315
2024-05-16 16:47:04 +02:00
Daniel James
766ecaed5a [ticket/17315] Add template events to acp_groups
PHPBB3-17315
2024-05-16 16:46:55 +02:00
Marc Alexander
a7a53de34d [ticket/17312] Add migration for user_last_active column
PHPBB3-17312
2024-05-14 21:42:02 +02:00
Marc Alexander
6aa980eadb Merge branch 'prep-release-3.3.12' into 3.3.x 2024-05-13 20:51:08 +02:00
Marc Alexander
dbf0b9ebe6 Merge pull request #6610 from Crizz0/ticket/17311
[ticket/17311] Update PHP requirements and tested version
2024-05-13 20:50:11 +02:00
Christian Schnegelberger
874fb8c59c [ticket/17311] Update PHP requirements and tested version
PHPBB3-17176
2024-05-13 19:21:52 +02:00
Marc Alexander
e195e2ad82 [3.3.x] Update version to 3.3.13-dev 2024-05-12 21:10:16 +02:00
Marc Alexander
9aebbcc1a0 Merge branch 'prep-release-3.3.12' into 3.3.x 2024-05-12 21:08:01 +02:00
battye
de31c4e797 [ticket/17175] Fix topic title in the breadcrumbs when emailing a topic
PHPBB3-17175
2024-05-12 09:53:07 +00:00
101 changed files with 2343 additions and 505 deletions

View File

@@ -30,7 +30,7 @@ installer:
server:
cookie_secure: false
server_protocol: http://
force_server_vars: false
force_server_vars: true
server_name: localhost
server_port: 80
script_path: /

View File

@@ -34,6 +34,14 @@ sudo ln -s /workspaces/phpbb/phpBB /var/www/html
echo "[Codespaces] Copy phpBB configuration"
cp /workspaces/phpbb/.devcontainer/resources/phpbb-config.yml /workspaces/phpbb/phpBB/install/install-config.yml
# Force the server URL to reflect the Codespace
# https://docs.github.com/en/codespaces/developing-in-a-codespace/default-environment-variables-for-your-codespace
if [ "$CODESPACES" = true ] ; then
echo "[Codespaces] Set the phpBB server name using default environment variables"
codespaces_url="${CODESPACE_NAME}-80.${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}"
sed -i "s/localhost/$codespaces_url/g" /workspaces/phpbb/phpBB/install/install-config.yml
fi
# Install phpBB
echo "[Codespaces] Run phpBB CLI installation"
cd /workspaces/phpbb/phpBB && composer install --no-interaction

View File

@@ -7,4 +7,4 @@ Checklist:
Tracker ticket:
https://tracker.phpbb.com/browse/PHPBB3-12345
https://tracker.phpbb.com/browse/PHPBB-12345

View File

@@ -128,6 +128,8 @@ jobs:
db: "mysql:5.7"
- php: '8.3'
db: "mysql:5.7"
- php: '8.4'
db: "mysql:5.7"
name: PHP ${{ matrix.php }} - ${{ matrix.db_alias != '' && matrix.db_alias || matrix.db }}
@@ -261,6 +263,8 @@ jobs:
db: "postgres:14"
- php: '8.3'
db: "postgres:14"
- php: '8.4'
db: "postgres:14"
name: PHP ${{ matrix.php }} - ${{ matrix.db }}
@@ -469,6 +473,9 @@ jobs:
- php: '8.3'
db: "postgres"
type: 'unit'
- php: '8.4'
db: "postgres"
type: 'unit'
- php: '7.4'
db: "postgres"
type: 'functional'
@@ -484,6 +491,9 @@ jobs:
- php: '8.3'
db: "postgres"
type: 'functional'
- php: '8.4'
db: "postgres"
type: 'functional'
name: Windows - PHP ${{ matrix.php }} - ${{ matrix.db }} - ${{ matrix.type }}

View File

@@ -2,9 +2,9 @@
<project name="phpBB" description="The phpBB forum software" default="all" basedir="../">
<!-- a few settings for the build -->
<property name="newversion" value="3.3.12-RC1" />
<property name="prevversion" value="3.3.11" />
<property name="olderversions" value="3.1.0, 3.1.1, 3.1.2, 3.1.3, 3.1.4, 3.1.5, 3.1.6, 3.1.7, 3.1.7-pl1, 3.1.8, 3.1.9, 3.1.10, 3.1.11, 3.1.12, 3.2.0, 3.2.1, 3.2.2, 3.2.3, 3.2.4, 3.2.5, 3.2.6, 3.2.7, 3.2.8, 3.2.9, 3.2.10, 3.2.11, 3.3.0, 3.3.1, 3.3.2, 3.3.3, 3.3.4, 3.3.5, 3.3.6, 3.3.7, 3.3.8, 3.3.9, 3.3.10" />
<property name="newversion" value="3.3.13-RC1" />
<property name="prevversion" value="3.3.12" />
<property name="olderversions" value="3.1.0, 3.1.1, 3.1.2, 3.1.3, 3.1.4, 3.1.5, 3.1.6, 3.1.7, 3.1.7-pl1, 3.1.8, 3.1.9, 3.1.10, 3.1.11, 3.1.12, 3.2.0, 3.2.1, 3.2.2, 3.2.3, 3.2.4, 3.2.5, 3.2.6, 3.2.7, 3.2.8, 3.2.9, 3.2.10, 3.2.11, 3.3.0, 3.3.1, 3.3.2, 3.3.3, 3.3.4, 3.3.5, 3.3.6, 3.3.7, 3.3.8, 3.3.9, 3.3.10, 3.3.11" />
<!-- no configuration should be needed beyond this point -->
<property name="oldversions" value="${olderversions}, ${prevversion}" />
@@ -181,6 +181,7 @@
<!-- create an empty config.php file (not for diffs) -->
<touch file="build/new_version/phpBB3/config.php" />
<copy file="build/new_version/phpBB3/phpbb/.htaccess" tofile="build/new_version/phpBB3/vendor/.htaccess" />
</target>

Binary file not shown.

View File

@@ -224,7 +224,7 @@ do
"footer")
err=$ERR_FOOTER;
# Each ticket is on its own line
echo "$line" | grep -Eq "^PHPBB3-[0-9]+$";
echo "$line" | grep -Eq "^PHPBB3?-[0-9]+$";
;;
"eof")
err=$ERR_EOF;
@@ -356,7 +356,7 @@ echo "$expecting" | grep -q "eof" || (
# Check the branch ticket is mentioned, doesn't make sense otherwise
if [ $ticket -gt 0 ]
then
echo "$tickets" | grep -Eq "\bPHPBB3-$ticket\b" || (
echo "$tickets" | grep -Eq "\bPHPBB3?-$ticket\b" || (
complain "Ticket ID [$ticket] of branch missing from list of tickets:" >&2;
complain "$tickets" | sed 's/ /\n/g;s/^/* /g' >&2;
quit $ERR_FOOTER;

View File

@@ -47,7 +47,7 @@ then
# Branch is prefixed with 'ticket/', append ticket ID to message
if [ "$branch" != "${branch##ticket/}" ];
then
tail="$(printf '\n\nPHPBB3-%s' "$ticket_id")";
tail="$(printf '\n\nPHPBB-%s' "$ticket_id")";
fi
fi

View File

@@ -225,6 +225,7 @@
<fieldset>
<legend>{L_ADD_USERS}</legend>
{% EVENT acp_groups_add_user_options_before %}
<dl>
<dt><label for="leader">{L_USER_GROUP_LEADER}{L_COLON}</label></dt>
<dd><label><input name="leader" type="radio" class="radio" value="1" /> {L_YES}</label>
@@ -235,11 +236,13 @@
<dd><label><input name="default" type="radio" class="radio" value="1" /> {L_YES}</label>
<label><input name="default" type="radio" class="radio" id="default" value="0" checked="checked" /> {L_NO}</label></dd>
</dl>
{% EVENT acp_groups_add_user_usernames_before %}
<dl>
<dt><label for="usernames">{L_USERNAME}{L_COLON}</label><br /><span>{L_USERNAMES_EXPLAIN}</span></dt>
<dd><textarea id="usernames" name="usernames" cols="40" rows="5"></textarea></dd>
<dd><!-- EVENT acp_groups_find_username_prepend -->[ <a href="{U_FIND_USERNAME}" onclick="find_username(this.href); return false;">{L_FIND_USERNAME}</a> ]<!-- EVENT acp_groups_find_username_append --></dd>
</dl>
{% EVENT acp_groups_add_user_options_after %}
<p class="quick">
<input class="button2" type="submit" name="addusers" value="{L_SUBMIT}" />

View File

@@ -14,27 +14,31 @@
<p>{L_ADMIN_INTRO}</p>
<!-- IF S_UPDATE_INCOMPLETE -->
{% if S_UPDATE_INCOMPLETE %}
<div class="errorbox">
<p>{L_UPDATE_INCOMPLETE} <a href="{U_VERSIONCHECK}">{L_MORE_INFORMATION}</a></p>
<p>{{ lang('UPDATE_INCOMPLETE') }} <a href="{{ U_VERSIONCHECK }}">{{ lang('MORE_INFORMATION') }}</a></p>
</div>
<!-- ELSEIF S_VERSIONCHECK_FAIL -->
{% elseif S_VERSIONCHECK_FAIL %}
<div class="errorbox notice">
<p>{L_VERSIONCHECK_FAIL}</p>
<p>{VERSIONCHECK_FAIL_REASON}</p>
<p><a href="{U_VERSIONCHECK_FORCE}">{L_VERSIONCHECK_FORCE_UPDATE}</a> &middot; <a href="{U_VERSIONCHECK}">{L_MORE_INFORMATION}</a></p>
<p>{{ lang('VERSIONCHECK_FAIL') }}</p>
<p>{{ VERSIONCHECK_FAIL_REASON }}</p>
<p><a href="{{ U_VERSIONCHECK_FORCE }}">{{ lang('VERSIONCHECK_FORCE_UPDATE') }}</a> &middot; <a href="{{ U_VERSIONCHECK }}">{{ lang('MORE_INFORMATION') }}</a></p>
</div>
<!-- ELSEIF not S_VERSION_UP_TO_DATE -->
{% elseif not S_VERSION_UP_TO_DATE %}
<div class="errorbox">
<p>{L_VERSION_NOT_UP_TO_DATE_TITLE}</p>
<p><a href="{U_VERSIONCHECK_FORCE}">{L_VERSIONCHECK_FORCE_UPDATE}</a> &middot; <a href="{U_VERSIONCHECK}">{L_MORE_INFORMATION}</a></p>
<p>{{ lang('VERSION_NOT_UP_TO_DATE_TITLE') }}</p>
<p><a href="{{ U_VERSIONCHECK_FORCE }}">{{ lang('VERSIONCHECK_FORCE_UPDATE') }}</a> &middot; <a href="{{ U_VERSIONCHECK }}">{{ lang('MORE_INFORMATION') }}</a></p>
</div>
<!-- ENDIF -->
<!-- IF S_VERSION_UPGRADEABLE -->
{% elseif S_VERSION_UP_TO_DATE && S_VERSIONCHECK_FORCE %}
<div class="successbox">
<p>{{ lang('VERSION_UP_TO_DATE_ACP') }}</p>
</div>
{% endif %}
{% if S_VERSION_UPGRADEABLE %}
<div class="errorbox notice">
<p>{UPGRADE_INSTRUCTIONS}</p>
<p>{{ UPGRADE_INSTRUCTIONS }}</p>
</div>
<!-- ENDIF -->
{% endif %}
<!-- IF S_SEARCH_INDEX_MISSING -->
<div class="errorbox">

176
phpBB/composer.lock generated
View File

@@ -3027,16 +3027,16 @@
},
{
"name": "myclabs/deep-copy",
"version": "1.11.1",
"version": "1.12.0",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c"
"reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
"reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
"shasum": ""
},
"require": {
@@ -3044,11 +3044,12 @@
},
"conflict": {
"doctrine/collections": "<1.6.8",
"doctrine/common": "<2.13.3 || >=3,<3.2.2"
"doctrine/common": "<2.13.3 || >=3 <3.2.2"
},
"require-dev": {
"doctrine/collections": "^1.6.8",
"doctrine/common": "^2.13.3 || ^3.2.2",
"phpspec/prophecy": "^1.10",
"phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
},
"type": "library",
@@ -3074,7 +3075,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.11.1"
"source": "https://github.com/myclabs/DeepCopy/tree/1.12.0"
},
"funding": [
{
@@ -3082,7 +3083,7 @@
"type": "tidelift"
}
],
"time": "2023-03-08T13:26:56+00:00"
"time": "2024-06-12T14:39:25+00:00"
},
{
"name": "phar-io/manifest",
@@ -3533,24 +3534,24 @@
},
{
"name": "phpspec/prophecy",
"version": "v1.18.0",
"version": "v1.19.0",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "d4f454f7e1193933f04e6500de3e79191648ed0c"
"reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/d4f454f7e1193933f04e6500de3e79191648ed0c",
"reference": "d4f454f7e1193933f04e6500de3e79191648ed0c",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/67a759e7d8746d501c41536ba40cd9c0a07d6a87",
"reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.2 || ^2.0",
"php": "^7.2 || 8.0.* || 8.1.* || 8.2.* || 8.3.*",
"phpdocumentor/reflection-docblock": "^5.2",
"sebastian/comparator": "^3.0 || ^4.0 || ^5.0",
"sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0"
"sebastian/comparator": "^3.0 || ^4.0 || ^5.0 || ^6.0",
"sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0 || ^6.0"
},
"require-dev": {
"phpspec/phpspec": "^6.0 || ^7.0",
@@ -3596,9 +3597,9 @@
],
"support": {
"issues": "https://github.com/phpspec/prophecy/issues",
"source": "https://github.com/phpspec/prophecy/tree/v1.18.0"
"source": "https://github.com/phpspec/prophecy/tree/v1.19.0"
},
"time": "2023-12-07T16:22:33+00:00"
"time": "2024-02-29T11:52:51+00:00"
},
{
"name": "phpunit/dbunit",
@@ -3726,16 +3727,16 @@
},
{
"name": "phpunit/php-file-iterator",
"version": "2.0.5",
"version": "2.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5"
"reference": "69deeb8664f611f156a924154985fbd4911eb36b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5",
"reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/69deeb8664f611f156a924154985fbd4911eb36b",
"reference": "69deeb8664f611f156a924154985fbd4911eb36b",
"shasum": ""
},
"require": {
@@ -3774,7 +3775,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
"source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.5"
"source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.6"
},
"funding": [
{
@@ -3782,7 +3783,7 @@
"type": "github"
}
],
"time": "2021-12-02T12:42:26+00:00"
"time": "2024-03-01T13:39:50+00:00"
},
{
"name": "phpunit/php-text-template",
@@ -3831,16 +3832,16 @@
},
{
"name": "phpunit/php-timer",
"version": "2.1.3",
"version": "2.1.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-timer.git",
"reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662"
"reference": "a691211e94ff39a34811abd521c31bd5b305b0bb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662",
"reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662",
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/a691211e94ff39a34811abd521c31bd5b305b0bb",
"reference": "a691211e94ff39a34811abd521c31bd5b305b0bb",
"shasum": ""
},
"require": {
@@ -3878,7 +3879,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-timer/issues",
"source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3"
"source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.4"
},
"funding": [
{
@@ -3886,7 +3887,7 @@
"type": "github"
}
],
"time": "2020-11-30T08:20:02+00:00"
"time": "2024-03-01T13:42:41+00:00"
},
{
"name": "phpunit/php-token-stream",
@@ -4038,16 +4039,16 @@
},
{
"name": "sebastian/code-unit-reverse-lookup",
"version": "1.0.2",
"version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
"reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619"
"reference": "92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619",
"reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619",
"url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54",
"reference": "92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54",
"shasum": ""
},
"require": {
@@ -4081,7 +4082,7 @@
"homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
"support": {
"issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
"source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2"
"source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.3"
},
"funding": [
{
@@ -4089,7 +4090,7 @@
"type": "github"
}
],
"time": "2020-11-30T08:15:22+00:00"
"time": "2024-03-01T13:45:45+00:00"
},
{
"name": "sebastian/comparator",
@@ -4167,16 +4168,16 @@
},
{
"name": "sebastian/diff",
"version": "3.0.4",
"version": "3.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "6296a0c086dd0117c1b78b059374d7fcbe7545ae"
"reference": "98ff311ca519c3aa73ccd3de053bdb377171d7b6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/6296a0c086dd0117c1b78b059374d7fcbe7545ae",
"reference": "6296a0c086dd0117c1b78b059374d7fcbe7545ae",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/98ff311ca519c3aa73ccd3de053bdb377171d7b6",
"reference": "98ff311ca519c3aa73ccd3de053bdb377171d7b6",
"shasum": ""
},
"require": {
@@ -4221,7 +4222,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
"source": "https://github.com/sebastianbergmann/diff/tree/3.0.4"
"source": "https://github.com/sebastianbergmann/diff/tree/3.0.6"
},
"funding": [
{
@@ -4229,20 +4230,20 @@
"type": "github"
}
],
"time": "2023-05-07T05:30:20+00:00"
"time": "2024-03-02T06:16:36+00:00"
},
{
"name": "sebastian/environment",
"version": "4.2.4",
"version": "4.2.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
"reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0"
"reference": "56932f6049a0482853056ffd617c91ffcc754205"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0",
"reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/56932f6049a0482853056ffd617c91ffcc754205",
"reference": "56932f6049a0482853056ffd617c91ffcc754205",
"shasum": ""
},
"require": {
@@ -4284,7 +4285,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/environment/issues",
"source": "https://github.com/sebastianbergmann/environment/tree/4.2.4"
"source": "https://github.com/sebastianbergmann/environment/tree/4.2.5"
},
"funding": [
{
@@ -4292,24 +4293,24 @@
"type": "github"
}
],
"time": "2020-11-30T07:53:42+00:00"
"time": "2024-03-01T13:49:59+00:00"
},
{
"name": "sebastian/exporter",
"version": "3.1.5",
"version": "3.1.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "73a9676f2833b9a7c36968f9d882589cd75511e6"
"reference": "1939bc8fd1d39adcfa88c5b35335910869214c56"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/73a9676f2833b9a7c36968f9d882589cd75511e6",
"reference": "73a9676f2833b9a7c36968f9d882589cd75511e6",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/1939bc8fd1d39adcfa88c5b35335910869214c56",
"reference": "1939bc8fd1d39adcfa88c5b35335910869214c56",
"shasum": ""
},
"require": {
"php": ">=7.0",
"php": ">=7.2",
"sebastian/recursion-context": "^3.0"
},
"require-dev": {
@@ -4361,7 +4362,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
"source": "https://github.com/sebastianbergmann/exporter/tree/3.1.5"
"source": "https://github.com/sebastianbergmann/exporter/tree/3.1.6"
},
"funding": [
{
@@ -4369,7 +4370,7 @@
"type": "github"
}
],
"time": "2022-09-14T06:00:17+00:00"
"time": "2024-03-02T06:21:38+00:00"
},
{
"name": "sebastian/global-state",
@@ -4428,16 +4429,16 @@
},
{
"name": "sebastian/object-enumerator",
"version": "3.0.4",
"version": "3.0.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-enumerator.git",
"reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2"
"reference": "ac5b293dba925751b808e02923399fb44ff0d541"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2",
"reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2",
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/ac5b293dba925751b808e02923399fb44ff0d541",
"reference": "ac5b293dba925751b808e02923399fb44ff0d541",
"shasum": ""
},
"require": {
@@ -4473,7 +4474,7 @@
"homepage": "https://github.com/sebastianbergmann/object-enumerator/",
"support": {
"issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
"source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4"
"source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.5"
},
"funding": [
{
@@ -4481,20 +4482,20 @@
"type": "github"
}
],
"time": "2020-11-30T07:40:27+00:00"
"time": "2024-03-01T13:54:02+00:00"
},
{
"name": "sebastian/object-reflector",
"version": "1.1.2",
"version": "1.1.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-reflector.git",
"reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d"
"reference": "1d439c229e61f244ff1f211e5c99737f90c67def"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d",
"reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d",
"url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/1d439c229e61f244ff1f211e5c99737f90c67def",
"reference": "1d439c229e61f244ff1f211e5c99737f90c67def",
"shasum": ""
},
"require": {
@@ -4528,7 +4529,7 @@
"homepage": "https://github.com/sebastianbergmann/object-reflector/",
"support": {
"issues": "https://github.com/sebastianbergmann/object-reflector/issues",
"source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2"
"source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.3"
},
"funding": [
{
@@ -4536,20 +4537,20 @@
"type": "github"
}
],
"time": "2020-11-30T07:37:18+00:00"
"time": "2024-03-01T13:56:04+00:00"
},
{
"name": "sebastian/recursion-context",
"version": "3.0.1",
"version": "3.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
"reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb"
"reference": "9bfd3c6f1f08c026f542032dfb42813544f7d64c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb",
"reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/9bfd3c6f1f08c026f542032dfb42813544f7d64c",
"reference": "9bfd3c6f1f08c026f542032dfb42813544f7d64c",
"shasum": ""
},
"require": {
@@ -4591,7 +4592,7 @@
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
"support": {
"issues": "https://github.com/sebastianbergmann/recursion-context/issues",
"source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1"
"source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.2"
},
"funding": [
{
@@ -4599,20 +4600,20 @@
"type": "github"
}
],
"time": "2020-11-30T07:34:24+00:00"
"time": "2024-03-01T14:07:30+00:00"
},
{
"name": "sebastian/resource-operations",
"version": "2.0.2",
"version": "2.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/resource-operations.git",
"reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3"
"reference": "72a7f7674d053d548003b16ff5a106e7e0e06eee"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3",
"reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3",
"url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/72a7f7674d053d548003b16ff5a106e7e0e06eee",
"reference": "72a7f7674d053d548003b16ff5a106e7e0e06eee",
"shasum": ""
},
"require": {
@@ -4642,8 +4643,7 @@
"description": "Provides a list of PHP built-in functions that operate on resources",
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
"support": {
"issues": "https://github.com/sebastianbergmann/resource-operations/issues",
"source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2"
"source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.3"
},
"funding": [
{
@@ -4651,7 +4651,7 @@
"type": "github"
}
],
"time": "2020-11-30T07:30:19+00:00"
"time": "2024-03-01T13:59:09+00:00"
},
{
"name": "sebastian/version",
@@ -4702,16 +4702,16 @@
},
{
"name": "squizlabs/php_codesniffer",
"version": "3.9.0",
"version": "3.10.1",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
"reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b"
"reference": "8f90f7a53ce271935282967f53d0894f8f1ff877"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/d63cee4890a8afaf86a22e51ad4d97c91dd4579b",
"reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b",
"url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/8f90f7a53ce271935282967f53d0894f8f1ff877",
"reference": "8f90f7a53ce271935282967f53d0894f8f1ff877",
"shasum": ""
},
"require": {
@@ -4778,7 +4778,7 @@
"type": "open_collective"
}
],
"time": "2024-02-16T15:06:51+00:00"
"time": "2024-05-22T21:24:41+00:00"
},
{
"name": "symfony/browser-kit",
@@ -4985,16 +4985,16 @@
},
{
"name": "theseer/tokenizer",
"version": "1.2.2",
"version": "1.2.3",
"source": {
"type": "git",
"url": "https://github.com/theseer/tokenizer.git",
"reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96"
"reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
"reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
"reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
"shasum": ""
},
"require": {
@@ -5023,7 +5023,7 @@
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
"support": {
"issues": "https://github.com/theseer/tokenizer/issues",
"source": "https://github.com/theseer/tokenizer/tree/1.2.2"
"source": "https://github.com/theseer/tokenizer/tree/1.2.3"
},
"funding": [
{
@@ -5031,7 +5031,7 @@
"type": "github"
}
],
"time": "2023-11-20T00:12:19+00:00"
"time": "2024-03-03T12:36:25+00:00"
},
{
"name": "webmozart/assert",

View File

@@ -281,6 +281,22 @@ services:
tags:
- { name: console.command }
console.command.user.delete_id:
class: phpbb\console\command\user\delete_id
arguments:
- '@dbal.conn'
- '@language'
- '@log'
- '@user'
- '@user_loader'
- '%tables.bots%'
- '%tables.user_group%'
- '%tables.users%'
- '%core.root_path%'
- '%core.php_ext%'
tags:
- { name: console.command }
console.command.user.reclean:
class: phpbb\console\command\user\reclean
arguments:

View File

@@ -1,4 +1,15 @@
services:
phpbb.ucp.controller.delete_cookies:
class: phpbb\ucp\controller\delete_cookies
arguments:
- '@config'
- '@dispatcher'
- '@language'
- '@request'
- '@user'
- '%core.root_path%'
- '%core.php_ext%'
phpbb.ucp.controller.reset_password:
class: phpbb\ucp\controller\reset_password
arguments:

View File

@@ -1,3 +1,7 @@
phpbb_ucp_delete_cookies_controller:
path: /delete_cookies
defaults: { _controller: phpbb.ucp.controller.delete_cookies:handle }
phpbb_ucp_reset_password_controller:
path: /reset_password
defaults: { _controller: phpbb.ucp.controller.reset_password:reset }

View File

@@ -50,6 +50,8 @@
<ol>
<li><a href="#changelog">Changelog</a>
<ul>
<li><a href="#v3312">Changes since 3.3.12</a></li>
<li><a href="#v3312rc1">Changes since 3.3.12-RC1</a></li>
<li><a href="#v3311">Changes since 3.3.11</a></li>
<li><a href="#v3310">Changes since 3.3.10</a></li>
<li><a href="#v3310rc1">Changes since 3.3.10-RC1</a></li>
@@ -168,6 +170,70 @@
<div class="inner">
<div class="content">
<a name="v3312"></a><h3>Changes since 3.3.12</h3>
<h4>Bug</h4>
<ul>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-13916">PHPBB-13916</a>] - Cancelling save draft removes previous notify setting on posting page</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-14454">PHPBB-14454</a>] - Accessing ACP modules while testing user permissions returns a General Error</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-15043">PHPBB-15043</a>] - Searching no longer working in 3.2.0</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-15576">PHPBB-15576</a>] - PM subject truncated to shorter length than maxlength</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-16213">PHPBB-16213</a>] - vendor and phpbb folders should have .htaccess files</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-16907">PHPBB-16907</a>] - &quot;phpbb&quot; value in &quot;hiddenSegments&quot; blocks client requests for extensions in IIS</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17109">PHPBB-17109</a>] - Users without the &quot;Can use signature&quot; permission should not see checkboxes for signature</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17175">PHPBB-17175</a>] - Breadcrumbs show wrong forum and topic when using 'email topic'</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17301">PHPBB-17301</a>] - Wrong length parameter for fread in phpbb/cache/driver/file.php can lead to unusable forum</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17327">PHPBB-17327</a>] - Fix linting issue in console user add command</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17332">PHPBB-17332</a>] - New permission copied from existing permission ignores permission set options</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17337">PHPBB-17337</a>] - Transaction begin is missing from mysqli driver </li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17338">PHPBB-17338</a>] - Incorrect members list sorting by user_last_visit</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17351">PHPBB-17351</a>] - phpBB2 password hashes incorrectly handled during rehash cron</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17352">PHPBB-17352</a>] - Long rank titles push other profile details below</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17353">PHPBB-17353</a>] - Gravatar avatar src is not image src</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17356">PHPBB-17356</a>] - Errors hidden by at are being displayed in PHP 8 or newer</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17358">PHPBB-17358</a>] - Redis cache never expires with the TTL of 0</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17362">PHPBB-17362</a>] - Missing declaration of property in extension manager</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17365">PHPBB-17365</a>] - Enforce the search word limit on queries containing operators without white space</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17366">PHPBB-17366</a>] - Captcha disappears on error message from registration &amp; posting</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17369">PHPBB-17369</a>] - Permanently deleting soft-deleted topics returns incorrect forum in redirect link</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17370">PHPBB-17370</a>] - Deleting Cookies on FAQ/other pages</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17374">PHPBB-17374</a>] - ACP - Maintenance - Logs: Deleting Error / Bug</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17375">PHPBB-17375</a>] - User lastvisit gets updated too often in session garbage collection</li>
</ul>
<h4>Improvement</h4>
<ul>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-16553">PHPBB-16553</a>] - Disapproving a reported post causes a &quot;Module not accessible&quot; error</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17308">PHPBB-17308</a>] - Rename tracker project key to PHPBB-</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17315">PHPBB-17315</a>] - Add new template events to group</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17316">PHPBB-17316</a>] - Add template events to ucp_groups_manage</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17317">PHPBB-17317</a>] - Update button text and make it more readable</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17325">PHPBB-17325</a>] - Show explicit message for &quot;Re-Check version&quot; if installed version is still up to date</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17340">PHPBB-17340</a>] - Update composer to 2.7.7</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17342">PHPBB-17342</a>] - Add PHP 8.4-dev tests to GitHub Actions</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17347">PHPBB-17347</a>] - Support deleting users by ID via console</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17350">PHPBB-17350</a>] - Add user IP address to log when installing extensions on fresh installs</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17355">PHPBB-17355</a>] - Update gravatar hash to sha256</li>
</ul>
<h4>Task</h4>
<ul>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-13933">PHPBB-13933</a>] - Update tokens' definitions in acp_bbcodes</li>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-16890">PHPBB-16890</a>] - Edit the config sample files and web.config to deny access to the &quot;config&quot; directory</li>
</ul>
<a name="v3312rc1"></a><h3>Changes since 3.3.12-RC1</h3>
<h4>Bug</h4>
<ul>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB3-17312">PHPBB3-17312</a>] - User last visit gets updated too often</li>
</ul>
<h4>Improvement</h4>
<ul>
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB3-17324">PHPBB3-17324</a>] - Add template event to notification_dropdown.html</li>
</ul>
<h4>Hardening</h4>
<ul>
<li>[<a href="https://tracker.phpbb.com/browse/SECURITY-276">SECURITY-276</a>] - Prevent resending activation email too often</li>
<li>[<a href="https://tracker.phpbb.com/browse/SECURITY-278">SECURITY-278</a>] - Always release cron lock, even invalid task is passed</li>
</ul>
<a name="v3311"></a><h3>Changes since 3.3.11</h3>
<h4>Bug</h4>
<ul>

View File

@@ -147,7 +147,7 @@
<li>Oracle</li>
</ul>
</li>
<li><strong>PHP 7.2.0+</strong> up to and including <strong>PHP 8.1</strong> with support for the database you intend to use.</li>
<li><strong>PHP 7.2.0+</strong> up to and including <strong>PHP 8.3</strong> with support for the database you intend to use.</li>
<li>The following PHP modules are required:
<ul>
<li>json</li>

View File

@@ -327,7 +327,7 @@
<p>Please remember that running any application on a development (unstable, e.g. a beta release) version of PHP can lead to strange/unexpected results which may appear to be bugs in the application. Therefore, we recommend you upgrade to the newest stable version of PHP before running phpBB. If you are running a development version of PHP please check any bugs you find on a system running a stable release before submitting.</p>
<p>This board has been developed and tested under Linux and Windows (amongst others) running Apache using MySQLi 4.1.3, 4.x, 5.x, MariaDB 5.x, PostgreSQL 8.x, Oracle 8 and SQLite 3. Versions of PHP used range from 7.2.0 to 7.4.x, 8.0.x and 8.1.x.</p>
<p>This board has been developed and tested under Linux and Windows (amongst others) running Apache using MySQLi 4.1.3, 4.x, 5.x, MariaDB 5.x, PostgreSQL 8.x, Oracle 8 and SQLite 3. Versions of PHP used range from 7.2.0 to 7.4.x and 8.0.x to 8.3.x.</p>
<a name="phpsec"></a><h3>7.i. Notice on PHP security issues</h3>

View File

@@ -184,6 +184,24 @@ acp_group_types_prepend
* Since: 3.2.9-RC1
* Purpose: Add additional group type options to group settings (prepend the list)
acp_groups_add_user_options_after
===
* Location: adm/style/acp_groups.html
* Since: 3.3.13-RC1
* Purpose: Add content after options for adding user to group in the ACP
acp_groups_add_user_options_before
===
* Location: adm/style/acp_groups.html
* Since: 3.3.13-RC1
* Purpose: Add content before options for adding user to group in the ACP
acp_groups_add_user_usernames_before
===
* Location: adm/style/acp_groups.html
* Since: 3.3.13-RC1
* Purpose: Add content before usernames option for adding user to group in the ACP
acp_groups_find_username_append
===
* Location: adm/style/acp_groups.html
@@ -1597,6 +1615,20 @@ navbar_header_username_prepend
* Since: 3.1.0-RC1
* Purpose: Add text and HTMl before the username shown in the navbar.
notification_dropdown_footer_after
===
* Locations:
+ styles/prosilver/template/notification_dropdown.html
* Since: 3.3.12
* Purpose: Add content after notifications list footer.
notification_dropdown_footer_before
===
* Locations:
+ styles/prosilver/template/notification_dropdown.html
* Since: 3.3.12
* Purpose: Add content before notifications list footer.
overall_footer_after
===
* Locations:
@@ -2521,6 +2553,20 @@ ucp_friend_list_before
* Since: 3.1.0-a4
* Purpose: Add optional elements before list of friends in UCP
ucp_group_settings_after
===
* Locations:
+ styles/prosilver/template/ucp_groups_manage.html
* Since: 3.3.13-RC1
* Purpose: Add content after options for managing a group in the UCP
ucp_group_settings_before
===
* Locations:
+ styles/prosilver/template/ucp_groups_manage.html
* Since: 3.3.13-RC1
* Purpose: Add content before options for managing a group in the UCP
ucp_header_content_before
===
* Locations:

View File

@@ -1,7 +1,7 @@
# Sample lighttpd configuration file for phpBB.
# Global settings have been removed, copy them
# from your system's lighttpd.conf.
# Tested with lighttpd 1.4.35
# Tested with lighttpd 1.4.36
# If you want to use the X-Sendfile feature,
# uncomment the 'allow-x-send-file' for the fastcgi
@@ -13,7 +13,7 @@
# for the details on X-Sendfile.
# Load moules
server.modules += (
server.modules += (
"mod_access",
"mod_fastcgi",
"mod_rewrite",
@@ -32,12 +32,12 @@ $HTTP["host"] == "www.myforums.com" {
server.name = "www.myforums.com"
server.document-root = "/path/to/phpbb"
server.dir-listing = "disable"
index-file.names = ( "index.php", "index.htm", "index.html" )
accesslog.filename = "/var/log/lighttpd/access-www.myforums.com.log"
# Deny access to internal phpbb files.
$HTTP["url"] =~ "^/(config\.php|common\.php|cache|files|images/avatars/upload|includes|phpbb|store|vendor)" {
# Deny access to internal phpbb files.
$HTTP["url"] =~ "^/(config|common\.php|cache|files|images/avatars/upload|includes|phpbb|store|vendor)" {
url.access-deny = ( "" )
}
@@ -45,27 +45,28 @@ $HTTP["host"] == "www.myforums.com" {
$HTTP["url"] =~ "/\.svn|/\.git" {
url.access-deny = ( "" )
}
# Deny access to apache configuration files.
$HTTP["url"] =~ "/\.htaccess|/\.htpasswd|/\.htgroups" {
url.access-deny = ( "" )
}
# The following 3 lines will rewrite URLs passed through the front controller
# to not require app.php in the actual URL. In other words, a controller is
# by default accessed at /app.php/my/controller, but can also be accessed at
# /my/controller
url.rewrite-if-not-file = (
"^/(.*)$" => "/app.php/$1"
"^/install/(.*)$" => "/install/app.php/$1",
"^/(.*)$" => "/app.php/$1"
)
fastcgi.server = ( ".php" =>
fastcgi.server = ( ".php" =>
((
"bin-path" => "/usr/bin/php-cgi",
"socket" => "/tmp/php.socket",
"max-procs" => 4,
"idle-timeout" => 30,
"bin-environment" => (
"bin-environment" => (
"PHP_FCGI_CHILDREN" => "10",
"PHP_FCGI_MAX_REQUESTS" => "10000"
),

View File

@@ -63,7 +63,7 @@ server {
}
# Deny access to internal phpbb files.
location ~ /(config\.php|common\.php|cache|files|images/avatars/upload|includes|(?<!ext/)phpbb(?!\w+)|store|vendor) {
location ~ /(config|common\.php|cache|files|images/avatars/upload|includes|(?<!ext/)phpbb(?!\w+)|store|vendor) {
deny all;
# deny was ignored before 0.8.40 for connections over IPv6.
# Use internal directive to prohibit access on older versions.

View File

@@ -110,6 +110,7 @@ class acp_bbcodes
);
$bbcode_tokens = array('TEXT', 'SIMPLETEXT', 'INTTEXT', 'IDENTIFIER', 'NUMBER', 'EMAIL', 'URL', 'LOCAL_URL', 'RELATIVE_URL', 'COLOR');
$bbcode_tokens = array_merge($bbcode_tokens, ['ALNUM', 'CHOICE', 'FLOAT', 'HASHMAP', 'INT', 'IP', 'IPPORT', 'IPV4', 'IPV6', 'MAP', 'RANGE', 'REGEXP', 'TIMESTAMP', 'UINT']);
/**
* Modify custom bbcode template data before we display the add/edit form

View File

@@ -238,10 +238,11 @@ class acp_inactive
$messenger->save_queue();
// Add the remind state to the database
// Add the remind state to the database and increase activation expiration by one day
$sql = 'UPDATE ' . USERS_TABLE . '
SET user_reminded = user_reminded + 1,
user_reminded_time = ' . time() . '
user_reminded_time = ' . time() . ',
user_actkey_expiration = ' . (int) $user::get_token_expiration() . '
WHERE ' . $db->sql_in_set('user_id', $user_ids);
$db->sql_query($sql);

View File

@@ -51,7 +51,7 @@ class acp_logs
$pagination = $phpbb_container->get('pagination');
// Delete entries if requested and able
if (($deletemark || $deleteall) && $auth->acl_get('a_clearlogs'))
if (($deleteall || ($deletemark && count($marked))) && $auth->acl_get('a_clearlogs'))
{
if (confirm_box(true))
{

View File

@@ -454,6 +454,7 @@ class acp_main
$template->assign_vars(array(
'S_VERSION_UP_TO_DATE' => empty($updates_available),
'S_VERSION_UPGRADEABLE' => !empty($upgrades_available),
'S_VERSIONCHECK_FORCE' => (bool) $recheck,
'UPGRADE_INSTRUCTIONS' => !empty($upgrades_available) ? $user->lang('UPGRADE_INSTRUCTIONS', $upgrades_available['current'], $upgrades_available['announcement']) : false,
));
}

View File

@@ -385,14 +385,18 @@ class acp_users
$user_actkey = empty($user_activation_key) ? $user_actkey : $user_activation_key;
}
if ($user_row['user_type'] == USER_NORMAL || empty($user_activation_key))
{
$sql = 'UPDATE ' . USERS_TABLE . "
SET user_actkey = '" . $db->sql_escape($user_actkey) . "'
WHERE user_id = $user_id";
$db->sql_query($sql);
}
// Always update actkey even if same and also update actkey expiration to 24 hours from now
$sql_ary = [
'user_actkey' => $user_actkey,
'user_actkey_expiration' => $user::get_token_expiration(),
];
$sql = 'UPDATE ' . USERS_TABLE . '
SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
WHERE user_id = ' . (int) $user_id;
$db->sql_query($sql);
// Start sending email
$messenger = new messenger(false);
$messenger->template($email_template, $user_row['user_lang']);
@@ -1084,7 +1088,7 @@ class acp_users
$s_action_options .= '<option value="' . $value . '">' . $user->lang['USER_ADMIN_' . $lang] . '</option>';
}
$last_active = (!empty($user_row['session_time'])) ? $user_row['session_time'] : $user_row['user_lastvisit'];
$last_active = $user_row['user_last_active'] ?: ($user_row['session_time'] ?? 0);
$inactive_reason = '';
if ($user_row['user_type'] == USER_INACTIVE)

View File

@@ -28,7 +28,7 @@ if (!defined('IN_PHPBB'))
*/
// phpBB Version
@define('PHPBB_VERSION', '3.3.12-RC1');
@define('PHPBB_VERSION', '3.3.13-RC1');
// QA-related
// define('PHPBB_QA', 1);

View File

@@ -3028,8 +3028,16 @@ function msg_handler($errno, $msg_text, $errfile, $errline)
global $phpbb_root_path, $msg_title, $msg_long_text, $phpbb_log;
global $phpbb_container;
// https://www.php.net/manual/en/language.operators.errorcontrol.php
// error_reporting() return a different error code inside the error handler after php 8.0
$suppresed = E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR | E_PARSE;
if (PHP_VERSION_ID < 80000)
{
$suppresed = 0;
}
// Do not display notices if we suppress them via @
if (error_reporting() == 0 && $errno != E_USER_ERROR && $errno != E_USER_WARNING && $errno != E_USER_NOTICE)
if (error_reporting() == $suppresed && $errno != E_USER_ERROR && $errno != E_USER_WARNING && $errno != E_USER_NOTICE)
{
return;
}
@@ -4053,7 +4061,7 @@ function page_header($page_title = '', $display_online_list = false, $item_id =
'U_SEARCH_UNANSWERED' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=unanswered'),
'U_SEARCH_UNREAD' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=unreadposts'),
'U_SEARCH_ACTIVE_TOPICS'=> append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=active_topics'),
'U_DELETE_COOKIES' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=delete_cookies'),
'U_DELETE_COOKIES' => $controller_helper->route('phpbb_ucp_delete_cookies_controller'),
'U_CONTACT_US' => ($config['contact_admin_form_enable'] && $config['email_enable']) ? append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=contactadmin') : '',
'U_TEAM' => (!$auth->acl_get('u_viewprofile')) ? '' : append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=team'),
'U_TERMS_USE' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=terms'),

View File

@@ -325,119 +325,98 @@ function bump_topic_allowed($forum_id, $topic_bumped, $last_post_time, $topic_po
*
* @return string Context of the specified words separated by "..."
*/
function get_context($text, $words, $length = 400)
function get_context(string $text, array $words, int $length = 400): string
{
// first replace all whitespaces with single spaces
$text = preg_replace('/ +/', ' ', strtr($text, "\t\n\r\x0C ", ' '));
if ($length <= 0)
{
return '...';
}
// we need to turn the entities back into their original form, to not cut the message in between them
$entities = array('&lt;', '&gt;', '&#91;', '&#93;', '&#46;', '&#58;', '&#058;');
$characters = array('<', '>', '[', ']', '.', ':', ':');
$text = str_replace($entities, $characters, $text);
$text = html_entity_decode($text);
$word_indizes = array();
if (count($words))
// Replace all spaces/invisible characters with single spaces
$text = preg_replace("/\s+/u", ' ', $text);
$text_length = utf8_strlen($text);
// Get first occurrence of each word
$word_indexes = [];
foreach ($words as $word)
{
$match = '';
// find the starting indizes of all words
foreach ($words as $word)
$pos = utf8_stripos($text, $word);
if ($pos !== false)
{
if ($word)
{
if (preg_match('#(?:[^\w]|^)(' . $word . ')(?:[^\w]|$)#i', $text, $match))
{
if (empty($match[1]))
{
continue;
}
$pos = utf8_strpos($text, $match[1]);
if ($pos !== false)
{
$word_indizes[] = $pos;
}
}
}
}
unset($match);
if (count($word_indizes))
{
$word_indizes = array_unique($word_indizes);
sort($word_indizes);
$wordnum = count($word_indizes);
// number of characters on the right and left side of each word
$sequence_length = (int) ($length / (2 * $wordnum)) - 2;
$final_text = '';
$word = $j = 0;
$final_text_index = -1;
// cycle through every character in the original text
for ($i = $word_indizes[$word], $n = utf8_strlen($text); $i < $n; $i++)
{
// if the current position is the start of one of the words then append $sequence_length characters to the final text
if (isset($word_indizes[$word]) && ($i == $word_indizes[$word]))
{
if ($final_text_index < $i - $sequence_length - 1)
{
$final_text .= '... ' . preg_replace('#^([^ ]*)#', '', utf8_substr($text, $i - $sequence_length, $sequence_length));
}
else
{
// if the final text is already nearer to the current word than $sequence_length we only append the text
// from its current index on and distribute the unused length to all other sequenes
$sequence_length += (int) (($final_text_index - $i + $sequence_length + 1) / (2 * $wordnum));
$final_text .= utf8_substr($text, $final_text_index + 1, $i - $final_text_index - 1);
}
$final_text_index = $i - 1;
// add the following characters to the final text (see below)
$word++;
$j = 1;
}
if ($j > 0)
{
// add the character to the final text and increment the sequence counter
$final_text .= utf8_substr($text, $i, 1);
$final_text_index++;
$j++;
// if this is a whitespace then check whether we are done with this sequence
if (utf8_substr($text, $i, 1) == ' ')
{
// only check whether we have to exit the context generation completely if we haven't already reached the end anyway
if ($i + 4 < $n)
{
if (($j > $sequence_length && $word >= $wordnum) || utf8_strlen($final_text) > $length)
{
$final_text .= ' ...';
break;
}
}
else
{
// make sure the text really reaches the end
$j -= 4;
}
// stop context generation and wait for the next word
if ($j > $sequence_length)
{
$j = 0;
}
}
}
}
return str_replace($characters, $entities, $final_text);
$word_indexes[$pos] = $word;
}
}
if (!count($words) || !count($word_indizes))
if (!empty($word_indexes))
{
return str_replace($characters, $entities, ((utf8_strlen($text) >= $length + 3) ? utf8_substr($text, 0, $length) . '...' : $text));
ksort($word_indexes);
// Size of the fragment of text per word
$num_indexes = count($word_indexes);
$characters_per_word = (int) ($length / $num_indexes) + 2; // 2 to leave one character of margin at the sides to don't cut words
// Get text fragment indexes
$fragments = [];
foreach ($word_indexes as $index => $word)
{
$word_length = utf8_strlen($word);
$start = max(0, min($text_length - 1 - $characters_per_word, (int) ($index + ($word_length / 2) - ($characters_per_word / 2))));
$end = $start + $characters_per_word;
// Check if we can merge this fragment into the previous fragment
if (!empty($fragments))
{
[$prev_start, $prev_end] = end($fragments);
if ($prev_end + $characters_per_word >= $index + $word_length)
{
array_pop($fragments);
$start = $prev_start;
$end = $prev_end + $characters_per_word;
}
}
$fragments[] = [$start, $end];
}
}
else
{
// There is no coincidences, so we just create a fragment with the first $length characters
$fragments[] = [0, $length];
$end = $length;
}
$output = [];
foreach ($fragments as [$start, $end])
{
$fragment = utf8_substr($text, $start, $end - $start + 1);
$fragment_start = 0;
$fragment_end = $end - $start + 1;
// Find the first valid alphanumeric character in the fragment to don't cut words
if ($start > 0)
{
preg_match('/[^a-zA-Z0-9][a-zA-Z0-9]/u', $fragment, $matches, PREG_OFFSET_CAPTURE);
$fragment_start = (int) $matches[0][1] + 1; // first valid alphanumeric character
}
// Find the last valid alphanumeric character in the fragment to don't cut words
if ($end < $text_length - 1)
{
preg_match_all('/[a-zA-Z0-9][^a-zA-Z0-9]/u', $fragment, $matches, PREG_OFFSET_CAPTURE);
$fragment_end = end($matches[0])[1]; // last valid alphanumeric character
}
$output[] = utf8_substr($fragment, $fragment_start, $fragment_end - $fragment_start + 1);
}
return ($fragments[0][0] !== 0 ? '... ' : '') . htmlentities(implode(' ... ', $output)) . ($end < $text_length - 1 ? ' ...' : '');
}
/**

View File

@@ -1603,7 +1603,7 @@ function phpbb_show_profile($data, $user_notes_enabled = false, $warn_user_enabl
if ($data['user_allow_viewonline'] || $auth->acl_get('u_viewonline'))
{
$last_active = (!empty($data['session_time'])) ? $data['session_time'] : $data['user_lastvisit'];
$last_active = $data['user_last_active'] ?: ($data['session_time'] ?? 0);
}
else
{

View File

@@ -480,7 +480,7 @@ class p_master
*/
function set_active($id = false, $mode = false)
{
global $request;
global $auth, $request, $user;
$icat = false;
$this->active_module = false;
@@ -502,6 +502,14 @@ class p_master
$id = $this->p_class . '_' . $id;
}
// Fallback to acp main page for special restore permission mode
if ($user->data['user_perm_from'] && $auth->acl_get('a_switchperm'))
{
$id = '';
$mode = '';
$icat = false;
}
$category = false;
foreach ($this->module_ary as $row_id => $item_ary)
{

View File

@@ -1689,7 +1689,7 @@ function submit_pm($mode, $subject, &$data_ary, $put_in_outbox = true)
}
// First of all make sure the subject are having the correct length.
$subject = truncate_string($subject);
$subject = truncate_string($subject, $mode === 'post' ? 120 : 124);
$db->sql_transaction('begin');

View File

@@ -210,18 +210,18 @@ function user_add($user_row, $cp_data = false, $notifications_data = null)
// These are the additional vars able to be specified
$additional_vars = array(
'user_permissions' => '',
'user_timezone' => $config['board_timezone'],
'user_dateformat' => $config['default_dateformat'],
'user_lang' => $config['default_lang'],
'user_style' => (int) $config['default_style'],
'user_actkey' => '',
'user_ip' => '',
'user_regdate' => time(),
'user_passchg' => time(),
'user_options' => 230271,
'user_permissions' => '',
'user_timezone' => $config['board_timezone'],
'user_dateformat' => $config['default_dateformat'],
'user_lang' => $config['default_lang'],
'user_style' => (int) $config['default_style'],
'user_actkey' => '',
'user_ip' => '',
'user_regdate' => time(),
'user_passchg' => time(),
'user_options' => 230271,
// We do not set the new flag here - registration scripts need to specify it
'user_new' => 0,
'user_new' => 0,
'user_inactive_reason' => 0,
'user_inactive_time' => 0,

View File

@@ -222,6 +222,7 @@ function mcp_post_details($id, $mode, $action)
'U_POST_ACTION' => "$url&amp;i=$id&amp;mode=post_details", // Use this for action parameters
'U_APPROVE_ACTION' => append_sid("{$phpbb_root_path}mcp.$phpEx", "i=queue&amp;p=$post_id"),
'S_CAN_APPROVE' => $auth->acl_get('m_approve', $post_info['forum_id']),
'S_CAN_VIEWIP' => $auth->acl_get('m_info', $post_info['forum_id']),
'S_CAN_CHGPOSTER' => $auth->acl_get('m_chgposter', $post_info['forum_id']),
'S_CAN_LOCK_POST' => $auth->acl_get('m_lock', $post_info['forum_id']),

View File

@@ -252,6 +252,7 @@ class mcp_reports
$report_template = array(
'S_MCP_REPORT' => true,
'S_CLOSE_ACTION' => append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=reports&amp;mode=report_details&amp;p=' . $post_id),
'S_CAN_APPROVE' => $auth->acl_get('m_approve', $post_info['forum_id']),
'S_CAN_VIEWIP' => $auth->acl_get('m_info', $post_info['forum_id']),
'S_POST_REPORTED' => $post_info['post_reported'],
'S_POST_UNAPPROVED' => $post_info['post_visibility'] == ITEM_UNAPPROVED || $post_info['post_visibility'] == ITEM_REAPPROVE,
@@ -260,6 +261,7 @@ class mcp_reports
'S_USER_NOTES' => true,
'U_EDIT' => ($auth->acl_get('m_edit', $post_info['forum_id'])) ? append_sid("{$phpbb_root_path}posting.$phpEx", "mode=edit&amp;p={$post_info['post_id']}") : '',
'U_APPROVE_ACTION' => append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=queue&amp;p=' . $post_id),
'U_MCP_APPROVE' => append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=queue&amp;mode=approve_details&amp;p=' . $post_id),
'U_MCP_REPORT' => append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=reports&amp;mode=report_details&amp;p=' . $post_id),
'U_MCP_REPORTER_NOTES' => append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=notes&amp;mode=user_notes&amp;u=' . $report['user_id']),

View File

@@ -488,6 +488,8 @@ class ucp_prefs
}
$template->assign_vars(array(
'S_SIG_ALLOWED' => $config['allow_sig'] && $auth->acl_get('u_sig'),
'S_BBCODE' => $data['bbcode'],
'S_SMILIES' => $data['smilies'],
'S_SIG' => $data['sig'],

View File

@@ -196,9 +196,10 @@ class ucp_profile
{
$notifications_manager = $phpbb_container->get('notification_manager');
$notifications_manager->add_notifications('notification.type.admin_activate_user', array(
'user_id' => $user->data['user_id'],
'user_actkey' => $user_actkey,
'user_regdate' => time(), // Notification time
'user_id' => $user->data['user_id'],
'user_actkey' => $user_actkey,
'user_actkey_expiration' => $user::get_token_expiration(),
'user_regdate' => time(), // Notification time
));
}

View File

@@ -381,18 +381,19 @@ class ucp_register
$passwords_manager = $phpbb_container->get('passwords.manager');
$user_row = array(
'username' => $data['username'],
'user_password' => $passwords_manager->hash($data['new_password']),
'user_email' => $data['email'],
'group_id' => (int) $group_id,
'user_timezone' => $data['tz'],
'user_lang' => $data['lang'],
'user_type' => $user_type,
'user_actkey' => $user_actkey,
'user_ip' => $user->ip,
'user_regdate' => time(),
'user_inactive_reason' => $user_inactive_reason,
'user_inactive_time' => $user_inactive_time,
'username' => $data['username'],
'user_password' => $passwords_manager->hash($data['new_password']),
'user_email' => $data['email'],
'group_id' => (int) $group_id,
'user_timezone' => $data['tz'],
'user_lang' => $data['lang'],
'user_type' => $user_type,
'user_actkey' => $user_actkey,
'user_actkey_expiration' => $user::get_token_expiration(),
'user_ip' => $user->ip,
'user_regdate' => time(),
'user_inactive_reason' => $user_inactive_reason,
'user_inactive_time' => $user_inactive_time,
);
if ($config['new_member_post_limit'])

View File

@@ -45,7 +45,7 @@ class ucp_resend
trigger_error('FORM_INVALID');
}
$sql = 'SELECT user_id, group_id, username, user_email, user_type, user_lang, user_actkey, user_inactive_reason
$sql = 'SELECT user_id, group_id, username, user_email, user_type, user_lang, user_actkey, user_actkey_expiration, user_inactive_reason
FROM ' . USERS_TABLE . "
WHERE user_email = '" . $db->sql_escape($email) . "'
AND username_clean = '" . $db->sql_escape(utf8_clean_string($username)) . "'";
@@ -73,6 +73,12 @@ class ucp_resend
trigger_error('ACCOUNT_DEACTIVATED');
}
// Do not resend activation email if valid one still exists
if (!empty($user_row['user_actkey']) && (int) $user_row['user_actkey_expiration'] >= time())
{
trigger_error('ACTIVATION_ALREADY_SENT');
}
// Determine coppa status on group (REGISTERED(_COPPA))
$sql = 'SELECT group_name, group_type
FROM ' . GROUPS_TABLE . '
@@ -144,6 +150,8 @@ class ucp_resend
$db->sql_freeresult($result);
}
$this->update_activation_expiration();
meta_refresh(3, append_sid("{$phpbb_root_path}index.$phpEx"));
$message = ($config['require_activation'] == USER_ACTIVATION_ADMIN) ? $user->lang['ACTIVATION_EMAIL_SENT_ADMIN'] : $user->lang['ACTIVATION_EMAIL_SENT'];
@@ -160,4 +168,23 @@ class ucp_resend
$this->tpl_name = 'ucp_resend';
$this->page_title = 'UCP_RESEND';
}
/**
* Update activation expiration to 1 day from now
*
* @return void
*/
protected function update_activation_expiration(): void
{
global $db, $user;
$sql_ary = [
'user_actkey_expiration' => $user::get_token_expiration(),
];
$sql = 'UPDATE ' . USERS_TABLE . '
SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
WHERE user_id = ' . (int) $user->id();
$db->sql_query($sql);
}
}

View File

@@ -72,6 +72,22 @@ function utf8_strpos($str, $needle, $offset = null)
}
}
/**
* UTF-8 aware alternative to stripos
* @ignore
*/
function utf8_stripos($str, $needle, $offset = null)
{
if (is_null($offset))
{
return mb_stripos($str, $needle);
}
else
{
return mb_stripos($str, $needle, $offset);
}
}
/**
* UTF-8 aware alternative to strtolower
* @ignore

View File

@@ -38,7 +38,7 @@ $dbms = $phpbb_config_php_file->convert_30_dbms_to_31($dbms);
$convertor_data = array(
'forum_name' => 'phpBB 2.0.x',
'version' => '1.0.3',
'phpbb_version' => '3.3.12',
'phpbb_version' => '3.3.13',
'author' => '<a href="https://www.phpbb.com/">phpBB Limited</a>',
'dbms' => $dbms,
'dbhost' => $dbhost,
@@ -901,6 +901,7 @@ if (!$get_info)
array('user_email', 'users.user_email', 'strtolower'),
array('user_birthday', ((defined('MOD_BIRTHDAY')) ? 'users.user_birthday' : ''), 'phpbb_get_birthday'),
array('user_lastvisit', 'users.user_lastvisit', 'intval'),
array('user_last_active', 'users.user_lastvisit', 'intval'),
array('user_lastmark', 'users.user_lastvisit', 'intval'),
array('user_lang', $config['default_lang'], ''),
array('', 'users.user_lang', ''),

View File

@@ -23,7 +23,7 @@ if (php_sapi_name() !== 'cli')
define('IN_PHPBB', true);
define('IN_INSTALL', true);
define('PHPBB_ENVIRONMENT', 'production');
define('PHPBB_VERSION', '3.3.12-RC1');
define('PHPBB_VERSION', '3.3.13-RC1');
$phpbb_root_path = __DIR__ . '/../';
$phpEx = substr(strrchr(__FILE__, '.'), 1);

View File

@@ -316,7 +316,7 @@ INSERT INTO phpbb_config (config_name, config_value) VALUES ('update_hashes_lock
INSERT INTO phpbb_config (config_name, config_value) VALUES ('upload_icons_path', 'images/upload_icons');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('upload_path', 'files');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('use_system_cron', '0');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('version', '3.3.12-RC1');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('version', '3.3.13-RC1');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('warnings_expire_days', '90');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('warnings_gc', '14400');
@@ -527,10 +527,10 @@ INSERT INTO phpbb_forums (forum_name, forum_desc, left_id, right_id, parent_id,
INSERT INTO phpbb_forums (forum_name, forum_desc, left_id, right_id, parent_id, forum_type, forum_posts_approved, forum_posts_unapproved, forum_posts_softdeleted, forum_topics_approved, forum_topics_unapproved, forum_topics_softdeleted, forum_last_post_id, forum_last_poster_id, forum_last_poster_name, forum_last_poster_colour, forum_last_post_subject, forum_last_post_time, forum_link, forum_password, forum_image, forum_rules, forum_rules_link, forum_rules_uid, forum_desc_uid, prune_freq, prune_days, prune_viewed, forum_parents, forum_flags) VALUES ('{L_FORUMS_TEST_FORUM_TITLE}', '{L_FORUMS_TEST_FORUM_DESC}', 2, 3, 1, 1, 1, 0, 0, 1, 0, 0, 1, 2, 'Admin', 'AA0000', '{L_TOPICS_TOPIC_TITLE}', 972086460, '', '', '', '', '', '', '', 1, 7, 7, '', 48);
# -- Users / Anonymous user
INSERT INTO phpbb_users (user_type, group_id, username, username_clean, user_regdate, user_password, user_email, user_lang, user_style, user_rank, user_colour, user_posts, user_permissions, user_ip, user_birthday, user_lastpage, user_last_confirm_key, user_post_sortby_type, user_post_sortby_dir, user_topic_sortby_type, user_topic_sortby_dir, user_avatar, user_sig, user_sig_bbcode_uid, user_jabber, user_actkey, user_newpasswd, user_allow_massemail) VALUES (2, 1, 'Anonymous', 'anonymous', 0, '', '', 'en', 1, 0, '', 0, '', '', '', '', '', 't', 'a', 't', 'd', '', '', '', '', '', '', 0);
INSERT INTO phpbb_users (user_type, group_id, username, username_clean, user_regdate, user_password, user_email, user_lang, user_style, user_rank, user_colour, user_posts, user_permissions, user_ip, user_birthday, user_lastpage, user_last_confirm_key, user_post_sortby_type, user_post_sortby_dir, user_topic_sortby_type, user_topic_sortby_dir, user_avatar, user_sig, user_sig_bbcode_uid, user_jabber, user_actkey, user_actkey_expiration, user_newpasswd, user_allow_massemail) VALUES (2, 1, 'Anonymous', 'anonymous', 0, '', '', 'en', 1, 0, '', 0, '', '', '', '', '', 't', 'a', 't', 'd', '', '', '', '', '', 0, '', 0);
# -- username: Admin password: admin (change this or remove it once everything is working!)
INSERT INTO phpbb_users (user_type, group_id, username, username_clean, user_regdate, user_password, user_email, user_lang, user_style, user_rank, user_colour, user_posts, user_permissions, user_ip, user_birthday, user_lastpage, user_last_confirm_key, user_post_sortby_type, user_post_sortby_dir, user_topic_sortby_type, user_topic_sortby_dir, user_avatar, user_sig, user_sig_bbcode_uid, user_jabber, user_actkey, user_newpasswd) VALUES (3, 5, 'Admin', 'admin', 0, '21232f297a57a5a743894a0e4a801fc3', 'admin@yourdomain.com', 'en', 1, 1, 'AA0000', 1, '', '', '', '', '', 't', 'a', 't', 'd', '', '', '', '', '', '');
INSERT INTO phpbb_users (user_type, group_id, username, username_clean, user_regdate, user_password, user_email, user_lang, user_style, user_rank, user_colour, user_posts, user_permissions, user_ip, user_birthday, user_lastpage, user_last_confirm_key, user_post_sortby_type, user_post_sortby_dir, user_topic_sortby_type, user_topic_sortby_dir, user_avatar, user_sig, user_sig_bbcode_uid, user_jabber, user_actkey, user_actkey_expiration, user_newpasswd) VALUES (3, 5, 'Admin', 'admin', 0, '21232f297a57a5a743894a0e4a801fc3', 'admin@yourdomain.com', 'en', 1, 1, 'AA0000', 1, '', '', '', '', '', 't', 'a', 't', 'd', '', '', '', '', '', 0, '');
# -- Groups
INSERT INTO phpbb_groups (group_name, group_type, group_founder_manage, group_colour, group_legend, group_avatar, group_desc, group_desc_uid, group_max_recipients) VALUES ('GUESTS', 3, 0, '', 0, '', '', '', 5);

View File

@@ -51,7 +51,15 @@ function installer_msg_handler($errno, $msg_text, $errfile, $errline)
{
global $phpbb_installer_container, $msg_long_text;
if (error_reporting() == 0)
// Acording to https://www.php.net/manual/en/language.operators.errorcontrol.php
// error_reporting() return a different error code inside the error handler after php 8.0
$suppresed = E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR | E_PARSE;
if (PHP_VERSION_ID < 80000)
{
$suppresed = 0;
}
if (error_reporting() == $suppresed)
{
return true;
}

View File

@@ -88,6 +88,20 @@ $lang = array_merge($lang, array(
'LOCAL_URL' => 'A local URL. The URL must be relative to the topic page and cannot contain a server name or protocol, as links are prefixed with “%s”',
'RELATIVE_URL' => 'A relative URL. You can use this to match parts of a URL, but be careful: a full URL is a valid relative URL. When you want to use relative URLs of your board, use the LOCAL_URL token.',
'COLOR' => 'A HTML colour, can be either in the numeric form <samp>#FF1234</samp> or a <a href="http://www.w3.org/TR/CSS21/syndata.html#value-def-color">CSS colour keyword</a> such as <samp>fuchsia</samp> or <samp>InactiveBorder</samp>',
'ALNUM' => 'Characters from the latin alphabet (A-Z) and numbers.',
'CHOICE' => 'A choice of specified values, e.g. <samp>{CHOICE=spades,hearts,diamonds,clubs}</samp>. The values are treated as case-insensitive by default and can be treated case-sensitive by specifying the <samp>caseSensitive</samp> option: <samp>{CHOICE=Spades,Hearts,Diamonds,Clubs;caseSensitive}</samp>',
'FLOAT' => 'A decimal value, e.g. <samp>0.5</samp>.',
'HASHMAP' => 'Maps strings to their replacement in the form <samp>{HASHMAP=string1:replacement1,string2:replacement2}</samp>. Case-sensitive. Preserves unknown values by default.',
'INT' => 'An integer value, e.g. <samp>2</samp>.',
'IP' => 'A valid IPv4 or IPv6 address.',
'IPPORT' => 'A valid IPv4 or IPv6 address with port number.',
'IPV4' => 'A valid IPv4 address.',
'IPV6' => 'A valid IPv6 address.',
'MAP' => 'Maps strings to their replacement in the form <samp>{MAP=string1:replacement1,string2:replacement2}</samp>. Case-insensitive. Preserves unknown values by default.',
'RANGE' => 'Accepts an integer in the given range, e.g. <samp>{RANGE=-10,42}</samp>.',
'REGEXP' => 'Validates its value against a given regexp, e.g. <samp>{REGEXP=/^foo\w+bar$/}</samp>.',
'TIMESTAMP' => 'A timestamp such as <samp>1h30m10s</samp> which will be converted to a number of seconds. Also accepts a number.',
'UINT' => 'An unsigned integer value. Same as <samp>{INT}</samp>, but rejects values less than 0.',
),
));

View File

@@ -108,6 +108,8 @@ $lang = array_merge($lang, array(
'CLI_DESCRIPTION_USER_ADD_OPTION_NOTIFY' => 'Send account activation email to the new user (not sent by default)',
'CLI_DESCRIPTION_USER_DELETE' => 'Delete a user account.',
'CLI_DESCRIPTION_USER_DELETE_USERNAME' => 'Username of the user to delete',
'CLI_DESCRIPTION_USER_DELETE_ID' => 'Delete user accounts by ID.',
'CLI_DESCRIPTION_USER_DELETE_ID_OPTION_ID' => 'User IDs of the users to delete',
'CLI_DESCRIPTION_USER_DELETE_OPTION_POSTS' => 'Delete all posts by the user. Without this option, the users posts will be retained.',
'CLI_DESCRIPTION_USER_RECLEAN' => 'Re-clean usernames.',
@@ -155,10 +157,14 @@ $lang = array_merge($lang, array(
'CLI_THUMBNAIL_NOTHING_TO_GENERATE' => 'No thumbnails to generate.',
'CLI_THUMBNAIL_NOTHING_TO_DELETE' => 'No thumbnails to delete.',
'CLI_USER_ADD_SUCCESS' => 'Successfully added user %s.',
'CLI_USER_DELETE_CONFIRM' => 'Are you sure you want to delete %s? [y/N]',
'CLI_USER_RECLEAN_START' => 'Re-cleaning usernames',
'CLI_USER_RECLEAN_DONE' => [
'CLI_USER_ADD_SUCCESS' => 'Successfully added user %s.',
'CLI_USER_DELETE_CONFIRM' => 'Are you sure you want to delete %s? [y/N]',
'CLI_USER_DELETE_ID_CONFIRM' => 'Are you sure you want to delete the user IDs %s? [y/N]',
'CLI_USER_DELETE_ID_SUCCESS' => 'Successfully deleted user IDs.',
'CLI_USER_DELETE_ID_START' => 'Deleting users by ID',
'CLI_USER_DELETE_NONE' => 'No users were deleted by user ID.',
'CLI_USER_RECLEAN_START' => 'Re-cleaning usernames',
'CLI_USER_RECLEAN_DONE' => [
0 => 'Re-cleaning complete. No usernames needed to be cleaned.',
1 => 'Re-cleaning complete. %d username was cleaned.',
2 => 'Re-cleaning complete. %d usernames were cleaned.',

View File

@@ -64,6 +64,7 @@ $lang = array_merge($lang, array(
'ACCOUNT_DEACTIVATED' => 'Your account has been manually deactivated and is only able to be reactivated by an administrator.',
'ACP' => 'Administration Control Panel',
'ACP_SHORT' => 'ACP',
'ACTIVATION_ALREADY_SENT' => 'The activation email has already been sent to your email address. You can try again after 24 hours. If you continue to have problems activating your account, please contact a board administrator.',
'ACTIVE' => 'active',
'ACTIVE_ERROR' => 'The specified username is currently inactive. If you have problems activating your account, please contact a board administrator.',
'ADMINISTRATOR' => 'Administrator',

View File

@@ -941,10 +941,19 @@ switch ($mode)
}
else if ($topic_id)
{
$sql = 'SELECT f.parent_id, f.forum_parents, f.left_id, f.right_id, f.forum_type, f.forum_name, f.forum_id, f.forum_desc, f.forum_desc_uid, f.forum_desc_bitfield, f.forum_desc_options, f.forum_options, t.topic_title
FROM ' . FORUMS_TABLE . ' as f,
' . TOPICS_TABLE . ' as t
WHERE t.forum_id = f.forum_id';
// Generate the navlinks based on the selected topic
$navlinks_sql_array = [
'SELECT' => 'f.parent_id, f.forum_parents, f.left_id, f.right_id, f.forum_type, f.forum_name,
f.forum_id, f.forum_desc, f.forum_desc_uid, f.forum_desc_bitfield, f.forum_desc_options,
f.forum_options, t.topic_title',
'FROM' => [
FORUMS_TABLE => 'f',
TOPICS_TABLE => 't',
],
'WHERE' => 't.forum_id = f.forum_id AND t.topic_id = ' . (int) $topic_id,
];
$sql = $db->sql_build_query('SELECT', $navlinks_sql_array);
$result = $db->sql_query($sql);
$topic_data = $db->sql_fetchrow($result);
$db->sql_freeresult($result);
@@ -1036,7 +1045,7 @@ switch ($mode)
if ($auth->acl_get('u_viewonline'))
{
$sort_key_text['l'] = $user->lang['SORT_LAST_ACTIVE'];
$sort_key_sql['l'] = 'u.user_lastvisit';
$sort_key_sql['l'] = 'u.user_last_active';
}
$sort_key_text['m'] = $user->lang['SORT_RANK'];
@@ -1138,15 +1147,15 @@ switch ($mode)
{
if ($active_select === 'lt' && (int) $active[0] == 0 && (int) $active[1] == 0 && (int) $active[2] == 0)
{
$sql_where .= ' AND u.user_lastvisit = 0';
$sql_where .= ' AND u.user_last_active = 0';
}
else if ($active_select === 'gt')
{
$sql_where .= ' AND u.user_lastvisit ' . $find_key_match[$active_select] . ' ' . $active_time;
$sql_where .= ' AND u.user_last_active ' . $find_key_match[$active_select] . ' ' . $active_time;
}
else
{
$sql_where .= ' AND (u.user_lastvisit > 0 AND u.user_lastvisit < ' . $active_time . ')';
$sql_where .= ' AND (u.user_last_active > 0 AND u.user_last_active < ' . $active_time . ')';
}
}
}
@@ -1713,7 +1722,7 @@ switch ($mode)
{
$row['session_time'] = $session_ary[$row['user_id']]['session_time'] ?? 0;
$row['session_viewonline'] = $session_ary[$row['user_id']]['session_viewonline'] ?? 0;
$row['last_visit'] = (!empty($row['session_time'])) ? $row['session_time'] : $row['user_lastvisit'];
$row['last_visit'] = $row['user_last_active'] ?: $row['session_time'];
$id_cache[$row['user_id']] = $row;
}

25
phpBB/phpbb/.htaccess Normal file
View File

@@ -0,0 +1,25 @@
# With Apache 2.4 the "Order, Deny" syntax has been deprecated and moved from
# module mod_authz_host to a new module called mod_access_compat (which may be
# disabled) and a new "Require" syntax has been introduced to mod_authz_core.
# We could just conditionally provide both versions, but unfortunately Apache
# does not explicitly tell us its version if the module mod_version is not
# available. In this case, we check for the availability of module
# mod_authz_core (which should be on 2.4 or higher only) as a best guess.
<IfModule mod_version.c>
<IfVersion < 2.4>
Order Allow,Deny
Deny from All
</IfVersion>
<IfVersion >= 2.4>
Require all denied
</IfVersion>
</IfModule>
<IfModule !mod_version.c>
<IfModule !mod_authz_core.c>
Order Allow,Deny
Deny from All
</IfModule>
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
</IfModule>

View File

@@ -21,7 +21,7 @@ class gravatar extends \phpbb\avatar\driver\driver
/**
* The URL for the gravatar service
*/
const GRAVATAR_URL = '//secure.gravatar.com/avatar/';
const GRAVATAR_URL = '//gravatar.com/avatar/';
/**
* {@inheritdoc}
@@ -29,7 +29,7 @@ class gravatar extends \phpbb\avatar\driver\driver
public function get_data($row)
{
return array(
'src' => $row['avatar'],
'src' => $this->get_gravatar_url($row),
'width' => $row['avatar_width'],
'height' => $row['avatar_height'],
);
@@ -53,7 +53,7 @@ class gravatar extends \phpbb\avatar\driver\driver
{
$template->assign_vars(array(
'AVATAR_GRAVATAR_WIDTH' => (($row['avatar_type'] == $this->get_name() || $row['avatar_type'] == 'gravatar') && $row['avatar_width']) ? $row['avatar_width'] : $request->variable('avatar_gravatar_width', ''),
'AVATAR_GRAVATAR_HEIGHT' => (($row['avatar_type'] == $this->get_name() || $row['avatar_type'] == 'gravatar') && $row['avatar_height']) ? $row['avatar_height'] : $request->variable('avatar_gravatar_width', ''),
'AVATAR_GRAVATAR_HEIGHT' => (($row['avatar_type'] == $this->get_name() || $row['avatar_type'] == 'gravatar') && $row['avatar_height']) ? $row['avatar_height'] : $request->variable('avatar_gravatar_height', ''),
'AVATAR_GRAVATAR_EMAIL' => (($row['avatar_type'] == $this->get_name() || $row['avatar_type'] == 'gravatar') && $row['avatar']) ? $row['avatar'] : '',
));
@@ -175,7 +175,7 @@ class gravatar extends \phpbb\avatar\driver\driver
global $phpbb_dispatcher;
$url = self::GRAVATAR_URL;
$url .= md5(strtolower(trim($row['avatar'])));
$url .= hash('sha256', strtolower(trim($row['avatar'])));
if ($row['avatar_width'] || $row['avatar_height'])
{

View File

@@ -330,6 +330,27 @@ class file extends \phpbb\cache\driver\base
return $query_result;
}
/**
* Cleanup when loading invalid data global file
*
* @param string $file Filename
* @param resource $handle
*
* @return void
*/
private function cleanup_invalid_data_global(string $file, $handle): void
{
if (is_resource($handle))
{
fclose($handle);
}
$this->vars = $this->var_expires = [];
$this->is_modified = false;
$this->remove_file($file);
}
/**
* Read cached data from a specified file
*
@@ -372,14 +393,7 @@ class file extends \phpbb\cache\driver\base
if (!is_numeric($bytes) || ($bytes = (int) $bytes) === 0)
{
// We cannot process the file without a valid number of bytes
// so we discard it
fclose($handle);
$this->vars = $this->var_expires = array();
$this->is_modified = false;
$this->remove_file($file);
$this->cleanup_invalid_data_global($file, $handle);
return false;
}
@@ -392,9 +406,17 @@ class file extends \phpbb\cache\driver\base
}
$var_name = substr(fgets($handle), 0, -1);
$data_length = $bytes - strlen($var_name);
if ($data_length <= 0)
{
$this->cleanup_invalid_data_global($file, $handle);
return false;
}
// Read the length of bytes that consists of data.
$data = fread($handle, $bytes - strlen($var_name));
$data = fread($handle, $data_length);
$data = @unserialize($data);
// Don't use the data if it was invalid

View File

@@ -129,6 +129,10 @@ class redis extends \phpbb\cache\driver\memory
/**
* Store data in the cache
*
* For the info, see https://phpredis.github.io/phpredis/Redis.html#method_set,
* https://redis.io/docs/latest/commands/set/
* and https://redis.io/docs/latest/commands/expire/#appendix-redis-expires
*
* @access protected
* @param string $var Cache key
* @param mixed $data Data to store
@@ -137,11 +141,7 @@ class redis extends \phpbb\cache\driver\memory
*/
function _write($var, $data, $ttl = 2592000)
{
if ($ttl == 0)
{
return $this->redis->set($var, $data);
}
return $this->redis->setex($var, $ttl, $data);
return $this->redis->set($var, $data, ['EXAT' => time() + $ttl]);
}
/**

View File

@@ -359,6 +359,7 @@ class recaptcha_v3 extends captcha_abstract
if ($result->isSuccess())
{
$this->solved = true;
$this->confirm_code = $this->code;
return false;
}

View File

@@ -100,7 +100,15 @@ class update_hashes extends \phpbb\console\command\command
while ($row = $this->db->sql_fetchrow($result))
{
$old_hash = preg_replace('/^\$CP\$/', '', $row['user_password']);
$new_hash = $this->passwords_manager->hash($old_hash, array($this->default_type));
// If stored hash type is unknown then it's md5 hash with no prefix
// First rehash it using $H$ as hash type identifier (salted_md5)
if (!$this->passwords_manager->detect_algorithm($old_hash))
{
$old_hash = $this->passwords_manager->hash($old_hash, '$H$');
}
$new_hash = $this->passwords_manager->hash($old_hash, [$this->default_type]);
$sql = 'UPDATE ' . USERS_TABLE . "
SET user_password = '" . $this->db->sql_escape($new_hash) . "'

View File

@@ -290,18 +290,17 @@ class add extends command
{
case USER_ACTIVATION_SELF:
$email_template = 'user_welcome_inactive';
$user_actkey = gen_rand_string(mt_rand(6, 10));
break;
case USER_ACTIVATION_ADMIN:
$email_template = 'admin_welcome_inactive';
$user_actkey = gen_rand_string(mt_rand(6, 10));
break;
default:
$email_template = 'user_welcome';
$user_actkey = '';
break;
}
$user_actkey = $this->get_activation_key($user_id);
if (!class_exists('messenger'))
{
require($this->phpbb_root_path . 'includes/functions_messenger.' . $this->php_ext);
@@ -321,6 +320,35 @@ class add extends command
$messenger->send(NOTIFY_EMAIL);
}
/**
* Get user activation key
*
* @param int $user_id User ID
*
* @return string User activation key for user
*/
protected function get_activation_key(int $user_id): string
{
$user_actkey = '';
if ($this->config['require_activation'] == USER_ACTIVATION_SELF || $this->config['require_activation'] == USER_ACTIVATION_ADMIN)
{
$user_actkey = gen_rand_string(mt_rand(6, 10));
$sql_ary = [
'user_actkey' => $user_actkey,
'user_actkey_expiration' => user::get_token_expiration(),
];
$sql = 'UPDATE ' . USERS_TABLE . '
SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . '
WHERE user_id = ' . (int) $user_id;
$this->db->sql_query($sql);
}
return $user_actkey;
}
/**
* Helper to translate questions to the user
*

View File

@@ -0,0 +1,227 @@
<?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\console\command\user;
use phpbb\console\command\command;
use phpbb\db\driver\driver_interface;
use phpbb\language\language;
use phpbb\log\log_interface;
use phpbb\user;
use phpbb\user_loader;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Style\SymfonyStyle;
class delete_id extends command
{
/** @var driver_interface */
protected $db;
/** @var language */
protected $language;
/** @var log_interface */
protected $log;
/** @var user_loader */
protected $user_loader;
/** @var string Bots table */
protected $bots_table;
/** @var string User group table */
protected $user_group_table;
/** @var string Users table */
protected $users_table;
/** @var string phpBB root path */
protected $phpbb_root_path;
/** @var string PHP extension */
protected $php_ext;
/**
* Construct method
*
* @param driver_interface $db
* @param language $language
* @param log_interface $log
* @param user $user
* @param user_loader $user_loader
* @param string $bots_table
* @param string $user_group_table
* @param string $users_table
* @param string $phpbb_root_path
* @param string $php_ext
*/
public function __construct(driver_interface $db, language $language, log_interface $log, user $user, user_loader $user_loader,
string $bots_table, string $user_group_table, string $users_table, string $phpbb_root_path, string $php_ext)
{
$this->db = $db;
$this->language = $language;
$this->log = $log;
$this->user_loader = $user_loader;
$this->bots_table = $bots_table;
$this->user_group_table = $user_group_table;
$this->users_table = $users_table;
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
$this->language->add_lang('acp/users');
parent::__construct($user);
}
/**
* Sets the command name and description
*
* @return void
*/
protected function configure(): void
{
$this
->setName('user:delete_id')
->setDescription($this->language->lang('CLI_DESCRIPTION_USER_DELETE_ID'))
->addArgument(
'user_ids',
InputArgument::REQUIRED | InputArgument::IS_ARRAY,
$this->language->lang('CLI_DESCRIPTION_USER_DELETE_ID_OPTION_ID')
)
->addOption(
'delete-posts',
null,
InputOption::VALUE_NONE,
$this->language->lang('CLI_DESCRIPTION_USER_DELETE_OPTION_POSTS')
)
;
}
/**
* Executes the command user:delete_ids
*
* Deletes a list of user ids from the database. An option to delete the users' posts
* is available, by default posts will be retained.
*
* @param InputInterface $input The input stream used to get the options
* @param OutputInterface $output The output stream, used to print messages
*
* @return int 0 if all is well, 1 if any errors occurred
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$user_ids = $input->getArgument('user_ids');
$mode = ($input->getOption('delete-posts')) ? 'remove' : 'retain';
$deleted_users = 0;
$io = new SymfonyStyle($input, $output);
if (count($user_ids) > 0)
{
$this->user_loader->load_users($user_ids);
$progress = $this->create_progress_bar(count($user_ids), $io, $output);
$progress->setMessage($this->language->lang('CLI_USER_DELETE_ID_START'));
$progress->start();
foreach ($user_ids as $user_id)
{
$user_row = $this->user_loader->get_user($user_id);
// Skip anonymous user
if ($user_row['user_id'] == ANONYMOUS)
{
$progress->advance();
continue;
}
else if ($user_row['user_type'] == USER_IGNORE)
{
$this->delete_bot_user($user_row);
}
else
{
if (!function_exists('user_delete'))
{
require($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);
}
user_delete($mode, $user_row['user_id'], $user_row['username']);
$this->log->add('admin', ANONYMOUS, '', 'LOG_USER_DELETED', false, array($user_row['username']));
}
$progress->advance();
$deleted_users++;
}
$progress->finish();
if ($deleted_users > 0)
{
$io->success($this->language->lang('CLI_USER_DELETE_ID_SUCCESS'));
}
}
if (!$deleted_users)
{
$io->note($this->language->lang('CLI_USER_DELETE_NONE'));
}
return 0;
}
/**
* Interacts with the user.
* Confirm they really want to delete the account...last chance!
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*/
protected function interact(InputInterface $input, OutputInterface $output): void
{
$helper = $this->getHelper('question');
$user_ids = $input->getArgument('user_ids');
if (count($user_ids) > 0)
{
$question = new ConfirmationQuestion(
$this->language->lang('CLI_USER_DELETE_ID_CONFIRM', implode(',', $user_ids)),
false
);
if (!$helper->ask($input, $output, $question))
{
$input->setArgument('user_ids', []);
}
}
}
/**
* Deletes a bot user
*
* @param array $user_row
* @return void
*/
protected function delete_bot_user(array $user_row): void
{
$delete_tables = [$this->bots_table, $this->user_group_table, $this->users_table];
foreach ($delete_tables as $table)
{
$sql = "DELETE FROM $table
WHERE user_id = " . (int) $user_row['user_id'];
$this->db->sql_query($sql);
}
}
}

View File

@@ -85,9 +85,8 @@ class cron_runner_listener implements EventSubscriberInterface
{
$task->run();
}
$this->cron_lock->release();
}
$this->cron_lock->release();
}
}

View File

@@ -107,7 +107,15 @@ class update_hashes extends \phpbb\cron\task\base
while ($row = $this->db->sql_fetchrow($result))
{
$old_hash = preg_replace('/^\$CP\$/', '', $row['user_password']);
$new_hash = $this->passwords_manager->hash($old_hash, array($this->default_type));
// If stored hash type is unknown then it's md5 hash with no prefix
// First rehash it using $H$ as hash type identifier (salted_md5)
if (!$this->passwords_manager->detect_algorithm($old_hash))
{
$old_hash = $this->passwords_manager->hash($old_hash, '$H$');
}
$new_hash = $this->passwords_manager->hash($old_hash, [$this->default_type]);
// Increase number so we know that users were selected from the database
$affected_rows++;

View File

@@ -155,7 +155,9 @@ class mysqli extends \phpbb\db\driver\mysql_base
switch ($status)
{
case 'begin':
return @mysqli_autocommit($this->db_connect_id, false);
@mysqli_autocommit($this->db_connect_id, false);
$result = @mysqli_begin_transaction($this->db_connect_id);
return $result;
break;
case 'commit':

View File

@@ -0,0 +1,48 @@
<?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\data\v33x;
use phpbb\db\migration\migration;
class add_resend_activation_expiration extends migration
{
public static function depends_on(): array
{
return [
'\phpbb\db\migration\data\v33x\v3311',
];
}
public function update_schema(): array
{
return [
'add_columns' => [
$this->table_prefix . 'users' => [
'user_actkey_expiration' => ['TIMESTAMP', 0, 'after' => 'user_actkey'],
],
],
];
}
public function revert_schema(): array
{
return [
'drop_columns' => [
$this->table_prefix . 'users' => [
'user_actkey_expiration',
],
],
];
}
}

View File

@@ -0,0 +1,79 @@
<?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\data\v33x;
use phpbb\db\migration\migration;
class add_user_last_active extends migration
{
public static function depends_on()
{
return [
'\phpbb\db\migration\data\v33x\v3311',
];
}
public function update_schema()
{
return [
'add_columns' => [
$this->table_prefix . 'users' => [
'user_last_active' => ['TIMESTAMP', 0, 'after' => 'user_lastvisit'],
],
],
];
}
public function revert_schema()
{
return [
'drop_columns' => [
$this->table_prefix . 'users' => ['user_last_active'],
],
];
}
public function update_data()
{
return [
['custom', [[$this, 'set_user_last_active']]],
];
}
public function set_user_last_active($start = 0)
{
// Get maximum user id from database
$sql = "SELECT MAX(user_id) AS max_user_id
FROM {$this->table_prefix}users";
$result = $this->db->sql_query($sql);
$max_id = (int) $this->db->sql_fetchfield('max_user_id');
$this->db->sql_freeresult($result);
if ($start > $max_id)
{
return;
}
// Keep setting user_last_active time
$next_start = $start + 10000;
$sql = 'UPDATE ' . $this->table_prefix . 'users
SET user_last_active = user_lastvisit
WHERE user_id > ' . (int) $start . '
AND user_id <= ' . (int) ($next_start);
$this->db->sql_query($sql);
return $next_start;
}
}

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 phpbb\db\migration\data\v33x;
class v3312 extends \phpbb\db\migration\migration
{
public function effectively_installed()
{
return version_compare($this->config['version'], '3.3.12', '>=');
}
public static function depends_on()
{
return [
'\phpbb\db\migration\data\v33x\add_resend_activation_expiration',
'\phpbb\db\migration\data\v33x\add_user_last_active',
'\phpbb\db\migration\data\v33x\v3312rc1',
];
}
public function update_data()
{
return [
['config.update', ['version', '3.3.12']],
];
}
}

View File

@@ -0,0 +1,36 @@
<?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\data\v33x;
class v3313rc1 extends \phpbb\db\migration\migration
{
public function effectively_installed()
{
return version_compare($this->config['version'], '3.3.13-RC1', '>=');
}
public static function depends_on()
{
return [
'\phpbb\db\migration\data\v33x\v3312',
];
}
public function update_data()
{
return [
['config.update', ['version', '3.3.13-RC1']],
];
}
}

View File

@@ -435,15 +435,14 @@ class permission implements \phpbb\db\migration\tool\tool_interface
}
$this->db->sql_freeresult($result);
if (empty($new_auth))
$type = (string) $type; // Prevent PHP bug.
if (empty($new_auth) || !in_array($type, ['role','group']))
{
return;
}
$current_auth = array();
$type = (string) $type; // Prevent PHP bug.
switch ($type)
{
case 'role':
@@ -525,40 +524,32 @@ class permission implements \phpbb\db\migration\tool\tool_interface
break;
}
$sql_ary = array();
switch ($type)
$sql_ary = $auth_update_list = [];
$table = $type == 'role' ? ACL_ROLES_DATA_TABLE : ACL_GROUPS_TABLE;
foreach ($new_auth as $auth_option_id)
{
case 'role':
foreach ($new_auth as $auth_option_id)
{
if (!isset($current_auth[$auth_option_id]))
{
$sql_ary[] = array(
'role_id' => $role_id,
'auth_option_id' => $auth_option_id,
'auth_setting' => $has_permission,
);
}
}
if (!isset($current_auth[$auth_option_id]))
{
$sql_ary[] = [
$type . '_id' => ${$type . '_id'},
'auth_option_id' => $auth_option_id,
'auth_setting' => (int) $has_permission,
];
}
else
{
$auth_update_list[] = $auth_option_id;
}
}
$this->db->sql_multi_insert($table, $sql_ary);
$this->db->sql_multi_insert(ACL_ROLES_DATA_TABLE, $sql_ary);
break;
case 'group':
foreach ($new_auth as $auth_option_id)
{
if (!isset($current_auth[$auth_option_id]))
{
$sql_ary[] = array(
'group_id' => $group_id,
'auth_option_id' => $auth_option_id,
'auth_setting' => $has_permission,
);
}
}
$this->db->sql_multi_insert(ACL_GROUPS_TABLE, $sql_ary);
break;
if (count($auth_update_list))
{
$sql = 'UPDATE ' . $table . '
SET auth_setting = ' . (int) $has_permission . '
WHERE ' . $this->db->sql_in_set('auth_option_id', $auth_update_list) . '
AND ' . $type . '_id = ' . (int) ${$type . '_id'};
$this->db->sql_query($sql);
}
$this->auth->acl_clear_prefetch();

View File

@@ -31,6 +31,7 @@ class manager
protected $php_ext;
protected $extensions;
protected $extension_table;
protected $filesystem;
protected $phpbb_root_path;
protected $cache_name;

View File

@@ -144,7 +144,7 @@ class install_extensions extends \phpbb\install\task_base
if (isset($extensions[$ext_name]) && $extensions[$ext_name]['ext_active'])
{
// Create log
$this->log->add('admin', ANONYMOUS, '', 'LOG_EXT_ENABLE', time(), array($ext_name));
$this->log->add('admin', ANONYMOUS, $this->user->ip, 'LOG_EXT_ENABLE', time(), array($ext_name));
$this->iohandler->add_success_message(array('CLI_EXTENSION_ENABLE_SUCCESS', $ext_name));
}
else

View File

@@ -174,7 +174,7 @@ class update_extensions extends task_base
if (isset($extensions[$ext_name]) && $extensions[$ext_name]['ext_active'])
{
// Create log
$this->log->add('admin', ANONYMOUS, '', 'LOG_EXT_UPDATE', time(), array($ext_name));
$this->log->add('admin', ANONYMOUS, $this->user->ip, 'LOG_EXT_UPDATE', time(), array($ext_name));
$this->iohandler->add_success_message(array('CLI_EXTENSION_UPDATE_SUCCESS', $ext_name));
}
else

View File

@@ -299,7 +299,11 @@ class fulltext_native extends \phpbb\search\base
);
$keywords = preg_replace($match, $replace, $keywords);
$num_keywords = count(explode(' ', $keywords));
// Ensure a space exists before +, - and | to make the split and count work correctly
$countable_keywords = preg_replace('/(?<!\s)(\+|\-|\|)/', ' $1', $keywords);
$num_keywords = count(explode(' ', $countable_keywords));
// We limit the number of allowed keywords to minimize load on the database
if ($this->config['max_num_search_keywords'] && $num_keywords > $this->config['max_num_search_keywords'])

View File

@@ -440,10 +440,10 @@ class session
// Is user banned? Are they excluded? Won't return on ban, exists within method
$this->check_ban_for_current_session($config);
// Update user last visit time accordingly, but in a minute or so
if ((int) $this->data['session_time'] - (int) $this->data['user_lastvisit'] > 60)
// Update user last active time accordingly, but in a minute or so
if ($this->time_now - (int) $this->data['user_last_active'] > 60)
{
$this->update_user_lastvisit();
$this->update_last_active_time();
}
return true;
@@ -690,8 +690,8 @@ class session
{
$this->session_id = $this->data['session_id'];
// Only sync user last visit time in a minute or so after last session data update or if the page changes
if ((int) $this->data['session_time'] - (int) $this->data['user_lastvisit'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page']))
// Only update session DB a minute or so after last update or if page changes
if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page']))
{
// Update the last visit time
$this->update_user_lastvisit();
@@ -818,22 +818,26 @@ class session
$this->data['user_form_salt'] = unique_id();
// Update the form key
$sql = 'UPDATE ' . USERS_TABLE . '
SET user_form_salt = \'' . $db->sql_escape($this->data['user_form_salt']) . '\'
SET user_form_salt = \'' . $db->sql_escape($this->data['user_form_salt']) . '\',
user_last_active = ' . (int) $this->time_now . '
WHERE user_id = ' . (int) $this->data['user_id'];
$db->sql_query($sql);
}
else
{
$this->update_last_active_time();
}
}
else
{
$this->data['session_time'] = $this->data['session_last_visit'] = $this->time_now;
$this->update_user_lastvisit();
$SID = '?sid=';
$_SID = '';
}
// Update the last visit time
$this->update_user_lastvisit();
$session_data = $sql_ary;
/**
* Event to send new session data to extension
@@ -960,8 +964,8 @@ class session
}
/**
* Get most recent session for each registered user to sync user last visit with it
* Inner SELECT gets most recent sessions for each unique session_user_id
* Get expired sessions for registered users, only most recent for each user
* Inner SELECT gets most recent expired sessions for unique session_user_id
* Outer SELECT gets data for them
*/
$sql_select = 'SELECT s1.session_page, s1.session_user_id, s1.session_time AS recent_time
@@ -969,7 +973,8 @@ class session
INNER JOIN (
SELECT session_user_id, MAX(session_time) AS recent_time
FROM ' . SESSIONS_TABLE . '
WHERE session_user_id <> ' . ANONYMOUS . '
WHERE session_time < ' . ($this->time_now - (int) $config['session_length']) . '
AND session_user_id <> ' . ANONYMOUS . '
GROUP BY session_user_id
) AS s2
ON s1.session_user_id = s2.session_user_id
@@ -1807,7 +1812,26 @@ class session
if (isset($this->data['session_time'], $this->data['user_id']))
{
$sql = 'UPDATE ' . USERS_TABLE . '
SET user_lastvisit = ' . (int) $this->data['session_time'] . '
SET user_lastvisit = ' . (int) $this->data['session_time'] . ',
user_last_active = ' . $this->time_now . '
WHERE user_id = ' . (int) $this->data['user_id'];
$db->sql_query($sql);
}
}
/**
* Update user's last active time
*
* @return void
*/
public function update_last_active_time()
{
global $db;
if (isset($this->time_now, $this->data['user_id']))
{
$sql = 'UPDATE ' . USERS_TABLE . '
SET user_last_active = ' . $this->time_now . '
WHERE user_id = ' . (int) $this->data['user_id'];
$db->sql_query($sql);
}

View File

@@ -0,0 +1,134 @@
<?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\ucp\controller;
use phpbb\config\config;
use phpbb\event\dispatcher_interface;
use phpbb\language\language;
use phpbb\request\request_interface;
use phpbb\user;
class delete_cookies
{
/** @var config */
private $config;
/** @var dispatcher_interface */
private $dispatcher;
/** @var language */
private $language;
/** @var request_interface */
private $request;
/** @var user */
private $user;
/** @var string phpBB root path */
private $phpbb_root_path;
/** @var string PHP extension */
private $php_ext;
/**
* Constructor for delete_cookies controller
*
* @param config $config
* @param dispatcher_interface $dispatcher
* @param language $language
* @param request_interface $request
* @param user $user
*/
public function __construct(config $config, dispatcher_interface $dispatcher, language $language, request_interface $request, user $user, string $phpbb_root_path, string $php_ext)
{
$this->config = $config;
$this->dispatcher = $dispatcher;
$this->language = $language;
$this->request = $request;
$this->user = $user;
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
}
/**
* Handle delete cookies requests
*
* @return void
*/
public function handle()
{
$this->language->add_lang(['ucp']);
// Delete Cookies with dynamic names (do NOT delete poll cookies)
if (confirm_box(true))
{
$set_time = time() - 31536000;
foreach ($this->request->variable_names(request_interface::COOKIE) as $cookie_name)
{
// Only delete board cookies
if (strpos($cookie_name, $this->config['cookie_name'] . '_') !== 0)
{
continue;
}
$cookie_name = str_replace($this->config['cookie_name'] . '_', '', $cookie_name);
/**
* Event to save custom cookies from deletion
*
* @event core.ucp_delete_cookies
* @var string cookie_name Cookie name to checking
* @var bool retain_cookie Do we retain our cookie or not, true if retain
* @since 3.1.3-RC1
* @changed 3.3.13-RC1 Moved to new delete_cookies controller
*/
$retain_cookie = false;
$vars = ['cookie_name', 'retain_cookie'];
extract($this->dispatcher->trigger_event('core.ucp_delete_cookies', compact($vars)));
if ($retain_cookie)
{
continue;
}
// Polls are stored as {cookie_name}_poll_{topic_id}, cookie_name_ got removed, therefore checking for poll_
if (strpos($cookie_name, 'poll_') !== 0)
{
$this->user->set_cookie($cookie_name, '', $set_time);
}
}
$this->user->set_cookie('track', '', $set_time);
$this->user->set_cookie('u', '', $set_time);
$this->user->set_cookie('k', '', $set_time);
$this->user->set_cookie('sid', '', $set_time);
// We destroy the session here, the user will be logged out nevertheless
$this->user->session_kill();
$this->user->session_begin();
meta_refresh(3, append_sid("{$this->phpbb_root_path}index.$this->php_ext"));
$message = $this->language->lang('COOKIES_DELETED') . '<br><br>' . $this->language->lang('RETURN_INDEX', '<a href="' . append_sid("{$this->phpbb_root_path}index.$this->php_ext") . '">', '</a>');
trigger_error($message);
}
else
{
confirm_box(false, 'DELETE_COOKIES', '');
}
redirect(append_sid("{$this->phpbb_root_path}index.$this->php_ext"));
}
}

View File

@@ -242,7 +242,7 @@ class reset_password
$sql_ary = [
'reset_token' => $reset_token,
'reset_token_expiration' => strtotime('+1 day'),
'reset_token_expiration' => $this->user::get_token_expiration(),
];
$sql = 'UPDATE ' . $this->users_table . '

View File

@@ -57,7 +57,7 @@ class user extends \phpbb\session
* @param \phpbb\language\language $lang phpBB's Language loader
* @param string $datetime_class Class name of datetime class
*/
function __construct(\phpbb\language\language $lang, $datetime_class)
public function __construct(\phpbb\language\language $lang, $datetime_class)
{
global $phpbb_root_path;
@@ -78,6 +78,16 @@ class user extends \phpbb\session
return $this->is_setup_flag;
}
/**
* Get expiration time for user tokens, e.g. activation or reset password tokens
*
* @return int Expiration for user tokens
*/
public static function get_token_expiration(): int
{
return strtotime('+1 day') ?: 0;
}
/**
* Magic getter for BC compatibility
*

View File

@@ -848,6 +848,7 @@ if ($save && $user->data['is_registered'] && $auth->acl_get('u_savedrafts') && (
'disable_smilies' => false,
'disable_magic_url' => false,
'attach_sig' => true,
'notify' => false,
'lock_topic' => false,
'topic_type' => POST_NORMAL,
@@ -1966,7 +1967,7 @@ $page_data = array(
'S_BBCODE_CHECKED' => ($bbcode_checked) ? ' checked="checked"' : '',
'S_SMILIES_ALLOWED' => $smilies_status,
'S_SMILIES_CHECKED' => ($smilies_checked) ? ' checked="checked"' : '',
'S_SIG_ALLOWED' => ($auth->acl_get('f_sigs', $forum_id) && $config['allow_sig'] && $user->data['is_registered']) ? true : false,
'S_SIG_ALLOWED' => ($user->data['is_registered'] && $config['allow_sig'] && $auth->acl_get('f_sigs', $forum_id) && $auth->acl_get('u_sig')) ? true : false,
'S_SIGNATURE_CHECKED' => ($sig_checked) ? ' checked="checked"' : '',
'S_NOTIFY_ALLOWED' => (!$user->data['is_registered'] || ($mode == 'edit' && $user->data['user_id'] != $post_data['poster_id']) || !$config['allow_topic_notify'] || !$config['email_enable']) ? false : true,
'S_NOTIFY_CHECKED' => ($notify_checked) ? ' checked="checked"' : '',

View File

@@ -21,8 +21,8 @@
# General Information about this style
name = prosilver
copyright = © phpBB Limited, 2007
style_version = 3.3.12
phpbb_version = 3.3.12
style_version = 3.3.13
phpbb_version = 3.3.13
# Defining a different template bitfield
# template_bitfield = //g=

View File

@@ -337,30 +337,6 @@ $('[data-ajax]').each(function() {
}
});
// Prevent accidental double submission of form
$('[data-prevent-flood] input[type=submit]').click(function(event) {
const $submitButton = $(this); // Store the button element
const $form = $submitButton.closest('form');
// Always add the disabled class for visual feedback
$submitButton.addClass('disabled');
// Submit form if it hasn't been submitted yet
if (!$form.prop('data-form-submitted')) {
$form.prop('data-form-submitted', true);
return;
}
// Prevent default submission for subsequent clicks within 5 seconds
event.preventDefault();
setTimeout(() => {
$form.prop('removeProp', 'data-form-submitted');
$submitButton.removeClass('disabled'); // Re-enable after 5 seconds
}, 5000);
});
/**
* This simply appends #preview to the action of the
* QR action when you click the Full Editor & Preview button

View File

@@ -329,17 +329,6 @@ function parseDocument($container) {
}, 100);
});
/**
* Adjust HTML code for IE8 and older versions
*/
// if (oldBrowser) {
// // Fix .linklist.bulletin lists
// $container
// .find('ul.linklist.bulletin > li')
// .filter(':first-child, .rightside:last-child')
// .addClass('no-bulletin');
// }
/**
* Resize navigation (breadcrumbs) block to keep all links on same line
*/

View File

@@ -78,6 +78,7 @@
<p class="author"><span><i class="icon fa-file fa-fw icon-lightgray icon-md" aria-hidden="true"></i><span class="sr-only">{MINI_POST_IMG}</span></span> {L_POSTED} {L_POST_BY_AUTHOR} {POST_AUTHOR_FULL} &raquo; {POST_DATE}</p>
<!-- ENDIF -->
{% if S_CAN_APPROVE %}
<!-- IF S_POST_UNAPPROVED -->
<form method="post" id="mcp_approve" action="{U_APPROVE_ACTION}">
@@ -101,6 +102,7 @@
</p>
</form>
<!-- ENDIF -->
{% endif %}
{% if S_POST_REPORTED and not S_MCP_REPORT %}
<p class="post-notice reported">

View File

@@ -9,7 +9,7 @@
<div class="inner">
<!-- IF AVATAR_IMG -->
<dl class="left-box">
<dl class="left-box avatar-rank-container">
<dt class="profile-avatar">{AVATAR_IMG}</dt>
<!-- EVENT memberlist_view_rank_avatar_before -->
<!-- IF RANK_TITLE --><dd style="text-align: center;">{RANK_TITLE}</dd><!-- ENDIF -->

View File

@@ -40,8 +40,10 @@
<!-- END notifications -->
</ul>
{% EVENT notification_dropdown_footer_before %}
<div class="footer">
<a href="{U_VIEW_ALL_NOTIFICATIONS}"><span>{L_SEE_ALL}</span></a>
</div>
{% EVENT notification_dropdown_footer_after %}
</div>
</div>

View File

@@ -100,7 +100,7 @@
<!-- IF not S_SHOW_DRAFTS and not $SIG_EDIT eq 1 -->
<div class="panel bg2">
<div class="inner">
<fieldset class="submit-buttons" data-prevent-flood>
<fieldset class="submit-buttons">
{S_HIDDEN_ADDRESS_FIELD}
{S_HIDDEN_FIELDS}
<!-- EVENT posting_editor_submit_buttons -->

View File

@@ -31,7 +31,11 @@
<ul class="recipients">
<!-- BEGIN to_recipient -->
<li>
<!-- IF not S_EDIT_POST --><input type="submit" name="remove_{to_recipient.TYPE}[{to_recipient.UG_ID}]" value="x" class="button2" /><!-- ENDIF -->
<!-- IF not S_EDIT_POST -->
<button type="submit" name="remove_{to_recipient.TYPE}[{to_recipient.UG_ID}]">
<i class="icon fa-times icon-red" aria-hidden="true"></i><span class="sr-only">{L_REMOVE}</span>
</button>
<!-- ENDIF -->
<!-- IF to_recipient.IS_GROUP --><a href="{to_recipient.U_VIEW}" style="color: {{ to_recipient.COLOUR }}"><strong>{to_recipient.NAME}</strong></a><!-- ELSE -->{to_recipient.NAME_FULL}<!-- ENDIF -->
</li>
<!-- END to_recipient -->
@@ -48,7 +52,11 @@
<ul class="recipients">
<!-- BEGIN bcc_recipient -->
<li>
<!-- IF not S_EDIT_POST --><input type="submit" name="remove_{bcc_recipient.TYPE}[{bcc_recipient.UG_ID}]" value="x" class="button2" /><!-- ENDIF -->
<!-- IF not S_EDIT_POST -->
<button type="submit" name="remove_{bcc_recipient.TYPE}[{bcc_recipient.UG_ID}]">
<i class="icon fa-times icon-red" aria-hidden="true"></i><span class="sr-only">{L_REMOVE}</span>
</button>
<!-- ENDIF -->
<!-- IF bcc_recipient.IS_GROUP --><a href="{bcc_recipient.U_VIEW}" style="color: {{ bcc_recipient.COLOUR }}"><strong>{bcc_recipient.NAME}</strong></a><!-- ELSE -->{bcc_recipient.NAME_FULL}<!-- ENDIF -->
</li>
<!-- END bcc_recipient -->
@@ -69,8 +77,12 @@
<ul class="recipients">
<!-- BEGIN to_recipient -->
<li>
<!-- IF to_recipient.IS_GROUP --><a href="{to_recipient.U_VIEW}"><strong>{to_recipient.NAME}</strong></a><!-- ELSE -->{to_recipient.NAME_FULL}<!-- ENDIF -->&nbsp;
<!-- IF not S_EDIT_POST --><input type="submit" name="remove_{to_recipient.TYPE}[{to_recipient.UG_ID}]" value="x" class="button2" /><!-- ENDIF -->
<!-- IF not S_EDIT_POST -->
<button type="submit" name="remove_{to_recipient.TYPE}[{to_recipient.UG_ID}]">
<i class="icon fa-times icon-red" aria-hidden="true"></i><span class="sr-only">{L_REMOVE}</span>
</button>
<!-- ENDIF -->
<!-- IF to_recipient.IS_GROUP --><a href="{to_recipient.U_VIEW}"><strong>{to_recipient.NAME}</strong></a><!-- ELSE -->{to_recipient.NAME_FULL}<!-- ENDIF -->
</li>
<!-- END to_recipient -->
</ul>

View File

@@ -52,6 +52,7 @@
<h3>{L_GROUP_SETTINGS_SAVE}</h3>
<fieldset>
<!-- EVENT ucp_group_settings_before -->
<dl>
<dt><label for="group_colour">{L_GROUP_COLOR}{L_COLON}</label><br /><span>{L_GROUP_COLOR_EXPLAIN}</span></dt>
<dd>
@@ -65,6 +66,7 @@
<dt><label for="group_rank">{L_GROUP_RANK}{L_COLON}</label></dt>
<dd><select name="group_rank" id="group_rank">{S_RANK_OPTIONS}</select></dd>
</dl>
<!-- EVENT ucp_group_settings_after -->
</fieldset>
</div>

View File

@@ -23,6 +23,7 @@
<label for="smilies0"><input type="radio" name="smilies" id="smilies0" value="0"<!-- IF not S_SMILIES --> checked="checked"<!-- ENDIF --> /> {L_NO}</label>
</dd>
</dl>
{% if S_SIG_ALLOWED %}
<dl>
<dt><label for="sig1">{L_DEFAULT_ADD_SIG}{L_COLON}</label></dt>
<dd>
@@ -30,6 +31,7 @@
<label for="sig0"><input type="radio" name="sig" id="sig0" value="0"<!-- IF not S_SIG --> checked="checked"<!-- ENDIF --> /> {L_NO}</label>
</dd>
</dl>
{% endif %}
<dl>
<dt><label for="notify1">{L_DEFAULT_NOTIFY}{L_COLON}</label></dt>
<dd>

View File

@@ -731,6 +731,10 @@ table.info tbody th {
max-width: 100%;
}
.avatar-rank-container {
max-width: 20%;
}
.left-box.profile-details {
width: 80%;
}
@@ -780,12 +784,6 @@ fieldset.fields1 dd.recipients {
margin-left: 1em;
}
fieldset.fields1 ul.recipients input.button2 {
font-size: 0.8em;
margin-right: 0;
padding: 0;
}
fieldset.fields1 dl.pmlist > dt {
width: auto !important;
}

View File

@@ -412,6 +412,10 @@
clear: both;
}
.avatar-rank-container {
max-width: 100%;
}
/* Polls
----------------------------------------*/
fieldset.polls dt {

View File

@@ -10,7 +10,7 @@
@import url("normalize.css?hash=48eb3f89");
@import url("base.css?hash=7c5543be");
@import url("utilities.css?hash=d8f72c42");
@import url("common.css?hash=a9741ba1");
@import url("common.css?hash=843d5d5f");
@import url("links.css?hash=18286e16");
@import url("content.css?hash=d0e24377");
@import url("buttons.css?hash=56f0d25f");
@@ -18,4 +18,4 @@
@import url("forms.css?hash=9016b55c");
@import url("icons.css?hash=64da33ce");
@import url("colours.css?hash=fcb2f289");
@import url("responsive.css?hash=91525545");
@import url("responsive.css?hash=c9d32cba");

View File

@@ -154,68 +154,11 @@ switch ($mode)
break;
case 'delete_cookies':
/** @var \phpbb\controller\helper $controller_helper */
$controller_helper = $phpbb_container->get('controller.helper');
// Delete Cookies with dynamic names (do NOT delete poll cookies)
if (confirm_box(true))
{
$set_time = time() - 31536000;
foreach ($request->variable_names(\phpbb\request\request_interface::COOKIE) as $cookie_name)
{
$cookie_data = $request->variable($cookie_name, '', true, \phpbb\request\request_interface::COOKIE);
// Only delete board cookies, no other ones...
if (strpos($cookie_name, $config['cookie_name'] . '_') !== 0)
{
continue;
}
$cookie_name = str_replace($config['cookie_name'] . '_', '', $cookie_name);
/**
* Event to save custom cookies from deletion
*
* @event core.ucp_delete_cookies
* @var string cookie_name Cookie name to checking
* @var bool retain_cookie Do we retain our cookie or not, true if retain
* @since 3.1.3-RC1
*/
$retain_cookie = false;
$vars = array('cookie_name', 'retain_cookie');
extract($phpbb_dispatcher->trigger_event('core.ucp_delete_cookies', compact($vars)));
if ($retain_cookie)
{
continue;
}
// Polls are stored as {cookie_name}_poll_{topic_id}, cookie_name_ got removed, therefore checking for poll_
if (strpos($cookie_name, 'poll_') !== 0)
{
$user->set_cookie($cookie_name, '', $set_time);
}
}
$user->set_cookie('track', '', $set_time);
$user->set_cookie('u', '', $set_time);
$user->set_cookie('k', '', $set_time);
$user->set_cookie('sid', '', $set_time);
// We destroy the session here, the user will be logged out nevertheless
$user->session_kill();
$user->session_begin();
meta_refresh(3, append_sid("{$phpbb_root_path}index.$phpEx"));
$message = $user->lang['COOKIES_DELETED'] . '<br /><br />' . sprintf($user->lang['RETURN_INDEX'], '<a href="' . append_sid("{$phpbb_root_path}index.$phpEx") . '">', '</a>');
trigger_error($message);
}
else
{
confirm_box(false, 'DELETE_COOKIES', '');
}
redirect(append_sid("{$phpbb_root_path}index.$phpEx"));
// Redirect to controller
redirect($controller_helper->route('phpbb_ucp_delete_cookies_controller'));
break;
case 'switch_perm':

View File

@@ -2071,7 +2071,7 @@ for ($i = 0, $end = count($post_list); $i < $end; ++$i)
'U_EMAIL' => $user_cache[$poster_id]['email'],
'U_JABBER' => $user_cache[$poster_id]['jabber'],
'U_APPROVE_ACTION' => append_sid("{$phpbb_root_path}mcp.$phpEx", "i=queue&amp;p={$row['post_id']}&amp;redirect=" . urlencode(str_replace('&amp;', '&', $viewtopic_url . '&amp;p=' . $row['post_id'] . '#p' . $row['post_id']))),
'U_APPROVE_ACTION' => append_sid("{$phpbb_root_path}mcp.$phpEx", "i=queue&amp;p={$row['post_id']}&amp;f={$row['forum_id']}&amp;redirect=" . urlencode(str_replace('&amp;', '&', $viewtopic_url . '&amp;p=' . $row['post_id'] . '#p' . $row['post_id']))),
'U_REPORT' => ($auth->acl_get('f_report', $forum_id)) ? $phpbb_container->get('controller.helper')->route('phpbb_report_post_controller', array('id' => $row['post_id'])) : '',
'U_MCP_REPORT' => ($auth->acl_get('m_report', $forum_id)) ? append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=reports&amp;mode=report_details&amp;p=' . $row['post_id'], true, $user->session_id) : '',
'U_MCP_APPROVE' => ($auth->acl_get('m_approve', $forum_id)) ? append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=queue&amp;mode=approve_details&amp;p=' . $row['post_id'], true, $user->session_id) : '',

View File

@@ -28,6 +28,33 @@
</requestFiltering>
</security>
</system.webServer>
<location path="config">
<system.webServer>
<security>
<requestFiltering>
<hiddenSegments>
<add segment="default" />
<add segment="development" />
<add segment="installer" />
<add segment="production" />
<add segment="test" />
<add segment=".htaccess" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
</location>
<location path="ext">
<system.webServer>
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="phpbb" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
</location>
<location path="images/avatars">
<system.webServer>
<security>

View File

@@ -115,6 +115,7 @@ class phpbb_auth_provider_apache_test extends phpbb_database_test_case
'user_email' => 'example@example.com',
'user_birthday' => '',
'user_lastvisit' => 0,
'user_last_active' => 0,
'user_lastmark' => 0,
'user_lastpost_time' => 0,
'user_lastpage' => '',
@@ -161,6 +162,7 @@ class phpbb_auth_provider_apache_test extends phpbb_database_test_case
'user_sig_bbcode_bitfield' => '',
'user_jabber' => '',
'user_actkey' => '',
'user_actkey_expiration' => 0,
'user_newpasswd' => '',
'user_form_salt' => '',
'user_new' => 1,

View File

@@ -0,0 +1,217 @@
<?php
use phpbb\avatar\driver\gravatar;
use phpbb\request\request;
use phpbb\request\request_interface;
/**
*
* 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.
*
*/
class phpbb_avatar_driver_gravatar_test extends \phpbb_database_test_case
{
/** @var \phpbb\config\config */
private $config;
/** @var gravatar */
private $gravatar;
/** @var request_interface */
private $request;
/** @var \phpbb\template\template */
private $template;
/** @var \phpbb\user */
private $user;
private $template_data = [];
public function getDataSet()
{
return $this->createXMLDataSet(__DIR__ . '/fixtures/users.xml');
}
protected function setUp(): void
{
global $phpbb_root_path, $phpEx;
$this->config = new \phpbb\config\config(array());
$this->request = $this->getMockBuilder(request::class)
->disableOriginalConstructor()
->setMethods(['get_super_global'])
->getMock();
$this->request->method('get_super_global')
->willReturn([]);
$this->template = $this->getMockBuilder(\phpbb\template\twig\twig::class)
->disableOriginalConstructor()
->setMethods(['assign_vars'])
->getMock();
$this->template->method('assign_vars')
->will($this->returnCallback([$this, 'template_assign_vars']));
$this->user = $this->getMockBuilder(\phpbb\user::class)
->disableOriginalConstructor()
->getMock();
$filesystem = new \phpbb\filesystem\filesystem();
$imagesize = new \FastImageSize\FastImageSize();
$cache = $this->createMock('\phpbb\cache\driver\driver_interface');
$path_helper = new \phpbb\path_helper(
new \phpbb\symfony_request(
$this->request
),
$filesystem,
$this->request,
$phpbb_root_path,
$phpEx
);
global $phpbb_dispatcher;
$phpbb_dispatcher = $this->getMockBuilder(\phpbb\event\dispatcher::class)
->disableOriginalConstructor()
->setMethods(['trigger_event'])
->getMock();
$phpbb_dispatcher->method('trigger_event')
->willReturnArgument(1);
$this->gravatar = new gravatar($this->config, $imagesize, $phpbb_root_path, $phpEx, $path_helper, $cache);
$this->gravatar->set_name('avatar.driver.gravatar');
}
public function template_assign_vars($data)
{
$this->template_data = array_merge($this->template_data, $data);
}
public function data_prepare_form(): array
{
return [
[
// Only default empty values, no request data
[
'AVATAR_GRAVATAR_WIDTH' => '',
'AVATAR_GRAVATAR_HEIGHT' => '',
'AVATAR_GRAVATAR_EMAIL' => '',
],
[],
[
'avatar_type' => '',
'avatar_width' => '',
'avatar_height' => '',
]
],
[
// Only default empty values, request data set
[
'AVATAR_GRAVATAR_WIDTH' => '80',
'AVATAR_GRAVATAR_HEIGHT' => '90',
'AVATAR_GRAVATAR_EMAIL' => '',
],
[
request_interface::POST => [
'avatar_type' => 'avatar.driver.gravatar',
'avatar_gravatar_width' => '80',
'avatar_gravatar_height' => '90',
],
],
[
'avatar_type' => '',
'avatar_width' => '80',
'avatar_height' => '90',
]
],
[
// Only default empty values, request data set
[
'AVATAR_GRAVATAR_WIDTH' => '70',
'AVATAR_GRAVATAR_HEIGHT' => '60',
'AVATAR_GRAVATAR_EMAIL' => 'bar@foo.com',
],
[
request_interface::POST => [
'avatar_type' => 'avatar.driver.gravatar',
'avatar_gravatar_width' => '80',
'avatar_gravatar_height' => '90',
],
],
[
'avatar_type' => 'avatar.driver.gravatar',
'avatar' => 'bar@foo.com',
'avatar_width' => '70',
'avatar_height' => '60',
]
],
];
}
/**
* @dataProvider data_prepare_form
*/
public function test_prepare_form($expected_vars, $request_data, $row)
{
$error = [];
$this->template_data = [];
$request = $this->getMockBuilder(request::class)
->disableOriginalConstructor()
->setMethods(['get_super_global'])
->getMock();
$request->method('get_super_global')
->willReturn([]);
$requestInputReflection = new \ReflectionProperty($request, 'input');
$requestInputReflection->setAccessible(true);
$request_data[request_interface::GET] = $request_data[request_interface::GET] ?? [];
$request_data[request_interface::POST] = $request_data[request_interface::POST] ?? [];
$request_data[request_interface::REQUEST] = $request_data[request_interface::GET] + $request_data[request_interface::POST];
$requestInputReflection->setValue($request, $request_data);
$requestTypeCastHelperReflection = new \ReflectionProperty($request, 'type_cast_helper');
$requestTypeCastHelperReflection->setAccessible(true);
$requestTypeCastHelperReflection->setValue($request, new \phpbb\request\type_cast_helper());
$this->gravatar->prepare_form($request, $this->template, $this->user, $row, $error);
// Error not touched by gravatar
$this->assertEquals([], $error);
$this->assertEquals($expected_vars, $this->template_data);
}
public function test_gravatar_misc(): void
{
$this->assertEquals('ucp_avatar_options_gravatar.html', $this->gravatar->get_template_name());
$this->assertEquals('acp_avatar_options_gravatar.html', $this->gravatar->get_acp_template_name());
$row = [
'avatar_type' => 'avatar.driver.gravatar',
'avatar' => 'bar@foo.com',
'avatar_width' => '70',
'avatar_height' => '60',
];
$this->assertEquals('<img src="//gravatar.com/avatar/e0ee9d02824d4320a999507150c5b8a371c635c41f645ba3a7205f36384dc199?s=70" width="70" height="60" alt="" />', $this->gravatar->get_custom_html($this->user, $row));
}
public function test_get_data(): void
{
$row = [
'avatar_type' => 'avatar.driver.gravatar',
'avatar' => 'bar@foo.com',
'avatar_width' => '70',
'avatar_height' => '60',
];
$this->assertEquals([
'src' => '//gravatar.com/avatar/e0ee9d02824d4320a999507150c5b8a371c635c41f645ba3a7205f36384dc199?s=70',
'width' => '70',
'height' => '60',
], $this->gravatar->get_data($row));
}
}

View File

@@ -17,6 +17,9 @@ class phpbb_cache_file_driver_test extends phpbb_cache_common_test_case
{
private $cache_dir;
/** @var \phpbb\cache\driver\file */
private $cache_file;
public function getDataSet()
{
return $this->createXMLDataSet(__DIR__ . '/fixtures/config.xml');
@@ -36,7 +39,8 @@ class phpbb_cache_file_driver_test extends phpbb_cache_common_test_case
}
$this->create_cache_dir();
$this->driver = new \phpbb\cache\driver\file($this->cache_dir);
$this->cache_file = new \phpbb\cache\driver\file($this->cache_dir);
$this->driver = $this->cache_file;
}
protected function tearDown(): void
@@ -49,6 +53,145 @@ class phpbb_cache_file_driver_test extends phpbb_cache_common_test_case
parent::tearDown();
}
public function test_read_not_readable()
{
if (strtolower(substr(PHP_OS, 0, 3)) === 'win')
{
$this->markTestSkipped('Unable to test unreadable files on Windows');
}
global $phpEx;
// Create file that is not readable
$this->assertTrue($this->cache_file->_write('unreadable', 'foo', time() + 86400));
$filename = "{$this->cache_dir}unreadable.$phpEx";
@chmod($filename, 0000);
$this->assertFalse($this->cache_file->_read('unreadable'));
@chmod($filename, 0600);
$this->assertNotFalse($this->cache_file->_read('unreadable'));
}
public function test_read_data_global_invalid()
{
global $phpEx;
$reflectionCacheVars = new \ReflectionProperty($this->cache_file, 'vars');
$reflectionCacheVars->setAccessible(true);
$reflectionCacheVars->setValue($this->cache_file, ['foo' => 'bar']);
$reflectionCacheVarExpires = new \ReflectionProperty($this->cache_file, 'var_expires');
$reflectionCacheVarExpires->setAccessible(true);
$reflectionCacheVarExpires->setValue($this->cache_file, ['foo' => time() + 86400]);
// Create file in invalid format
$this->assertTrue($this->cache_file->_write('data_global'));
$filename = "{$this->cache_dir}data_global.$phpEx";
$cache_data = file_get_contents($filename);
// Force negative read when retrieving data_global
$cache_data = str_replace("\n13\n", "\n1\n", $cache_data);
file_put_contents($filename, $cache_data);
$this->assertFalse($this->cache_file->_read('data_global'));
}
public function test_read_data_global_zero_bytes()
{
global $phpEx;
$reflectionCacheVars = new \ReflectionProperty($this->cache_file, 'vars');
$reflectionCacheVars->setAccessible(true);
$reflectionCacheVars->setValue($this->cache_file, ['foo' => 'bar']);
$reflectionCacheVarExpires = new \ReflectionProperty($this->cache_file, 'var_expires');
$reflectionCacheVarExpires->setAccessible(true);
$reflectionCacheVarExpires->setValue($this->cache_file, ['foo' => time() + 86400]);
// Create file in invalid format
$this->assertTrue($this->cache_file->_write('data_global'));
$filename = "{$this->cache_dir}data_global.$phpEx";
$cache_data = file_get_contents($filename);
// Force negative read when retrieving data_global
$cache_data = str_replace("\n13\n", "\n0\n", $cache_data);
file_put_contents($filename, $cache_data);
$this->assertFalse($this->cache_file->_read('data_global'));
}
public function test_read_data_global_hex_bytes()
{
global $phpEx;
$reflectionCacheVars = new \ReflectionProperty($this->cache_file, 'vars');
$reflectionCacheVars->setAccessible(true);
$reflectionCacheVars->setValue($this->cache_file, ['foo' => 'bar']);
$reflectionCacheVarExpires = new \ReflectionProperty($this->cache_file, 'var_expires');
$reflectionCacheVarExpires->setAccessible(true);
$reflectionCacheVarExpires->setValue($this->cache_file, ['foo' => time() + 86400]);
// Create file in invalid format
$this->assertTrue($this->cache_file->_write('data_global'));
$filename = "{$this->cache_dir}data_global.$phpEx";
$cache_data = file_get_contents($filename);
// Force negative read when retrieving data_global
$cache_data = str_replace("\n13\n", "\nA\n", $cache_data);
file_put_contents($filename, $cache_data);
$this->assertFalse($this->cache_file->_read('data_global'));
}
public function test_read_data_global_expired()
{
$reflectionCacheVars = new \ReflectionProperty($this->cache_file, 'vars');
$reflectionCacheVars->setAccessible(true);
$reflectionCacheVars->setValue($this->cache_file, ['foo' => 'bar']);
$reflectionCacheVarExpires = new \ReflectionProperty($this->cache_file, 'var_expires');
$reflectionCacheVarExpires->setAccessible(true);
$reflectionCacheVarExpires->setValue($this->cache_file, ['foo' => time() - 86400]);
// Create file in invalid format
$this->assertTrue($this->cache_file->_write('data_global'));
// Clear data
$reflectionCacheVars->setValue($this->cache_file, []);
$reflectionCacheVarExpires->setValue($this->cache_file, []);
$this->assertTrue($this->cache_file->_read('data_global'));
// Check data, should be empty
$this->assertEquals([], $reflectionCacheVars->getValue($this->cache_file));
}
public function test_read_data_global()
{
$reflectionCacheVars = new \ReflectionProperty($this->cache_file, 'vars');
$reflectionCacheVars->setAccessible(true);
$expectedVars = ['foo' => 'bar'];
$reflectionCacheVars->setValue($this->cache_file, $expectedVars);
$reflectionCacheVarExpires = new \ReflectionProperty($this->cache_file, 'var_expires');
$reflectionCacheVarExpires->setAccessible(true);
$expectedVarExpires = ['foo' => time() + 86400];
$reflectionCacheVarExpires->setValue($this->cache_file, $expectedVarExpires);
// Create file in invalid format
$this->assertTrue($this->cache_file->_write('data_global'));
// Clear data
$reflectionCacheVars->setValue($this->cache_file, []);
$reflectionCacheVarExpires->setValue($this->cache_file, []);
$this->assertEquals([], $reflectionCacheVars->getValue($this->cache_file));
$this->assertEquals([], $reflectionCacheVarExpires->getValue($this->cache_file));
$this->assertTrue($this->cache_file->_read('data_global'));
// Check data, should be empty
$this->assertEquals($expectedVars, $reflectionCacheVars->getValue($this->cache_file));
$this->assertEquals($expectedVarExpires, $reflectionCacheVarExpires->getValue($this->cache_file));
}
private function create_cache_dir()
{
$this->get_test_case_helpers()->makedirs($this->cache_dir);

View File

@@ -0,0 +1,129 @@
<?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.
*
*/
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
use phpbb\console\command\user\delete_id;
require_once __DIR__ . '/base.php';
class phpbb_console_user_delete_ids_test extends phpbb_console_user_base
{
public function get_command_tester()
{
$application = new Application();
$application->add(new delete_id(
$this->db,
$this->language,
$this->log,
$this->user,
$this->user_loader,
BOTS_TABLE,
USER_GROUP_TABLE,
USERS_TABLE,
$this->phpbb_root_path,
$this->php_ext
));
$command = $application->find('user:delete_id');
$this->command_name = $command->getName();
$this->question = $command->getHelper('question');
return new CommandTester($command);
}
public function test_delete()
{
$command_tester = $this->get_command_tester();
$command_tester->setInputs(['yes', '']);
$command_tester->execute(array(
'command' => $this->command_name,
'user_ids' => [3, 4],
'--delete-posts' => false,
));
$this->assertNull($this->get_user_id('Test'));
$this->assertNull($this->get_user_id('Test 2'));
$this->assertStringContainsString('CLI_USER_DELETE_ID_SUCCESS', $command_tester->getDisplay());
}
public function test_delete_one()
{
$command_tester = $this->get_command_tester();
$command_tester->setInputs(['yes', '']);
$command_tester->execute(array(
'command' => $this->command_name,
'user_ids' => [3],
'--delete-posts' => false,
));
$this->assertNull($this->get_user_id('Test'));
$this->assertNotNull($this->get_user_id('Test 2'));
$this->assertStringContainsString('CLI_USER_DELETE_ID_SUCCESS', $command_tester->getDisplay());
}
public function test_delete_bot()
{
$command_tester = $this->get_command_tester();
$this->assertNotNull($this->get_user_id('Test Bot'));
$command_tester->setInputs(['yes', '']);
$command_tester->execute(array(
'command' => $this->command_name,
'user_ids' => [6],
'--delete-posts' => false,
));
$this->assertNull($this->get_user_id('Test Bot'));
$this->assertStringContainsString('CLI_USER_DELETE_ID_SUCCESS', $command_tester->getDisplay());
}
public function test_delete_non_user()
{
$command_tester = $this->get_command_tester();
$command_tester->setInputs(['yes', '']);
$command_tester->execute(array(
'command' => $this->command_name,
'user_ids' => [999],
'--delete-posts' => false,
));
$this->assertStringContainsString('CLI_USER_DELETE_NONE', $command_tester->getDisplay());
}
public function test_delete_cancel()
{
$command_tester = $this->get_command_tester();
$this->assertEquals(3, $this->get_user_id('Test'));
$command_tester->setInputs(['no', '']);
$command_tester->execute(array(
'command' => $this->command_name,
'user_ids' => [3, 4],
'--delete-posts' => false,
));
$this->assertNotNull($this->get_user_id('Test'));
$this->assertNotNull($this->get_user_id('Test 2'));
}
}

View File

@@ -47,6 +47,14 @@
<value></value>
<value>0</value>
</row>
<row>
<value>6</value>
<value></value>
<value>Test Bot</value>
<value>Test Bot</value>
<value></value>
<value>2</value>
</row>
</table>
<table name="phpbb_groups">
<column>group_id</column>
@@ -59,5 +67,23 @@
<value>3</value>
<value>foobar</value>
</row>
<row>
<value>6</value>
<value>BOTS</value>
<value>3</value>
<value></value>
</row>
</table>
<table name="phpbb_user_group">
<column>group_id</column>
<column>user_id</column>
<column>group_leader</column>
<column>user_pending</column>
<row>
<value>6</value>
<value>6</value>
<value>0</value>
<value>0</value>
</row>
</table>
</dataset>

View File

@@ -28,6 +28,12 @@ class phpbb_dbal_migrator_tool_permission_role_test extends phpbb_database_test_
'ADMINISTRATORS' => 5,
];
public $role_ids = [
'ROLE_ADMIN_STANDARD' => 1,
'ROLE_USER_FULL' => 5,
'ROLE_MOD_FULL' => 10,
];
public $new_roles = [
[
'ROLE_ADMIN_NEW',
@@ -196,4 +202,32 @@ class phpbb_dbal_migrator_tool_permission_role_test extends phpbb_database_test_
$this->assertFalse($this->db->sql_fetchfield('auth_role_id'));
$this->db->sql_freeresult($result);
}
public function test_copied_permission_set()
{
$sql = 'SELECT rdt.auth_setting
FROM ' . ACL_OPTIONS_TABLE. ' ot, ' . ACL_ROLES_DATA_TABLE . ' rdt
WHERE rdt.role_id = ' . $this->role_ids['ROLE_ADMIN_STANDARD'] . "
AND auth_option = 'u_copied_permission'
AND ot.auth_option_id = rdt.auth_option_id";
// Add new local 'u_copied_permission' copied from 'u_test'
// It should be added to the ROLE_ADMIN_STANDARD role automatically similar to 'u_test' permission
$this->tool->add('u_copied_permission', false, 'u_test');
$this->assertEquals(true, $this->tool->exists('u_copied_permission', false));
// Copied permission setting should be equal to what it was copied from
$result = $this->db->sql_query($sql);
$this->assertEquals(0, $this->db->sql_fetchfield('auth_setting'));
$this->db->sql_freeresult($result);
// Set new permission for copied auth option for the role
$this->tool->permission_set('ROLE_ADMIN_STANDARD', 'u_copied_permission', 'role', true);
// Copied permission setting should be updated
$result = $this->db->sql_query($sql);
$this->assertEquals(1, $this->db->sql_fetchfield('auth_setting'));
$this->db->sql_freeresult($result);
}
}

View File

@@ -73,6 +73,67 @@ class phpbb_dbal_write_test extends phpbb_database_test_case
$db->sql_freeresult($result);
}
public function test_delete_rollback()
{
$db = $this->new_dbal();
$is_myisam = false;
if ($db->get_sql_layer() === 'mysqli')
{
$table_status = $db->get_table_status('phpbb_config');
$is_myisam = isset($table_status['Engine']) && $table_status['Engine'] === 'MyISAM';
}
$db->sql_transaction('begin');
$sql = "DELETE FROM phpbb_config
WHERE config_name = 'config1'";
$db->sql_query($sql);
// Rollback and check that nothing was changed
$db->sql_transaction('rollback');
$sql = 'SELECT *
FROM phpbb_config';
$result = $db->sql_query($sql);
$rows = $db->sql_fetchrowset($result);
$db->sql_freeresult($result);
if (!$is_myisam)
{
$this->assertEquals(2, count($rows));
$this->assertEquals('config1', $rows[0]['config_name']);
}
else
{
// Rollback does not work on MyISAM
$this->assertEquals(1, count($rows));
$this->assertEquals('config2', $rows[0]['config_name']);
// Restore deleted config value on MyISAM
$sql = "INSERT INTO phpbb_config (config_name, config_value, is_dynamic) VALUES ('config1', 'foo', 0)";
$db->sql_query($sql);
}
$db->sql_transaction('begin');
$sql = "DELETE FROM phpbb_config
WHERE config_name = 'config1'";
$db->sql_query($sql);
// Commit and check that data was actually changed
$db->sql_transaction('commit');
$sql = 'SELECT *
FROM phpbb_config';
$result = $db->sql_query($sql);
$rows = $db->sql_fetchrowset($result);
$db->sql_freeresult($result);
$this->assertEquals(1, count($rows));
$this->assertEquals('config2', $rows[0]['config_name']);
}
public function test_multiple_insert()
{
$db = $this->new_dbal();

View File

@@ -146,7 +146,7 @@ abstract class phpbb_functional_search_base extends phpbb_functional_test_case
foreach (['', 'a', 't', 'f', 'i', 's'] as $sort_key)
{
$this->assert_search_found('phpbb3+installation', 1, 3, $sort_key);
$this->assert_search_found('phpbb3+installation', 1, 4, $sort_key);
$this->assert_search_found('foosubject+barsearch', 1, 2, $sort_key);
$this->assert_search_found('barsearch-testing', 1, 2, $sort_key); // test hyphen ignored
$this->assert_search_found('barsearch+-+testing', 1, 2, $sort_key); // test hyphen wrapped with space ignored

View File

@@ -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.
*
*/
/**
* @group functional
*/
class phpbb_functional_switch_permissions_test extends phpbb_functional_test_case
{
private const TEST_USER = 'switch-permissions-test';
protected function setUp(): void
{
parent::setUp();
$this->login();
$this->admin_login();
$this->add_lang(['common', 'ucp']);
}
public function test_switch_permissions()
{
$user_id = $this->create_user(self::TEST_USER);
// Open user administration page for new user
$crawler = self::request('GET', "adm/index.php?i=users&mode=overview&u={$user_id}&sid={$this->sid}");
// Use permissions
$link = $crawler->selectLink($this->lang('USE_PERMISSIONS'))->link();
$crawler = self::$client->click($link);
// Check that we switched permissions to test user
$this->assertStringContainsString(
str_replace('<br />', '<br>', $this->lang('PERMISSIONS_TRANSFERRED', self::TEST_USER)),
$crawler->html()
);
// Check that ACP pages get forced to acp main with restore permission info
$this->add_lang('acp/common');
$crawler = self::request('GET', "adm/index.php?i=users&mode=overview&u={$user_id}&sid={$this->sid}");
$this->assertStringContainsString(
$this->lang('PERMISSIONS_TRANSFERRED'),
$crawler->text()
);
// Check that restore permissions link exists
$crawler = self::$client->request('GET', '../index.php?sid=' . $this->sid);
$this->assertStringContainsString(
$this->lang('RESTORE_PERMISSIONS'),
$crawler->text()
);
// Check that restore permissions works
$crawler = self::$client->request('GET', 'ucp.php?mode=restore_perm&sid=' . $this->sid);
$this->assertStringContainsString(
$this->lang('PERMISSIONS_RESTORED'),
$crawler->text()
);
}
}

View File

@@ -18,10 +18,14 @@ class phpbb_functional_user_password_reset_test extends phpbb_functional_test_ca
{
protected $user_data;
protected const TEST_USER = 'reset-password-test-user';
protected const TEST_EMAIL = 'reset-password-test-user@test.com';
public function test_password_reset()
{
$this->add_lang('ucp');
$user_id = $this->create_user('reset-password-test-user', 'reset-password-test-user@test.com');
$user_id = $this->create_user(self::TEST_USER, self::TEST_EMAIL);
// test without email
$crawler = self::request('GET', "ucp.php?mode=sendpassword&sid={$this->sid}");
@@ -41,13 +45,13 @@ class phpbb_functional_user_password_reset_test extends phpbb_functional_test_ca
// test with correct email
$crawler = self::request('GET', "app.php/user/forgot_password?sid={$this->sid}");
$form = $crawler->selectButton('submit')->form(array(
'email' => 'reset-password-test-user@test.com',
'email' => self::TEST_EMAIL,
));
$crawler = self::submit($form);
$this->assertContainsLang('PASSWORD_RESET_LINK_SENT', $crawler->text());
// Check if columns in database were updated for password reset
$this->get_user_data('reset-password-test-user');
$this->get_user_data(self::TEST_USER);
$this->assertNotEmpty($this->user_data['reset_token']);
$this->assertNotEmpty($this->user_data['reset_token_expiration']);
$reset_token = $this->user_data['reset_token'];
@@ -56,31 +60,31 @@ class phpbb_functional_user_password_reset_test extends phpbb_functional_test_ca
// Check that reset token is only created once per day
$crawler = self::request('GET', "app.php/user/forgot_password?sid={$this->sid}");
$form = $crawler->selectButton('submit')->form(array(
'email' => 'reset-password-test-user@test.com',
'email' => self::TEST_EMAIL,
));
$crawler = self::submit($form);
$this->assertContainsLang('PASSWORD_RESET_LINK_SENT', $crawler->text());
$this->get_user_data('reset-password-test-user');
$this->get_user_data(self::TEST_USER);
$this->assertNotEmpty($this->user_data['reset_token']);
$this->assertNotEmpty($this->user_data['reset_token_expiration']);
$this->assertEquals($reset_token, $this->user_data['reset_token']);
$this->assertEquals($reset_token_expiration, $this->user_data['reset_token_expiration']);
// Create another user with the same email
$this->create_user('reset-password-test-user1', 'reset-password-test-user@test.com');
$this->create_user('reset-password-test-user1', self::TEST_EMAIL);
// Test that username is now also required
$crawler = self::request('GET', "app.php/user/forgot_password?sid={$this->sid}");
$form = $crawler->selectButton('submit')->form(array(
'email' => 'reset-password-test-user@test.com',
'email' => self::TEST_EMAIL,
));
$crawler = self::submit($form);
$this->assertContainsLang('EMAIL_NOT_UNIQUE', $crawler->text());
// Provide both username and email
$form = $crawler->selectButton('submit')->form(array(
'email' => 'reset-password-test-user@test.com',
'email' => self::TEST_EMAIL,
'username' => 'reset-password-test-user1',
));
$crawler = self::submit($form);
@@ -95,7 +99,7 @@ class phpbb_functional_user_password_reset_test extends phpbb_functional_test_ca
public function test_login_after_reset()
{
$this->login('reset-password-test-user');
$this->login(self::TEST_USER);
}
public function data_reset_user_password()
@@ -117,7 +121,7 @@ class phpbb_functional_user_password_reset_test extends phpbb_functional_test_ca
public function test_reset_user_password($expected, $user_id, $token)
{
$this->add_lang('ucp');
$this->get_user_data('reset-password-test-user');
$this->get_user_data(self::TEST_USER);
$user_id = !$user_id ? $this->user_data['user_id'] : $user_id;
$token = !$token ? $this->user_data['reset_token'] : $token;
@@ -131,8 +135,8 @@ class phpbb_functional_user_password_reset_test extends phpbb_functional_test_ca
{
$form = $crawler->filter('input[type=submit]')->form();
$values = array_merge($form->getValues(), [
'new_password' => 'reset-password-test-user',
'new_password_confirm' => 'reset-password-test-user',
'new_password' => self::TEST_USER,
'new_password_confirm' => self::TEST_USER,
]);
$crawler = self::submit($form, $values);
$this->assertContainsLang('PASSWORD_RESET', $crawler->text());
@@ -146,7 +150,7 @@ class phpbb_functional_user_password_reset_test extends phpbb_functional_test_ca
$this->assertStringContainsString($this->lang('LOGIN_EXPLAIN_UCP'), $crawler->filter('html')->text());
$form = $crawler->selectButton($this->lang('LOGIN'))->form();
$crawler = self::submit($form, array('username' => 'reset-password-test-user', 'password' => 'reset-password-test-user'));
$crawler = self::submit($form, array('username' => self::TEST_USER, 'password' => self::TEST_USER));
$this->assertStringNotContainsString($this->lang('LOGIN'), $crawler->filter('.navbar')->text());
$cookies = self::$cookieJar->all();
@@ -167,17 +171,17 @@ class phpbb_functional_user_password_reset_test extends phpbb_functional_test_ca
$form = $crawler->selectButton($this->lang('LOGIN'))->form();
// Try logging in with the old password
$crawler = self::submit($form, array('username' => 'reset-password-test-user', 'password' => 'reset-password-test-userreset-password-test-user'));
$crawler = self::submit($form, array('username' => self::TEST_USER, 'password' => 'reset-password-test-userreset-password-test-user'));
$this->assertStringContainsString($this->lang('LOGIN_ERROR_PASSWORD', '', ''), $crawler->filter('html')->text());
}
/**
* @depends test_login
*/
public function test_acivateAfterDeactivate()
public function test_activateAfterDeactivate()
{
// User is active, actkey should not exist
$this->get_user_data('reset-password-test-user');
$this->get_user_data(self::TEST_USER);
$this->assertEmpty($this->user_data['user_actkey']);
$this->login();
@@ -189,7 +193,7 @@ class phpbb_functional_user_password_reset_test extends phpbb_functional_test_ca
$this->assertContainsLang('FIND_USERNAME', $crawler->filter('html')->text());
$form = $crawler->selectButton('Submit')->form();
$crawler = self::submit($form, array('username' => 'reset-password-test-user'));
$crawler = self::submit($form, array('username' => self::TEST_USER));
// Deactivate account and go back to overview of current user
$this->assertContainsLang('USER_TOOLS', $crawler->filter('html')->text());
@@ -201,7 +205,7 @@ class phpbb_functional_user_password_reset_test extends phpbb_functional_test_ca
$crawler = self::request('GET', preg_replace('#(.+)(adm/index.php.+)#', '$2', $link->getUri()));
// Ensure again that actkey is empty after deactivation
$this->get_user_data('reset-password-test-user');
$this->get_user_data(self::TEST_USER);
$this->assertEmpty($this->user_data['user_actkey']);
// Force reactivation of account and check that act key is not empty anymore
@@ -210,8 +214,50 @@ class phpbb_functional_user_password_reset_test extends phpbb_functional_test_ca
$crawler = self::submit($form, array('action' => 'reactivate'));
$this->assertContainsLang('FORCE_REACTIVATION_SUCCESS', $crawler->filter('html')->text());
$this->get_user_data('reset-password-test-user');
$this->get_user_data(self::TEST_USER);
$this->assertNotEmpty($this->user_data['user_actkey']);
// Logout and try resending activation email, account is deactivated though
$this->logout();
$this->add_lang('ucp');
$crawler = self::request('GET', 'ucp.php?mode=resend_act');
$this->assertContainsLang('UCP_RESEND', $crawler->filter('html')->text());
$form = $crawler->filter('input[name=submit]')->selectButton('Submit')->form();
$crawler = self::submit($form, [
'username' => self::TEST_USER,
'email' => self::TEST_EMAIL,
]);
$this->assertContainsLang('ACCOUNT_DEACTIVATED', $crawler->filter('html')->text());
}
/**
* @depends test_activateAfterDeactivate
*/
public function test_resendActivation()
{
// User is deactivated and should have actkey, actkey should not exist
$this->get_user_data(self::TEST_USER);
$this->assertNotEmpty($this->user_data['user_actkey']);
// Change reason for inactivity
$db = $this->get_db();
$sql = 'UPDATE ' . USERS_TABLE . '
SET user_inactive_reason = ' . INACTIVE_REMIND . '
WHERE user_id = ' . (int) $this->user_data['user_id'];
$db->sql_query($sql);
$this->add_lang('ucp');
$crawler = self::request('GET', 'ucp.php?mode=resend_act');
$this->assertContainsLang('UCP_RESEND', $crawler->filter('html')->text());
$form = $crawler->filter('input[name=submit]')->selectButton('Submit')->form();
$crawler = self::submit($form, [
'username' => self::TEST_USER,
'email' => self::TEST_EMAIL,
]);
$this->assertContainsLang('ACTIVATION_ALREADY_SENT', $crawler->filter('html')->text());
}
protected function get_user_data($username)

View File

@@ -0,0 +1,127 @@
<?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.
*
*/
use PHPUnit\Framework\TestCase;
class phpbb_functions_content_get_context_test extends TestCase
{
/**
* Data provider for get_context test cases.
*
* @return array
*/
public function data_get_context(): array
{
return [
'text contains words and length greater than text' => [
'text' => 'This is a sample text containing several words, including sample, text, and words.',
'words' => ['sample', 'words'],
'length' => 100,
'expected' => 'This is a sample text containing several words, including sample, text, and words.',
],
'text contains words and length less than text' => [
'text' => 'This is a sample text containing several words, including sample, text, and words.',
'words' => ['sample', 'words'],
'length' => 50,
'expected' => 'This is a sample text containing several words ...',
],
'text does not contain words' => [
'text' => 'This is a sample text containing several words, but none of them match the given words.',
'words' => ['nonexistent'],
'length' => 50,
'expected' => 'This is a sample text containing several words ...',
],
'desired length equal to text length' => [
'text' => 'Exact length text.',
'words' => ['Exact', 'text'],
'length' => 18,
'expected' => 'Exact length text.',
],
'text with html entities' => [
'text' => 'This is a sample text containing &amp; and &lt; and &gt; entities.',
'words' => ['sample', 'containing'],
'length' => 50,
'expected' => 'This is a sample text containing &amp; and &lt; and ...',
],
'text with html entities and contains last word' => [
'text' => 'This is a sample text containing &amp; and &lt; and &gt; entities.',
'words' => ['sample', 'entities'],
'length' => 50,
'expected' => 'This is a sample text ... and &lt; and &gt; entities.',
],
'text with multiple spaces and special characters' => [
'text' => 'This is a sample text containing several words.',
'words' => ['sample', 'several'],
'length' => 50,
'expected' => 'This is a sample text containing several words.',
],
'empty text' => [
'text' => '',
'words' => ['sample', 'words'],
'length' => 50,
'expected' => '',
],
'empty words array' => [
'text' => 'This is a sample text containing several words.',
'words' => [],
'length' => 50,
'expected' => 'This is a sample text containing several words.',
],
'zero length' => [
'text' => 'This is a sample text.',
'words' => ['sample'],
'length' => 0,
'expected' => '...',
],
'negative length' => [
'text' => 'This is a sample text.',
'words' => ['sample'],
'length' => -10,
'expected' => '...',
],
'ellipses_beginning' => [
'text' => 'foo foo foo foo foo foo foo foo bar',
'words' => ['bar'],
'length' => 10,
'expected' => '... foo foo bar',
],
'ellipsis_end' => [
'text' => 'bar foo foo foo foo foo foo foo foo',
'words' => ['bar'],
'length' => 10,
'expected' => 'bar foo foo ...',
],
'ellipsis_middle' => [
'text' => 'foo word1 foo foo foo foo foo foo foo foo foo word2 foo',
'words' => ['word1', 'word2'],
'length' => 10,
'expected' => '... word1 ... word2 ...',
],
'ellipsis_middle2' => [
'text' => 'word1 foo foo foo foo foo foo foo foo foo word2',
'words' => ['word1', 'word2'],
'length' => 10,
'expected' => 'word1 ... word2',
]
];
}
/**
* @dataProvider data_get_context
*/
public function test_get_context($text, $words, $length, $expected)
{
$this->assertEquals($expected, get_context($text, $words, $length));
}
}

View File

@@ -25,19 +25,24 @@ class phpbb_search_native_test extends phpbb_search_test_case
protected function setUp(): void
{
global $phpbb_root_path, $phpEx, $config, $user, $cache;
global $phpbb_root_path, $phpEx, $config, $cache;
parent::setUp();
// dbal uses cache
$cache = new phpbb_mock_cache();
$lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx);
$lang = new \phpbb\language\language($lang_loader);
$user = new \phpbb\user($lang, '\phpbb\datetime');;
$this->db = $this->new_dbal();
$phpbb_dispatcher = new phpbb_mock_event_dispatcher();
$error = null;
$class = self::get_search_wrapper('\phpbb\search\fulltext_native');
$config['fulltext_native_min_chars'] = 2;
$config['fulltext_native_max_chars'] = 14;
$config['max_num_search_keywords'] = 10;
$this->search = new $class($error, $phpbb_root_path, $phpEx, null, $config, $this->db, $user, $phpbb_dispatcher);
}
@@ -259,4 +264,78 @@ class phpbb_search_native_test extends phpbb_search_test_case
}
$this->assert_array_content_equals($common, $this->search->get_common_words());
}
public function data_split_keywords_max(): array
{
return [
'character count within limits separated by more spaces' => [
'foo bar baz boo far faz roo rar raz zoo',
'all',
false,
],
'character count within limits separated by spaces' => [
'foo bar baz boo far faz roo rar raz zoo',
'all',
false,
],
'character count within limits separated by +, spaces after +' => [
'foo+ bar+ baz+ boo+ far+ faz+ roo+ rar+ raz+ zoo',
'all',
false,
],
'character count within limits separated by +, no spaces' => [
'foo+bar+baz+boo+far+faz+roo+rar+raz+zoo',
'all',
false,
],
'character count outside limits separated by +, no spaces' => [
'foo+bar+baz+boo+far+faz+roo+rar+raz+zoo+zar',
'all',
true,
],
'character count outside limits separated by + and spaces' => [
'foo +bar +baz +boo +far +faz +roo +rar +raz +zoo +zar',
'all',
true,
],
'character count outside limits separated by spaces' => [
'foo bar baz boo far faz roo rar raz zoo zar',
'all',
true,
],
'character count outside limits separated by -, no spaces' => [
'foo-bar-baz-boo-far-faz-roo-rar-raz-zoo-zar',
'all',
true,
],
'character count outside limits separated by - and spaces' => [
'foo -bar -baz -boo -far -faz -roo -rar -raz -zoo -zar',
'all',
true,
],
'character count outside limits separated by |, no spaces' => [
'foo|bar|baz|boo|far|faz|roo|rar|raz|zoo|zar',
'all',
true,
],
'character count outside limits separated by | and spaces' => [
'foo |bar |baz |boo |far |faz |roo |rar |raz |zoo |zar',
'all',
true,
],
];
}
/**
* @dataProvider data_split_keywords_max
*/
public function test_split_max_keywords($keywords, $terms, $expect_error)
{
if ($expect_error)
{
$this->setExpectedTriggerError(E_USER_NOTICE, 'MAX_NUM_SEARCH_KEYWORDS_REFINE');
}
$this->assertTrue($this->search->split_keywords($keywords, $terms));
}
}

Some files were not shown because too many files have changed in this diff Show More