mirror of
https://github.com/getgrav/grav.git
synced 2025-12-05 15:29:57 +01:00
Compare commits
427 Commits
1.7.37.1
...
1.8.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc97e2ff45 | ||
|
|
d92c430b8a | ||
|
|
184cdea75d | ||
|
|
7b9567ec28 | ||
|
|
75d8356f1b | ||
|
|
c82645a42a | ||
|
|
9e84d5d004 | ||
|
|
fd0d3dc463 | ||
|
|
eb985e875d | ||
|
|
ba3493adce | ||
|
|
d785042a0d | ||
|
|
49096b61f3 | ||
|
|
70e986074c | ||
|
|
4af22edd36 | ||
|
|
5bc7d6943f | ||
|
|
8eb4085bcd | ||
|
|
b47758e3c7 | ||
|
|
972ec26035 | ||
|
|
9116079e97 | ||
|
|
51ddb3984c | ||
|
|
22de638e52 | ||
|
|
365ab93e7e | ||
|
|
c172964025 | ||
|
|
cb0bbcdb8b | ||
|
|
35f5d2f329 | ||
|
|
03849923d4 | ||
|
|
3664096550 | ||
|
|
6664d98de8 | ||
|
|
17323ec76c | ||
|
|
9b9079bdd7 | ||
|
|
db3df738e8 | ||
|
|
38075f9c86 | ||
|
|
b0dac5f4b4 | ||
|
|
f7c77d1173 | ||
|
|
0fd734c8df | ||
|
|
b642b2c999 | ||
|
|
ae147fa53b | ||
|
|
9e6df39bff | ||
|
|
8ac6076f88 | ||
|
|
cef7812472 | ||
|
|
6f461395a7 | ||
|
|
162fe1acc2 | ||
|
|
332748a1f9 | ||
|
|
1a9a60115d | ||
|
|
a55052b8b0 | ||
|
|
dc7354e7e1 | ||
|
|
afc1513aad | ||
|
|
466b2a16e8 | ||
|
|
a21640ace6 | ||
|
|
09920deabc | ||
|
|
0bd72f4bb9 | ||
|
|
ec55a80183 | ||
|
|
d07ac5b6ea | ||
|
|
fa29f6672a | ||
|
|
006d8c85a0 | ||
|
|
c608ed10cf | ||
|
|
9d71de8e54 | ||
|
|
89764a51fb | ||
|
|
b851d9bf9d | ||
|
|
a0679fc050 | ||
|
|
e497a93da6 | ||
|
|
56cc894c1d | ||
|
|
d07f3770bc | ||
|
|
3baaf19c31 | ||
|
|
7236862a15 | ||
|
|
639be5ac0d | ||
|
|
8811b7aad0 | ||
|
|
4a22f3dc8d | ||
|
|
d3e32799ab | ||
|
|
3bebfc6dac | ||
|
|
9d80a4d992 | ||
|
|
fc7f72f89d | ||
|
|
fa56984dc3 | ||
|
|
90fd68d4a5 | ||
|
|
7613e38b6d | ||
|
|
830a442faa | ||
|
|
8d7b658aa6 | ||
|
|
83d098b891 | ||
|
|
d798859acd | ||
|
|
53391466b0 | ||
|
|
2fadc14c01 | ||
|
|
08d74df6e3 | ||
|
|
de2af9e470 | ||
|
|
edfb1a0868 | ||
|
|
9893a605a6 | ||
|
|
2620e836d4 | ||
|
|
83d291c24b | ||
|
|
7e723eb7f5 | ||
|
|
1d1d8da431 | ||
|
|
ca1b05ba57 | ||
|
|
4097d85daa | ||
|
|
350e4c04cd | ||
|
|
d8123a3662 | ||
|
|
1e430f635e | ||
|
|
17548131d3 | ||
|
|
4a27bd780c | ||
|
|
2e975dfa90 | ||
|
|
5666d0f211 | ||
|
|
d17ab9e06c | ||
|
|
a15fe29f43 | ||
|
|
3126fa8388 | ||
|
|
b0c339c9eb | ||
|
|
f812ee8555 | ||
|
|
a1e583f657 | ||
|
|
8c6388bb74 | ||
|
|
d7aaef986e | ||
|
|
ab9363c478 | ||
|
|
7f2da96c0b | ||
|
|
5cf7ef864b | ||
|
|
199cdd4364 | ||
|
|
2896aea30a | ||
|
|
3952491ce9 | ||
|
|
6b07088189 | ||
|
|
b16db4b9c0 | ||
|
|
42c0682dd8 | ||
|
|
1969ec1876 | ||
|
|
c30661736c | ||
|
|
3d2dfa2faf | ||
|
|
f06bbfc563 | ||
|
|
37e5526a4f | ||
|
|
85c4b8279e | ||
|
|
46736ce256 | ||
|
|
800b2e1ecb | ||
|
|
4f065b95a7 | ||
|
|
b59a3adc80 | ||
|
|
4ec9a3a489 | ||
|
|
a0614dc3eb | ||
|
|
346d194125 | ||
|
|
173d08243a | ||
|
|
62c60b8ba1 | ||
|
|
59031a8711 | ||
|
|
5cd859865f | ||
|
|
16eafbbb04 | ||
|
|
3947bb03aa | ||
|
|
ee55d097f2 | ||
|
|
ae567469b7 | ||
|
|
f6decaab15 | ||
|
|
964e37c6f0 | ||
|
|
c55f2bed4a | ||
|
|
ecf664c8e6 | ||
|
|
10d36a10bc | ||
|
|
574a430a10 | ||
|
|
302f02ca5d | ||
|
|
c334479e4c | ||
|
|
e2257a9783 | ||
|
|
6fa63197a0 | ||
|
|
f686e0ac64 | ||
|
|
b96483c49a | ||
|
|
cccce836f6 | ||
|
|
5a741d9b10 | ||
|
|
16a50767dd | ||
|
|
d72fca121f | ||
|
|
bdcb77d429 | ||
|
|
e0ff168cf3 | ||
|
|
e15cc86716 | ||
|
|
dcfbd73d43 | ||
|
|
1967910789 | ||
|
|
4f1f9a7755 | ||
|
|
bbfc3b5658 | ||
|
|
106dc58329 | ||
|
|
ca1e5ebb8a | ||
|
|
6a585857b0 | ||
|
|
f81a4ca008 | ||
|
|
8c1b4448e9 | ||
|
|
f73813103d | ||
|
|
9c697f178d | ||
|
|
20ca44fd53 | ||
|
|
24cd0e133f | ||
|
|
9f4a86317b | ||
|
|
25ace6458b | ||
|
|
856a478bd6 | ||
|
|
faa8ee5fe1 | ||
|
|
785f641ea5 | ||
|
|
013ff7ee1b | ||
|
|
c97a0ffb16 | ||
|
|
51623ee0da | ||
|
|
8c941cc6d3 | ||
|
|
b6bba9eb99 | ||
|
|
77adfcb831 | ||
|
|
ee8d783d05 | ||
|
|
d184e25f05 | ||
|
|
04f9385aa8 | ||
|
|
afb5b02e57 | ||
|
|
4187a04235 | ||
|
|
26a6cb75ad | ||
|
|
37d0498e1b | ||
|
|
dd8d610ae0 | ||
|
|
b9529d0010 | ||
|
|
4149c81339 | ||
|
|
2da91d9c8b | ||
|
|
d69adcf347 | ||
|
|
45e2c27c66 | ||
|
|
f77df43d7a | ||
|
|
de1ccfa12d | ||
|
|
5928411b86 | ||
|
|
15dc7568a5 | ||
|
|
b435d2b884 | ||
|
|
dbedb60634 | ||
|
|
f9f5781af8 | ||
|
|
ad8b1b79bd | ||
|
|
cd2a7d8d98 | ||
|
|
1dc6866eab | ||
|
|
0b16401a91 | ||
|
|
78b8125eae | ||
|
|
0d7cd64d0d | ||
|
|
3ea86e1794 | ||
|
|
6df03063c8 | ||
|
|
e5990f431d | ||
|
|
b3d55ca81a | ||
|
|
a0e728b540 | ||
|
|
171a5c074c | ||
|
|
f33e89fa45 | ||
|
|
e33d71e4b9 | ||
|
|
ddbb1362dc | ||
|
|
a71403f158 | ||
|
|
88eb9f915a | ||
|
|
70e5262512 | ||
|
|
a1c116dd82 | ||
|
|
cc08da0c74 | ||
|
|
f7eab6b163 | ||
|
|
f59fa9a291 | ||
|
|
458c64086e | ||
|
|
345086538c | ||
|
|
c62e173955 | ||
|
|
1b8e267d0a | ||
|
|
eb72cb32bb | ||
|
|
4e01398545 | ||
|
|
b0dd2358f4 | ||
|
|
0c9333e60d | ||
|
|
0b53609fa0 | ||
|
|
cfa510e7f7 | ||
|
|
6d5f0ff9ba | ||
|
|
71939e18be | ||
|
|
45f8fe4d0b | ||
|
|
2179ef33a7 | ||
|
|
d0ae677e61 | ||
|
|
6a9b1f2214 | ||
|
|
b1117e45c9 | ||
|
|
382a836d80 | ||
|
|
db3e39f0cb | ||
|
|
80ce87e4a9 | ||
|
|
f0f29891d6 | ||
|
|
c66da5bedb | ||
|
|
1f21d259ea | ||
|
|
21b218e464 | ||
|
|
3b2fb023b8 | ||
|
|
92babda742 | ||
|
|
3cdbc5890a | ||
|
|
a8042a666c | ||
|
|
79f9640b12 | ||
|
|
65aeb82e21 | ||
|
|
e3b0aa0c50 | ||
|
|
7e617a632e | ||
|
|
fb5dd14875 | ||
|
|
490bdd6ce7 | ||
|
|
893b1dd1db | ||
|
|
1146959806 | ||
|
|
45103f81b4 | ||
|
|
c426f4a9cc | ||
|
|
0d27f2d77e | ||
|
|
b4c62101a4 | ||
|
|
950cd0854f | ||
|
|
4cd137830b | ||
|
|
aa19bcdcbe | ||
|
|
cf6bf7d1ec | ||
|
|
47665dbddb | ||
|
|
dc209453d0 | ||
|
|
5b89091f13 | ||
|
|
50ee844759 | ||
|
|
244758d438 | ||
|
|
71bbed12f9 | ||
|
|
8c2c1cb726 | ||
|
|
9d01140a63 | ||
|
|
259e775db8 | ||
|
|
d4c617ff19 | ||
|
|
c7680bb50a | ||
|
|
722ce55ccb | ||
|
|
5b950ce73f | ||
|
|
8dfa2110bf | ||
|
|
31aeaf6309 | ||
|
|
e4f483998d | ||
|
|
7a393101ee | ||
|
|
d96b023d72 | ||
|
|
4de3cab522 | ||
|
|
b34f70f91d | ||
|
|
9da8cad7fe | ||
|
|
e4a30f5966 | ||
|
|
814a050858 | ||
|
|
b6179bd2de | ||
|
|
e5ac37e3cf | ||
|
|
66463ddff3 | ||
|
|
956c2993ae | ||
|
|
3cf67cb2fd | ||
|
|
36afa9d848 | ||
|
|
694ab76d1e | ||
|
|
369c2e9ffa | ||
|
|
95ae35216a | ||
|
|
9c0477fa52 | ||
|
|
e1ab15e323 | ||
|
|
ff77d58acb | ||
|
|
adfbd5730b | ||
|
|
bf175983ec | ||
|
|
470b69c775 | ||
|
|
60648c43db | ||
|
|
75cd4f4306 | ||
|
|
2412115f41 | ||
|
|
598836d656 | ||
|
|
e1019c4420 | ||
|
|
a8a6c0c520 | ||
|
|
685d76231a | ||
|
|
0f9b9f780f | ||
|
|
1e2792874d | ||
|
|
0a061ce95e | ||
|
|
d82ee029e1 | ||
|
|
3b83c8204d | ||
|
|
9ab7a4759a | ||
|
|
c261d0d3f7 | ||
|
|
940415dddb | ||
|
|
60506e6f34 | ||
|
|
bd7a74d79e | ||
|
|
5fcf690918 | ||
|
|
904ec46a9f | ||
|
|
259c148edb | ||
|
|
0ae980062f | ||
|
|
a888f19ad1 | ||
|
|
479b89134d | ||
|
|
d0d083d985 | ||
|
|
396b412dda | ||
|
|
8be02e44c6 | ||
|
|
ec115a6a64 | ||
|
|
3b92c1aca4 | ||
|
|
2d9df03766 | ||
|
|
f086f84ff2 | ||
|
|
a2b23ad80e | ||
|
|
88350d9090 | ||
|
|
b2f27fbdf2 | ||
|
|
72b769aa63 | ||
|
|
8a7e38751a | ||
|
|
3e6c719441 | ||
|
|
8efb000801 | ||
|
|
6d6e92048e | ||
|
|
8c365d45a4 | ||
|
|
ee6448c307 | ||
|
|
f8c9e9ada4 | ||
|
|
87ab3ae4a7 | ||
|
|
68dc461bc0 | ||
|
|
4dd98610a4 | ||
|
|
0358e55aed | ||
|
|
9e5ed10925 | ||
|
|
3b7eb198cf | ||
|
|
81ed7379a9 | ||
|
|
e1950e985b | ||
|
|
84c61af807 | ||
|
|
93755c7329 | ||
|
|
5329918e2f | ||
|
|
efd7726646 | ||
|
|
4c762c0ac3 | ||
|
|
81a911572c | ||
|
|
c56bb86b61 | ||
|
|
ea010f19f0 | ||
|
|
1fae4504a2 | ||
|
|
d99c84d9f8 | ||
|
|
c732bfaeef | ||
|
|
4f0fee684a | ||
|
|
884faa91bb | ||
|
|
8c261a05cc | ||
|
|
95aa57ca50 | ||
|
|
0bc0e58707 | ||
|
|
a86e0d4b96 | ||
|
|
44c819b021 | ||
|
|
3f13d81c6f | ||
|
|
720a965c7e | ||
|
|
f0e263a404 | ||
|
|
ad33a63ad2 | ||
|
|
f7b7f3337d | ||
|
|
983fcc5e40 | ||
|
|
9c5b8b6496 | ||
|
|
71fc4eb16b | ||
|
|
ea2858ea2b | ||
|
|
d080578e83 | ||
|
|
8427eb6d3e | ||
|
|
e3a342dabd | ||
|
|
10b15bedf2 | ||
|
|
8c14a9907e | ||
|
|
f490d9a7e1 | ||
|
|
81ca0c2e25 | ||
|
|
fbfac9f8f4 | ||
|
|
d6e72708bf | ||
|
|
2aff274c31 | ||
|
|
74c1dfa433 | ||
|
|
f51a9d9d87 | ||
|
|
e5498f58e6 | ||
|
|
8f58a4494c | ||
|
|
5bfd43256d | ||
|
|
69bc3f7f25 | ||
|
|
782ceada80 | ||
|
|
36392acbea | ||
|
|
4b564df38f | ||
|
|
9d179e5b2a | ||
|
|
6032bd07dc | ||
|
|
9a87a509b0 | ||
|
|
46f2a81b21 | ||
|
|
f5e21645f6 | ||
|
|
12c8cf9c40 | ||
|
|
93bb929b38 | ||
|
|
85eaf308d5 | ||
|
|
d9ede28b99 | ||
|
|
2e65b0eea4 | ||
|
|
7660a80ef7 | ||
|
|
6d0a436834 | ||
|
|
176fd8f1d8 | ||
|
|
2f85fe9c99 | ||
|
|
d93d297dc4 | ||
|
|
fcd9093f84 | ||
|
|
58b54a70bd | ||
|
|
9daa0a9041 | ||
|
|
965e03daf2 | ||
|
|
b6c3db082a | ||
|
|
26b68953c4 | ||
|
|
fba015c5d9 | ||
|
|
7223c177c4 | ||
|
|
a68bdd2b75 | ||
|
|
38e4624506 | ||
|
|
ad9287ee0f | ||
|
|
777ac119de | ||
|
|
076c64841e | ||
|
|
62cb40fef5 |
16
.github/workflows/build.yaml
vendored
16
.github/workflows/build.yaml
vendored
@@ -4,9 +4,14 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: "!github.event.release.prerelease"
|
||||
permissions:
|
||||
contents: write # for release creation (svenstaro/upload-release-action)
|
||||
|
||||
#if: "!github.event.release.prerelease"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -18,7 +23,7 @@ jobs:
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 7.3
|
||||
php-version: 8.2
|
||||
extensions: opcache, gd
|
||||
tools: composer:v2
|
||||
coverage: none
|
||||
@@ -33,10 +38,10 @@ jobs:
|
||||
- name: Retrieval of Builder Scripts
|
||||
run: |
|
||||
# Real Grav URL
|
||||
curl --silent -H "Authorization: token ${{ secrets.GLOBAL_TOKEN }}" -H "Accept: application/vnd.github.v3.raw" ${{ secrets.BUILD_SCRIPT_URL }} --output build-grav.sh
|
||||
curl --silent -H "Authorization: token ${{ secrets.GLOBAL_TOKEN }}" -H "Accept: application/vnd.github.v3.raw" ${{ secrets.BUILD_SCRIPT_URL_18 }} --output build-grav.sh
|
||||
|
||||
# Development Local URL
|
||||
# curl ${{ secrets.BUILD_SCRIPT_URL }} --output build-grav.sh
|
||||
# curl ${{ secrets.BUILD_SCRIPT_URL_18 }} --output build-grav.sh
|
||||
|
||||
- name: Grav Builder
|
||||
run: |
|
||||
@@ -52,6 +57,9 @@ jobs:
|
||||
file_glob: true
|
||||
|
||||
slack:
|
||||
permissions:
|
||||
actions: read # to list jobs for workflow run (technote-space/workflow-conclusion-action)
|
||||
|
||||
name: Slack
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
33
.github/workflows/tests.yaml
vendored
33
.github/workflows/tests.yaml
vendored
@@ -2,25 +2,26 @@ name: PHP Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ develop ]
|
||||
branches: [ develop, 1.8 ]
|
||||
pull_request:
|
||||
branches: [ develop ]
|
||||
branches: [ develop, 1.8 ]
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
|
||||
unit-tests:
|
||||
strategy:
|
||||
matrix:
|
||||
php: [8.4, 8.3, 8.2]
|
||||
os: [ubuntu-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php: [ 8.1, 8.0, 7.4, 7.3]
|
||||
os: [ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
- name: Setup PHP ${{ matrix.php }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
@@ -28,20 +29,14 @@ jobs:
|
||||
tools: composer:v2
|
||||
coverage: none
|
||||
env:
|
||||
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# - name: Update composer
|
||||
# run: composer update
|
||||
#
|
||||
# - name: Validate composer.json and composer.lock
|
||||
# run: composer validate
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get composer cache directory
|
||||
id: composer-cache
|
||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
|
||||
5
.github/workflows/trigger-skeletons.yml
vendored
5
.github/workflows/trigger-skeletons.yml
vendored
@@ -10,7 +10,10 @@ on:
|
||||
admin:
|
||||
description: 'Create also a package with Admin'
|
||||
required: true
|
||||
default: true
|
||||
default: 'true'
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -25,8 +25,11 @@ user/plugins/*
|
||||
!user/plugins/.*
|
||||
user/themes/*
|
||||
!user/themes/.*
|
||||
user/localhost/config/security.yaml
|
||||
user/config/security.yaml
|
||||
user/**/config/security.yaml
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.gravenv
|
||||
|
||||
# OS Generated
|
||||
.DS_Store*
|
||||
@@ -45,4 +48,3 @@ tests/cache/*
|
||||
tests/error.log
|
||||
system/templates/testing/*
|
||||
/user/config/versions.yaml
|
||||
/user/cli/config/security.yaml
|
||||
|
||||
362
CHANGELOG.md
362
CHANGELOG.md
@@ -1,3 +1,365 @@
|
||||
# v1.8.0-beta.8
|
||||
## 10/14/2025
|
||||
|
||||
1. [](#improved)
|
||||
* Upgraded to latest Symfony 7 (might cause issues with some plugins)
|
||||
* `wordCount` twig filter (merged from 1.7 branch)
|
||||
* More PHP 8.4 compatibility fixes
|
||||
* Update all vendor libraries to latest
|
||||
1. [](#bugfix)
|
||||
* Fixed some CLI level bugs
|
||||
* Fixed a Twig Sandbox bybpass issue
|
||||
|
||||
# v1.8.0-beta.7
|
||||
## 09/22/2025
|
||||
|
||||
1. [](#bugfix)
|
||||
* Changed `private` to `public` for YamlUpdater::get() and YamUpdater::set() methods
|
||||
* Fixed a session cookie issue that manifested when logging-in to client side
|
||||
|
||||
# v1.8.0-beta.6
|
||||
## 09/22/2025
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed a missing YamlUpdater::exists() method
|
||||
|
||||
# v1.8.0-beta.5
|
||||
## 09/22/2025
|
||||
|
||||
1. [](#new)
|
||||
* Deferred Extension support in Forked version of Twig 3
|
||||
* Added separate `strict_mode.twig2_compat` and `strict_mode.twig3_compat` toggles to manage auto-escape behaviour and automatic Twig 3 compatible template rewrites
|
||||
1. [](#bugfix)
|
||||
* Fix for cache blowing up when upgrading from 1.7 to 1.8 via CLI
|
||||
|
||||
# v1.8.0-beta.4
|
||||
## 01/27/2025
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed a PHP compatibility issue with `AbstractLazyCollection`
|
||||
1. [](#improved)
|
||||
* Global PHP 8.2 code optimizations
|
||||
* More PHP 8.4 compatibility fixes
|
||||
* Twig 2.x forked to getgrav/twig 2.x for PHP 8.4 compatibility
|
||||
* Switch to cache@v4 + limit PHP version for Github actions
|
||||
* Trigger testing Github action for Grav 1.8
|
||||
* Merge latest Grav 1.7 fixes into Grav 1.8
|
||||
|
||||
# v1.8.0-beta.3
|
||||
## 11/21/2024
|
||||
|
||||
1. [](#improved)
|
||||
* Updated composer libraries to latest versions for compatibility fixes
|
||||
|
||||
# v1.8.0-beta.2
|
||||
## 10/28/2024
|
||||
|
||||
1. [](#new)
|
||||
* Use `dev-master` branch of Clockwork to support Monolog2 / Monolog3
|
||||
* `AVIF` image support via updates to `getgrav/Image` library
|
||||
* Upgraded to **Doctrine Collection 2.2**
|
||||
1. [](#improved)
|
||||
* Updated composer libraries
|
||||
* Updated composer.php binary to `v2.8.1`
|
||||
* Fixes for PHP 8.4 - Implicitly nullable parameter declarations deprecated
|
||||
* Added back Missing `RocketTheme\Toolbox\Event\EventSubscriberInterface` for Gantry5
|
||||
1. [](#bugfix)
|
||||
* Various fixes to use `$log->debug()`, `$log->info()`, `$log->warning()` and `$log->error()` For Monolog2 support
|
||||
|
||||
# v1.8.0-beta.1
|
||||
## 10/23/2024
|
||||
|
||||
1. [](#new)
|
||||
* Set minimum requirements to **PHP 8.2**
|
||||
* Updated to **Twig 2.14**
|
||||
* Updated to **Symfony 6.4**
|
||||
* Updated to **Monolog 2.3**
|
||||
* Updated to **RocketTheme/Toolbox 2.0**
|
||||
* Updated to **Composer/Semver 3.2**
|
||||
* Use **Symfony Cache** instead of unmaintained **Doctrine Cache**
|
||||
* Removed unsupported **APC**, **WinCache**, **XCache** and **Memcache**, use apcu or memcached instead
|
||||
* Removed `system.umask_fix` setting for security reasons
|
||||
* Support phpstan level 6 in Framework classes
|
||||
|
||||
# v1.7.49.5
|
||||
## 09/10/2025
|
||||
|
||||
1. [](#bugfix)
|
||||
* Backup not honoring ignored paths [#3952](https://github.com/getgrav/grav/issues/3952)
|
||||
|
||||
# v1.7.49.4
|
||||
## 09/03/2025
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed cron force running jobs severy minute! [#3951](https://github.com/getgrav/grav/issues/3951)
|
||||
|
||||
# v1.7.49.3
|
||||
## 09/02/2025
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed an error in ZipArchive that was causing issues on some systems
|
||||
* Fixed namespace change for `Cron\Expression`
|
||||
* Removed broken cron install field... use 'instructions' instead
|
||||
* Fixed duplicate jobs listing in some CLI commands
|
||||
|
||||
# v1.7.49.2
|
||||
## 08/28/2025
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fix translation of key for image adapter [#3944](https://github.com/getgrav/grav/pull/3944)
|
||||
|
||||
# v1.7.49.1
|
||||
## 08/25/2025
|
||||
|
||||
1. [](#new)
|
||||
* Rerelease to include all updated plugins/theme etc.
|
||||
|
||||
# v1.7.49
|
||||
## 08/25/2025
|
||||
|
||||
1. [](#new)
|
||||
* Revamped Grav Scheduler to support webhook to call call scheduler + concurrent jobs + jobs queue + logging, and other improvements
|
||||
* Revamped Grav Cache purge capabilities to only clear obsolete old cache items
|
||||
* Added full imagick support in Grav Image library
|
||||
* Added support for Validate `match` and `match_any` in forms
|
||||
1. [](#improved)
|
||||
* Handle empty values on require with ignore fields in Forms
|
||||
* Use `actions/cache@v4` in github workflows
|
||||
* Use `actions/checkout@v4`in github workflows [#3867](https://github.com/getgrav/grav/pull/3867)
|
||||
* Update code block in README.md [#3886](https://github.com/getgrav/grav/pull/3886)
|
||||
* Updated vendor libs to latest
|
||||
1. [](#bugfix)
|
||||
* Bug in `exif_read_data` [#3878](https://github.com/getgrav/grav/pull/3878)
|
||||
* Fix parser error in URI: [#3894](https://github.com/getgrav/grav/issues/3894)
|
||||
|
||||
>>>>>>> develop
|
||||
|
||||
# v1.7.48
|
||||
## 10/28/2024
|
||||
|
||||
1. [](#new)
|
||||
* New Trait for fetchPriority attribute on images [#3850](https://github.com/getgrav/grav/pull/3850)
|
||||
1. [](#improved)
|
||||
* Fix for #3164. Adds aliases as possible commands during lookup [#3863](https://github.com/getgrav/grav/pull/3863)
|
||||
1. [](#bugfix)
|
||||
* Fix style conflict with Clockwork and tooltips [#3861](https://github.com/getgrav/grav/pull/3861)
|
||||
|
||||
# v1.7.47
|
||||
## 10/23/2024
|
||||
|
||||
1. [](#new)
|
||||
* New `Utils::toAscii()` method
|
||||
* Added support for Clockwork Debugger to allow web UI (requires new `clockwork-web` plugin)
|
||||
1. [](#improved)
|
||||
* Include modular sub-pages in last-modification date computation [#3562](https://github.com/getgrav/grav/pull/3562)
|
||||
* Updated vendor libs to latest versions
|
||||
* Updated JQuery to `3.7.1` [#3787](https://github.com/getgrav/grav/pull/3827)
|
||||
* Updated vendor libraries to latest versions
|
||||
* Support for Fediverse Creator meta tag [#3844](https://github.com/getgrav/grav/pull/3844)
|
||||
1. [](#bugfix)
|
||||
* Fixes deprecated for return type in Filesystem with PHP 8.3.6 [#3831](https://github.com/getgrav/grav/issues/3831)
|
||||
* Fix for `exif_imagtetype()` throwing an exception when file doesn't exist
|
||||
* Fix JSON output comments check with content type [#3859](https://github.com/getgrav/grav/pull/3859)
|
||||
|
||||
# v1.7.46
|
||||
## 05/15/2024
|
||||
|
||||
1. [](#new)
|
||||
* Added a new `Utils::toAscii()` method to remove UTF-8 characters from string
|
||||
1. [](#improved)
|
||||
* Removed unused `symfony/service-contracts` [#3828](https://github.com/getgrav/grav/pull/3828)
|
||||
* Upgraded bundled legacy JQuery to `3.7.1` [#3727](https://github.com/getgrav/grav/pull/3827)
|
||||
* Include modular pages in header `last-modified:` calculation [#3562](https://github.com/getgrav/grav/pull/3562)
|
||||
* Updated vendor libs to latest versions
|
||||
1. [](#bugfix)
|
||||
* Fixed some deprecated issues in Filesystem [#3831](https://github.com/getgrav/grav/issues/3831)
|
||||
|
||||
# v1.7.46
|
||||
## 05/15/2024
|
||||
|
||||
1. [](#improved)
|
||||
* Better handling of external protocols in `Utils::url()` such as `mailto:`, `tel:`, etc.
|
||||
* Handle `GRAV_ROOT` or `GRAV_WEBROOT` when `/` [#3667](https://github.com/getgrav/grav/pull/3667)
|
||||
1. [](#bugfix)
|
||||
* Fixes for multi-lang taxonomy when reinitializing the languages (e.g. LangSwitcher plugin)
|
||||
* Ensure the full filepath is checked for invalid filename in `MediaUploadTrait::checkFileMetadata()`
|
||||
* Fixed a bug in the `on_events` REGEX pattern of `Security::detectXss()` as it was not matching correctly.
|
||||
* Fixed an issue where `read_file()` Twig function could be used nefariously in content [#GHSA-f8v5-jmfh-pr69](https://github.com/getgrav/grav/security/advisories/GHSA-f8v5-jmfh-pr69)
|
||||
|
||||
# v1.7.45
|
||||
## 03/18/2024
|
||||
|
||||
1. [](#new)
|
||||
* Added new Image trait for `decoding` attribute [#3796](https://github.com/getgrav/grav/pull/3796)
|
||||
1. [](#bugfix)
|
||||
* Fixed some multibyte issues in Inflector class [#732](https://github.com/getgrav/grav/issues/732)
|
||||
* Fallback to page modified date if Page date provided is invalid and can't be parsed [getgrav/grav-plugin-admin#2394](https://github.com/getgrav/grav-plugin-admin/issues/2394)
|
||||
* Fixed a path traversal vulnerability with file uploads [#GHSA-m7hx-hw6h-mqmc](https://github.com/getgrav/grav/security/advisories/GHSA-m7hx-hw6h-mqmc)
|
||||
* Fixed a security issue with insecure Twig functions be processed [#GHSA-2m7x-c7px-hp58](https://github.com/getgrav/grav/security/advisories/GHSA-2m7x-c7px-hp58) [#GHSA-r6vw-8v8r-pmp4](https://github.com/getgrav/grav/security/advisories/GHSA-r6vw-8v8r-pmp4) [#GHSA-qfv4-q44r-g7rv](https://github.com/getgrav/grav/security/advisories/GHSA-qfv4-q44r-g7rv) [#GHSA-c9gp-64c4-2rrh](https://github.com/getgrav/grav/security/advisories/GHSA-c9gp-64c4-2rrh)
|
||||
1. [](#improved)
|
||||
* Updated composer packages
|
||||
* Updated `bin/composer.phar` to latest `2.7.2`
|
||||
|
||||
# v1.7.44
|
||||
## 01/05/2024
|
||||
|
||||
1. [](#new)
|
||||
* Added PHP `8.3` to tests [#3782](https://github.com/getgrav/grav/pull/3782)
|
||||
* Added debugger messages when Page routes conflict
|
||||
* Added `ISO 8601` date format [#3721](https://github.com/getgrav/grav/pull/37210)
|
||||
* Added support for `.vcf` (vCard) in media configuration [#3772](https://github.com/getgrav/grav/pull/3772)
|
||||
1. [](#improved)
|
||||
* Update jQuery to `v3.6.4` [#3713](https://github.com/getgrav/grav/pull/3713)
|
||||
* Updated vendor libraries including Dom-Sanitizer `v1.0.7` that addresses an XSS issue
|
||||
* Updated `bin/composer.phar` to latest `2.6.6`
|
||||
* Updated vendor libraries to latest
|
||||
* Updated language files
|
||||
* Updated copyright year
|
||||
1. [](#bugfix)
|
||||
* Fixed a math rounding issue with number validation when using floating point steps [#3761](https://github.com/getgrav/grav/issues/3761)
|
||||
* Fixed an issue with `Inflector::ordinalize()` not working as expected [#3759](https://github.com/getgrav/grav/pull/3759)
|
||||
* Fixed various issues with file extension checking with dangerous extensions [#3756(https://github.com/getgrav/grav/pull/3756)]
|
||||
* Fix for invalid input to foreach in `UserGroupObject` [#3724](https://github.com/getgrav/grav/pull/3724)
|
||||
* Fixed exception: `Property 'jsmodule_pipeline_include_externals' does not exist in object` [#3661](https://github.com/getgrav/grav/pull/3661)
|
||||
* Fixed `too few arguments exception` in FlexObjects [#3658](https://github.com/getgrav/grav/pull/3658)
|
||||
|
||||
# v1.7.43
|
||||
## 10/02/2023
|
||||
|
||||
1. [](#new)
|
||||
* Add the ability to programatically set a page's `modified` timestamp via a `modified:` frontmatter entry
|
||||
2. [](#improved)
|
||||
* Update vendor libraries
|
||||
* Include `phar` in the list of `security.uploads_dangerous_extensions`
|
||||
* When enabled `system.languages.debug` now dumps **Key -> Value** to debugger [#3752](https://github.com/getgrav/grav/issues/3752)
|
||||
* Updated built-in composer to latest `2.6.4` [#3748](https://github.com/getgrav/grav/issues/3748)
|
||||
* Added support for `@import` to ensure paths are rewritten correctly in CSS pipeline [#3750](https://github.com/getgrav/grav/pull/3750)
|
||||
|
||||
# v1.7.42.3
|
||||
## 07/18/2023
|
||||
|
||||
2. [](#improved)
|
||||
* Fixed a typo in `Utils::isDangerousFunction`
|
||||
|
||||
# v1.7.42.2
|
||||
## 07/18/2023
|
||||
|
||||
2. [](#improved)
|
||||
* In `Utils::isDangerousFunction`, handle double `\\` in `|map` twig filter to mitigate SSTI attack
|
||||
* Better handle empty email in `Validatoin::typeEmail()`
|
||||
|
||||
# v1.7.42.1
|
||||
## 06/15/2023
|
||||
|
||||
2. [](#improved)
|
||||
* Quick fix for `isDangerousFunction` when `$name` was a closure [#3727](https://github.com/getgrav/grav/issues/3727)
|
||||
|
||||
# v1.7.42
|
||||
## 06/14/2023
|
||||
|
||||
1. [](#new)
|
||||
* Added a new `system.languages.debug` option that adds a `<span class="translate-debug"></span>` around strings translated with `|t`. This can be styled by the theme as needed.
|
||||
1. [](#improved)
|
||||
* More robust SSTI handling in `filter`, `map`, and `reduce` Twig filters and functions
|
||||
* Various SSTI improvements `Utils::isDangerousFunction()`
|
||||
1. [](#bugfix)
|
||||
* Fixed Twig `|map()` allowing code execution
|
||||
* Fixed Twig `|reduce()` allowing code execution
|
||||
|
||||
# v1.7.41.2
|
||||
## 06/01/2023
|
||||
|
||||
1. [](#improved)
|
||||
* Added the ability to set a configurable 'key' for the Twig Cache Tag: `{% cache 'my-key' 600 %}`
|
||||
1. [](#bugfix)
|
||||
* Fixed an issue with special characters in slug's would cause redirect loops
|
||||
|
||||
# v1.7.41.1
|
||||
## 05/10/2023
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed certain UTF-8 characters breaking `Truncator` class [#3716](https://github.com/getgrav/grav/issues/3716)
|
||||
|
||||
# v1.7.41
|
||||
## 05/09/2023
|
||||
|
||||
1. [](#improved)
|
||||
* Removed `FILTER_SANITIZE_STRING` input filter in favor of `htmlspecialchars(strip_tags())` for PHP 8.2+
|
||||
* Added `GRAV_SANITIZE_STRING` constant to replace `FILTER_SANITIZE_STRING` for PHP 8.2+
|
||||
* Support non-deprecated style dynamic properties in `Parsedown` class via `ParseDownGravTrait` for PHP 8.2+
|
||||
* Modified `Truncator` to not use deprecated `mb_convert_encoding()` for PHP 8.2+
|
||||
* Fixed passing null into `mb_strpos()` deprecated for PHP 8.2+
|
||||
* Updated internal `TwigDeferredExtension` to be PHP 8.2+ compatible
|
||||
* Upgraded `getgrav/image` fork to take advantage of various PHP 8.2+ fixes
|
||||
* Use `UserGroupObject::groupNames` method in blueprints for PHP 8.2+
|
||||
* Comment out `files-upload` deprecated message as this is not going to be removed
|
||||
* Added various public `Twig` class variables used by admin to address deprecated messages for PHP 8.2+
|
||||
* Added `parse_url` to list of PHP functions supported in Twig Extension
|
||||
* Added support for dynamic functions in `Parsedown` to stop deprecation messages in PHP 8.2+
|
||||
|
||||
# v1.7.40
|
||||
## 03/22/2023
|
||||
|
||||
1. [](#new)
|
||||
* Added a new `timestamp: true|false` option for individual assets
|
||||
1. [](#improved)
|
||||
* Removed outdated `xcache` setting [#3615](https://github.com/getgrav/grav/pull/3615)
|
||||
* Updated `robots.txt` [#3625](https://github.com/getgrav/grav/pull/3625)
|
||||
* Handle the situation when GRAV_ROOT or GRAV_WEBROOT are `/` [#3625](https://github.com/getgrav/grav/pull/3667)
|
||||
1. [](#bugfix)
|
||||
* Fixed `force_ssl` redirect in case of undefined hostname [#3702](https://github.com/getgrav/grav/pull/3702)
|
||||
* Fixed an issue with duplicate identical page paths
|
||||
* Fixed `BlueprintSchema:flattenData` to properly handle ignored fields
|
||||
* Fixed LogViewer regex greediness [#3684](https://github.com/getgrav/grav/pull/3684)
|
||||
* Fixed `whoami` command [#3695](https://github.com/getgrav/grav/pull/3695)
|
||||
|
||||
# v1.7.39.4
|
||||
## 02/22/2023
|
||||
|
||||
1. [](#bugfix)
|
||||
* Reverted a reorganization of `account.yaml` that caused username to be disabled [admin#2344](https://github.com/getgrav/grav-plugin-admin/issues/2344)
|
||||
|
||||
# v1.7.39.3
|
||||
## 02/21/2023
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fix for overzealous modular page template rendering fix in 1.7.39 causing Feed plugin to break [#3689](https://github.com/getgrav/grav/issues/3689)
|
||||
|
||||
# v1.7.39.2
|
||||
## 02/20/2023
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fix for invalid session breaking Flex Accounts (when switching from Regular to Flex)
|
||||
|
||||
# v1.7.39.1
|
||||
## 02/20/2023
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fix for broken image CSS with the latest version of DebugBar
|
||||
|
||||
# v1.7.39
|
||||
## 02/19/2023
|
||||
|
||||
1. [](#improved)
|
||||
* Vendor library updates to latest versions
|
||||
1. [](#bugfix)
|
||||
* Various PHP 8.2 fixes
|
||||
* Fixed an issue with modular pages rendering thew wrong template when dynamically changing the page
|
||||
* Fixed an issue with `email` validation that was failing on UTF-8 characters. Following best practices and now only check for `@` and length.
|
||||
* Fixed PHPUnit tests to remove deprecation warnings
|
||||
|
||||
# v1.7.38
|
||||
## 01/02/2023
|
||||
|
||||
1. [](#new)
|
||||
* New `onBeforeSessionStart()` event to be used to store data lost during session regeneration (e.g. login)
|
||||
1. [](#improved)
|
||||
* Vendor library updates to latest versions
|
||||
* Updated `bin/composer.phar` to latest `2.4.4` version [#3627](https://github.com/getgrav/grav/issues/3627)
|
||||
1. [](#bugfix)
|
||||
* Don't fail hard if pages recurse with same path
|
||||
* Github workflows security hardening [#3624](https://github.com/getgrav/grav/pull/3624)
|
||||
|
||||
# v1.7.37.1
|
||||
## 10/05/2022
|
||||
|
||||
|
||||
37
README.md
37
README.md
@@ -20,7 +20,7 @@ The underlying architecture of Grav is designed to use well-established and _bes
|
||||
|
||||
# Requirements
|
||||
|
||||
- PHP 7.3.6 or higher. Check the [required modules list](https://learn.getgrav.org/basics/requirements#php-requirements)
|
||||
- PHP 8.2 or higher. Check the [required modules list](https://learn.getgrav.org/basics/requirements#php-requirements)
|
||||
- Check the [Apache](https://learn.getgrav.org/basics/requirements#apache-requirements) or [IIS](https://learn.getgrav.org/basics/requirements#iis-requirements) requirements
|
||||
|
||||
# Documentation
|
||||
@@ -39,22 +39,22 @@ You can download a **ready-built** package from the [Downloads page on https://g
|
||||
|
||||
You can create a new project with the latest **stable** Grav release with the following command:
|
||||
|
||||
```
|
||||
$ composer create-project getgrav/grav ~/webroot/grav
|
||||
```bash
|
||||
composer create-project getgrav/grav ~/webroot/grav
|
||||
```
|
||||
|
||||
### From GitHub
|
||||
|
||||
1. Clone the Grav repository from [https://github.com/getgrav/grav]() to a folder in the webroot of your server, e.g. `~/webroot/grav`. Launch a **terminal** or **console** and navigate to the webroot folder:
|
||||
```
|
||||
$ cd ~/webroot
|
||||
$ git clone https://github.com/getgrav/grav.git
|
||||
```bash
|
||||
cd ~/webroot
|
||||
git clone https://github.com/getgrav/grav.git
|
||||
```
|
||||
|
||||
2. Install the **plugin** and **theme dependencies** by using the [Grav CLI application](https://learn.getgrav.org/advanced/grav-cli) `bin/grav`:
|
||||
```
|
||||
$ cd ~/webroot/grav
|
||||
$ bin/grav install
|
||||
```bash
|
||||
cd ~/webroot/grav
|
||||
bin/grav install
|
||||
```
|
||||
|
||||
Check out the [install procedures](https://learn.getgrav.org/basics/installation) for more information.
|
||||
@@ -63,35 +63,36 @@ Check out the [install procedures](https://learn.getgrav.org/basics/installation
|
||||
|
||||
You can download [plugins](https://getgrav.org/downloads/plugins) or [themes](https://getgrav.org/downloads/themes) manually from the appropriate tab on the [Downloads page on https://getgrav.org](https://getgrav.org/downloads), but the preferred solution is to use the [Grav Package Manager](https://learn.getgrav.org/advanced/grav-gpm) or `GPM`:
|
||||
|
||||
```
|
||||
$ bin/gpm index
|
||||
```bash
|
||||
bin/gpm index
|
||||
```
|
||||
|
||||
This will display all the available plugins and then you can install one or more with:
|
||||
|
||||
```
|
||||
$ bin/gpm install <plugin/theme>
|
||||
```bash
|
||||
bin/gpm install <plugin/theme>
|
||||
```
|
||||
|
||||
# Updating
|
||||
|
||||
To update Grav you should use the [Grav Package Manager](https://learn.getgrav.org/advanced/grav-gpm) or `GPM`:
|
||||
|
||||
```
|
||||
$ bin/gpm selfupgrade
|
||||
```bash
|
||||
bin/gpm selfupgrade
|
||||
```
|
||||
|
||||
To update plugins and themes:
|
||||
|
||||
```
|
||||
$ bin/gpm update
|
||||
```bash
|
||||
bin/gpm update
|
||||
```
|
||||
|
||||
## Upgrading from older version
|
||||
|
||||
* [Upgrading to Grav 1.8](https://learn.getgrav.org/16/advanced/grav-development/grav-18-upgrade-guide)
|
||||
* [Upgrading to Grav 1.7](https://learn.getgrav.org/16/advanced/grav-development/grav-17-upgrade-guide)
|
||||
* [Upgrading to Grav 1.6](https://learn.getgrav.org/16/advanced/grav-development/grav-16-upgrade-guide)
|
||||
* [Upgrading from Grav <1.6](https://learn.getgrav.org/16/advanced/grav-development/grav-15-upgrade-guide)
|
||||
* [Upgrading from Grav before 1.6](https://learn.getgrav.org/16/advanced/grav-development/grav-15-upgrade-guide)
|
||||
|
||||
# Contributing
|
||||
We appreciate any contribution to Grav, whether it is related to bugs, grammar, or simply a suggestion or improvement! Please refer to the [Contributing guide](CONTRIBUTING.md) for more guidance on this topic.
|
||||
|
||||
23
SECURITY.md
23
SECURITY.md
@@ -7,15 +7,32 @@ We are focusing our security updates on the following versions
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 1.7.x | :white_check_mark: |
|
||||
| 1.6.x | :warning: |
|
||||
| 1.6.x | :x: |
|
||||
| < 1.6 | :x: |
|
||||
|
||||
## :pushpin: Note on Security Severity
|
||||
|
||||
> NOTE: Please use the following guidelines when selecting a **Severity**. Submitted advisories that are marked **High** or **Critical** that don't meet the guidelines below will be closed.
|
||||
|
||||
* **CRITICAL** - no account required, can modify content, or run malicious code or nefarious activity without any access.
|
||||
* **HIGH** - publisher level account able to run malicious code or nefarious activity, or other high level security things.
|
||||
* **MODERATE** - admin level account able to run malicious code or do nefarious things. other moderate security things.
|
||||
* **LOW** - super admin level account able to run malicious code or do nefarious things. other minor security things.
|
||||
|
||||
## :warning: Versions
|
||||
|
||||
Versions with :warning: will be supported for security issues, however you won't be able to update to them, you will need to manually update through the [`direct-install` command](https://learn.getgrav.org/17/admin-panel/tools).
|
||||
|
||||
If you cannot update to the latest stable version available because, for example, your server does not meet the minimum PHP requirements, you can manually install a previous version by downloading the package from our Releases directory (https://github.com/getgrav/grav/releases).
|
||||
|
||||
## Reporting a Vulnerability
|
||||
## :pencil: Reporting a Vulnerability
|
||||
|
||||
Please contact security@getgrav.org with a detailed explanation of the security issue found. If it appears to be a legitimate issues, please submit an **advisory via GitHub Security**: https://github.com/getgrav/grav/security/advisories
|
||||
|
||||
> NOTE: Please do not use 3rd party security issue reporting services, we like to keep everything in the GitHub ecosystem for easier manageability.
|
||||
|
||||
## :bug: Bug Bounties
|
||||
|
||||
We do greatly appreciate your efforts to improve Grav, but unfortunately because we are a small open source project, we **do not have the resources to offer bounties** for security issues found.
|
||||
|
||||
|
||||
Please contact security@getgrav.org with a detailed explaination of the security issue found and we will work with you to get it resolved as fast as possible.
|
||||
|
||||
@@ -1 +1 @@
|
||||
/* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. */
|
||||
/* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved. */
|
||||
|
||||
@@ -1 +1 @@
|
||||
/* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. */
|
||||
/* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved. */
|
||||
|
||||
Binary file not shown.
2
bin/gpm
2
bin/gpm
@@ -2,7 +2,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
|
||||
2
bin/grav
2
bin/grav
@@ -2,7 +2,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
|
||||
2
cache/.gitkeep
vendored
2
cache/.gitkeep
vendored
@@ -1 +1 @@
|
||||
/* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. */
|
||||
/* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved. */
|
||||
|
||||
@@ -2,7 +2,7 @@ actor: Tester
|
||||
bootstrap: _bootstrap.php
|
||||
paths:
|
||||
tests: tests
|
||||
log: tests/_output
|
||||
output: tests/_output
|
||||
data: tests/_data
|
||||
support: tests/_support
|
||||
envs: tests/_envs
|
||||
|
||||
119
composer.json
119
composer.json
@@ -12,7 +12,7 @@
|
||||
"homepage": "https://getgrav.org",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^7.3.6 || ^8.0",
|
||||
"php": "^8.2",
|
||||
"ext-json": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-curl": "*",
|
||||
@@ -20,62 +20,79 @@
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-gd": "*",
|
||||
"symfony/polyfill-mbstring": "~1.23",
|
||||
"symfony/polyfill-iconv": "^1.23",
|
||||
"symfony/polyfill-php74": "^1.23",
|
||||
"symfony/polyfill-php80": "^1.23",
|
||||
"symfony/polyfill-php81": "^1.23",
|
||||
"psr/simple-cache": "^1.0",
|
||||
"psr/http-message": "^1.0",
|
||||
"symfony/polyfill-mbstring": "^1.24",
|
||||
"symfony/polyfill-iconv": "^1.24",
|
||||
"symfony/polyfill-php80": "^1.24",
|
||||
"symfony/polyfill-php81": "^1.24",
|
||||
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0",
|
||||
"psr/http-message": "^1.1 || ^2.0",
|
||||
"psr/http-server-middleware": "^1.0",
|
||||
"psr/container": "~1.1.0",
|
||||
"nyholm/psr7-server": "^1.0",
|
||||
"nyholm/psr7": "^1.3",
|
||||
"twig/twig": "~v1.44",
|
||||
"psr/container": "^1.1 || ^2.0",
|
||||
"psr/log": "^1.1 || ^2.0 || ^3.0",
|
||||
"symfony/cache": "^6.4 || ^7.0",
|
||||
"symfony/yaml": "^6.4 || ^7.0",
|
||||
"symfony/console": "^6.4 || ^7.0",
|
||||
"symfony/event-dispatcher": "^6.4 || ^7.0",
|
||||
"symfony/var-exporter": "^6.4 || ^7.0",
|
||||
"symfony/var-dumper": "^6.4 || ^7.0",
|
||||
"symfony/process": "^6.4 || ^7.0",
|
||||
"symfony/http-client": "^6.4 || ^7.0",
|
||||
"twig/twig": "3.x-dev",
|
||||
"monolog/monolog": "^3.0",
|
||||
"doctrine/cache": "^2.2",
|
||||
"doctrine/collections": "^2.2",
|
||||
"pimple/pimple": "~3.5.0",
|
||||
"nyholm/psr7-server": "^1.1",
|
||||
"nyholm/psr7": "^1.8",
|
||||
"erusev/parsedown": "^1.7",
|
||||
"erusev/parsedown-extra": "~0.8",
|
||||
"symfony/contracts": "~1.1",
|
||||
"symfony/yaml": "~4.4",
|
||||
"symfony/console": "~4.4",
|
||||
"symfony/event-dispatcher": "~4.4",
|
||||
"symfony/var-dumper": "~4.4",
|
||||
"symfony/process": "~4.4",
|
||||
"doctrine/cache": "^1.10",
|
||||
"doctrine/collections": "^1.6",
|
||||
"guzzlehttp/psr7": "^1.7",
|
||||
"filp/whoops": "~2.9",
|
||||
"matthiasmullie/minify": "^1.3",
|
||||
"monolog/monolog": "~1.25",
|
||||
"getgrav/image": "^3.0",
|
||||
"getgrav/cache": "^2.0",
|
||||
"donatj/phpuseragentparser": "~1.1",
|
||||
"pimple/pimple": "~3.5.0",
|
||||
"rockettheme/toolbox": "~1.5",
|
||||
"maximebf/debugbar": "~1.16",
|
||||
"league/climate": "^3.6",
|
||||
"miljar/php-exif": "^0.6",
|
||||
"composer/ca-bundle": "^1.2",
|
||||
"dragonmantank/cron-expression": "^1.2",
|
||||
"willdurand/negotiation": "^3.0",
|
||||
"itsgoingd/clockwork": "^5.0",
|
||||
"symfony/http-client": "^4.4",
|
||||
"composer/semver": "^1.4",
|
||||
"rockettheme/toolbox": "v2.x-dev",
|
||||
"composer/ca-bundle": "^1.5",
|
||||
"composer/semver": "^3.4",
|
||||
"dragonmantank/cron-expression": "^3.3",
|
||||
"willdurand/negotiation": "^3.1",
|
||||
"rhukster/dom-sanitizer": "^1.0",
|
||||
"matthiasmullie/minify": "^1.3",
|
||||
"donatj/phpuseragentparser": "~1.9",
|
||||
"guzzlehttp/psr7": "^2.7",
|
||||
"filp/whoops": "~2.16",
|
||||
"itsgoingd/clockwork": "^5.3",
|
||||
"php-debugbar/php-debugbar": "~2.1",
|
||||
"getgrav/image": "^4.0",
|
||||
"getgrav/cache": "^2.0",
|
||||
"antoligy/dom-string-iterators": "^1.0",
|
||||
"miljar/php-exif": "^0.6",
|
||||
"league/climate": "^3.10",
|
||||
"multiavatar/multiavatar-php": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "^4.1",
|
||||
"phpstan/phpstan": "^1.8",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.0",
|
||||
"phpunit/php-code-coverage": "~9.2",
|
||||
"codeception/codeception": "^5.1",
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpstan/phpstan-deprecation-rules": "^2.0",
|
||||
"phpunit/php-code-coverage": "^11.0",
|
||||
"getgrav/markdowndocs": "^2.0",
|
||||
"codeception/module-asserts": "^1.3",
|
||||
"codeception/module-phpbrowser": "^1.0",
|
||||
"symfony/service-contracts": "*"
|
||||
"codeception/module-asserts": "*",
|
||||
"codeception/module-phpbrowser": "*",
|
||||
"rector/rector": "^2.1"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/rockettheme/toolbox"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/getgrav/twig"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/getgrav/parsedown"
|
||||
}
|
||||
],
|
||||
"replace": {
|
||||
"symfony/polyfill-php72": "*",
|
||||
"symfony/polyfill-php73": "*"
|
||||
"symfony/polyfill-php73": "*",
|
||||
"symfony/polyfill-php74": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "Recommended for better performance",
|
||||
@@ -90,12 +107,14 @@
|
||||
"config": {
|
||||
"apcu-autoloader": true,
|
||||
"platform": {
|
||||
"php": "7.3.6"
|
||||
"php": "8.2"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Grav\\": "system/src/Grav",
|
||||
"Doctrine\\": "system/src/Doctrine",
|
||||
"RocketTheme\\": "system/src/RocketTheme",
|
||||
"Twig\\": "system/src/Twig"
|
||||
},
|
||||
"files": [
|
||||
@@ -115,10 +134,12 @@
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"api-17": "vendor/bin/phpdoc-md generate system/src > user/pages/14.api/default.17.md",
|
||||
"api-18": "vendor/bin/phpdoc-md generate system/src > user/pages/14.api/default.18.md",
|
||||
"post-create-project-cmd": "bin/grav install",
|
||||
"rector": "vendor/bin/rector",
|
||||
"rector:php-compat": "@php vendor/bin/rector process --config=system/rector.php --ansi --no-progress-bar",
|
||||
"phpstan": "vendor/bin/phpstan analyse -l 2 -c ./tests/phpstan/phpstan.neon --memory-limit=720M system/src",
|
||||
"phpstan-framework": "vendor/bin/phpstan analyse -l 5 -c ./tests/phpstan/phpstan.neon --memory-limit=480M system/src/Grav/Framework system/src/Grav/Events system/src/Grav/Installer",
|
||||
"phpstan-framework": "vendor/bin/phpstan analyse -l 6 -c ./tests/phpstan/phpstan.neon --memory-limit=480M system/src/Grav/Framework system/src/Grav/Events system/src/Grav/Installer",
|
||||
"phpstan-plugins": "vendor/bin/phpstan analyse -l 1 -c ./tests/phpstan/plugins.neon --memory-limit=400M user/plugins",
|
||||
"test": "vendor/bin/codecept run unit",
|
||||
"test-windows": "vendor\\bin\\codecept run unit"
|
||||
|
||||
4168
composer.lock
generated
4168
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
/* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. */
|
||||
/* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved. */
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
/**
|
||||
* @package Grav.Core
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2024 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav;
|
||||
|
||||
\define('GRAV_REQUEST_TIME', microtime(true));
|
||||
\define('GRAV_PHP_MIN', '7.3.6');
|
||||
\define('GRAV_PHP_MIN', '8.2.0');
|
||||
|
||||
if (PHP_SAPI === 'cli-server') {
|
||||
$symfony_server = stripos(getenv('_'), 'symfony') !== false || stripos($_SERVER['SERVER_SOFTWARE'] ?? '', 'symfony') !== false || stripos($_ENV['SERVER_SOFTWARE'] ?? '', 'symfony') !== false;
|
||||
@@ -40,12 +40,12 @@ use Grav\Common\Grav;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
|
||||
// Get the Grav instance
|
||||
$grav = Grav::instance(array('loader' => $loader));
|
||||
$grav = Grav::instance(['loader' => $loader]);
|
||||
|
||||
// Process the page
|
||||
try {
|
||||
$grav->process();
|
||||
} catch (\Error|\Exception $e) {
|
||||
$grav->fireEvent('onFatalException', new Event(array('exception' => $e)));
|
||||
$grav->fireEvent('onFatalException', new Event(['exception' => $e]));
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
/* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. */
|
||||
/* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved. */
|
||||
|
||||
505
needs_fixing.txt
Normal file
505
needs_fixing.txt
Normal file
@@ -0,0 +1,505 @@
|
||||
------ ----------------------------------------------------
|
||||
Line src/Grav/Common/GPM/Response.php
|
||||
------ ----------------------------------------------------
|
||||
3 Class Grav\Common\GPM\Response not found.
|
||||
🪪 class.notFound
|
||||
💡 Learn more at
|
||||
https://phpstan.org/user-guide/discovering-symbols
|
||||
------ ----------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Common/Grav.php
|
||||
------ -----------------------------------------------------------
|
||||
148 No error to ignore is reported on line 148.
|
||||
681 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Common/Page/Collection.php
|
||||
------ -----------------------------------------------------------
|
||||
112 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
209 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Common/Processors/InitializeProcessor.php
|
||||
------ -----------------------------------------------------------
|
||||
58 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -------------------------------------------------------
|
||||
Line src/Grav/Common/Scheduler/Job.php
|
||||
------ -------------------------------------------------------
|
||||
574 Call to static method sendEmail() on an unknown class
|
||||
Grav\Plugin\Email\Utils.
|
||||
🪪 class.notFound
|
||||
💡 Learn more at
|
||||
https://phpstan.org/user-guide/discovering-symbols
|
||||
------ -------------------------------------------------------
|
||||
|
||||
------ ----------------------------------------------------------
|
||||
Line src/Grav/Common/Scheduler/SchedulerController.php
|
||||
------ ----------------------------------------------------------
|
||||
41 Class Grav\Common\Scheduler\ModernScheduler not found.
|
||||
🪪 class.notFound
|
||||
💡 Learn more at
|
||||
https://phpstan.org/user-guide/discovering-symbols
|
||||
45 Instantiated class Grav\Common\Scheduler\ModernScheduler
|
||||
not found.
|
||||
🪪 class.notFound
|
||||
💡 Learn more at
|
||||
https://phpstan.org/user-guide/discovering-symbols
|
||||
------ ----------------------------------------------------------
|
||||
|
||||
------ --------------------------------------------------------
|
||||
Line src/Grav/Common/Service/SchedulerServiceProvider.php
|
||||
------ --------------------------------------------------------
|
||||
55 Instantiated class Grav\Common\Scheduler\JobWorker not
|
||||
found.
|
||||
🪪 class.notFound
|
||||
💡 Learn more at
|
||||
https://phpstan.org/user-guide/discovering-symbols
|
||||
------ --------------------------------------------------------
|
||||
|
||||
------ ---------------------------------------------
|
||||
Line src/Grav/Common/Session.php
|
||||
------ ---------------------------------------------
|
||||
132 No error to ignore is reported on line 132.
|
||||
137 No error to ignore is reported on line 137.
|
||||
------ ---------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Common/Uri.php
|
||||
------ -----------------------------------------------------------
|
||||
1131 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ --------------------------------------------------------
|
||||
Line src/Grav/Console/Application/Application.php
|
||||
------ --------------------------------------------------------
|
||||
125 Return type mixed of method
|
||||
Grav\Console\Application\Application::configureIO() is
|
||||
not covariant with return type void of method
|
||||
Symfony\Component\Console\Application::configureIO().
|
||||
------ --------------------------------------------------------
|
||||
|
||||
------ ---------------------------------------------------------
|
||||
Line src/Grav/Console/ConsoleCommand.php
|
||||
------ ---------------------------------------------------------
|
||||
29 Return type mixed of method
|
||||
Grav\Console\ConsoleCommand::execute() is not covariant
|
||||
with return type int of method
|
||||
Symfony\Component\Console\Command\Command::execute().
|
||||
------ ---------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Console/ConsoleTrait.php (in context of class
|
||||
Grav\Console\ConsoleCommand)
|
||||
------ -----------------------------------------------------------
|
||||
89 Method Grav\Console\ConsoleCommand::addOption() overrides
|
||||
method
|
||||
Symfony\Component\Console\Command\Command::addOption()
|
||||
but misses parameter #6 $suggestedValues.
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ --------------------------------------------------------
|
||||
Line src/Grav/Console/ConsoleTrait.php (in context of class
|
||||
Grav\Console\GpmCommand)
|
||||
------ --------------------------------------------------------
|
||||
89 Method Grav\Console\GpmCommand::addOption() overrides
|
||||
method
|
||||
Symfony\Component\Console\Command\Command::addOption()
|
||||
but misses parameter #6 $suggestedValues.
|
||||
------ --------------------------------------------------------
|
||||
|
||||
------ --------------------------------------------------------
|
||||
Line src/Grav/Console/ConsoleTrait.php (in context of class
|
||||
Grav\Console\GravCommand)
|
||||
------ --------------------------------------------------------
|
||||
89 Method Grav\Console\GravCommand::addOption() overrides
|
||||
method
|
||||
Symfony\Component\Console\Command\Command::addOption()
|
||||
but misses parameter #6 $suggestedValues.
|
||||
------ --------------------------------------------------------
|
||||
|
||||
------ ----------------------------------------------------------
|
||||
Line src/Grav/Console/GpmCommand.php
|
||||
------ ----------------------------------------------------------
|
||||
31 Return type mixed of method
|
||||
Grav\Console\GpmCommand::execute() is not covariant with
|
||||
return type int of method
|
||||
Symfony\Component\Console\Command\Command::execute().
|
||||
39 No error to ignore is reported on line 39.
|
||||
------ ----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Console/GravCommand.php
|
||||
------ -----------------------------------------------------------
|
||||
29 Return type mixed of method
|
||||
Grav\Console\GravCommand::execute() is not covariant with
|
||||
return type int of method
|
||||
Symfony\Component\Console\Command\Command::execute().
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Acl/RecursiveActionIterator.php
|
||||
------ -----------------------------------------------------------
|
||||
62 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ ----------------------------------------------------------
|
||||
Line src/Grav/Framework/Cache/CacheTrait.php (in context of
|
||||
class Grav\Framework\Cache\AbstractCache)
|
||||
------ ----------------------------------------------------------
|
||||
87 Return type mixed of method
|
||||
Grav\Framework\Cache\AbstractCache::get() is not
|
||||
covariant with return type mixed of method
|
||||
Psr\SimpleCache\CacheInterface::get().
|
||||
102 Return type mixed of method
|
||||
Grav\Framework\Cache\AbstractCache::set() is not
|
||||
covariant with return type bool of method
|
||||
Psr\SimpleCache\CacheInterface::set().
|
||||
117 Return type mixed of method
|
||||
Grav\Framework\Cache\AbstractCache::delete() is not
|
||||
covariant with return type bool of method
|
||||
Psr\SimpleCache\CacheInterface::delete().
|
||||
127 Return type mixed of method
|
||||
Grav\Framework\Cache\AbstractCache::clear() is not
|
||||
covariant with return type bool of method
|
||||
Psr\SimpleCache\CacheInterface::clear().
|
||||
138 Return type mixed of method
|
||||
Grav\Framework\Cache\AbstractCache::getMultiple() is not
|
||||
covariant with return type iterable of method
|
||||
Psr\SimpleCache\CacheInterface::getMultiple().
|
||||
181 Return type mixed of method
|
||||
Grav\Framework\Cache\AbstractCache::setMultiple() is not
|
||||
covariant with return type bool of method
|
||||
Psr\SimpleCache\CacheInterface::setMultiple().
|
||||
214 Return type mixed of method
|
||||
Grav\Framework\Cache\AbstractCache::deleteMultiple() is
|
||||
not covariant with return type bool of method
|
||||
Psr\SimpleCache\CacheInterface::deleteMultiple().
|
||||
242 Return type mixed of method
|
||||
Grav\Framework\Cache\AbstractCache::has() is not
|
||||
covariant with return type bool of method
|
||||
Psr\SimpleCache\CacheInterface::has().
|
||||
------ ----------------------------------------------------------
|
||||
|
||||
------ ----------------------------------------------------------
|
||||
Line src/Grav/Framework/Collection/AbstractFileCollection.php
|
||||
------ ----------------------------------------------------------
|
||||
95 No error to ignore is reported on line 95.
|
||||
------ ----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Collection/AbstractIndexCollection.php
|
||||
------ -----------------------------------------------------------
|
||||
154 No error to ignore is reported on line 154.
|
||||
168 No error to ignore is reported on line 168.
|
||||
185 No error to ignore is reported on line 185.
|
||||
201 No error to ignore is reported on line 201.
|
||||
507 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Collection/AbstractLazyCollection.php
|
||||
------ -----------------------------------------------------------
|
||||
29 Property
|
||||
Grav\Framework\Collection\AbstractLazyCollection::$collec
|
||||
tion overriding property
|
||||
Doctrine\Common\Collections\AbstractLazyCollection<TKey o
|
||||
f (int|string),T>::$collection (Doctrine\Common\Collectio
|
||||
ns\Collection|null) should also have native type
|
||||
Doctrine\Common\Collections\Collection|null.
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Contracts/Relationships/RelationshipIn
|
||||
terface.php
|
||||
------ -----------------------------------------------------------
|
||||
80 Return type iterable of method
|
||||
Grav\Framework\Contracts\Relationships\RelationshipInterf
|
||||
ace::getIterator() is not covariant with tentative return
|
||||
type Traversable of method IteratorAggregate<string,T of
|
||||
Grav\Framework\Contracts\Object\IdentifierInterface>::get
|
||||
Iterator().
|
||||
💡 Make it covariant, or use the #[\ReturnTypeWillChange]
|
||||
attribute to temporarily suppress the error.
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Filesystem/Filesystem.php
|
||||
------ -----------------------------------------------------------
|
||||
51 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
252 No error to ignore is reported on line 252.
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ ---------------------------------------------
|
||||
Line src/Grav/Framework/Flex/FlexCollection.php
|
||||
------ ---------------------------------------------
|
||||
102 No error to ignore is reported on line 102.
|
||||
------ ---------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Flex/FlexIdentifier.php
|
||||
------ -----------------------------------------------------------
|
||||
27 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ ----------------------------------------------------------
|
||||
Line src/Grav/Framework/Flex/FlexIndex.php
|
||||
------ ----------------------------------------------------------
|
||||
109 No error to ignore is reported on line 109.
|
||||
934 Method Grav\Framework\Flex\FlexIndex::reduce() should
|
||||
return TInitial|TReturn but return statement is missing.
|
||||
🪪 return.missing
|
||||
------ ----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Form/FormFlashFile.php
|
||||
------ -----------------------------------------------------------
|
||||
62 Return type mixed of method
|
||||
Grav\Framework\Form\FormFlashFile::getStream() is not
|
||||
covariant with return type
|
||||
Psr\Http\Message\StreamInterface of method
|
||||
Psr\Http\Message\UploadedFileInterface::getStream().
|
||||
83 Return type mixed of method
|
||||
Grav\Framework\Form\FormFlashFile::moveTo() is not
|
||||
covariant with return type void of method
|
||||
Psr\Http\Message\UploadedFileInterface::moveTo().
|
||||
123 Return type mixed of method
|
||||
Grav\Framework\Form\FormFlashFile::getSize() is not
|
||||
covariant with return type int|null of method
|
||||
Psr\Http\Message\UploadedFileInterface::getSize().
|
||||
131 Return type mixed of method
|
||||
Grav\Framework\Form\FormFlashFile::getError() is not
|
||||
covariant with return type int of method
|
||||
Psr\Http\Message\UploadedFileInterface::getError().
|
||||
139 Return type mixed of method
|
||||
Grav\Framework\Form\FormFlashFile::getClientFilename() is
|
||||
not covariant with return type string|null of method
|
||||
Psr\Http\Message\UploadedFileInterface::getClientFilename
|
||||
().
|
||||
147 Return type mixed of method
|
||||
Grav\Framework\Form\FormFlashFile::getClientMediaType()
|
||||
is not covariant with return type string|null of method
|
||||
Psr\Http\Message\UploadedFileInterface::getClientMediaTyp
|
||||
e().
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Logger/Processors/UserProcessor.php
|
||||
------ -----------------------------------------------------------
|
||||
24 Parameter #1 $record (array) of method
|
||||
Grav\Framework\Logger\Processors\UserProcessor::__invoke(
|
||||
) is not contravariant with parameter #1 $record
|
||||
(Monolog\LogRecord) of method
|
||||
Monolog\Processor\ProcessorInterface::__invoke().
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Media/MediaIdentifier.php
|
||||
------ -----------------------------------------------------------
|
||||
30 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Media/UploadedMediaObject.php
|
||||
------ -----------------------------------------------------------
|
||||
36 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Mime/MimeTypes.php
|
||||
------ -----------------------------------------------------------
|
||||
42 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ ------------------------------------------------
|
||||
Line src/Grav/Framework/Object/ObjectCollection.php
|
||||
------ ------------------------------------------------
|
||||
96 No error to ignore is reported on line 96.
|
||||
------ ------------------------------------------------
|
||||
|
||||
------ ---------------------------------------------
|
||||
Line src/Grav/Framework/Object/ObjectIndex.php
|
||||
------ ---------------------------------------------
|
||||
193 No error to ignore is reported on line 193.
|
||||
------ ---------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Psr7/Stream.php
|
||||
------ -----------------------------------------------------------
|
||||
31 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Psr7/Traits/ServerRequestDecoratorTrai
|
||||
t.php (in context of class
|
||||
Grav\Framework\Psr7\ServerRequest)
|
||||
------ -----------------------------------------------------------
|
||||
51 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::getAttributes() is not
|
||||
covariant with return type array of method
|
||||
Psr\Http\Message\ServerRequestInterface::getAttributes().
|
||||
60 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::getCookieParams() is
|
||||
not covariant with return type array of method
|
||||
Psr\Http\Message\ServerRequestInterface::getCookieParams(
|
||||
).
|
||||
76 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::getQueryParams() is
|
||||
not covariant with return type array of method
|
||||
Psr\Http\Message\ServerRequestInterface::getQueryParams()
|
||||
.
|
||||
84 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::getServerParams() is
|
||||
not covariant with return type array of method
|
||||
Psr\Http\Message\ServerRequestInterface::getServerParams(
|
||||
).
|
||||
92 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::getUploadedFiles() is
|
||||
not covariant with return type array of method
|
||||
Psr\Http\Message\ServerRequestInterface::getUploadedFiles
|
||||
().
|
||||
100 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::withAttribute() is not
|
||||
covariant with return type
|
||||
Psr\Http\Message\ServerRequestInterface of method
|
||||
Psr\Http\Message\ServerRequestInterface::withAttribute().
|
||||
125 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::withoutAttribute() is
|
||||
not covariant with return type
|
||||
Psr\Http\Message\ServerRequestInterface of method
|
||||
Psr\Http\Message\ServerRequestInterface::withoutAttribute
|
||||
().
|
||||
136 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::withCookieParams() is
|
||||
not covariant with return type
|
||||
Psr\Http\Message\ServerRequestInterface of method
|
||||
Psr\Http\Message\ServerRequestInterface::withCookieParams
|
||||
().
|
||||
147 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::withParsedBody() is
|
||||
not covariant with return type
|
||||
Psr\Http\Message\ServerRequestInterface of method
|
||||
Psr\Http\Message\ServerRequestInterface::withParsedBody()
|
||||
.
|
||||
158 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::withQueryParams() is
|
||||
not covariant with return type
|
||||
Psr\Http\Message\ServerRequestInterface of method
|
||||
Psr\Http\Message\ServerRequestInterface::withQueryParams(
|
||||
).
|
||||
169 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::withUploadedFiles() is
|
||||
not covariant with return type
|
||||
Psr\Http\Message\ServerRequestInterface of method
|
||||
Psr\Http\Message\ServerRequestInterface::withUploadedFile
|
||||
s().
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Relationships/Relationships.php
|
||||
------ -----------------------------------------------------------
|
||||
107 Return type mixed of method
|
||||
Grav\Framework\Relationships\Relationships::offsetSet()
|
||||
is not covariant with tentative return type void of
|
||||
method
|
||||
ArrayAccess<string,Grav\Framework\Contracts\Relationships
|
||||
\RelationshipInterface<T of Grav\Framework\Contracts\Obje
|
||||
ct\IdentifierInterface, P of
|
||||
Grav\Framework\Contracts\Object\IdentifierInterface>>::of
|
||||
fsetSet().
|
||||
💡 Make it covariant, or use the #[\ReturnTypeWillChange]
|
||||
attribute to temporarily suppress the error.
|
||||
116 Return type mixed of method
|
||||
Grav\Framework\Relationships\Relationships::offsetUnset()
|
||||
is not covariant with tentative return type void of
|
||||
method
|
||||
ArrayAccess<string,Grav\Framework\Contracts\Relationships
|
||||
\RelationshipInterface<T of Grav\Framework\Contracts\Obje
|
||||
ct\IdentifierInterface, P of
|
||||
Grav\Framework\Contracts\Object\IdentifierInterface>>::of
|
||||
fsetUnset().
|
||||
💡 Make it covariant, or use the #[\ReturnTypeWillChange]
|
||||
attribute to temporarily suppress the error.
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Twig/DeferredExtension/DeferredNodeVisitorCompat.php
|
||||
------ -----------------------------------------------------------
|
||||
30 Parameter #1 $node (Twig_NodeInterface) of method
|
||||
Twig\DeferredExtension\DeferredNodeVisitorCompat::enterNo
|
||||
de() is not contravariant with parameter #1 $node
|
||||
(Twig\Node\Node) of method
|
||||
Twig\NodeVisitor\NodeVisitorInterface::enterNode().
|
||||
30 Parameter $node of method
|
||||
Twig\DeferredExtension\DeferredNodeVisitorCompat::enterNo
|
||||
de() has invalid type Twig_NodeInterface.
|
||||
🪪 class.notFound
|
||||
46 Parameter #1 $node (Twig_NodeInterface) of method
|
||||
Twig\DeferredExtension\DeferredNodeVisitorCompat::leaveNo
|
||||
de() is not contravariant with parameter #1 $node
|
||||
(Twig\Node\Node) of method
|
||||
Twig\NodeVisitor\NodeVisitorInterface::leaveNode().
|
||||
46 Parameter $node of method
|
||||
Twig\DeferredExtension\DeferredNodeVisitorCompat::leaveNo
|
||||
de() has invalid type Twig_NodeInterface.
|
||||
🪪 class.notFound
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
[ERROR] Found 74 errors
|
||||
|
||||
11
robots.txt
11
robots.txt
@@ -1,16 +1,21 @@
|
||||
User-agent: *
|
||||
Disallow: /.github/
|
||||
Disallow: /.phan/
|
||||
Disallow: /assets/
|
||||
Disallow: /backup/
|
||||
Disallow: /bin/
|
||||
Disallow: /cache/
|
||||
Disallow: /grav/
|
||||
Disallow: /logs/
|
||||
Disallow: /system/
|
||||
Disallow: /vendor/
|
||||
Disallow: /tests/
|
||||
Disallow: /tmp/
|
||||
Disallow: /user/
|
||||
Disallow: /vendor/
|
||||
Disallow: /webserver-configs/
|
||||
Allow: /user/pages/
|
||||
Allow: /user/themes/
|
||||
Allow: /user/images/
|
||||
Allow: /
|
||||
Allow: *.css$
|
||||
Allow: *.js$
|
||||
Allow: /system/*.js$
|
||||
Allow: /system/*.js$
|
||||
|
||||
@@ -1,2 +1,61 @@
|
||||
/** Clockwork Debugger CSS **/
|
||||
.clockwork-badge{position:fixed;z-index:10;bottom:0;left:0;padding:2px 4px;background-color:#eee;border:1px solid #ccc;border-bottom:0;border-left:0;display:flex;align-items:center}.clockwork-badge:hover{width:auto}.clockwork-badge:hover:after{content:'Grav Clockwork debugger enabled. Install Clockwork Browser extension (Chrome or Firefox), open your Developer tools and then select the Clockwork tab.'}.clockwork-badge:after{margin-left:10px;font-family:Monaco,Consolas,"Lucida Console",monospace;font-size:12px;line-height:1.5;color:#666}.clockwork-badge i{display:block;float:left;height:22px;width:22px;min-width:22px;background-size:contain;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAA/1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHh4AAAD///8EBAT7+/sLCwv29vYVFRUvLy/t7e3m5ubCwsKxsbE/Pz+mpqZMTEwcHBzy8vLp6emfn5+AgIA2Njbi4uLf39+rq6tzc3NWVlYhISHa2trW1tbS0tLMzMy7u7uZmZmUlJSMjIxvb29kZGRHR0c7Ozt5eXkqKiq1tbWQkJBqampbW1tSUlLHx8eHh4ckJCRDQ0M3wD42AAAAI3RSTlMA/PibTbQ0x76TVAlw4LhZLOuEYCAN9Hjx0a2ppGZEGYw97djhXHwAAATZSURBVFjDlVcHW+MwDO1eFCjj2McNOzvdpXTTXVbL/P+/5SQ7QSSX5Di1X1onfi/Sk+Q4sTDbKqWK+YuznZ2zi3wxVdqK/Zf92M1nT9gnO8rmd398GX6Z3xaoOFoiAQcx3E5efgmeSuN8F6Xg1x3G06l/wjNpMR1B0uif4EhnIuFb+0diIoFXk3IVfokisR+h52GO4JKgyjmfaMhAFNlSaPR7DpwI+lzn/E4QKIqmKIJirxCMP4izBPPZPXhgXwMBYgULw0nfg/BF5scDbslb7QeJ08yqqTEmGYoB95d4H8ETL8+n9wBqrLu6ao3bBsMwAnxISf/9BHcqxNB8Y7cWl3Zz7TAUfPrvAT6AoNEFFXvsjutL01yOuMrtBxnFXsmT/1wQHmdWAFNnI3uI48Yj0FUcHbKf62GfUfr8eeQt7Uk3mQZpZNoVRPEui5vtEz5zFEpgWnyqVBZMc6oaGNriH2hGVZ0OxEvInPeMaZWJBA7vmPbCr5jjws5HBnAUxvDMH40aCIf4G5BjRQSs8E8HFFYf8bGxgDvD55bzGhwWkoBcuIyHR/AMdaCagxXDhtL6tSqoWpd4BMnlIR+Or+rYTK/a3EAGcc6e4AWHISnWv20iCCojsHoVlQdjrMexFF2C7UMg2A2WEGWbQhXN6l3eXC6XGp4b9qxbuEB2EBGBwtocrK90cVG5mbRXm6vmx/0phq1sIAGKDgLOBiN1MrO5a9aDl+D0W6x0Ar9BCTRuIIANa90Y7LrLVRXzwVtDInCqMRWcf2bUOEAsa4wJqFowQALL9EiAtVRk8QC4OW+1pOM9jIaVASwYagyNXDj+W0NcfuZNzjtXOiL0Zzg30Llj+ptfxQs4+vBPNiL5PawFCBkgXpUaVtqGl+A8dgZHL34BcBUQrwPptToW+o37Ku+UH9eYByJIx3YkAeFnMFuGO7S5gEp7YhXxa5OOAM39RXDPXb0qmpROsswZe+twXdU55oUIZAiEv3bD1UFwIYKkmGqytPCDCwKFQCKK0yL7qtSAPX54UAbtsLuBHkb9zyLmPQSNjsSgmQwKUOIfEY8F8t4B34DvndJY9BA8tNBJq1Nev9axmaStFcQLhgYoCTo0salkIaW8OUDdWjMTR2sHPhrAFZqx6cqcKE4pl2BJJ4K6hfwvqNgAnXfKX/HU6X3Zrhnu0k7tLNZtTBRv1hkwTDBY1NzFU6doDYjJbWdQkQhWwuU7/LvhTh3SDoco4ECL4i5dwURbc8NdDZz2IwKicE8d0KIqWetLE3+lL4hvUuGSeRfVWNLfj/gpOw4smBJBkKQHCzlHGwvAj4woB1gq5NGGLSXtORBPnUQPV5/MPVkDMxbpwG7w4x0xL6Ltxka0A/4NBvV09UVk4DoSn/jl2+JQS9q9KYawisAD4CfhsZ4TH3htylsdEHARIQBusqCKyUpymycgbbkkXEXjT3z7/oKQFTFVuZD2FMJHZIDsO5x2d4aAr2jR+GLwZhtAb028/0yJ9J8dE87jQyKObcjtTXT8dH+fDuKF4/eiPwzH44wTf/yUi6wrpRIOZ9lM1EtXAifFI+CJn9+iX/t2xMQwOMth/UZbASi8btAwR9FHWSpJr75g9Oqbin3VDg+SpwlP6k6TB4ex/7JvmcJx8jydy6XPk8eFTKhyfwCgX71MSvaBHgAAAABJRU5ErkJggg==)}
|
||||
.clockwork-badge {
|
||||
position: fixed;
|
||||
z-index: 1000; /* Increased z-index for better visibility */
|
||||
bottom: 0; /* Added some spacing from the bottom */
|
||||
left: 0; /* Added some spacing from the left */
|
||||
padding: 5px;
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
border-bottom: 0;
|
||||
border-left: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 0 4px 0 0; /* Rounded top corners */
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.clockwork-badge:hover {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
.clockwork-badge i {
|
||||
display: block;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
background-size: contain;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAA/1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHh4AAAD///8EBAT7+/sLCwv29vYVFRUvLy/t7e3m5ubCwsKxsbE/Pz+mpqZMTEwcHBzy8vLp6emfn5+AgIA2Njbi4uLf39+rq6tzc3NWVlYhISHa2trW1tbS0tLMzMy7u7uZmZmUlJSMjIxvb29kZGRHR0c7Ozt5eXkqKiq1tbWQkJBqampbW1tSUlLHx8eHh4ckJCRDQ0M3wD42AAAAI3RSTlMA/PibTbQ0x76TVAlw4LhZLOuEYCAN9Hjx0a2ppGZEGYw97djhXHwAAATZSURBVFjDlVcHW+MwDO1eFCjj2McNOzvdpXTTXVbL/P+/5SQ7QSSX5Di1X1onfi/Sk+Q4sTDbKqWK+YuznZ2zi3wxVdqK/Zf92M1nT9gnO8rmd398GX6Z3xaoOFoiAQcx3E5efgmeSuN8F6Xg1x3G06l/wjNpMR1B0uif4EhnIuFb+0diIoFXk3IVfokisR+h52GO4JKgyjmfaMhAFNlSaPR7DpwI+lzn/E4QKIqmKIJirxCMP4izBPPZPXhgXwMBYgULw0nfg/BF5scDbslb7QeJ08yqqTEmGYoB95d4H8ETL8+n9wBqrLu6ao3bBsMwAnxISf/9BHcqxNB8Y7cWl3Zz7TAUfPrvAT6AoNEFFXvsjutL01yOuMrtBxnFXsmT/1wQHmdWAFNnI3uI48Yj0FUcHbKf62GfUfr8eeQt7Uk3mQZpZNoVRPEui5vtEz5zFEpgWnyqVBZMc6oaGNriH2hGVZ0OxEvInPeMaZWJBA7vmPbCr5jjws5HBnAUxvDMH40aCIf4G5BjRQSs8E8HFFYf8bGxgDvD55bzGhwWkoBcuIyHR/AMdaCagxXDhtL6tSqoWpd4BMnlIR+Or+rYTK/a3EAGcc6e4AWHISnWv20iCCojsHoVlQdjrMexFF2C7UMg2A2WEGWbQhXN6l3eXC6XGp4b9qxbuEB2EBGBwtocrK90cVG5mbRXm6vmx/0phq1sIAGKDgLOBiN1MrO5a9aDl+D0W6x0Ar9BCTRuIIANa90Y7LrLVRXzwVtDInCqMRWcf2bUOEAsa4wJqFowQALL9EiAtVRk8QC4OW+1pOM9jIaVASwYagyNXDj+W0NcfuZNzjtXOiL0Zzg30Llj+ptfxQs4+vBPNiL5PawFCBkgXpUaVtqGl+A8dgZHL34BcBUQrwPptToW+o37Ku+UH9eYByJIx3YkAeFnMFuGO7S5gEp7YhXxa5OOAM39RXDPXb0qmpROsswZe+twXdU55oUIZAiEv3bD1UFwIYKkmGqytPCDCwKFQCKK0yL7qtSAPX54UAbtsLuBHkb9zyLmPQSNjsSgmQwKUOIfEY8F8t4B34DvndJY9BA8tNBJq1Nev9axmaStFcQLhgYoCTo0salkIaW8OUDdWjMTR2sHPhrAFZqx6cqcKE4pl2BJJ4K6hfwvqNgAnXfKX/HU6X3Zrhnu0k7tLNZtTBRv1hkwTDBY1NzFU6doDYjJbWdQkQhWwuU7/LvhTh3SDoco4ECL4i5dwURbc8NdDZz2IwKicE8d0KIqWetLE3+lL4hvUuGSeRfVWNLfj/gpOw4smBJBkKQHCzlHGwvAj4woB1gq5NGGLSXtORBPnUQPV5/MPVkDMxbpwG7w4x0xL6Ltxka0A/4NBvV09UVk4DoSn/jl2+JQS9q9KYawisAD4CfhsZ4TH3htylsdEHARIQBusqCKyUpymycgbbkkXEXjT3z7/oKQFTFVuZD2FMJHZIDsO5x2d4aAr2jR+GLwZhtAb028/0yJ9J8dE87jQyKObcjtTXT8dH+fDuKF4/eiPwzH44wTf/yUi6wrpRIOZ9lM1EtXAifFI+CJn9+iX/t2xMQwOMth/UZbASi8btAwR9FHWSpJr75g9Oqbin3VDg+SpwlP6k6TB4ex/7JvmcJx8jydy6XPk8eFTKhyfwCgX71MSvaBHgAAAABJRU5ErkJggg==);
|
||||
}
|
||||
|
||||
.clockwork-badge .tooltip {
|
||||
display: none; /* Hidden by default */
|
||||
position: absolute;
|
||||
bottom: 35px; /* Position above the badge */
|
||||
left: 0;
|
||||
width: 450px;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
||||
z-index: 1001; /* Ensure it appears above other elements */
|
||||
}
|
||||
|
||||
.clockwork-badge:hover .tooltip {
|
||||
display: block; /* Show tooltip on hover */
|
||||
}
|
||||
|
||||
.clockwork-badge .tooltip a {
|
||||
color: #007BFF;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.clockwork-badge .tooltip a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,37 @@
|
||||
/** Clockwork Debugger JS **/
|
||||
document.addEventListener("DOMContentLoaded",function () {
|
||||
var e=document.createElement("div");e.appendChild(document.createElement("i")),e.className="clockwork-badge",document.body.appendChild(e)});
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
// Directly select the script tag by its id
|
||||
var currentScript = document.getElementById('clockwork-script');
|
||||
|
||||
if (!currentScript) {
|
||||
console.error("Clockwork Debugger: Script tag with id 'clockwork-script' not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
var route = currentScript.getAttribute('data-route') || '/clockwork'; // Default route if not specified
|
||||
|
||||
// Debugging: Log the route to verify
|
||||
console.log("Clockwork Debugger Route:", route);
|
||||
|
||||
// Create the badge container
|
||||
var badge = document.createElement("div");
|
||||
badge.className = "clockwork-badge";
|
||||
badge.setAttribute('aria-label', 'Clockwork Debugger Enabled');
|
||||
badge.setAttribute('role', 'button');
|
||||
|
||||
// Create the icon element
|
||||
var icon = document.createElement("i");
|
||||
badge.appendChild(icon);
|
||||
|
||||
// Create the tooltip element
|
||||
var tooltip = document.createElement("div");
|
||||
tooltip.className = "tooltip";
|
||||
tooltip.innerHTML = `
|
||||
<b>Grav Clockwork Debugger Enabled.</b><br>
|
||||
Install the <b>Clockwork Browser extension</b> (Chrome or Firefox) or use the <b>"Clockwork Web"</b> Grav plugin to <a href="${route}" target="_blank">View Debug Info 🔗</a>.
|
||||
`;
|
||||
badge.appendChild(tooltip);
|
||||
|
||||
// Append the badge to the body
|
||||
document.body.appendChild(badge);
|
||||
});
|
||||
@@ -1,70 +1,5 @@
|
||||
div.phpdebugbar {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
div.phpdebugbar a.phpdebugbar-restore-btn::after {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAA/1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHh4AAAD///8EBAT7+/sLCwv29vYVFRUvLy/t7e3m5ubCwsKxsbE/Pz+mpqZMTEwcHBzy8vLp6emfn5+AgIA2Njbi4uLf39+rq6tzc3NWVlYhISHa2trW1tbS0tLMzMy7u7uZmZmUlJSMjIxvb29kZGRHR0c7Ozt5eXkqKiq1tbWQkJBqampbW1tSUlLHx8eHh4ckJCRDQ0M3wD42AAAAI3RSTlMA/PibTbQ0x76TVAlw4LhZLOuEYCAN9Hjx0a2ppGZEGYw97djhXHwAAATZSURBVFjDlVcHW+MwDO1eFCjj2McNOzvdpXTTXVbL/P+/5SQ7QSSX5Di1X1onfi/Sk+Q4sTDbKqWK+YuznZ2zi3wxVdqK/Zf92M1nT9gnO8rmd398GX6Z3xaoOFoiAQcx3E5efgmeSuN8F6Xg1x3G06l/wjNpMR1B0uif4EhnIuFb+0diIoFXk3IVfokisR+h52GO4JKgyjmfaMhAFNlSaPR7DpwI+lzn/E4QKIqmKIJirxCMP4izBPPZPXhgXwMBYgULw0nfg/BF5scDbslb7QeJ08yqqTEmGYoB95d4H8ETL8+n9wBqrLu6ao3bBsMwAnxISf/9BHcqxNB8Y7cWl3Zz7TAUfPrvAT6AoNEFFXvsjutL01yOuMrtBxnFXsmT/1wQHmdWAFNnI3uI48Yj0FUcHbKf62GfUfr8eeQt7Uk3mQZpZNoVRPEui5vtEz5zFEpgWnyqVBZMc6oaGNriH2hGVZ0OxEvInPeMaZWJBA7vmPbCr5jjws5HBnAUxvDMH40aCIf4G5BjRQSs8E8HFFYf8bGxgDvD55bzGhwWkoBcuIyHR/AMdaCagxXDhtL6tSqoWpd4BMnlIR+Or+rYTK/a3EAGcc6e4AWHISnWv20iCCojsHoVlQdjrMexFF2C7UMg2A2WEGWbQhXN6l3eXC6XGp4b9qxbuEB2EBGBwtocrK90cVG5mbRXm6vmx/0phq1sIAGKDgLOBiN1MrO5a9aDl+D0W6x0Ar9BCTRuIIANa90Y7LrLVRXzwVtDInCqMRWcf2bUOEAsa4wJqFowQALL9EiAtVRk8QC4OW+1pOM9jIaVASwYagyNXDj+W0NcfuZNzjtXOiL0Zzg30Llj+ptfxQs4+vBPNiL5PawFCBkgXpUaVtqGl+A8dgZHL34BcBUQrwPptToW+o37Ku+UH9eYByJIx3YkAeFnMFuGO7S5gEp7YhXxa5OOAM39RXDPXb0qmpROsswZe+twXdU55oUIZAiEv3bD1UFwIYKkmGqytPCDCwKFQCKK0yL7qtSAPX54UAbtsLuBHkb9zyLmPQSNjsSgmQwKUOIfEY8F8t4B34DvndJY9BA8tNBJq1Nev9axmaStFcQLhgYoCTo0salkIaW8OUDdWjMTR2sHPhrAFZqx6cqcKE4pl2BJJ4K6hfwvqNgAnXfKX/HU6X3Zrhnu0k7tLNZtTBRv1hkwTDBY1NzFU6doDYjJbWdQkQhWwuU7/LvhTh3SDoco4ECL4i5dwURbc8NdDZz2IwKicE8d0KIqWetLE3+lL4hvUuGSeRfVWNLfj/gpOw4smBJBkKQHCzlHGwvAj4woB1gq5NGGLSXtORBPnUQPV5/MPVkDMxbpwG7w4x0xL6Ltxka0A/4NBvV09UVk4DoSn/jl2+JQS9q9KYawisAD4CfhsZ4TH3htylsdEHARIQBusqCKyUpymycgbbkkXEXjT3z7/oKQFTFVuZD2FMJHZIDsO5x2d4aAr2jR+GLwZhtAb028/0yJ9J8dE87jQyKObcjtTXT8dH+fDuKF4/eiPwzH44wTf/yUi6wrpRIOZ9lM1EtXAifFI+CJn9+iX/t2xMQwOMth/UZbASi8btAwR9FHWSpJr75g9Oqbin3VDg+SpwlP6k6TB4ex/7JvmcJx8jydy6XPk8eFTKhyfwCgX71MSvaBHgAAAABJRU5ErkJggg==) !important;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.phpdebugbar pre {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header > div > * {
|
||||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header > div.phpdebugbar-header-right > * {
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header, .phpdebugbar a.phpdebugbar-restore-btn {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAA/1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHh4AAAD///8EBAT7+/sLCwv29vYVFRUvLy/t7e3m5ubCwsKxsbE/Pz+mpqZMTEwcHBzy8vLp6emfn5+AgIA2Njbi4uLf39+rq6tzc3NWVlYhISHa2trW1tbS0tLMzMy7u7uZmZmUlJSMjIxvb29kZGRHR0c7Ozt5eXkqKiq1tbWQkJBqampbW1tSUlLHx8eHh4ckJCRDQ0M3wD42AAAAI3RSTlMA/PibTbQ0x76TVAlw4LhZLOuEYCAN9Hjx0a2ppGZEGYw97djhXHwAAATZSURBVFjDlVcHW+MwDO1eFCjj2McNOzvdpXTTXVbL/P+/5SQ7QSSX5Di1X1onfi/Sk+Q4sTDbKqWK+YuznZ2zi3wxVdqK/Zf92M1nT9gnO8rmd398GX6Z3xaoOFoiAQcx3E5efgmeSuN8F6Xg1x3G06l/wjNpMR1B0uif4EhnIuFb+0diIoFXk3IVfokisR+h52GO4JKgyjmfaMhAFNlSaPR7DpwI+lzn/E4QKIqmKIJirxCMP4izBPPZPXhgXwMBYgULw0nfg/BF5scDbslb7QeJ08yqqTEmGYoB95d4H8ETL8+n9wBqrLu6ao3bBsMwAnxISf/9BHcqxNB8Y7cWl3Zz7TAUfPrvAT6AoNEFFXvsjutL01yOuMrtBxnFXsmT/1wQHmdWAFNnI3uI48Yj0FUcHbKf62GfUfr8eeQt7Uk3mQZpZNoVRPEui5vtEz5zFEpgWnyqVBZMc6oaGNriH2hGVZ0OxEvInPeMaZWJBA7vmPbCr5jjws5HBnAUxvDMH40aCIf4G5BjRQSs8E8HFFYf8bGxgDvD55bzGhwWkoBcuIyHR/AMdaCagxXDhtL6tSqoWpd4BMnlIR+Or+rYTK/a3EAGcc6e4AWHISnWv20iCCojsHoVlQdjrMexFF2C7UMg2A2WEGWbQhXN6l3eXC6XGp4b9qxbuEB2EBGBwtocrK90cVG5mbRXm6vmx/0phq1sIAGKDgLOBiN1MrO5a9aDl+D0W6x0Ar9BCTRuIIANa90Y7LrLVRXzwVtDInCqMRWcf2bUOEAsa4wJqFowQALL9EiAtVRk8QC4OW+1pOM9jIaVASwYagyNXDj+W0NcfuZNzjtXOiL0Zzg30Llj+ptfxQs4+vBPNiL5PawFCBkgXpUaVtqGl+A8dgZHL34BcBUQrwPptToW+o37Ku+UH9eYByJIx3YkAeFnMFuGO7S5gEp7YhXxa5OOAM39RXDPXb0qmpROsswZe+twXdU55oUIZAiEv3bD1UFwIYKkmGqytPCDCwKFQCKK0yL7qtSAPX54UAbtsLuBHkb9zyLmPQSNjsSgmQwKUOIfEY8F8t4B34DvndJY9BA8tNBJq1Nev9axmaStFcQLhgYoCTo0salkIaW8OUDdWjMTR2sHPhrAFZqx6cqcKE4pl2BJJ4K6hfwvqNgAnXfKX/HU6X3Zrhnu0k7tLNZtTBRv1hkwTDBY1NzFU6doDYjJbWdQkQhWwuU7/LvhTh3SDoco4ECL4i5dwURbc8NdDZz2IwKicE8d0KIqWetLE3+lL4hvUuGSeRfVWNLfj/gpOw4smBJBkKQHCzlHGwvAj4woB1gq5NGGLSXtORBPnUQPV5/MPVkDMxbpwG7w4x0xL6Ltxka0A/4NBvV09UVk4DoSn/jl2+JQS9q9KYawisAD4CfhsZ4TH3htylsdEHARIQBusqCKyUpymycgbbkkXEXjT3z7/oKQFTFVuZD2FMJHZIDsO5x2d4aAr2jR+GLwZhtAb028/0yJ9J8dE87jQyKObcjtTXT8dH+fDuKF4/eiPwzH44wTf/yUi6wrpRIOZ9lM1EtXAifFI+CJn9+iX/t2xMQwOMth/UZbASi8btAwR9FHWSpJr75g9Oqbin3VDg+SpwlP6k6TB4ex/7JvmcJx8jydy6XPk8eFTKhyfwCgX71MSvaBHgAAAABJRU5ErkJggg==);
|
||||
}
|
||||
|
||||
.phpdebugbar a.phpdebugbar-restore-btn {
|
||||
width: 13px;
|
||||
}
|
||||
|
||||
.phpdebugbar a.phpdebugbar-tab.phpdebugbar-active {
|
||||
background: #3DB9EC;
|
||||
color: #fff;
|
||||
margin-top: -1px;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.phpdebugbar .phpdebugbar-widgets-toolbar {
|
||||
border-top: 1px solid #ddd;
|
||||
padding-left: 5px;
|
||||
padding-right: 2px;
|
||||
padding-top: 2px;
|
||||
background-color: #fafafa !important;
|
||||
width: auto !important;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.phpdebugbar .phpdebugbar-widgets-toolbar input {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.phpdebugbar .phpdebugbar-widgets-toolbar .phpdebugbar-widgets-filter {
|
||||
|
||||
}
|
||||
|
||||
|
||||
.phpdebugbar input[type=text] {
|
||||
padding: 0;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.phpdebugbar dl.phpdebugbar-widgets-varlist, ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label {
|
||||
font-family: "DejaVu Sans Mono", Menlo, Monaco, Consolas, Courier, monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label {
|
||||
text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.phpdebugbar pre, .phpdebugbar code {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
4
system/assets/jquery/jquery-3.x.min.js
vendored
4
system/assets/jquery/jquery-3.x.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -4,74 +4,788 @@ form:
|
||||
validation: loose
|
||||
|
||||
fields:
|
||||
scheduler_tabs:
|
||||
type: tabs
|
||||
active: 1
|
||||
|
||||
status_title:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.SCHEDULER_STATUS
|
||||
underline: true
|
||||
fields:
|
||||
status_tab:
|
||||
type: tab
|
||||
title: PLUGIN_ADMIN.SCHEDULER_STATUS
|
||||
|
||||
status:
|
||||
type: cronstatus
|
||||
validate:
|
||||
type: commalist
|
||||
fields:
|
||||
status_title:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.SCHEDULER_STATUS
|
||||
underline: true
|
||||
|
||||
jobs_title:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.SCHEDULER_JOBS
|
||||
underline: true
|
||||
status:
|
||||
type: cronstatus
|
||||
validate:
|
||||
type: commalist
|
||||
|
||||
webhook_status_override:
|
||||
type: display
|
||||
label:
|
||||
content: |
|
||||
<script>
|
||||
(function() {
|
||||
function updateSchedulerStatus() {
|
||||
// Find all notice bars
|
||||
var notices = document.querySelectorAll('.notice');
|
||||
var webhookStatusChecked = false;
|
||||
|
||||
// Check for modern scheduler and webhook settings
|
||||
fetch(window.location.origin + '/grav-editor-pro/scheduler/health')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.webhook_enabled) {
|
||||
notices.forEach(function(notice) {
|
||||
if (notice.textContent.includes('Not Enabled for user:')) {
|
||||
// This is the cron status notice - replace it
|
||||
notice.className = 'notice info';
|
||||
notice.innerHTML = '<i class="fa fa-fw fa-check-circle"></i> <strong>Webhook Active</strong> - Scheduler can be triggered via webhook. Cron is not configured.';
|
||||
}
|
||||
});
|
||||
|
||||
// Also update the main status if it exists
|
||||
var statusDiv = document.querySelector('.cronstatus-status');
|
||||
if (statusDiv && statusDiv.textContent.includes('Not Enabled')) {
|
||||
statusDiv.className = 'cronstatus-status success';
|
||||
statusDiv.innerHTML = '<i class="fa fa-fw fa-check"></i> Webhook Ready';
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('Webhook status check failed:', error);
|
||||
});
|
||||
}
|
||||
|
||||
// Run on page load
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', updateSchedulerStatus);
|
||||
} else {
|
||||
updateSchedulerStatus();
|
||||
}
|
||||
|
||||
// Also run after a short delay to catch any late-rendered elements
|
||||
setTimeout(updateSchedulerStatus, 500);
|
||||
})();
|
||||
</script>
|
||||
markdown: false
|
||||
|
||||
status_enhanced:
|
||||
type: display
|
||||
label:
|
||||
content: |
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Check if webhook is enabled
|
||||
var webhookEnabled = document.querySelector('[name="data[scheduler][modern][webhook][enabled]"]:checked');
|
||||
var statusDiv = document.querySelector('.cronstatus-status');
|
||||
|
||||
// Also find the parent notice bar
|
||||
var noticeBar = document.querySelector('.notice.alert');
|
||||
|
||||
if (statusDiv) {
|
||||
var currentStatus = statusDiv.textContent || statusDiv.innerText;
|
||||
var cronReady = currentStatus.includes('Ready');
|
||||
var cronNotEnabled = currentStatus.includes('Not Enabled');
|
||||
|
||||
// Check if scheduler-webhook plugin exists
|
||||
var webhookPluginInstalled = false;
|
||||
fetch(window.location.origin + '/grav-editor-pro/scheduler/health')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
webhookPluginInstalled = true;
|
||||
updateStatusDisplay(data);
|
||||
})
|
||||
.catch(error => {
|
||||
updateStatusDisplay(null);
|
||||
});
|
||||
|
||||
function updateStatusDisplay(healthData) {
|
||||
var isWebhookEnabled = webhookEnabled && webhookEnabled.value == '1';
|
||||
var isWebhookReady = webhookPluginInstalled && isWebhookEnabled && healthData && healthData.webhook_enabled;
|
||||
|
||||
// Update the main status text
|
||||
var mainStatusText = '';
|
||||
var mainStatusClass = '';
|
||||
|
||||
if (cronReady && isWebhookReady) {
|
||||
mainStatusText = 'Cron and Webhook Ready';
|
||||
mainStatusClass = 'success';
|
||||
} else if (cronReady) {
|
||||
mainStatusText = 'Cron Ready';
|
||||
mainStatusClass = 'success';
|
||||
} else if (isWebhookReady) {
|
||||
mainStatusText = 'Webhook Ready (No Cron)';
|
||||
mainStatusClass = 'success'; // Changed from warning to success
|
||||
} else if (cronNotEnabled && !isWebhookReady) {
|
||||
mainStatusText = 'Not Configured';
|
||||
mainStatusClass = 'error';
|
||||
} else {
|
||||
mainStatusText = 'Configuration Pending';
|
||||
mainStatusClass = 'warning';
|
||||
}
|
||||
|
||||
// Update the notice bar if webhooks are ready
|
||||
if (noticeBar && isWebhookReady) {
|
||||
// Change from error (red) to success (green) or info (blue)
|
||||
noticeBar.classList.remove('alert');
|
||||
noticeBar.classList.add('info');
|
||||
|
||||
var noticeIcon = noticeBar.querySelector('i.fa');
|
||||
if (noticeIcon) {
|
||||
noticeIcon.classList.remove('fa-times-circle');
|
||||
noticeIcon.classList.add('fa-check-circle');
|
||||
}
|
||||
|
||||
var noticeText = noticeBar.querySelector('strong') || noticeBar;
|
||||
var username = noticeText.textContent.match(/user:\s*(\w+)/);
|
||||
if (username) {
|
||||
noticeText.innerHTML = 'Webhook Ready for user: <b>' + username[1] + '</b> (Cron not configured)';
|
||||
} else {
|
||||
noticeText.innerHTML = mainStatusText;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the main status div
|
||||
if (statusDiv) {
|
||||
statusDiv.innerHTML = '<i class="fa fa-fw fa-' +
|
||||
(mainStatusClass === 'success' ? 'check' : mainStatusClass === 'warning' ? 'exclamation' : 'times') +
|
||||
'"></i> ' + mainStatusText;
|
||||
statusDiv.className = 'cronstatus-status ' + mainStatusClass;
|
||||
}
|
||||
|
||||
// Update install instructions button/content
|
||||
var installButton = document.querySelector('.cronstatus-install-button');
|
||||
var installDiv = document.querySelector('.cronstatus-install');
|
||||
|
||||
if (installDiv) {
|
||||
var installHtml = '<div class="alert alert-info">';
|
||||
installHtml += '<h4>Setup Instructions:</h4>';
|
||||
|
||||
var hasInstructions = false;
|
||||
|
||||
// Cron setup
|
||||
if (!cronReady) {
|
||||
installHtml += '<p><strong>Option 1: Traditional Cron</strong><br>';
|
||||
installHtml += 'Run: <code>bin/grav scheduler --install</code><br>';
|
||||
installHtml += 'This will add a cron job that runs every minute.</p>';
|
||||
hasInstructions = true;
|
||||
}
|
||||
|
||||
// Webhook setup
|
||||
if (!webhookPluginInstalled) {
|
||||
installHtml += '<p><strong>Option 2: Webhook Support</strong><br>';
|
||||
installHtml += '1. Install plugin: <code>bin/gpm install scheduler-webhook</code><br>';
|
||||
installHtml += '2. Configure webhook token in Advanced Features tab<br>';
|
||||
installHtml += '3. Use webhook URL in your CI/CD or cloud scheduler</p>';
|
||||
hasInstructions = true;
|
||||
} else if (!isWebhookEnabled) {
|
||||
installHtml += '<p><strong>Webhook Plugin Installed</strong><br>';
|
||||
installHtml += 'Enable webhooks in Advanced Features tab and set a secure token.</p>';
|
||||
hasInstructions = true;
|
||||
} else if (isWebhookReady) {
|
||||
installHtml += '<p><strong>✅ Webhook is Active!</strong><br>';
|
||||
installHtml += 'Trigger URL: <code>' + window.location.origin + '/grav-editor-pro/scheduler/webhook</code><br>';
|
||||
installHtml += 'Use with Authorization header: <code>Bearer YOUR_TOKEN</code></p>';
|
||||
|
||||
if (!cronReady) {
|
||||
installHtml += '<p class="text-muted"><small>Note: No cron job configured. Scheduler runs only via webhook triggers.</small></p>';
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasInstructions && cronReady) {
|
||||
installHtml += '<p><strong>✅ Cron is configured and ready!</strong><br>';
|
||||
installHtml += 'The scheduler runs automatically every minute via system cron.</p>';
|
||||
|
||||
}
|
||||
|
||||
installHtml += '</div>';
|
||||
installDiv.innerHTML = installHtml;
|
||||
|
||||
// Update button text based on status
|
||||
if (installButton) {
|
||||
if (cronReady && isWebhookReady) {
|
||||
installButton.innerHTML = '<i class="fa fa-info-circle"></i> Configuration Details';
|
||||
} else if (cronReady || isWebhookReady) {
|
||||
installButton.innerHTML = '<i class="fa fa-plus-circle"></i> Add More Triggers';
|
||||
} else {
|
||||
installButton.innerHTML = '<i class="fa fa-exclamation-triangle"></i> Install Instructions';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
custom_jobs:
|
||||
type: list
|
||||
style: vertical
|
||||
label:
|
||||
classes: cron-job-list compact
|
||||
key: id
|
||||
fields:
|
||||
.id:
|
||||
type: key
|
||||
label: ID
|
||||
placeholder: 'process-name'
|
||||
validate:
|
||||
required: true
|
||||
pattern: '[a-zа-я0-9_\-]+'
|
||||
max: 20
|
||||
message: 'ID must be lowercase with dashes/underscores only and less than 20 characters'
|
||||
.command:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.COMMAND
|
||||
placeholder: 'ls'
|
||||
validate:
|
||||
required: true
|
||||
.args:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.EXTRA_ARGUMENTS
|
||||
placeholder: '-lah'
|
||||
.at:
|
||||
type: text
|
||||
wrapper_classes: cron-selector
|
||||
label: PLUGIN_ADMIN.SCHEDULER_RUNAT
|
||||
help: PLUGIN_ADMIN.SCHEDULER_RUNAT_HELP
|
||||
placeholder: '* * * * *'
|
||||
validate:
|
||||
required: true
|
||||
.output:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.SCHEDULER_OUTPUT
|
||||
help: PLUGIN_ADMIN.SCHEDULER_OUTPUT_HELP
|
||||
placeholder: 'logs/ls-cron.out'
|
||||
.output_mode:
|
||||
type: select
|
||||
label: PLUGIN_ADMIN.SCHEDULER_OUTPUT_TYPE
|
||||
help: PLUGIN_ADMIN.SCHEDULER_OUTPUT_TYPE_HELP
|
||||
default: append
|
||||
options:
|
||||
append: Append
|
||||
overwrite: Overwrite
|
||||
.email:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.SCHEDULER_EMAIL
|
||||
help: PLUGIN_ADMIN.SCHEDULER_EMAIL_HELP
|
||||
placeholder: 'notifications@yoursite.com'
|
||||
modern_health:
|
||||
type: display
|
||||
label: Health Status
|
||||
content: |
|
||||
<div id="scheduler-health-status">
|
||||
<div class="text-muted">Checking health...</div>
|
||||
</div>
|
||||
<script>
|
||||
(function() {
|
||||
function loadHealthStatus() {
|
||||
fetch(window.location.origin + '/grav-editor-pro/scheduler/health')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
var statusEl = document.getElementById('scheduler-health-status');
|
||||
if (!statusEl) return;
|
||||
|
||||
// Modern card-based layout
|
||||
var statusColor = '#6c757d';
|
||||
var statusLabel = data.status || 'unknown';
|
||||
if (data.status === 'healthy') statusColor = '#28a745';
|
||||
else if (data.status === 'warning') statusColor = '#ffc107';
|
||||
else if (data.status === 'critical') statusColor = '#dc3545';
|
||||
|
||||
var html = '<div style="display: flex; flex-direction: column; gap: 1rem;">';
|
||||
|
||||
// Status card
|
||||
html += '<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: linear-gradient(135deg, #f8f9fa 0%, #fff 100%); border-radius: 6px; border: 1px solid #e9ecef; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">';
|
||||
html += '<span style="font-weight: 500; color: #495057;">Status:</span>';
|
||||
html += '<span style="background: ' + statusColor + '; color: white; padding: 0.375rem 0.75rem; font-size: 0.875rem; font-weight: 500; border-radius: 4px; text-transform: uppercase; letter-spacing: 0.025em;">' + statusLabel + '</span>';
|
||||
html += '</div>';
|
||||
|
||||
// Info grid
|
||||
html += '<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem;">';
|
||||
|
||||
// Last run card
|
||||
html += '<div style="background: white; border: 1px solid #e9ecef; border-radius: 6px; padding: 0.75rem; box-shadow: 0 1px 2px rgba(0,0,0,0.03);">';
|
||||
html += '<div style="color: #6c757d; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.25rem;">Last Run</div>';
|
||||
if (data.last_run) {
|
||||
var age = data.last_run_age;
|
||||
var ageText = 'just now';
|
||||
if (age > 86400) {
|
||||
ageText = Math.floor(age / 86400) + ' day(s) ago';
|
||||
} else if (age > 3600) {
|
||||
ageText = Math.floor(age / 3600) + ' hour(s) ago';
|
||||
} else if (age > 60) {
|
||||
ageText = Math.floor(age / 60) + ' minute(s) ago';
|
||||
} else if (age > 0) {
|
||||
ageText = age + ' second(s) ago';
|
||||
}
|
||||
html += '<div style="font-size: 1rem; color: #212529; font-weight: 500;">' + ageText + '</div>';
|
||||
} else {
|
||||
html += '<div style="font-size: 1rem; color: #6c757d;">Never</div>';
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
// Jobs count card
|
||||
html += '<div style="background: white; border: 1px solid #e9ecef; border-radius: 6px; padding: 0.75rem; box-shadow: 0 1px 2px rgba(0,0,0,0.03);">';
|
||||
html += '<div style="color: #6c757d; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.25rem;">Scheduled Jobs</div>';
|
||||
html += '<div style="font-size: 1rem; color: #212529; font-weight: 500;">' + (data.scheduled_jobs || 0) + '</div>';
|
||||
html += '</div>';
|
||||
|
||||
html += '</div>'; // Close grid
|
||||
|
||||
// Additional info if available
|
||||
if (data.modern_features && data.queue_size !== undefined) {
|
||||
html += '<div style="background: white; border: 1px solid #e9ecef; border-radius: 6px; padding: 0.75rem; box-shadow: 0 1px 2px rgba(0,0,0,0.03);">';
|
||||
html += '<span style="color: #6c757d; font-size: 0.875rem;">Queue Size: </span>';
|
||||
html += '<span style="font-weight: 500;">' + data.queue_size + '</span>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
// Failed jobs warning
|
||||
if (data.failed_jobs_24h > 0) {
|
||||
html += '<div style="background: #fff5f5; border: 1px solid #feb2b2; border-radius: 6px; padding: 0.75rem; color: #c53030;">';
|
||||
html += '<strong>⚠️ Failed Jobs (24h):</strong> ' + data.failed_jobs_24h;
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</div>'; // Close main container
|
||||
statusEl.innerHTML = html;
|
||||
})
|
||||
.catch(error => {
|
||||
var statusEl = document.getElementById('scheduler-health-status');
|
||||
if (statusEl) {
|
||||
statusEl.innerHTML = '<div class="alert alert-warning">Unable to fetch health status. Ensure scheduler-webhook plugin is installed.</div>';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Load on page ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', loadHealthStatus);
|
||||
} else {
|
||||
loadHealthStatus();
|
||||
}
|
||||
|
||||
// Refresh every 30 seconds
|
||||
setInterval(loadHealthStatus, 30000);
|
||||
})();
|
||||
</script>
|
||||
markdown: false
|
||||
|
||||
trigger_methods:
|
||||
type: display
|
||||
label: Active Triggers
|
||||
content: |
|
||||
<div id="scheduler-triggers">
|
||||
<div class="text-muted">Checking triggers...</div>
|
||||
</div>
|
||||
<script>
|
||||
(function() {
|
||||
function loadTriggers() {
|
||||
// Check cron status from the main status field
|
||||
var cronReady = false;
|
||||
var statusDiv = document.querySelector('.cronstatus-status');
|
||||
if (statusDiv) {
|
||||
var statusText = statusDiv.textContent || statusDiv.innerText;
|
||||
cronReady = statusText.includes('Ready');
|
||||
}
|
||||
|
||||
// Check webhook status
|
||||
fetch(window.location.origin + '/grav-editor-pro/scheduler/health')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
var triggersEl = document.getElementById('scheduler-triggers');
|
||||
if (!triggersEl) return;
|
||||
|
||||
var html = '<div style="display: flex; flex-direction: column; gap: 0.5rem;">';
|
||||
|
||||
// Cron trigger card
|
||||
var cronIcon = cronReady ? '✅' : '❌';
|
||||
var cronStatus = cronReady ? 'Active' : 'Not Configured';
|
||||
var cronStatusColor = cronReady ? '#28a745' : '#6c757d';
|
||||
var cardBg = cronReady ? '#f8f9fa' : '#fff';
|
||||
|
||||
html += '<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: ' + cardBg + '; border: 1px solid #e9ecef; border-radius: 4px;">';
|
||||
html += '<div style="display: flex; align-items: center; gap: 0.75rem;">';
|
||||
html += '<span style="font-size: 1.25rem; line-height: 1;">' + cronIcon + '</span>';
|
||||
html += '<span style="font-weight: 500; color: #212529; font-size: 1rem;">Cron:</span>';
|
||||
html += '</div>';
|
||||
html += '<span style="background: ' + cronStatusColor + '; color: white; padding: 0.25rem 0.75rem; font-size: 0.875rem; font-weight: 500; border-radius: 3px; text-transform: uppercase; letter-spacing: 0.025em;">' + cronStatus + '</span>';
|
||||
html += '</div>';
|
||||
|
||||
// Webhook trigger card
|
||||
if (data.webhook_enabled) {
|
||||
html += '<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 4px;">';
|
||||
html += '<div style="display: flex; align-items: center; gap: 0.75rem;">';
|
||||
html += '<span style="font-size: 1.25rem; line-height: 1;">✅</span>';
|
||||
html += '<span style="font-weight: 500; color: #212529; font-size: 1rem;">Webhook:</span>';
|
||||
html += '</div>';
|
||||
html += '<span style="background: #28a745; color: white; padding: 0.25rem 0.75rem; font-size: 0.875rem; font-weight: 500; border-radius: 3px; text-transform: uppercase; letter-spacing: 0.025em;">ACTIVE</span>';
|
||||
html += '</div>';
|
||||
} else {
|
||||
// Show webhook as not configured/disabled
|
||||
html += '<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: #fff; border: 1px solid #e9ecef; border-radius: 4px;">';
|
||||
html += '<div style="display: flex; align-items: center; gap: 0.75rem;">';
|
||||
html += '<span style="font-size: 1.25rem; line-height: 1;">⚠️</span>';
|
||||
html += '<span style="font-weight: 500; color: #212529; font-size: 1rem;">Webhook:</span>';
|
||||
html += '</div>';
|
||||
html += '<span style="background: #ffc107; color: #212529; padding: 0.25rem 0.75rem; font-size: 0.875rem; font-weight: 500; border-radius: 3px; text-transform: uppercase; letter-spacing: 0.025em;">DISABLED</span>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
// Add warning if no triggers active
|
||||
if (!cronReady && !data.webhook_enabled) {
|
||||
html += '<div class="alert alert-warning" style="margin-top: 1rem;"><i class="fa fa-exclamation-triangle"></i> No triggers active! Configure cron or enable webhooks.</div>';
|
||||
}
|
||||
|
||||
triggersEl.innerHTML = html;
|
||||
})
|
||||
.catch(error => {
|
||||
var triggersEl = document.getElementById('scheduler-triggers');
|
||||
if (triggersEl) {
|
||||
// Show just cron status if health endpoint not available
|
||||
var html = '<ul class="list-unstyled">';
|
||||
if (cronReady) {
|
||||
html += '<li>✅ <strong>Cron:</strong> <span class="badge badge-success">Active</span></li>';
|
||||
} else {
|
||||
html += '<li>❌ <strong>Cron:</strong> <span class="badge badge-secondary">Not Configured</span></li>';
|
||||
}
|
||||
html += '<li>⚠️ <strong>Webhook:</strong> <span class="badge badge-secondary">Plugin Not Installed</span></li>';
|
||||
html += '</ul>';
|
||||
triggersEl.innerHTML = html;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Load on page ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', loadTriggers);
|
||||
} else {
|
||||
loadTriggers();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
markdown: false
|
||||
|
||||
jobs_tab:
|
||||
type: tab
|
||||
title: PLUGIN_ADMIN.SCHEDULER_JOBS
|
||||
|
||||
fields:
|
||||
jobs_title:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.SCHEDULER_JOBS
|
||||
underline: true
|
||||
|
||||
custom_jobs:
|
||||
type: list
|
||||
style: vertical
|
||||
label:
|
||||
classes: cron-job-list compact
|
||||
key: id
|
||||
fields:
|
||||
.id:
|
||||
type: key
|
||||
label: ID
|
||||
placeholder: 'process-name'
|
||||
validate:
|
||||
required: true
|
||||
pattern: '[a-zа-я0-9_\-]+'
|
||||
max: 20
|
||||
message: 'ID must be lowercase with dashes/underscores only and less than 20 characters'
|
||||
.command:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.COMMAND
|
||||
placeholder: 'ls'
|
||||
validate:
|
||||
required: true
|
||||
.args:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.EXTRA_ARGUMENTS
|
||||
placeholder: '-lah'
|
||||
.at:
|
||||
type: text
|
||||
wrapper_classes: cron-selector
|
||||
label: PLUGIN_ADMIN.SCHEDULER_RUNAT
|
||||
help: PLUGIN_ADMIN.SCHEDULER_RUNAT_HELP
|
||||
placeholder: '* * * * *'
|
||||
validate:
|
||||
required: true
|
||||
.output:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.SCHEDULER_OUTPUT
|
||||
help: PLUGIN_ADMIN.SCHEDULER_OUTPUT_HELP
|
||||
placeholder: 'logs/ls-cron.out'
|
||||
.output_mode:
|
||||
type: select
|
||||
label: PLUGIN_ADMIN.SCHEDULER_OUTPUT_TYPE
|
||||
help: PLUGIN_ADMIN.SCHEDULER_OUTPUT_TYPE_HELP
|
||||
default: append
|
||||
options:
|
||||
append: Append
|
||||
overwrite: Overwrite
|
||||
.email:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.SCHEDULER_EMAIL
|
||||
help: PLUGIN_ADMIN.SCHEDULER_EMAIL_HELP
|
||||
placeholder: 'notifications@yoursite.com'
|
||||
|
||||
modern_tab:
|
||||
type: tab
|
||||
title: Advanced Features
|
||||
|
||||
fields:
|
||||
workers_section:
|
||||
type: section
|
||||
title: Worker Configuration
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
modern.workers:
|
||||
type: number
|
||||
label: Concurrent Workers
|
||||
help: Number of jobs that can run simultaneously (1 = sequential)
|
||||
default: 4
|
||||
size: x-small
|
||||
append: workers
|
||||
validate:
|
||||
type: int
|
||||
min: 1
|
||||
max: 10
|
||||
|
||||
retry_section:
|
||||
type: section
|
||||
title: Retry Configuration
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
modern.retry.enabled:
|
||||
type: toggle
|
||||
label: Enable Job Retry
|
||||
help: Automatically retry failed jobs
|
||||
highlight: 1
|
||||
default: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
modern.retry.max_attempts:
|
||||
type: number
|
||||
label: Maximum Retry Attempts
|
||||
help: Maximum number of times to retry a failed job
|
||||
default: 3
|
||||
size: x-small
|
||||
append: retries
|
||||
validate:
|
||||
type: int
|
||||
min: 1
|
||||
max: 10
|
||||
|
||||
modern.retry.backoff:
|
||||
type: select
|
||||
label: Retry Backoff Strategy
|
||||
help: How to calculate delay between retries
|
||||
default: exponential
|
||||
options:
|
||||
linear: Linear (fixed delay)
|
||||
exponential: Exponential (increasing delay)
|
||||
|
||||
queue_section:
|
||||
type: section
|
||||
title: Queue Configuration
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
modern.queue.path:
|
||||
type: text
|
||||
label: Queue Storage Path
|
||||
help: Where to store queued jobs
|
||||
default: 'user-data://scheduler/queue'
|
||||
placeholder: 'user-data://scheduler/queue'
|
||||
|
||||
modern.queue.max_size:
|
||||
type: number
|
||||
label: Maximum Queue Size
|
||||
help: Maximum number of jobs that can be queued
|
||||
default: 1000
|
||||
size: x-small
|
||||
append: jobs
|
||||
validate:
|
||||
type: int
|
||||
min: 100
|
||||
max: 10000
|
||||
|
||||
history_section:
|
||||
type: section
|
||||
title: Job History
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
modern.history.enabled:
|
||||
type: toggle
|
||||
label: Enable Job History
|
||||
help: Track execution history for all jobs
|
||||
highlight: 1
|
||||
default: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
modern.history.retention_days:
|
||||
type: number
|
||||
label: History Retention (days)
|
||||
help: How long to keep job history
|
||||
default: 30
|
||||
size: x-small
|
||||
append: days
|
||||
validate:
|
||||
type: int
|
||||
min: 1
|
||||
max: 365
|
||||
|
||||
webhook_section:
|
||||
type: section
|
||||
title: Webhook Configuration
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
webhook_plugin_status:
|
||||
type: webhook-status
|
||||
label:
|
||||
modern.webhook.enabled:
|
||||
type: toggle
|
||||
label: Enable Webhook Triggers
|
||||
help: Allow triggering scheduler via HTTP webhook
|
||||
highlight: 0
|
||||
default: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
modern.webhook.token:
|
||||
type: text
|
||||
label: Webhook Security Token
|
||||
help: Secret token for authenticating webhook requests. Keep this secret!
|
||||
placeholder: 'Click Generate to create a secure token'
|
||||
autocomplete: 'off'
|
||||
|
||||
webhook_token_generate:
|
||||
type: display
|
||||
label:
|
||||
content: |
|
||||
<div style="margin-top: -10px; margin-bottom: 15px;">
|
||||
<button type="button" class="button button-primary" onclick="generateWebhookToken()">
|
||||
<i class="fa fa-refresh"></i> Generate Token
|
||||
</button>
|
||||
</div>
|
||||
<script>
|
||||
function generateWebhookToken() {
|
||||
try {
|
||||
// Generate token
|
||||
const array = new Uint8Array(32);
|
||||
crypto.getRandomValues(array);
|
||||
const token = Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
|
||||
|
||||
// Try multiple selectors to find the field
|
||||
let field = document.querySelector('[name="data[scheduler][modern][webhook][token]"]');
|
||||
if (!field) {
|
||||
field = document.querySelector('input[name*="webhook][token"]');
|
||||
}
|
||||
if (!field) {
|
||||
field = document.getElementById('scheduler-modern-webhook-token');
|
||||
}
|
||||
if (!field) {
|
||||
// Look for any text input in the webhook section
|
||||
const webhookSection = document.querySelector('.webhook_section');
|
||||
if (webhookSection) {
|
||||
const inputs = webhookSection.querySelectorAll('input[type="text"]');
|
||||
// Find the token field by checking for the placeholder
|
||||
for (let input of inputs) {
|
||||
if (input.placeholder && input.placeholder.includes('Generate')) {
|
||||
field = input;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (field) {
|
||||
field.value = token;
|
||||
field.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
field.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
// Flash the field to show it was updated
|
||||
field.style.backgroundColor = '#d4edda';
|
||||
setTimeout(function() {
|
||||
field.style.backgroundColor = '';
|
||||
}, 500);
|
||||
// Also try to trigger Grav's form change detection
|
||||
if (window.jQuery) {
|
||||
jQuery(field).trigger('change');
|
||||
}
|
||||
} else {
|
||||
// Log more debugging info
|
||||
console.error('Token field not found. Looking for input fields...');
|
||||
console.log('All inputs:', document.querySelectorAll('input[type="text"]'));
|
||||
alert('Could not find the token field. Please ensure you are in the Advanced Features tab and the Webhook Configuration section is visible.');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error generating token:', e);
|
||||
alert('Error generating token: ' + e.message);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
markdown: false
|
||||
|
||||
modern.webhook.path:
|
||||
type: text
|
||||
label: Webhook Path
|
||||
help: URL path for webhook endpoint
|
||||
default: '/scheduler/webhook'
|
||||
placeholder: '/scheduler/webhook'
|
||||
|
||||
health_section:
|
||||
type: section
|
||||
title: Health Check Configuration
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
modern.health.enabled:
|
||||
type: toggle
|
||||
label: Enable Health Check
|
||||
help: Provide health status endpoint for monitoring
|
||||
highlight: 1
|
||||
default: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
modern.health.path:
|
||||
type: text
|
||||
label: Health Check Path
|
||||
help: URL path for health check endpoint
|
||||
default: '/scheduler/health'
|
||||
placeholder: '/scheduler/health'
|
||||
|
||||
webhook_usage:
|
||||
type: section
|
||||
title: Usage Examples
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
webhook_examples:
|
||||
type: display
|
||||
label:
|
||||
content: |
|
||||
<script src="{{ url('plugin://admin/themes/grav/js/clipboard-helper.js') }}"></script>
|
||||
<div class="webhook-examples">
|
||||
<script>
|
||||
// Initialize webhook commands when page loads
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (typeof GravClipboard !== 'undefined') {
|
||||
GravClipboard.initWebhookCommands();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<h4>How to use webhooks:</h4>
|
||||
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<label style="display: block; margin-bottom: 0.25rem; font-weight: 500;">Trigger all due jobs (respects schedule):</label>
|
||||
<div class="form-input-wrapper form-input-addon-wrapper">
|
||||
<textarea id="webhook-all-cmd" readonly rows="2" style="font-family: monospace; background: #f5f5f5; resize: none;">Loading...</textarea>
|
||||
<div class="form-input-addon form-input-append" style="cursor: pointer;" onclick="GravClipboard.copy(this)"><i class="fa fa-copy"></i> Copy</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<label style="display: block; margin-bottom: 0.25rem; font-weight: 500;">Force-run specific job (ignores schedule):</label>
|
||||
<div class="form-input-wrapper form-input-addon-wrapper">
|
||||
<textarea id="webhook-job-cmd" readonly rows="2" style="font-family: monospace; background: #f5f5f5; resize: none;">Loading...</textarea>
|
||||
<div class="form-input-addon form-input-append" style="cursor: pointer;" onclick="GravClipboard.copy(this)"><i class="fa fa-copy"></i> Copy</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<label style="display: block; margin-bottom: 0.25rem; font-weight: 500;">Check health status:</label>
|
||||
<div class="form-input-wrapper form-input-addon-wrapper">
|
||||
<input type="text" id="webhook-health-cmd" readonly value="Loading..." style="font-family: monospace; background: #f5f5f5;">
|
||||
<div class="form-input-addon form-input-append" style="cursor: pointer;" onclick="GravClipboard.copy(this)"><i class="fa fa-copy"></i> Copy</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 1rem;">
|
||||
<p><strong>GitHub Actions example:</strong></p>
|
||||
<pre>- name: Trigger Scheduler
|
||||
run: |
|
||||
curl -X POST ${{ secrets.SITE_URL }}/scheduler/webhook \
|
||||
-H "Authorization: Bearer ${{ secrets.WEBHOOK_TOKEN }}"</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
markdown: false
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ form:
|
||||
"D, d M Y G:i:s": Date3
|
||||
"d-m-y G:i": Date4
|
||||
"jS M Y": Date5
|
||||
"Y-m-d G:i": Date6
|
||||
|
||||
pages.dateformat.long:
|
||||
type: dateformat
|
||||
@@ -116,6 +117,7 @@ form:
|
||||
"D, d M Y G:i:s": Date3
|
||||
"d-m-y G:i": Date4
|
||||
"jS M Y": Date5
|
||||
"Y-m-d G:i:s": Date6
|
||||
|
||||
pages.order.by:
|
||||
type: select
|
||||
@@ -448,6 +450,17 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
languages.debug:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.LANGUAGE_DEBUG
|
||||
help: PLUGIN_ADMIN.LANGUAGE_DEBUG_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
http_headers:
|
||||
type: tab
|
||||
title: PLUGIN_ADMIN.HTTP_HEADERS
|
||||
@@ -597,6 +610,15 @@ form:
|
||||
hash: All files timestamps
|
||||
none: No timestamp checking
|
||||
|
||||
cache.check.interval:
|
||||
type: number
|
||||
size: x-small
|
||||
label: Cache Check Interval
|
||||
help: Seconds to reuse the previously computed filesystem hash before checking again. Zero keeps existing per-request checks.
|
||||
validate:
|
||||
type: int
|
||||
min: 0
|
||||
|
||||
cache.driver:
|
||||
type: select
|
||||
size: small
|
||||
@@ -606,12 +628,9 @@ form:
|
||||
options:
|
||||
auto: Auto detect
|
||||
file: File
|
||||
apc: APC
|
||||
apcu: APCu
|
||||
xcache: Xcache
|
||||
memcache: Memcache
|
||||
memcached: Memcached
|
||||
wincache: WinCache
|
||||
redis: Redis
|
||||
|
||||
cache.prefix:
|
||||
@@ -621,6 +640,19 @@ form:
|
||||
help: PLUGIN_ADMIN.CACHE_PREFIX_HELP
|
||||
placeholder: PLUGIN_ADMIN.CACHE_PREFIX_PLACEHOLDER
|
||||
|
||||
cache.purge_max_age_days:
|
||||
type: text
|
||||
size: x-small
|
||||
append: GRAV.NICETIME.DAY_PLURAL
|
||||
label: PLUGIN_ADMIN.CACHE_PURGE_AGE
|
||||
help: PLUGIN_ADMIN.CACHE_PURGE_AGE_HELP
|
||||
validate:
|
||||
type: number
|
||||
min: 1
|
||||
max: 365
|
||||
step: 1
|
||||
default: 30
|
||||
|
||||
cache.purge_at:
|
||||
type: cron
|
||||
label: PLUGIN_ADMIN.CACHE_PURGE_JOB
|
||||
@@ -695,20 +727,6 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
cache.memcache.server:
|
||||
type: text
|
||||
size: medium
|
||||
label: PLUGIN_ADMIN.MEMCACHE_SERVER
|
||||
help: PLUGIN_ADMIN.MEMCACHE_SERVER_HELP
|
||||
placeholder: "localhost"
|
||||
|
||||
cache.memcache.port:
|
||||
type: text
|
||||
size: small
|
||||
label: PLUGIN_ADMIN.MEMCACHE_PORT
|
||||
help: PLUGIN_ADMIN.MEMCACHE_PORT_HELP
|
||||
placeholder: "11211"
|
||||
|
||||
cache.memcached.server:
|
||||
type: text
|
||||
size: medium
|
||||
@@ -766,8 +784,8 @@ form:
|
||||
flex.cache.index.enabled:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.FLEX_INDEX_CACHE_ENABLED
|
||||
highlight: 1
|
||||
default: 1
|
||||
highlight: 0
|
||||
default: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
@@ -784,8 +802,8 @@ form:
|
||||
flex.cache.object.enabled:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.FLEX_OBJECT_CACHE_ENABLED
|
||||
highlight: 1
|
||||
default: 1
|
||||
highlight: 0
|
||||
default: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
@@ -872,17 +890,6 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
twig.umask_fix:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.TWIG_UMASK_FIX
|
||||
help: PLUGIN_ADMIN.TWIG_UMASK_FIX_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
assets:
|
||||
type: tab
|
||||
title: PLUGIN_ADMIN.ASSETS
|
||||
@@ -1226,6 +1233,16 @@ form:
|
||||
title: PLUGIN_ADMIN.MEDIA
|
||||
underline: true
|
||||
|
||||
images.adapter:
|
||||
type: select
|
||||
size: small
|
||||
label: PLUGIN_ADMIN.IMAGE_ADAPTER
|
||||
help: PLUGIN_ADMIN.IMAGE_ADAPTER_HELP
|
||||
highlight: gd
|
||||
options:
|
||||
gd: GD (PHP built-in)
|
||||
imagick: Imagick
|
||||
|
||||
images.default_image_quality:
|
||||
type: range
|
||||
append: '%'
|
||||
@@ -1288,6 +1305,28 @@ form:
|
||||
auto: Auto
|
||||
lazy: Lazy
|
||||
eager: Eager
|
||||
|
||||
images.defaults.decoding:
|
||||
type: select
|
||||
size: small
|
||||
label: PLUGIN_ADMIN.IMAGES_DECODING
|
||||
help: PLUGIN_ADMIN.IMAGES_DECODING_HELP
|
||||
highlight: auto
|
||||
options:
|
||||
auto: Auto
|
||||
sync: Sync
|
||||
async: Async
|
||||
|
||||
images.defaults.fetchpriority:
|
||||
type: select
|
||||
size: small
|
||||
label: PLUGIN_ADMIN.IMAGES_FETCHPRIORITY
|
||||
help: PLUGIN_ADMIN.IMAGES_FETCHPRIORITY_HELP
|
||||
highlight: auto
|
||||
options:
|
||||
auto: Auto
|
||||
high: High
|
||||
low: Low
|
||||
|
||||
images.seofriendly:
|
||||
type: toggle
|
||||
@@ -1726,8 +1765,8 @@ form:
|
||||
http_x_forwarded.host:
|
||||
type: toggle
|
||||
label: HTTP_X_FORWARDED_HOST Enabled
|
||||
highlight: 0
|
||||
default: 0
|
||||
highlight: 1
|
||||
default: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
@@ -1760,8 +1799,8 @@ form:
|
||||
strict_mode.blueprint_compat:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.STRICT_BLUEPRINT_COMPAT
|
||||
highlight: 0
|
||||
default: 0
|
||||
highlight: 1
|
||||
default: 1
|
||||
help: PLUGIN_ADMIN.STRICT_BLUEPRINT_COMPAT_HELP
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
@@ -1781,7 +1820,7 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
strict_mode.twig_compat:
|
||||
strict_mode.twig2_compat:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.STRICT_TWIG_COMPAT
|
||||
highlight: 0
|
||||
@@ -1793,6 +1832,18 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
strict_mode.twig3_compat:
|
||||
type: toggle
|
||||
label: Twig 3 Compatibility
|
||||
highlight: 0
|
||||
default: 0
|
||||
help: Enable automatic rewrites for legacy Twig 1/2 syntax that breaks on Twig 3 (e.g. `for ... if ...` guards)
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
|
||||
accounts:
|
||||
type: tab
|
||||
@@ -1855,6 +1906,3 @@ form:
|
||||
#
|
||||
# pages.type:
|
||||
# type: hidden
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -216,3 +216,8 @@ types:
|
||||
type: file
|
||||
thumb: media/thumb-json.png
|
||||
mime: application/json
|
||||
vcf:
|
||||
type: file
|
||||
thumb: media/thumb-vcf.png
|
||||
mime: text/x-vcard
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
68
system/config/scheduler.yaml
Normal file
68
system/config/scheduler.yaml
Normal file
@@ -0,0 +1,68 @@
|
||||
# Grav Scheduler Configuration
|
||||
|
||||
# Default scheduler settings (backward compatible)
|
||||
defaults:
|
||||
output: true
|
||||
output_type: file
|
||||
email: null
|
||||
|
||||
# Status of individual jobs (enabled/disabled)
|
||||
status: {}
|
||||
|
||||
# Custom scheduled jobs
|
||||
custom_jobs: {}
|
||||
|
||||
# Modern scheduler features (disabled by default for backward compatibility)
|
||||
modern:
|
||||
# Enable modern scheduler features
|
||||
enabled: false
|
||||
|
||||
# Number of concurrent workers (1 = sequential execution like legacy)
|
||||
workers: 1
|
||||
|
||||
# Job retry configuration
|
||||
retry:
|
||||
enabled: true
|
||||
max_attempts: 3
|
||||
backoff: exponential # 'linear' or 'exponential'
|
||||
|
||||
# Job queue configuration
|
||||
queue:
|
||||
path: user-data://scheduler/queue
|
||||
max_size: 1000
|
||||
|
||||
# Webhook trigger configuration
|
||||
webhook:
|
||||
enabled: false
|
||||
token: null # Set a secure token to enable webhook triggers
|
||||
path: /scheduler/webhook
|
||||
|
||||
# Health check endpoint
|
||||
health:
|
||||
enabled: true
|
||||
path: /scheduler/health
|
||||
|
||||
# Job execution history
|
||||
history:
|
||||
enabled: true
|
||||
retention_days: 30
|
||||
path: user-data://scheduler/history
|
||||
|
||||
# Performance settings
|
||||
performance:
|
||||
job_timeout: 300 # Default timeout in seconds
|
||||
lock_timeout: 10 # Lock acquisition timeout in seconds
|
||||
|
||||
# Monitoring and alerts
|
||||
monitoring:
|
||||
enabled: false
|
||||
alert_on_failure: true
|
||||
alert_email: null
|
||||
webhook_url: null
|
||||
|
||||
# Trigger detection methods
|
||||
triggers:
|
||||
check_cron: true
|
||||
check_systemd: true
|
||||
check_webhook: true
|
||||
check_external: true
|
||||
@@ -32,8 +32,16 @@ xss_dangerous_tags:
|
||||
- base
|
||||
uploads_dangerous_extensions:
|
||||
- php
|
||||
- php2
|
||||
- php3
|
||||
- php4
|
||||
- php5
|
||||
- phar
|
||||
- phtml
|
||||
- html
|
||||
- htm
|
||||
- shtml
|
||||
- shtm
|
||||
- js
|
||||
- exe
|
||||
sanitize_svg: true
|
||||
|
||||
@@ -25,7 +25,7 @@ routes:
|
||||
# '/new/(.*)': '/blog/$1' # Regex any /new/my-page URL to /blog/my-page Route
|
||||
|
||||
blog:
|
||||
route: '/blog' # Custom value added (accessible via system.blog.route)
|
||||
route: '/blog' # Custom value added (accessible via site.blog.route)
|
||||
|
||||
#menu: # Menu Example
|
||||
# - text: Source
|
||||
|
||||
@@ -28,6 +28,7 @@ languages:
|
||||
override_locale: false # Override the default or system locale with language specific one
|
||||
content_fallback: {} # Custom language fallbacks. eg: {fr: ['fr', 'en']}
|
||||
pages_fallback_only: false # DEPRECATED: Use `content_fallback` instead
|
||||
debug: false # Debug language detection
|
||||
|
||||
home:
|
||||
alias: '/home' # Default path for home, ie /
|
||||
@@ -92,14 +93,16 @@ cache:
|
||||
enabled: true # Set to true to enable caching
|
||||
check:
|
||||
method: file # Method to check for updates in pages: file|folder|hash|none
|
||||
driver: auto # One of: auto|file|apcu|memcache|wincache
|
||||
interval: 0 # Seconds to reuse previous filesystem hash before rechecking (0 = every request)
|
||||
driver: auto # One of: auto|file|apcu|memcached|redis
|
||||
prefix: 'g' # Cache prefix string (prevents cache conflicts)
|
||||
purge_at: '0 4 * * *' # How often to purge old file cache (using new scheduler)
|
||||
clear_at: '0 3 * * *' # How often to clear cache (using new scheduler)
|
||||
clear_job_type: 'standard' # Type to clear when processing the scheduled clear job `standard`|`all`
|
||||
clear_images_by_default: false # By default grav does not include processed images in cache clear, this can be enabled
|
||||
cli_compatibility: false # Ensures only non-volatile drivers are used (file, redis, memcache, etc.)
|
||||
cli_compatibility: false # Ensures only non-volatile drivers are used (file, redis, memcached, etc.)
|
||||
lifetime: 604800 # Lifetime of cached data in seconds (0 = infinite)
|
||||
purge_max_age_days: 30 # Maximum age of cache items in days before they are purged
|
||||
gzip: false # GZip compress the page output
|
||||
allow_webserver_gzip: false # If true, `content-encoding: identity` but connection isn't closed before `onShutDown()` event
|
||||
redis:
|
||||
@@ -116,7 +119,6 @@ twig:
|
||||
undefined_filters: true # Allow undefined filters
|
||||
safe_functions: [] # List of PHP functions which are allowed to be used as Twig functions
|
||||
safe_filters: [] # List of PHP functions which are allowed to be used as Twig filters
|
||||
umask_fix: false # By default Twig creates cached files as 755, fix switches this to 775
|
||||
|
||||
assets: # Configuration for Assets Manager (JS, CSS)
|
||||
css_pipeline: false # The CSS pipeline is the unification of multiple CSS resources into one file
|
||||
@@ -155,6 +157,7 @@ debugger:
|
||||
close_connection: true # Close the connection before calling onShutdown(). false for debugging
|
||||
|
||||
images:
|
||||
adapter: gd # Image adapter to use: gd | imagick
|
||||
default_image_quality: 85 # Default image quality to use when resampling images (85%)
|
||||
cache_all: false # Cache all image by default
|
||||
cache_perms: '0755' # MUST BE IN QUOTES!! Default cache folder perms. Usually '0755' or '0775'
|
||||
@@ -167,6 +170,8 @@ images:
|
||||
retina_scale: 1 # scale to adjust auto-sizes for better handling of HiDPI resolutions
|
||||
defaults:
|
||||
loading: auto # Let browser pick [auto|lazy|eager]
|
||||
decoding: auto # Let browser pick [auto|sync|async]
|
||||
fetchpriority: auto # Let browser pick [auto|high|low]
|
||||
watermark:
|
||||
image: 'system://images/watermark.png' # Path to a watermark image
|
||||
position_y: 'center' # top|center|bottom
|
||||
@@ -226,5 +231,6 @@ flex:
|
||||
|
||||
strict_mode:
|
||||
yaml_compat: false # Set to true to enable YAML backwards compatibility
|
||||
twig_compat: false # Set to true to enable deprecated Twig settings (autoescape: false)
|
||||
twig2_compat: false # Set to true to enable deprecated Twig settings (autoescape: false)
|
||||
twig3_compat: true # Set to true to enable automatic fixes for Twig 3 syntax changes
|
||||
blueprint_compat: false # Set to true to enable backward compatible strict support for blueprints
|
||||
|
||||
@@ -3,19 +3,19 @@
|
||||
/**
|
||||
* @package Grav\Core
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
// Some standard defines
|
||||
define('GRAV', true);
|
||||
define('GRAV_VERSION', '1.7.37.1');
|
||||
define('GRAV_SCHEMA', '1.7.0_2020-11-20_1');
|
||||
define('GRAV_TESTING', false);
|
||||
define('GRAV_VERSION', '1.8.0-beta.8');
|
||||
define('GRAV_SCHEMA', '1.8.0_2025-09-21_0');
|
||||
define('GRAV_TESTING', true);
|
||||
|
||||
// PHP minimum requirement
|
||||
if (!defined('GRAV_PHP_MIN')) {
|
||||
define('GRAV_PHP_MIN', '7.3.6');
|
||||
define('GRAV_PHP_MIN', '8.2.0');
|
||||
}
|
||||
|
||||
// Directory separator
|
||||
@@ -26,12 +26,12 @@ if (!defined('DS')) {
|
||||
// Absolute path to Grav root. This is where Grav is installed into.
|
||||
if (!defined('GRAV_ROOT')) {
|
||||
$path = rtrim(str_replace(DIRECTORY_SEPARATOR, DS, getenv('GRAV_ROOT') ?: getcwd()), DS);
|
||||
define('GRAV_ROOT', $path);
|
||||
define('GRAV_ROOT', $path ?: DS);
|
||||
}
|
||||
// Absolute path to Grav webroot. This is the path where your site is located in.
|
||||
if (!defined('GRAV_WEBROOT')) {
|
||||
$path = rtrim(getenv('GRAV_WEBROOT') ?: GRAV_ROOT, DS);
|
||||
define('GRAV_WEBROOT', $path);
|
||||
define('GRAV_WEBROOT', $path ?: DS);
|
||||
}
|
||||
// Relative path to user folder. This path needs to be located under GRAV_WEBROOT.
|
||||
if (!defined('GRAV_USER_PATH')) {
|
||||
@@ -99,3 +99,6 @@ define('RAW_CONTENT', 1);
|
||||
define('TWIG_CONTENT', 2);
|
||||
define('TWIG_CONTENT_LIST', 3);
|
||||
define('TWIG_TEMPLATES', 4);
|
||||
|
||||
// Filters
|
||||
define('GRAV_SANITIZE_STRING', 5001);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/**
|
||||
* @package Grav\Core
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ GRAV:
|
||||
BAD_DATE: Невалидна дата
|
||||
AGO: преди
|
||||
FROM_NOW: от сега
|
||||
JUST_NOW: току що
|
||||
SECOND: секунда
|
||||
MINUTE: минута
|
||||
HOUR: час
|
||||
@@ -60,3 +61,12 @@ GRAV:
|
||||
- 'петък'
|
||||
- 'събота'
|
||||
- 'неделя'
|
||||
YES: "Да"
|
||||
NO: "Не"
|
||||
CRON:
|
||||
EVERY: всеки
|
||||
EVERY_HOUR: Всеки час
|
||||
EVERY_MINUTE: Всяка минута
|
||||
EVERY_DAY_OF_WEEK: Всеки ден от седмицата
|
||||
EVERY_DAY_OF_MONTH: Всеки ден от месеца
|
||||
EVERY_MONTH: Всеки месец
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# S'ha produït un error: Frontmatter invàlid\n\nRuta: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'equipment'
|
||||
- ''
|
||||
- 'informació'
|
||||
- 'rice'
|
||||
- 'money'
|
||||
- 'species'
|
||||
- 'series'
|
||||
- 'fish'
|
||||
- 'sheep'
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: No s'ha proporcionat data
|
||||
BAD_DATE: Data invàlida
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Chyba: Chybný frontmatter\n\nPath: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Chyba: Chybná hlavička\n\nCesta: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_PLURALS:
|
||||
'/(quiz)$/i': '\1zes'
|
||||
'/^(ox)$/i': '\1en'
|
||||
|
||||
40
system/languages/eo.yaml
Normal file
40
system/languages/eo.yaml
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Eraro: Nevalida Frontmatter\n\nVojo: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_PLURALS:
|
||||
'/sis$/i': 'j'
|
||||
NICETIME:
|
||||
FROM_NOW: ekde nun
|
||||
JUST_NOW: Ĝuste nun
|
||||
SECOND: sekundo
|
||||
MINUTE: minuto
|
||||
HOUR: horo
|
||||
DAY: tago
|
||||
WEEK: semajno
|
||||
MONTH: monato
|
||||
YEAR: jaro
|
||||
DECADE: jardeko
|
||||
SEC: sek.
|
||||
MIN: min.
|
||||
HR: horo
|
||||
SECOND_PLURAL: sekundoj
|
||||
MINUTE_PLURAL: minutoj
|
||||
HOUR_PLURAL: horoj
|
||||
DAY_PLURAL: tagoj
|
||||
WEEK_PLURAL: semajnoj
|
||||
MONTH_PLURAL: monatoj
|
||||
YEAR_PLURAL: jaroj
|
||||
DECADE_PLURAL: jardekoj
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'januaro'
|
||||
- 'februaro'
|
||||
- 'marto'
|
||||
- 'aprilo'
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
@@ -21,9 +21,9 @@ GRAV:
|
||||
'sex': 'sexos'
|
||||
'move': 'movido'
|
||||
INFLECTOR_ORDINALS:
|
||||
'first': 'ro'
|
||||
'second': 'do'
|
||||
'third': 'ro'
|
||||
'first': '.º'
|
||||
'second': '.º'
|
||||
'third': '.º'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: No se proporcionó fecha
|
||||
BAD_DATE: Fecha errónea
|
||||
@@ -101,7 +101,7 @@ GRAV:
|
||||
TEXT_DOW: ' en <b />'
|
||||
TEXT_MONTH: ' de<b />'
|
||||
TEXT_DOM: ' en<b />'
|
||||
ERROR1: '¡La etiqueta %s no está soportada!'
|
||||
ERROR1: No se admite la etiqueta %s.
|
||||
ERROR2: El número de elementos es erróneo
|
||||
ERROR3: El jquery_element debería establecerse en la configuración del jqCron
|
||||
ERROR4: Expresión no reconocida
|
||||
|
||||
@@ -13,12 +13,12 @@ GRAV:
|
||||
'/(tive)s$/i': '\1'
|
||||
'/(hive)s$/i': '\1'
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'equipment'
|
||||
- ''
|
||||
- 'informatsioon'
|
||||
- 'riis'
|
||||
- 'raha'
|
||||
- 'species'
|
||||
- 'series'
|
||||
- ''
|
||||
- ''
|
||||
- 'kala'
|
||||
- 'lammas'
|
||||
INFLECTOR_IRREGULAR:
|
||||
@@ -70,6 +70,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>Kinnitamine nurjus:</b>'
|
||||
INVALID_INPUT: 'Vigane sisend:'
|
||||
MISSING_REQUIRED_FIELD: 'Nõutud väli puudub:'
|
||||
XSS_ISSUES: "Tuvastasime '%s' väljal võimaliku XSS-riski"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'jaanuar'
|
||||
- 'veebruar'
|
||||
@@ -91,11 +92,14 @@ GRAV:
|
||||
- 'reede'
|
||||
- 'laupäev'
|
||||
- 'pühapäev'
|
||||
YES: "Jah"
|
||||
NO: "Ei"
|
||||
CRON:
|
||||
EVERY: iga
|
||||
EVERY_HOUR: iga tund
|
||||
EVERY_MINUTE: iga minut
|
||||
EVERY_DAY_OF_WEEK: iga nädala päev
|
||||
EVERY_DAY_OF_WEEK: nädala igal päeval
|
||||
EVERY_DAY_OF_MONTH: kuu igal päeval
|
||||
EVERY_MONTH: iga kuu
|
||||
TEXT_PERIOD: Iga <b />
|
||||
ERROR1: Silt %s pole toetatud!
|
||||
|
||||
@@ -45,12 +45,12 @@ GRAV:
|
||||
'/([ti])a$/i': '\1um'
|
||||
'/(n)ews$/i': '\1ews'
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'equipment'
|
||||
- 'information'
|
||||
- ''
|
||||
- ''
|
||||
- 'riisi'
|
||||
- 'raha'
|
||||
- 'lajit'
|
||||
- 'series'
|
||||
- ''
|
||||
- 'kala'
|
||||
- 'lammas'
|
||||
INFLECTOR_IRREGULAR:
|
||||
|
||||
@@ -22,8 +22,27 @@ GRAV:
|
||||
'/$/': 's'
|
||||
INFLECTOR_SINGULAR:
|
||||
'/(quiz)zes$/i': '\1'
|
||||
'/(matr)ices$/i': '\1ix'
|
||||
'/(vert|ind)ices$/i': '\1ex'
|
||||
'/^(ox)en/i': '\1'
|
||||
'/(alias|status)es$/i': '\1'
|
||||
'/([octop|vir])i$/i': '\1us'
|
||||
'/(cris|ax|test)es$/i': '\1is'
|
||||
'/(shoe)s$/i': '\1'
|
||||
'/(o)es$/i': '\1'
|
||||
'/(bus)es$/i': '\1'
|
||||
'/([m|l])ice$/i': '\1ouse'
|
||||
'/(x|ch|ss|sh)es$/i': '\1'
|
||||
'/(m)ovies$/i': '\1ovie'
|
||||
'/(s)eries$/i': '\1eries'
|
||||
'/([^aeiouy]|qu)ies$/i': '\1y'
|
||||
'/([lr])ves$/i': '\1f'
|
||||
'/(tive)s$/i': '\1'
|
||||
'/(hive)s$/i': '\1'
|
||||
'/([^f])ves$/i': '\1fe'
|
||||
'/(^analy)ses$/i': '\1sis'
|
||||
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2sis'
|
||||
'/([ti])a$/i': '\1um'
|
||||
'/(n)ews$/i': '\1ouvelles'
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'équipement'
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\nכותרת: %1$s\n---\n# שגיאה: Fronmatter לא חוקי\nנתיב: `%2$s`\n**%3$s**\n```\n%4$s\n```"
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'ציוד'
|
||||
- 'מידע'
|
||||
- 'אורז'
|
||||
- 'כסף'
|
||||
- 'מינים'
|
||||
- 'סדרה'
|
||||
- 'דג'
|
||||
- 'כבשה'
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'אנשים'
|
||||
'man': 'גברים'
|
||||
'child': 'ילדים'
|
||||
'sex': 'מינים'
|
||||
'move': 'מהלכים'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: לא סופק תאריך
|
||||
BAD_DATE: תאריך פגום
|
||||
AGO: לפני
|
||||
FROM_NOW: כרגע
|
||||
JUST_NOW: כרגע
|
||||
SECOND: שנייה
|
||||
MINUTE: דקה
|
||||
HOUR: שעה
|
||||
@@ -40,6 +56,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>האימות נכשל:</b>'
|
||||
INVALID_INPUT: 'קלט לא חוקי'
|
||||
MISSING_REQUIRED_FIELD: 'שדות חובה חסרים:'
|
||||
XSS_ISSUES: "בעיות XSS פוטנציאליות זוהו בשדה '%s'"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'ינואר'
|
||||
- 'פברואר'
|
||||
@@ -61,3 +78,22 @@ GRAV:
|
||||
- 'שישי'
|
||||
- 'שבת'
|
||||
- 'ראשון'
|
||||
YES: "כן"
|
||||
NO: "לא"
|
||||
CRON:
|
||||
EVERY: בכל
|
||||
EVERY_HOUR: בכל שעה
|
||||
EVERY_MINUTE: כל דקה
|
||||
EVERY_DAY_OF_WEEK: כל יום בשבוע
|
||||
EVERY_DAY_OF_MONTH: בכל יום בחודש
|
||||
EVERY_MONTH: כל חודש
|
||||
TEXT_PERIOD: כל <b />
|
||||
TEXT_MINS: 'ב <b /> דקות אחרי השעה'
|
||||
TEXT_TIME: 'ב <b />:<b />'
|
||||
TEXT_DOW: 'ב <b />'
|
||||
TEXT_MONTH: 'של <b />'
|
||||
TEXT_DOM: 'ב <b />'
|
||||
ERROR1: התגית %s אינו נתמכת
|
||||
ERROR2: מספר לא חוקי של משתנים.
|
||||
ERROR3: יש להגדיר את ה-jquery_element להגדרות jqCron
|
||||
ERROR4: ביטוי לא מזוהה
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\nnaslov: %1$s\n---\n\n# Pogreška: nevažeći frontmatter\n\nPutanja datoteke: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'oprema'
|
||||
- 'informacije'
|
||||
- 'informacija'
|
||||
- 'riža'
|
||||
- 'novac'
|
||||
- 'vrsta'
|
||||
@@ -15,11 +16,17 @@ GRAV:
|
||||
'child': 'djeca'
|
||||
'sex': 'spolovi'
|
||||
'move': 'Pomakni'
|
||||
INFLECTOR_ORDINALS:
|
||||
'default': '.'
|
||||
'first': '.'
|
||||
'second': '.'
|
||||
'third': '.'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Datum nije upisan
|
||||
BAD_DATE: Pogrešan datum
|
||||
AGO: prije
|
||||
FROM_NOW: od sada
|
||||
JUST_NOW: upravo sad
|
||||
SECOND: sekunda
|
||||
MINUTE: minuta
|
||||
HOUR: sat
|
||||
@@ -29,6 +36,7 @@ GRAV:
|
||||
YEAR: godina
|
||||
DECADE: desetljeće
|
||||
SEC: sek
|
||||
MIN: min
|
||||
HR: sat
|
||||
WK: t
|
||||
MO: m
|
||||
@@ -53,6 +61,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>Validacija nije uspjela:</b>'
|
||||
INVALID_INPUT: 'Pogrešan unos u'
|
||||
MISSING_REQUIRED_FIELD: 'Nedostaje obavezno polje:'
|
||||
XSS_ISSUES: "Potencijalni XSS problemi otkriveni u polju '%s'"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'Siječanj'
|
||||
- 'Veljača'
|
||||
@@ -74,3 +83,22 @@ GRAV:
|
||||
- 'Petak'
|
||||
- 'Subota'
|
||||
- 'Nedjelja'
|
||||
YES: "Da"
|
||||
NO: "Ne"
|
||||
CRON:
|
||||
EVERY: svaki
|
||||
EVERY_HOUR: svaki sat
|
||||
EVERY_MINUTE: svake minute
|
||||
EVERY_DAY_OF_WEEK: svaki dan u tjednu
|
||||
EVERY_DAY_OF_MONTH: svaki dan u mjesecu
|
||||
EVERY_MONTH: svaki mjesec
|
||||
TEXT_PERIOD: Svakih <b />
|
||||
TEXT_MINS: ' u <b /> minut(e) nakon sata'
|
||||
TEXT_TIME: ' u <b />:<b />'
|
||||
TEXT_DOW: ' na <b />'
|
||||
TEXT_MONTH: ' <b />'
|
||||
TEXT_DOM: ' na <b />'
|
||||
ERROR1: Oznaka %s nije podržana!
|
||||
ERROR2: Pogrešan broj elemenata.
|
||||
ERROR3: jquery_element treba postaviti u postavke jqCron
|
||||
ERROR4: Izraz nije prepoznat
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitill: %1$s\n---\n\n# Villa: Ógilt efni á forsíðu\n\nSlóð: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'equipment'
|
||||
- ''
|
||||
- 'upplýsingar'
|
||||
- 'rice'
|
||||
- 'money'
|
||||
- 'species'
|
||||
- 'series'
|
||||
- 'fish'
|
||||
- 'sheep'
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Engin dagsetning gefin
|
||||
BAD_DATE: Röng dagsetning
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
GRAV:
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'equipment'
|
||||
- ''
|
||||
- '情報'
|
||||
- 'rice'
|
||||
- ''
|
||||
- 'お金'
|
||||
- 'species'
|
||||
- 'series'
|
||||
- ''
|
||||
- ''
|
||||
- '魚'
|
||||
- 'ヒツジ'
|
||||
INFLECTOR_IRREGULAR:
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# 오류: 무효의 Frontmatter\n\n경로: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- '장비'
|
||||
- '정보'
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- '시리즈'
|
||||
- '물고기'
|
||||
- ''
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': '사람들'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: 제공된 날짜가 없습니다
|
||||
BAD_DATE: 잘못된 날짜
|
||||
AGO: 전
|
||||
FROM_NOW: 후
|
||||
JUST_NOW: 방금
|
||||
SECOND: 초
|
||||
MINUTE: 분
|
||||
HOUR: 시간
|
||||
@@ -40,6 +52,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>유효성 검사 실패:</b>'
|
||||
INVALID_INPUT: '잘못된 입력'
|
||||
MISSING_REQUIRED_FIELD: '누락 된 필수 필드:'
|
||||
XSS_ISSUES: "'%s' 필드에서 잠재적인 XSS 문제가 감지되었습니다."
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- '일월'
|
||||
- '이월'
|
||||
@@ -61,3 +74,17 @@ GRAV:
|
||||
- '금요일'
|
||||
- '토요일'
|
||||
- '일요일'
|
||||
YES: "네"
|
||||
NO: "아니요"
|
||||
CRON:
|
||||
EVERY: 모두
|
||||
EVERY_HOUR: 매 시간
|
||||
EVERY_MINUTE: 매 분
|
||||
EVERY_DAY_OF_WEEK: 일주일간 매일
|
||||
EVERY_DAY_OF_MONTH: 일개월간 매일
|
||||
EVERY_MONTH: 매달
|
||||
TEXT_PERIOD: 모든 <b />
|
||||
ERROR1: '%s 태그는 지원되지 않습니다. '
|
||||
ERROR2: 잘못된 요소 수
|
||||
ERROR3: jquery_element는 jqCron 설정에서 설정할 수 있습니다.
|
||||
ERROR4: 인식할 수 없는 표현
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Klaida: klaidinga įžanginė konfigūracija\n\nPath: `%2$s`\n\n**%3$s**\n\n```\n %4$s\n```"
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'equipment'
|
||||
- 'information'
|
||||
- ''
|
||||
- ''
|
||||
- 'ryžiai'
|
||||
- 'pinigai'
|
||||
- 'prieskoniai'
|
||||
|
||||
84
system/languages/lv.yaml
Normal file
84
system/languages/lv.yaml
Normal file
@@ -0,0 +1,84 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\nNosaukums: %1$s\n---\n\n# Kļūda: Nederīgs Frontmatter\n\nCeļš: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_ORDINALS:
|
||||
'default': '.'
|
||||
'first': '.'
|
||||
'second': '.'
|
||||
'third': '.'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Nav norādīts datums
|
||||
BAD_DATE: Nederīgs datums
|
||||
AGO: iepriekš
|
||||
FROM_NOW: no šī brīža
|
||||
JUST_NOW: tikko
|
||||
SECOND: sekundes
|
||||
MINUTE: minūte
|
||||
HOUR: stunda
|
||||
DAY: diena
|
||||
WEEK: nedēļa
|
||||
MONTH: mēnesis
|
||||
YEAR: gads
|
||||
DECADE: dekāde
|
||||
SEC: s
|
||||
MIN: m
|
||||
HR: st
|
||||
WK: ned
|
||||
MO: mēn.
|
||||
YR: g.
|
||||
DEC: dec
|
||||
SECOND_PLURAL: sekundes
|
||||
MINUTE_PLURAL: minūtes
|
||||
HOUR_PLURAL: stundas
|
||||
DAY_PLURAL: dienas
|
||||
WEEK_PLURAL: nedēļas
|
||||
MONTH_PLURAL: mēneši
|
||||
YEAR_PLURAL: gadi
|
||||
DECADE_PLURAL: desmitgades
|
||||
SEC_PLURAL: s
|
||||
MIN_PLURAL: m
|
||||
HR_PLURAL: st.
|
||||
WK_PLURAL: ned.
|
||||
MO_PLURAL: mēn.
|
||||
YR_PLURAL: g.
|
||||
DEC_PLURAL: d
|
||||
FORM:
|
||||
VALIDATION_FAIL: '<b>Validācija neizdevās:</b>'
|
||||
INVALID_INPUT: 'Nederīga ievade'
|
||||
MISSING_REQUIRED_FIELD: 'Laukā trūkst datu'
|
||||
XSS_ISSUES: "Atrastas iespējamas XSS problēmas laukā '%s'"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'Janvāris'
|
||||
- 'Februāris'
|
||||
- 'Marts'
|
||||
- 'Aprīlis'
|
||||
- 'Maijs'
|
||||
- 'Jūnijs'
|
||||
- 'Jūlijs'
|
||||
- 'Augusts'
|
||||
- 'Septembris'
|
||||
- 'Oktobris'
|
||||
- 'Novembris'
|
||||
- 'Decembris'
|
||||
DAYS_OF_THE_WEEK:
|
||||
- 'Pirmdiena'
|
||||
- 'Otrdiena'
|
||||
- 'Trešdiena'
|
||||
- 'Ceturtdiena'
|
||||
- 'Piektdiena'
|
||||
- 'Sestdiena'
|
||||
- 'Svētdiena'
|
||||
YES: "Jā"
|
||||
NO: "Nē"
|
||||
CRON:
|
||||
EVERY: katru
|
||||
EVERY_HOUR: katru stundu
|
||||
EVERY_MINUTE: katru minūti
|
||||
EVERY_DAY_OF_WEEK: katru nedēļas dienu
|
||||
EVERY_DAY_OF_MONTH: katru mēneša dienu
|
||||
EVERY_MONTH: katru mēnesi
|
||||
TEXT_PERIOD: Katru <b />
|
||||
ERROR1: Marķieris %s nav atbalstīts!
|
||||
ERROR2: Nederīgs elementu skaits
|
||||
ERROR3: jquery_element nevajadzētu definēt jqCron iestatījumos
|
||||
ERROR4: Neatpazīta izteiksme
|
||||
147
system/languages/my.yaml
Normal file
147
system/languages/my.yaml
Normal file
@@ -0,0 +1,147 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\nခေါင်းစဥ်: %1$s\n---\n\n# အမှား - Frontmatter မမှန်ကန်ပါ\n\nလမ်းကြောင်း `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_PLURALS:
|
||||
'/(quiz)$/i': '\1zes'
|
||||
'/^(ox)$/i': '\1en'
|
||||
'/([m|l])ouse$/i': '\1ice'
|
||||
'/(matr|vert|ind)ix|ex$/i': '\1ices'
|
||||
'/(x|ch|ss|sh)$/i': '\1es'
|
||||
'/([^aeiouy]|qu)ies$/i': '\1y'
|
||||
'/([^aeiouy]|qu)y$/i': '\1ies'
|
||||
'/(hive)$/i': '\1s'
|
||||
'/(?:([^f])fe|([lr])f)$/i': '\1\2ves'
|
||||
'/sis$/i': 'ses'
|
||||
'/([ti])um$/i': '\1a'
|
||||
'/(buffal|tomat)o$/i': '\1oes'
|
||||
'/(bu)s$/i': '\1ses'
|
||||
'/(alias|status)/i': '\1es'
|
||||
'/(octop|vir)us$/i': '\1i'
|
||||
'/(ax|test)is$/i': '\1es'
|
||||
'/s$/i': 's'
|
||||
'/$/': 's'
|
||||
INFLECTOR_SINGULAR:
|
||||
'/(quiz)zes$/i': '\1'
|
||||
'/(matr)ices$/i': '\1ix'
|
||||
'/(vert|ind)ices$/i': '\1ex'
|
||||
'/^(ox)en/i': '\1'
|
||||
'/(alias|status)es$/i': '\1'
|
||||
'/([octop|vir])i$/i': '\1us'
|
||||
'/(cris|ax|test)es$/i': '\1is'
|
||||
'/(shoe)s$/i': '\1'
|
||||
'/(o)es$/i': '\1'
|
||||
'/(bus)es$/i': '\1'
|
||||
'/([m|l])ice$/i': '\1ouse'
|
||||
'/(x|ch|ss|sh)es$/i': '\1'
|
||||
'/(m)ovies$/i': '\1ovie'
|
||||
'/(s)eries$/i': '\1eries'
|
||||
'/([^aeiouy]|qu)ies$/i': '\1y'
|
||||
'/([lr])ves$/i': '\1f'
|
||||
'/(tive)s$/i': '\1'
|
||||
'/(hive)s$/i': '\1'
|
||||
'/([^f])ves$/i': '\1fe'
|
||||
'/(^analy)ses$/i': '\1sis'
|
||||
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2sis'
|
||||
'/([ti])a$/i': '\1um'
|
||||
'/(n)ews$/i': '\1ews'
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'ကိရိယာ'
|
||||
- 'အချက်အလက်'
|
||||
- 'ဆန်'
|
||||
- 'ငွေ'
|
||||
- 'မျိုးစိတ်'
|
||||
- 'အတွဲများ'
|
||||
- 'ငါး'
|
||||
- 'သိုးများ'
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'လူ'
|
||||
'man': 'ယောက်ျား'
|
||||
'child': 'ကလေးများ'
|
||||
'sex': 'လိင်'
|
||||
'move': 'ရွှေ့ခြင်း'
|
||||
INFLECTOR_ORDINALS:
|
||||
'default': 'th'
|
||||
'first': 'st'
|
||||
'second': 'nd'
|
||||
'third': 'rd'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: နေ့စွဲ မသတ်မှတ်ထား
|
||||
BAD_DATE: ရက်စွဲမမှန်ပါ
|
||||
AGO: လွန်ခဲ့တဲ့
|
||||
FROM_NOW: ယခုမှ
|
||||
JUST_NOW: အခုပဲ
|
||||
SECOND: ဒုတိယ
|
||||
MINUTE: မိနစ်
|
||||
HOUR: နာရီ
|
||||
DAY: နေ့
|
||||
WEEK: တစ်ပတ်
|
||||
MONTH: လ
|
||||
YEAR: နှစ်
|
||||
DECADE: ဆယ်စုနှစ်
|
||||
SEC: စက္ကန့်
|
||||
MIN: မိနစ်
|
||||
HR: နာရီ
|
||||
WK: တစ်ပတ်
|
||||
MO: လ
|
||||
YR: နှစ်
|
||||
DEC: ဒီဇင်ဘာ
|
||||
SECOND_PLURAL: စက္ကန့်
|
||||
MINUTE_PLURAL: မိနစ်
|
||||
HOUR_PLURAL: နာရီ
|
||||
DAY_PLURAL: နေ့
|
||||
WEEK_PLURAL: ရက်သတ္တပတ်
|
||||
MONTH_PLURAL: လ
|
||||
YEAR_PLURAL: နှစ်
|
||||
DECADE_PLURAL: ဆယ်စုနှစ်များစွ
|
||||
SEC_PLURAL: စက္ကန့်
|
||||
MIN_PLURAL: မိနစ်
|
||||
HR_PLURAL: နာရီ
|
||||
WK_PLURAL: အပတ်
|
||||
MO_PLURAL: လ
|
||||
YR_PLURAL: နှစ်
|
||||
DEC_PLURAL: ဆယ်စုနှစ်
|
||||
FORM:
|
||||
VALIDATION_FAIL: '<b> အတည်ပြုခြင်းမအောင်မြင်ပါ: </b>'
|
||||
INVALID_INPUT: 'ထည့်သွင်းမှုမမှန်ပါ'
|
||||
MISSING_REQUIRED_FIELD: 'လိုအပ်သောအကွက်ပျောက်နေသည်'
|
||||
XSS_ISSUES: "XSS ပြဿနာ ဖြစ်နိုင်ချေ ကို '%s' အကွက်တွင် တွေ့"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'ဇန်နဝါရီ'
|
||||
- 'ဖေဖော်ဝါရီ'
|
||||
- 'မတ်'
|
||||
- 'ဧပြီ'
|
||||
- 'မေ'
|
||||
- 'ဇွန်'
|
||||
- 'ဇူလိုင်'
|
||||
- 'သြဂုတ်'
|
||||
- 'စက်တင်ဘာ'
|
||||
- 'အောက်တိုဘာ'
|
||||
- 'နိုဝင်ဘာ'
|
||||
- 'ဒီဇင်ဘာ'
|
||||
DAYS_OF_THE_WEEK:
|
||||
- 'တနင်္လာ'
|
||||
- ' အင်္ဂါ'
|
||||
- 'ဗုဒ္ဓဟူး'
|
||||
- 'ကြာသပတေး'
|
||||
- 'သောကြာ'
|
||||
- 'စနေ'
|
||||
- 'တနင်္ဂနွေ'
|
||||
YES: "လုပ်"
|
||||
NO: "မလုပ်"
|
||||
CRON:
|
||||
EVERY: အမြဲတမ်း
|
||||
EVERY_HOUR: နာရီတိုင်း
|
||||
EVERY_MINUTE: မိနစ်တိုင်း
|
||||
EVERY_DAY_OF_WEEK: တစ်ပတ်လုံး နေ့တိုင်း
|
||||
EVERY_DAY_OF_MONTH: တစ်လလုံး နေ့တိုင်း
|
||||
EVERY_MONTH: လစဉ်လတိုင်း
|
||||
TEXT_PERIOD: </b>တိုင်း
|
||||
TEXT_MINS: 'နာရီ ကျော်ပြီး <b /> မိနစ် တွင်'
|
||||
TEXT_TIME: ' <b />:<b /> တွင် '
|
||||
TEXT_DOW: '<b /> ပေါ်တွင် '
|
||||
TEXT_MONTH: '<b />၏ '
|
||||
TEXT_DOM: '<b /> တွင် '
|
||||
ERROR1: ဤ %s တက် ကိုပံ့ပိုးမထားပါ။
|
||||
ERROR2: လိုအပ်သောထည့်သွင်း နာပတ် အမှားဖြစ်နေသည်
|
||||
ERROR3: jquery_element ကို jqCron ဆက်တင် တွင်ထားရမည်
|
||||
ERROR4: အသိအမှတ်မပြုသော အသုံးအနှုန်း
|
||||
@@ -104,6 +104,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>Validatie mislukt:</b>'
|
||||
INVALID_INPUT: 'Ongeldige invoer in'
|
||||
MISSING_REQUIRED_FIELD: 'Ontbrekend verplicht veld:'
|
||||
XSS_ISSUES: "Mogelijke XSS-problemen ontdekt in '%s' veld"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'Januari'
|
||||
- 'Februari'
|
||||
@@ -125,6 +126,8 @@ GRAV:
|
||||
- 'Vrijdag'
|
||||
- 'Zaterdag'
|
||||
- 'Zondag'
|
||||
YES: "Ja"
|
||||
NO: "Nee"
|
||||
CRON:
|
||||
EVERY: elke
|
||||
EVERY_HOUR: elk uur
|
||||
|
||||
@@ -6,10 +6,10 @@ GRAV:
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'wyposażenie'
|
||||
- 'informacja'
|
||||
- 'rice'
|
||||
- ''
|
||||
- 'pieniądze'
|
||||
- 'species'
|
||||
- 'series'
|
||||
- ''
|
||||
- ''
|
||||
- 'ryba'
|
||||
- 'owca'
|
||||
INFLECTOR_IRREGULAR:
|
||||
|
||||
@@ -67,7 +67,7 @@ GRAV:
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Nenhuma data fornecida
|
||||
BAD_DATE: Data inválida
|
||||
AGO: atrás
|
||||
AGO: há
|
||||
FROM_NOW: a partir de agora
|
||||
JUST_NOW: mesmo agora
|
||||
SECOND: segundo
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Ошибка: недопустимое содержимое Frontmatter\n\nПуть: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_SINGULAR:
|
||||
'/([octop|vir])i$/i': '\1us'
|
||||
'/(cris|ax|test)es$/i': '\1is'
|
||||
'/(shoe)s$/i': '\1'
|
||||
'/([lr])ves$/i': '\1f'
|
||||
'/(tive)s$/i': "\\1\n"
|
||||
'/(hive)s$/i': '\1'
|
||||
'/([^f])ves$/i': '\1fe'
|
||||
'/(^analy)ses$/i': '\1sis'
|
||||
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2sis'
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'экипировка'
|
||||
- 'информация'
|
||||
|
||||
@@ -1,9 +1,120 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\nමාතෘකාව: %1$s\n---\n\n# දෝෂය: වලංගු නොවන ඉදිරිපස\n\nමාර්ගය: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_PLURALS:
|
||||
'/([m|l])ouse$/i': '\1අයිස්'
|
||||
'/(matr|vert|ind)ix|ex$/i': '\1අයිස්'
|
||||
'/(?:([^f])fe|([lr])f)$/i': '\1\2වෙස්'
|
||||
'/([ti])um$/i': '\1අ'
|
||||
'/(buffal|tomat)o$/i': '\1ඕඑස්'
|
||||
'/(bu)s$/i': '\1සෙස්'
|
||||
INFLECTOR_SINGULAR:
|
||||
'/(quiz)zes$/i': '\1'
|
||||
'/^(ox)en/i': '\1'
|
||||
'/(alias|status)es$/i': '\1'
|
||||
'/([octop|vir])i$/i': '\1 අප'
|
||||
'/(cris|ax|test)es$/i': '\1 වේ'
|
||||
'/(o)es$/i': '\1'
|
||||
'/(bus)es$/i': '\1'
|
||||
'/([m|l])ice$/i': '\1 භාවිතා කරන්න'
|
||||
'/(x|ch|ss|sh)es$/i': '\1'
|
||||
'/(m)ovies$/i': '\1ඕවී'
|
||||
'/(s)eries$/i': '\1මාලා'
|
||||
'/(^analy)ses$/i': '\1සිස්'
|
||||
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2සිස්'
|
||||
'/([ti])a$/i': '\1ම්'
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'උපකරණ'
|
||||
- 'විස්තර'
|
||||
- 'සහල්'
|
||||
- 'මුදල'
|
||||
- 'විශේෂ'
|
||||
- 'මාලාවක්'
|
||||
- 'මාළු'
|
||||
- 'බැටළුවන්'
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'මහජන'
|
||||
'man': 'මිනිසුන්'
|
||||
'child': 'දරුවන්'
|
||||
'sex': 'ලිංගිකත්වය'
|
||||
'move': 'චලනය කරයි'
|
||||
INFLECTOR_ORDINALS:
|
||||
'first': 'ශාන්ත'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: දිනයක් සපයා නැත
|
||||
BAD_DATE: නරක දිනය
|
||||
AGO: පෙර
|
||||
FROM_NOW: මෙතැන් සිට
|
||||
JUST_NOW: මේ දැන්
|
||||
SECOND: දෙවැනි
|
||||
MINUTE: මිනිත්තුව
|
||||
HOUR: පැය
|
||||
DAY: දින
|
||||
WEEK: සතිය
|
||||
MONTH: මස
|
||||
YEAR: වර්ෂය
|
||||
DECADE: දශකය
|
||||
SEC: තත්පර
|
||||
MIN: මිනි
|
||||
HR: පැය
|
||||
YR: වසර
|
||||
DEC: දෙසැ
|
||||
SECOND_PLURAL: තත්පර
|
||||
MINUTE_PLURAL: මිනිත්තු
|
||||
HOUR_PLURAL: පැය
|
||||
DAY_PLURAL: දින
|
||||
WEEK_PLURAL: සති
|
||||
MONTH_PLURAL: මාස
|
||||
YEAR_PLURAL: වසර
|
||||
DECADE_PLURAL: දශක
|
||||
SEC_PLURAL: තත්පර
|
||||
MIN_PLURAL: මිනිත්තු
|
||||
HR_PLURAL: පැය
|
||||
WK_PLURAL: සති
|
||||
YR_PLURAL: වසර
|
||||
DEC_PLURAL: දෙසැ
|
||||
FORM:
|
||||
VALIDATION_FAIL: '<b>වලංගු කිරීම අසාර්ථක විය:</b>'
|
||||
INVALID_INPUT: 'වලංගු නොවන ආදානය'
|
||||
MISSING_REQUIRED_FIELD: 'අවශ්ය ක්ෂේත්රය අස්ථානගත වී ඇත:'
|
||||
XSS_ISSUES: "විභව XSS ගැටළු '%s' ක්ෂේත්රයේ අනාවරණය විය"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'ජනවාරි'
|
||||
- 'පෙබරවාරි'
|
||||
- 'මාර්තු'
|
||||
- 'අප්රේල්'
|
||||
- 'මැයි'
|
||||
- 'ජූනි'
|
||||
- 'ජුලි'
|
||||
- 'අගෝස්තු'
|
||||
- 'සැප්තැම්බර්'
|
||||
- 'ඔක්තෝම්බර්'
|
||||
- 'නොවැම්බර්'
|
||||
- 'දෙසැම්බර්'
|
||||
DAYS_OF_THE_WEEK:
|
||||
- 'සඳුදා'
|
||||
- 'අඟහරුවාදා'
|
||||
- 'බදාදා'
|
||||
- 'බ්රහස්පතින්දා'
|
||||
- 'සිකුරාදා'
|
||||
- 'සෙනසුරාදා'
|
||||
- 'ඉරිදා'
|
||||
YES: "ඔව්"
|
||||
NO: "නැත"
|
||||
CRON:
|
||||
EVERY: සෑම
|
||||
EVERY_HOUR: සෑම පැයකටම
|
||||
EVERY_MINUTE: සෑම විනාඩියකටම
|
||||
EVERY_DAY_OF_WEEK: සතියේ සෑම දිනකම
|
||||
EVERY_DAY_OF_MONTH: මාසයේ සෑම දිනකම
|
||||
EVERY_MONTH: සෑම මාසයකම
|
||||
TEXT_PERIOD: සෑම <b />
|
||||
TEXT_MINS: ' පැයට පසු විනාඩි <b /> කින්'
|
||||
TEXT_TIME: ' <b />:<b />ට'
|
||||
TEXT_DOW: ' <b />මත'
|
||||
TEXT_MONTH: ' <b />'
|
||||
TEXT_DOM: ' <b />මත'
|
||||
ERROR1: ටැගය %s සහාය නොදක්වයි!
|
||||
ERROR2: නරක මූලද්රව්ය සංඛ්යාව
|
||||
ERROR3: jquery_element jqCron සැකසුම් වලට සැකසිය යුතුය
|
||||
ERROR4: හඳුනා නොගත් ප්රකාශනය
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Napaka: Neveljavna Frontmatter\n\nPath: `%2$s`\n\n**%3$s ** \n\n```\n%4$s \n```"
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'oprema'
|
||||
- 'informacija'
|
||||
- 'riž'
|
||||
- 'denar'
|
||||
- 'vrste'
|
||||
- 'serija'
|
||||
- 'riba'
|
||||
- 'ovca'
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'ljudje'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Datum ni na voljo
|
||||
BAD_DATE: Neveljaven datum
|
||||
@@ -43,15 +54,15 @@ GRAV:
|
||||
- 'Januar'
|
||||
- 'Februar'
|
||||
- 'Marec'
|
||||
- 'April'
|
||||
- 'april'
|
||||
- 'Maj'
|
||||
- 'Junij'
|
||||
- 'Julij'
|
||||
- 'Avgust'
|
||||
- 'September'
|
||||
- 'september'
|
||||
- 'Oktober'
|
||||
- 'November'
|
||||
- 'December'
|
||||
- 'november'
|
||||
- 'december'
|
||||
DAYS_OF_THE_WEEK:
|
||||
- 'Ponedeljek'
|
||||
- 'Torek'
|
||||
@@ -60,3 +71,15 @@ GRAV:
|
||||
- 'Petek'
|
||||
- 'Sobota'
|
||||
- 'Nedelja'
|
||||
YES: "Da"
|
||||
NO: "Ne"
|
||||
CRON:
|
||||
EVERY: vsak
|
||||
EVERY_HOUR: vsako uro
|
||||
EVERY_MINUTE: vsako minuto
|
||||
EVERY_DAY_OF_WEEK: vsak dan v tednu
|
||||
EVERY_DAY_OF_MONTH: vsak dan v mesecu
|
||||
EVERY_MONTH: vsak mesec
|
||||
ERROR1: Oznaka %s ni podprta!
|
||||
ERROR2: Napačno število elementov.
|
||||
ERROR4: Neznan izraz
|
||||
|
||||
@@ -104,6 +104,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>Провера неуспела:</b>'
|
||||
INVALID_INPUT: 'Неисправан унос у'
|
||||
MISSING_REQUIRED_FIELD: 'Недостаје обавезн поље:'
|
||||
XSS_ISSUES: "Потенцијална грешка у XSS-у детектована у пољу '%s' "
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'Јануар'
|
||||
- 'Фебруар'
|
||||
@@ -125,6 +126,8 @@ GRAV:
|
||||
- 'Петак'
|
||||
- 'Субота'
|
||||
- 'Недеља'
|
||||
YES: "Да"
|
||||
NO: "Не"
|
||||
CRON:
|
||||
EVERY: сваки
|
||||
EVERY_HOUR: сваки сат
|
||||
|
||||
147
system/languages/sw.yaml
Normal file
147
system/languages/sw.yaml
Normal file
@@ -0,0 +1,147 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\nkichwa: %1$s\n---\n\n# Kosa: Mbele ya Mbele\n\nNjia: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_PLURALS:
|
||||
'/(quiz)$/i': '\1zes'
|
||||
'/^(ox)$/i': '\1en'
|
||||
'/([m|l])ouse$/i': '\1ice'
|
||||
'/(matr|vert|ind)ix|ex$/i': '\1ices'
|
||||
'/(x|ch|ss|sh)$/i': '\1es'
|
||||
'/([^aeiouy]|qu)ies$/i': '\1y'
|
||||
'/([^aeiouy]|qu)y$/i': '\1ies'
|
||||
'/(hive)$/i': '\1s'
|
||||
'/(?:([^f])fe|([lr])f)$/i': '\1\2ves'
|
||||
'/sis$/i': 'ses'
|
||||
'/([ti])um$/i': '\1a'
|
||||
'/(buffal|tomat)o$/i': '\1oes'
|
||||
'/(bu)s$/i': '\1ses'
|
||||
'/(alias|status)/i': '\1es'
|
||||
'/(octop|vir)us$/i': '\1i'
|
||||
'/(ax|test)is$/i': '\1es'
|
||||
'/s$/i': 's'
|
||||
'/$/': 's'
|
||||
INFLECTOR_SINGULAR:
|
||||
'/(quiz)zes$/i': '\1'
|
||||
'/(matr)ices$/i': '\1ix'
|
||||
'/(vert|ind)ices$/i': '\1ex'
|
||||
'/^(ox)en/i': '\1'
|
||||
'/(alias|status)es$/i': '\1'
|
||||
'/([octop|vir])i$/i': '\1us'
|
||||
'/(cris|ax|test)es$/i': '\1is'
|
||||
'/(shoe)s$/i': '\1'
|
||||
'/(o)es$/i': '\1'
|
||||
'/(bus)es$/i': '\1'
|
||||
'/([m|l])ice$/i': '\1ouse'
|
||||
'/(x|ch|ss|sh)es$/i': '\1'
|
||||
'/(m)ovies$/i': '\1ovie'
|
||||
'/(s)eries$/i': '\1eries'
|
||||
'/([^aeiouy]|qu)ies$/i': '\1y'
|
||||
'/([lr])ves$/i': '\1f'
|
||||
'/(tive)s$/i': '\1'
|
||||
'/(hive)s$/i': '\1'
|
||||
'/([^f])ves$/i': '\1fe'
|
||||
'/(^analy)ses$/i': '\1sis'
|
||||
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2sis'
|
||||
'/([ti])a$/i': '\1um'
|
||||
'/(n)ews$/i': '\1ews'
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'vifaa'
|
||||
- 'habari'
|
||||
- 'mchele'
|
||||
- 'pesa'
|
||||
- 'spishi'
|
||||
- 'mfululizo'
|
||||
- 'samaki'
|
||||
- 'kondoo'
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'watu'
|
||||
'man': 'wanaume'
|
||||
'child': 'watoto'
|
||||
'sex': 'jinsia'
|
||||
'move': 'songa'
|
||||
INFLECTOR_ORDINALS:
|
||||
'default': 'th'
|
||||
'first': 'st'
|
||||
'second': 'nd'
|
||||
'third': 'rd'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Hakuna tarehe iliyotolewa
|
||||
BAD_DATE: Tarehe mbaya
|
||||
AGO: zilizopita
|
||||
FROM_NOW: kuanzia sasa
|
||||
JUST_NOW: sasa hivi
|
||||
SECOND: pili
|
||||
MINUTE: dakika
|
||||
HOUR: saa
|
||||
DAY: siku
|
||||
WEEK: wiki
|
||||
MONTH: mwezi
|
||||
YEAR: mwaka
|
||||
DECADE: muongo
|
||||
SEC: sec
|
||||
MIN: min
|
||||
HR: hr
|
||||
WK: wk
|
||||
MO: mo
|
||||
YR: yr
|
||||
DEC: dec
|
||||
SECOND_PLURAL: sekunde
|
||||
MINUTE_PLURAL: dakika
|
||||
HOUR_PLURAL: masaa
|
||||
DAY_PLURAL: siku
|
||||
WEEK_PLURAL: wiki
|
||||
MONTH_PLURAL: miezi
|
||||
YEAR_PLURAL: miaka
|
||||
DECADE_PLURAL: miongo
|
||||
SEC_PLURAL: secs
|
||||
MIN_PLURAL: mins
|
||||
HR_PLURAL: hrs
|
||||
WK_PLURAL: wks
|
||||
MO_PLURAL: mos
|
||||
YR_PLURAL: yrs
|
||||
DEC_PLURAL: decs
|
||||
FORM:
|
||||
VALIDATION_FAIL: '<b> Uthibitishaji umeshindwa: </b>'
|
||||
INVALID_INPUT: 'Ingizo batili katika'
|
||||
MISSING_REQUIRED_FIELD: 'Sehemu inayokosekana inahitajika:'
|
||||
XSS_ISSUES: "Masuala yanayowezekana ya XSS yamegunduliwa katika uwanja wa '% s"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'Januari'
|
||||
- 'Februari'
|
||||
- 'Machi'
|
||||
- 'Aprili'
|
||||
- 'Mei'
|
||||
- 'Juni'
|
||||
- 'Julai'
|
||||
- 'Agosti'
|
||||
- 'Septemba'
|
||||
- 'Oktoba'
|
||||
- 'Novemba'
|
||||
- 'Desemba'
|
||||
DAYS_OF_THE_WEEK:
|
||||
- 'Jumatatu'
|
||||
- 'Jumanne'
|
||||
- 'Jumatano'
|
||||
- 'Alhamisi'
|
||||
- 'Ijumaa'
|
||||
- 'Jumamosi'
|
||||
- 'Jumapili'
|
||||
YES: "Ndiyo"
|
||||
NO: "Hapana"
|
||||
CRON:
|
||||
EVERY: kila
|
||||
EVERY_HOUR: kila saa
|
||||
EVERY_MINUTE: kila dakika
|
||||
EVERY_DAY_OF_WEEK: kila siku ya juma
|
||||
EVERY_DAY_OF_MONTH: kila siku ya mwezi
|
||||
EVERY_MONTH: kila mwezi
|
||||
TEXT_PERIOD: Kila <b />
|
||||
TEXT_MINS: ' saa <b /> dakika (saa) zilizopita saa'
|
||||
TEXT_TIME: ' saa <b />: <b />'
|
||||
TEXT_DOW: ' kwenye <b />'
|
||||
TEXT_MONTH: ' ya <b />'
|
||||
TEXT_DOM: ' kwenye <b />'
|
||||
ERROR1: Lebo% s haitumiki!
|
||||
ERROR2: Idadi mbaya ya vitu
|
||||
ERROR3: Jquery_element inapaswa kuwekwa kwenye mipangilio ya jqCron
|
||||
ERROR4: Maneno yasiyotambulika
|
||||
@@ -1,11 +1,75 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\nชื่อเรื่อง: %1$s\n---\n\n# ข้อผิดพลาด: Invalid Frontmatter\n\nPath: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Error: Invalid Frontmatter\n\nPath: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_PLURALS:
|
||||
'/(quiz)$/i': '\1zes'
|
||||
'/^(ox)$/i': '\1en'
|
||||
'/([m|l])ouse$/i': '\1ice'
|
||||
'/(matr|vert|ind)ix|ex$/i': '\1ices'
|
||||
'/(x|ch|ss|sh)$/i': '\1es'
|
||||
'/([^aeiouy]|qu)ies$/i': '\1y'
|
||||
'/([^aeiouy]|qu)y$/i': '\1ies'
|
||||
'/(hive)$/i': '\1s'
|
||||
'/(?:([^f])fe|([lr])f)$/i': '\1\2ves'
|
||||
'/sis$/i': 'ses'
|
||||
'/([ti])um$/i': '\1a'
|
||||
'/(buffal|tomat)o$/i': '\1oes'
|
||||
'/(bu)s$/i': '\1ses'
|
||||
'/(alias|status)/i': '\1es'
|
||||
'/(octop|vir)us$/i': '\1i'
|
||||
'/(ax|test)is$/i': '\1es'
|
||||
'/s$/i': 's'
|
||||
'/$/': 's'
|
||||
INFLECTOR_SINGULAR:
|
||||
'/(quiz)zes$/i': '\1'
|
||||
'/(matr)ices$/i': '\1ix'
|
||||
'/(vert|ind)ices$/i': '\1ex'
|
||||
'/^(ox)en/i': '\1'
|
||||
'/(alias|status)es$/i': '\1'
|
||||
'/([octop|vir])i$/i': '\1us'
|
||||
'/(cris|ax|test)es$/i': '\1is'
|
||||
'/(shoe)s$/i': '\1'
|
||||
'/(o)es$/i': '\1'
|
||||
'/(bus)es$/i': '\1'
|
||||
'/([m|l])ice$/i': '\1ouse'
|
||||
'/(x|ch|ss|sh)es$/i': '\1'
|
||||
'/(m)ovies$/i': '\1ovie'
|
||||
'/(s)eries$/i': '\1eries'
|
||||
'/([^aeiouy]|qu)ies$/i': '\1y'
|
||||
'/([lr])ves$/i': '\1f'
|
||||
'/(tive)s$/i': '\1'
|
||||
'/(hive)s$/i': '\1'
|
||||
'/([^f])ves$/i': '\1fe'
|
||||
'/(^analy)ses$/i': '\1sis'
|
||||
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2sis'
|
||||
'/([ti])a$/i': '\1um'
|
||||
'/(n)ews$/i': '\1ews'
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'อุปกรณ์'
|
||||
- 'ข้อมูล'
|
||||
- 'ข้าว'
|
||||
- 'เงิน'
|
||||
- 'สายพันธุ์'
|
||||
- 'ซีรีส์'
|
||||
- 'ปลา'
|
||||
- 'แกะ'
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'คน'
|
||||
'man': 'ผู้ชาย'
|
||||
'child': 'เด็กเด็ก'
|
||||
'sex': 'เพศ'
|
||||
'move': 'ย้าย'
|
||||
INFLECTOR_ORDINALS:
|
||||
'default': 'th'
|
||||
'first': 'st'
|
||||
'second': 'nd'
|
||||
'third': 'rd'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: ไม่มีวันที่ให้
|
||||
BAD_DATE: รูปแบบวันที่ผิด
|
||||
AGO: ที่ผ่านมา
|
||||
FROM_NOW: จากตอนนี้
|
||||
JUST_NOW: เมื่อกี้
|
||||
SECOND: วินาที
|
||||
MINUTE: นาที
|
||||
HOUR: ชั่วโมง
|
||||
@@ -17,6 +81,10 @@ GRAV:
|
||||
SEC: วิ
|
||||
MIN: นาที
|
||||
HR: ชม.
|
||||
WK: wk
|
||||
MO: mo
|
||||
YR: yr
|
||||
DEC: dec
|
||||
SECOND_PLURAL: วินาที
|
||||
MINUTE_PLURAL: นาที
|
||||
HOUR_PLURAL: ชั่วโมง
|
||||
@@ -28,11 +96,15 @@ GRAV:
|
||||
SEC_PLURAL: วินาที
|
||||
MIN_PLURAL: นาที
|
||||
HR_PLURAL: ชั่วโมง
|
||||
WK_PLURAL: wks
|
||||
MO_PLURAL: mos
|
||||
YR_PLURAL: ปี
|
||||
DEC_PLURAL: decs
|
||||
FORM:
|
||||
VALIDATION_FAIL: '<b>ตรวจสอบล้มเหลว: </b>'
|
||||
INVALID_INPUT: 'ป้อนข้อมูลไม่ถูกต้องใน'
|
||||
MISSING_REQUIRED_FIELD: 'ขาดข้อมูลที่จำเป็น:'
|
||||
XSS_ISSUES: "ตรวจพบปัญหา XSS ที่เป็นไปได้ในฟิลด์ '%s'"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'มกราคม'
|
||||
- 'กุมภาพันธ์'
|
||||
@@ -54,3 +126,22 @@ GRAV:
|
||||
- 'ศุกร์'
|
||||
- 'เสาร์'
|
||||
- 'อาทิตย์'
|
||||
YES: "ใช่"
|
||||
NO: "ไม่"
|
||||
CRON:
|
||||
EVERY: ทุก ๆ
|
||||
EVERY_HOUR: ทุกชั่วโมง
|
||||
EVERY_MINUTE: ทุกนาที
|
||||
EVERY_DAY_OF_WEEK: ทุกวันในสัปดาห์
|
||||
EVERY_DAY_OF_MONTH: ทุกวันของเดือน
|
||||
EVERY_MONTH: ทุกเดือน
|
||||
TEXT_PERIOD: ทุก ๆ <b />
|
||||
TEXT_MINS: ' ที่ <b /> นาทีที่ผ่านไปแล้ว'
|
||||
TEXT_TIME: ' เวลา <b />:<b />'
|
||||
TEXT_DOW: ' บน <b />'
|
||||
TEXT_MONTH: ' จาก <b />'
|
||||
TEXT_DOM: ' บน <b />'
|
||||
ERROR1: ไม่รองรับแท็ก %s!
|
||||
ERROR2: จำนวนองค์ประกอบไม่ดี
|
||||
ERROR3: ควรตั้งค่า jquery_element เป็นการตั้งค่า jqCron
|
||||
ERROR4: นิพจน์ที่ไม่รู้จัก
|
||||
|
||||
@@ -125,6 +125,8 @@ GRAV:
|
||||
- '星期五'
|
||||
- '星期六'
|
||||
- '星期日'
|
||||
YES: "是"
|
||||
NO: "否"
|
||||
CRON:
|
||||
EVERY: 每隔
|
||||
EVERY_HOUR: 每小时
|
||||
|
||||
@@ -62,6 +62,8 @@ GRAV:
|
||||
- '星期五'
|
||||
- '星期六'
|
||||
- '星期日'
|
||||
YES: "是"
|
||||
NO: "否"
|
||||
CRON:
|
||||
EVERY: 每
|
||||
EVERY_HOUR: 每小時
|
||||
|
||||
@@ -125,6 +125,8 @@ GRAV:
|
||||
- '星期五'
|
||||
- '星期六'
|
||||
- '星期日'
|
||||
YES: "是"
|
||||
NO: "否"
|
||||
CRON:
|
||||
EVERY: 每隔
|
||||
EVERY_HOUR: 每小时
|
||||
|
||||
16
system/rector.php
Normal file
16
system/rector.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use Rector\Config\RectorConfig;
|
||||
|
||||
return RectorConfig::configure()
|
||||
->withSkip([
|
||||
__DIR__ . '/vendor',
|
||||
])
|
||||
->withPaths([
|
||||
__DIR__
|
||||
])
|
||||
->withPhpSets(php82: true)
|
||||
->withPhpVersion(Rector\ValueObject\PhpVersion::PHP_84)
|
||||
->withRules([
|
||||
Rector\Php84\Rector\Param\ExplicitNullableParamTypeRector::class,
|
||||
]);
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Core
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -18,17 +18,17 @@ $path = $_SERVER['SCRIPT_NAME'];
|
||||
if ($path !== '/index.php' && is_file($root . $path)) {
|
||||
if (!(
|
||||
// Block all direct access to files and folders beginning with a dot
|
||||
strpos($path, '/.') !== false
|
||||
str_contains((string) $path, '/.')
|
||||
// Block all direct access for these folders
|
||||
|| preg_match('`^/(\.git|cache|bin|logs|backup|webserver-configs|tests)/`ui', $path)
|
||||
|| preg_match('`^/(\.git|cache|bin|logs|backup|webserver-configs|tests)/`ui', (string) $path)
|
||||
// Block access to specific file types for these system folders
|
||||
|| preg_match('`^/(system|vendor)/(.*)\.(txt|xml|md|html|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$`ui', $path)
|
||||
|| preg_match('`^/(system|vendor)/(.*)\.(txt|xml|md|html|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$`ui', (string) $path)
|
||||
// Block access to specific file types for these user folders
|
||||
|| preg_match('`^/(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$`ui', $path)
|
||||
|| preg_match('`^/(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$`ui', (string) $path)
|
||||
// Block all direct access to .md files
|
||||
|| preg_match('`\.md$`ui', $path)
|
||||
|| preg_match('`\.md$`ui', (string) $path)
|
||||
// Block access to specific files in the root folder
|
||||
|| preg_match('`^/(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess)$`ui', $path)
|
||||
|| preg_match('`^/(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess)$`ui', (string) $path)
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
93
system/src/Doctrine/Common/Cache/FilesystemCache.php
Normal file
93
system/src/Doctrine/Common/Cache/FilesystemCache.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
|
||||
/**
|
||||
* Filesystem cache driver (backwards compatibility).
|
||||
*/
|
||||
class FilesystemCache extends CacheProvider
|
||||
{
|
||||
public const EXTENSION = '.doctrinecache.data';
|
||||
|
||||
/** @var FilesystemAdapter */
|
||||
private $pool;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($directory, $extension = self::EXTENSION, $umask = 0002)
|
||||
{
|
||||
user_error(self::class . ' is deprecated since Grav 1.8, use Symfony cache instead', E_USER_DEPRECATED);
|
||||
|
||||
$this->pool = new FilesystemAdapter('', 0, $directory);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
$item = $this->pool->getItem(rawurlencode($id));
|
||||
|
||||
return $item->isHit() ? $item->get() : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
return $this->pool->hasItem(rawurlencode($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
$item = $this->pool->getItem(rawurlencode($id));
|
||||
|
||||
if (0 < $lifeTime) {
|
||||
$item->expiresAfter($lifeTime);
|
||||
}
|
||||
|
||||
return $this->pool->save($item->set($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
return $this->pool->deleteItem(rawurlencode($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doFlush()
|
||||
{
|
||||
return $this->pool->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
protected function doGetStats()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -194,12 +194,12 @@ class Assets extends PropertyObject
|
||||
}
|
||||
|
||||
$params = array_merge([$location], $params);
|
||||
call_user_func_array([$this, 'add'], $params);
|
||||
call_user_func_array($this->add(...), $params);
|
||||
}
|
||||
} elseif (isset($this->collections[$asset])) {
|
||||
array_shift($args);
|
||||
$args = array_merge([$this->collections[$asset]], $args);
|
||||
call_user_func_array([$this, 'add'], $args);
|
||||
call_user_func_array($this->add(...), $args);
|
||||
} else {
|
||||
// Get extension
|
||||
$path = parse_url($asset, PHP_URL_PATH);
|
||||
@@ -209,11 +209,11 @@ class Assets extends PropertyObject
|
||||
if ($extension !== '') {
|
||||
$extension = strtolower($extension);
|
||||
if ($extension === 'css') {
|
||||
call_user_func_array([$this, 'addCss'], $args);
|
||||
call_user_func_array($this->addCss(...), $args);
|
||||
} elseif ($extension === 'js') {
|
||||
call_user_func_array([$this, 'addJs'], $args);
|
||||
call_user_func_array($this->addJs(...), $args);
|
||||
} elseif ($extension === 'mjs') {
|
||||
call_user_func_array([$this, 'addJsModule'], $args);
|
||||
call_user_func_array($this->addJsModule(...), $args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -261,14 +261,20 @@ class Assets extends PropertyObject
|
||||
$default = 'before';
|
||||
}
|
||||
|
||||
$options['position'] = $options['position'] ?? $default;
|
||||
$options['position'] ??= $default;
|
||||
}
|
||||
|
||||
unset($options['pipeline']);
|
||||
}
|
||||
|
||||
// Add timestamp
|
||||
$options['timestamp'] = $this->timestamp;
|
||||
$timestamp_override = $options['timestamp'] ?? true;
|
||||
|
||||
if (filter_var($timestamp_override, FILTER_VALIDATE_BOOLEAN)) {
|
||||
$options['timestamp'] = $this->timestamp;
|
||||
} else {
|
||||
$options['timestamp'] = null;
|
||||
}
|
||||
|
||||
// Set order
|
||||
$group = $options['group'] ?? 'head';
|
||||
@@ -392,6 +398,9 @@ class Assets extends PropertyObject
|
||||
|
||||
if ($key === 'position' && $value === 'pipeline') {
|
||||
$type = $asset->getType();
|
||||
if ($type === 'jsmodule') {
|
||||
$type = 'js_module';
|
||||
}
|
||||
|
||||
if ($asset->getRemote() && $this->{strtolower($type) . '_pipeline_include_externals'} === false && $asset['position'] === 'pipeline') {
|
||||
if ($this->{strtolower($type) . '_pipeline_before_excludes'}) {
|
||||
@@ -423,9 +432,7 @@ class Assets extends PropertyObject
|
||||
*/
|
||||
protected function sortAssets($assets)
|
||||
{
|
||||
uasort($assets, static function ($a, $b) {
|
||||
return $b['priority'] <=> $a['priority'] ?: $a['order'] <=> $b['order'];
|
||||
});
|
||||
uasort($assets, static fn($a, $b) => $b['priority'] <=> $a['priority'] ?: $a['order'] <=> $b['order']);
|
||||
|
||||
return $assets;
|
||||
}
|
||||
@@ -568,18 +575,11 @@ class Assets extends PropertyObject
|
||||
*/
|
||||
protected function getBaseType($type)
|
||||
{
|
||||
switch ($type) {
|
||||
case $this::JS_TYPE:
|
||||
case $this::INLINE_JS_TYPE:
|
||||
$base_type = $this::JS;
|
||||
break;
|
||||
case $this::JS_MODULE_TYPE:
|
||||
case $this::INLINE_JS_MODULE_TYPE:
|
||||
$base_type = $this::JS_MODULE;
|
||||
break;
|
||||
default:
|
||||
$base_type = $this::CSS;
|
||||
}
|
||||
$base_type = match ($type) {
|
||||
$this::JS_TYPE, $this::INLINE_JS_TYPE => $this::JS,
|
||||
$this::JS_MODULE_TYPE, $this::INLINE_JS_MODULE_TYPE => $this::JS_MODULE,
|
||||
default => $this::CSS,
|
||||
};
|
||||
|
||||
return $base_type;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -114,7 +114,7 @@ abstract class BaseAsset extends PropertyObject
|
||||
|
||||
// Do some special stuff for CSS/JS (not inline)
|
||||
if (!Utils::startsWith($this->getType(), 'inline')) {
|
||||
$this->base_url = rtrim($uri->rootUrl($config->get('system.absolute_urls')), '/') . '/';
|
||||
$this->base_url = rtrim((string) $uri->rootUrl($config->get('system.absolute_urls')), '/') . '/';
|
||||
$this->remote = static::isRemoteLink($asset);
|
||||
|
||||
// Move this to render?
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -192,15 +192,15 @@ class BlockAssets
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
|
||||
$base = rtrim($grav['base_url'], '/') ?: '/';
|
||||
$base = rtrim((string) $grav['base_url'], '/') ?: '/';
|
||||
|
||||
if (strpos($url, $base) === 0) {
|
||||
if (str_starts_with($url, $base)) {
|
||||
if ($pipeline) {
|
||||
// Remove file timestamp if CSS pipeline has been enabled.
|
||||
$url = preg_replace('|[?#].*|', '', $url);
|
||||
}
|
||||
|
||||
return substr($url, strlen($base) - 1);
|
||||
return substr((string) $url, strlen($base) - 1);
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -34,7 +34,7 @@ class Pipeline extends PropertyObject
|
||||
protected const JS_MODULE_ASSET = 3;
|
||||
|
||||
/** @const Regex to match CSS urls */
|
||||
protected const CSS_URL_REGEX = '{url\(([\'\"]?)(.*?)\1\)}';
|
||||
protected const CSS_URL_REGEX = '{url\(([\'\"]?)(.*?)\1\)|(@import)\s+([\'\"])(.*?)\4}';
|
||||
|
||||
/** @const Regex to match JS imports */
|
||||
protected const JS_IMPORT_REGEX = '{import.+from\s?[\'|\"](.+?)[\'|\"]}';
|
||||
@@ -257,9 +257,14 @@ class Pipeline extends PropertyObject
|
||||
// Find any css url() elements, grab the URLs and calculate an absolute path
|
||||
// Then replace the old url with the new one
|
||||
$file = (string)preg_replace_callback(self::CSS_URL_REGEX, function ($matches) use ($dir, $local) {
|
||||
$isImport = count($matches) > 3 && $matches[3] === '@import';
|
||||
|
||||
$old_url = $matches[2];
|
||||
|
||||
if ($isImport) {
|
||||
$old_url = $matches[5];
|
||||
} else {
|
||||
$old_url = $matches[2];
|
||||
}
|
||||
|
||||
// Ensure link is not rooted to web server, a data URL, or to a remote host
|
||||
if (preg_match(self::FIRST_FORWARDSLASH_REGEX, $old_url) || Utils::startsWith($old_url, 'data:') || $this->isRemoteLink($old_url)) {
|
||||
return $matches[0];
|
||||
@@ -273,8 +278,12 @@ class Pipeline extends PropertyObject
|
||||
|
||||
$new_url = ($local ? $this->base_url : '') . $old_url;
|
||||
|
||||
return str_replace($matches[2], $new_url, $matches[0]);
|
||||
}, $file);
|
||||
if ($isImport) {
|
||||
return str_replace($matches[5], $new_url, $matches[0]);
|
||||
} else {
|
||||
return str_replace($matches[2], $new_url, $matches[0]);
|
||||
}
|
||||
}, (string) $file);
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets\Traits
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -55,7 +55,7 @@ trait AssetUtilsTrait
|
||||
return false;
|
||||
}
|
||||
|
||||
return (0 === strpos($link, 'http://') || 0 === strpos($link, 'https://') || 0 === strpos($link, '//'));
|
||||
return (str_starts_with($link, 'http://') || str_starts_with($link, 'https://') || str_starts_with($link, '//'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,18 +76,18 @@ trait AssetUtilsTrait
|
||||
|
||||
if (static::isRemoteLink($link)) {
|
||||
$local = false;
|
||||
if (0 === strpos($link, '//')) {
|
||||
if (str_starts_with((string) $link, '//')) {
|
||||
$link = 'http:' . $link;
|
||||
}
|
||||
$relative_dir = dirname($relative_path);
|
||||
$relative_dir = dirname((string) $relative_path);
|
||||
} else {
|
||||
// Fix to remove relative dir if grav is in one
|
||||
if (($this->base_url !== '/') && Utils::startsWith($relative_path, $this->base_url)) {
|
||||
$base_url = '#' . preg_quote($this->base_url, '#') . '#';
|
||||
$relative_path = ltrim(preg_replace($base_url, '/', $link, 1), '/');
|
||||
$relative_path = ltrim((string) preg_replace($base_url, '/', (string) $link, 1), '/');
|
||||
}
|
||||
|
||||
$relative_dir = dirname($relative_path);
|
||||
$relative_dir = dirname((string) $relative_path);
|
||||
$link = GRAV_ROOT . '/' . $relative_path;
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ trait AssetUtilsTrait
|
||||
|
||||
// Double check last character being
|
||||
if ($type === self::JS_ASSET || $type === self::JS_MODULE_ASSET) {
|
||||
$file = rtrim($file, ' ;') . ';';
|
||||
$file = rtrim((string) $file, ' ;') . ';';
|
||||
}
|
||||
|
||||
// If this is CSS + the file is local + rewrite enabled
|
||||
@@ -113,7 +113,7 @@ trait AssetUtilsTrait
|
||||
$file = $this->jsRewrite($file, $relative_dir, $local);
|
||||
}
|
||||
|
||||
$file = rtrim($file) . PHP_EOL;
|
||||
$file = rtrim((string) $file) . PHP_EOL;
|
||||
$buffer .= $file;
|
||||
}
|
||||
|
||||
@@ -170,9 +170,9 @@ trait AssetUtilsTrait
|
||||
}
|
||||
|
||||
if (in_array($key, $no_key, true)) {
|
||||
$element = htmlentities($value, ENT_QUOTES, 'UTF-8', false);
|
||||
$element = htmlentities((string) $value, ENT_QUOTES, 'UTF-8', false);
|
||||
} else {
|
||||
$element = $key . '="' . htmlentities($value, ENT_QUOTES, 'UTF-8', false) . '"';
|
||||
$element = $key . '="' . htmlentities((string) $value, ENT_QUOTES, 'UTF-8', false) . '"';
|
||||
}
|
||||
|
||||
$html .= ' ' . $element;
|
||||
@@ -191,7 +191,8 @@ trait AssetUtilsTrait
|
||||
{
|
||||
$querystring = '';
|
||||
|
||||
$asset = $asset ?? $this->asset;
|
||||
$asset ??= $this->asset;
|
||||
$attributes = $this->attributes;
|
||||
|
||||
if (!empty($this->query)) {
|
||||
if (Utils::contains($asset, '?')) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets\Traits
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -113,7 +113,7 @@ trait LegacyAssetsTrait
|
||||
*/
|
||||
public function addAsyncJs($asset, $priority = 10, $pipeline = true, $group = 'head')
|
||||
{
|
||||
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.6, use dynamic method with [\'loading\' => \'async\']', E_USER_DEPRECATED);
|
||||
user_error(self::class . '::' . __FUNCTION__ . '() is deprecated since Grav 1.6, use dynamic method with [\'loading\' => \'async\']', E_USER_DEPRECATED);
|
||||
|
||||
return $this->addJs($asset, $priority, $pipeline, 'async', $group);
|
||||
}
|
||||
@@ -130,7 +130,7 @@ trait LegacyAssetsTrait
|
||||
*/
|
||||
public function addDeferJs($asset, $priority = 10, $pipeline = true, $group = 'head')
|
||||
{
|
||||
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.6, use dynamic method with [\'loading\' => \'defer\']', E_USER_DEPRECATED);
|
||||
user_error(self::class . '::' . __FUNCTION__ . '() is deprecated since Grav 1.6, use dynamic method with [\'loading\' => \'defer\']', E_USER_DEPRECATED);
|
||||
|
||||
return $this->addJs($asset, $priority, $pipeline, 'defer', $group);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets\Traits
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -338,7 +338,7 @@ trait TestingAssetsTrait
|
||||
$directory,
|
||||
FilesystemIterator::SKIP_DOTS
|
||||
)), $pattern);
|
||||
$offset = strlen($ltrim);
|
||||
$offset = strlen((string) $ltrim);
|
||||
$files = [];
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Backup
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -54,7 +54,7 @@ class Backups
|
||||
|
||||
/** @var EventDispatcher $dispatcher */
|
||||
$dispatcher = $grav['events'];
|
||||
$dispatcher->addListener('onSchedulerInitialized', [$this, 'onSchedulerInitialized']);
|
||||
$dispatcher->addListener('onSchedulerInitialized', $this->onSchedulerInitialized(...));
|
||||
|
||||
$grav->fireEvent('onBackupsInitialized', new Event(['backups' => $this]));
|
||||
}
|
||||
@@ -106,7 +106,7 @@ class Backups
|
||||
{
|
||||
$param_sep = Grav::instance()['config']->get('system.param_sep', ':');
|
||||
$download = urlencode(base64_encode(Utils::basename($backup)));
|
||||
$url = rtrim(Grav::instance()['uri']->rootUrl(true), '/') . '/' . trim(
|
||||
$url = rtrim((string) Grav::instance()['uri']->rootUrl(true), '/') . '/' . trim(
|
||||
$base_url,
|
||||
'/'
|
||||
) . '/task' . $param_sep . 'backup/download' . $param_sep . $download . '/admin-nonce' . $param_sep . Utils::getNonce('admin-form');
|
||||
@@ -158,7 +158,7 @@ class Backups
|
||||
static::$backups = [];
|
||||
|
||||
$grav = Grav::instance();
|
||||
$backups_itr = new GlobIterator(static::$backup_dir . '/*.zip', FilesystemIterator::KEY_AS_FILENAME);
|
||||
$backups_itr = new GlobIterator(static::$backup_dir . '/*.zip', FilesystemIterator::KEY_AS_FILENAME | \FilesystemIterator::SKIP_DOTS);
|
||||
$inflector = $grav['inflector'];
|
||||
$long_date_format = DATE_RFC2822;
|
||||
|
||||
@@ -194,7 +194,7 @@ class Backups
|
||||
* @param callable|null $status
|
||||
* @return string|null
|
||||
*/
|
||||
public static function backup($id = 0, callable $status = null)
|
||||
public static function backup($id = 0, ?callable $status = null)
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
|
||||
@@ -210,7 +210,7 @@ class Backups
|
||||
|
||||
$name = $grav['inflector']->underscorize($backup->name);
|
||||
$date = date(static::BACKUP_DATE_FORMAT, time());
|
||||
$filename = trim($name, '_') . '--' . $date . '.zip';
|
||||
$filename = trim((string) $name, '_') . '--' . $date . '.zip';
|
||||
$destination = static::$backup_dir . DS . $filename;
|
||||
$max_execution_time = ini_set('max_execution_time', '600');
|
||||
$backup_root = $backup->root;
|
||||
@@ -218,7 +218,7 @@ class Backups
|
||||
if ($locator->isStream($backup_root)) {
|
||||
$backup_root = $locator->findResource($backup_root);
|
||||
} else {
|
||||
$backup_root = rtrim(GRAV_ROOT . $backup_root, '/');
|
||||
$backup_root = rtrim(GRAV_ROOT . $backup_root, DS) ?: DS;
|
||||
}
|
||||
|
||||
if (!$backup_root || !file_exists($backup_root)) {
|
||||
@@ -315,7 +315,10 @@ class Backups
|
||||
*/
|
||||
protected static function convertExclude($exclude)
|
||||
{
|
||||
$lines = preg_split("/[\s,]+/", $exclude);
|
||||
// Split by newlines, commas, or multiple spaces
|
||||
$lines = preg_split("/[\r\n,]+|[\s]{2,}/", $exclude);
|
||||
// Remove empty values and trim
|
||||
$lines = array_filter(array_map('trim', $lines));
|
||||
|
||||
return array_map('trim', $lines, array_fill(0, count($lines), '/'));
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -27,7 +27,7 @@ class Browser
|
||||
{
|
||||
try {
|
||||
$this->useragent = parse_user_agent();
|
||||
} catch (InvalidArgumentException $e) {
|
||||
} catch (InvalidArgumentException) {
|
||||
$this->useragent = parse_user_agent("Mozilla/5.0 (compatible; Unknown;)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
/**
|
||||
* @package Grav\Common
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common;
|
||||
|
||||
use DirectoryIterator;
|
||||
use \Doctrine\Common\Cache as DoctrineCache;
|
||||
use Doctrine\Common\Cache\CacheProvider;
|
||||
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
|
||||
use Exception;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
@@ -18,6 +19,12 @@ use Grav\Common\Scheduler\Scheduler;
|
||||
use LogicException;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||
use Symfony\Component\Cache\Adapter\ApcuAdapter;
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use Symfony\Component\Cache\Adapter\MemcachedAdapter;
|
||||
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||||
use Symfony\Component\Cache\Psr16Cache;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use function dirname;
|
||||
use function extension_loaded;
|
||||
@@ -27,12 +34,11 @@ use function is_array;
|
||||
|
||||
/**
|
||||
* The GravCache object is used throughout Grav to store and retrieve cached data.
|
||||
* It uses DoctrineCache library and supports a variety of caching mechanisms. Those include:
|
||||
* It uses Symfony library (adding backward compatibility to Doctrine Cache) and supports a variety of caching mechanisms. Those include:
|
||||
*
|
||||
* APCu
|
||||
* RedisCache
|
||||
* MemCache
|
||||
* MemCacheD
|
||||
* MemCached
|
||||
* FileSystem
|
||||
*/
|
||||
class Cache extends Getters
|
||||
@@ -49,7 +55,10 @@ class Cache extends Getters
|
||||
/** @var Config $config */
|
||||
protected $config;
|
||||
|
||||
/** @var DoctrineCache\CacheProvider */
|
||||
/** @var AdapterInterface */
|
||||
protected $adapter;
|
||||
|
||||
/** @var CacheProvider */
|
||||
protected $driver;
|
||||
|
||||
/** @var CacheInterface */
|
||||
@@ -70,6 +79,7 @@ class Cache extends Getters
|
||||
protected static $standard_remove = [
|
||||
'cache://twig/',
|
||||
'cache://doctrine/',
|
||||
'cache://grav/',
|
||||
'cache://compiled/',
|
||||
'cache://clockwork/',
|
||||
'cache://validated-',
|
||||
@@ -80,6 +90,7 @@ class Cache extends Getters
|
||||
protected static $standard_remove_no_images = [
|
||||
'cache://twig/',
|
||||
'cache://doctrine/',
|
||||
'cache://grav/',
|
||||
'cache://compiled/',
|
||||
'cache://clockwork/',
|
||||
'cache://validated-',
|
||||
@@ -142,14 +153,14 @@ class Cache extends Getters
|
||||
|
||||
// Cache key allows us to invalidate all cache on configuration changes.
|
||||
$this->key = ($prefix ?: 'g') . '-' . $uniqueness;
|
||||
$this->cache_dir = $grav['locator']->findResource('cache://doctrine/' . $uniqueness, true, true);
|
||||
$this->cache_dir = $grav['locator']->findResource('cache://grav/' . $uniqueness, true, true);
|
||||
$this->driver_setting = $this->config->get('system.cache.driver');
|
||||
$this->driver = $this->getCacheDriver();
|
||||
$this->driver->setNamespace($this->key);
|
||||
$this->adapter = $this->getCacheAdapter();
|
||||
$this->driver = $this->getCacheDriver($this->adapter);
|
||||
|
||||
/** @var EventDispatcher $dispatcher */
|
||||
$dispatcher = Grav::instance()['events'];
|
||||
$dispatcher->addListener('onSchedulerInitialized', [$this, 'onSchedulerInitialized']);
|
||||
$dispatcher->addListener('onSchedulerInitialized', $this->onSchedulerInitialized(...));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,36 +169,82 @@ class Cache extends Getters
|
||||
public function getSimpleCache()
|
||||
{
|
||||
if (null === $this->simpleCache) {
|
||||
$cache = new \Grav\Framework\Cache\Adapter\DoctrineCache($this->driver, '', $this->getLifetime());
|
||||
|
||||
// Disable cache key validation.
|
||||
$cache->setValidation(false);
|
||||
|
||||
$this->simpleCache = $cache;
|
||||
$this->simpleCache = new Psr16Cache($this->adapter);
|
||||
}
|
||||
|
||||
return $this->simpleCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the old out of date file-based caches
|
||||
* Deletes old cache files based on age
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function purgeOldCache()
|
||||
{
|
||||
// Get the max age for cache files from config (default 30 days)
|
||||
$max_age_days = $this->config->get('system.cache.purge_max_age_days', 30);
|
||||
$max_age_seconds = $max_age_days * 86400; // Convert days to seconds
|
||||
$now = time();
|
||||
$count = 0;
|
||||
|
||||
// First, clean up old orphaned cache directories (not the current one)
|
||||
$cache_dir = dirname($this->cache_dir);
|
||||
$current = Utils::basename($this->cache_dir);
|
||||
$count = 0;
|
||||
|
||||
|
||||
foreach (new DirectoryIterator($cache_dir) as $file) {
|
||||
$dir = $file->getBasename();
|
||||
if ($dir === $current || $file->isDot() || $file->isFile()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Folder::delete($file->getPathname());
|
||||
$count++;
|
||||
|
||||
// Check if directory is old and empty or very old (90+ days)
|
||||
$dir_age = $now - $file->getMTime();
|
||||
if ($dir_age > 7776000) { // 90 days
|
||||
Folder::delete($file->getPathname());
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Now clean up old cache files within the current cache directory
|
||||
if (is_dir($this->cache_dir)) {
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($this->cache_dir, \RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if ($file->isFile()) {
|
||||
$file_age = $now - $file->getMTime();
|
||||
if ($file_age > $max_age_seconds) {
|
||||
@unlink($file->getPathname());
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also clean up old files in compiled cache
|
||||
$grav = Grav::instance();
|
||||
$compiled_dir = $this->config->get('system.cache.compiled_dir', 'cache://compiled');
|
||||
$compiled_path = $grav['locator']->findResource($compiled_dir, true);
|
||||
|
||||
if ($compiled_path && is_dir($compiled_path)) {
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($compiled_path, \RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if ($file->isFile()) {
|
||||
$file_age = $now - $file->getMTime();
|
||||
// Compiled files can be kept longer (60 days)
|
||||
if ($file_age > ($max_age_seconds * 2)) {
|
||||
@unlink($file->getPathname());
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
@@ -229,53 +286,42 @@ class Cache extends Getters
|
||||
* If there is no config option for $driver in the config, or it's set to 'auto', it will
|
||||
* pick the best option based on which cache extensions are installed.
|
||||
*
|
||||
* @return DoctrineCache\CacheProvider The cache driver to use
|
||||
* @param string|null $namespace
|
||||
* @param int|null $defaultLifetime
|
||||
* @return AdapterInterface The cache driver to use
|
||||
* @throws \RedisException
|
||||
* @throws \Symfony\Component\Cache\Exception\CacheException
|
||||
*/
|
||||
public function getCacheDriver()
|
||||
public function getCacheAdapter(?string $namespace = null, ?int $defaultLifetime = null): AdapterInterface
|
||||
{
|
||||
$setting = $this->driver_setting;
|
||||
$setting = $this->driver_setting ?? 'auto';
|
||||
$driver_name = 'file';
|
||||
|
||||
if (in_array($setting, ['apc', 'xcache', 'wincache', 'memcache'], true)) {
|
||||
throw new LogicException(sprintf('Cache driver for %s has been removed, use auto, file, apcu or memcached instead!', $setting));
|
||||
}
|
||||
|
||||
// CLI compatibility requires a non-volatile cache driver
|
||||
if ($this->config->get('system.cache.cli_compatibility') && (
|
||||
$setting === 'auto' || $this->isVolatileDriver($setting))) {
|
||||
if ($this->config->get('system.cache.cli_compatibility') && ($setting === 'auto' || $this->isVolatileDriver($setting))) {
|
||||
$setting = $driver_name;
|
||||
}
|
||||
|
||||
if (!$setting || $setting === 'auto') {
|
||||
if ($setting === 'auto' || $this->isVolatileDriver($setting)) {
|
||||
if (extension_loaded('apcu')) {
|
||||
$driver_name = 'apcu';
|
||||
} elseif (extension_loaded('wincache')) {
|
||||
$driver_name = 'wincache';
|
||||
}
|
||||
} else {
|
||||
$driver_name = $setting;
|
||||
}
|
||||
|
||||
$this->driver_name = $driver_name;
|
||||
$namespace ??= $this->key;
|
||||
$defaultLifetime ??= 0;
|
||||
|
||||
switch ($driver_name) {
|
||||
case 'apc':
|
||||
case 'apcu':
|
||||
$driver = new DoctrineCache\ApcuCache();
|
||||
break;
|
||||
|
||||
case 'wincache':
|
||||
$driver = new DoctrineCache\WinCacheCache();
|
||||
break;
|
||||
|
||||
case 'memcache':
|
||||
if (extension_loaded('memcache')) {
|
||||
$memcache = new \Memcache();
|
||||
$memcache->connect(
|
||||
$this->config->get('system.cache.memcache.server', 'localhost'),
|
||||
$this->config->get('system.cache.memcache.port', 11211)
|
||||
);
|
||||
$driver = new DoctrineCache\MemcacheCache();
|
||||
$driver->setMemcache($memcache);
|
||||
} else {
|
||||
throw new LogicException('Memcache PHP extension has not been installed');
|
||||
}
|
||||
$adapter = new ApcuAdapter($namespace, $defaultLifetime);
|
||||
break;
|
||||
|
||||
case 'memcached':
|
||||
@@ -285,8 +331,7 @@ class Cache extends Getters
|
||||
$this->config->get('system.cache.memcached.server', 'localhost'),
|
||||
$this->config->get('system.cache.memcached.port', 11211)
|
||||
);
|
||||
$driver = new DoctrineCache\MemcachedCache();
|
||||
$driver->setMemcached($memcached);
|
||||
$adapter = new MemcachedAdapter($memcached, $namespace, $defaultLifetime);
|
||||
} else {
|
||||
throw new LogicException('Memcached PHP extension has not been installed');
|
||||
}
|
||||
@@ -318,19 +363,39 @@ class Cache extends Getters
|
||||
throw new \RedisException('Could not select alternate Redis database ID');
|
||||
}
|
||||
|
||||
$driver = new DoctrineCache\RedisCache();
|
||||
$driver->setRedis($redis);
|
||||
$adapter = new RedisAdapter($redis, $namespace, $defaultLifetime);
|
||||
} else {
|
||||
throw new LogicException('Redis PHP extension has not been installed');
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$driver = new DoctrineCache\FilesystemCache($this->cache_dir);
|
||||
$adapter = new FilesystemAdapter($namespace, $defaultLifetime, $this->cache_dir);
|
||||
break;
|
||||
}
|
||||
|
||||
return $driver;
|
||||
return $adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically picks the cache mechanism to use. If you pick one manually it will use that
|
||||
* If there is no config option for $driver in the config, or it's set to 'auto', it will
|
||||
* pick the best option based on which cache extensions are installed.
|
||||
*
|
||||
* @return CacheProvider The cache driver to use
|
||||
*/
|
||||
public function getCacheDriver(?AdapterInterface $adapter = null)
|
||||
{
|
||||
if (null === $adapter) {
|
||||
$adapter = $this->getCacheAdapter();
|
||||
}
|
||||
|
||||
$cache = DoctrineProvider::wrap($adapter);
|
||||
if (!$cache instanceof CacheProvider) {
|
||||
throw new \RuntimeException('Internal error');
|
||||
}
|
||||
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -472,8 +537,17 @@ class Cache extends Getters
|
||||
|
||||
// Delete entries in the doctrine cache if required
|
||||
if (in_array($remove, ['all', 'standard'])) {
|
||||
$cache = Grav::instance()['cache'];
|
||||
$cache->driver->deleteAll();
|
||||
try {
|
||||
$grav = Grav::instance();
|
||||
if ($grav->offsetExists('cache')) {
|
||||
$cache = $grav['cache'];
|
||||
if (isset($cache->driver)) {
|
||||
$cache->driver->deleteAll();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$output[] = 'cache: ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Clearing cache event to add paths to clear
|
||||
@@ -618,7 +692,7 @@ class Cache extends Getters
|
||||
*/
|
||||
public function isVolatileDriver($setting)
|
||||
{
|
||||
return in_array($setting, ['apc', 'apcu', 'xcache', 'wincache'], true);
|
||||
return $setting === 'apcu';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -632,8 +706,10 @@ class Cache extends Getters
|
||||
{
|
||||
/** @var Cache $cache */
|
||||
$cache = Grav::instance()['cache'];
|
||||
$deleted_folders = $cache->purgeOldCache();
|
||||
$msg = 'Purged ' . $deleted_folders . ' old cache folders...';
|
||||
$deleted_items = $cache->purgeOldCache();
|
||||
|
||||
$max_age = $cache->config->get('system.cache.purge_max_age_days', 30);
|
||||
$msg = 'Purged ' . $deleted_items . ' old cache items (files older than ' . $max_age . ' days)';
|
||||
|
||||
if ($echo) {
|
||||
echo $msg;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Config
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,10 @@ use BadMethodCallException;
|
||||
use Exception;
|
||||
use RocketTheme\Toolbox\File\PhpFile;
|
||||
use RuntimeException;
|
||||
use function filter_var;
|
||||
use function function_exists;
|
||||
use function get_class;
|
||||
use function ini_get;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
@@ -202,7 +205,7 @@ abstract class CompiledBase
|
||||
$cache = include $filename;
|
||||
if (!is_array($cache)
|
||||
|| !isset($cache['checksum'], $cache['data'], $cache['@class'])
|
||||
|| $cache['@class'] !== get_class($this)
|
||||
|| $cache['@class'] !== static::class
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -235,7 +238,7 @@ abstract class CompiledBase
|
||||
// Attempt to lock the file for writing.
|
||||
try {
|
||||
$file->lock(false);
|
||||
} catch (Exception $e) {
|
||||
} catch (Exception) {
|
||||
// Another process has locked the file; we will check this in a bit.
|
||||
}
|
||||
|
||||
@@ -245,7 +248,7 @@ abstract class CompiledBase
|
||||
}
|
||||
|
||||
$cache = [
|
||||
'@class' => get_class($this),
|
||||
'@class' => static::class,
|
||||
'timestamp' => time(),
|
||||
'checksum' => $this->checksum(),
|
||||
'files' => $this->files,
|
||||
@@ -254,6 +257,9 @@ abstract class CompiledBase
|
||||
|
||||
$file->save($cache);
|
||||
$file->unlock();
|
||||
|
||||
$this->preloadOpcodeCache($file);
|
||||
|
||||
$file->free();
|
||||
|
||||
$this->modified();
|
||||
@@ -266,4 +272,40 @@ abstract class CompiledBase
|
||||
{
|
||||
return $this->object->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure compiled cache file is primed into OPcache when available.
|
||||
*/
|
||||
protected function preloadOpcodeCache(PhpFile $file): void
|
||||
{
|
||||
if (!function_exists('opcache_invalidate') || !$this->isOpcacheEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$filename = $file->filename();
|
||||
if (!$filename) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Silence errors for restricted functions while keeping best effort behavior.
|
||||
@opcache_invalidate($filename, true);
|
||||
|
||||
if (function_exists('opcache_compile_file')) {
|
||||
@opcache_compile_file($filename);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if OPcache is active for current SAPI.
|
||||
*/
|
||||
protected function isOpcacheEnabled(): bool
|
||||
{
|
||||
$enabled = filter_var(ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN);
|
||||
|
||||
if (PHP_SAPI === 'cli') {
|
||||
$enabled = $enabled || filter_var(ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
|
||||
return $enabled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Config
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Config
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Config
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Config
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -149,7 +149,7 @@ class Config extends Data
|
||||
*/
|
||||
public function getLanguages()
|
||||
{
|
||||
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.5, use Grav::instance()[\'languages\'] instead', E_USER_DEPRECATED);
|
||||
user_error(self::class . '::' . __FUNCTION__ . '() is deprecated since Grav 1.5, use Grav::instance()[\'languages\'] instead', E_USER_DEPRECATED);
|
||||
|
||||
return Grav::instance()['languages'];
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Config
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -178,9 +178,7 @@ class ConfigFileFinder
|
||||
'filters' => [
|
||||
'pre-key' => $this->base,
|
||||
'key' => $pattern,
|
||||
'value' => function (RecursiveDirectoryIterator $file) use ($path) {
|
||||
return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()];
|
||||
}
|
||||
'value' => fn(RecursiveDirectoryIterator $file) => ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()]
|
||||
],
|
||||
'key' => 'SubPathname'
|
||||
];
|
||||
@@ -254,9 +252,7 @@ class ConfigFileFinder
|
||||
'filters' => [
|
||||
'pre-key' => $this->base,
|
||||
'key' => $pattern,
|
||||
'value' => function (RecursiveDirectoryIterator $file) use ($path) {
|
||||
return ["{$path}/{$file->getSubPathname()}" => $file->getMTime()];
|
||||
}
|
||||
'value' => fn(RecursiveDirectoryIterator $file) => ["{$path}/{$file->getSubPathname()}" => $file->getMTime()]
|
||||
],
|
||||
'key' => 'SubPathname'
|
||||
];
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Config
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Config
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -202,7 +202,7 @@ class Setup extends Data
|
||||
$setupFile = defined('GRAV_SETUP_PATH') ? GRAV_SETUP_PATH : (getenv('GRAV_SETUP_PATH') ?: null);
|
||||
if (null !== $setupFile) {
|
||||
// Make sure that the custom setup file exists. Terminates the script if not.
|
||||
if (!str_starts_with($setupFile, '/')) {
|
||||
if (!str_starts_with((string) $setupFile, '/')) {
|
||||
$setupFile = GRAV_WEBROOT . '/' . $setupFile;
|
||||
}
|
||||
if (!is_file($setupFile)) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Data
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -142,7 +142,7 @@ class Blueprint extends BlueprintForm
|
||||
{
|
||||
foreach ($this->dynamic as $key => $data) {
|
||||
// Locate field.
|
||||
$path = explode('/', $key);
|
||||
$path = explode('/', (string) $key);
|
||||
$current = &$this->items;
|
||||
|
||||
foreach ($path as $field) {
|
||||
@@ -168,7 +168,7 @@ class Blueprint extends BlueprintForm
|
||||
// Set dynamic property.
|
||||
foreach ($data as $property => $call) {
|
||||
$action = $call['action'];
|
||||
$method = 'dynamic' . ucfirst($action);
|
||||
$method = 'dynamic' . ucfirst((string) $action);
|
||||
$call['object'] = $this->object;
|
||||
|
||||
if (isset($this->handlers[$action])) {
|
||||
@@ -434,7 +434,7 @@ class Blueprint extends BlueprintForm
|
||||
$params = [];
|
||||
}
|
||||
|
||||
[$o, $f] = explode('::', $function, 2);
|
||||
[$o, $f] = explode('::', (string) $function, 2);
|
||||
|
||||
$data = null;
|
||||
if (!$f) {
|
||||
@@ -574,10 +574,9 @@ class Blueprint extends BlueprintForm
|
||||
/**
|
||||
* @param array $field
|
||||
* @param string $property
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public static function addPropertyRecursive(array &$field, $property, $value)
|
||||
public static function addPropertyRecursive(array &$field, $property, mixed $value)
|
||||
{
|
||||
if (is_array($value) && isset($field[$property]) && is_array($field[$property])) {
|
||||
$field[$property] = array_merge_recursive($field[$property], $value);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Data
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -129,7 +129,8 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
$items = $name !== '' ? $this->getProperty($name)['fields'] ?? [] : $this->items;
|
||||
foreach ($items as $key => $rules) {
|
||||
$type = $rules['type'] ?? '';
|
||||
if (!str_starts_with($type, '_') && !str_contains($key, '*')) {
|
||||
$ignore = (bool) array_filter((array)($rules['validate']['ignore'] ?? [])) ?? false;
|
||||
if (!str_starts_with((string) $type, '_') && !str_contains((string) $key, '*') && $ignore !== true) {
|
||||
$list[$prefix . $key] = null;
|
||||
}
|
||||
}
|
||||
@@ -195,6 +196,38 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
|
||||
$messages += Validation::validate($child, $rule);
|
||||
|
||||
if (isset($rule['validate']['match']) || isset($rule['validate']['match_exact']) || isset($rule['validate']['match_any'])) {
|
||||
$ruleKey = current(array_intersect(['match', 'match_exact', 'match_any'], array_keys($rule['validate'])));
|
||||
$otherKey = $rule['validate'][$ruleKey] ?? null;
|
||||
$otherVal = $data[$otherKey] ?? null;
|
||||
$otherLabel = $this->items[$otherKey]['label'] ?? $otherKey;
|
||||
$currentVal = $data[$key] ?? null;
|
||||
$currentLabel = $this->items[$key]['label'] ?? $key;
|
||||
|
||||
// Determine comparison type (loose, strict, substring)
|
||||
// Perform comparison:
|
||||
$isValid = false;
|
||||
if ($ruleKey === 'match') {
|
||||
$isValid = ($currentVal == $otherVal);
|
||||
} elseif ($ruleKey === 'match_exact') {
|
||||
$isValid = ($currentVal === $otherVal);
|
||||
} elseif ($ruleKey === 'match_any') {
|
||||
// If strings:
|
||||
if (is_string($currentVal) && is_string($otherVal)) {
|
||||
$isValid = (strlen($currentVal) && strlen($otherVal) && (str_contains($currentVal,
|
||||
$otherVal) || str_contains($otherVal, $currentVal)));
|
||||
}
|
||||
// If arrays:
|
||||
if (is_array($currentVal) && is_array($otherVal)) {
|
||||
$common = array_intersect($currentVal, $otherVal);
|
||||
$isValid = !empty($common);
|
||||
}
|
||||
}
|
||||
if (!$isValid) {
|
||||
$messages[$rule['name']][] = sprintf(Grav::instance()['language']->translate('PLUGIN_FORM.VALIDATION_MATCH'), $currentLabel, $otherLabel);
|
||||
}
|
||||
}
|
||||
|
||||
} elseif (is_array($child) && is_array($val)) {
|
||||
// Array has been defined in blueprints.
|
||||
$messages += $this->validateArray($child, $val, $strict);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Data
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -22,8 +22,6 @@ use function is_object;
|
||||
*/
|
||||
class Blueprints
|
||||
{
|
||||
/** @var array|string */
|
||||
protected $search;
|
||||
/** @var array */
|
||||
protected $types;
|
||||
/** @var array */
|
||||
@@ -32,9 +30,8 @@ class Blueprints
|
||||
/**
|
||||
* @param string|array $search Search path.
|
||||
*/
|
||||
public function __construct($search = 'blueprints://')
|
||||
public function __construct(protected $search = 'blueprints://')
|
||||
{
|
||||
$this->search = $search;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Data
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -103,7 +103,7 @@ class Data implements DataInterface, ArrayAccess, \Countable, JsonSerializable,
|
||||
* @return $this
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function join($name, $value, $separator = '.')
|
||||
public function join($name, mixed $value, $separator = '.')
|
||||
{
|
||||
$old = $this->get($name, null, $separator);
|
||||
if ($old !== null) {
|
||||
@@ -145,7 +145,7 @@ class Data implements DataInterface, ArrayAccess, \Countable, JsonSerializable,
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return $this
|
||||
*/
|
||||
public function joinDefaults($name, $value, $separator = '.')
|
||||
public function joinDefaults($name, mixed $value, $separator = '.')
|
||||
{
|
||||
if (is_object($value)) {
|
||||
$value = (array) $value;
|
||||
@@ -323,7 +323,7 @@ class Data implements DataInterface, ArrayAccess, \Countable, JsonSerializable,
|
||||
* @param FileInterface|null $storage Optionally enter a new storage.
|
||||
* @return FileInterface|null
|
||||
*/
|
||||
public function file(FileInterface $storage = null)
|
||||
public function file(?FileInterface $storage = null)
|
||||
{
|
||||
if ($storage) {
|
||||
$this->storage = $storage;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Data
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -28,7 +28,7 @@ interface DataInterface
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return mixed Value.
|
||||
*/
|
||||
public function value($name, $default = null, $separator = '.');
|
||||
public function value($name, mixed $default = null, $separator = '.');
|
||||
|
||||
/**
|
||||
* Merge external data.
|
||||
@@ -80,5 +80,5 @@ interface DataInterface
|
||||
* @param FileInterface|null $storage Optionally enter a new storage.
|
||||
* @return FileInterface
|
||||
*/
|
||||
public function file(FileInterface $storage = null);
|
||||
public function file(?FileInterface $storage = null);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Data
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -37,23 +37,24 @@ class Validation
|
||||
/**
|
||||
* Validate value against a blueprint field definition.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param array $field
|
||||
* @return array
|
||||
*/
|
||||
public static function validate($value, array $field)
|
||||
public static function validate(mixed $value, array $field)
|
||||
{
|
||||
if (!isset($field['type'])) {
|
||||
$field['type'] = 'text';
|
||||
}
|
||||
|
||||
$validate = (array)($field['validate'] ?? null);
|
||||
$type = $validate['type'] ?? $field['type'];
|
||||
$validate_type = $validate['type'] ?? null;
|
||||
$required = $validate['required'] ?? false;
|
||||
$type = $validate_type ?? $field['type'];
|
||||
|
||||
$required = $required && ($validate_type !== 'ignore');
|
||||
|
||||
// If value isn't required, we will stop validation if empty value is given.
|
||||
if ($required !== true && ($value === null || $value === '' || (($field['type'] === 'checkbox' || $field['type'] === 'switch') && $value == false))
|
||||
) {
|
||||
if ($required !== true && ($value === null || $value === '' || empty($value) || (($field['type'] === 'checkbox' || $field['type'] === 'switch') && $value == false))) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -76,7 +77,7 @@ class Validation
|
||||
|
||||
$messages = [];
|
||||
|
||||
$success = method_exists(__CLASS__, $method) ? self::$method($value, $validate, $field) : true;
|
||||
$success = method_exists(self::class, $method) ? self::$method($value, $validate, $field) : true;
|
||||
if (!$success) {
|
||||
$messages[$field['name']][] = $message;
|
||||
}
|
||||
@@ -85,7 +86,7 @@ class Validation
|
||||
foreach ($validate as $rule => $params) {
|
||||
$method = 'validate' . ucfirst(str_replace('-', '_', $rule));
|
||||
|
||||
if (method_exists(__CLASS__, $method)) {
|
||||
if (method_exists(self::class, $method)) {
|
||||
$success = self::$method($value, $params);
|
||||
|
||||
if (!$success) {
|
||||
@@ -98,11 +99,10 @@ class Validation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $field
|
||||
* @return array
|
||||
*/
|
||||
public static function checkSafety($value, array $field)
|
||||
public static function checkSafety(mixed $value, array $field)
|
||||
{
|
||||
$messages = [];
|
||||
|
||||
@@ -115,7 +115,7 @@ class Validation
|
||||
$options = [];
|
||||
}
|
||||
|
||||
$name = ucfirst($field['label'] ?? $field['name'] ?? 'UNKNOWN');
|
||||
$name = ucfirst((string) ($field['label'] ?? $field['name'] ?? 'UNKNOWN'));
|
||||
|
||||
/** @var UserInterface $user */
|
||||
$user = Grav::instance()['user'] ?? null;
|
||||
@@ -162,7 +162,7 @@ class Validation
|
||||
* @param UserInterface|null $user
|
||||
* @return bool
|
||||
*/
|
||||
public static function authorize($action, UserInterface $user = null)
|
||||
public static function authorize($action, ?UserInterface $user = null)
|
||||
{
|
||||
if (!$user) {
|
||||
return false;
|
||||
@@ -186,11 +186,10 @@ class Validation
|
||||
/**
|
||||
* Filter value against a blueprint field definition.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param array $field
|
||||
* @return mixed Filtered value.
|
||||
*/
|
||||
public static function filter($value, array $field)
|
||||
public static function filter(mixed $value, array $field)
|
||||
{
|
||||
$validate = (array)($field['filter'] ?? $field['validate'] ?? null);
|
||||
|
||||
@@ -211,7 +210,7 @@ class Validation
|
||||
$method = 'filterYaml';
|
||||
}
|
||||
|
||||
if (!method_exists(__CLASS__, $method)) {
|
||||
if (!method_exists(self::class, $method)) {
|
||||
$method = isset($field['array']) && $field['array'] === true ? 'filterArray' : 'filterText';
|
||||
}
|
||||
|
||||
@@ -226,7 +225,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeText($value, array $params, array $field)
|
||||
public static function typeText(mixed $value, array $params, array $field)
|
||||
{
|
||||
if (!is_string($value) && !is_numeric($value)) {
|
||||
return false;
|
||||
@@ -239,7 +238,7 @@ class Validation
|
||||
}
|
||||
|
||||
$value = preg_replace("/\r\n|\r/um", "\n", $value);
|
||||
$len = mb_strlen($value);
|
||||
$len = mb_strlen((string) $value);
|
||||
|
||||
$min = (int)($params['min'] ?? 0);
|
||||
if ($min && $len < $min) {
|
||||
@@ -258,7 +257,7 @@ class Validation
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$multiline && preg_match('/\R/um', $value)) {
|
||||
if (!$multiline && preg_match('/\R/um', (string) $value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -266,12 +265,11 @@ class Validation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return string
|
||||
*/
|
||||
protected static function filterText($value, array $params, array $field)
|
||||
protected static function filterText(mixed $value, array $params, array $field)
|
||||
{
|
||||
if (!is_string($value) && !is_numeric($value)) {
|
||||
return '';
|
||||
@@ -287,12 +285,11 @@ class Validation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return string|null
|
||||
*/
|
||||
protected static function filterCheckbox($value, array $params, array $field)
|
||||
protected static function filterCheckbox(mixed $value, array $params, array $field)
|
||||
{
|
||||
$value = (string)$value;
|
||||
$field_value = (string)($field['value'] ?? '1');
|
||||
@@ -301,23 +298,21 @@ class Validation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return array|array[]|false|string[]
|
||||
*/
|
||||
protected static function filterCommaList($value, array $params, array $field)
|
||||
protected static function filterCommaList(mixed $value, array $params, array $field)
|
||||
{
|
||||
return is_array($value) ? $value : preg_split('/\s*,\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
|
||||
return is_array($value) ? $value : preg_split('/\s*,\s*/', (string) $value, -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return bool
|
||||
*/
|
||||
public static function typeCommaList($value, array $params, array $field)
|
||||
public static function typeCommaList(mixed $value, array $params, array $field)
|
||||
{
|
||||
if (!isset($params['max'])) {
|
||||
$params['max'] = 2048;
|
||||
@@ -327,34 +322,31 @@ class Validation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return array|array[]|false|string[]
|
||||
*/
|
||||
protected static function filterLines($value, array $params, array $field)
|
||||
protected static function filterLines(mixed $value, array $params, array $field)
|
||||
{
|
||||
return is_array($value) ? $value : preg_split('/\s*[\r\n]+\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
|
||||
return is_array($value) ? $value : preg_split('/\s*[\r\n]+\s*/', (string) $value, -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @return string
|
||||
*/
|
||||
protected static function filterLower($value, array $params)
|
||||
protected static function filterLower(mixed $value, array $params)
|
||||
{
|
||||
return mb_strtolower($value);
|
||||
return mb_strtolower((string) $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @return string
|
||||
*/
|
||||
protected static function filterUpper($value, array $params)
|
||||
protected static function filterUpper(mixed $value, array $params)
|
||||
{
|
||||
return mb_strtoupper($value);
|
||||
return mb_strtoupper((string) $value);
|
||||
}
|
||||
|
||||
|
||||
@@ -366,7 +358,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeTextarea($value, array $params, array $field)
|
||||
public static function typeTextarea(mixed $value, array $params, array $field)
|
||||
{
|
||||
if (!isset($params['multiline'])) {
|
||||
$params['multiline'] = true;
|
||||
@@ -383,7 +375,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typePassword($value, array $params, array $field)
|
||||
public static function typePassword(mixed $value, array $params, array $field)
|
||||
{
|
||||
if (!isset($params['max'])) {
|
||||
$params['max'] = 256;
|
||||
@@ -400,7 +392,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeHidden($value, array $params, array $field)
|
||||
public static function typeHidden(mixed $value, array $params, array $field)
|
||||
{
|
||||
return self::typeText($value, $params, $field);
|
||||
}
|
||||
@@ -413,7 +405,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeCheckboxes($value, array $params, array $field)
|
||||
public static function typeCheckboxes(mixed $value, array $params, array $field)
|
||||
{
|
||||
// Set multiple: true so checkboxes can easily use min/max counts to control number of options required
|
||||
$field['multiple'] = true;
|
||||
@@ -422,12 +414,11 @@ class Validation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return array|null
|
||||
*/
|
||||
protected static function filterCheckboxes($value, array $params, array $field)
|
||||
protected static function filterCheckboxes(mixed $value, array $params, array $field)
|
||||
{
|
||||
return self::filterArray($value, $params, $field);
|
||||
}
|
||||
@@ -440,7 +431,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeCheckbox($value, array $params, array $field)
|
||||
public static function typeCheckbox(mixed $value, array $params, array $field)
|
||||
{
|
||||
$value = (string)$value;
|
||||
$field_value = (string)($field['value'] ?? '1');
|
||||
@@ -456,7 +447,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeRadio($value, array $params, array $field)
|
||||
public static function typeRadio(mixed $value, array $params, array $field)
|
||||
{
|
||||
return self::typeArray((array) $value, $params, $field);
|
||||
}
|
||||
@@ -469,7 +460,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeToggle($value, array $params, array $field)
|
||||
public static function typeToggle(mixed $value, array $params, array $field)
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
$value = (int)$value;
|
||||
@@ -486,18 +477,17 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeFile($value, array $params, array $field)
|
||||
public static function typeFile(mixed $value, array $params, array $field)
|
||||
{
|
||||
return self::typeArray((array)$value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return array
|
||||
*/
|
||||
protected static function filterFile($value, array $params, array $field)
|
||||
protected static function filterFile(mixed $value, array $params, array $field)
|
||||
{
|
||||
return (array)$value;
|
||||
}
|
||||
@@ -510,7 +500,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeSelect($value, array $params, array $field)
|
||||
public static function typeSelect(mixed $value, array $params, array $field)
|
||||
{
|
||||
return self::typeArray((array) $value, $params, $field);
|
||||
}
|
||||
@@ -523,7 +513,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeNumber($value, array $params, array $field)
|
||||
public static function typeNumber(mixed $value, array $params, array $field)
|
||||
{
|
||||
if (!is_numeric($value)) {
|
||||
return false;
|
||||
@@ -550,7 +540,7 @@ class Validation
|
||||
$step = (float)$params['step'];
|
||||
// Count of how many steps we are above/below the minimum value.
|
||||
$pos = ($value - $min) / $step;
|
||||
|
||||
$pos = round($pos, 10);
|
||||
return is_int(static::filterNumber($pos, $params, $field));
|
||||
}
|
||||
|
||||
@@ -558,23 +548,21 @@ class Validation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return float|int
|
||||
*/
|
||||
protected static function filterNumber($value, array $params, array $field)
|
||||
protected static function filterNumber(mixed $value, array $params, array $field)
|
||||
{
|
||||
return (string)(int)$value !== (string)(float)$value ? (float)$value : (int)$value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return string
|
||||
*/
|
||||
protected static function filterDateTime($value, array $params, array $field)
|
||||
protected static function filterDateTime(mixed $value, array $params, array $field)
|
||||
{
|
||||
$format = Grav::instance()['config']->get('system.pages.dateformat.default');
|
||||
if ($format) {
|
||||
@@ -592,18 +580,17 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeRange($value, array $params, array $field)
|
||||
public static function typeRange(mixed $value, array $params, array $field)
|
||||
{
|
||||
return self::typeNumber($value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return float|int
|
||||
*/
|
||||
protected static function filterRange($value, array $params, array $field)
|
||||
protected static function filterRange(mixed $value, array $params, array $field)
|
||||
{
|
||||
return self::filterNumber($value, $params, $field);
|
||||
}
|
||||
@@ -616,9 +603,9 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeColor($value, array $params, array $field)
|
||||
public static function typeColor(mixed $value, array $params, array $field)
|
||||
{
|
||||
return (bool)preg_match('/^\#[0-9a-fA-F]{3}[0-9a-fA-F]{3}?$/u', $value);
|
||||
return (bool)preg_match('/^\#[0-9a-fA-F]{3}[0-9a-fA-F]{3}?$/u', (string) $value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -629,16 +616,20 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeEmail($value, array $params, array $field)
|
||||
public static function typeEmail(mixed $value, array $params, array $field)
|
||||
{
|
||||
if (empty($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isset($params['max'])) {
|
||||
$params['max'] = 320;
|
||||
}
|
||||
|
||||
$values = !is_array($value) ? explode(',', preg_replace('/\s+/', '', $value)) : $value;
|
||||
$values = !is_array($value) ? explode(',', (string) preg_replace('/\s+/', '', (string) $value)) : $value;
|
||||
|
||||
foreach ($values as $val) {
|
||||
if (!(self::typeText($val, $params, $field) && filter_var($val, FILTER_VALIDATE_EMAIL))) {
|
||||
if (!(self::typeText($val, $params, $field) && strpos((string) $val, '@', 1))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -654,7 +645,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeUrl($value, array $params, array $field)
|
||||
public static function typeUrl(mixed $value, array $params, array $field)
|
||||
{
|
||||
if (!isset($params['max'])) {
|
||||
$params['max'] = 2048;
|
||||
@@ -671,7 +662,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeDatetime($value, array $params, array $field)
|
||||
public static function typeDatetime(mixed $value, array $params, array $field)
|
||||
{
|
||||
if ($value instanceof DateTime) {
|
||||
return true;
|
||||
@@ -696,7 +687,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeDatetimeLocal($value, array $params, array $field)
|
||||
public static function typeDatetimeLocal(mixed $value, array $params, array $field)
|
||||
{
|
||||
return self::typeDatetime($value, $params, $field);
|
||||
}
|
||||
@@ -709,7 +700,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeDate($value, array $params, array $field)
|
||||
public static function typeDate(mixed $value, array $params, array $field)
|
||||
{
|
||||
if (!isset($params['format'])) {
|
||||
$params['format'] = 'Y-m-d';
|
||||
@@ -726,7 +717,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeTime($value, array $params, array $field)
|
||||
public static function typeTime(mixed $value, array $params, array $field)
|
||||
{
|
||||
if (!isset($params['format'])) {
|
||||
$params['format'] = 'H:i';
|
||||
@@ -743,7 +734,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeMonth($value, array $params, array $field)
|
||||
public static function typeMonth(mixed $value, array $params, array $field)
|
||||
{
|
||||
if (!isset($params['format'])) {
|
||||
$params['format'] = 'Y-m';
|
||||
@@ -760,9 +751,9 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeWeek($value, array $params, array $field)
|
||||
public static function typeWeek(mixed $value, array $params, array $field)
|
||||
{
|
||||
if (!isset($params['format']) && !preg_match('/^\d{4}-W\d{2}$/u', $value)) {
|
||||
if (!isset($params['format']) && !preg_match('/^\d{4}-W\d{2}$/u', (string) $value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -777,7 +768,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeArray($value, array $params, array $field)
|
||||
public static function typeArray(mixed $value, array $params, array $field)
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
return false;
|
||||
@@ -825,12 +816,11 @@ class Validation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return array|null
|
||||
*/
|
||||
protected static function filterFlatten_array($value, $params, $field)
|
||||
protected static function filterFlatten_array(mixed $value, $params, $field)
|
||||
{
|
||||
$value = static::filterArray($value, $params, $field);
|
||||
|
||||
@@ -838,12 +828,11 @@ class Validation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return array|null
|
||||
*/
|
||||
protected static function filterArray($value, $params, $field)
|
||||
protected static function filterArray(mixed $value, $params, $field)
|
||||
{
|
||||
$values = (array) $value;
|
||||
$options = isset($field['options']) ? array_keys($field['options']) : [];
|
||||
@@ -867,7 +856,7 @@ class Validation
|
||||
$val = implode(',', $val);
|
||||
$values[$key] = array_map('trim', explode(',', $val));
|
||||
} else {
|
||||
$values[$key] = trim($val);
|
||||
$values[$key] = trim((string) $val);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -891,16 +880,11 @@ class Validation
|
||||
{
|
||||
foreach ($values as $key => &$val) {
|
||||
if ($params['key_type']) {
|
||||
switch ($params['key_type']) {
|
||||
case 'int':
|
||||
$result = is_int($key);
|
||||
break;
|
||||
case 'string':
|
||||
$result = is_string($key);
|
||||
break;
|
||||
default:
|
||||
$result = false;
|
||||
}
|
||||
$result = match ($params['key_type']) {
|
||||
'int' => is_int($key),
|
||||
'string' => is_string($key),
|
||||
default => false,
|
||||
};
|
||||
if (!$result) {
|
||||
unset($values[$key]);
|
||||
}
|
||||
@@ -933,7 +917,7 @@ class Validation
|
||||
$val = (string)$val;
|
||||
break;
|
||||
case 'trim':
|
||||
$val = trim($val);
|
||||
$val = trim((string) $val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -948,12 +932,11 @@ class Validation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return bool
|
||||
*/
|
||||
public static function typeList($value, array $params, array $field)
|
||||
public static function typeList(mixed $value, array $params, array $field)
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
return false;
|
||||
@@ -962,7 +945,7 @@ class Validation
|
||||
if (isset($field['fields'])) {
|
||||
foreach ($value as $key => $item) {
|
||||
foreach ($field['fields'] as $subKey => $subField) {
|
||||
$subKey = trim($subKey, '.');
|
||||
$subKey = trim((string) $subKey, '.');
|
||||
$subValue = $item[$subKey] ?? null;
|
||||
self::validate($subValue, $subField);
|
||||
}
|
||||
@@ -973,22 +956,20 @@ class Validation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return array
|
||||
*/
|
||||
protected static function filterList($value, array $params, array $field)
|
||||
protected static function filterList(mixed $value, array $params, array $field)
|
||||
{
|
||||
return (array) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public static function filterYaml($value, $params)
|
||||
public static function filterYaml(mixed $value, $params)
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
return $value;
|
||||
@@ -1005,18 +986,17 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeIgnore($value, array $params, array $field)
|
||||
public static function typeIgnore(mixed $value, array $params, array $field)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return mixed
|
||||
*/
|
||||
public static function filterIgnore($value, array $params, array $field)
|
||||
public static function filterIgnore(mixed $value, array $params, array $field)
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
@@ -1029,30 +1009,27 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeUnset($value, array $params, array $field)
|
||||
public static function typeUnset(mixed $value, array $params, array $field)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return null
|
||||
*/
|
||||
public static function filterUnset($value, array $params, array $field)
|
||||
public static function filterUnset(mixed $value, array $params, array $field)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// HTML5 attributes (min, max and range are handled inside the types)
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param bool $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateRequired($value, $params)
|
||||
public static function validateRequired(mixed $value, $params)
|
||||
{
|
||||
if (is_scalar($value)) {
|
||||
return (bool) $params !== true || $value !== '';
|
||||
@@ -1062,105 +1039,85 @@ class Validation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param string $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validatePattern($value, $params)
|
||||
public static function validatePattern(mixed $value, $params)
|
||||
{
|
||||
return (bool) preg_match("`^{$params}$`u", $value);
|
||||
return (bool) preg_match("`^{$params}$`u", (string) $value);
|
||||
}
|
||||
|
||||
// Internal types
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateAlpha($value, $params)
|
||||
public static function validateAlpha(mixed $value, mixed $params)
|
||||
{
|
||||
return ctype_alpha($value);
|
||||
return ctype_alpha((string) $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateAlnum($value, $params)
|
||||
public static function validateAlnum(mixed $value, mixed $params)
|
||||
{
|
||||
return ctype_alnum($value);
|
||||
return ctype_alnum((string) $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function typeBool($value, $params)
|
||||
public static function typeBool(mixed $value, mixed $params)
|
||||
{
|
||||
return is_bool($value) || $value == 1 || $value == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateBool($value, $params)
|
||||
public static function validateBool(mixed $value, mixed $params)
|
||||
{
|
||||
return is_bool($value) || $value == 1 || $value == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
protected static function filterBool($value, $params)
|
||||
protected static function filterBool(mixed $value, mixed $params)
|
||||
{
|
||||
return (bool) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateDigit($value, $params)
|
||||
public static function validateDigit(mixed $value, mixed $params)
|
||||
{
|
||||
return ctype_digit($value);
|
||||
return ctype_digit((string) $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateFloat($value, $params)
|
||||
public static function validateFloat(mixed $value, mixed $params)
|
||||
{
|
||||
return is_float(filter_var($value, FILTER_VALIDATE_FLOAT));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return float
|
||||
*/
|
||||
protected static function filterFloat($value, $params)
|
||||
protected static function filterFloat(mixed $value, mixed $params)
|
||||
{
|
||||
return (float) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateHex($value, $params)
|
||||
public static function validateHex(mixed $value, mixed $params)
|
||||
{
|
||||
return ctype_xdigit($value);
|
||||
return ctype_xdigit((string) $value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1171,7 +1128,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeInt($value, array $params, array $field)
|
||||
public static function typeInt(mixed $value, array $params, array $field)
|
||||
{
|
||||
$params['step'] = max(1, (int)($params['step'] ?? 0));
|
||||
|
||||
@@ -1179,54 +1136,42 @@ class Validation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateInt($value, $params)
|
||||
public static function validateInt(mixed $value, mixed $params)
|
||||
{
|
||||
return is_numeric($value) && (int)$value == $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return int
|
||||
*/
|
||||
protected static function filterInt($value, $params)
|
||||
protected static function filterInt(mixed $value, mixed $params)
|
||||
{
|
||||
return (int)$value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateArray($value, $params)
|
||||
public static function validateArray(mixed $value, mixed $params)
|
||||
{
|
||||
return is_array($value) || ($value instanceof ArrayAccess && $value instanceof Traversable && $value instanceof Countable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return array
|
||||
*/
|
||||
public static function filterItem_List($value, $params)
|
||||
public static function filterItem_List(mixed $value, mixed $params)
|
||||
{
|
||||
return array_values(array_filter($value, static function ($v) {
|
||||
return !empty($v);
|
||||
}));
|
||||
return array_values(array_filter($value, static fn($v) => !empty($v)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateJson($value, $params)
|
||||
public static function validateJson(mixed $value, mixed $params)
|
||||
{
|
||||
return (bool) (@json_decode($value));
|
||||
return (bool) (@json_decode((string) $value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Data
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -37,7 +37,7 @@ class ValidationException extends RuntimeException implements JsonSerializable
|
||||
foreach ($messages as $list) {
|
||||
$list = array_unique($list);
|
||||
foreach ($list as $message) {
|
||||
$this->message .= '<br/>' . htmlspecialchars($message, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
$this->message .= '<br/>' . htmlspecialchars((string) $message, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class ValidationException extends RuntimeException implements JsonSerializable
|
||||
$first = reset($this->messages);
|
||||
$message = reset($first);
|
||||
|
||||
$this->message = $escape ? htmlspecialchars($message, ENT_QUOTES | ENT_HTML5, 'UTF-8') : $message;
|
||||
$this->message = $escape ? htmlspecialchars((string) $message, ENT_QUOTES | ENT_HTML5, 'UTF-8') : $message;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user