Compare commits

...

309 Commits

Author SHA1 Message Date
Andy Miller
7e3fccce54 Merge branch 'release/1.7.50.9' 2025-11-09 15:55:30 +00:00
Andy Miller
48c6d2eb93 prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-09 15:55:20 +00:00
Andy Miller
e86820d438 update changelog
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-09 15:54:44 +00:00
Andy Miller
4c324ef4b8 don’t error when trying to force —safe
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-09 15:50:42 +00:00
Andy Miller
a07a1b332a test fixes
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-09 13:11:41 +00:00
Andy Miller
c8204f442a major/minor upgrade warnings
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-09 12:52:04 +00:00
Andy Miller
ba479007ac less confusing messages
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-09 12:07:46 +00:00
Andy Miller
38494b2c1c fall back to safe upgrade
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-08 19:54:20 +00:00
Andy Miller
ba3e0686a6 revert testing repo
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-08 12:17:49 +00:00
Andy Miller
f0ed8e0ea0 some more fixes
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-08 11:43:50 +00:00
Andy Miller
02fbe27efd fix some errors after upgrade
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-08 11:08:45 +00:00
Andy Miller
cfa18a8fd1 mostly working
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-08 11:03:50 +00:00
Andy Miller
89f44631bd Merge branch 'release/1.7.50.8' 2025-11-06 18:56:04 +00:00
Andy Miller
2f2f1e518d Merge tag '1.7.50.8' into develop
Release v1.7.50.8

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmkM70QACgkQnyzziuvb
# CuCj0Q/7Bscki0vFiumTFx7GUg/MQ+N29qbAsEoDe/VDoaFn73P/kqioNN6EPMBm
# 3rqo9J679nQWWCfEfXw2KdvzxiP1aHf+KFnQUD4M5sw/wYdJ/SBH5R+CI9/7NNgM
# YC+ZDqf32d9fr/iGH7fdGwzxbt+GWJD+turD/40TMuZUT49LY5gymXE4X8Q7f2qS
# 6aCHLfjo+pZWkcOH34j0jRFUUM20+Tbzyea3Y7cSRVSf/DnkGIhBAhcsHYTc5dez
# b1NLLBLPuYCcOBFxWHDBTaOiYzV8ZlxuKbWOop1y6Ak2Q5fzGhrewAbhGtT6RXOj
# 90BTjeN1M0A9VOGCjdlhoK+zLzjy2BDmEh/qGeXwGg8GXe7eOxeZok4/DQisl3Uz
# nVUj/VZk9czv2QdmsC3D4jPCOSoqzM9lsJ/HSoSAqfxlQf8Li/YwLYYHw2vold3P
# xqv/KO/uoLV30oR3Q/btvZN+//cIFb6wAA9MoASQ0FjRMe4vHQMcRQFyMX3lnHDV
# MTFSJwmEmSwZCQkr+L9Y4QdX0+6YIgJv0WO9sIj29V9VLLD2VGkDWXdBE1ntx54j
# ozCBrTBBydpDlcEgfprecBjObfA+jc80EGg5YFYLNOT8PJQ3/wRMelYZ8DM9/w8D
# nr9pM4l9/CIFG/Qt7JU5VlrNYsvKEBTx3niy6H8bk8x2sLnju1s=
# =qREv
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu Nov  6 18:56:04 2025 GMT
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2025-11-06 18:56:04 +00:00
Andy Miller
682109bf3b prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-06 18:55:56 +00:00
Andy Miller
f420db0eea has some legit uses - this is actually causing problems
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-06 18:36:57 +00:00
Andy Miller
c6764f9815 removed check causing false positives
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-06 18:36:57 +00:00
Andy Miller
a2f944e6c7 Merge branch 'release/1.7.50.7' 2025-11-05 23:39:36 +00:00
Andy Miller
68ff6ae342 Merge tag '1.7.50.7' into develop
Release 1.7.50.7

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmkL4DgACgkQnyzziuvb
# CuCoSw//UiX7orbI5Qcel8ZUELHJJDhyq2AGmfg58gvyaUC/VqKY4HdAOGesDNgr
# Qo/FsJ56wKgizXlv1G4k955MZas6oN6IkGTPDl0xjnvYq3Nuxoe3m5zYRAmitgk6
# j8BA2aTdK4erGuxDa9WKJMLOGaen4OrGKIO9WfpuKKs8sP80Z69uPXFnVZIqWPx2
# 5N0yUfo0nnBsigumFBrMbsxLxi56iMRIxpJQ6p7iRRSoXdridO+LVlHlj8l6oWrN
# B1AUYbltUUnX5yJwyoxyktT9kOJ7FEY5OnUw+Cg0hthS07TukymmrrZM+tDjM2Lc
# gKTTH8tQRwao3PFyvoVnMB2Ox+hkqoZMzsivsccQxVNle1wdydvrZ1pwGWh0ynlg
# S42hFEVnpE4o3mbAt2NzCerx5vdjkrUSMBITG6bol2jmyglZx3fy0lNLwMUo9WGV
# djg+yzk8MW/qtTwhGcBr4nfD2bTM9LhSjHHTjanqiWPf+nM19rmhoWQsRI477br4
# Do7XCfvk6/GN6aOfgoJE1DumwMQO+lm23HxzwZ888i3N5mqrk8JixPRt1a1HbMLX
# anoXSlAtqwy4m2Ql1KsTmM8CgLU+ZDsp2uTsTQ9gTpfgoYBTrrZN7v/9Yb2YT9rf
# 1769MwoNQq1Wumnpl7SNv29kcMeq2XZNP1AU0LvtML+vBGMigMA=
# =VlDG
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed Nov  5 23:39:36 2025 GMT
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2025-11-05 23:39:36 +00:00
Andy Miller
505fc208bb prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-05 23:39:25 +00:00
Andy Miller
cd50bd6d63 update clean commant
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-05 23:38:31 +00:00
Andy Miller
0278eb17cb Merge branch 'release/1.7.50.7' 2025-11-05 23:25:26 +00:00
Andy Miller
14fba5170e Merge tag '1.7.50.7' into develop
Release 1.7.50.7

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmkL3OYACgkQnyzziuvb
# CuBXag/+Lo9xqsP8XsArdYjyjxAaKFOJikXKEtEwwyYpPgfPFLvX29TJHitqRsJ0
# /Dhd+gSWDzQ1jectvl1Acbffelb3PE95SWLB7pnc/TKlFGLd2JB6p36hb9jO+Moo
# MB9h3X2jTnf0GzxRCBa8Ih+sTvE2ski1DDscavaQ4yyEqpJrkW6hyat6io4ZAAQT
# Th68wgcSBoYRTgt2zbj+W8lOU9sS46rF3sFZn5ysfAl+bH8s+dFSZZMIi4NyhCcB
# O+C3DxQbIvwHO+LT5OPiDHI+2JhSeW/QM5eTD5fqATBSyBjYH55LuOHc8iWOHEyw
# 6oA89Egxxd+EKhwblIOCNvQa0ZOiABefup9oYp+60K/WDtV6lmEFRIttFflyOuwT
# P038XnvitQp1aKL664G4WqjNrOYgSaQvRPUQkDyD9lQQbZ5g7AwsgYztt9d9yNQO
# dXOhwyScK9yMU0roKNYEvknzHCkd8OwXXa4o54xwElRHid/T0FKNPpeEz7LVNLeY
# UKKzWt7AaXTkde/A0JM+OSD+bfRHShGc802dgNaRXuyaCVHPmNtbzluCAJ/YrDh8
# Vby/nBvZVBYLFN2A4pJ/URGb1JN3TpFW8KfMBdfY/PVF/T1kHEDWtOlh2smX/qG2
# YcxsZEmqvo7JwA9wBKgUyhgamE1K/SUSOrv8RgZPNlLD5n1CF30=
# =GlG4
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed Nov  5 23:25:26 2025 GMT
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2025-11-05 23:25:26 +00:00
Andy Miller
5af47f0634 prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-05 23:25:15 +00:00
Andy Miller
6263a34c09 update changelog
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-05 23:24:46 +00:00
Andy Miller
5a6c00f68c ignore .github and .phan folders, fixed path check
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-05 23:23:18 +00:00
Andy Miller
52a8854f6b Merge branch 'release/1.7.50.6' 2025-11-05 21:27:00 +00:00
Andy Miller
945cd6aa8f Merge tag '1.7.50.6' into develop
Release v1.7.50.6

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmkLwSQACgkQnyzziuvb
# CuBW1BAAsRPsRLDq2LOVC8pUcHlzJFV+0Yiz3752xchS/4nubg7xO4cjvdkvqtoL
# MOu/s7/DpbEQYP5nGjEQVmo1pug1YnybPprlTGcoT1Swfw0YIrHPnO8WmpPaPYKp
# zKUhkD+n6FNVKA+GhvjXcK7JsB1EbtIC4348uTNKen8zzSSs0EV+k3GhQ0CMxAft
# XgnIHddn4evlJ9MaV1JswJNZs2+BextRd3M4zEdDbvTfnsAZZsb7MOrQSfgl8VbI
# QLAA6ZAQBWlDUkHTMWa2n1ocZAbm12kl8OGfR1hgPjodCQ1eEwE6HQK1Cg4W3M2d
# 6EGI8Iteb5yMTGMa1CN5M9YfPFDM83ngTMYHdAJrztfOD3Bctibg+14oHsdYB5c8
# LbPTxlVIc/9JVyS+cfKtNtCtWLFxzlEs3EMvBaBbFIQze6GxTsw9Dq6GQwqA8CA6
# hCKl4stQwTCCv+UMPB1eEbljAYM5TmVzCndTSTTw6urIyb6to/ITPcofbFzzfwgn
# tFJxTG+p3OFLd1lOp4rxb2UWqNTq1T/TEPtxZD1zijgX+0N4kdgVTY8U1p/GNduF
# m4tXlGzBX+mHPIwkJIGZoJJwH9TmWm/tviRgXNDIM2bHTzY9NmYGnQC7zbv3LzE1
# pGbc2Z11P3faC0xPFlY2nFOBKv+n6hPEGBifPWjJwKiB+s4V35k=
# =/gqY
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed Nov  5 21:27:00 2025 GMT
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2025-11-05 21:27:00 +00:00
Andy Miller
070b53180d prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-05 21:26:47 +00:00
Andy Miller
042b845b8d don’t copy non-upgrade root folders
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-05 21:23:23 +00:00
Andy Miller
84b3a9e68e Merge branch 'release/1.7.50.5' 2025-11-05 18:39:40 +00:00
Andy Miller
70a2e668ec Merge tag '1.7.50.5' into develop
Release v1.7.50.5

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmkLmewACgkQnyzziuvb
# CuC3sBAAqBgVbWmZbwB7GsTYphx3teLPIvEz+GG80LL4w97oGnoxi7IhoRKDUqPd
# QAOMV81J7+R8DLS7+WJyGwZtdjc5rNVicyJgH4+A8PLXJwxt5Dsw/uE6DfOWtfTp
# oNBznCgi1ocva5VFUg1ypPAevyfb2pM2XrwWNjPDcwpXFUZhB8SwA6aGQfwQMTHq
# CdAyEw6GNqzA4LN0Ko8jEipquWrqeQterT0UkGy4VNAs222D03vhhU3G5SQ7qDt6
# ExoNdAjhMq5lRhAJavFsO1vb67OaNCoiYk0XN0t8xM8GqBqDu6+Kliecmq2G6i8T
# KlL2W2vM3C69ScsR/vDqa4ITnDxyVUbXVdPhS8zQPymFG/E641O7vaVparPs75bt
# wvTZEcqgt4w2KCHwLkVqtw5khduUT10tBfogfuCRj4FpPMgbwOUMhpjgyRYNMx/K
# PaeZT0GJTI3m+p8oXb+Dph0zAGrqwJZlfxLeLXd9lGMZdvdM5yZob356ppaYKR9H
# tUwGkuolBNxJ40EuqHIptAPvztexPuatD/cu/GPqqBl6zAQByBA7beFEEdKKrvaX
# XGrHMl64wRmE1sxf6sAuFQUnpDJ546cuIry49lNtM48wY08ulpatcq5BkxKb6V4/
# w5LjCMzAdlNiE5Ztrq3taoSHcVGOjqIuOQjtgYvnrX3+yryKHOA=
# =Tb9i
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed Nov  5 18:39:40 2025 GMT
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2025-11-05 18:39:40 +00:00
Andy Miller
e04391484e prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-05 18:39:30 +00:00
Andy Miller
6d72867bef more safeupgrade fixes
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-05 18:30:42 +00:00
Andy Miller
7e8c0e3f6f selfupgrade fix
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-04 14:13:03 +00:00
Andy Miller
4adc7672ac add preflight command and —safe and —legacy for self-upgrade
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-04 14:08:25 +00:00
Andy Miller
dd89d7e25b improved js assets pipline handling to support defer
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-11-03 23:49:20 +00:00
Andy Miller
ce817c1bd1 Merge branch 'release/1.7.50.4' 2025-10-31 19:12:03 +00:00
Andy Miller
918bfc6f2b Merge tag '1.7.50.4' into develop
Release v1.7.50.4

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmkFCgMACgkQnyzziuvb
# CuBcMRAAlWFsBs1sXbLrm6x/nX1gwWWZ/PPYvCp7Hl00cylAa4WnaUxTqcTyobld
# LoXwOZARivW7Vxxj1FTFBFtCAx4v2V2UcIJ0EZCMDBPpV6YIoIryggqghO3IMJaH
# H44bnKahRrVQMdfcXtM0A9RZY3zU4HVXe2loybINAGl86TPMINGdfWEf2ePJlmdO
# lrv/ACuBXiF++lHjBqor6uSltHTIPmq0AVEbd3jnvnGF1isikPi2CN5mECf9pd0p
# 74rU3eNhiHCrkj2eN0NsmOkxaaY4Ri20OMgP6OE+sotNy3O47FlHNn2o7pZ3kt4i
# ejRHbsrcqvEEi35+xsXiz2kNFJ7PyHJIgsTLtjrXvVd7dTsJTJEoG3cXuv1ZsPhg
# w0Nd/eWCwuWBLzrU5+/gaaIlSODAdIzJHjW7g2WxhUcHYaKnxqs13hSzaIGgyBzN
# xXMrXSMNoPaXu7z5UI/nEjWfAxJkcc+nJxHHgXICtpMcHAXlgqPT8ZPy23lCqfVg
# qihPI1zpEWq97R8Gl0SH8NQLtgAoz5TfomTlnXSIVtOu6V7htoFErmVSSzyH73iX
# 2BvxRiSWk4y9Vei6LAVkRWZ5WiCKUTTID3VDI98D8FpgzNZaDnUqEJTOgAlHAfxo
# OSIDOmvp9IkRyHnrluuBrRh97iTxC3N24qMRuwUqeb2QHSy472Q=
# =1FsF
# -----END PGP SIGNATURE-----
# gpg: Signature made Fri Oct 31 19:12:03 2025 GMT
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2025-10-31 19:12:03 +00:00
Andy Miller
0afbce518d prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-31 19:11:50 +00:00
Andy Miller
ff0de91bab more fixes for safe upgrade
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-31 19:08:48 +00:00
Andy Miller
2a18c07a64 Merge branch 'release/1.7.50.3' 2025-10-22 21:16:20 -06:00
Andy Miller
16b0b562fb Merge tag '1.7.50.3' into develop
Release v1.7.50.3

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmj5ngQACgkQnyzziuvb
# CuCJ+Q//cS9O8FMLU3rQ28HLNj7pkWj4R6XEWMX/OezRKQzCRXgAXDUL4t8L1Psm
# o3p/pm3HUmMhcp00Qo3yQdDktvIa1t3/HY7jN0mPBjmv3dPtnHFgDZC+GPymkxWs
# 1KGH8MWVkQxuBC9FqaxrYN1WonPjj7frR21tpJ72yaGtuGCxmRqQOkU/uiOHr6uE
# IlXsC5Bmlg1LYcbmkDR/fNhahfCBZFcB8u/M3lqTzjpVKZCR/OJFXPvFt8FCsjA8
# i6Z1aXIQU4vdFMnHU9U4ksl7Ftd5pfovVY75yKPhY1Uk3WJkOi/+G7sL72O6lRj9
# 8JNXQoqHPe1MiXe/MsbGmSpJGsUo4/6fl79iJTlI69Y6LOfYGG8zh3PJKItev9lp
# CW8eWmsO6oUb4V3KbMfhyK3TQsVntffN4hB9KnCCfcEiRoLaQyJsMBIM1QpFKh5o
# T4Rz2k5SAtaLqK64clUV0uY6CEA/r/potf0w8VFxQgdBH/aXhLvm9kjcFeWdyyps
# hSMef5spzSX2vHlc919A8YIvXwsH5ZaXBR5ENUnSjLdjiz+RWoaWtrgmAH6LTXnD
# q+aVfx8fu1l/z+fDkTKEz64O358bF43jAvkdRZuq8RWuiE8HOHO5vDnp1suKPoXD
# MygVnzf3FUBzZjwv5PnUS+4ypDoMWlbE0ooL1kxd58o5kZ7Tajs=
# =mDao
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed Oct 22 21:16:20 2025 MDT
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2025-10-22 21:16:20 -06:00
Andy Miller
ef48476c88 prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-22 21:16:07 -06:00
Andy Miller
f73df193ad reset system.yaml to default
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-22 21:15:06 -06:00
Andy Miller
8c1e4252f2 update ignore
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-21 13:59:10 -06:00
Andy Miller
de260489ee remove phpstan file
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-21 13:59:01 -06:00
Andy Miller
1c5c2ac08d Merge tag '1.7.50.2' into develop
Release v1.7.50.2

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmj33WQACgkQnyzziuvb
# CuD9iw/9FQH9MTS8wiQnLJWYkFPqzK9fxdIEwawU/p9hYSMd3K0XbdAav3iSsrYV
# 5vBljWRGXgWOyEauBSnajQeL+fFLnpXKZYymsHNu0y7fM76jJ3sSYiTwJ/mtsi9O
# 7UK4mTDkVqlO7Ad+2ujP+B7LrmN25seusxrHzgrG0AG9wS7eCQitXjxuhOLOv671
# VzXfeZJF4Hja+85thv6r2Jco49tXAxafn4n7NC9fv8XypeBrVFryhSwzzahxC36u
# TCIyuusV5VppMYyQWhHpOqNqv5Vl1fusexPAgvS3sJ8XDVBfWv0Sdt9XbsQNhcJ1
# dBdrKOQTsPXL7UrBU2Us+tHnfwklqNkHsKUo/5fwfY9SLKo1Y1wQ7sh61599fiyb
# dUmA78aWy8TrEE7RoL/eUuTo4nCQoB2CaImN2d7kp568VP+xl3uJor5oJ6anQxAQ
# T1MGHg+ViXD4yFiZANjFWB+WpjF8ZSkjIAolZMOhXrAegh+AfYYAnXWx24GwH3oI
# EVNV/l/E5rN0ZY6xT96j0Z2hqb3wVck94h5oIfBtaHeC+gzHTAz/d07HBm2bPhD4
# fnMHS9gpiogSenqrZ+uBVx3i1/ShkfNaMNQEpqubJKpjGT++SY0eXsRxi5DlpHui
# 6lnxz/3j8NqQb9MT9TeVzxrm2v73/WDTUDkp6dUDHxfgCGUw4yw=
# =IG5K
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue Oct 21 13:22:12 2025 MDT
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2025-10-21 13:22:12 -06:00
Andy Miller
841259ca2a Merge branch 'release/1.7.50.2' 2025-10-21 13:22:11 -06:00
Andy Miller
20809f3fea prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-21 13:22:01 -06:00
Andy Miller
1b75df73ef fix for #3966
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-21 13:19:00 -06:00
Andy Miller
a620556e4c Merge branch 'release/1.7.50.1' 2025-10-20 13:38:28 -06:00
Andy Miller
975a2a8dd3 Merge tag '1.7.50.1' into develop
Release 1.7.50.1

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmj2j7QACgkQnyzziuvb
# CuC/Iw//SuM31uHMaVCk0aVKXrLQtTHGwmXJq3A6cE+su0ODSXJyp2T/Jj+ydUa3
# vsDp0rK4Dw7YoO1v5upTc1GY+AlGBEIO+6AFzusN/yTz7ihL1hddua2TtjN0sxUQ
# kz8p5CSxQMz87plC1jtTsWRh18J28/g7z76FoBInDninoB8+EKp1Lm30XUsMsrv0
# hU0+DbUI5GO7C8T7bAWGRh+WGh8RwGiNfbiSaUoeDKyT1kFrnOrDN4OtCvUk/Y52
# 2J8sUtEUlwPqNZxa6UtwRf4nLMKKv+6oJMZCL6Sg6bHlhVfpNwlg5LzRQuS0CaIN
# q+crP145IK038RI9DMyq6cfYX574i12iEzqGlrM4kPfpIxHNtCRJT33/b9+g/64j
# 9RT0PPxHBzqHTjp2WmjMzyZkYmPRBORUuYckmxTIT02Fa+H9Rv0pJQQBXvN1UDsk
# qDbfdOS01IIvOIo1Dyj+5EUZDOa1pWIEqs/1HoMHns9txBXIy7qW7OAIsIwvagm/
# ctPfm2lrZ1nPBcW9oEn+SM2mGHj71K6a+TvaeLAqkgd1vnOfJ5vPyNaqtXOpCdnB
# eUjp/ReYgmf0UAoxHWg2pOQjltmOzPLTU9vaULmONs7B+I0Gwc+dDH/k9FlI5ITd
# xU6AKR1ERsu3Oo26jtzhZp+NId5zFn6wPoMx+YKH5RkRimlcVIw=
# =xZ8q
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon Oct 20 13:38:28 2025 MDT
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2025-10-20 13:38:28 -06:00
Andy Miller
915991ac6a prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-20 13:38:14 -06:00
Andy Miller
3fad2a8173 fix for GRAV_ROOT
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-20 13:34:42 -06:00
Andy Miller
06471eb8cf sync with 1.8 changes
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-20 13:23:50 -06:00
Andy Miller
71eb774a39 support labels in recovery mode
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-19 21:43:58 -06:00
Andy Miller
65689101ab fix recovery mode
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-19 21:09:16 -06:00
Andy Miller
23d92e6a41 jump into recovery mode
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-19 20:47:34 -06:00
Andy Miller
1982717272 more recovery manage fixes
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-19 18:36:50 -06:00
Andy Miller
e82a0ce8bd more recovery fixes
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-19 18:17:59 -06:00
Andy Miller
38840ff080 recovery/command fixes
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-19 17:54:15 -06:00
Andy Miller
3cf616e609 more fixes for recovery.window and recovery.flag
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-19 17:32:55 -06:00
Andy Miller
ea5ba5dda3 Merge branch 'release/1.7.50' 2025-10-19 16:47:51 -06:00
Andy Miller
f437235eb9 Merge tag '1.7.50' into develop
Release v1.7.50

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmj1apcACgkQnyzziuvb
# CuAfWA/8D21MkMWNjirnjc/osndB/SiEkSD2tB1TAIv3099vcGb4n1+OQ4hV0Cr+
# zixeH/bBZJwi1s1nrb6MKnDJMuzOpBOxgfFPWjU3FhG6pBhx7i1YXCH4CvANVFnP
# HNY9mI9PV8ZZ1ymjZF4I3dhOJqKzY9cY1F8RwUTS/iM03cMgrE8V66uMJIYF2Ti7
# d9QcH9ICCOv5en/u26vWRUDkRA+OseSOrc0FMbzMb9x0yqbTDFq4zjOoa6urAwj4
# kMXf8fBdE2N47DkyBr5aEPJqh7fd0xqCfBPQneHciZcBfGGKG4j4PT7IiFq7X6yA
# 4fCJ5+VuSUYP2aSUBugngwTledzh0JfMa0pRP6q6S97HAJwqYJ1eNm3NCI5xplpK
# n4ck/z//06qrYTvxx/ZTLQGGVRvP7OH/Zs1JfYbLb5TNGCTYL18v/t2xeThoGZBo
# vYsv6wXXSlELV5XVCzIsUwsilRgIwa+b2RvEcyEL6JG5QRRvDo4GcBBTHdZ6NVLH
# qkWId9bSlNBYMHuz0AR+LCTrr4KvMwLobLORu2yBP+qtiQ9x3sxapmCJ1iCQE0SZ
# Bxbdd8PPiQ/pANT6snLqM/IXTJcZn4dCGp0yaFkOeNkXOFJAFUGwW9AYQJU3fEHq
# nDu57yI9c0g4GCmnzmcZ2bLvG69C073ciRZIl+f5lGGn2Xp31pM=
# =Va0w
# -----END PGP SIGNATURE-----
# gpg: Signature made Sun Oct 19 16:47:51 2025 MDT
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2025-10-19 16:47:51 -06:00
Andy Miller
80b8389432 prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-19 16:47:42 -06:00
Andy Miller
5815c8cae5 move recover.flag
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-19 16:03:25 -06:00
Andy Miller
0ac77271cc updated changelog
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-19 14:59:42 -06:00
Andy Miller
269bf78084 ignore unpublished plugins - part 2 2025-10-19 11:04:18 -06:00
Andy Miller
cd5f3842ed ignore unpublished plugins 2025-10-19 10:51:23 -06:00
Andy Miller
997bdfff07 fix test
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-18 19:09:26 -06:00
Andy Miller
da0fbf9dd6 better label handling for snapshots
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-18 19:06:27 -06:00
Andy Miller
6a4ab16529 more restore bin fixes
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-18 18:55:33 -06:00
Andy Miller
c9640d7258 create adhoc snapshot
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-18 18:42:08 -06:00
Andy Miller
7325eb2cfe run / restore feature
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-18 13:48:02 -06:00
Andy Miller
f30cd26956 bin/restore enhancement
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-17 22:18:54 -06:00
Andy Miller
17706d5647 stop cache clearing snapshots
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-17 21:08:08 -06:00
Andy Miller
a0b64b6d88 more refactoring of safe install
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-17 20:47:48 -06:00
Andy Miller
4650bd073e filter out extra folders
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-17 19:54:05 -06:00
Andy Miller
4cab0a7ba1 optimized staged package
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-17 19:33:03 -06:00
Andy Miller
44fd1172b8 more granular install for self upgrade
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-17 18:18:53 -06:00
Andy Miller
920642411c move back to cp instead of mv for snapshots
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-17 17:53:48 -06:00
Andy Miller
2999c06a3a change snapshot storage 2025-10-17 16:49:42 -06:00
Andy Miller
d97b2d70bd logic fixes 2025-10-17 16:18:40 -06:00
Andy Miller
5e7b482972 test fix
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-17 11:34:35 -06:00
Andy Miller
9230a5a40f ingore recovery window
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-17 11:32:38 -06:00
Andy Miller
286b5a5179 fix for binary permissions in CLI 2025-10-17 11:26:43 -06:00
Andy Miller
70d6aec1a7 another fix for safe upgrade 2025-10-17 11:07:17 -06:00
Andy Miller
9dd507b717 route safeupgrade status 2025-10-16 23:31:05 -06:00
Andy Miller
b6a37cfff3 preserver root files 2025-10-16 23:17:34 -06:00
Andy Miller
09aa2fb8fd ensureJobResult
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-16 21:28:08 -06:00
Andy Miller
3f0b204728 Add new SafeUpgradeRun CLI command
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-16 17:32:43 -06:00
Andy Miller
f10894fe47 fixes for permission retention
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-16 15:24:12 -06:00
Andy Miller
b68872e3fd Monolog 3 compatible shim to handle upgrades
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-16 14:32:05 -06:00
Andy Miller
43126b09e4 fixes for 1.8 upgrades
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-16 14:19:16 -06:00
Andy Miller
2c4b69f9ec Merge branch 'develop' of github.com:getgrav/grav into develop 2025-10-16 12:01:14 -06:00
Andy Miller
d6cbc263e7 source fix in restore bin + missing dot files after upgrade
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-16 11:56:40 -06:00
Andy Miller
c56d24c0d7 timelimt on recovery status
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-16 09:08:53 -06:00
Andy Miller
7192cfe549 synced restore changes
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-16 08:09:47 -06:00
Andy Miller
a5c6f1dbe9 Merge branch 'feature/installer-rewrite' into develop 2025-10-15 20:15:29 -06:00
Andy Miller
c8227b38fc standalone grav-restore fixes
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-15 20:14:15 -06:00
Andy Miller
77114ecdd0 grav/restore dedicated binary
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-15 14:20:30 -06:00
Andy Miller
23da92d0ff honor staging_root
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-15 13:49:36 -06:00
Andy Miller
f88c09adca update GRAV_VERSION for testing
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-15 13:12:28 -06:00
Andy Miller
7dd5c8a0ba staging root config option
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-15 13:00:05 -06:00
Andy Miller
cf2ac28be2 bugfixes in safeupgradeservice
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-15 12:50:54 -06:00
Andy Miller
43ddf45057 latest tweak
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-15 12:42:38 -06:00
Andy Miller
57212ec9a5 better plugin checks
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-15 11:24:50 -06:00
Andy Miller
b55e86a8ba force upgrades before updating
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-15 11:00:54 -06:00
Andy Miller
2b1a7d3fb6 upgrade manager fix
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-10-15 10:44:19 -06:00
Andy Miller
250568bae5 initial safeupgrade work 2025-10-15 10:29:26 -06:00
Nakkouch Tarek
75d8356f1b Fixed Twig Sandbox Bypass due to nested expression (#3939) 2025-10-13 13:36:49 -06:00
pmoreno.rodriguez
c82645a42a wordCount Filter for Grav (#3957) 2025-10-13 13:35:33 -06:00
Andy Miller
3664096550 Merge branch 'release/1.7.49.5' 2025-09-10 14:16:51 -06:00
Andy Miller
03849923d4 Merge tag '1.7.49.5' into develop
Release v1.7.49.5

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmjB3LMACgkQnyzziuvb
# CuC9CQ/8CURB0sfbeDcjZbFIxNei0nGSCQkD8Wqp8IWOpSQ4iu5KiS6y6aQu7OIn
# LzvM6/wlbxJ6BhcYjt7uwMLQOH9mA4uPYMF7SQd5ElZZnoIp0zLcZ3CgDvznwOQj
# m2Kcuij3z749ORl8HjG56uhDwP5pk1C6u8OriGbb/z7uLTirLwxGE9yOoNJjlvWv
# K4ddGYcxF0OphRrIBmwbnMFwx+CoRBB1wg9pWtIba4cpgwu2OUYU9orNcVxGxPMI
# 8kXglIPrQZCKbzkjW2TNxdOVlycLKvw4J0jq+E8zgHVYFuS8yFX8JMR21wBxgCgH
# iCtigNUNwApvQafTpqWNhk+kY7nA2823NjaLNqIRRjEr3jQS7/zNorlRoaGyM7MB
# 8utneW8bH0Km2s6V4KDAKJex1Q3iE8KzFTIdM8kZ/Xnani8unrstHBAkcGkTWz0x
# NgKGEjB8moYb9+t+yRdXj7hcqHh4VkaRXm1Ac1UitaEfeEP+WwfyeBhOWBvIPDHQ
# vrdoSDWGvBHngR6Iq0cXKph7NzzQsxGVAPvKYVMlCtkFRDYNAsQOksVl5UunF5P9
# gpuLkANT7hr0WDWhCi7OMHgQW8IAMkiApp+phfrTK0GNBU2P7O+0MkeJ8LKd7mAi
# cOmZ/FDvtAzZUlwVpTiWuMq2MknwwfTU1BJblXxKGvt5Ysmx7HA=
# =gegb
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed Sep 10 14:16:51 2025 MDT
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2025-09-10 14:16:51 -06:00
Andy Miller
6664d98de8 prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-09-10 14:16:37 -06:00
Andy Miller
17323ec76c prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-09-10 14:16:04 -06:00
Andy Miller
9b9079bdd7 Backup not honoring ignored paths - fixes #3952
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-09-03 18:09:17 -06:00
Andy Miller
db3df738e8 Merge tag '1.7.49.4' into develop
Release v1.7.49.4

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmi40TMACgkQnyzziuvb
# CuDw6g//edg76Q77jQBXrdYyuR+WChQrGE3G+AOBJmTjff7YMdQQZG3dl4bUK0L3
# 52Ft0q5dQHdn1gUyAgwxJBnH19TxNrjCC/GqBXFWFOnmJSiKzAwqB2FeWtgKTMY4
# 5RS7AmzCGEGDvqTzhIB9N1x1omHAFZDlKplevUhyTzQ+12/GKd1hIJ3V5JrlVmt2
# HRrOU403mOHEqfGH5Z4kPFM2nCddeHLGqBA/3kosh0Dq548pYgE26o3A02z537F0
# KwulRDITSoR8lN3fwq2MDjhCOJzu9MLiRcWl3s3nMcVC6LhYmZUdZjRvuBF6DmSH
# SdkZ3K1aibdknwWcm1cCPD23QwLblXr9zhTaXNvHEkSY7n8elKLxEVekh7OKDTFF
# gDdtIsaMHb891TucaF/Ejo0fFdwzacaoph4Lr+CYX/Xfs9eh2V+8+Y6hrG2ZOHdd
# M4GvI+U9+LjBbN3pKAbzRF6jiXCXl5qjJSrp7DQd4SBxXCOhO/VfTanXm+8I9f58
# Srhn9kzb2/EqQA64sGjZIZwrV7/yXfqoF5dBpVrsEi1sH4zzm0g9/DCwqGxAhH+b
# zNrxVlHu845wW7K8pWDQX0YbnvYcE83mzOhEYiP5qFUew03fDSIPs30QflApmy+8
# qfXLc4kM/XTMKYnq9febhmjLg2RXBMorxUovCaNPjHGXa0UaK4Q=
# =1m/V
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed Sep  3 17:37:23 2025 MDT
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2025-09-03 17:37:23 -06:00
Andy Miller
38075f9c86 Merge branch 'release/1.7.49.4' 2025-09-03 17:37:22 -06:00
Andy Miller
b0dac5f4b4 prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-09-03 17:36:39 -06:00
Andy Miller
f7c77d1173 fix for cron force running
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-09-03 17:32:48 -06:00
Andy Miller
0fd734c8df Merge tag '1.7.49.3' into develop
Release v1.7.49.3

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmi3XKUACgkQnyzziuvb
# CuCOzw//SUVGgXNxcvWvBGFya/vKhsO9V39buLzDx7DZk5acYA2E0SHN5aRucGmq
# 3p4GaXjHQ9aSRwIB3DTNoW9fWPGd8DFURLGTti7vh4EkvQclF1fI+22sNdlXrs6P
# uRO2DvuJ+5VFDNv7LzyNhx8HdYXlANrC8jFmBS2ehAgkTUQDI3T1JmY+/H/ayyik
# pFmzty9y3jy1IG/8qAhL+g8VPWccDIHTvxM+6EQjJvCQzHybrUk1nb3zjFGqsjQD
# ocIu+jwx05qvymmqvb0S5ULlqK6BBZTz2zBH3xF4viSF4ZF9YqDLhepOrA5wsVs+
# rNRSjUvMs05tIzA7stxwapxTB8s6C8Bm8mSu3XA+0ERtde5F3E40bhptDCwhlCLI
# dTgKb0+UaEcDPl1E7VSZJ8UlrZtvO3dtg5fTt7Ieebv0b1fbRoyxWNldhxbEcgDo
# HaeZAHNu8RMuTq36ACH7vgx2BEghAfe1/qXNfHtGokNdRxQSdvCUPGl8FS5hpBYG
# JVLOkS9mABKNENpqzWyp0pYJ0MORdrv74GnhY62/ue9mG4+Eo/kFcsZtoTUsFiHI
# 7eIhZTdazWxW27BkeI94g/B6sPxcQGFs8qHhaiOZLvveKvRJHynZez5zdHnm2X1G
# 4qa5m2ebjGpxhKK8zDF1nlZEKPsaK6ZZQid4o8LL1nv6PwRcsIk=
# =xk/M
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue Sep  2 15:07:49 2025 MDT
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2025-09-02 15:07:49 -06:00
Andy Miller
b642b2c999 Merge branch 'release/1.7.49.3' 2025-09-02 15:07:48 -06:00
Andy Miller
ae147fa53b prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-09-02 15:07:38 -06:00
Andy Miller
9e6df39bff fix duplicate job issue
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-09-02 15:05:38 -06:00
Andy Miller
8ac6076f88 allow you to run -r and -d in command line together
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-09-02 15:05:30 -06:00
Andy Miller
cef7812472 fix an error in ZipArchive that sometimes occurs
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-09-02 15:04:51 -06:00
Andy Miller
6f461395a7 update changelog
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-09-02 14:41:40 -06:00
Andy Miller
162fe1acc2 namespace change for cron expression
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-09-02 14:40:44 -06:00
Andy Miller
332748a1f9 removed hardcoded setup commands
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-09-02 14:08:46 -06:00
Andy Miller
1a9a60115d Merge branch 'release/1.7.49.2' 2025-08-28 11:37:21 -06:00
Andy Miller
a55052b8b0 Merge tag '1.7.49.2' into develop
Release v1.7.49.2

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmiwk9EACgkQnyzziuvb
# CuDx/A//dPvNxTkFAMbAy6j7W1UrinLF8QZV7jnSR8ig8LYN6gprwRVWKOT8Oui2
# vyfgvt9NfOHaW29EDXGdHRIeuqORTxztHOY/PrsRdP7zKQzZcNTJ4LjEcR2uGc1S
# UndhkPr4KL2hOq66JgoFif8O7Z0zl7EjLshy2d9pA7PU0LXxbLzYhNEmvkZjdLSZ
# RBmq0GmpQGTFV7l+4Rrsr0nK9KX8D/YgbBXwOo9oxIHMQzcH0i0MXFCIZdKX6V6V
# iKPbbA5A4xq7+F3zufUld2PSMIyfsdyPuoA2uJKeMGIdj4hjPUHhsOY2688EUyyf
# MRqLwoX/ZSeT5dIacXitNrS9vhLWwmxvIbNrGbZn/Jrppmwcj6r6ACkbJwkOPjaN
# kSSu6gU6SFEc7gILZXhNH1e5DCE7r1VjTDr/BoebQGZHQWafr9PG5lMMXhZRWpo+
# zPvn5cRKtI8/zDZwwR9RzJx/lUkaO7pJNg5tW5z4oo/XBmtwxYoD++JMxF3BqQvy
# Q1e+uyWhi01YpG+ofPKA5tRbD0H1++N5e5gghqhqdmazLZV3Doz2igRQUy6eVwxL
# KoSZdweJLDIFPIoSMCchU0O9wR+ZAVNNdYuQe9snezNYiBJqXcXOq3nLiEJhMVV+
# CySVvLrM85Yq0cOq6FXsVsYwHhnH8PeW8ebs7+c8EPmzxrLHAdk=
# =qw1P
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu Aug 28 11:37:21 2025 MDT
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2025-08-28 11:37:21 -06:00
Andy Miller
dc7354e7e1 prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-08-28 11:37:09 -06:00
pmoreno.rodriguez
afc1513aad Fix translation key for image adapter label and help (#3944) 2025-08-25 18:56:12 +01:00
Andy Miller
466b2a16e8 Merge branch 'release/1.7.49.1' 2025-08-25 14:16:45 +01:00
Andy Miller
a21640ace6 Merge tag '1.7.49.1' into develop
Release v1.7.49.1

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmisYj0ACgkQnyzziuvb
# CuA6NxAAmqhEkRNcLhnyVDyZrwcinrANkYvD/Z9MKoy9NQfUaQaQmb5v7rl2Qj+T
# etmPUj9qNQI1KpDrwB2lYVAvvrQoVt73Y72GsnX/6OwNtsVNWCT2NhWN8MkRwu5d
# 6h+k7jhiotVAGMGDiI+Srk7v1LbXj+Y29pOalUDA+fZFRVHdXmQGFrYafV7LmMvH
# 7kMRS3Sj8oPII9BTBAWCiBEQ/DVil9Q+RuKwL35u76RRbh3yYJRO6fZym+IWD4YW
# ARXaWDsfG7ayoW12dg+Ufj4yooCxIghLFEXyPBr/2FqXn9/iAndQlEINGN5dSya/
# PvmhWer0bH2Wz3NUFqkTpBoK8QPjaPJThGhl2C6O2RscXIIrAvKa+VViWzNE7AU4
# Jdx7quFyC3xuElxBEPF99vabBpisxdzdtyu5v5VhlpTClNTQswbnRjax7qrUKHt8
# SLzp6tdM3SKE/AN+nXtexpVWjZTUaskl241/MPjRORtatniWSnemddjbfU9aBPmF
# /B8HjRFzGT+UFHt8b+AnP02Bt032JEdPvL8TfEAmfO55WiMpK6+Qutr2oogOznrN
# zn2D+1rUNs+8V3lKKJwaGe4nNe/BVZ1yoPQXWu3q8Z2cxJXRY2+4L66y5RdoNoew
# 1S2HyESWInAjVwHKIoyGoeabnnhVTHJpSLb8fiLNp017BTIISto=
# =LlNI
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon Aug 25 14:16:45 2025 BST
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2025-08-25 14:16:45 +01:00
Andy Miller
09920deabc prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-08-25 14:16:32 +01:00
Andy Miller
0bd72f4bb9 Merge tag '1.7.49' into develop
Release v1.7.49

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmisWskACgkQnyzziuvb
# CuDTlg//TOFe5VqIBV03SDHVFW3frF/nK+2pOGUXgfLXN9sfPHrT8F89drsPDBar
# YwxTynix8NvPXumt/DeeGvEtDJOZt8UYDUiAlTc++F3duNGjCBId/H1XqRKk91jL
# jilxrXPboemQ+nqzVFTcACFi5/GjYKXvscOVPJ6Pflr/EsLjUfzIF11zXG2vCJsv
# rdI2W3J+cGS6VoaxvGF8vfC9k1g+rC9E1bKcj5wSI98bVQf5HCR8kLRxfyfz4fvX
# w0mLvI9qCqJ4CsIa7kLw5AxPI3bFLBQjhQtiuH6WJMNkUNvcUJ51MmU5S4hEZMLt
# BSQAD4xfG5Hb98YZaQyISBVMw3ed4rA3MLYfHVi6kYmL5FHl3lTSIL8RTe9sQSqI
# cojdIlCNJTAOL65TQik78BqmjjfMzi28B8DBCfKF5ZL5SrpHDYNqcKojzSscbytJ
# S7h9mucquI5MaMDKdMi79vZHPrmbk21ML6DbuefVxpGHDQbLGflVIpyDRzMOgvzs
# MPtYVo8gG7ljlD0EcVkUO4lZgSrImw2Ko6U8RccF8rzSG2ZmrlQEik10t/e6SorO
# g20CbctHKYgWq38rkgZmmJ+mUrtpHaYI2N/HrCTPqaaqDV0toG8lvM2/TyKq7TKh
# Aq9Kwhqz9mqDnMqMqb0l0SjA2DFdIKc9Y3Uzqi/VXRgxOmjDoq8=
# =3bpd
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon Aug 25 13:44:57 2025 BST
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2025-08-25 13:44:58 +01:00
Andy Miller
ec55a80183 Merge branch 'release/1.7.49' 2025-08-25 13:44:57 +01:00
Andy Miller
d07ac5b6ea prepare for release
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-08-25 13:44:47 +01:00
Andy Miller
fa29f6672a updated changelog + vendor libs again
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-08-25 13:37:44 +01:00
Andy Miller
006d8c85a0 Merge branch 'feature/modern-scheduler' into develop 2025-08-25 13:27:37 +01:00
Andy Miller
c608ed10cf implement a better purge strategy
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-08-25 13:27:23 +01:00
Andy Miller
9d71de8e54 more scheduler improvements
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-08-25 12:53:47 +01:00
Andy Miller
89764a51fb more scheduler fixes
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-08-25 12:36:06 +01:00
Andy Miller
b851d9bf9d added scheduler logging
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-08-24 23:58:28 +01:00
Andy Miller
a0679fc050 scheduler improvements
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-08-24 22:23:18 +01:00
Andy Miller
e497a93da6 simplify extended jobs logic
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-08-24 22:14:14 +01:00
Andy Miller
56cc894c1d initial improved schedular functionality
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-08-24 20:27:28 +01:00
Andy Miller
d07f3770bc chagne order
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-08-17 13:12:08 +01:00
Andy Miller
3baaf19c31 update lock file
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-08-16 13:00:39 +01:00
Andy Miller
7236862a15 Add Imagick adapter support
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-08-16 12:52:55 +01:00
Andy Miller
8811b7aad0 revert PHP 8.4 fixes
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-06-12 10:41:38 -06:00
Andy Miller
4a22f3dc8d better match
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-06-05 11:22:28 -06:00
Andy Miller
fa56984dc3 use lang string
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-03-31 19:26:01 -06:00
Andy Miller
90fd68d4a5 handle empty value on require with ignore
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-03-31 14:12:05 -06:00
Andy Miller
7613e38b6d Add support for validate match
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-03-31 14:11:35 -06:00
Andy Miller
830a442faa update vendor libs
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-03-30 15:23:22 -06:00
Andy Miller
8d7b658aa6 Some fixes that impacted file uploads
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-03-30 15:22:45 -06:00
Andy Miller
83d098b891 throws error
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-03-07 12:06:58 -07:00
Rotzbua
d798859acd chore(ci): update GH Actions php test (#3867)
- php versions should be strings
- caching updated to current recommendation
- fixed env var name
2025-02-05 11:16:51 -07:00
Andy Miller
08d74df6e3 Fix parse error: #3894
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-01-24 15:23:06 -07:00
Olivier Dolbeau
2620e836d4 Update code blocks in README (#3886) 2025-01-18 12:39:11 +00:00
Andy Miller
7e723eb7f5 actions/cache@v4 cache for tests
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-01-13 12:30:17 +00:00
Rotzbua
1d1d8da431 fix: twig filter exif_read_data throws critical error (#3878) 2025-01-07 13:53:09 +00:00
Andy Miller
4097d85daa updated copyright date to current year
Signed-off-by: Andy Miller <rhuk@mac.com>
2025-01-06 14:14:42 +00:00
Andy Miller
2e975dfa90 bug in AbastractLazyCollection
Signed-off-by: Andy Miller <rhuk@mac.com>
2024-12-03 15:33:32 -07:00
Bob Conan
a1e583f657 Updated SECURITY.md, fix typo(s) (#3870) 2024-11-13 14:01:07 -07:00
Andy Miller
5cf7ef864b Merge tag '1.7.48' into develop
Release v1.7.48

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmcfdcoACgkQnyzziuvb
# CuBypA/+KweJfAVBOokFPS1iFKoQ1mrBQ0jpy4zlYLP2dFdI9OBjIXOyv15dPEkJ
# +9kxQpgsjTV4sc3WmdTUw7OVLdEqhuVZA/xhlZV3x9g3ehYSgx0guvmZTLCWqyex
# b4H4xvWHcIqF7OuNO7jv4vRZijLWTeXE9F8KQajjMIoZvG6z3GVnk20mLKzvV9KD
# mn3PWd8pnXT171TtL59WLcM47rZ93C8vqcInGjHXC2ww0+4ky63/KQbcGi+fSc6U
# /7LyWOqGXuoc8DylLVXXwpDiFGQZoyQKDxH/C9RejgnKdP/fysugBwiSsMpmQPFU
# 1KfU3BZhB74L8pRLbbAbPB0B7qhKxMlOF+S/Gr/HAjmpMwOy7JsfF10E8dIkx3FO
# R9K8awpTg08xzpLXTyeSR5CcN52/rBEgAxASF30t2QWl/t6rmMWZOfaDlPseFdtb
# d1Bz7HrtFdCYLtWGkkxF1ZhI233cR4BnxuvKml9zpjHOTnonlZTo7N2j/zG0hAom
# sFxHmtrDa0sYoWlGvAbwBa0T5SVGe0Y8DbIskud1IKepRRJROOquqtyGpLKL53HJ
# NfNPszfwiN49z9kl1afYq/dK8f497sk2DAok0AhXgVMbes99LaqsPBNZwwioSMkD
# o94p3yW7aR6rzTcqkLr/FyZAa/Iyl58h6ZFlgYTatbMFKi/haDw=
# =XhZk
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon Oct 28 11:30:18 2024 GMT
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2024-10-28 11:30:18 +00:00
Andy Miller
199cdd4364 Merge branch 'release/1.7.48' 2024-10-28 11:30:18 +00:00
Andy Miller
2896aea30a prepare for release 2024-10-28 11:30:04 +00:00
Andy Miller
3952491ce9 updated changelog 2024-10-27 12:24:46 +00:00
Andy Miller
6b07088189 cli Alias support in 1.7 2024-10-27 12:24:39 +00:00
Artyom Mezin
a0614dc3eb Resolving style conflict for other tooltips (#3861) 2024-10-25 10:44:26 +01:00
pmoreno.rodriguez
346d194125 New Trait for fetchPriority attribute in images (#3850) 2024-10-25 10:42:20 +01:00
Andy Miller
964e37c6f0 Merge branch 'release/1.7.47' 2024-10-23 13:02:17 +01:00
Andy Miller
c55f2bed4a Merge tag '1.7.47' into develop
Release 1.7.47

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEwbiolRD/eEYBHGp5nyzziuvbCuAFAmcY5ckACgkQnyzziuvb
# CuDjiQ//aAyIya3K+mJOMB4AkKiT24eIq9FPSBCw2tOCsF+YcoAd5WxPe6eE2BKF
# qjrTK99hB5Adig8tkIdecIxvd3FKiDyVyjkQXDkO0UTDLQT++G3S/OBN/2tLfskc
# zJ+Esqco2QJZrKXUj1lttM4z52XFV0eiXZ1qQmnQngaa1uXCgUIKR8MuqjiMl0kF
# IT0a8Y8nHZmy2dDqw186c61cZn90qDE4foSQTJt5Rouy3qmoOzR5J0u6WKWSCbJH
# HkLwbfdDj4ZaOJ0a0hBuoxo/SzalOi3mkwaTqLIYACiMNf/NLuP/K0GJJFIY60lB
# yHa136Hi+nYlAmInGzTiAmhaWJNodB3mIJWc4gjZrDyMHfYD2BxYpjSYd1nDIJ7M
# omUPB37SQufotb58hJf5LRIDrKd2k70joRfiGlb+x0P5AuMOmhZQo0yYqqM7pNPs
# AHNf6Q01wVRu0OUws77BUBEnaNcr/APGHjw4bkucE0VaCoG3tRF9FXM6EoDdLwDy
# tIUh7peuEqhz05ViczqMtWl4YgCKpjU+bBriWUB7ge85YJIZuH8IkNy/PJnLnvfF
# U23zmClG2h+Ke4ACzzAUOjccxxCFPCqcNKrZPkiLx4R7N+abHYHRiwEcH31KVhm4
# GUJlYWLEPtAO80z4igKJibaTeEA/FTm5K79yMUAxs/4iUTVGr/w=
# =oUN6
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed Oct 23 13:02:17 2024 BST
# gpg:                using RSA key C1B8A89510FF7846011C6A799F2CF38AEBDB0AE0
# gpg: Good signature from "Andy Miller <rhuk@mac.com>" [ultimate]
2024-10-23 13:02:17 +01:00
Andy Miller
ecf664c8e6 prepare for release 2024-10-23 13:02:02 +01:00
Andy Miller
e2257a9783 logic fix 2024-10-22 15:29:47 +01:00
Andy Miller
6fa63197a0 Fallback to extension on Flex-Objects json check 2024-10-22 14:10:18 +01:00
Andy Miller
f686e0ac64 Added extra checks in Uri::getContentType() 2024-10-22 14:10:03 +01:00
Andy Miller
5a741d9b10 updated changelog 2024-10-22 12:05:21 +01:00
Ricardo Verdugo
16a50767dd Fix JSON output comments check with content type (#3859)
* Update FlexCollection.php

* Update FlexObject.php (#3858)
2024-10-22 10:54:40 +01:00
Artyom Mezin
d72fca121f Annoying output comments fix for json Flex (#3856) 2024-10-22 10:32:54 +01:00
Andy Miller
e0ff168cf3 Utils::toAscii() should be static 2024-10-21 14:20:05 +01:00
Andy Miller
bbfc3b5658 updated changelog 2024-10-14 17:28:48 +01:00
Andy Miller
106dc58329 more improvements for clockwork-web support 2024-10-08 17:29:39 +01:00
Andy Miller
ca1e5ebb8a fixes for clockwork web UI 2024-10-08 15:54:59 +01:00
Andy Miller
6a585857b0 vendor updates 2024-10-08 12:50:05 +01:00
Andy Miller
f81a4ca008 Merge branch 'develop' of github.com:getgrav/grav into develop 2024-08-22 15:16:11 +01:00
Andy Miller
8c1b4448e9 fix for exif_imagetype() throwing exception when file !exists 2024-08-22 15:15:57 +01:00
pmoreno.rodriguez
f73813103d Support to Fediverse Creator meta tag (#3844) 2024-07-12 15:04:45 -06:00
Andy Miller
9c697f178d composer update 2024-06-04 14:57:02 +01:00
Raphaël Droz
20ca44fd53 Include modulars for last modification date computation (#3562)
* Include modulars for last modification date computation

Fix #3561

* use ->modified()

---------

Co-authored-by: Andy Miller <1084697+rhukster@users.noreply.github.com>
2024-06-04 14:46:58 +01:00
Rotzbua
24cd0e133f Update jquery-3.x.min.js to v3.7.1 (#3827)
Source: https://code.jquery.com/jquery-3.7.1.min.js
2024-06-04 14:38:48 +01:00
Rotzbua
9f4a86317b fix(composer): remove unused dependency (#3828) 2024-06-04 14:37:32 +01:00
Andy Miller
25ace6458b Utils::toAscii() func 2024-06-04 14:36:47 +01:00
Andy Miller
856a478bd6 fixes #3831 2024-06-04 14:35:33 +01:00
Andy Miller
faa8ee5fe1 Merge branch 'release/1.7.46' 2024-05-15 17:16:00 +01:00
Andy Miller
785f641ea5 Merge tag '1.7.46' into develop
Release v1.7.46
2024-05-15 17:16:00 +01:00
Andy Miller
013ff7ee1b prepare for release 2024-05-15 17:15:30 +01:00
Andy Miller
c97a0ffb16 reworked to use the modified Uri::parseUrl(), plus better fix for multi slashes 2024-05-08 12:45:52 +01:00
Andy Miller
51623ee0da Added custom_base test 2024-05-08 12:35:16 +01:00
Andy Miller
8c941cc6d3 update changelog 2024-05-06 12:48:52 +01:00
Andy Miller
b6bba9eb99 fixes #GHSA-f8v5-jmfh-pr69 2024-05-06 12:48:45 +01:00
Andy Miller
77adfcb831 missed a check in MediaUploadTrait::checkFileMetadata() 2024-05-06 11:31:23 +01:00
Andy Miller
ee8d783d05 better support for external urls in Utils::url() 2024-04-20 15:42:42 +01:00
thebodzio
d184e25f05 Handle the situation when GRAV_ROOT or GRAV_WEBROOT are / (#3667)
* Handle the situation when GRAV_ROOT or GARV_WEBROOT are `/`

* Update defines.php

Replaced `/` with `DS`

* Update Backups.php

Replaced `/` with `DS` in `backup` function
2024-04-16 11:52:38 -06:00
pmoreno.rodriguez
04f9385aa8 Fixed "news" to "new" in Changelog V1.7.45 (#3810) 2024-04-16 11:51:22 -06:00
Andy Miller
afb5b02e57 Fixes for multilang taxonomy 2024-03-21 15:08:44 -06:00
Andy Miller
4187a04235 Merge branch 'release/1.7.45' 2024-03-18 11:35:28 -06:00
Andy Miller
26a6cb75ad Merge tag '1.7.45' into develop
Release v1.7.45
2024-03-18 11:35:28 -06:00
Andy Miller
37d0498e1b prepare for release 2024-03-18 11:35:20 -06:00
Andy Miller
dd8d610ae0 updates + changelog 2024-03-18 11:34:37 -06:00
Andy Miller
b9529d0010 minor lang updates 2024-03-18 11:20:51 -06:00
Andy Miller
4149c81339 fix for safe_functions attack #GHSA-c9gp-64c4-2rrh 2024-03-06 14:53:53 -07:00
Andy Miller
2da91d9c8b Update SECURITY.md 2024-03-04 15:55:29 -07:00
Andy Miller
d69adcf347 Update SECURITY.md 2024-03-04 15:54:54 -07:00
Andy Miller
45e2c27c66 Update SECURITY.md 2024-03-04 15:54:11 -07:00
Andy Miller
f77df43d7a Update SECURITY.md 2024-03-04 15:49:48 -07:00
Andy Miller
de1ccfa12d Mitigate various SSTI injections 2024-03-04 15:41:30 -07:00
Andy Miller
5928411b86 fixed path traversal by santize checking fiilename 2024-03-04 13:39:50 -07:00
Andy Miller
15dc7568a5 typo 2024-03-04 13:31:40 -07:00
Andy Miller
b435d2b884 upgraded built-in composer to 2.7.1 2024-02-13 12:47:21 -07:00
Andy Miller
dbedb60634 update vendor libs 2024-02-13 12:47:00 -07:00
Andy Miller
f9f5781af8 fix for bad page dates + changelog update 2024-02-03 13:45:35 -07:00
pmoreno.rodriguez
ad8b1b79bd New Trait for decoding attribute in images (#3796)
* New Trait for decoding attribute in images

* Update comments info

* decoding default in system/config/system.yaml and system/blueprints/config/system.yaml for the images.defaults.decoding value

* Fixed predefined option in the decoding attribute
2024-02-03 13:24:12 -07:00
Andy Miller
cd2a7d8d98 changelog updated 2024-01-19 12:41:54 +00:00
Andy Miller
1dc6866eab fix other multibyte issues in inflector 2024-01-19 12:40:55 +00:00
Andy Miller
0b16401a91 fix special-chars in titleize - fixes #732 2024-01-19 12:39:24 +00:00
Andy Miller
78b8125eae Merge branch 'release/1.7.44' 2024-01-05 12:43:46 +00:00
Andy Miller
0d7cd64d0d Merge tag '1.7.44' into develop
Release v1.7.44
2024-01-05 12:43:46 +00:00
Andy Miller
3ea86e1794 remvoed outdated PR as it was DRAFT 2024-01-05 12:43:27 +00:00
Andy Miller
6df03063c8 remove test 2024-01-05 12:34:59 +00:00
Andy Miller
e5990f431d Revert "Added 'outdated' option to scheduler command (#3771)"
This reverts commit a71403f158.

# Conflicts:
#	tests/unit/Grav/Common/Scheduler/SchedulerTest.php
2024-01-05 12:31:53 +00:00
Andy Miller
b3d55ca81a remove a debug 2024-01-05 12:26:08 +00:00
Andy Miller
a0e728b540 Merge tag '1.7.44' into develop
Releaese v1.7.44
2024-01-05 11:59:45 +00:00
Andy Miller
171a5c074c Merge branch 'release/1.7.44' 2024-01-05 11:59:44 +00:00
Andy Miller
f33e89fa45 prepare for release 2024-01-05 11:59:37 +00:00
Andy Miller
e33d71e4b9 updated changelog 2024-01-05 11:58:34 +00:00
Andy Miller
ddbb1362dc updated composer again 2024-01-05 11:58:23 +00:00
maelanleborgne
a71403f158 Added 'outdated' option to scheduler command (#3771) 2024-01-05 11:46:14 +00:00
Ron Wardenier
88eb9f915a Allow empty and maolformed links in markdown (#3782)
When a user adds an invalid link in a page in markdown for example [](https://) and that page is parsed to be shown in a blog listing page that blog listing page crashes with a CRITICAL error. Instead of throwing an error the URL is now ignored. See also https://discord.com/channels/501836936584101899/506916956637495306/1185616779486167141
2024-01-05 11:44:44 +00:00
Andy Miller
70e5262512 Merge branch 'develop' of github.com:getgrav/grav into develop 2024-01-05 11:44:00 +00:00
Andy Miller
a1c116dd82 update copyright year 2024-01-05 11:43:52 +00:00
Rotzbua
cc08da0c74 add php 8.3 to test (#3778)
https://www.php.net/releases/8.3/en.php
2024-01-05 11:43:34 +00:00
Andy Miller
f7eab6b163 composer updates 2024-01-05 11:27:02 +00:00
Andy Miller
f59fa9a291 language updates 2024-01-05 11:26:45 +00:00
Andy Miller
458c64086e Revert "Use new groupNames method"
This reverts commit 470b69c775.
2024-01-05 11:20:40 +00:00
Andy Miller
345086538c updated composer and vendor libraries 2023-11-09 12:38:20 +00:00
Andy Miller
c62e173955 updated changelog 2023-11-09 12:37:02 +00:00
JS Media Creation
1b8e267d0a Add mime type for vCards (.vcf files) (#3772)
Adds support for vCards (.vcf files) in case of e.g. scanning a qr-code with the direct url to the file, so that it can be downloaded. 

Only a thumb-vcf.png should be added then too.
2023-11-08 12:06:04 +00:00
Andy Miller
eb72cb32bb updated changelog 2023-11-06 16:52:28 +00:00
Andy Miller
4e01398545 Added debugger output when routes conflict 2023-11-06 16:50:27 +00:00
Andy Miller
b0dd2358f4 Updated packages (including dom-sanitizer 1.0.7) 2023-11-06 16:50:15 +00:00
Djamil Legato
0c9333e60d Revert "fix whitespace encoding in urls" (#3764)
* Revert "fix whitespace encoding in urls (#3719)"

This reverts commit 6a9b1f2214.

* Revert change
2023-10-27 23:58:08 -07:00
Andy Miller
0b53609fa0 updated changelog 2023-10-25 14:49:52 +01:00
Andy Miller
cfa510e7f7 Merge branch 'master' into develop 2023-10-25 12:38:41 +01:00
Andy Miller
6d5f0ff9ba validaiton math rounding - fixes #3761 2023-10-25 12:38:12 +01:00
Angela Ugrinovska
71939e18be Fixed too few arguments exception thrown in the admin with using flex objects (#3658)
Going through older PRs, thanks for this.
2023-10-24 10:33:58 +01:00
Junky Junkerson
45f8fe4d0b Correcting comment in about custom site.yaml value (#3659)
Corrected blog: route: '/blog' comment from system.blog.route to site.blog.route
2023-10-24 10:32:49 +01:00
Vital
2179ef33a7 Fixed exception: "Property 'jsmodule_pipeline_include_externals' does not exist in the object!" (#3661)
Co-authored-by: Artemkin_V <avr@vital-web.ru>
2023-10-24 10:32:19 +01:00
Rotzbua
d0ae677e61 Update jquery-3.x.min.js to v3.6.4 (#3713)
Source: https://code.jquery.com/jquery-3.6.4.min.js
2023-10-24 10:30:19 +01:00
dirkjf
6a9b1f2214 fix whitespace encoding in urls (#3719)
* fix broken src url encoding

* remove redundant code

* Revert "remove redundant code"

This reverts commit 4e0020114e.

* Revert "fix broken src url encoding"

This reverts commit 3e8259da3a.

* encode whitespaces in url paths
2023-10-24 10:30:00 +01:00
yiwu
b1117e45c9 Update system.yaml (#3721)
add ISO 8601 dateformat
2023-10-24 10:28:44 +01:00
Ricardo Verdugo
382a836d80 Fix invalid input to foreach (#3724)
* Fix invalid input to foreach

This happens with discord oauth, possibly others

* Update UserGroupObject.php

---------

Co-authored-by: Andy Miller <1084697+rhukster@users.noreply.github.com>
2023-10-24 10:28:23 +01:00
Raffael Herrmann
db3e39f0cb Added detection of external triggers of the scheduler (#3726)
Added extension to the isCrontabSetup method to detect external triggers of the scheduler, so that in the admin interface the error message is hidden when the scheduler is called by an external trigger.
2023-10-24 10:25:44 +01:00
Jeremy Angele
80ce87e4a9 Update dangerous extensions (#3756)
Thanks for this!
2023-10-24 10:20:22 +01:00
Jeff
f0f29891d6 Update Inflector::ordinalize() (#3759)
put the init() call before the $ordinals test
2023-10-24 10:19:24 +01:00
Andy Miller
c66da5bedb Merge tag '1.7.43' into develop
Release 1.7.43
2023-10-02 10:41:37 -06:00
Andy Miller
1f21d259ea Merge branch 'release/1.7.43' 2023-10-02 10:41:36 -06:00
Andy Miller
21b218e464 prepare for release 2023-10-02 10:41:26 -06:00
Andy Miller
3b2fb023b8 change version number 2023-10-02 10:36:33 -06:00
Andy Miller
92babda742 updated changelog 2023-10-02 10:30:31 -06:00
pamtbaau
3cdbc5890a Fix url of @import not being rewritten (#3750)
Looks good.  thanks.
2023-10-02 10:04:29 -06:00
Andy Miller
a8042a666c updated composer - fixes #3748 2023-10-02 09:52:50 -06:00
Andy Miller
79f9640b12 move language debug to debugger - fixes #3752 2023-10-02 09:51:22 -06:00
Andy Miller
65aeb82e21 add ability to override modified date via frontmatter 2023-10-02 09:36:22 -06:00
Andy Miller
e3b0aa0c50 inlcude phar in dangerous extensions 2023-08-22 11:57:13 +01:00
Andy Miller
7e617a632e updated security.md 2023-08-22 09:24:41 +01:00
Andy Miller
fb5dd14875 Merge branch 'release/1.7.42.3' 2023-07-18 12:41:08 -06:00
Andy Miller
490bdd6ce7 Merge tag '1.7.42.3' into develop
Release v1.7.42.3
2023-07-18 12:41:08 -06:00
Andy Miller
893b1dd1db prepare for release 2023-07-18 12:40:57 -06:00
Andy Miller
1146959806 fixed a typo 2023-07-18 12:40:27 -06:00
Andy Miller
45103f81b4 Merge branch 'release/1.7.42.2' 2023-07-18 10:50:51 -06:00
Andy Miller
c426f4a9cc Merge tag '1.7.42.2' into develop
Release v1.7.42.2
2023-07-18 10:50:51 -06:00
Andy Miller
0d27f2d77e prepare for release 2023-07-18 10:50:36 -06:00
Andy Miller
b4c62101a4 SSTI attack mitigation - GHSA-9436-3gmp-4f53 2023-07-18 10:49:47 -06:00
Andy Miller
950cd0854f updated vendor libs 2023-06-15 15:32:06 -06:00
Andy Miller
4cd137830b Merge branch 'release/1.7.42.1' 2023-06-15 12:57:56 -06:00
Andy Miller
aa19bcdcbe Merge tag '1.7.42.1' into develop
Release v1.7.42.1
2023-06-15 12:57:56 -06:00
Andy Miller
cf6bf7d1ec prepare for release 2023-06-15 12:57:46 -06:00
Andy Miller
47665dbddb Fixes #3727 - filter field being a closure 2023-06-15 09:03:12 -06:00
Andy Miller
dc209453d0 Merge branch 'release/1.7.42' 2023-06-14 14:19:10 -06:00
Andy Miller
5b89091f13 Merge tag '1.7.42' into develop
Release 1.7.42
2023-06-14 14:19:10 -06:00
Andy Miller
50ee844759 prepare for release 2023-06-14 14:19:00 -06:00
Andy Miller
244758d438 also handle SSTI in reduce twig filter + function 2023-06-14 11:08:17 -06:00
Andy Miller
71bbed12f9 more SSTI fixes in Utils::isDangerousFunction() 2023-06-13 17:57:11 -06:00
Andy Miller
8c2c1cb726 better SSTI in |map and |filter 2023-06-13 17:45:40 -06:00
Andy Miller
9d01140a63 Fix for dangerous tags in |map filter 2023-06-13 17:07:39 -06:00
Andy Miller
259e775db8 Added languages debug option 2023-06-08 14:50:52 -06:00
Andy Miller
d4c617ff19 Merge branch 'release/1.7.41.2' 2023-06-01 15:19:12 -06:00
Andy Miller
c7680bb50a Merge tag '1.7.41.2' into develop
Release v1.7.41.2
2023-06-01 15:19:12 -06:00
Andy Miller
722ce55ccb prepare for release 2023-06-01 15:18:53 -06:00
Andy Miller
5b950ce73f update changelog 2023-06-01 15:17:47 -06:00
Andy Miller
8dfa2110bf fix for special chars in slugs causing redirect loops 2023-06-01 15:16:56 -06:00
Andy Miller
31aeaf6309 improved the Twig Cache Tag with customizable key (lang specific if needed) 2023-05-23 15:54:48 -06:00
Andy Miller
4de3cab522 Merge tag '1.7.41.1' into develop
Release v1.7.41.1
2023-05-10 08:34:58 -06:00
527 changed files with 12193 additions and 1403 deletions

View File

@@ -10,20 +10,18 @@ permissions:
contents: read # to fetch code (actions/checkout)
jobs:
unit-tests:
strategy:
matrix:
php: ['8.3', '8.2', '8.1', '8.0', '7.4', '7.3']
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
strategy:
matrix:
php: [8.2, 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 }}
@@ -31,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') }}

4
.gitignore vendored
View File

@@ -48,3 +48,7 @@ tests/cache/*
tests/error.log
system/templates/testing/*
/user/config/versions.yaml
/system/recovery.window
tmp/*
#needs_fixing.txt
/AGENTS.md

View File

@@ -1,3 +1,272 @@
# v1.7.50.9
## 11/09/2025
1. [](#improved)
* Better error warnings regarding upgrading from 1.7 -> 1.7 vs 1.7 -> 1.8
1. [](#bugfix)
* Fix for update-provided `Install.php` not used if local version called first
* Fix class loading error when trying to use `bin/gpm self-upgrade --safe`
# v1.7.50.8
## 11/06/2025
1. [](#bugfix)
* Removed over zealous safety checks
* Removed .gitattributes which was causing some unintended issues
# v1.7.50.7
## 11/05/2025
1. [](#improved)
* Exclude dev files from exports
* Remove dev file in clean command
1. [](#bugfix)
* Ignore .github and .phan folders during self-upgrade
* Fixed path check in self-upgrade
# v1.7.50.6
## 11/05/2025
1. [](#bugfix)
* Fixed an issue where non-upgradable root-level folders were snapshotted
# v1.7.50.5
## 11/05/2025
1. [](#new)
* Added new `bin/gpm preflight` command
* Added `--safe` and `--legacy` overrides for `bin/gpm self-upgrade` command
1. [](#improved)
* Improved JS assets pipeline handling to support different loading strategies
* More safe-upgrade fixes around safe guarding `/user/` and maintaining permissions better
1. [](#bugfix)
* Fixed a regex issue that corrupted safe-upgrade output
# v1.7.50.4
## 10/31/2025
1. [](#improved)
* More fixes and improvements for safe-uprade process
# v1.7.50.3
## 10/21/2025
1. [](#bugfix)
* Restored `user/config/system.yaml` to 1.7 branch version (testing mode off)
# v1.7.50.2
## 10/21/2025
1. [](#bugfix)
* Fix for `SafeUpgradeService::getLastManifest()` fatal error on upgrade [#3966](https://github.com/getgrav/grav/issues/3966)
# v1.7.50.1
## 10/20/2025
1. [](#bugfix)
* Fix for broken `GRAV_ROOT`
# v1.7.50
## 10/19/2025
1. [](#new)
* Added new **Safe Core Upgrade** process with snapshots for backup and restore, better preflight and postflight checks, as well as exception checking post-install for easy rollback.
* Introduced recovery mode with token-gated UI, plugin quarantine, and CLI rollback support.
* Added `bin/gpm preflight` compatibility scanner and `bin/gpm rollback` utility.
* Added `wordCount` Twig filter [#3957](https://github.com/getgrav/grav/pulls/3957)
# 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)
# 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
@@ -29,6 +298,7 @@
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

View File

@@ -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,28 +63,28 @@ 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

View File

@@ -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.

209
bin/build-test-update.php Executable file
View File

@@ -0,0 +1,209 @@
#!/usr/bin/env php
<?php
declare(strict_types=1);
use Grav\Common\Filesystem\Folder;
require __DIR__ . '/../vendor/autoload.php';
if (!\defined('GRAV_ROOT')) {
\define('GRAV_ROOT', realpath(__DIR__ . '/..') ?: getcwd());
}
if (!\extension_loaded('zip')) {
fwrite(STDERR, "The PHP zip extension is required.\n");
exit(1);
}
$options = getopt('', [
'version:',
'output::',
'port::',
'base-url::',
'serve',
]);
if (!isset($options['version'])) {
fwrite(
STDERR,
"Usage: php bin/build-test-update.php --version=1.7.999 [--output=tmp/test-gpm] [--port=8043] [--base-url=http://127.0.0.1:8043] [--serve]\n"
);
exit(1);
}
$version = trim((string) $options['version']);
if ($version === '') {
fwrite(STDERR, "A non-empty --version value is required.\n");
exit(1);
}
$root = GRAV_ROOT;
$output = $options['output'] ?? $root . '/tmp/test-gpm';
if (!str_starts_with($output, DIRECTORY_SEPARATOR)) {
$output = $root . '/' . ltrim($output, '/');
}
$output = rtrim($output, DIRECTORY_SEPARATOR);
$defaultPort = isset($options['port']) ? (int) $options['port'] : 8043;
$baseUrl = $options['base-url'] ?? sprintf('http://127.0.0.1:%d', $defaultPort);
$serve = array_key_exists('serve', $options);
Folder::create($output);
$downloadName = sprintf('grav-update-%s.zip', $version);
$zipPath = $output . '/' . $downloadName;
$jsonPath = $output . '/grav.json';
$zipPrefix = 'grav-update/';
$excludeDirs = [
'.build',
'.crush',
'.ddev',
'.git',
'.github',
'.gitlab',
'.circleci',
'.idea',
'.vscode',
'.pytest_cache',
'backup',
'cache',
'images',
'logs',
'node_modules',
'tests',
'tmp',
'user',
];
$excludeFiles = [
'.htaccess',
'.DS_Store',
'robots.txt',
];
$directory = new RecursiveDirectoryIterator($root, RecursiveDirectoryIterator::SKIP_DOTS);
$filtered = new RecursiveCallbackFilterIterator(
$directory,
function (SplFileInfo $current) use ($root, $excludeDirs, $excludeFiles): bool {
$relative = ltrim(str_replace($root, '', $current->getPathname()), DIRECTORY_SEPARATOR);
$relative = str_replace('\\', '/', $relative);
if ($relative === '') {
return true;
}
if (str_contains($relative, '..')) {
return false;
}
foreach ($excludeDirs as $prefix) {
$prefix = trim($prefix, '/');
if ($prefix === '') {
continue;
}
if ($relative === $prefix || str_starts_with($relative, $prefix . '/')) {
return false;
}
}
if (in_array($current->getFilename(), $excludeFiles, true)) {
return false;
}
return true;
}
);
$zip = new ZipArchive();
if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
throw new RuntimeException(sprintf('Unable to open archive at %s', $zipPath));
}
$zip->addEmptyDir($zipPrefix);
$iterator = new RecursiveIteratorIterator($filtered, RecursiveIteratorIterator::SELF_FIRST);
/** @var SplFileInfo $fileInfo */
foreach ($iterator as $fileInfo) {
$fullPath = $fileInfo->getPathname();
$relative = ltrim(str_replace($root, '', $fullPath), DIRECTORY_SEPARATOR);
$relative = str_replace('\\', '/', $relative);
$targetPath = $zipPrefix . $relative;
if ($fileInfo->isDir()) {
$zip->addEmptyDir(rtrim($targetPath, '/') . '/');
continue;
}
if ($fileInfo->isLink()) {
$target = readlink($fullPath);
$zip->addFromString($targetPath, $target === false ? '' : $target);
$zip->setExternalAttributesName($targetPath, ZipArchive::OPSYS_UNIX, 0120000 << 16);
continue;
}
$zip->addFile($fullPath, $targetPath);
$perms = @fileperms($fullPath);
if ($perms !== false) {
$zip->setExternalAttributesName($targetPath, ZipArchive::OPSYS_UNIX, ($perms & 0xFFFF) << 16);
}
}
$zip->close();
$size = filesize($zipPath);
$sha256 = hash_file('sha256', $zipPath);
$timestamp = date('c');
$downloadUrl = rtrim($baseUrl, '/') . '/' . rawurlencode($downloadName);
$manifest = [
'version' => $version,
'date' => $timestamp,
'min_php' => '8.3.0',
'assets' => [
'grav-update' => [
'name' => $downloadName,
'slug' => 'grav-update',
'version' => $version,
'date' => $timestamp,
'testing' => false,
'description' => 'Local test update package generated for safe-upgrade validation.',
'download' => $downloadUrl,
'size' => $size,
'checksum' => 'sha256:' . $sha256,
'sha256' => $sha256,
'host' => parse_url($downloadUrl, PHP_URL_HOST),
],
],
'changelog' => [
$version => [
'date' => $timestamp,
'content' => "- Local test update package generated by build-test-update.\n",
],
],
];
file_put_contents($jsonPath, json_encode($manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL);
$manifestUrl = rtrim($baseUrl, '/') . '/grav.json';
echo "Update package created at: {$zipPath}\n";
echo "Manifest written to: {$jsonPath}\n";
echo "Manifest URL: {$manifestUrl}\n";
echo "Download URL: {$downloadUrl}\n";
echo "Archive size: {$size} bytes\n";
echo "SHA256: {$sha256}\n";
if ($serve) {
$host = parse_url($baseUrl, PHP_URL_HOST) ?: '127.0.0.1';
$port = parse_url($baseUrl, PHP_URL_PORT) ?: $defaultPort;
$command = sprintf('php -S %s:%d -t %s', $host, $port, escapeshellarg($output));
echo "\nServing files using PHP built-in server. Press Ctrl+C to stop.\n";
echo $command . "\n\n";
passthru($command);
}

Binary file not shown.

View File

@@ -2,7 +2,7 @@
<?php
/**
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -2,7 +2,7 @@
<?php
/**
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -2,7 +2,7 @@
<?php
/**
* @copyright Copyright (c) 2015 - 2023 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.
*/

634
bin/restore Executable file
View File

@@ -0,0 +1,634 @@
#!/usr/bin/env php
<?php
/**
* Grav Snapshot Restore Utility
*
* Lightweight CLI that can list and apply safe-upgrade snapshots without
* bootstrapping the full Grav application (or any plugins).
*/
$root = dirname(__DIR__);
define('GRAV_CLI', true);
define('GRAV_REQUEST_TIME', microtime(true));
if (!file_exists($root . '/vendor/autoload.php')) {
fwrite(STDERR, "Unable to locate vendor/autoload.php. Run composer install first.\n");
exit(1);
}
$autoload = require $root . '/vendor/autoload.php';
if (!file_exists($root . '/index.php')) {
fwrite(STDERR, "FATAL: Must be run from Grav root directory.\n");
exit(1);
}
use Grav\Common\Filesystem\Folder;
use Grav\Common\Recovery\RecoveryManager;
use Grav\Common\Upgrade\SafeUpgradeService;
use Symfony\Component\Yaml\Yaml;
const RESTORE_USAGE = <<<USAGE
Grav Restore Utility
Usage:
bin/restore list [--staging-root=/absolute/path]
Lists all available snapshots (most recent first).
bin/restore apply <snapshot-id> [--staging-root=/absolute/path]
Restores the specified snapshot created by safe-upgrade.
bin/restore remove [<snapshot-id> ...] [--staging-root=/absolute/path]
Deletes one or more snapshots (interactive selection when no id provided).
bin/restore snapshot [--label=\"optional description\"] [--staging-root=/absolute/path]
Creates a manual snapshot of the current Grav core files.
bin/restore recovery [status|clear]
Shows the recovery flag context or clears it.
Options:
--staging-root Overrides the staging directory (defaults to configured value).
--label Optional label to store with the manual snapshot.
Examples:
bin/restore list
bin/restore apply stage-68eff31cc4104
bin/restore apply stage-68eff31cc4104 --staging-root=/var/grav-backups
bin/restore snapshot --label=\"Before plugin install\"
bin/restore recovery status
bin/restore recovery clear
USAGE;
/**
* @param array $args
* @return array{command:string,arguments:array,options:array}
*/
function parseArguments(array $args): array
{
array_shift($args); // remove script name
$command = null;
$arguments = [];
$options = [];
while ($args) {
$arg = array_shift($args);
if (strncmp($arg, '--', 2) === 0) {
$parts = explode('=', substr($arg, 2), 2);
$name = $parts[0] ?? '';
if ($name === '') {
continue;
}
$value = $parts[1] ?? null;
if ($value === null && $args && substr($args[0], 0, 2) !== '--') {
$value = array_shift($args);
}
$options[$name] = $value ?? true;
continue;
}
if (null === $command) {
$command = $arg;
} else {
$arguments[] = $arg;
}
}
if (null === $command) {
$command = 'interactive';
}
return [
'command' => $command,
'arguments' => $arguments,
'options' => $options,
];
}
/**
* @param array $options
* @return SafeUpgradeService
*/
function createUpgradeService(array $options): SafeUpgradeService
{
$serviceOptions = ['root' => GRAV_ROOT];
if (isset($options['staging-root']) && is_string($options['staging-root']) && $options['staging-root'] !== '') {
$serviceOptions['staging_root'] = $options['staging-root'];
}
return new SafeUpgradeService($serviceOptions);
}
/**
* @return list<array{id:string,label:?string,source_version:?string,target_version:?string,created_at:int}>
*/
function loadSnapshots(): array
{
$manifestDir = GRAV_ROOT . '/user/data/upgrades';
if (!is_dir($manifestDir)) {
return [];
}
$files = glob($manifestDir . '/*.json') ?: [];
rsort($files);
$snapshots = [];
foreach ($files as $file) {
$decoded = json_decode(file_get_contents($file) ?: '', true);
if (!is_array($decoded) || empty($decoded['id'])) {
continue;
}
$snapshots[] = [
'id' => $decoded['id'],
'label' => $decoded['label'] ?? null,
'source_version' => $decoded['source_version'] ?? null,
'target_version' => $decoded['target_version'] ?? null,
'created_at' => (int)($decoded['created_at'] ?? 0),
];
}
return $snapshots;
}
/**
* @param list<array{id:string,label:?string,source_version:?string,target_version:?string,created_at:int}> $snapshots
* @return string
*/
function formatSnapshotListLine(array $snapshot): string
{
$restoreVersion = $snapshot['source_version'] ?? $snapshot['target_version'] ?? 'unknown';
$timeLabel = formatSnapshotTimestamp($snapshot['created_at']);
$label = $snapshot['label'] ?? null;
$display = $label ? sprintf('%s [%s]', $label, $snapshot['id']) : $snapshot['id'];
return sprintf('%s (restore to Grav %s, %s)', $display, $restoreVersion, $timeLabel);
}
function formatSnapshotTimestamp(int $timestamp): string
{
if ($timestamp <= 0) {
return 'time unknown';
}
try {
$timezone = resolveTimezone();
$dt = new DateTime('@' . $timestamp);
$dt->setTimezone($timezone);
$formatted = $dt->format('Y-m-d H:i:s T');
} catch (\Throwable $e) {
$formatted = date('Y-m-d H:i:s T', $timestamp);
}
return $formatted . ' (' . formatRelative(time() - $timestamp) . ')';
}
function resolveTimezone(): DateTimeZone
{
static $resolved = null;
if ($resolved instanceof DateTimeZone) {
return $resolved;
}
$timezone = null;
$configFile = GRAV_ROOT . '/user/config/system.yaml';
if (is_file($configFile)) {
try {
$data = Yaml::parse(file_get_contents($configFile) ?: '') ?: [];
if (!empty($data['system']['timezone']) && is_string($data['system']['timezone'])) {
$timezone = $data['system']['timezone'];
}
} catch (\Throwable $e) {
// ignore parse errors, fallback below
}
}
if (!$timezone) {
$timezone = ini_get('date.timezone') ?: 'UTC';
}
try {
$resolved = new DateTimeZone($timezone);
} catch (\Throwable $e) {
$resolved = new DateTimeZone('UTC');
}
return $resolved;
}
function formatRelative(int $seconds): string
{
if ($seconds < 5) {
return 'just now';
}
$negative = $seconds < 0;
$seconds = abs($seconds);
$units = [
31536000 => 'y',
2592000 => 'mo',
604800 => 'w',
86400 => 'd',
3600 => 'h',
60 => 'm',
1 => 's',
];
foreach ($units as $size => $label) {
if ($seconds >= $size) {
$value = (int)floor($seconds / $size);
$suffix = $label === 'mo' ? 'month' : ($label === 'y' ? 'year' : ($label === 'w' ? 'week' : ($label === 'd' ? 'day' : ($label === 'h' ? 'hour' : ($label === 'm' ? 'minute' : 'second')))));
if ($value !== 1) {
$suffix .= 's';
}
$phrase = $value . ' ' . $suffix;
return $negative ? 'in ' . $phrase : $phrase . ' ago';
}
}
return $negative ? 'in 0 seconds' : '0 seconds ago';
}
/**
* @param string $snapshotId
* @param array $options
* @return void
*/
function applySnapshot(string $snapshotId, array $options): void
{
try {
$service = createUpgradeService($options);
$manifest = $service->rollback($snapshotId);
} catch (\Throwable $e) {
fwrite(STDERR, "Restore failed: " . $e->getMessage() . "\n");
exit(1);
}
if (!$manifest) {
fwrite(STDERR, "Snapshot {$snapshotId} not found.\n");
exit(1);
}
$version = $manifest['source_version'] ?? $manifest['target_version'] ?? 'unknown';
echo "Restored snapshot {$snapshotId} (Grav {$version}).\n";
if (!empty($manifest['id'])) {
echo "Snapshot manifest: {$manifest['id']}\n";
}
if (!empty($manifest['backup_path'])) {
echo "Snapshot path: {$manifest['backup_path']}\n";
}
exit(0);
}
/**
* @param array $options
* @return void
*/
function createManualSnapshot(array $options): void
{
$label = null;
if (isset($options['label']) && is_string($options['label'])) {
$label = trim($options['label']);
if ($label === '') {
$label = null;
}
}
try {
$service = createUpgradeService($options);
$manifest = $service->createSnapshot($label);
} catch (\Throwable $e) {
fwrite(STDERR, "Snapshot creation failed: " . $e->getMessage() . "\n");
exit(1);
}
$snapshotId = $manifest['id'] ?? null;
if (!$snapshotId) {
$snapshotId = 'unknown';
}
$version = $manifest['source_version'] ?? $manifest['target_version'] ?? 'unknown';
echo "Created snapshot {$snapshotId} (Grav {$version}).\n";
if ($label) {
echo "Label: {$label}\n";
}
if (!empty($manifest['backup_path'])) {
echo "Snapshot path: {$manifest['backup_path']}\n";
}
exit(0);
}
/**
* @param list<array{id:string,source_version:?string,target_version:?string,created_at:int}> $snapshots
* @return string|null
*/
function promptSnapshotSelection(array $snapshots): ?string
{
echo "Available snapshots:\n";
foreach ($snapshots as $index => $snapshot) {
$line = formatSnapshotListLine($snapshot);
$number = $index + 1;
echo sprintf(" [%d] %s\n", $number, $line);
}
$default = $snapshots[0]['id'];
echo "\nSelect a snapshot to restore [1]: ";
$input = trim((string)fgets(STDIN));
if ($input === '') {
return $default;
}
if (ctype_digit($input)) {
$idx = (int)$input - 1;
if (isset($snapshots[$idx])) {
return $snapshots[$idx]['id'];
}
}
foreach ($snapshots as $snapshot) {
if (strcasecmp($snapshot['id'], $input) === 0) {
return $snapshot['id'];
}
}
echo "Invalid selection. Aborting.\n";
return null;
}
/**
* @param list<array{id:string,source_version:?string,target_version:?string,created_at:int}> $snapshots
* @return array<string>
*/
function promptSnapshotsRemoval(array $snapshots): array
{
echo "Available snapshots:\n";
foreach ($snapshots as $index => $snapshot) {
$line = formatSnapshotListLine($snapshot);
$number = $index + 1;
echo sprintf(" [%d] %s\n", $number, $line);
}
echo "\nSelect snapshots to remove (comma or space separated numbers / ids, 'all' for everything, empty to cancel): ";
$input = trim((string)fgets(STDIN));
if ($input === '') {
return [];
}
$inputLower = strtolower($input);
if ($inputLower === 'all' || $inputLower === '*') {
return array_values(array_unique(array_column($snapshots, 'id')));
}
$tokens = preg_split('/[\\s,]+/', $input, -1, PREG_SPLIT_NO_EMPTY) ?: [];
$selected = [];
foreach ($tokens as $token) {
if (ctype_digit($token)) {
$idx = (int)$token - 1;
if (isset($snapshots[$idx])) {
$selected[] = $snapshots[$idx]['id'];
continue;
}
}
foreach ($snapshots as $snapshot) {
if (strcasecmp($snapshot['id'], $token) === 0) {
$selected[] = $snapshot['id'];
break;
}
}
}
return array_values(array_unique(array_filter($selected)));
}
/**
* @param string $snapshotId
* @return array{success:bool,message:string}
*/
function removeSnapshot(string $snapshotId): array
{
$manifestDir = GRAV_ROOT . '/user/data/upgrades';
$manifestPath = $manifestDir . '/' . $snapshotId . '.json';
if (!is_file($manifestPath)) {
return [
'success' => false,
'message' => "Snapshot {$snapshotId} not found."
];
}
$manifest = json_decode(file_get_contents($manifestPath) ?: '', true);
if (!is_array($manifest)) {
return [
'success' => false,
'message' => "Snapshot {$snapshotId} manifest is invalid."
];
}
$pathsToDelete = [];
foreach (['package_path', 'backup_path'] as $key) {
if (!empty($manifest[$key]) && is_string($manifest[$key])) {
$pathsToDelete[] = $manifest[$key];
}
}
$errors = [];
foreach ($pathsToDelete as $path) {
if (!$path) {
continue;
}
if (!file_exists($path)) {
continue;
}
try {
if (is_dir($path)) {
Folder::delete($path);
} else {
@unlink($path);
}
} catch (\Throwable $e) {
$errors[] = "Unable to remove {$path}: " . $e->getMessage();
}
}
if (!@unlink($manifestPath)) {
$errors[] = "Unable to delete manifest file {$manifestPath}.";
}
if ($errors) {
return [
'success' => false,
'message' => implode(' ', $errors)
];
}
return [
'success' => true,
'message' => "Removed snapshot {$snapshotId}."
];
}
$cli = parseArguments($argv);
$command = $cli['command'];
$arguments = $cli['arguments'];
$options = $cli['options'];
switch ($command) {
case 'interactive':
$snapshots = loadSnapshots();
if (!$snapshots) {
echo "No snapshots found. Run bin/gpm self-upgrade (with safe upgrade enabled) to create one.\n";
exit(0);
}
$selection = promptSnapshotSelection($snapshots);
if (!$selection) {
exit(1);
}
applySnapshot($selection, $options);
break;
case 'list':
$snapshots = loadSnapshots();
if (!$snapshots) {
echo "No snapshots found. Run bin/gpm self-upgrade (with safe upgrade enabled) to create one.\n";
exit(0);
}
echo "Available snapshots:\n";
foreach ($snapshots as $snapshot) {
echo ' - ' . formatSnapshotListLine($snapshot) . "\n";
}
exit(0);
case 'remove':
$snapshots = loadSnapshots();
if (!$snapshots) {
echo "No snapshots found. Nothing to remove.\n";
exit(0);
}
$selectedIds = [];
if ($arguments) {
foreach ($arguments as $arg) {
if (!$arg) {
continue;
}
$selectedIds[] = $arg;
}
} else {
$selectedIds = promptSnapshotsRemoval($snapshots);
if (!$selectedIds) {
echo "No snapshots selected. Aborting.\n";
exit(1);
}
}
$selectedIds = array_values(array_unique($selectedIds));
echo "Snapshots selected for removal:\n";
foreach ($selectedIds as $id) {
echo " - {$id}\n";
}
$autoConfirm = isset($options['yes']) || isset($options['y']);
if (!$autoConfirm) {
echo "\nThis action cannot be undone. Proceed? [y/N] ";
$confirmation = strtolower(trim((string)fgets(STDIN)));
if (!in_array($confirmation, ['y', 'yes'], true)) {
echo "Aborted.\n";
exit(1);
}
}
$success = 0;
foreach ($selectedIds as $id) {
$result = removeSnapshot($id);
echo $result['message'] . "\n";
if ($result['success']) {
$success++;
}
}
exit($success > 0 ? 0 : 1);
case 'apply':
$snapshotId = $arguments[0] ?? null;
if (!$snapshotId) {
echo "Missing snapshot id.\n\n" . RESTORE_USAGE . "\n";
exit(1);
}
applySnapshot($snapshotId, $options);
break;
case 'snapshot':
createManualSnapshot($options);
break;
case 'recovery':
$action = strtolower($arguments[0] ?? 'status');
$manager = new RecoveryManager(GRAV_ROOT);
switch ($action) {
case 'clear':
if ($manager->isActive()) {
$manager->clear();
echo "Recovery flag cleared.\n";
} else {
echo "Recovery mode is not active.\n";
}
exit(0);
case 'status':
if (!$manager->isActive()) {
echo "Recovery mode is not active.\n";
exit(0);
}
$context = $manager->getContext();
if (!$context) {
echo "Recovery flag present but context could not be parsed.\n";
exit(1);
}
$created = isset($context['created_at']) ? date('c', (int)$context['created_at']) : 'unknown';
$token = $context['token'] ?? '(missing)';
$message = $context['message'] ?? '(no message)';
$plugin = $context['plugin'] ?? '(none detected)';
$file = $context['file'] ?? '(unknown file)';
$line = $context['line'] ?? '(unknown line)';
echo "Recovery flag context:\n";
echo " Token: {$token}\n";
echo " Message: {$message}\n";
echo " Plugin: {$plugin}\n";
echo " File: {$file}\n";
echo " Line: {$line}\n";
echo " Created: {$created}\n";
$window = $manager->getUpgradeWindow();
if ($window) {
$expires = isset($window['expires_at']) ? date('c', (int)$window['expires_at']) : 'unknown';
$reason = $window['reason'] ?? '(unknown)';
echo " Window: active ({$reason}, expires {$expires})\n";
} else {
echo " Window: inactive\n";
}
exit(0);
default:
echo "Unknown recovery action: {$action}\n\n" . RESTORE_USAGE . "\n";
exit(1);
}
case 'help':
default:
echo RESTORE_USAGE . "\n";
exit($command === 'help' ? 0 : 1);
}

View File

@@ -46,7 +46,7 @@
"filp/whoops": "~2.9",
"matthiasmullie/minify": "^1.3",
"monolog/monolog": "~1.25",
"getgrav/image": "^3.0",
"getgrav/image": "^4.0",
"getgrav/cache": "^2.0",
"donatj/phpuseragentparser": "~1.1",
"pimple/pimple": "~3.5.0",
@@ -55,7 +55,7 @@
"league/climate": "^3.6",
"miljar/php-exif": "^0.6",
"composer/ca-bundle": "^1.2",
"dragonmantank/cron-expression": "^1.2",
"dragonmantank/cron-expression": "^3.3",
"willdurand/negotiation": "^3.0",
"itsgoingd/clockwork": "^5.0",
"symfony/http-client": "^4.4",
@@ -70,8 +70,7 @@
"phpunit/php-code-coverage": "~9.2",
"getgrav/markdowndocs": "^2.0",
"codeception/module-asserts": "^1.3",
"codeception/module-phpbrowser": "^1.0",
"symfony/service-contracts": "*"
"codeception/module-phpbrowser": "^1.0"
},
"replace": {
"symfony/polyfill-php72": "*",
@@ -88,10 +87,7 @@
"ext-exif": "Needed to use exif data from images."
},
"config": {
"apcu-autoloader": true,
"platform": {
"php": "7.3.6"
}
"apcu-autoloader": true
},
"autoload": {
"psr-4": {

1045
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
/**
* @package Grav.Core
*
* @copyright Copyright (c) 2015 - 2023 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.
*/
@@ -20,6 +20,36 @@ if (PHP_SAPI === 'cli-server') {
}
}
if (PHP_SAPI !== 'cli') {
$requestUri = $_SERVER['REQUEST_URI'] ?? '';
$scriptName = $_SERVER['SCRIPT_NAME'] ?? '';
$path = parse_url($requestUri, PHP_URL_PATH) ?? '/';
$path = str_replace('\\', '/', $path);
$scriptDir = str_replace('\\', '/', dirname($scriptName));
if ($scriptDir && $scriptDir !== '/' && $scriptDir !== '.') {
if (strpos($path, $scriptDir) === 0) {
$path = substr($path, strlen($scriptDir));
$path = $path === '' ? '/' : $path;
}
}
if ($path === '/___safe-upgrade-status') {
$statusEndpoint = __DIR__ . '/user/plugins/admin/safe-upgrade-status.php';
header('Content-Type: application/json; charset=utf-8');
if (is_file($statusEndpoint)) {
require $statusEndpoint;
} else {
http_response_code(404);
echo json_encode([
'status' => 'error',
'message' => 'Safe upgrade status endpoint unavailable.',
]);
}
exit;
}
}
// Ensure vendor libraries exist
$autoload = __DIR__ . '/vendor/autoload.php';
if (!is_file($autoload)) {
@@ -29,6 +59,18 @@ if (!is_file($autoload)) {
// Register the auto-loader.
$loader = require $autoload;
if (!class_exists(\Symfony\Component\ErrorHandler\Exception\FlattenException::class, false) && class_exists(\Symfony\Component\HttpKernel\Exception\FlattenException::class)) {
class_alias(\Symfony\Component\HttpKernel\Exception\FlattenException::class, \Symfony\Component\ErrorHandler\Exception\FlattenException::class);
}
if (!class_exists(\Monolog\Logger::class, false)) {
class_exists(\Monolog\Logger::class);
}
if (defined('Monolog\Logger::API') && \Monolog\Logger::API < 3) {
require_once __DIR__ . '/system/src/Grav/Framework/Compat/Monolog/bootstrap.php';
}
// Set timezone to default, falls back to system if php.ini not set
date_default_timezone_set(@date_default_timezone_get());
@@ -36,6 +78,12 @@ date_default_timezone_set(@date_default_timezone_get());
@ini_set('default_charset', 'UTF-8');
mb_internal_encoding('UTF-8');
$recoveryFlag = __DIR__ . '/user/data/recovery.flag';
if (PHP_SAPI !== 'cli' && is_file($recoveryFlag)) {
require __DIR__ . '/system/recovery.php';
return 0;
}
use Grav\Common\Grav;
use RocketTheme\Toolbox\Event\Event;
@@ -46,6 +94,13 @@ $grav = Grav::instance(array('loader' => $loader));
try {
$grav->process();
} catch (\Error|\Exception $e) {
$grav->fireEvent('onFatalException', new Event(array('exception' => $e)));
$grav->fireEvent('onFatalException', new Event(['exception' => $e]));
if (PHP_SAPI !== 'cli' && is_file($recoveryFlag)) {
require __DIR__ . '/system/recovery.php';
return 0;
}
throw $e;
}

View File

@@ -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;
}

View File

@@ -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);
});

File diff suppressed because one or more lines are too long

View File

@@ -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

View File

@@ -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
@@ -620,6 +633,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
@@ -1225,6 +1251,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: '%'
@@ -1287,6 +1323,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
@@ -1540,6 +1598,22 @@ form:
validate:
type: bool
updates_section:
type: section
title: PLUGIN_ADMIN.UPDATES_SECTION
updates.safe_upgrade:
type: toggle
label: PLUGIN_ADMIN.SAFE_UPGRADE
help: PLUGIN_ADMIN.SAFE_UPGRADE_HELP
highlight: 1
default: true
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: bool
http_section:
type: section
title: PLUGIN_ADMIN.HTTP_SECTION
@@ -1854,6 +1928,3 @@ form:
#
# pages.type:
# type: hidden

View File

@@ -140,7 +140,7 @@ form:
multiple: true
size: large
label: PLUGIN_ADMIN.GROUPS
data-options@: 'Grav\Common\Flex\Types\UserGroups\UserGroupObject::groupNames'
data-options@: '\Grav\Common\User\Group::groupNames'
classes: fancy
help: PLUGIN_ADMIN.GROUPS_HELP
validate:

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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 /
@@ -100,6 +101,7 @@ cache:
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.)
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:
@@ -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
@@ -198,6 +203,9 @@ gpm:
releases: stable # Set to either 'stable' or 'testing'
official_gpm_only: true # By default GPM direct-install will only allow URLs via the official GPM proxy to ensure security
updates:
safe_upgrade: true # Enable guarded staging+rollback pipeline for Grav self-updates
http:
method: auto # Either 'curl', 'fopen' or 'auto'. 'auto' will try fopen first and if not available cURL
enable_proxy: true # Enable proxy server configuration

View File

@@ -3,13 +3,13 @@
/**
* @package Grav\Core
*
* @copyright Copyright (c) 2015 - 2023 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.41.1');
define('GRAV_VERSION', '1.7.50.9');
define('GRAV_SCHEMA', '1.7.0_2020-11-20_1');
define('GRAV_TESTING', false);
@@ -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')) {

View File

@@ -2,7 +2,7 @@
/**
* @package Grav\Core
*
* @copyright Copyright (c) 2015 - 2023 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.
*/
@@ -10,6 +10,31 @@ if (!defined('GRAV_ROOT')) {
die();
}
// Check if Install class is already loaded (from an older Grav version)
// This happens when upgrading from older versions where the OLD Install class
// was loaded via autoloader before extracting the update package (e.g., via Install::forceSafeUpgrade())
if (class_exists('Grav\\Installer\\Install', false)) {
// OLD Install class is already loaded. We cannot load the NEW one due to PHP limitations.
// However, we can work around this by:
// 1. Using a different class name for the NEW installer
// 2. Or, accepting that the OLD Install class will run but ensuring it can still upgrade properly
// For now, use the OLD Install class but set its location to this extracted package
// so it processes files from here
$install = Grav\Installer\Install::instance();
// Use reflection to update the location property to point to this package
$reflection = new \ReflectionClass($install);
if ($reflection->hasProperty('location')) {
$locationProp = $reflection->getProperty('location');
$locationProp->setAccessible(true);
$locationProp->setValue($install, __DIR__ . '/..');
}
return $install;
}
// Normal case: Install class not yet loaded, load the NEW one
require_once __DIR__ . '/src/Grav/Installer/Install.php';
return Grav\Installer\Install::instance();

View File

@@ -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: Всеки месец

View File

@@ -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

View File

@@ -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'

View File

@@ -119,3 +119,8 @@ GRAV:
ERROR2: Bad number of elements
ERROR3: The jquery_element should be set into jqCron settings
ERROR4: Unrecognized expression
PLUGIN_ADMIN:
UPDATES_SECTION: Updates
SAFE_UPGRADE: Safe self-upgrade
SAFE_UPGRADE_HELP: When enabled, Grav core updates use staged installation with automatic rollback support.

40
system/languages/eo.yaml Normal file
View 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'
- ''
- ''
- ''
- ''
- ''
- ''
- ''
- ''

View File

@@ -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

View File

@@ -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!

View File

@@ -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:

View File

@@ -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'

View File

@@ -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: ביטוי לא מזוהה

View File

@@ -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

View File

@@ -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

View File

@@ -1,12 +1,12 @@
---
GRAV:
INFLECTOR_UNCOUNTABLE:
- 'equipment'
- ''
- '情報'
- 'rice'
- ''
- 'お金'
- 'species'
- 'series'
- ''
- ''
- '魚'
- 'ヒツジ'
INFLECTOR_IRREGULAR:

View File

@@ -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: 인식할 수 없는 표현

View File

@@ -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
View 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
View 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: အသိအမှတ်မပြုသော အသုံးအနှုန်း

View File

@@ -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

View File

@@ -6,10 +6,10 @@ GRAV:
INFLECTOR_UNCOUNTABLE:
- 'wyposażenie'
- 'informacja'
- 'rice'
- ''
- 'pieniądze'
- 'species'
- 'series'
- ''
- ''
- 'ryba'
- 'owca'
INFLECTOR_IRREGULAR:

View File

@@ -67,7 +67,7 @@ GRAV:
NICETIME:
NO_DATE_PROVIDED: Nenhuma data fornecida
BAD_DATE: Data inválida
AGO: atrás
AGO:
FROM_NOW: a partir de agora
JUST_NOW: mesmo agora
SECOND: segundo

View File

@@ -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:
- 'экипировка'
- 'информация'

View File

@@ -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: හඳුනා නොගත් ප්‍රකාශනය

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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: นิพจน์ที่ไม่รู้จัก

View File

@@ -125,6 +125,8 @@ GRAV:
- '星期五'
- '星期六'
- '星期日'
YES: "是"
NO: "否"
CRON:
EVERY: 每隔
EVERY_HOUR: 每小时

View File

@@ -62,6 +62,8 @@ GRAV:
- '星期五'
- '星期六'
- '星期日'
YES: "是"
NO: "否"
CRON:
EVERY:
EVERY_HOUR: 每小時

View File

@@ -125,6 +125,8 @@ GRAV:
- '星期五'
- '星期六'
- '星期日'
YES: "是"
NO: "否"
CRON:
EVERY: 每隔
EVERY_HOUR: 每小时

202
system/recovery.php Normal file
View File

@@ -0,0 +1,202 @@
<?php
use Grav\Common\Recovery\RecoveryManager;
use Grav\Common\Upgrade\SafeUpgradeService;
if (!\defined('GRAV_ROOT')) {
\define('GRAV_ROOT', dirname(__DIR__));
}
session_start([
'name' => 'grav-recovery',
'cookie_httponly' => true,
'cookie_secure' => !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off',
'cookie_samesite' => 'Lax',
]);
$manager = new RecoveryManager();
$context = $manager->getContext() ?? [];
$token = $context['token'] ?? null;
$authenticated = $token && isset($_SESSION['grav_recovery_authenticated']) && hash_equals($_SESSION['grav_recovery_authenticated'], $token);
$errorMessage = null;
$notice = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
if ($action === 'authenticate') {
$provided = trim($_POST['token'] ?? '');
if ($token && hash_equals($token, $provided)) {
$_SESSION['grav_recovery_authenticated'] = $token;
header('Location: ' . $_SERVER['REQUEST_URI']);
exit;
}
$errorMessage = 'Invalid recovery token.';
} elseif ($authenticated) {
$service = new SafeUpgradeService();
try {
if ($action === 'rollback' && !empty($_POST['manifest'])) {
$service->rollback(trim($_POST['manifest']));
$manager->clear();
$_SESSION['grav_recovery_authenticated'] = null;
$notice = 'Rollback complete. Please reload Grav.';
}
if ($action === 'clear-flag') {
$manager->clear();
$_SESSION['grav_recovery_authenticated'] = null;
$notice = 'Recovery flag cleared.';
}
} catch (\Throwable $e) {
$errorMessage = $e->getMessage();
}
} else {
$errorMessage = 'Authentication required.';
}
}
$quarantineFile = GRAV_ROOT . '/user/data/upgrades/quarantine.json';
$quarantine = [];
if (is_file($quarantineFile)) {
$decoded = json_decode(file_get_contents($quarantineFile), true);
if (is_array($decoded)) {
$quarantine = $decoded;
}
}
$manifestDir = GRAV_ROOT . '/user/data/upgrades';
$snapshots = [];
if (is_dir($manifestDir)) {
$files = glob($manifestDir . '/*.json');
if ($files) {
foreach ($files as $file) {
$decoded = json_decode(file_get_contents($file), true);
if (!is_array($decoded)) {
continue;
}
$id = $decoded['id'] ?? pathinfo($file, PATHINFO_FILENAME);
if (!is_string($id) || $id === '' || strncmp($id, 'snapshot-', 9) !== 0) {
continue;
}
$decoded['id'] = $id;
$decoded['file'] = basename($file);
$decoded['created_at'] = (int)($decoded['created_at'] ?? filemtime($file) ?: 0);
$snapshots[] = $decoded;
}
if ($snapshots) {
usort($snapshots, static function (array $a, array $b): int {
return ($b['created_at'] ?? 0) <=> ($a['created_at'] ?? 0);
});
}
}
}
$latestSnapshot = $snapshots[0] ?? null;
header('Content-Type: text/html; charset=utf-8');
?><!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Grav Recovery Mode</title>
<style>
body { font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif; margin: 0; padding: 40px; background: #111; color: #eee; }
.panel { max-width: 720px; margin: 0 auto; background: #1d1d1f; padding: 24px 32px; border-radius: 12px; box-shadow: 0 10px 45px rgba(0,0,0,0.4); }
h1 { font-size: 2.5rem; margin-top: 0; color: #fff; display:flex;align-items:center; }
h1 > img {margin-right:1rem;}
code { background: rgba(255,255,255,0.08); padding: 2px 4px; border-radius: 4px; }
form { margin-top: 16px; }
input[type="text"] { width: 100%; padding: 10px; border: 1px solid #333; border-radius: 6px; background: #151517; color: #fff; }
button { margin-top: 12px; padding: 10px 16px; border: 0; border-radius: 6px; cursor: pointer; background: #3c8bff; color: #fff; font-weight: 600; }
button.secondary { background: #444; }
.message { padding: 10px 14px; border-radius: 6px; margin-top: 12px; }
.error { background: rgba(220, 53, 69, 0.15); color: #ffb3b8; }
.notice { background: rgba(25, 135, 84, 0.2); color: #bdf8d4; }
ul { padding-left: 20px; }
li { margin-bottom: 8px; }
.card { border: 1px solid #2a2a2d; border-radius: 8px; padding: 14px 16px; margin-top: 16px; background: #161618; }
small { color: #888; }
</style>
</head>
<body>
<div class="panel">
<h1><img src="system/assets/grav.png">Grav Recovery Mode</h1>
<?php if ($notice): ?>
<div class="message notice"><?php echo htmlspecialchars($notice, ENT_QUOTES, 'UTF-8'); ?></div>
<?php endif; ?>
<?php if ($errorMessage): ?>
<div class="message error"><?php echo htmlspecialchars($errorMessage, ENT_QUOTES, 'UTF-8'); ?></div>
<?php endif; ?>
<?php if (!$authenticated): ?>
<p>This site is running in recovery mode because Grav detected a fatal error.</p>
<p>Locate the recovery token in <code>user/data/recovery.flag</code> and enter it below.</p>
<form method="post">
<input type="hidden" name="action" value="authenticate">
<label for="token">Recovery token</label>
<input id="token" name="token" type="text" autocomplete="one-time-code" required>
<button type="submit">Unlock Recovery</button>
</form>
<?php else: ?>
<div class="card">
<h2>Failure Details</h2>
<ul>
<li><strong>Message:</strong> <?php echo htmlspecialchars($context['message'] ?? 'Unknown', ENT_QUOTES, 'UTF-8'); ?></li>
<li><strong>File:</strong> <?php echo htmlspecialchars($context['file'] ?? 'n/a', ENT_QUOTES, 'UTF-8'); ?></li>
<li><strong>Line:</strong> <?php echo htmlspecialchars((string)($context['line'] ?? 'n/a'), ENT_QUOTES, 'UTF-8'); ?></li>
<?php if (!empty($context['plugin'])): ?>
<li><strong>Quarantined plugin:</strong> <?php echo htmlspecialchars($context['plugin'], ENT_QUOTES, 'UTF-8'); ?></li>
<?php endif; ?>
</ul>
</div>
<?php if ($quarantine): ?>
<div class="card">
<h3>Quarantined Plugins</h3>
<ul>
<?php foreach ($quarantine as $entry): ?>
<li>
<strong><?php echo htmlspecialchars($entry['slug'], ENT_QUOTES, 'UTF-8'); ?></strong>
<small>(disabled at <?php echo date('c', $entry['disabled_at']); ?>)</small><br>
<?php echo htmlspecialchars($entry['message'] ?? '', ENT_QUOTES, 'UTF-8'); ?>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<div class="card">
<h3>Rollback</h3>
<?php if ($latestSnapshot): ?>
<form method="post">
<input type="hidden" name="action" value="rollback">
<input type="hidden" name="manifest" value="<?php echo htmlspecialchars($latestSnapshot['id'], ENT_QUOTES, 'UTF-8'); ?>">
<p>
Latest snapshot:
<code><?php echo htmlspecialchars($latestSnapshot['id'], ENT_QUOTES, 'UTF-8'); ?></code>
<?php if (!empty($latestSnapshot['label'])): ?>
<br><small><?php echo htmlspecialchars($latestSnapshot['label'], ENT_QUOTES, 'UTF-8'); ?></small>
<?php endif; ?>
— Grav <?php echo htmlspecialchars($latestSnapshot['target_version'] ?? 'unknown', ENT_QUOTES, 'UTF-8'); ?>
<?php if (!empty($latestSnapshot['created_at'])): ?>
<br><small>Created <?php echo htmlspecialchars(date('c', (int)$latestSnapshot['created_at']), ENT_QUOTES, 'UTF-8'); ?></small>
<?php endif; ?>
</p>
<button type="submit" class="secondary">Rollback to Latest Snapshot</button>
</form>
<?php else: ?>
<p>No upgrade snapshots were found.</p>
<?php endif; ?>
</div>
<form method="post">
<input type="hidden" name="action" value="clear-flag">
<button type="submit" class="secondary">Exit Recovery Mode</button>
</form>
<?php endif; ?>
</div>
</body>
</html>

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Core
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common
*
* @copyright Copyright (c) 2015 - 2023 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.
*/
@@ -398,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'}) {
@@ -461,8 +464,18 @@ class Assets extends PropertyObject
if ($this->{$pipeline_enabled} ?? false) {
$options = array_merge($this->pipeline_options, ['timestamp' => $this->timestamp]);
$pipeline = new Pipeline($options);
$pipeline_output = $pipeline->$render_pipeline($pipeline_assets, $group, $attributes);
$grouped_pipeline_assets = $this->splitPipelineAssetsByAttribute($pipeline_assets, 'loading');
foreach ($grouped_pipeline_assets as $pipeline_group) {
if (empty($pipeline_group['assets'])) {
continue;
}
$group_attributes = array_merge($attributes, $pipeline_group['attributes']);
$pipeline = new Pipeline($options);
$pipeline_output .= $pipeline->$render_pipeline($pipeline_group['assets'], $group, $group_attributes);
}
} else {
foreach ($pipeline_assets as $asset) {
$pipeline_output .= $asset->render();
@@ -589,4 +602,71 @@ class Assets extends PropertyObject
return $base_type;
}
/**
* Split pipeline assets into ordered groups based on the value of a given attribute.
*
* This preserves the original order of the assets while ensuring assets that require
* special handling (such as different loading strategies) are rendered separately.
*
* @param array $assets
* @param string $attribute
* @return array<int, array{assets: array, attributes: array}>
*/
protected function splitPipelineAssetsByAttribute(array $assets, string $attribute): array
{
$groups = [];
$currentAssets = [];
$currentValue = null;
$hasCurrentGroup = false;
foreach ($assets as $key => $asset) {
$value = null;
if (method_exists($asset, 'hasNestedProperty')) {
if ($asset->hasNestedProperty($attribute)) {
$value = $asset->getNestedProperty($attribute);
} elseif ($asset->hasNestedProperty('attributes.' . $attribute)) {
$value = $asset->getNestedProperty('attributes.' . $attribute);
}
}
if ($value === null && isset($asset[$attribute])) {
$value = $asset[$attribute];
}
if ($value === '' || $value === false) {
$value = null;
}
if (!$hasCurrentGroup) {
$currentAssets = [$key => $asset];
$currentValue = $value;
$hasCurrentGroup = true;
continue;
}
if ($value === $currentValue) {
$currentAssets[$key] = $asset;
continue;
}
$groups[] = [
'assets' => $currentAssets,
'attributes' => $currentValue !== null ? [$attribute => $currentValue] : []
];
$currentAssets = [$key => $asset];
$currentValue = $value;
}
if ($hasCurrentGroup) {
$groups[] = [
'assets' => $currentAssets,
'attributes' => $currentValue !== null ? [$attribute => $currentValue] : []
];
}
return $groups;
}
}

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2023 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,7 +278,11 @@ class Pipeline extends PropertyObject
$new_url = ($local ? $this->base_url : '') . $old_url;
return str_replace($matches[2], $new_url, $matches[0]);
if ($isImport) {
return str_replace($matches[5], $new_url, $matches[0]);
} else {
return str_replace($matches[2], $new_url, $matches[0]);
}
}, $file);
return $file;

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets\Traits
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets\Traits
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets\Traits
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Backup
*
* @copyright Copyright (c) 2015 - 2023 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.
*/
@@ -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), '/'));
}

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common
*
* @copyright Copyright (c) 2015 - 2023 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.
*/
@@ -170,24 +170,75 @@ class Cache extends Getters
}
/**
* 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;
@@ -499,6 +550,9 @@ class Cache extends Getters
$anything = true;
}
} elseif (is_dir($file)) {
if (basename($file) === 'grav-snapshots') {
continue;
}
if (Folder::delete($file, false)) {
$anything = true;
}
@@ -632,8 +686,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;

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Data
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Data
*
* @copyright Copyright (c) 2015 - 2023 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.
*/
@@ -196,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) || strpos($otherVal, $currentVal) !== false));
}
// 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);

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Data
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Data
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Data
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Data
*
* @copyright Copyright (c) 2015 - 2023 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.
*/
@@ -48,12 +48,14 @@ class Validation
}
$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 [];
}
@@ -550,7 +552,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));
}
@@ -631,6 +633,10 @@ class Validation
*/
public static function typeEmail($value, array $params, array $field)
{
if (empty($value)) {
return false;
}
if (!isset($params['max'])) {
$params['max'] = 320;
}

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Data
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common
*
* @copyright Copyright (c) 2015 - 2023 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.
*/
@@ -279,11 +279,7 @@ class Debugger
->withHeader('X-Clockwork-Id', $clockworkRequest->id)
->withHeader('X-Clockwork-Version', $clockwork::VERSION);
$grav = Grav::instance();
$basePath = $this->grav['base_url_relative'] . $grav['pages']->base();
if ($basePath) {
$response = $response->withHeader('X-Clockwork-Path', $basePath . '/__clockwork/');
}
$response = $response->withHeader('X-Clockwork-Path', Utils::url('/__clockwork/'));
return $response->withHeader('Server-Timing', ServerTiming::fromRequest($clockworkRequest)->value());
}
@@ -307,7 +303,7 @@ class Debugger
}
$id = $matches['id'] ?? null;
$direction = $matches['direction'] ?? null;
$direction = $matches['direction'] ?? 'latest';
$count = $matches['count'] ?? null;
$storage = $clockwork->getStorage();
@@ -316,7 +312,7 @@ class Debugger
$data = $storage->previous($id, $count);
} elseif ($direction === 'next') {
$data = $storage->next($id, $count);
} elseif ($id === 'latest') {
} elseif ($direction === 'latest' || $id === 'latest') {
$data = $storage->latest();
} else {
$data = $storage->find($id);
@@ -403,8 +399,16 @@ class Debugger
// Clockwork specific assets
if ($this->clockwork) {
$assets->addCss('/system/assets/debugger/clockwork.css', ['loading' => 'inline']);
$assets->addJs('/system/assets/debugger/clockwork.js', ['loading' => 'inline']);
if ($this->config->get('plugins.clockwork-web.enabled')) {
$route = Utils::url($this->grav['config']->get('plugins.clockwork-web.route'));
} else {
$route = 'https://github.com/getgrav/grav-plugin-clockwork-web';
}
$assets->addCss('/system/assets/debugger/clockwork.css');
$assets->addJs('/system/assets/debugger/clockwork.js', [
'id' => 'clockwork-script',
'data-route' => $route
]);
}

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Errors
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Errors
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Errors
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Errors
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\File
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\File
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\File
*
* @copyright Copyright (c) 2015 - 2023 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.
*/

View File

@@ -3,7 +3,7 @@
/**
* @package Grav\Common\File
*
* @copyright Copyright (c) 2015 - 2023 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 files were not shown because too many files have changed in this diff Show More