mirror of
https://github.com/phpbb/phpbb.git
synced 2025-09-11 16:50:47 +02:00
Compare commits
103 Commits
release-3.
...
prep-relea
Author | SHA1 | Date | |
---|---|---|---|
|
72ae28d583 | ||
|
ff08756ea0 | ||
|
1f48926b1d | ||
|
bf83c73a60 | ||
|
c252fb9bfb | ||
|
c992272036 | ||
|
7c090c6e2a | ||
|
4c419c5acd | ||
|
eaa0c779a6 | ||
|
3c7bf782a7 | ||
|
aa21aff080 | ||
|
443d82d082 | ||
|
5abc36f651 | ||
|
f971d7ffcb | ||
|
ed145b8317 | ||
|
26e49d4b15 | ||
|
43ff56a7a7 | ||
|
6c35cea146 | ||
|
62f9f53f46 | ||
|
4a039d853f | ||
|
b1282fcbc7 | ||
|
58d87c7c06 | ||
|
fd58e4f338 | ||
|
8129208e56 | ||
|
ecceeab709 | ||
|
660f3d2b86 | ||
|
ae1d830e84 | ||
|
688c7cad49 | ||
|
44f04a91f3 | ||
|
db7b4c999d | ||
|
1b0c9b22e0 | ||
|
4217dc46ca | ||
|
46ea7a8ffd | ||
|
f80b41aa77 | ||
|
501ecf5dec | ||
|
13b267ca6f | ||
|
6db142b8ef | ||
|
4ba4b94fe0 | ||
|
7c95839951 | ||
|
cd619d350b | ||
|
fe4d8bfa7e | ||
|
283b4f8171 | ||
|
990cbe093b | ||
|
80957d12eb | ||
|
d8dc1204c2 | ||
|
d5e94f1d97 | ||
|
9ace4dc585 | ||
|
2ac9c0ae7c | ||
|
914383a581 | ||
|
45a43e85d8 | ||
|
08d184b0fa | ||
|
d6a17c1fe7 | ||
|
ba03a05d27 | ||
|
17d066a980 | ||
|
b574a0294c | ||
|
4fb06b00b7 | ||
|
3d70d87b57 | ||
|
f55d747bb4 | ||
|
c5f13dd327 | ||
|
e406308244 | ||
|
15556319d9 | ||
|
7a21119d17 | ||
|
5a09105c3c | ||
|
48c2ca9dfe | ||
|
d6ed21f3f2 | ||
|
929013388f | ||
|
d844f82f56 | ||
|
f7d16fc626 | ||
|
9eb30b5d8e | ||
|
544e0900e6 | ||
|
c26ded6025 | ||
|
1c73b7ab19 | ||
|
472b36877c | ||
|
66b6a5e1f3 | ||
|
8acba2db02 | ||
|
bf4e26a0b8 | ||
|
7c4b6cde8b | ||
|
7f08efb04c | ||
|
6ca926c146 | ||
|
a7b673a1b6 | ||
|
03f315f7b7 | ||
|
76752800c2 | ||
|
23f872d138 | ||
|
92730af7a4 | ||
|
80f4c07bc5 | ||
|
dc0561c8bf | ||
|
9c9dcb1e35 | ||
|
45515b7072 | ||
|
cc4579ee04 | ||
|
6d0a965db2 | ||
|
629a909b65 | ||
|
331bea8b22 | ||
|
9b4743c028 | ||
|
6623e7fe1d | ||
|
9d64579dea | ||
|
31f0e7b882 | ||
|
ee3b351419 | ||
|
99861a8c10 | ||
|
8104facb30 | ||
|
e53db0775a | ||
|
8aef7b5659 | ||
|
9e11624693 | ||
|
e6bdecbfee |
70
.github/workflows/check_merge_to_master.yml
vendored
Normal file
70
.github/workflows/check_merge_to_master.yml
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
name: Check merge to master
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [ opened, synchronize, reopened ]
|
||||
branches:
|
||||
- 3.3.x
|
||||
|
||||
jobs:
|
||||
merge-check:
|
||||
if: github.event_name == 'pull_request_target' && github.event.pull_request.base.ref == '3.3.x'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Ensure full history is fetched
|
||||
|
||||
- name: Set up Git user
|
||||
run: |
|
||||
git config --global user.name "github-actions"
|
||||
git config --global user.email "github-actions@github.com"
|
||||
|
||||
- name: Fetch all branches
|
||||
run: git fetch origin
|
||||
|
||||
- name: Simulate merging PR into 3.3.x
|
||||
id: simulate_merge
|
||||
run: |
|
||||
git checkout 3.3.x
|
||||
git fetch origin pull/${{ github.event.pull_request.number }}/head
|
||||
git merge --no-ff FETCH_HEAD || exit 1
|
||||
|
||||
- name: Attempt to merge updated 3.3.x into master
|
||||
id: merge_master
|
||||
run: |
|
||||
git checkout master
|
||||
if git merge --no-ff 3.3.x --no-commit; then
|
||||
echo "mergeable=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "mergeable=false" >> $GITHUB_OUTPUT
|
||||
git merge --abort
|
||||
fi
|
||||
|
||||
- name: Find Comment
|
||||
uses: peter-evans/find-comment@v3
|
||||
id: fc
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-author: 'github-actions[bot]'
|
||||
body-includes: The attempt to merge branch `3.3.x` into `master` has completed
|
||||
|
||||
- name: Post comment on PR
|
||||
if: always() # Ensure this step always runs, regardless of merge result
|
||||
uses: peter-evans/create-or-update-comment@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||
edit-mode: replace
|
||||
body: |
|
||||
The attempt to merge branch `3.3.x` into `master` has completed after considering the changes in this PR.
|
||||
|
||||
- Merge result: ${{ steps.merge_master.outputs.mergeable == 'true' && 'Success ✅' || 'Conflict ❌' }}
|
||||
|
||||
${{ steps.merge_master.outputs.mergeable == 'true' && 'This PR is ready to be merged.' || 'A separate PR will be needed to merge `3.3.x` into `master`.' }}
|
||||
|
||||
- name: Mark job as succeeded
|
||||
if: always()
|
||||
run: echo "Merge check completed. Ignoring the result to avoid failed status."
|
60
.github/workflows/merge_3.3.x_to_master.yml
vendored
Normal file
60
.github/workflows/merge_3.3.x_to_master.yml
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
name: Merge 3.3.x into master
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 3.3.x
|
||||
|
||||
jobs:
|
||||
merge-branch:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/create-github-app-token@v1
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.MERGE_MASTER_APP_ID }}
|
||||
private-key: ${{ secrets.MERGE_MASTER_SECRET }}
|
||||
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Fetch full history for proper merging
|
||||
ref: 3.3.x # Checkout the 3.3.x branch
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- name: Fetch the latest commit information
|
||||
id: get-commit-info
|
||||
run: |
|
||||
# Get the latest commit SHA and its author details
|
||||
COMMIT_SHA=$(git rev-parse HEAD)
|
||||
COMMIT_AUTHOR_NAME=$(git log -1 --pretty=format:'%an' $COMMIT_SHA)
|
||||
COMMIT_AUTHOR_EMAIL=$(git log -1 --pretty=format:'%ae' $COMMIT_SHA)
|
||||
|
||||
# Save them as output for later steps
|
||||
echo "commit_sha=$COMMIT_SHA" >> $GITHUB_ENV
|
||||
echo "commit_author_name=$COMMIT_AUTHOR_NAME" >> $GITHUB_ENV
|
||||
echo "commit_author_email=$COMMIT_AUTHOR_EMAIL" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up Git with the pull request author's info
|
||||
run: |
|
||||
git config --global user.name "${{ env.commit_author_name }}"
|
||||
git config --global user.email "${{ env.commit_author_email }}"
|
||||
|
||||
- name: Fetch all branches
|
||||
run: git fetch --all
|
||||
|
||||
- name: Merge 3.3.x into master
|
||||
run: |
|
||||
git checkout master
|
||||
if git merge --no-ff 3.3.x; then
|
||||
echo "merge_failed=false" >> $GITHUB_ENV
|
||||
else
|
||||
echo "merge_failed=true" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Push changes to master if merge was successful
|
||||
if: env.merge_failed == 'false'
|
||||
run: git push origin master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -351,7 +351,7 @@ jobs:
|
||||
|
||||
# Other database types, namely sqlite3 and mssql
|
||||
other-tests:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
|
@@ -42,8 +42,8 @@ We have unit and functional tests in order to prevent regressions. You can view
|
||||
|
||||
Branch | Description | GitHub Actions |
|
||||
------- | ----------- | -------------- |
|
||||
**master** | Latest development version |  |
|
||||
**3.3.x** | Development of version 3.3.x |  |
|
||||
**master** | Latest development version |  |
|
||||
**3.3.x** | Development of version 3.3.x |  |
|
||||
|
||||
## 📜 License
|
||||
|
||||
|
@@ -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.13" />
|
||||
<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, 3.3.13-RC1" />
|
||||
<property name="newversion" value="3.3.14" />
|
||||
<property name="prevversion" value="3.3.13" />
|
||||
<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, 3.3.12, 3.3.14-RC1" />
|
||||
<!-- no configuration should be needed beyond this point -->
|
||||
|
||||
<property name="oldversions" value="${olderversions}, ${prevversion}" />
|
||||
|
@@ -42,59 +42,52 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- IF .enabled -->
|
||||
<tr>
|
||||
<td class="row3" colspan="4"><strong>{L_EXTENSIONS_ENABLED}</strong><!-- EVENT acp_ext_list_enabled_title_after --></td>
|
||||
</tr>
|
||||
<!-- BEGIN enabled -->
|
||||
<tr class="ext_enabled row-highlight">
|
||||
<td><strong title="{enabled.NAME}">{enabled.META_DISPLAY_NAME}</strong><!-- EVENT acp_ext_list_enabled_name_after --></td>
|
||||
<td style="text-align: center;">
|
||||
<!-- IF enabled.S_VERSIONCHECK -->
|
||||
<strong class="<!-- IF enabled.S_UP_TO_DATE -->current-ext<!-- ELSE -->outdated-ext<!-- ENDIF -->">{enabled.META_VERSION}</strong>
|
||||
<!-- IF not enabled.S_UP_TO_DATE --><i class="fa fa-exclamation-circle outdated-ext" aria-hidden="true"></i><!-- ENDIF -->
|
||||
<!-- ELSE -->
|
||||
{enabled.META_VERSION}
|
||||
<!-- ENDIF -->
|
||||
</td>
|
||||
<td style="text-align: center;"><a href="{enabled.U_DETAILS}">{L_DETAILS}</a></td>
|
||||
<td style="text-align: center;">
|
||||
<!-- BEGIN actions -->
|
||||
<a href="{enabled.actions.U_ACTION}"<!-- IF enabled.actions.L_ACTION_EXPLAIN --> title="{enabled.actions.L_ACTION_EXPLAIN}"<!-- ENDIF -->>{enabled.actions.L_ACTION}</a>
|
||||
<!-- IF not enabled.actions.S_LAST_ROW --> | <!-- ENDIF -->
|
||||
<!-- END actions -->
|
||||
</td>
|
||||
</tr>
|
||||
<!-- END enabled -->
|
||||
<!-- ENDIF -->
|
||||
|
||||
<!-- IF .disabled -->
|
||||
<tr>
|
||||
<td class="row3" colspan="4"><strong>{L_EXTENSIONS_DISABLED}</strong><!-- EVENT acp_ext_list_disabled_title_after --></td>
|
||||
</tr>
|
||||
<!-- BEGIN disabled -->
|
||||
<tr class="ext_disabled row-highlight">
|
||||
<td><strong title="{disabled.NAME}">{disabled.META_DISPLAY_NAME}</strong><!-- EVENT acp_ext_list_disabled_name_after --></td>
|
||||
<td style="text-align: center;">
|
||||
<!-- IF disabled.S_VERSIONCHECK -->
|
||||
<strong class="<!-- IF disabled.S_UP_TO_DATE -->current-ext<!-- ELSE -->outdated-ext<!-- ENDIF -->">{disabled.META_VERSION}</strong>
|
||||
<!-- IF not disabled.S_UP_TO_DATE --><i class="fa fa-exclamation-circle outdated-ext" aria-hidden="true"></i><!-- ENDIF -->
|
||||
<!-- ELSE -->
|
||||
{disabled.META_VERSION}
|
||||
<!-- ENDIF -->
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<!-- IF disabled.U_DETAILS --><a href="{disabled.U_DETAILS}">{L_DETAILS}</a><!-- ENDIF -->
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<!-- BEGIN actions -->
|
||||
<a href="{disabled.actions.U_ACTION}"<!-- IF disabled.actions.L_ACTION_EXPLAIN --> title="{disabled.actions.L_ACTION_EXPLAIN}"<!-- ENDIF -->>{disabled.actions.L_ACTION}</a>
|
||||
<!-- IF not disabled.actions.S_LAST_ROW --> | <!-- ENDIF -->
|
||||
<!-- END actions -->
|
||||
</td>
|
||||
</tr>
|
||||
<!-- END disabled -->
|
||||
<!-- ENDIF -->
|
||||
{% for list in ['enabled', 'disabled', 'not_installed'] %}
|
||||
{% set blockname = attribute(loops, list) %}
|
||||
{% if blockname|length %}
|
||||
<tr>
|
||||
<td class="row3" colspan="4"><strong>{{ lang('EXTENSIONS_' ~ list|upper) }}</strong>
|
||||
{% if list == 'enabled' %}
|
||||
{% EVENT acp_ext_list_enabled_title_after %}
|
||||
{% elseif list == 'disabled' %}
|
||||
{% EVENT acp_ext_list_disabled_title_after %}
|
||||
{% elseif list == 'not_installed' %}
|
||||
{% EVENT acp_ext_list_not_installed_title_after %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% for data in blockname %}
|
||||
<tr class="ext_{{ list }} row-highlight">
|
||||
<td><strong title="{{ data.NAME }}">{{ data.META_DISPLAY_NAME }}</strong>
|
||||
{% if list == 'enabled' %}
|
||||
{% EVENT acp_ext_list_enabled_name_after %}
|
||||
{% elseif list == 'disabled' %}
|
||||
{% EVENT acp_ext_list_disabled_name_after %}
|
||||
{% elseif list == 'not_installed' %}
|
||||
{% EVENT acp_ext_list_not_installed_name_after %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
{% if data.S_VERSIONCHECK %}
|
||||
<strong class="{% if data.S_UP_TO_DATE %}current-ext{% else %}outdated-ext{% endif %}">{{ data.META_VERSION }}</strong>
|
||||
{% if not data.S_UP_TO_DATE %}<i class="fa fa-exclamation-circle outdated-ext" aria-hidden="true"></i>{% endif %}
|
||||
{% else %}
|
||||
{{ data.META_VERSION }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
{% if data.U_DETAILS %}<a href="{{ data.U_DETAILS }}">{{ lang ('DETAILS') }}</a>{% endif %}
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
{% for actions in data.actions %}
|
||||
<a href="{{ actions.U_ACTION }}"{% if actions.L_ACTION_EXPLAIN %} title="{{ actions.L_ACTION_EXPLAIN }}"{% endif %}>{{ actions.L_ACTION }}</a>
|
||||
{% if not actions.S_LAST_ROW %} | {% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@@ -50,6 +50,8 @@
|
||||
<ol>
|
||||
<li><a href="#changelog">Changelog</a>
|
||||
<ul>
|
||||
<li><a href="#v3314rc1">Changes since 3.3.14-RC1</a></li>
|
||||
<li><a href="#v3313">Changes since 3.3.13</a></li>
|
||||
<li><a href="#v3313rc1">Changes since 3.3.13-RC1</a></li>
|
||||
<li><a href="#v3312">Changes since 3.3.12</a></li>
|
||||
<li><a href="#v3312rc1">Changes since 3.3.12-RC1</a></li>
|
||||
@@ -171,6 +173,42 @@
|
||||
<div class="inner">
|
||||
|
||||
<div class="content">
|
||||
<a name="v3314rc1"></a><h3>Changes since 3.3.14-RC1</h3>
|
||||
<h4>Improvement</h4>
|
||||
<ul>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17421">PHPBB-17421</a>] - Rename section for not installed extensions to not installed</li>
|
||||
</ul>
|
||||
|
||||
<a name="v3313"></a><h3>Changes since 3.3.13</h3>
|
||||
<h4>Bug</h4>
|
||||
<ul>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17181">PHPBB-17181</a>] - If statement to highlight Reported PMS on the view message page doesn't work.</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17383">PHPBB-17383</a>] - HELO/EHLO error while using gethostbyaddr()</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17384">PHPBB-17384</a>] - Passing E_USER_ERROR to trigger_error() is deprecated in PHP 8.4</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17385">PHPBB-17385</a>] - Version check without SSL flag for CDB extensions fails</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17386">PHPBB-17386</a>] - Incorrect trace result while tracing user-based permissions</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17387">PHPBB-17387</a>] - PHP warnings in search results</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17390">PHPBB-17390</a>] - Missing buttons for approval of new messages</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17391">PHPBB-17391</a>] - PHP warnings on attempt to create user group having name already in use</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17398">PHPBB-17398</a>] - Ajax error on deleting cookies</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17405">PHPBB-17405</a>] - Function phpbb_gmgetdate() returns incorrect result for edge cases</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17410">PHPBB-17410</a>] - UCP tabs do not work when testing out another user's permissions</li>
|
||||
</ul>
|
||||
<h4>Improvement</h4>
|
||||
<ul>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-16852">PHPBB-16852</a>] - Addition of a new PHP event concerning bump topics</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17359">PHPBB-17359</a>] - Distinct disabled and not installed extensions in the list</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17376">PHPBB-17376</a>] - Link reference to quote post is a poor accessibility experience</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17382">PHPBB-17382</a>] - Incorrect grammar on FAQ page regarding searching for members</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17388">PHPBB-17388</a>] - Add php event to bump_topic_allowed function</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17394">PHPBB-17394</a>] - Check mergeability of PR on GitHub Actions</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17396">PHPBB-17396</a>] - Automatically handle merges of 3.3.x into master</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17397">PHPBB-17397</a>] - Add event for forum_data query in viewforum</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17402">PHPBB-17402</a>] - Add possibility to force reparsing BBCode via CLI</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17407">PHPBB-17407</a>] - Limit mergeability check to single comment</li>
|
||||
<li>[<a href="https://tracker.phpbb.com/browse/PHPBB-17411">PHPBB-17411</a>] - Add core event to search.php</li>
|
||||
</ul>
|
||||
|
||||
<a name="v3313rc1"></a><h3>Changes since 3.3.13-RC1</h3>
|
||||
<h4>Bug</h4>
|
||||
<ul>
|
||||
|
@@ -94,6 +94,20 @@ acp_ext_list_enabled_title_after
|
||||
* Since: 3.1.11-RC1
|
||||
* Purpose: Add text after enabled extensions section title.
|
||||
|
||||
acp_ext_list_not_installed_name_after
|
||||
===
|
||||
* Location: adm/style/acp_ext_list.html
|
||||
* Since: 3.3.14-RC1
|
||||
* Changed: 3.3.14 Renamed from acp_ext_list_available_name_after
|
||||
* Purpose: Add content after the name of not installed extensions in the list
|
||||
|
||||
acp_ext_list_not_installed_title_after
|
||||
===
|
||||
* Location: adm/style/acp_ext_list.html
|
||||
* Since: 3.3.14-RC1
|
||||
* Changed: 3.3.14 Renamed from acp_ext_list_available_title_after
|
||||
* Purpose: Add text after not installed extensions section title.
|
||||
|
||||
acp_forums_custom_settings
|
||||
===
|
||||
* Location: adm/style/acp_forums.html
|
||||
|
@@ -575,7 +575,7 @@ class acp_extensions
|
||||
catch (exception_interface $e)
|
||||
{
|
||||
$message = call_user_func_array(array($this->user, 'lang'), array_merge(array($e->getMessage()), $e->get_parameters()));
|
||||
$this->template->assign_block_vars('disabled', array(
|
||||
$this->template->assign_block_vars('not_installed', array(
|
||||
'META_DISPLAY_NAME' => $this->user->lang('EXTENSION_INVALID_LIST', $name, $message),
|
||||
'S_VERSIONCHECK' => false,
|
||||
));
|
||||
@@ -589,9 +589,9 @@ class acp_extensions
|
||||
$block_vars['NAME'] = $name;
|
||||
$block_vars['U_DETAILS'] = $this->u_action . '&action=details&ext_name=' . urlencode($name);
|
||||
|
||||
$this->template->assign_block_vars('disabled', $block_vars);
|
||||
$this->template->assign_block_vars('not_installed', $block_vars);
|
||||
|
||||
$this->output_actions('disabled', array(
|
||||
$this->output_actions('not_installed', array(
|
||||
'ENABLE' => $this->u_action . '&action=enable_pre&ext_name=' . urlencode($name),
|
||||
));
|
||||
}
|
||||
|
@@ -396,7 +396,7 @@ class acp_groups
|
||||
$allow_desc_urls = $request->variable('desc_parse_urls', false);
|
||||
$allow_desc_smilies = $request->variable('desc_parse_smilies', false);
|
||||
|
||||
$submit_ary = array(
|
||||
$submit_ary = [
|
||||
'colour' => $request->variable('group_colour', ''),
|
||||
'rank' => $request->variable('group_rank', 0),
|
||||
'receive_pm' => isset($_REQUEST['group_receive_pm']) ? 1 : 0,
|
||||
@@ -406,7 +406,13 @@ class acp_groups
|
||||
'max_recipients' => $request->variable('group_max_recipients', 0),
|
||||
'founder_manage' => 0,
|
||||
'skip_auth' => $request->variable('group_skip_auth', 0),
|
||||
);
|
||||
|
||||
// Initialize avatar data
|
||||
'avatar' => $avatar_data['avatar'] ?? '',
|
||||
'avatar_type' => $avatar_data['avatar_type'] ?? '',
|
||||
'avatar_height' => $avatar_data['avatar_height'] ?? 0,
|
||||
'avatar_width' => $avatar_data['avatar_width'] ?? 0,
|
||||
];
|
||||
|
||||
if ($user->data['user_type'] == USER_FOUNDER)
|
||||
{
|
||||
|
@@ -28,7 +28,7 @@ if (!defined('IN_PHPBB'))
|
||||
*/
|
||||
|
||||
// phpBB Version
|
||||
@define('PHPBB_VERSION', '3.3.13');
|
||||
@define('PHPBB_VERSION', '3.3.14');
|
||||
|
||||
// QA-related
|
||||
// define('PHPBB_QA', 1);
|
||||
|
@@ -107,9 +107,17 @@ function phpbb_gmgetdate($time = false)
|
||||
}
|
||||
|
||||
// getdate() interprets timestamps in local time.
|
||||
// What follows uses the fact that getdate() and
|
||||
// date('Z') balance each other out.
|
||||
return getdate($time - date('Z'));
|
||||
// So use UTC timezone temporarily to get UTC date info array.
|
||||
$current_timezone = date_default_timezone_get();
|
||||
|
||||
// Set UTC timezone and get respective date info
|
||||
date_default_timezone_set('UTC');
|
||||
$date_info = getdate($time);
|
||||
|
||||
// Restore timezone back
|
||||
date_default_timezone_set($current_timezone);
|
||||
|
||||
return $date_info;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -289,7 +289,27 @@ function make_jumpbox($action, $forum_id = false, $select_all = false, $acl_list
|
||||
*/
|
||||
function bump_topic_allowed($forum_id, $topic_bumped, $last_post_time, $topic_poster, $last_topic_poster)
|
||||
{
|
||||
global $config, $auth, $user;
|
||||
global $config, $auth, $user, $phpbb_dispatcher;
|
||||
|
||||
/**
|
||||
* Event to run code before the topic bump checks
|
||||
*
|
||||
* @event core.bump_topic_allowed_before
|
||||
* @var int forum_id ID of the forum
|
||||
* @var int topic_bumped Flag indicating if the topic was already bumped (0/1)
|
||||
* @var int last_post_time The time of the topic last post
|
||||
* @var int topic_poster User ID of the topic author
|
||||
* @var int last_topic_poster User ID of the topic last post author
|
||||
* @since 3.3.14-RC1
|
||||
*/
|
||||
$vars = [
|
||||
'forum_id',
|
||||
'topic_bumped',
|
||||
'last_post_time',
|
||||
'topic_poster',
|
||||
'last_topic_poster',
|
||||
];
|
||||
extract($phpbb_dispatcher->trigger_event('core.bump_topic_allowed_before', compact($vars)));
|
||||
|
||||
// Check permission and make sure the last post was not already bumped
|
||||
if (!$auth->acl_get('f_bump', $forum_id) || $topic_bumped)
|
||||
@@ -312,6 +332,28 @@ function bump_topic_allowed($forum_id, $topic_bumped, $last_post_time, $topic_po
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event to run code after the topic bump checks
|
||||
*
|
||||
* @event core.bump_topic_allowed_after
|
||||
* @var int forum_id ID of the forum
|
||||
* @var int topic_bumped Flag indicating if the topic was already bumped (0/1)
|
||||
* @var int last_post_time The time of the topic last post
|
||||
* @var int topic_poster User ID of the topic author
|
||||
* @var int last_topic_poster User ID of the topic last post author
|
||||
* @var int bump_time Bump time range
|
||||
* @since 3.3.14-RC1
|
||||
*/
|
||||
$vars = [
|
||||
'forum_id',
|
||||
'topic_bumped',
|
||||
'last_post_time',
|
||||
'topic_poster',
|
||||
'last_topic_poster',
|
||||
'bump_time',
|
||||
];
|
||||
extract($phpbb_dispatcher->trigger_event('core.bump_topic_allowed_after', compact($vars)));
|
||||
|
||||
// A bump time of 0 will completely disable the bump feature... not intended but might be useful.
|
||||
return $bump_time;
|
||||
}
|
||||
@@ -329,14 +371,14 @@ function get_context(string $text, array $words, int $length = 400): string
|
||||
{
|
||||
if ($length <= 0)
|
||||
{
|
||||
return '...';
|
||||
return $text;
|
||||
}
|
||||
|
||||
// we need to turn the entities back into their original form, to not cut the message in between them
|
||||
$text = html_entity_decode($text);
|
||||
// We need to turn the entities back into their original form, to not cut the message in between them
|
||||
$text = htmlspecialchars_decode($text);
|
||||
|
||||
// Replace all spaces/invisible characters with single spaces
|
||||
$text = preg_replace("/\s+/u", ' ', $text);
|
||||
$text = preg_replace("/[\p{Z}\h\v]+/u", ' ', $text);
|
||||
|
||||
$text_length = utf8_strlen($text);
|
||||
|
||||
@@ -351,7 +393,6 @@ function get_context(string $text, array $words, int $length = 400): string
|
||||
$word_indexes[$pos] = $word;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($word_indexes))
|
||||
{
|
||||
ksort($word_indexes);
|
||||
@@ -400,23 +441,21 @@ function get_context(string $text, array $words, int $length = 400): string
|
||||
$fragment_end = $end - $start + 1;
|
||||
|
||||
// Find the first valid alphanumeric character in the fragment to don't cut words
|
||||
if ($start > 0)
|
||||
if ($start > 0 && preg_match('/[^\p{L}\p{N}][\p{L}\p{N}]/u', $fragment, $matches, PREG_OFFSET_CAPTURE))
|
||||
{
|
||||
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
|
||||
$fragment_start = utf8_strlen(substr($fragment, 0, (int) $matches[0][1])) + 1;
|
||||
}
|
||||
|
||||
// Find the last valid alphanumeric character in the fragment to don't cut words
|
||||
if ($end < $text_length - 1)
|
||||
if ($end < $text_length - 1 && preg_match_all('/[\p{L}\p{N}][^\p{L}\p{N}]/u', $fragment, $matches, PREG_OFFSET_CAPTURE))
|
||||
{
|
||||
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
|
||||
$fragment_end = utf8_strlen(substr($fragment, 0, end($matches[0])[1]));
|
||||
}
|
||||
|
||||
$output[] = utf8_substr($fragment, $fragment_start, $fragment_end - $fragment_start + 1);
|
||||
}
|
||||
|
||||
return ($fragments[0][0] !== 0 ? '... ' : '') . htmlentities(implode(' ... ', $output)) . ($end < $text_length - 1 ? ' ...' : '');
|
||||
return ($fragments[0][0] !== 0 ? '... ' : '') . utf8_htmlspecialchars(implode(' ... ', $output)) . ($end < $text_length - 1 ? ' ...' : '');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1418,22 +1418,22 @@ class smtp_class
|
||||
global $user;
|
||||
|
||||
// Here we try to determine the *real* hostname (reverse DNS entry preferrably)
|
||||
$local_host = $user->host;
|
||||
|
||||
if (function_exists('php_uname'))
|
||||
if (function_exists('php_uname') && !empty($local_host = php_uname('n')))
|
||||
{
|
||||
$local_host = php_uname('n');
|
||||
|
||||
// Able to resolve name to IP
|
||||
if (($addr = @gethostbyname($local_host)) !== $local_host)
|
||||
{
|
||||
// Able to resolve IP back to name
|
||||
if (($name = @gethostbyaddr($addr)) !== $addr)
|
||||
if (!empty($name = @gethostbyaddr($addr)) && $name !== $addr)
|
||||
{
|
||||
$local_host = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$local_host = $user->host;
|
||||
}
|
||||
|
||||
// If we are authenticating through pop-before-smtp, we
|
||||
// have to login ones before we get authenticated
|
||||
|
@@ -502,8 +502,8 @@ 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'))
|
||||
// Fallback to acp main page for special test permission mode
|
||||
if ($this->p_class === 'acp' && $user->data['user_perm_from'] && $auth->acl_get('a_switchperm'))
|
||||
{
|
||||
$id = '';
|
||||
$mode = '';
|
||||
|
@@ -284,6 +284,7 @@ class mcp_queue
|
||||
$post_data = array(
|
||||
'S_MCP_QUEUE' => true,
|
||||
'U_APPROVE_ACTION' => append_sid("{$phpbb_root_path}mcp.$phpEx", "i=queue&p=$post_id"),
|
||||
'S_CAN_APPROVE' => $auth->acl_get('m_approve', $post_info['forum_id']),
|
||||
'S_CAN_DELETE_POST' => $auth->acl_get('m_delete', $post_info['forum_id']),
|
||||
'S_CAN_VIEWIP' => $auth->acl_get('m_info', $post_info['forum_id']),
|
||||
'S_POST_REPORTED' => $post_info['post_reported'],
|
||||
|
@@ -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.13',
|
||||
'phpbb_version' => '3.3.14',
|
||||
'author' => '<a href="https://www.phpbb.com/">phpBB Limited</a>',
|
||||
'dbms' => $dbms,
|
||||
'dbhost' => $dbhost,
|
||||
|
@@ -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.13');
|
||||
define('PHPBB_VERSION', '3.3.14');
|
||||
$phpbb_root_path = __DIR__ . '/../';
|
||||
$phpEx = substr(strrchr(__FILE__, '.'), 1);
|
||||
|
||||
|
@@ -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.13');
|
||||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('version', '3.3.14');
|
||||
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');
|
||||
|
||||
|
@@ -47,8 +47,9 @@ $lang = array_merge($lang, array(
|
||||
|
||||
'DETAILS' => 'Details',
|
||||
|
||||
'EXTENSIONS_DISABLED' => 'Disabled Extensions',
|
||||
'EXTENSIONS_ENABLED' => 'Enabled Extensions',
|
||||
'EXTENSIONS_NOT_INSTALLED' => 'Not installed Extensions',
|
||||
'EXTENSIONS_DISABLED' => 'Disabled Extensions',
|
||||
'EXTENSIONS_ENABLED' => 'Enabled Extensions',
|
||||
|
||||
'EXTENSION_DELETE_DATA' => 'Delete data',
|
||||
'EXTENSION_DISABLE' => 'Disable',
|
||||
|
@@ -75,6 +75,7 @@ $lang = array_merge($lang, array(
|
||||
'CLI_DESCRIPTION_REPARSER_REPARSE' => 'Reparses stored text with the current text_formatter services.',
|
||||
'CLI_DESCRIPTION_REPARSER_REPARSE_ARG_1' => 'Type of text to reparse. Leave blank to reparse everything.',
|
||||
'CLI_DESCRIPTION_REPARSER_REPARSE_OPT_DRY_RUN' => 'Do not save any changes; just print what would happen',
|
||||
'CLI_DESCRIPTION_REPARSER_REPARSE_OPT_FORCE_BBCODE' => 'Re-parse all BBCodes without exception. Note that any previously disabled BBCodes will be reprocessed, enabled, and fully rendered.',
|
||||
'CLI_DESCRIPTION_REPARSER_REPARSE_OPT_RANGE_MIN' => 'Lowest record ID to process',
|
||||
'CLI_DESCRIPTION_REPARSER_REPARSE_OPT_RANGE_MAX' => 'Highest record ID to process',
|
||||
'CLI_DESCRIPTION_REPARSER_REPARSE_OPT_RANGE_SIZE' => 'Approximate number of records to process at a time',
|
||||
|
@@ -158,7 +158,7 @@ $lang = array_merge($lang, array(
|
||||
'HELP_FAQ_SEARCH_BLANK_QUESTION' => 'Why does my search return a blank page!?',
|
||||
'HELP_FAQ_SEARCH_FORUM_ANSWER' => 'Enter a search term in the search box located on the index, forum or topic pages. Advanced search can be accessed by clicking the “Advance Search” link which is available on all pages on the forum. How to access the search may depend on the style used.',
|
||||
'HELP_FAQ_SEARCH_FORUM_QUESTION' => 'How can I search a forum or forums?',
|
||||
'HELP_FAQ_SEARCH_MEMBERS_ANSWER' => 'Visit to the “Members” page and click the “Find a member” link.',
|
||||
'HELP_FAQ_SEARCH_MEMBERS_ANSWER' => 'Visit the memberlist and click the “Find a member” link.',
|
||||
'HELP_FAQ_SEARCH_MEMBERS_QUESTION' => 'How do I search for members?',
|
||||
'HELP_FAQ_SEARCH_NO_RESULT_ANSWER' => 'Your search was probably too vague and included many common terms which are not indexed by phpBB. Be more specific and use the options available within Advanced search.',
|
||||
'HELP_FAQ_SEARCH_NO_RESULT_QUESTION' => 'Why does my search return no results?',
|
||||
|
@@ -112,6 +112,7 @@ $lang = array_merge($lang, array(
|
||||
'VIEW_INFO' => 'Post details',
|
||||
'VIEW_NEXT_TOPIC' => 'Next topic',
|
||||
'VIEW_PREVIOUS_TOPIC' => 'Previous topic',
|
||||
'VIEW_QUOTED_POST' => 'View quoted post',
|
||||
'VIEW_RESULTS' => 'View results',
|
||||
'VIEW_TOPIC_POSTS' => array(
|
||||
1 => '%d post',
|
||||
|
@@ -776,7 +776,7 @@ class auth
|
||||
|
||||
$sql_group = ($group_id !== false) ? ((!is_array($group_id)) ? 'group_id = ' . (int) $group_id : $db->sql_in_set('group_id', array_map('intval', $group_id))) : '';
|
||||
$sql_forum = ($forum_id !== false) ? ((!is_array($forum_id)) ? 'AND a.forum_id = ' . (int) $forum_id : 'AND ' . $db->sql_in_set('a.forum_id', array_map('intval', $forum_id))) : '';
|
||||
$sql_is_local = $forum_id !== false ? 'AND ao.is_local <> 0' : '';
|
||||
$sql_is_local = !empty($forum_id) ? 'AND ao.is_local <> 0' : '';
|
||||
|
||||
$sql_opts = '';
|
||||
$hold_ary = $sql_ary = array();
|
||||
|
@@ -92,6 +92,12 @@ class reparse extends \phpbb\console\command\command
|
||||
InputOption::VALUE_NONE,
|
||||
$this->user->lang('CLI_DESCRIPTION_REPARSER_REPARSE_OPT_DRY_RUN')
|
||||
)
|
||||
->addOption(
|
||||
'force-bbcode-reparsing',
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
$this->user->lang('CLI_DESCRIPTION_REPARSER_REPARSE_OPT_FORCE_BBCODE')
|
||||
)
|
||||
->addOption(
|
||||
'resume',
|
||||
null,
|
||||
@@ -222,13 +228,15 @@ class reparse extends \phpbb\console\command\command
|
||||
|
||||
// Start from $max and decrement $current by $size until we reach $min
|
||||
$current = $max;
|
||||
|
||||
$force_bbcode_reparsing = (bool) $this->get_option('force-bbcode-reparsing');
|
||||
while ($current >= $min)
|
||||
{
|
||||
$start = max($min, $current + 1 - $size);
|
||||
$end = max($min, $current);
|
||||
|
||||
$progress->setMessage($this->user->lang('CLI_REPARSER_REPARSE_REPARSING', $reparser->get_name(), $start, $end));
|
||||
$reparser->reparse_range($start, $end);
|
||||
$reparser->reparse_range($start, $end, $force_bbcode_reparsing);
|
||||
|
||||
$current = $start - 1;
|
||||
$progress->setProgress($max + 1 - $start);
|
||||
|
36
phpBB/phpbb/db/migration/data/v33x/v3314.php
Normal file
36
phpBB/phpbb/db/migration/data/v33x/v3314.php
Normal 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 v3314 extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
return version_compare($this->config['version'], '3.3.14', '>=');
|
||||
}
|
||||
|
||||
public static function depends_on()
|
||||
{
|
||||
return [
|
||||
'\phpbb\db\migration\data\v33x\v3314rc1',
|
||||
];
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return [
|
||||
['config.update', ['version', '3.3.14']],
|
||||
];
|
||||
}
|
||||
}
|
36
phpBB/phpbb/db/migration/data/v33x/v3314rc1.php
Normal file
36
phpBB/phpbb/db/migration/data/v33x/v3314rc1.php
Normal 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 v3314rc1 extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
return version_compare($this->config['version'], '3.3.14-RC1', '>=');
|
||||
}
|
||||
|
||||
public static function depends_on()
|
||||
{
|
||||
return [
|
||||
'\phpbb\db\migration\data\v33x\v3313',
|
||||
];
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return [
|
||||
['config.update', ['version', '3.3.14-RC1']],
|
||||
];
|
||||
}
|
||||
}
|
@@ -13,91 +13,116 @@
|
||||
|
||||
namespace phpbb;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use phpbb\exception\runtime_exception;
|
||||
|
||||
class file_downloader
|
||||
{
|
||||
const OK = 200;
|
||||
const NOT_FOUND = 404;
|
||||
const REQUEST_TIMEOUT = 408;
|
||||
|
||||
/** @var string Error string */
|
||||
protected $error_string = '';
|
||||
|
||||
/** @var int Error number */
|
||||
protected $error_number = 0;
|
||||
|
||||
/**
|
||||
* Create new guzzle client
|
||||
*
|
||||
* @param string $host
|
||||
* @param int $port
|
||||
* @param int $timeout
|
||||
*
|
||||
* @return Client
|
||||
*/
|
||||
protected function create_client(string $host, int $port = 443, int $timeout = 6): Client
|
||||
{
|
||||
// Only set URL scheme if not specified in URL
|
||||
$url_parts = parse_url($host);
|
||||
if (!isset($url_parts['scheme']))
|
||||
{
|
||||
$host = (($port === 443) ? 'https://' : 'http://') . $host;
|
||||
}
|
||||
|
||||
// Initialize Guzzle client
|
||||
return new Client([
|
||||
'base_uri' => $host,
|
||||
'timeout' => $timeout,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve contents from remotely stored file
|
||||
*
|
||||
* @param string $host File host
|
||||
* @param string $directory Directory file is in
|
||||
* @param string $filename Filename of file to retrieve
|
||||
* @param int $port Port to connect to; default: 80
|
||||
* @param int $timeout Connection timeout in seconds; default: 6
|
||||
* @param string $host File host
|
||||
* @param string $directory Directory file is in
|
||||
* @param string $filename Filename of file to retrieve
|
||||
* @param int $port Port to connect to; default: 443
|
||||
* @param int $timeout Connection timeout in seconds; default: 6
|
||||
*
|
||||
* @return mixed File data as string if file can be read and there is no
|
||||
* timeout, false if there were errors or the connection timed out
|
||||
* @return false|string File data as string if file can be read and there is no
|
||||
* timeout, false if there were errors or the connection timed out
|
||||
*
|
||||
* @throws \phpbb\exception\runtime_exception If data can't be retrieved and no error
|
||||
* message is returned
|
||||
* @throws runtime_exception If data can't be retrieved and no error
|
||||
* message is returned
|
||||
*/
|
||||
public function get($host, $directory, $filename, $port = 80, $timeout = 6)
|
||||
public function get(string $host, string $directory, string $filename, int $port = 443, int $timeout = 6)
|
||||
{
|
||||
// Initialize Guzzle client
|
||||
$client = $this->create_client($host, $port, $timeout);
|
||||
|
||||
// Set default values for error variables
|
||||
$this->error_number = 0;
|
||||
$this->error_string = '';
|
||||
|
||||
if (function_exists('fsockopen') &&
|
||||
$socket = @fsockopen(($port == 443 ? 'ssl://' : '') . $host, $port, $this->error_number, $this->error_string, $timeout)
|
||||
)
|
||||
try
|
||||
{
|
||||
@fputs($socket, "GET $directory/$filename HTTP/1.0\r\n");
|
||||
@fputs($socket, "HOST: $host\r\n");
|
||||
@fputs($socket, "Connection: close\r\n\r\n");
|
||||
$response = $client->request('GET', "$directory/$filename");
|
||||
|
||||
$timer_stop = time() + $timeout;
|
||||
stream_set_timeout($socket, $timeout);
|
||||
|
||||
$file_info = '';
|
||||
$get_info = false;
|
||||
|
||||
while (!@feof($socket))
|
||||
// Check if the response status code is 200 (OK)
|
||||
if ($response->getStatusCode() == self::OK)
|
||||
{
|
||||
if ($get_info)
|
||||
{
|
||||
$file_info .= @fread($socket, 1024);
|
||||
}
|
||||
else
|
||||
{
|
||||
$line = @fgets($socket, 1024);
|
||||
if ($line == "\r\n")
|
||||
{
|
||||
$get_info = true;
|
||||
}
|
||||
else if (stripos($line, '404 not found') !== false)
|
||||
{
|
||||
throw new \phpbb\exception\runtime_exception('FILE_NOT_FOUND', array($filename));
|
||||
}
|
||||
}
|
||||
|
||||
$stream_meta_data = stream_get_meta_data($socket);
|
||||
|
||||
if (!empty($stream_meta_data['timed_out']) || time() >= $timer_stop)
|
||||
{
|
||||
throw new \phpbb\exception\runtime_exception('FSOCK_TIMEOUT');
|
||||
}
|
||||
}
|
||||
@fclose($socket);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($this->error_string)
|
||||
{
|
||||
$this->error_string = utf8_convert_message($this->error_string);
|
||||
return false;
|
||||
return $response->getBody()->getContents();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \phpbb\exception\runtime_exception('FSOCK_DISABLED');
|
||||
$this->error_number = $response->getStatusCode();
|
||||
throw new runtime_exception('FILE_NOT_FOUND', [$filename]);
|
||||
}
|
||||
}
|
||||
catch (RequestException $exception)
|
||||
{
|
||||
if ($exception->hasResponse())
|
||||
{
|
||||
$this->error_number = $exception->getResponse()->getStatusCode();
|
||||
|
||||
return $file_info;
|
||||
if ($this->error_number == self::NOT_FOUND)
|
||||
{
|
||||
throw new runtime_exception('FILE_NOT_FOUND', [$filename]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->error_number = self::REQUEST_TIMEOUT;
|
||||
throw new runtime_exception('FSOCK_TIMEOUT');
|
||||
}
|
||||
|
||||
$this->error_string = utf8_convert_message($exception->getMessage());
|
||||
return false;
|
||||
}
|
||||
catch (runtime_exception $exception)
|
||||
{
|
||||
// Rethrow runtime_exceptions, only handle unknown cases below
|
||||
throw $exception;
|
||||
}
|
||||
catch (\Throwable $exception)
|
||||
{
|
||||
$this->error_string = utf8_convert_message($exception->getMessage());
|
||||
throw new runtime_exception('FSOCK_DISABLED');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,7 +130,7 @@ class file_downloader
|
||||
*
|
||||
* @return string Error string
|
||||
*/
|
||||
public function get_error_string()
|
||||
public function get_error_string(): string
|
||||
{
|
||||
return $this->error_string;
|
||||
}
|
||||
@@ -115,7 +140,7 @@ class file_downloader
|
||||
*
|
||||
* @return int Error number
|
||||
*/
|
||||
public function get_error_number()
|
||||
public function get_error_number(): int
|
||||
{
|
||||
return $this->error_number;
|
||||
}
|
||||
|
@@ -223,13 +223,13 @@ class path_helper
|
||||
*
|
||||
* The referer must be specified as a parameter in the query.
|
||||
*/
|
||||
if ($this->request->is_ajax() && $this->symfony_request->get('_referer'))
|
||||
if ($this->request->is_ajax() && $this->request->header('Referer'))
|
||||
{
|
||||
// We need to escape $absolute_board_url because it can be partially concatenated to the result.
|
||||
$absolute_board_url = $this->request->escape($this->symfony_request->getSchemeAndHttpHost() . $this->symfony_request->getBasePath(), true);
|
||||
|
||||
$referer_web_root_path = $this->get_web_root_path_from_ajax_referer(
|
||||
$this->symfony_request->get('_referer'),
|
||||
$this->request->header('Referer'),
|
||||
$absolute_board_url
|
||||
);
|
||||
return $this->web_root_path = $referer_web_root_path;
|
||||
|
@@ -216,11 +216,11 @@ abstract class base implements reparser_interface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reparse_range($min_id, $max_id)
|
||||
public function reparse_range($min_id, $max_id, bool $force_bbcode_reparsing = false)
|
||||
{
|
||||
foreach ($this->get_records_by_range($min_id, $max_id) as $record)
|
||||
{
|
||||
$this->reparse_record($record);
|
||||
$this->reparse_record($record, $force_bbcode_reparsing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,16 +228,17 @@ abstract class base implements reparser_interface
|
||||
* Reparse given record
|
||||
*
|
||||
* @param array $record Associative array containing the record's data
|
||||
* @param bool $force_bbcode_reparsing Flag indicating if BBCode should be reparsed unconditionally
|
||||
*/
|
||||
protected function reparse_record(array $record)
|
||||
protected function reparse_record(array $record, bool $force_bbcode_reparsing = false)
|
||||
{
|
||||
// Guess magic URL state based on actual record content before adding fields
|
||||
$record['enable_magic_url'] = $this->guess_magic_url($record);
|
||||
$record = $this->add_missing_fields($record);
|
||||
|
||||
$flags = ($record['enable_bbcode']) ? OPTION_FLAG_BBCODE : 0;
|
||||
$flags |= ($record['enable_smilies']) ? OPTION_FLAG_SMILIES : 0;
|
||||
$flags |= ($record['enable_magic_url']) ? OPTION_FLAG_LINKS : 0;
|
||||
$flags = ($record['enable_bbcode'] || $force_bbcode_reparsing) ? OPTION_FLAG_BBCODE : 0;
|
||||
$flags |= ($record['enable_smilies'] || $force_bbcode_reparsing) ? OPTION_FLAG_SMILIES : 0;
|
||||
$flags |= ($record['enable_magic_url'] || $force_bbcode_reparsing) ? OPTION_FLAG_LINKS : 0;
|
||||
$unparsed = array_merge(
|
||||
$record,
|
||||
generate_text_for_edit($record['text'], $record['bbcode_uid'], $flags)
|
||||
@@ -252,13 +253,13 @@ abstract class base implements reparser_interface
|
||||
$unparsed['bbcode_uid'],
|
||||
$bitfield,
|
||||
$flags,
|
||||
$unparsed['enable_bbcode'],
|
||||
$unparsed['enable_magic_url'],
|
||||
$unparsed['enable_smilies'],
|
||||
$unparsed['enable_img_bbcode'],
|
||||
$unparsed['enable_bbcode'] || $force_bbcode_reparsing,
|
||||
$unparsed['enable_magic_url'] || $force_bbcode_reparsing,
|
||||
$unparsed['enable_smilies'] || $force_bbcode_reparsing,
|
||||
$unparsed['enable_img_bbcode'] || $force_bbcode_reparsing,
|
||||
$unparsed['enable_flash_bbcode'],
|
||||
$unparsed['enable_quote_bbcode'],
|
||||
$unparsed['enable_url_bbcode'],
|
||||
$unparsed['enable_quote_bbcode'] || $force_bbcode_reparsing,
|
||||
$unparsed['enable_url_bbcode'] || $force_bbcode_reparsing,
|
||||
'text_reparser.' . $this->get_name()
|
||||
);
|
||||
|
||||
|
@@ -41,6 +41,7 @@ interface reparser_interface
|
||||
*
|
||||
* @param integer $min_id Lower bound
|
||||
* @param integer $max_id Upper bound
|
||||
* @param bool $force_bbcode_reparsing Flag indicating if BBCode should be reparsed unconditionally
|
||||
*/
|
||||
public function reparse_range($min_id, $max_id);
|
||||
public function reparse_range($min_id, $max_id, bool $force_bbcode_reparsing = false);
|
||||
}
|
||||
|
@@ -90,8 +90,21 @@ switch ($search_id)
|
||||
break;
|
||||
}
|
||||
|
||||
$search_auth_check_override = false;
|
||||
/**
|
||||
* This event allows you to override search auth checks
|
||||
*
|
||||
* @event core.search_auth_check_override
|
||||
* @var bool search_auth_check_override Whether or not the search auth check overridden
|
||||
* @since 3.3.14-RC1
|
||||
*/
|
||||
$vars = [
|
||||
'search_auth_check_override',
|
||||
];
|
||||
extract($phpbb_dispatcher->trigger_event('core.search_auth_check_override', compact($vars)));
|
||||
|
||||
// Is user able to search? Has search been disabled?
|
||||
if (!$auth->acl_get('u_search') || !$auth->acl_getf_global('f_search') || !$config['load_search'])
|
||||
if (!$search_auth_check_override && (!$auth->acl_get('u_search') || !$auth->acl_getf_global('f_search') || !$config['load_search']))
|
||||
{
|
||||
$template->assign_var('S_NO_SEARCH', true);
|
||||
trigger_error('NO_SEARCH');
|
||||
|
@@ -21,8 +21,8 @@
|
||||
# General Information about this style
|
||||
name = prosilver
|
||||
copyright = © phpBB Limited, 2007
|
||||
style_version = 3.3.13
|
||||
phpbb_version = 3.3.13
|
||||
style_version = 3.3.14
|
||||
phpbb_version = 3.3.14
|
||||
|
||||
# Defining a different template bitfield
|
||||
# template_bitfield = //g=
|
||||
|
@@ -38,11 +38,11 @@
|
||||
<xsl:value-of select="$L_COLON"/>
|
||||
<xsl:if test="@post_url">
|
||||
<xsl:text> </xsl:text>
|
||||
<a href="{@post_url}" data-post-id="{@post_id}" onclick="if(document.getElementById(hash.substr(1)))href=hash">↑</a>
|
||||
<a href="{@post_url}" aria-label="{L_VIEW_QUOTED_POST}" data-post-id="{@post_id}" onclick="if(document.getElementById(hash.substr(1)))href=hash"><i class="icon fa-arrow-circle-up fa-fw" aria-hidden="true"></i></a>
|
||||
</xsl:if>
|
||||
<xsl:if test="@msg_url">
|
||||
<xsl:text> </xsl:text>
|
||||
<a href="{@msg_url}" data-msg-id="{@msg_id}">↑</a>
|
||||
<a href="{@msg_url}" aria-label="{L_VIEW_QUOTED_POST}" data-msg-id="{@msg_id}"><i class="icon fa-arrow-circle-up fa-fw" aria-hidden="true"></i></a>
|
||||
</xsl:if>
|
||||
<xsl:if test="@date">
|
||||
<span class="responsive-hide"><xsl:value-of select="@date"/></span>
|
||||
|
@@ -22,7 +22,7 @@
|
||||
<!-- ENDIF -->
|
||||
|
||||
|
||||
<div id="post-{MESSAGE_ID}" class="post pm has-profile<!-- IF S_POST_UNAPPROVED or S_POST_REPORTED --> reported<!-- ENDIF --><!-- IF S_ONLINE --> online<!-- ENDIF -->">
|
||||
<div id="post-{MESSAGE_ID}" class="post pm has-profile<!-- IF S_ONLINE --> online<!-- ENDIF -->">
|
||||
<div class="inner">
|
||||
|
||||
<dl class="postprofile" id="profile{MESSAGE_ID}">
|
||||
|
@@ -47,27 +47,45 @@ if (!$forum_id)
|
||||
trigger_error('NO_FORUM');
|
||||
}
|
||||
|
||||
$sql_from = FORUMS_TABLE . ' f';
|
||||
$sql_ary = [
|
||||
'SELECT' => 'f.*',
|
||||
'FROM' => [
|
||||
FORUMS_TABLE => 'f',
|
||||
],
|
||||
'WHERE' => 'f.forum_id = ' . $forum_id,
|
||||
];
|
||||
|
||||
$lastread_select = '';
|
||||
|
||||
// Grab appropriate forum data
|
||||
if ($config['load_db_lastread'] && $user->data['is_registered'])
|
||||
{
|
||||
$sql_from .= ' LEFT JOIN ' . FORUMS_TRACK_TABLE . ' ft ON (ft.user_id = ' . $user->data['user_id'] . '
|
||||
AND ft.forum_id = f.forum_id)';
|
||||
$lastread_select .= ', ft.mark_time';
|
||||
$sql_ary['LEFT_JOIN'][] = [
|
||||
'FROM' => [FORUMS_TRACK_TABLE => 'ft'],
|
||||
'ON' => 'ft.user_id = ' . $user->data['user_id'] . ' AND ft.forum_id = f.forum_id',
|
||||
];
|
||||
$sql_ary['SELECT'] .= ', ft.mark_time';
|
||||
}
|
||||
|
||||
if ($user->data['is_registered'])
|
||||
{
|
||||
$sql_from .= ' LEFT JOIN ' . FORUMS_WATCH_TABLE . ' fw ON (fw.forum_id = f.forum_id AND fw.user_id = ' . $user->data['user_id'] . ')';
|
||||
$lastread_select .= ', fw.notify_status';
|
||||
$sql_ary['LEFT_JOIN'][] = [
|
||||
'FROM' => [FORUMS_WATCH_TABLE => 'fw'],
|
||||
'ON' => 'fw.forum_id = f.forum_id AND fw.user_id = ' . $user->data['user_id'],
|
||||
];
|
||||
$sql_ary['SELECT'] .= ', fw.notify_status';
|
||||
}
|
||||
|
||||
$sql = "SELECT f.* $lastread_select
|
||||
FROM $sql_from
|
||||
WHERE f.forum_id = $forum_id";
|
||||
$result = $db->sql_query($sql);
|
||||
/**
|
||||
* You can use this event to modify the sql that selects the forum on the viewforum page.
|
||||
*
|
||||
* @event core.viewforum_modify_sql
|
||||
* @var array sql_ary The SQL array to get the data for a forum
|
||||
* @since 3.3.14-RC1
|
||||
*/
|
||||
$vars = ['sql_ary'];
|
||||
extract($phpbb_dispatcher->trigger_event('core.viewforum_modify_sql', compact($vars)));
|
||||
$result = $db->sql_query($db->sql_build_query('SELECT', $sql_ary));
|
||||
$forum_data = $db->sql_fetchrow($result);
|
||||
$db->sql_freeresult($result);
|
||||
|
||||
|
@@ -123,4 +123,20 @@ class phpbb_functional_acp_groups_test extends phpbb_functional_common_groups_te
|
||||
$this->assertEquals((bool) $tick_teampage, (bool) ($this->form_data['group_teampage'] ?? false));
|
||||
}
|
||||
}
|
||||
|
||||
public function test_acp_groups_create_existing_name()
|
||||
{
|
||||
$this->group_manage_login();
|
||||
|
||||
$crawler = self::request('GET', 'adm/index.php?i=groups&mode=manage&sid=' . $this->sid);
|
||||
$form = $crawler->selectButton($this->lang('SUBMIT'))->form([
|
||||
'group_name' => 'Guests', // 'Guests' is the group name already in use for predefined Guests group
|
||||
]);
|
||||
|
||||
$crawler = self::submit($form);
|
||||
$form = $crawler->selectButton($this->lang('SUBMIT'))->form();
|
||||
$crawler = self::submit($form); // Just submit the form with selected group name
|
||||
|
||||
$this->assertStringContainsString($this->lang('GROUP_NAME_TAKEN'), $crawler->text());
|
||||
}
|
||||
}
|
||||
|
@@ -149,4 +149,26 @@ class phpbb_functional_acp_permissions_test extends phpbb_functional_test_case
|
||||
$this->assertContainsLang('ACL_M_EDIT', $page_text);
|
||||
$this->assertContainsLang('ACL_M_MOVE', $page_text);
|
||||
}
|
||||
|
||||
public function test_tracing_user_based_permissions()
|
||||
{
|
||||
$this->create_user('newlyregistereduser');
|
||||
|
||||
// Open user-based permissions masks page
|
||||
$crawler = self::request('GET', "adm/index.php?i=acp_permissions&icat=16&mode=view_user_global&sid=" . $this->sid);
|
||||
|
||||
// Select newlyregistereduser
|
||||
$form = $crawler->filter('#add_user')->form(['username' => ['newlyregistereduser']]);
|
||||
$crawler = self::submit($form);
|
||||
|
||||
// Test 1st "Yes" permission tracing result match
|
||||
$trace_link_yes = $crawler->filter('td.yes')->eq(0)->siblings()->filter('th > a.trace')->link();
|
||||
$crawler_trace_yes = self::$client->click($trace_link_yes);
|
||||
$this->assertEquals(1, $crawler_trace_yes->filter('tr.row2 > td.yes')->count());
|
||||
|
||||
// Test 1st "Never" permission tracing result match
|
||||
$trace_link_never = $crawler->filter('td.never')->eq(0)->siblings()->filter('th > a.trace')->link();
|
||||
$crawler_trace_never = self::$client->click($trace_link_never);
|
||||
$this->assertEquals(1, $crawler_trace_never->filter('tr.row2 > td.never')->count());
|
||||
}
|
||||
}
|
||||
|
@@ -84,7 +84,8 @@ class phpbb_functional_extension_acp_test extends phpbb_functional_test_case
|
||||
$crawler = self::request('GET', 'adm/index.php?i=acp_extensions&mode=main&sid=' . $this->sid);
|
||||
|
||||
$this->assertCount(1, $crawler->filter('.ext_enabled'));
|
||||
$this->assertCount(7, $crawler->filter('.ext_disabled'));
|
||||
$this->assertCount(3, $crawler->filter('.ext_disabled'));
|
||||
$this->assertCount(4, $crawler->filter('.ext_not_installed'));
|
||||
|
||||
$this->assertStringContainsString('phpBB Foo Extension', $crawler->filter('.ext_enabled')->eq(0)->text());
|
||||
$this->assertContainsLang('EXTENSION_DISABLE', $crawler->filter('.ext_enabled')->eq(0)->text());
|
||||
@@ -98,9 +99,9 @@ class phpbb_functional_extension_acp_test extends phpbb_functional_test_case
|
||||
|
||||
$this->assertStringContainsString('The “vendor/test3” extension is not valid.', $crawler->filter('.ext_disabled')->eq(1)->text());
|
||||
|
||||
$this->assertStringContainsString('phpBB Bar Extension', $crawler->filter('.ext_disabled')->eq(3)->text());
|
||||
$this->assertContainsLang('DETAILS', $crawler->filter('.ext_disabled')->eq(3)->text());
|
||||
$this->assertContainsLang('EXTENSION_ENABLE', $crawler->filter('.ext_disabled')->eq(3)->text());
|
||||
$this->assertStringContainsString('phpBB Bar Extension', $crawler->filter('.ext_not_installed')->eq(0)->text());
|
||||
$this->assertContainsLang('DETAILS', $crawler->filter('.ext_not_installed')->eq(0)->text());
|
||||
$this->assertContainsLang('EXTENSION_ENABLE', $crawler->filter('.ext_not_installed')->eq(0)->text());
|
||||
|
||||
// Check that invalid extensions are not listed.
|
||||
$this->assertStringNotContainsString('phpBB BarFoo Extension', $crawler->filter('.table1')->text());
|
||||
|
@@ -28,7 +28,7 @@ class phpbb_functional_switch_permissions_test extends phpbb_functional_test_cas
|
||||
$this->add_lang(['common', 'ucp']);
|
||||
}
|
||||
|
||||
public function test_switch_permissions()
|
||||
public function test_switch_permissions_acp()
|
||||
{
|
||||
$user_id = $this->create_user(self::TEST_USER);
|
||||
|
||||
@@ -67,4 +67,53 @@ class phpbb_functional_switch_permissions_test extends phpbb_functional_test_cas
|
||||
$crawler->text()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends test_switch_permissions_acp
|
||||
*/
|
||||
public function test_switch_permissions_ucp()
|
||||
{
|
||||
$db = $this->get_db();
|
||||
$sql = 'SELECT user_id
|
||||
FROM ' . USERS_TABLE . "
|
||||
WHERE username = '" . self::TEST_USER . "'";
|
||||
$result = $db->sql_query($sql);
|
||||
$user_id = $db->sql_fetchfield('user_id');
|
||||
$db->sql_freeresult($result);
|
||||
|
||||
// Open memberlist profile page for user
|
||||
$crawler = self::request('GET', "memberlist.php?mode=viewprofile&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 UCP pages don't get forced to UCP main with restore permission info
|
||||
$this->add_lang(['memberlist', 'ucp']);
|
||||
$crawler = self::request('GET', "ucp.php?i=ucp_profile&mode=profile_info&sid={$this->sid}");
|
||||
$this->assertStringContainsString(
|
||||
$this->lang('EDIT_PROFILE'),
|
||||
$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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -81,13 +81,13 @@ class phpbb_functions_content_get_context_test extends TestCase
|
||||
'text' => 'This is a sample text.',
|
||||
'words' => ['sample'],
|
||||
'length' => 0,
|
||||
'expected' => '...',
|
||||
'expected' => 'This is a sample text.',
|
||||
],
|
||||
'negative length' => [
|
||||
'text' => 'This is a sample text.',
|
||||
'words' => ['sample'],
|
||||
'length' => -10,
|
||||
'expected' => '...',
|
||||
'expected' => 'This is a sample text.',
|
||||
],
|
||||
'ellipses_beginning' => [
|
||||
'text' => 'foo foo foo foo foo foo foo foo bar',
|
||||
@@ -112,12 +112,120 @@ class phpbb_functions_content_get_context_test extends TestCase
|
||||
'words' => ['word1', 'word2'],
|
||||
'length' => 10,
|
||||
'expected' => 'word1 ... word2',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for unicode get_context test cases.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data_get_context_unicode(): array
|
||||
{
|
||||
return [
|
||||
'text contains words and length greater than text' => [
|
||||
'text' => 'Это пример текста, содержащего разнообразные слова, включая пример, текст и слова.',
|
||||
'words' => ['пример', 'слова'],
|
||||
'length' => 100,
|
||||
'expected' => 'Это пример текста, содержащего разнообразные слова, включая пример, текст и слова.',
|
||||
],
|
||||
'text contains words and length less than text' => [
|
||||
'text' => 'Это пример текста, содержащего разнообразные слова, включая шаблон, текст и слова.',
|
||||
'words' => ['пример', 'слова'],
|
||||
'length' => 50,
|
||||
'expected' => 'Это пример текста, содержащего разнообразные слова ...',
|
||||
],
|
||||
'text does not contain words' => [
|
||||
'text' => 'Это пример текста, содержащего разнообразные слова, но ни одно из них не совпадает с искомыми.',
|
||||
'words' => ['nonexistent'],
|
||||
'length' => 50,
|
||||
'expected' => 'Это пример текста, содержащего разнообразные слова ...',
|
||||
],
|
||||
'desired length equal to text length' => [
|
||||
'text' => 'Текст точной длины.',
|
||||
'words' => ['Текст', 'точной'],
|
||||
'length' => 19,
|
||||
'expected' => 'Текст точной длины.',
|
||||
],
|
||||
'text with html entities' => [
|
||||
'text' => 'Это пример текста, содержащего & и < и > лексемы.',
|
||||
'words' => ['пример', 'содержащего'],
|
||||
'length' => 40,
|
||||
'expected' => 'Это пример текста, содержащего & и < и ...',
|
||||
],
|
||||
'text with html entities and contains last word' => [
|
||||
'text' => 'Это пример текста, содержащего & и < и > лексемы.',
|
||||
'words' => ['пример', 'лексемы'],
|
||||
'length' => 40,
|
||||
'expected' => 'Это пример текста ... и < и > лексемы.',
|
||||
],
|
||||
'text with multiple spaces and special characters' => [
|
||||
'text' => 'Это пример текста, содержащего разнообразные слова.',
|
||||
'words' => ['пример', 'разнообразные'],
|
||||
'length' => 50,
|
||||
'expected' => 'Это пример текста, содержащего разнообразные слова.',
|
||||
],
|
||||
'empty text' => [
|
||||
'text' => '',
|
||||
'words' => ['пример', 'слова'],
|
||||
'length' => 50,
|
||||
'expected' => '',
|
||||
],
|
||||
'empty words array' => [
|
||||
'text' => 'Это пример текста, содержащего разнообразные слова.',
|
||||
'words' => [],
|
||||
'length' => 50,
|
||||
'expected' => 'Это пример текста, содержащего разнообразные слова.',
|
||||
],
|
||||
'zero length' => [
|
||||
'text' => 'Это пример текста.',
|
||||
'words' => ['пример'],
|
||||
'length' => 0,
|
||||
'expected' => 'Это пример текста.',
|
||||
],
|
||||
'negative length' => [
|
||||
'text' => 'Это пример текста.',
|
||||
'words' => ['sample'],
|
||||
'length' => -10,
|
||||
'expected' => 'Это пример текста.',
|
||||
],
|
||||
'ellipses_beginning' => [
|
||||
'text' => 'раз раз раз раз раз раз раз раз два',
|
||||
'words' => ['два'],
|
||||
'length' => 10,
|
||||
'expected' => '... раз раз два',
|
||||
],
|
||||
'ellipsis_end' => [
|
||||
'text' => 'два раз раз раз раз раз раз раз раз',
|
||||
'words' => ['два'],
|
||||
'length' => 10,
|
||||
'expected' => 'два раз раз ...',
|
||||
],
|
||||
'ellipsis_middle' => [
|
||||
'text' => 'раз слово1 раз раз раз раз раз раз раз раз раз слово2 раз',
|
||||
'words' => ['слово1', 'слово2'],
|
||||
'length' => 15,
|
||||
'expected' => '... слово1 ... слово2 ...',
|
||||
],
|
||||
'ellipsis_middle2' => [
|
||||
'text' => 'слово1 foo foo foo foo foo foo foo foo foo слово2',
|
||||
'words' => ['слово1', 'слово2'],
|
||||
'length' => 10,
|
||||
'expected' => 'слово1 ... слово2',
|
||||
],
|
||||
'fruits_spanish' => [
|
||||
'text' => 'Manzana,plátano,naranja,fresa,mango,uva,piña,pera,kiwi,cereza,sandía,melón,papaya,arándano,durazno',
|
||||
'words' => ['piña'],
|
||||
'length' => 20,
|
||||
'expected' => '... uva,piña,pera ...',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider data_get_context
|
||||
* @dataProvider data_get_context_unicode
|
||||
*/
|
||||
public function test_get_context($text, $words, $length, $expected)
|
||||
{
|
||||
|
@@ -470,9 +470,6 @@ class phpbb_path_helper_test extends phpbb_test_case
|
||||
->setConstructorArgs([new phpbb_mock_request()])
|
||||
->setMethods(['get', 'getSchemeAndHttpHost', 'getBasePath', 'getPathInfo'])
|
||||
->getMock();
|
||||
$symfony_request->method('get')
|
||||
->with('_referer')
|
||||
->willReturn('http://www.phpbb.com/community/route1/route2/');
|
||||
$symfony_request->method('getSchemeAndHttpHost')
|
||||
->willReturn('http://www.phpbb.com');
|
||||
$symfony_request->method('getBasePath')
|
||||
@@ -486,6 +483,9 @@ class phpbb_path_helper_test extends phpbb_test_case
|
||||
->willReturn(true);
|
||||
$request->method('escape')
|
||||
->willReturnArgument(0);
|
||||
$request->method('header')
|
||||
->with('Referer')
|
||||
->willReturn('http://www.phpbb.com/community/route1/route2/');
|
||||
|
||||
$path_helper = new \phpbb\path_helper(
|
||||
$symfony_request,
|
||||
|
@@ -283,7 +283,7 @@ class phpbb_textformatter_s9e_default_formatting_test extends phpbb_test_case
|
||||
),
|
||||
array(
|
||||
'[quote=Username post_id=123]...[/quote]',
|
||||
'<blockquote cite="phpBB/viewtopic.php?p=123#p123"><div><cite>Username wrote: <a href="phpBB/viewtopic.php?p=123#p123" data-post-id="123" onclick="if(document.getElementById(hash.substr(1)))href=hash">↑</a></cite>...</div></blockquote>'
|
||||
'<blockquote cite="phpBB/viewtopic.php?p=123#p123"><div><cite>Username wrote: <a href="phpBB/viewtopic.php?p=123#p123" aria-label="VIEW_QUOTED_POST" data-post-id="123" onclick="if(document.getElementById(hash.substr(1)))href=hash"><i class="icon fa-arrow-circle-up fa-fw" aria-hidden="true"></i></a></cite>...</div></blockquote>'
|
||||
),
|
||||
array(
|
||||
// Users are not allowed to submit their own URL for the post
|
||||
|
4
tests/version/fixture/30x.txt
Normal file
4
tests/version/fixture/30x.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
3.0.14
|
||||
https://www.phpbb.com/community/viewtopic.php?f=14&t=2313941
|
||||
3.3.12
|
||||
https://www.phpbb.com/community/viewtopic.php?t=2653732
|
@@ -1,4 +1,7 @@
|
||||
<?php
|
||||
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
@@ -17,6 +20,11 @@ class version_helper_remote_test extends \phpbb_test_case
|
||||
protected $cache;
|
||||
protected $version_helper;
|
||||
|
||||
// Guzzle mock data
|
||||
protected $guzzle_status = 200; // Default to 200 status
|
||||
protected $guzzle_data;
|
||||
protected $guzzle_mock;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
@@ -38,9 +46,30 @@ class version_helper_remote_test extends \phpbb_test_case
|
||||
|
||||
$this->cache->expects($this->any())
|
||||
->method('get')
|
||||
->with($this->anything())
|
||||
->withAnyParameters()
|
||||
->will($this->returnValue(false));
|
||||
$this->file_downloader = new phpbb_mock_file_downloader();
|
||||
|
||||
$this->guzzle_mock = $this->getMockBuilder('\GuzzleHttp\Client')
|
||||
->setMethods(['set_data', 'request'])
|
||||
->getMock();
|
||||
$this->guzzle_mock->method('set_data')
|
||||
->will($this->returnCallback(function($data)
|
||||
{
|
||||
$this->guzzle_data = $data;
|
||||
}
|
||||
));
|
||||
$this->guzzle_mock->method('request')
|
||||
->will($this->returnCallback(function()
|
||||
{
|
||||
return new \GuzzleHttp\Psr7\Response($this->guzzle_status, [], $this->guzzle_data);
|
||||
}
|
||||
));
|
||||
|
||||
$this->file_downloader = $this->getMockBuilder('\phpbb\file_downloader')
|
||||
->setMethods(['create_client'])
|
||||
->getMock();
|
||||
$this->file_downloader->method('create_client')
|
||||
->will($this->returnValue($this->guzzle_mock));
|
||||
|
||||
$lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx);
|
||||
|
||||
@@ -202,7 +231,7 @@ class version_helper_remote_test extends \phpbb_test_case
|
||||
*/
|
||||
public function test_get_versions($input, $valid_data, $expected_return = '', $expected_exception = '')
|
||||
{
|
||||
$this->file_downloader->set($input);
|
||||
$this->guzzle_mock->set_data($input);
|
||||
|
||||
// version_helper->get_versions() doesn't return a value on VERSIONCHECK_FAIL but only throws exception
|
||||
// so the $return is undefined. Define it here
|
||||
@@ -213,7 +242,7 @@ class version_helper_remote_test extends \phpbb_test_case
|
||||
try {
|
||||
$return = $this->version_helper->get_versions();
|
||||
} catch (\phpbb\exception\runtime_exception $e) {
|
||||
$this->assertEquals((string)$e->getMessage(), $expected_exception);
|
||||
$this->assertEquals($expected_exception, $e->getMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -223,4 +252,206 @@ class version_helper_remote_test extends \phpbb_test_case
|
||||
|
||||
$this->assertEquals($expected_return, $return);
|
||||
}
|
||||
|
||||
public function test_version_phpbb_com()
|
||||
{
|
||||
$guzzle_mock = $this->getMockBuilder('\GuzzleHttp\Client')
|
||||
->setMethods(['request'])
|
||||
->getMock();
|
||||
|
||||
$guzzle_mock->method('request')
|
||||
->will($this->returnCallback(function()
|
||||
{
|
||||
return new \GuzzleHttp\Psr7\Response(200, [], file_get_contents(__DIR__ . '/fixture/30x.txt'));
|
||||
}
|
||||
));
|
||||
|
||||
$file_downloader = $this->getMockBuilder(\phpbb\file_downloader::class)
|
||||
->setMethods(['create_client'])
|
||||
->getMock();
|
||||
|
||||
$file_downloader->method('create_client')
|
||||
->willReturn($guzzle_mock);
|
||||
|
||||
$hostname = 'version.phpbb.com';
|
||||
|
||||
$file = $file_downloader->get($hostname, '/phpbb', '30x.txt');
|
||||
$errstr = $file_downloader->get_error_string();
|
||||
$errno = $file_downloader->get_error_number();
|
||||
|
||||
$this->assertNotEquals(
|
||||
0,
|
||||
strlen($file),
|
||||
'Failed asserting that the response is not empty.'
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
'',
|
||||
$errstr,
|
||||
'Failed asserting that the error string is empty.'
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
0,
|
||||
$errno,
|
||||
'Failed asserting that the error number is 0 (i.e. no error occurred).'
|
||||
);
|
||||
|
||||
$lines = explode("\n", $file);
|
||||
|
||||
$this->assertGreaterThanOrEqual(
|
||||
2,
|
||||
count($lines),
|
||||
'Failed asserting that the version file has at least two lines.'
|
||||
);
|
||||
|
||||
$this->assertStringStartsWith(
|
||||
'3.',
|
||||
$lines[0],
|
||||
"Failed asserting that the first line of the version file starts with '3.'"
|
||||
);
|
||||
|
||||
$this->assertNotSame(
|
||||
false,
|
||||
filter_var($lines[1], FILTER_VALIDATE_URL),
|
||||
'Failed asserting that the second line of the version file is a valid URL.'
|
||||
);
|
||||
|
||||
$this->assertStringContainsString('http', $lines[1]);
|
||||
$this->assertStringContainsString('phpbb.com', $lines[1], '', true);
|
||||
}
|
||||
|
||||
public function test_file_downloader_file_not_found()
|
||||
{
|
||||
$this->guzzle_mock = $this->getMockBuilder('\GuzzleHttp\Client')
|
||||
->setMethods(['request'])
|
||||
->getMock();
|
||||
|
||||
$this->guzzle_mock->method('request')
|
||||
->will($this->returnCallback(function()
|
||||
{
|
||||
return new \GuzzleHttp\Psr7\Response(404, [], '');
|
||||
}
|
||||
));
|
||||
|
||||
$file_downloader = $this->getMockBuilder(\phpbb\file_downloader::class)
|
||||
->setMethods(['create_client'])
|
||||
->getMock();
|
||||
|
||||
$file_downloader->method('create_client')
|
||||
->willReturn($this->guzzle_mock);
|
||||
|
||||
$this->expectException(\phpbb\exception\runtime_exception::class);
|
||||
$this->expectExceptionMessage('FILE_NOT_FOUND');
|
||||
|
||||
$file_downloader->get('foo.com', 'bar', 'foo.txt');
|
||||
}
|
||||
|
||||
public function test_file_downloader_exception_not_found()
|
||||
{
|
||||
$this->guzzle_mock = $this->getMockBuilder('\GuzzleHttp\Client')
|
||||
->setMethods(['request'])
|
||||
->getMock();
|
||||
|
||||
$this->guzzle_mock->method('request')
|
||||
->will($this->returnCallback(function($method, $uri)
|
||||
{
|
||||
$request = new \GuzzleHttp\Psr7\Request('GET', $uri);
|
||||
$response = new \GuzzleHttp\Psr7\Response(404, [], '');
|
||||
throw new RequestException('FILE_NOT_FOUND', $request, $response);
|
||||
}
|
||||
));
|
||||
|
||||
$file_downloader = $this->getMockBuilder(\phpbb\file_downloader::class)
|
||||
->setMethods(['create_client'])
|
||||
->getMock();
|
||||
|
||||
$file_downloader->method('create_client')
|
||||
->willReturn($this->guzzle_mock);
|
||||
|
||||
$this->expectException(\phpbb\exception\runtime_exception::class);
|
||||
$this->expectExceptionMessage('FILE_NOT_FOUND');
|
||||
|
||||
$file_downloader->get('foo.com', 'bar', 'foo.txt');
|
||||
}
|
||||
|
||||
public function test_file_downloader_exception_moved()
|
||||
{
|
||||
$this->guzzle_mock = $this->getMockBuilder('\GuzzleHttp\Client')
|
||||
->setMethods(['request'])
|
||||
->getMock();
|
||||
|
||||
$this->guzzle_mock->method('request')
|
||||
->will($this->returnCallback(function($method, $uri)
|
||||
{
|
||||
$request = new \GuzzleHttp\Psr7\Request('GET', $uri);
|
||||
$response = new \GuzzleHttp\Psr7\Response(302, [], '');
|
||||
throw new RequestException('FILE_MOVED', $request, $response);
|
||||
}
|
||||
));
|
||||
|
||||
$file_downloader = $this->getMockBuilder(\phpbb\file_downloader::class)
|
||||
->setMethods(['create_client'])
|
||||
->getMock();
|
||||
|
||||
$file_downloader->method('create_client')
|
||||
->willReturn($this->guzzle_mock);
|
||||
|
||||
$this->assertFalse($file_downloader->get('foo.com', 'bar', 'foo.txt'));
|
||||
$this->assertEquals(302, $file_downloader->get_error_number());
|
||||
$this->assertEquals('FILE_MOVED', $file_downloader->get_error_string());
|
||||
}
|
||||
|
||||
public function test_file_downloader_exception_timeout()
|
||||
{
|
||||
$this->guzzle_mock = $this->getMockBuilder('\GuzzleHttp\Client')
|
||||
->setMethods(['request'])
|
||||
->getMock();
|
||||
|
||||
$this->guzzle_mock->method('request')
|
||||
->will($this->returnCallback(function($method, $uri)
|
||||
{
|
||||
$request = new \GuzzleHttp\Psr7\Request('GET', $uri);
|
||||
throw new RequestException('FILE_NOT_FOUND', $request);
|
||||
}
|
||||
));
|
||||
|
||||
$file_downloader = $this->getMockBuilder(\phpbb\file_downloader::class)
|
||||
->setMethods(['create_client'])
|
||||
->getMock();
|
||||
|
||||
$file_downloader->method('create_client')
|
||||
->willReturn($this->guzzle_mock);
|
||||
|
||||
$this->expectException(\phpbb\exception\runtime_exception::class);
|
||||
$this->expectExceptionMessage('FSOCK_TIMEOUT');
|
||||
|
||||
$file_downloader->get('foo.com', 'bar', 'foo.txt');
|
||||
}
|
||||
|
||||
public function test_file_downloader_exception_other()
|
||||
{
|
||||
$this->guzzle_mock = $this->getMockBuilder('\GuzzleHttp\Client')
|
||||
->setMethods(['request'])
|
||||
->getMock();
|
||||
|
||||
$this->guzzle_mock->method('request')
|
||||
->will($this->returnCallback(function($method, $uri)
|
||||
{
|
||||
throw new \RuntimeException('FSOCK_NOT_SUPPORTED');
|
||||
}
|
||||
));
|
||||
|
||||
$file_downloader = $this->getMockBuilder(\phpbb\file_downloader::class)
|
||||
->setMethods(['create_client'])
|
||||
->getMock();
|
||||
|
||||
$file_downloader->method('create_client')
|
||||
->willReturn($this->guzzle_mock);
|
||||
|
||||
$this->expectException(\phpbb\exception\runtime_exception::class);
|
||||
$this->expectExceptionMessage('FSOCK_DISABLED');
|
||||
|
||||
$file_downloader->get('foo.com', 'bar', 'foo.txt');
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user