85 Commits
Author SHA1 Message Date
bachir bd4137e00d updated grav to 1.7.14 2021-05-12 10:33:19 +02:00
bachir 3d5bc71a7c removed core plugins from git tree 2021-05-12 09:34:13 +02:00
bachir 376bac8ee5 added user/pages/.gitkeep to git tree 2021-05-12 09:23:07 +02:00
bachir 62fc885728 fixperms 2021-05-12 09:20:45 +02:00
bachir c1f2c6d606 chmod +x fixperms 2021-05-12 09:16:16 +02:00
bachir f8b2b21c51 added plugin git submodule definition 2021-05-12 08:48:23 +02:00
bachir acbc9e1333 added plugin git submodule definition 2021-05-12 08:45:10 +02:00
bachir bdacbf28a5 Merge branch 'prod' 2021-05-10 16:47:05 +02:00
kevin 4f8532faac add plug 2020-09-08 14:44:34 +02:00
kevin c471d6ad2b install plugin 2020-09-08 14:40:03 +02:00
kevin 29a83739f4 modif ux close 2020-09-08 14:01:19 +02:00
kevin 4a00ce0ead Merge branch 'master' into prod 2020-09-08 12:14:35 +02:00
kevin d9f2562300 maj grav and plugin 2020-09-08 12:12:59 +02:00
kevin 3e5e731c2c merge master in prod 2020-09-08 12:00:45 +02:00
kevin 72aae1eb2d update grav et plugin 2020-09-08 11:32:48 +02:00
kevin 7435174adc modif croix 2020-09-07 20:29:11 +02:00
kevin e9bff067d8 clean 2020-09-07 20:19:53 +02:00
kevin d785c8c1cc Merge branch 'master' of https://figureslibres.io/gogs/kevin/figureslibres.cc into master 2020-09-07 20:19:12 +02:00
kevin bc9407d3f5 merge prod 2020-09-07 20:16:24 +02:00
kevin b925edaa9e underline link txt 2020-09-07 20:15:15 +02:00
kevin b7f459bbb8 burger taille 2020-06-13 12:09:43 +02:00
kevin 3ac48c8344 ajust nav et font 2020-02-03 12:36:04 +01:00
kevin 6e5febb489 ajust nav et font 2020-02-03 12:35:15 +01:00
kevin 9bf38b0b12 ajust nav et font 2020-02-03 12:33:59 +01:00
kevin cee8b98df2 ajust nav et font 2020-02-03 12:31:08 +01:00
kevin 7236cd63ed bold et saut de ligne 2020-01-24 10:50:54 +01:00
kevin 41506c9c4b bold et saut de ligne 2020-01-24 10:49:28 +01:00
kevin 3b41d52917 bold et saut de ligne 2020-01-24 10:47:35 +01:00
kevin 21c038bfbf bold et saut de ligne 2020-01-24 10:45:43 +01:00
kevin 8625b63df9 bold et saut de ligne 2020-01-24 10:44:15 +01:00
kevin 76004032f6 mgr strong 2020-01-22 19:03:58 +01:00
kevin 81142a8166 mgr strong 2020-01-22 19:03:31 +01:00
kevin 5f75b95822 mgr strong 2020-01-22 19:02:06 +01:00
kevin 00ff61237a txt fontfamily 2020-01-22 19:01:08 +01:00
kevin d69cc34fea underline txt 2020-01-22 18:41:28 +01:00
kevin bd8883b390 Merge branch 'prod' of CT-figureslibres.cc:git-repos/figureslibres.cc into prod 2020-01-20 12:34:55 +01:00
kevin 6b3e0f2c44 test commit prod 2020-01-20 12:34:20 +01:00
Linux User cf35edb0c2 Merge branch 'prod' of /home/webdev/git-repos/figureslibres.cc 2020-01-20 12:26:27 +01:00
Linux User 56c3ba40ab accounts and plugin/problems inline 2020-01-20 12:25:59 +01:00
kevin 7d1cc59413 change gitignore 2020-01-20 12:16:29 +01:00
kevin b81be5ec03 modif css 2020-01-20 11:51:20 +01:00
kevin 7ca69e1549 menu 2019-12-03 19:09:35 +01:00
bachir c31a4942c0 updated account 2019-10-13 22:21:51 +02:00
kevin fd56b2a0f4 add users 2019-10-13 21:50:19 +02:00
kevin f28cc01e82 add plugins 2019-10-13 21:45:30 +02:00
kevin 4eac59dd5f Merge branch 'master' of https://figureslibres.io/gogs/kevin/figureslibres.cc 2019-10-13 21:43:36 +02:00
bachir 8cda072968 updated theme to last version 2019-10-13 20:55:47 +02:00
kevin 8d9a1543dc update_theming 2019-10-13 19:23:05 +02:00
kevin 1fc0590e4b Merge branch 'master' of https://figureslibres.io/gogs/kevin/figureslibres.cc 2019-10-13 19:17:28 +02:00
kevin 80ff067b38 maj 2019-10-13 19:16:33 +02:00
bachir 0dd3cc77a7 fixed fixperms 2019-10-13 17:43:44 +02:00
bachir c475d0e5bc fixed fixperms 2019-10-13 17:19:21 +02:00
bachir efbece443e fixed fixperms 2019-10-13 17:14:29 +02:00
bachir 2d61a4adad fixed fixperms 2019-10-13 17:06:52 +02:00
bachir 84c3fe2c49 fixed fixperms 2019-10-13 16:57:40 +02:00
bachir cf1111cd46 test 2019-10-13 16:51:16 +02:00
bachir e94774b849 added fixperms script 2019-10-13 16:49:22 +02:00
bachir 2987210451 improved .gitignore 2019-10-13 15:55:43 +02:00
bachir c3e46357a3 added tmp to git ignore 2019-10-13 15:33:16 +02:00
kevin a36123b071 ajust close item and loader ajax 2019-10-10 18:32:07 +02:00
kevin c28778eb23 responsive item 2019-10-10 17:13:31 +02:00
kevin 78c3a80a65 add scrolltop 2019-10-09 22:26:28 +02:00
kevin 588d796db5 load ajax + anchorfixed 2019-10-07 23:02:29 +02:00
kevin ba59f31741 loader ajax 2019-10-06 01:13:05 +02:00
kevin 8a48a6ac0f add lazyload 2019-09-27 17:22:14 +02:00
kevin b857814072 maj grav 2019-09-27 00:29:18 +02:00
kevin 40af43681e ajaxbeta 2019-09-25 15:05:50 +02:00
kevin af7ca72760 ajax 2019-09-04 18:36:55 +02:00
kevin 8c9e1bc509 ajax 2019-07-16 01:03:26 +02:00
kevin c66917c9a0 update + add list_taxonomy 2019-02-21 04:02:36 +01:00
bachir 3791a9f435 added fixeperms script 2019-02-12 16:22:03 +01:00
kevin 6222addd9c html 2019-01-30 13:44:14 +01:00
kevin fd2eac3dc4 add content + modif html 2019-01-30 12:24:08 +01:00
kevin 858bff0f81 add scss 2018-12-18 18:37:47 +01:00
kevin e955181216 Merge branch 'template'
Test

C
C

C
.
2018-12-18 13:09:04 +01:00
kevin 8341b9dab9 add proj 2018-12-17 15:14:14 +01:00
kevin 287a7ccd0c maj 2018-12-17 14:43:33 +01:00
kevin 111b575321 suppr footer 2018-12-17 14:38:34 +01:00
kevin 206d1fb0d0 change config 2018-12-14 15:02:14 +01:00
kevin ed77d6eb2b add sidebar 2018-12-14 14:57:42 +01:00
kevin 6accae1acd test_modif_template 2018-12-14 11:27:29 +01:00
kevin 7541bbacc6 maj 2018-12-14 11:20:31 +01:00
kevin 6f5eca2799 add projets 2018-12-14 10:29:18 +01:00
kevin 5fed785d86 first commit 2018-12-12 09:17:41 +01:00
kevin 8b139acd6e first commit 2018-12-12 07:32:23 +01:00
2223 changed files with 82323 additions and 282027 deletions
-1
View File
@@ -25,7 +25,6 @@ user/data/*
!user/data/.* !user/data/.*
user/themes/quark/ user/themes/quark/
!user/themes/quark/.* !user/themes/quark/.*
user/themes/figureslibres/*
user/pages/* user/pages/*
!user/pages/.* !user/pages/.*
user/localhost/config/security.yaml user/localhost/config/security.yaml
+80
View File
@@ -0,0 +1,80 @@
<IfModule mod_rewrite.c>
RewriteEngine On
## Begin RewriteBase
# If you are getting 500 or 404 errors on subpages, you may have to uncomment the RewriteBase entry
# You should change the '/' to your appropriate subfolder. For example if you have
# your Grav install at the root of your site '/' should work, else it might be something
# along the lines of: RewriteBase /<your_sub_folder>
##
# RewriteBase /
## End - RewriteBase
## Begin - X-Forwarded-Proto
# In some hosted or load balanced environments, SSL negotiation happens upstream.
# In order for Grav to recognize the connection as secure, you need to uncomment
# the following lines.
#
# RewriteCond %{HTTP:X-Forwarded-Proto} https
# RewriteRule .* - [E=HTTPS:on]
#
## End - X-Forwarded-Proto
## Begin - Exploits
# If you experience problems on your site block out the operations listed below
# This attempts to block the most common type of exploit `attempts` to Grav
#
# Block out any script trying to base64_encode data within the URL.
RewriteCond %{QUERY_STRING} base64_encode[^(]*\([^)]*\) [OR]
# Block out any script that includes a <script> tag in URL.
RewriteCond %{QUERY_STRING} (<|%3C)([^s]*s)+cript.*(>|%3E) [NC,OR]
# Block out any script trying to set a PHP GLOBALS variable via URL.
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
# Block out any script trying to modify a _REQUEST variable via URL.
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2})
# Return 403 Forbidden header and show the content of the root homepage
RewriteRule .* index.php [F]
#
## End - Exploits
## Begin - Index
# If the requested path and file is not /index.php and the request
# has not already been internally rewritten to the index.php script
RewriteCond %{REQUEST_URI} !^/index\.php
# and the requested path and file doesn't directly match a physical file
RewriteCond %{REQUEST_FILENAME} !-f
# and the requested path and file doesn't directly match a physical folder
RewriteCond %{REQUEST_FILENAME} !-d
# internally rewrite the request to the index.php script
RewriteRule .* index.php [L]
## End - Index
## Begin - Security
# Block all direct access for these folders
RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F]
# Block access to specific file types for these system folders
RewriteRule ^(system|vendor)/(.*)\.(txt|xml|md|html|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
# Block access to specific file types for these user folders
RewriteRule ^(user)/(.*)\.(txt|md|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
# Block all direct access to .md files:
RewriteRule \.md$ error [F]
# Block all direct access to files and folders beginning with a dot
RewriteRule (^|/)\.(?!well-known) - [F]
# Block access to specific files in the root folder
RewriteRule ^(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess)$ error [F]
## End - Security
</IfModule>
# Begin - Prevent Browsing and Set Default Resources
Options -Indexes
DirectoryIndex index.php index.html index.htm
# End - Prevent Browsing and Set Default Resources
# Redirect http://figureslibres.kevintessier.net/ https://figureslibres.kevintessier.net/
Redirect 301 /sitemap /sitemap.xml
-175
View File
@@ -1,178 +1,3 @@
# v1.7.25
## 11/16/2021
1. [](#new)
* Updated phpstan to v1.0
* Added `FlexObject::getDiff()` to see difference to the saved object
2. [](#improved)
* Use Symfony `dump` instead of PHP's `vardump` in side the `{{ vardump(x) }}` Twig vardump function
* Added `route` and `request` to `onPagesInitialized` event
* Improved page cloning, added method `Page::initialize()`
* Improved `FlexObject::getChanges()`: return changed lists and arrays as whole instead of just changed keys/values
* Improved form validation JSON responses to contain list of failed fields with their error messages
* Improved redirects: send redirect response in JSON if the request was in JSON
3. [](#bugfix)
* Fixed path traversal vulnerability when using `bin/grav server`
* Fixed unescaped error messages in JSON error responses
* Fixed `|t(variable)` twig filter in admin
* Fixed `FlexObject::getChanges()` always returning empty array
* Fixed form validation exceptions to use `400 Bad Request` instead of `500 Internal Server Error`
# v1.7.24
## 10/26/2021
1. [](#new)
* Added support for image watermarks
* Added support to disable a form, making it readonly
2. [](#improved)
* Flex `$user->authorize()` now checks user groups before `admin.super`, allowing deny rules to work properly
3. [](#bugfix)
* Fixed a bug in `PermissionsReader` in PHP 7.3
* Fixed `session_store_active` language option (#3464)
* Fixed deprecated warnings on `ArrayAccess` in PHP 8.1
* Fixed XSS detection with `&colon;`
# v1.7.23
## 09/29/2021
1. [](#new)
* Added method `Pages::referrerRoute()` to get the referrer route and language
* Added true unique `Utils::uniqueId()` / `{{ unique_id() }}` utilities with length, prefix, and suffix support
* Added `UserObject::isMyself()` method to check if flex user is currently logged in
* Added support for custom form field options validation with `validate: options: key|ignore`
2. [](#improved)
* Replaced GPL `SVG-Sanitizer` with MIT licensed `DOM-Sanitizer`
* `Uri::referrer()` now accepts third parameter, if set to `true`, it returns route without base or language code [#3411](https://github.com/getgrav/grav/issues/3411)
* Updated vendor libs with latest
* Updated with latest language strings via Crowdin.com
3. [](#bugfix)
* Fixed `Folder::move()` throwing an error when target folder is changed by only appending characters to the end [#3445](https://github.com/getgrav/grav/issues/3445)
* Fixed some phpstan issues (all code back to level 1, Framework level 3)
* Fixed form reset causing image uploads to fail when using Flex
# v1.7.22
## 09/16/2021
1. [](#new)
* Register plugin autoloaders into plugin objects
2. [](#improved)
* Improve Twig 2 compatibility
* Update to customized version of Twig DeferredExtension (Twig 1/2 compatible)
3. [](#bugfix)
* Fixed conflicting `$_original` variable in `Flex Pages`
# v1.7.21
## 09/14/2021
1. [](#new)
* Added `|yaml` filter to convert input to YAML
* Added `route` and `request` to `onPageNotFound` event
* Added file upload/remove support for `Flex Forms`
* Added support for `flex-required@: not exists` and `flex-required@: '!exists'` in blueprints
* Added `$object->getOriginalData()` to get flex objects data before it was modified with `update()`
* Throwing exceptions from Twig templates fires `onDisplayErrorPage.[code]` event allowing better error pages
2. [](#improved)
* Use a simplified text-based `cron` field for scheduler
* Add timestamp to logging output of scheduler jobs to see when they ran
3. [](#bugfix)
* Fixed escaping in PageIndex::getLevelListing()
* Fixed validation of `number` type [#3433](https://github.com/getgrav/grav/issues/3433)
* Fixed excessive `security.yaml` file creation [#3432](https://github.com/getgrav/grav/issues/3432)
* Fixed incorrect port :0 with nginx unix socket setup [#3439](https://github.com/getgrav/grav/issues/3439)
* Fixed `Session::setFlashCookieObject()` to use the same options as the main session cookie
# v1.7.20
## 09/01/2021
2. [](#improved)
* Added support for `task` and `action` inside JSON request body
# v1.7.19
## 08/31/2021
1. [](#new)
* Include active form and request in `onPageTask` and `onPageAction` events (defaults to `null`)
* Added `UserObject::$authorizeCallable` to allow `$user->authorize()` customization
2. [](#improved)
* Added meta support for `UploadedFile` class
* Added support for multiple mime-types per file extension [#3422](https://github.com/getgrav/grav/issues/3422)
* Added `setCurrent()` method to Page Collection [#3398](https://github.com/getgrav/grav/pull/3398)
* Initialize `$grav['uri']` before session
3. [](#bugfix)
* Fixed `Warning: Undefined array key "SERVER_SOFTWARE" in index.php` [#3408](https://github.com/getgrav/grav/issues/3408)
* Fixed error in `loadDirectoryConfig()` if configuration hasn't been saved [#3409](https://github.com/getgrav/grav/issues/3409)
* Fixed GPM not using non-standard cache path [#3410](https://github.com/getgrav/grav/issues/3410)
* Fixed broken `environment://` stream when it doesn't have configuration
* Fixed `Flex Object` missing key field value when using `FolderStorage`
* Fixed broken Twig try tag when catch has not been defined or is empty
* Fixed `FlexForm` serialization
* Fixed form validation for numeric values in PHP 8
* Fixed `flex-options@` in blueprints duplicating items in array
* Fixed wrong form issue with flex objects after cache clear
* Fixed Flex object types not implementing `MediaInterface`
* Fixed issue with `svgImageFunction()` that was causing broken output
# v1.7.18
## 07/19/2021
1. [](#improved)
* Added support for loading Flex Directory configuration from main configuration
* Move SVGs that cannot be sanitized to quarantine folder under `log://quarantine`
* Added support for CloudFlare-forwarded client IP in the `URI::ip()` method
1. [](#bugfix)
* Fixed error when using Flex `SimpleStorage` with no entries
* Fixed page search to include slug field [#3316](https://github.com/getgrav/grav/issues/3316)
* Fixed Admin becoming unusable when GPM cannot be reached [#3383](https://github.com/getgrav/grav/issues/3383)
* Fixed `Failed to save entry: Forbidden` when moving a page to a visible page [#3389](https://github.com/getgrav/grav/issues/3389)
* Better support for Symfony local server on linux [#3400](https://github.com/getgrav/grav/pull/3400)
* Fixed `open_basedir()` error with some forms
# v1.7.17
## 06/15/2021
1. [](#new)
* Interface `FlexDirectoryInterface` now extends `FlexAuthorizeInterface`
1. [](#improved)
* Allow to unset an asset attribute by specifying null (ie, `'defer': null`)
* Support specifying custom attributes to assets in a collection [Read more](https://learn.getgrav.org/17/themes/asset-manager#collections-with-attributes?target=_blank) [#3358](https://github.com/getgrav/grav/issues/3358)
* File `frontmatter.yaml` isn't part of media, ignore it
* Switched default `JQuery` collection to use 3.x rather than 2.x
1. [](#bugfix)
* Fixed missing styles when CSS/JS Pipeline is used and `asset://` folder is missing
* Fixed permission check when moving a page [#3382](https://github.com/getgrav/grav/issues/3382)
# v1.7.16
## 06/02/2021
1. [](#new)
* Added 'addFrame()' method to ImageMedium [#3323](https://github.com/getgrav/grav/pull/3323)
1. [](#improved)
* Set `cache.clear_images_by_default` to `false` by default
* Improve error on bad nested form data [#3364](https://github.com/getgrav/grav/issues/3364)
1. [](#bugfix)
* Improve Plugin and Theme initialization to fix PHP8 bug [#3368](https://github.com/getgrav/grav/issues/3368)
* Fixed `pathinfo()` twig filter in PHP7
* Fixed the first visible child page getting ordering number `999999.` [#3365](https://github.com/getgrav/grav/issues/3365)
* Fixed flex pages search using only folder name [#3316](https://github.com/getgrav/grav/issues/3316)
* Fixed flex pages using wrong type in `onBlueprintCreated` event [#3157](https://github.com/getgrav/grav/issues/3157)
* Fixed wrong SRI paths invoked when Grav instance as a sub folder [#3358](https://github.com/getgrav/grav/issues/3358)
* Fixed SRI trying to calculate remote assets, only ever set integrity for local files. Use the SRI provided by the remote source and manually add it in the `addJs/addCss` call for remote support. [#3358](https://github.com/getgrav/grav/issues/3358)
* Fix for weird regex issue with latest PHP versions on Intel Macs causing params to not parse properly in URI object
# v1.7.15
## 05/19/2021
1. [](#improved)
* Allow optional start date in page collections [#3350](https://github.com/getgrav/grav/pull/3350)
* Added `page` and `output` properties to `onOutputGenerated` and `onOutputRendered` events
1. [](#bugfix)
* Fixed twig deprecated TwigFilter messages [#3348](https://github.com/getgrav/grav/issues/3348)
* Fixed fatal error with some markdown links [getgrav/grav-premium-issues#95](https://github.com/getgrav/grav-premium-issues/issues/95)
* Fixed markdown media operations not working when using `image://` stream [#3333](https://github.com/getgrav/grav/issues/3333) [#3349](https://github.com/getgrav/grav/issues/3349)
* Fixed copying page without changing the slug [getgrav/grav-plugin-admin#2135](https://github.com/getgrav/grav-plugin-admin/issues/2139)
* Fixed missing and commonly used methods when using `system.twig.undefined_functions = false` [getgrav/grav-plugin-admin#2138](https://github.com/getgrav/grav-plugin-admin/issues/2138)
* Fixed uploading images into Flex Object if field destination is not set
# v1.7.14 # v1.7.14
## 04/29/2021 ## 04/29/2021
+1
View File
@@ -1,6 +1,7 @@
# ![](https://avatars1.githubusercontent.com/u/8237355?v=2&s=50) Grav # ![](https://avatars1.githubusercontent.com/u/8237355?v=2&s=50) Grav
[![PHPStan](https://img.shields.io/badge/PHPStan-enabled-brightgreen.svg?style=flat)](https://github.com/phpstan/phpstan) [![PHPStan](https://img.shields.io/badge/PHPStan-enabled-brightgreen.svg?style=flat)](https://github.com/phpstan/phpstan)
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/cfd20465-d0f8-4a0a-8444-467f5b5f16ad/mini.png)](https://insight.sensiolabs.com/projects/cfd20465-d0f8-4a0a-8444-467f5b5f16ad)
[![Discord](https://img.shields.io/discord/501836936584101899.svg?logo=discord&colorB=728ADA&label=Discord%20Chat)](https://chat.getgrav.org) [![Discord](https://img.shields.io/discord/501836936584101899.svg?logo=discord&colorB=728ADA&label=Discord%20Chat)](https://chat.getgrav.org)
[![PHP Tests](https://github.com/getgrav/grav/workflows/PHP%20Tests/badge.svg?branch=develop)](https://github.com/getgrav/grav/actions?query=workflow%3A%22PHP+Tests%22) [![OpenCollective](https://opencollective.com/grav/backers/badge.svg)](#backers) [![OpenCollective](https://opencollective.com/grav/sponsors/badge.svg)](#sponsors) [![PHP Tests](https://github.com/getgrav/grav/workflows/PHP%20Tests/badge.svg?branch=develop)](https://github.com/getgrav/grav/actions?query=workflow%3A%22PHP+Tests%22) [![OpenCollective](https://opencollective.com/grav/backers/badge.svg)](#backers) [![OpenCollective](https://opencollective.com/grav/sponsors/badge.svg)](#sponsors)
+1 -1
View File
@@ -1 +1 @@
/* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. */ /* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. */
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
+11 -12
View File
@@ -20,10 +20,9 @@
"ext-dom": "*", "ext-dom": "*",
"ext-libxml": "*", "ext-libxml": "*",
"symfony/polyfill-mbstring": "~1.20", "symfony/polyfill-mbstring": "~1.20",
"symfony/polyfill-iconv": "^1.23", "symfony/polyfill-iconv": "^1.20",
"symfony/polyfill-php74": "^1.23", "symfony/polyfill-php74": "^1.20",
"symfony/polyfill-php80": "^1.23", "symfony/polyfill-php80": "^1.20",
"symfony/polyfill-php81": "^1.23",
"psr/simple-cache": "^1.0", "psr/simple-cache": "^1.0",
"psr/http-message": "^1.0", "psr/http-message": "^1.0",
"psr/http-server-middleware": "^1.0", "psr/http-server-middleware": "^1.0",
@@ -56,16 +55,17 @@
"miljar/php-exif": "^0.6", "miljar/php-exif": "^0.6",
"composer/ca-bundle": "^1.2", "composer/ca-bundle": "^1.2",
"dragonmantank/cron-expression": "^1.2", "dragonmantank/cron-expression": "^1.2",
"phive/twig-extensions-deferred": "^1.0",
"willdurand/negotiation": "^3.0", "willdurand/negotiation": "^3.0",
"itsgoingd/clockwork": "^5.0", "itsgoingd/clockwork": "^5.0",
"enshrined/svg-sanitize": "~0.13",
"symfony/http-client": "^4.4", "symfony/http-client": "^4.4",
"composer/semver": "^1.4", "composer/semver": "^1.4"
"rhukster/dom-sanitizer": "^1.0"
}, },
"require-dev": { "require-dev": {
"codeception/codeception": "^4.1", "codeception/codeception": "^4.1",
"phpstan/phpstan": "^1.0", "phpstan/phpstan": "^0.12",
"phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-deprecation-rules": "^0.12",
"phpunit/php-code-coverage": "~9.2", "phpunit/php-code-coverage": "~9.2",
"getgrav/markdowndocs": "^2.0", "getgrav/markdowndocs": "^2.0",
"codeception/module-asserts": "^1.3", "codeception/module-asserts": "^1.3",
@@ -93,8 +93,7 @@
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Grav\\": "system/src/Grav", "Grav\\": "system/src/Grav"
"Twig\\": "system/src/Twig"
}, },
"files": [ "files": [
"system/defines.php" "system/defines.php"
@@ -108,8 +107,8 @@
"scripts": { "scripts": {
"api-17": "vendor/bin/phpdoc-md generate system/src > user/pages/14.api/default.17.md", "api-17": "vendor/bin/phpdoc-md generate system/src > user/pages/14.api/default.17.md",
"post-create-project-cmd": "bin/grav install", "post-create-project-cmd": "bin/grav install",
"phpstan": "vendor/bin/phpstan analyse -l 1 -c ./tests/phpstan/phpstan.neon --memory-limit=520M system/src", "phpstan": "vendor/bin/phpstan analyse -l 1 -c ./tests/phpstan/phpstan.neon --memory-limit=480M system/src",
"phpstan-framework": "vendor/bin/phpstan analyse -l 3 -c ./tests/phpstan/phpstan.neon --memory-limit=480M system/src/Grav/Framework system/src/Grav/Events system/src/Grav/Installer", "phpstan-framework": "vendor/bin/phpstan analyse -l 1 -c ./tests/phpstan/phpstan.neon --memory-limit=480M system/src/Grav/Framework system/src/Grav/Events system/src/Grav/Installer",
"phpstan-plugins": "vendor/bin/phpstan analyse -l 1 -c ./tests/phpstan/plugins.neon --memory-limit=400M user/plugins", "phpstan-plugins": "vendor/bin/phpstan analyse -l 1 -c ./tests/phpstan/plugins.neon --memory-limit=400M user/plugins",
"test": "vendor/bin/codecept run unit", "test": "vendor/bin/codecept run unit",
"test-windows": "vendor\\bin\\codecept run unit" "test-windows": "vendor\\bin\\codecept run unit"
Generated
+454 -644
View File
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -1,8 +1,7 @@
#!/bin/sh #!/bin/sh
chown -R webdev:www-data . chown -R 1000:www-data .
find . -type f -exec chmod 664 {} \; find . -type f -exec chmod 664 {} \;
find ./bin -type f -exec chmod 775 {} \; find ./bin -type f -exec chmod 775 {} \;
find . -type d -exec chmod 775 {} \; find . -type d -exec chmod 775 {} \;
find . -type d -exec chmod +s {} \; find . -type d -exec chmod +s {} \;
chmod +x ./fixperms.sh chmod +x ./fixperms.sh
#
+2 -2
View File
@@ -17,8 +17,8 @@ if (version_compare($ver = PHP_VERSION, $req = GRAV_PHP_MIN, '<')) {
} }
if (PHP_SAPI === 'cli-server') { if (PHP_SAPI === 'cli-server') {
$symfony_server = stripos(getenv('_'), 'symfony') !== false || stripos($_SERVER['SERVER_SOFTWARE'] ?? '', 'symfony') !== false || stripos($_ENV['SERVER_SOFTWARE'] ?? '', 'symfony') !== false; $symfony_server = stripos(getenv('_'), 'symfony') !== false || stripos($_SERVER['SERVER_SOFTWARE'], 'symfony
') !== false;
if (!isset($_SERVER['PHP_CLI_ROUTER']) && !$symfony_server) { if (!isset($_SERVER['PHP_CLI_ROUTER']) && !$symfony_server) {
die("PHP webserver requires a router to run Grav, please use: <pre>php -S {$_SERVER['SERVER_NAME']}:{$_SERVER['SERVER_PORT']} system/router.php</pre>"); die("PHP webserver requires a router to run Grav, please use: <pre>php -S {$_SERVER['SERVER_NAME']}:{$_SERVER['SERVER_PORT']} system/router.php</pre>");
} }
+1 -2
View File
@@ -47,8 +47,7 @@ form:
label: PLUGIN_ADMIN.EXTRA_ARGUMENTS label: PLUGIN_ADMIN.EXTRA_ARGUMENTS
placeholder: '-lah' placeholder: '-lah'
.at: .at:
type: text type: cron
wrapper_classes: cron-selector
label: PLUGIN_ADMIN.SCHEDULER_RUNAT label: PLUGIN_ADMIN.SCHEDULER_RUNAT
help: PLUGIN_ADMIN.SCHEDULER_RUNAT_HELP help: PLUGIN_ADMIN.SCHEDULER_RUNAT_HELP
placeholder: '* * * * *' placeholder: '* * * * *'
+21 -71
View File
@@ -646,7 +646,7 @@ form:
type: toggle type: toggle
label: PLUGIN_ADMIN.CLEAR_IMAGES_BY_DEFAULT label: PLUGIN_ADMIN.CLEAR_IMAGES_BY_DEFAULT
help: PLUGIN_ADMIN.CLEAR_IMAGES_BY_DEFAULT_HELP help: PLUGIN_ADMIN.CLEAR_IMAGES_BY_DEFAULT_HELP
highlight: 0 highlight: 1
options: options:
1: PLUGIN_ADMIN.YES 1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO 0: PLUGIN_ADMIN.NO
@@ -1446,10 +1446,6 @@ form:
title: PLUGIN_ADMIN.ADVANCED title: PLUGIN_ADMIN.ADVANCED
underline: true underline: true
gpm_section:
type: section
title: PLUGIN_ADMIN.GPM_SECTION
gpm.releases: gpm.releases:
type: toggle type: toggle
label: PLUGIN_ADMIN.GPM_RELEASES label: PLUGIN_ADMIN.GPM_RELEASES
@@ -1459,6 +1455,23 @@ form:
stable: PLUGIN_ADMIN.STABLE stable: PLUGIN_ADMIN.STABLE
testing: PLUGIN_ADMIN.TESTING testing: PLUGIN_ADMIN.TESTING
gpm.proxy_url:
type: text
size: medium
placeholder: "e.g. 127.0.0.1:3128"
label: PLUGIN_ADMIN.PROXY_URL
help: PLUGIN_ADMIN.PROXY_URL_HELP
gpm.method:
type: toggle
label: PLUGIN_ADMIN.GPM_METHOD
highlight: auto
help: PLUGIN_ADMIN.GPM_METHOD_HELP
options:
auto: PLUGIN_ADMIN.AUTO
fopen: PLUGIN_ADMIN.FOPEN
curl: PLUGIN_ADMIN.CURL
gpm.official_gpm_only: gpm.official_gpm_only:
type: toggle type: toggle
label: PLUGIN_ADMIN.GPM_OFFICIAL_ONLY label: PLUGIN_ADMIN.GPM_OFFICIAL_ONLY
@@ -1471,80 +1484,17 @@ form:
validate: validate:
type: bool type: bool
http_section: gpm.verify_peer:
type: section
title: PLUGIN_ADMIN.HTTP_SECTION
http.method:
type: toggle type: toggle
label: PLUGIN_ADMIN.GPM_METHOD label: PLUGIN_ADMIN.GPM_VERIFY_PEER
highlight: auto
help: PLUGIN_ADMIN.GPM_METHOD_HELP
options:
auto: PLUGIN_ADMIN.AUTO
fopen: PLUGIN_ADMIN.FOPEN
curl: PLUGIN_ADMIN.CURL
http.enable_proxy:
type: toggle
label: PLUGIN_ADMIN.SSL_ENABLE_PROXY
highlight: 1 highlight: 1
options: help: PLUGIN_ADMIN.GPM_VERIFY_PEER_HELP
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
default: false
validate:
type: bool
http.proxy_url:
type: text
size: medium
placeholder: "e.g. 127.0.0.1:3128"
label: PLUGIN_ADMIN.PROXY_URL
help: PLUGIN_ADMIN.PROXY_URL_HELP
http.proxy_cert_path:
type: text
size: medium
placeholder: "e.g. /Users/bob/certs/"
label: PLUGIN_ADMIN.PROXY_CERT
help: PLUGIN_ADMIN.PROXY_CERT_HELP
http.verify_peer:
type: toggle
label: PLUGIN_ADMIN.SSL_VERIFY_PEER
highlight: 1
help: PLUGIN_ADMIN.SSL_VERIFY_PEER_HELP
options: options:
1: PLUGIN_ADMIN.YES 1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO 0: PLUGIN_ADMIN.NO
validate: validate:
type: bool type: bool
http.verify_host:
type: toggle
label: PLUGIN_ADMIN.SSL_VERIFY_HOST
highlight: 1
help: PLUGIN_ADMIN.SSL_VERIFY_HOST_HELP
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: bool
http.concurrent_connections:
type: number
size: x-small
label: PLUGIN_ADMIN.HTTP_CONNECTIONS
help: PLUGIN_ADMIN.HTTP_CONNECTIONS_HELP
validate:
min: 1
max: 20
misc_section:
type: section
title: PLUGIN_ADMIN.MISC_SECTION
reverse_proxy_setup: reverse_proxy_setup:
type: toggle type: toggle
label: PLUGIN_ADMIN.REVERSE_PROXY label: PLUGIN_ADMIN.REVERSE_PROXY
+1 -1
View File
@@ -184,9 +184,9 @@ config:
# Fields to be searched # Fields to be searched
fields: fields:
- key - key
- slug
- menu - menu
- title - title
- name
blueprints: blueprints:
configure: configure:
+2 -6
View File
@@ -28,10 +28,6 @@ types:
type: image type: image
thumb: media/thumb-webp.png thumb: media/thumb-webp.png
mime: image/webp mime: image/webp
avif:
type: image
thumb: media/thumb.png
mime: image/avif
gif: gif:
type: animated type: animated
thumb: media/thumb-gif.png thumb: media/thumb-gif.png
@@ -95,7 +91,7 @@ types:
aif: aif:
type: audio type: audio
thumb: media/thumb-aif.png thumb: media/thumb-aif.png
mime: audio/aiff mime: audio/aif
txt: txt:
type: file type: file
thumb: media/thumb-txt.png thumb: media/thumb-txt.png
@@ -211,7 +207,7 @@ types:
js: js:
type: file type: file
thumb: media/thumb-js.png thumb: media/thumb-js.png
mime: text/javascript mime: application/javascript
json: json:
type: file type: file
thumb: media/thumb-json.png thumb: media/thumb-json.png
File diff suppressed because it is too large Load Diff
+5 -17
View File
@@ -96,7 +96,7 @@ cache:
purge_at: '0 4 * * *' # How often to purge old file cache (using new scheduler) purge_at: '0 4 * * *' # How often to purge old file cache (using new scheduler)
clear_at: '0 3 * * *' # How often to clear cache (using new scheduler) clear_at: '0 3 * * *' # How often to clear cache (using new scheduler)
clear_job_type: 'standard' # Type to clear when processing the scheduled clear job `standard`|`all` clear_job_type: 'standard' # Type to clear when processing the scheduled clear job `standard`|`all`
clear_images_by_default: false # By default grav does not include processed images in cache clear, this can be enabled clear_images_by_default: true # By default grav will include processed images in cache clear, this can be disabled
cli_compatibility: false # Ensures only non-volatile drivers are used (file, redis, memcache, etc.) cli_compatibility: false # Ensures only non-volatile drivers are used (file, redis, memcache, etc.)
lifetime: 604800 # Lifetime of cached data in seconds (0 = infinite) lifetime: 604800 # Lifetime of cached data in seconds (0 = infinite)
gzip: false # GZip compress the page output gzip: false # GZip compress the page output
@@ -131,7 +131,7 @@ assets: # Configuration for Assets Mana
enable_asset_timestamp: false # Enable asset timestamps enable_asset_timestamp: false # Enable asset timestamps
enable_asset_sri: false # Enable asset SRI enable_asset_sri: false # Enable asset SRI
collections: collections:
jquery: system://assets/jquery/jquery-3.x.min.js jquery: system://assets/jquery/jquery-2.x.min.js
errors: errors:
display: 0 # Display either (1) Full backtrace | (0) Simple Error | (-1) System Error display: 0 # Display either (1) Full backtrace | (0) Simple Error | (-1) System Error
@@ -162,12 +162,6 @@ images:
retina_scale: 1 # scale to adjust auto-sizes for better handling of HiDPI resolutions retina_scale: 1 # scale to adjust auto-sizes for better handling of HiDPI resolutions
defaults: defaults:
loading: auto # Let browser pick [auto|lazy|eager] loading: auto # Let browser pick [auto|lazy|eager]
watermark:
image: 'system://images/watermark.png' # Path to a watermark image
position_y: 'center' # top|center|bottom
position_x: 'center' # left|center|right
scale: 33 # percentage of watermark scale
watermark_all: false # automatically watermark all images
media: media:
enable_media_timestamp: false # Enable media timestamps enable_media_timestamp: false # Enable media timestamps
@@ -190,16 +184,10 @@ session:
gpm: gpm:
releases: stable # Set to either 'stable' or 'testing' 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
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
proxy_url: # Configure a manual proxy URL for GPM (eg 127.0.0.1:3128) proxy_url: # Configure a manual proxy URL for GPM (eg 127.0.0.1:3128)
proxy_cert_path: # Local path to proxy certificate folder containing pem files method: 'auto' # Either 'curl', 'fopen' or 'auto'. 'auto' will try fopen first and if not available cURL
concurrent_connections: 5 # Concurrent HTTP connections when multiplexing verify_peer: true # Sometimes on some systems (Windows most commonly) GPM is unable to connect because the SSL certificate cannot be verified. Disabling this setting might help.
verify_peer: true # Enable/Disable SSL verification of peer certificates official_gpm_only: true # By default GPM direct-install will only allow URLs via the official GPM proxy to ensure security
verify_host: true # Enable/Disable SSL verification of host certificates
accounts: accounts:
type: regular # EXPERIMENTAL: Account type: regular or flex type: regular # EXPERIMENTAL: Account type: regular or flex
+1 -1
View File
@@ -9,7 +9,7 @@
// Some standard defines // Some standard defines
define('GRAV', true); define('GRAV', true);
define('GRAV_VERSION', '1.7.25'); define('GRAV_VERSION', '1.7.14');
define('GRAV_SCHEMA', '1.7.0_2020-11-20_1'); define('GRAV_SCHEMA', '1.7.0_2020-11-20_1');
define('GRAV_TESTING', false); define('GRAV_TESTING', false);
Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

-11
View File
@@ -51,7 +51,6 @@ GRAV:
VALIDATION_FAIL: '<b>فشل التحقق من صحة:</b>' VALIDATION_FAIL: '<b>فشل التحقق من صحة:</b>'
INVALID_INPUT: 'إدخال غير صحيح في' INVALID_INPUT: 'إدخال غير صحيح في'
MISSING_REQUIRED_FIELD: 'حقل مطلوب مفقود:' MISSING_REQUIRED_FIELD: 'حقل مطلوب مفقود:'
XSS_ISSUES: "مشاكل XSS محتملة تم اكتشافها في حقل '%s' '"
MONTHS_OF_THE_YEAR: MONTHS_OF_THE_YEAR:
- 'كانون الثاني' - 'كانون الثاني'
- 'شباط' - 'شباط'
@@ -73,8 +72,6 @@ GRAV:
- 'الجمعة' - 'الجمعة'
- 'السبت' - 'السبت'
- 'الأحد' - 'الأحد'
YES: "نعم"
NO: "لا"
CRON: CRON:
EVERY: كل EVERY: كل
EVERY_HOUR: كل ساعة EVERY_HOUR: كل ساعة
@@ -83,11 +80,3 @@ GRAV:
EVERY_DAY_OF_MONTH: كل يوم في الشهر EVERY_DAY_OF_MONTH: كل يوم في الشهر
EVERY_MONTH: ' كل شهر' EVERY_MONTH: ' كل شهر'
TEXT_PERIOD: كل <b /> TEXT_PERIOD: كل <b />
TEXT_MINS: ' في <b /> دقيقة(دقائق) بعد الساعة'
TEXT_TIME: ' في <b />:<b />'
TEXT_DOW: ' في <b />'
TEXT_MONTH: ' من <b />'
TEXT_DOM: ' في <b />'
ERROR1: الوسم %s غير مدعوم!
ERROR2: عدد عناصر غير صالح.
ERROR4: تعبير غير معروف
-16
View File
@@ -15,7 +15,6 @@ GRAV:
BAD_DATE: Data invàlida BAD_DATE: Data invàlida
AGO: abans AGO: abans
FROM_NOW: des d'ara FROM_NOW: des d'ara
JUST_NOW: Ara mateix
SECOND: segon SECOND: segon
MINUTE: minut MINUTE: minut
HOUR: hora HOUR: hora
@@ -49,7 +48,6 @@ GRAV:
VALIDATION_FAIL: '<b>Ha fallat la validació:</b>' VALIDATION_FAIL: '<b>Ha fallat la validació:</b>'
INVALID_INPUT: 'Entrada no vàlida a' INVALID_INPUT: 'Entrada no vàlida a'
MISSING_REQUIRED_FIELD: 'Falta camp obligatori:' MISSING_REQUIRED_FIELD: 'Falta camp obligatori:'
XSS_ISSUES: "Detectats potencials problemes XSS al camp '%s'"
MONTHS_OF_THE_YEAR: MONTHS_OF_THE_YEAR:
- 'Gener' - 'Gener'
- 'Febrer' - 'Febrer'
@@ -71,17 +69,3 @@ GRAV:
- 'Divendres' - 'Divendres'
- 'Dissabte' - 'Dissabte'
- 'Diumenge' - 'Diumenge'
YES: "Sí"
NO: "No"
CRON:
EVERY: cada
EVERY_HOUR: cada hora
EVERY_MINUTE: cada minut
EVERY_DAY_OF_WEEK: cada dia de la setmana
EVERY_DAY_OF_MONTH: cada dia del mes
EVERY_MONTH: cada mes
TEXT_PERIOD: Cada <b />
ERROR1: L'etiqueta %s no està suportada!
ERROR2: Nombre d'elements incorrecte
ERROR3: El jquery_element s'ha d'establir a la configuració de jqCron
ERROR4: Expressió no reconeguda
+5 -9
View File
@@ -24,7 +24,6 @@ GRAV:
'/(quiz)zes$/i': '\1' '/(quiz)zes$/i': '\1'
'/(alias|status)es$/i': '\1' '/(alias|status)es$/i': '\1'
'/([octop|vir])i$/i': '\1us' '/([octop|vir])i$/i': '\1us'
'/(n)ews$/i': '\1ouvelles'
INFLECTOR_UNCOUNTABLE: INFLECTOR_UNCOUNTABLE:
- 'équipement' - 'équipement'
- 'information' - 'information'
@@ -59,10 +58,10 @@ GRAV:
MONTH: mois MONTH: mois
YEAR: année YEAR: année
DECADE: décennie DECADE: décennie
SEC: sec. SEC: s
MIN: min. MIN: m
HR: hr. HR: h
WK: sem. WK: sem
MO: m MO: m
YR: an YR: an
DEC: déc DEC: déc
@@ -85,7 +84,6 @@ GRAV:
VALIDATION_FAIL: '<b>La validation a échoué :</b>' VALIDATION_FAIL: '<b>La validation a échoué :</b>'
INVALID_INPUT: 'Saisie non valide' INVALID_INPUT: 'Saisie non valide'
MISSING_REQUIRED_FIELD: 'Champ obligatoire manquant :' MISSING_REQUIRED_FIELD: 'Champ obligatoire manquant :'
XSS_ISSUES: "Erreurs XSS probablement détectées dans le champ '%s'"
MONTHS_OF_THE_YEAR: MONTHS_OF_THE_YEAR:
- 'janvier' - 'janvier'
- 'février' - 'février'
@@ -107,8 +105,6 @@ GRAV:
- 'vendredi' - 'vendredi'
- 'samedi' - 'samedi'
- 'dimanche' - 'dimanche'
YES: "Oui"
NO: "Non"
CRON: CRON:
EVERY: chaque EVERY: chaque
EVERY_HOUR: toutes les heures EVERY_HOUR: toutes les heures
@@ -122,7 +118,7 @@ GRAV:
TEXT_DOW: ' sur <b/>' TEXT_DOW: ' sur <b/>'
TEXT_MONTH: ' de <b />' TEXT_MONTH: ' de <b />'
TEXT_DOM: ' sur <b/>' TEXT_DOM: ' sur <b/>'
ERROR1: La balise %s n'est pas prise en charge ! ERROR1: La balise %s n'est pas supportée!
ERROR2: Nombre invalide d'éléments ERROR2: Nombre invalide d'éléments
ERROR3: L'élément jquery_element doit être défini dans les paramètres jqCron ERROR3: L'élément jquery_element doit être défini dans les paramètres jqCron
ERROR4: Expression non reconnue ERROR4: Expression non reconnue
-3
View File
@@ -104,7 +104,6 @@ GRAV:
VALIDATION_FAIL: '<b>Fallou a validación:</b>' VALIDATION_FAIL: '<b>Fallou a validación:</b>'
INVALID_INPUT: 'Entrada incorrecta en' INVALID_INPUT: 'Entrada incorrecta en'
MISSING_REQUIRED_FIELD: 'Falta un campo requirido:' MISSING_REQUIRED_FIELD: 'Falta un campo requirido:'
XSS_ISSUES: "Detectáronse posibles problemas XSS no campo '% s'"
MONTHS_OF_THE_YEAR: MONTHS_OF_THE_YEAR:
- 'xaneiro' - 'xaneiro'
- 'febreiro' - 'febreiro'
@@ -126,8 +125,6 @@ GRAV:
- 'venres' - 'venres'
- 'sábado' - 'sábado'
- 'domingo' - 'domingo'
YES: "Si"
NO: "Non"
CRON: CRON:
EVERY: cada EVERY: cada
EVERY_HOUR: Cada hora EVERY_HOUR: Cada hora
+31 -81
View File
@@ -3,72 +3,26 @@ GRAV:
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Error: Frontmatter tidak valid\n\nLokasi: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```" FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Error: Frontmatter tidak valid\n\nLokasi: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
INFLECTOR_PLURALS: INFLECTOR_PLURALS:
'/(quiz)$/i': '\1zes' '/(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_UNCOUNTABLE:
- 'Peralatan' - 'peralatan'
- 'Informasi ' - 'informasi'
- 'Nasi' - 'nasi'
- 'Uang' - 'uang'
- 'Jenis' - 'spesies'
- 'Seri' - 'rangkaian'
- 'Ikan' - 'ikan'
- 'Domba' - 'domba'
INFLECTOR_IRREGULAR: INFLECTOR_IRREGULAR:
'person': 'Orang-orang' 'person': 'orang-orang'
'man': 'Pria' 'man': 'laki-laki'
'child': 'Balita' 'child': 'anak-anak'
'sex': 'Jenis Kelamin' 'sex': 'jenis kelamin'
'move': 'pindahkan' 'move': 'pindahkan'
INFLECTOR_ORDINALS:
'default': 'ke'
'first': 'pertama'
'second': 'nd'
'third': 'rd'
NICETIME: NICETIME:
NO_DATE_PROVIDED: Tidak ada tanggal yang disediakan NO_DATE_PROVIDED: Tanggal tidak tersedia
BAD_DATE: Format tanggal salah BAD_DATE: Format tanggal salah
AGO: yang lalu AGO: yang lalu
FROM_NOW: dari sekarang FROM_NOW: dari saat ini
JUST_NOW: baru saja JUST_NOW: baru saja
SECOND: detik SECOND: detik
MINUTE: menit MINUTE: menit
@@ -78,12 +32,12 @@ GRAV:
MONTH: bulan MONTH: bulan
YEAR: tahun YEAR: tahun
DECADE: dekade DECADE: dekade
SEC: detik SEC: dtk
MIN: menit MIN: mnt
HR: ' jam' HR: j
WK: minggu WK: mng
MO: bulan MO: bln
YR: tahun YR: thn
DEC: desimal DEC: desimal
SECOND_PLURAL: detik SECOND_PLURAL: detik
MINUTE_PLURAL: menit MINUTE_PLURAL: menit
@@ -93,18 +47,17 @@ GRAV:
MONTH_PLURAL: bulan MONTH_PLURAL: bulan
YEAR_PLURAL: tahun YEAR_PLURAL: tahun
DECADE_PLURAL: dekade DECADE_PLURAL: dekade
SEC_PLURAL: detik SEC_PLURAL: dtk
MIN_PLURAL: menit MIN_PLURAL: mnt
HR_PLURAL: jam HR_PLURAL: j
WK_PLURAL: minggu WK_PLURAL: mgg
MO_PLURAL: bulan MO_PLURAL: bln
YR_PLURAL: tahun YR_PLURAL: thn
DEC_PLURAL: dekade DEC_PLURAL: dekade
FORM: FORM:
VALIDATION_FAIL: '<b>Validasi gagal:</b>' VALIDATION_FAIL: '<b>Validasi gagal:</b>'
INVALID_INPUT: 'Input tidak valid di' INVALID_INPUT: 'Input tidak valid di'
MISSING_REQUIRED_FIELD: 'Data yang diperlukan belum terisi:' MISSING_REQUIRED_FIELD: 'Data yang diperlukan belum terisi:'
XSS_ISSUES: "Isu berpotensial XSS terdeteksi dalam baris %s"
MONTHS_OF_THE_YEAR: MONTHS_OF_THE_YEAR:
- 'Januari' - 'Januari'
- 'Februari' - 'Februari'
@@ -123,25 +76,22 @@ GRAV:
- 'Selasa' - 'Selasa'
- 'Rabu' - 'Rabu'
- 'Kamis' - 'Kamis'
- 'Jum''at' - 'Jumat'
- 'Sabtu' - 'Sabtu'
- 'Minggu' - 'Minggu'
YES: "Ya"
NO: "Tidak"
CRON: CRON:
EVERY: Setiap EVERY: Setiap
EVERY_HOUR: Setiap jam EVERY_HOUR: Setiap jam
EVERY_MINUTE: Setiap menit EVERY_MINUTE: Setiap menit
EVERY_DAY_OF_WEEK: Setiap hari selama seminggu EVERY_DAY_OF_WEEK: Setiap hari selama seminggu
EVERY_DAY_OF_MONTH: Setiap hari dalam sebulan EVERY_DAY_OF_MONTH: pada tanggal setiap bulannya
EVERY_MONTH: setiap bulan EVERY_MONTH: setiap bulan
TEXT_PERIOD: Setiap <b /> TEXT_PERIOD: Setiap <b />
TEXT_MINS: 'dalam <b /> menit setelah jam yang lalu'
TEXT_TIME: ' pada <b />:<b />' TEXT_TIME: ' pada <b />:<b />'
TEXT_DOW: ' pada <b />' TEXT_DOW: ' pada <b />'
TEXT_MONTH: ' pada <b />' TEXT_MONTH: ' pada <b />'
TEXT_DOM: ' pada <b />' TEXT_DOM: ' pada <b />'
ERROR1: Tag %s tidak didukung! ERROR1: Tag %s tidak didukung!
ERROR2: Jumlah elemen yang buruk ERROR2: Jumlah elemen tidak valid
ERROR3: jquery_element harus diatur ke dalam pengaturan jqCron ERROR3: jquery_element harus ditetapkan ke pengaturan jqCron
ERROR4: Ekspresi tidak dikenal ERROR4: Ekspresi tidak dikenali
-147
View File
@@ -1,147 +0,0 @@
---
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:
'/(quiz)$/i': '\1зүүд'
'/^(ox)$/i': '\1ууд'
'/([m|l])ouse$/i': '\1ууд'
'/(matr|vert|ind)ix|ex$/i': '\1иксүүд'
'/(x|ch|ss|sh)$/i': '\1үүд'
'/([^aeiouy]|qu)ies$/i': '\1үүд'
'/([^aeiouy]|qu)y$/i': '\1үүд'
'/(hive)$/i': '\1үүд'
'/(?:([^f])fe|([lr])f)$/i': '\1\2үүд'
'/sis$/i': 'үүд'
'/([ti])um$/i': '\1үүд'
'/(buffal|tomat)o$/i': '\1үүд'
'/(bu)s$/i': '\1үүд'
'/(alias|status)/i': '\1үүд'
'/(octop|vir)us$/i': '\1үүд'
'/(ax|test)is$/i': '\1үүд'
'/s$/i': 'үүд'
'/$/': 'үүд'
INFLECTOR_SINGULAR:
'/(quiz)zes$/i': '\1'
'/(matr)ices$/i': '\1икс'
'/(vert|ind)ices$/i': '\1икс'
'/^(ox)en/i': '\1'
'/(alias|status)es$/i': '\1'
'/([octop|vir])i$/i': '\1'
'/(cris|ax|test)es$/i': '\1'
'/(shoe)s$/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'
'/([^aeiouy]|qu)ies$/i': '\1үүд'
'/([lr])ves$/i': '\1'
'/(tive)s$/i': '\1'
'/(hive)s$/i': '\1'
'/([^f])ves$/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'
'/(n)ews$/i': '\1'
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: "'%s' талбарт XSS -ийн болзошгүй асуудлууд илэрсэн"
MONTHS_OF_THE_YEAR:
- '1-р сар'
- '2-р сар'
- '3-р сар'
- '4-р сар'
- '5 сар'
- '6 сар'
- '7 сар'
- '8 сар'
- '9 сар'
- '10 сар'
- '11 сар'
- '12 сар'
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: Танигдаагүй илэрхийлэл
-3
View File
@@ -104,7 +104,6 @@ GRAV:
VALIDATION_FAIL: '<b>Falha na validação:</b>' VALIDATION_FAIL: '<b>Falha na validação:</b>'
INVALID_INPUT: 'Dados inseridos são inválidos em' INVALID_INPUT: 'Dados inseridos são inválidos em'
MISSING_REQUIRED_FIELD: 'Campo obrigatório em falta:' MISSING_REQUIRED_FIELD: 'Campo obrigatório em falta:'
XSS_ISSUES: "Potenciais problemas de XSS detectados no campo '%s'"
MONTHS_OF_THE_YEAR: MONTHS_OF_THE_YEAR:
- 'Janeiro' - 'Janeiro'
- 'Fevereiro' - 'Fevereiro'
@@ -126,8 +125,6 @@ GRAV:
- 'Sexta-feira' - 'Sexta-feira'
- 'Sábado' - 'Sábado'
- 'Domingo' - 'Domingo'
YES: "Sim"
NO: "Não"
CRON: CRON:
EVERY: cada EVERY: cada
EVERY_HOUR: cada hora EVERY_HOUR: cada hora
-9
View File
@@ -1,9 +0,0 @@
---
GRAV:
INFLECTOR_SINGULAR:
'/(quiz)zes$/i': '\1'
'/^(ox)en/i': '\1'
'/(alias|status)es$/i': '\1'
'/(o)es$/i': '\1'
'/(bus)es$/i': '\1'
'/(x|ch|ss|sh)es$/i': '\1'
-2
View File
@@ -82,8 +82,6 @@ GRAV:
- 'Cuma' - 'Cuma'
- 'Cumartesi' - 'Cumartesi'
- 'Pazar' - 'Pazar'
YES: "Evet"
NO: "Hayır"
CRON: CRON:
EVERY: her EVERY: her
EVERY_HOUR: saatte bir EVERY_HOUR: saatte bir
+1 -16
View File
@@ -38,9 +38,7 @@ GRAV:
YR_PLURAL: YR_PLURAL:
DEC_PLURAL: 十年 DEC_PLURAL: 十年
FORM: FORM:
VALIDATION_FAIL: '<b>確驗證失敗:</b>' MISSING_REQUIRED_FIELD: 遺漏必填欄位:
INVALID_INPUT: '無效輸入:'
MISSING_REQUIRED_FIELD: '遺漏必填欄位:'
MONTHS_OF_THE_YEAR: MONTHS_OF_THE_YEAR:
- '一月' - '一月'
- '二月' - '二月'
@@ -62,16 +60,3 @@ GRAV:
- '星期五' - '星期五'
- '星期六' - '星期六'
- '星期日' - '星期日'
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 />'
+2 -19
View File
@@ -13,25 +13,8 @@ if (PHP_SAPI !== 'cli-server') {
$_SERVER['PHP_CLI_ROUTER'] = true; $_SERVER['PHP_CLI_ROUTER'] = true;
$root = $_SERVER['DOCUMENT_ROOT']; if (is_file($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . $_SERVER['SCRIPT_NAME'])) {
$path = $_SERVER['SCRIPT_NAME']; return false;
if ($path !== '/index.php' && is_file($root . $path)) {
if (!(
// Block all direct access to files and folders beginning with a dot
strpos($path, '/.') !== false
// Block all direct access for these folders
|| preg_match('`^/(\.git|cache|bin|logs|backup|webserver-configs|tests)/`ui', $path)
// Block access to specific file types for these system folders
|| preg_match('`^/(system|vendor)/(.*)\.(txt|xml|md|html|yaml|yml|php|pl|py|cgi|twig|sh|bat)$`ui', $path)
// Block access to specific file types for these user folders
|| preg_match('`^/(user)/(.*)\.(txt|md|yaml|yml|php|pl|py|cgi|twig|sh|bat)$`ui', $path)
// Block all direct access to .md files
|| preg_match('`\.md$`ui', $path)
// Block access to specific files in the root folder
|| preg_match('`^/(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess)$`ui', $path)
)) {
return false;
}
} }
$grav_index = 'index.php'; $grav_index = 'index.php';
+7 -21
View File
@@ -110,7 +110,7 @@ class Assets extends PropertyObject
/** @var UniformResourceLocator $locator */ /** @var UniformResourceLocator $locator */
$locator = $grav['locator']; $locator = $grav['locator'];
$this->assets_dir = $locator->findResource('asset://'); $this->assets_dir = $locator->findResource('asset://') . DS;
$this->assets_url = $locator->findResource('asset://', false); $this->assets_url = $locator->findResource('asset://', false);
$this->config($asset_config); $this->config($asset_config);
@@ -164,19 +164,10 @@ class Assets extends PropertyObject
// More than one asset // More than one asset
if (is_array($asset)) { if (is_array($asset)) {
foreach ($asset as $index => $location) { foreach ($asset as $a) {
$params = array_slice($args, 1); array_shift($args);
if (is_array($location)) { $args = array_merge([$a], $args);
$params = array_shift($params); call_user_func_array([$this, 'add'], $args);
if (is_numeric($params)) {
$params = [ 'priority' => $params ];
}
$params = [array_replace_recursive([], $location, $params)];
$location = $index;
}
$params = array_merge([$location], $params);
call_user_func_array([$this, 'add'], $params);
} }
} elseif (isset($this->collections[$asset])) { } elseif (isset($this->collections[$asset])) {
array_shift($args); array_shift($args);
@@ -210,13 +201,8 @@ class Assets extends PropertyObject
protected function addType($collection, $type, $asset, $options) protected function addType($collection, $type, $asset, $options)
{ {
if (is_array($asset)) { if (is_array($asset)) {
foreach ($asset as $index => $location) { foreach ($asset as $a) {
$assetOptions = $options; $this->addType($collection, $type, $a, $options);
if (is_array($location)) {
$assetOptions = array_replace_recursive([], $options, $location);
$location = $index;
}
$this->addType($collection, $type, $location, $assetOptions);
} }
return $this; return $this;
+7 -17
View File
@@ -15,7 +15,6 @@ use Grav\Common\Grav;
use Grav\Common\Uri; use Grav\Common\Uri;
use Grav\Common\Utils; use Grav\Common\Utils;
use Grav\Framework\Object\PropertyObject; use Grav\Framework\Object\PropertyObject;
use RocketTheme\Toolbox\File\File;
use SplFileInfo; use SplFileInfo;
/** /**
@@ -92,10 +91,6 @@ abstract class BaseAsset extends PropertyObject
*/ */
public function init($asset, $options) public function init($asset, $options)
{ {
if (!$asset) {
return false;
}
$config = Grav::instance()['config']; $config = Grav::instance()['config'];
$uri = Grav::instance()['uri']; $uri = Grav::instance()['uri'];
@@ -187,21 +182,16 @@ abstract class BaseAsset extends PropertyObject
public static function integrityHash($input) public static function integrityHash($input)
{ {
$grav = Grav::instance(); $grav = Grav::instance();
$uri = $grav['uri'];
$assetsConfig = $grav['config']->get('system.assets'); $assetsConfig = $grav['config']->get('system.assets');
if (!self::isRemoteLink($input) && !empty($assetsConfig['enable_asset_sri']) && $assetsConfig['enable_asset_sri']) { if ( !empty($assetsConfig['enable_asset_sri']) && $assetsConfig['enable_asset_sri'] )
$input = preg_replace('#^' . $uri->rootUrl() . '#', '', $input); {
$asset = File::instance(GRAV_WEBROOT . $input); $dataToHash = file_get_contents( GRAV_WEBROOT . $input);
if ($asset->exists()) { $hash = hash('sha256', $dataToHash, true);
$dataToHash = $asset->content(); $hash_base64 = base64_encode($hash);
$hash = hash('sha256', $dataToHash, true); return ' integrity="sha256-' . $hash_base64 . '"';
$hash_base64 = base64_encode($hash);
return ' integrity="sha256-' . $hash_base64 . '"';
}
} }
return ''; return '';
@@ -263,6 +253,6 @@ abstract class BaseAsset extends PropertyObject
*/ */
protected function cssRewrite($file, $dir, $local) protected function cssRewrite($file, $dir, $local)
{ {
return ''; return;
} }
} }
+13 -18
View File
@@ -9,9 +9,9 @@
namespace Grav\Common\Assets; namespace Grav\Common\Assets;
use Grav\Common\Assets\BaseAsset;
use Grav\Common\Assets\Traits\AssetUtilsTrait; use Grav\Common\Assets\Traits\AssetUtilsTrait;
use Grav\Common\Config\Config; use Grav\Common\Config\Config;
use Grav\Common\Filesystem\Folder;
use Grav\Common\Grav; use Grav\Common\Grav;
use Grav\Common\Uri; use Grav\Common\Uri;
use Grav\Common\Utils; use Grav\Common\Utils;
@@ -88,14 +88,7 @@ class Pipeline extends PropertyObject
$uri = Grav::instance()['uri']; $uri = Grav::instance()['uri'];
$this->base_url = rtrim($uri->rootUrl($config->get('system.absolute_urls')), '/') . '/'; $this->base_url = rtrim($uri->rootUrl($config->get('system.absolute_urls')), '/') . '/';
$this->assets_dir = $locator->findResource('asset://'); $this->assets_dir = $locator->findResource('asset://') . DS;
if (!$this->assets_dir) {
// Attempt to create assets folder if it doesn't exist yet.
$this->assets_dir = $locator->findResource('asset://', true, true);
Folder::mkdir($this->assets_dir);
$locator->clearCache();
}
$this->assets_url = $locator->findResource('asset://', false); $this->assets_url = $locator->findResource('asset://', false);
} }
@@ -126,9 +119,10 @@ class Pipeline extends PropertyObject
$file = $uid . '.css'; $file = $uid . '.css';
$relative_path = "{$this->base_url}{$this->assets_url}/{$file}"; $relative_path = "{$this->base_url}{$this->assets_url}/{$file}";
$filepath = "{$this->assets_dir}/{$file}"; $buffer = null;
if (file_exists($filepath)) {
$buffer = file_get_contents($filepath) . "\n"; if (file_exists($this->assets_dir . $file)) {
$buffer = file_get_contents($this->assets_dir . $file) . "\n";
} else { } else {
//if nothing found get out of here! //if nothing found get out of here!
if (empty($assets)) { if (empty($assets)) {
@@ -147,7 +141,7 @@ class Pipeline extends PropertyObject
// Write file // Write file
if (trim($buffer) !== '') { if (trim($buffer) !== '') {
file_put_contents($filepath, $buffer); file_put_contents($this->assets_dir . $file, $buffer);
} }
} }
@@ -188,9 +182,10 @@ class Pipeline extends PropertyObject
$file = $uid . '.js'; $file = $uid . '.js';
$relative_path = "{$this->base_url}{$this->assets_url}/{$file}"; $relative_path = "{$this->base_url}{$this->assets_url}/{$file}";
$filepath = "{$this->assets_dir}/{$file}"; $buffer = null;
if (file_exists($filepath)) {
$buffer = file_get_contents($filepath) . "\n"; if (file_exists($this->assets_dir . $file)) {
$buffer = file_get_contents($this->assets_dir . $file) . "\n";
} else { } else {
//if nothing found get out of here! //if nothing found get out of here!
if (empty($assets)) { if (empty($assets)) {
@@ -209,7 +204,7 @@ class Pipeline extends PropertyObject
// Write file // Write file
if (trim($buffer) !== '') { if (trim($buffer) !== '') {
file_put_contents($filepath, $buffer); file_put_contents($this->assets_dir . $file, $buffer);
} }
} }
@@ -254,7 +249,7 @@ class Pipeline extends PropertyObject
$old_url = ltrim($old_url, '/'); $old_url = ltrim($old_url, '/');
} }
$new_url = ($local ? $this->base_url : '') . $old_url; $new_url = ($local ? $this->base_url: '') . $old_url;
return str_replace($matches[2], $new_url, $matches[0]); return str_replace($matches[2], $new_url, $matches[0]);
}, $file); }, $file);
@@ -68,6 +68,8 @@ trait AssetUtilsTrait
protected function gatherLinks(array $assets, $css = true) protected function gatherLinks(array $assets, $css = true)
{ {
$buffer = ''; $buffer = '';
foreach ($assets as $id => $asset) { foreach ($assets as $id => $asset) {
$local = true; $local = true;
@@ -133,7 +135,7 @@ trait AssetUtilsTrait
$imports = []; $imports = [];
$file = (string)preg_replace_callback($regex, static function ($matches) use (&$imports) { $file = (string)preg_replace_callback($regex, function ($matches) use (&$imports) {
$imports[] = $matches[0]; $imports[] = $matches[0];
return ''; return '';
@@ -154,10 +156,6 @@ trait AssetUtilsTrait
$no_key = ['loading']; $no_key = ['loading'];
foreach ($this->attributes as $key => $value) { foreach ($this->attributes as $key => $value) {
if ($value === null) {
continue;
}
if (is_numeric($key)) { if (is_numeric($key)) {
$key = $value; $key = $value;
} }
@@ -198,7 +196,7 @@ trait AssetUtilsTrait
} }
if ($this->timestamp) { if ($this->timestamp) {
if ($querystring || Utils::contains($asset, '?')) { if (Utils::contains($asset, '?') || $querystring) {
$querystring .= '&' . $this->timestamp; $querystring .= '&' . $this->timestamp;
} else { } else {
$querystring .= '?' . $this->timestamp; $querystring .= '?' . $this->timestamp;
+3 -2
View File
@@ -144,8 +144,9 @@ class Backups
public static function getTotalBackupsSize() public static function getTotalBackupsSize()
{ {
$backups = static::getAvailableBackups(); $backups = static::getAvailableBackups();
$size = array_sum(array_column($backups, 'size'));
return array_sum(array_column($backups, 'size')); return $size ?? 0;
} }
/** /**
@@ -221,7 +222,7 @@ class Backups
$backup_root = rtrim(GRAV_ROOT . $backup_root, '/'); $backup_root = rtrim(GRAV_ROOT . $backup_root, '/');
} }
if (!$backup_root || !file_exists($backup_root)) { if (!file_exists($backup_root)) {
throw new RuntimeException("Backup location: {$backup_root} does not exist..."); throw new RuntimeException("Backup location: {$backup_root} does not exist...");
} }
+6 -2
View File
@@ -141,7 +141,7 @@ class Cache extends Getters
$uniqueness = substr(md5($uri->rootUrl(true) . $this->config->key() . GRAV_VERSION), 2, 8); $uniqueness = substr(md5($uri->rootUrl(true) . $this->config->key() . GRAV_VERSION), 2, 8);
// Cache key allows us to invalidate all cache on configuration changes. // Cache key allows us to invalidate all cache on configuration changes.
$this->key = ($prefix ?: 'g') . '-' . $uniqueness; $this->key = ($prefix ? $prefix : 'g') . '-' . $uniqueness;
$this->cache_dir = $grav['locator']->findResource('cache://doctrine/' . $uniqueness, true, true); $this->cache_dir = $grav['locator']->findResource('cache://doctrine/' . $uniqueness, true, true);
$this->driver_setting = $this->config->get('system.cache.driver'); $this->driver_setting = $this->config->get('system.cache.driver');
$this->driver = $this->getCacheDriver(); $this->driver = $this->getCacheDriver();
@@ -618,7 +618,11 @@ class Cache extends Getters
*/ */
public function isVolatileDriver($setting) public function isVolatileDriver($setting)
{ {
return in_array($setting, ['apc', 'apcu', 'xcache', 'wincache'], true); if (in_array($setting, ['apc', 'apcu', 'xcache', 'wincache'])) {
return true;
}
return false;
} }
/** /**
+3 -13
View File
@@ -41,9 +41,6 @@ class Setup extends Data
*/ */
public static $environment; public static $environment;
/** @var string */
public static $securityFile = 'config://security.yaml';
/** @var array */ /** @var array */
protected $streams = [ protected $streams = [
'user' => [ 'user' => [
@@ -393,19 +390,12 @@ class Setup extends Data
if (!$locator->findResource('environment://config', true)) { if (!$locator->findResource('environment://config', true)) {
// If environment does not have its own directory, remove it from the lookup. // If environment does not have its own directory, remove it from the lookup.
$prefixes = $this->get('streams.schemes.environment.prefixes'); $this->set('streams.schemes.environment.prefixes', ['config' => []]);
$prefixes['config'] = [];
$this->set('streams.schemes.environment.prefixes', $prefixes);
$this->initializeLocator($locator); $this->initializeLocator($locator);
} }
// Create security.yaml salt if it doesn't exist into existing configuration environment if possible. // Create security.yaml if it doesn't exist.
$securityFile = basename(static::$securityFile); $filename = $locator->findResource('config://security.yaml', true, true);
$securityFolder = substr(static::$securityFile, 0, -\strlen($securityFile));
$securityFolder = $locator->findResource($securityFolder, true) ?: $locator->findResource($securityFolder, true, true);
$filename = "{$securityFolder}/{$securityFile}";
$security_file = CompiledYamlFile::instance($filename); $security_file = CompiledYamlFile::instance($filename);
$security_content = (array)$security_file->content(); $security_content = (array)$security_file->content();
+2 -2
View File
@@ -37,7 +37,7 @@ class Blueprint extends BlueprintForm
/** @var string|null */ /** @var string|null */
protected $scope; protected $scope;
/** @var BlueprintSchema|null */ /** @var BlueprintSchema */
protected $blueprintSchema; protected $blueprintSchema;
/** @var object|null */ /** @var object|null */
@@ -54,7 +54,7 @@ class Blueprint extends BlueprintForm
*/ */
public function __clone() public function __clone()
{ {
if (null !== $this->blueprintSchema) { if ($this->blueprintSchema) {
$this->blueprintSchema = clone $this->blueprintSchema; $this->blueprintSchema = clone $this->blueprintSchema;
} }
} }
@@ -56,15 +56,6 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
return $this->types[$name] ?? []; return $this->types[$name] ?? [];
} }
/**
* @param string $name
* @return array|null
*/
public function getNestedRules(string $name)
{
return $this->getNested($name);
}
/** /**
* Validate data against blueprints. * Validate data against blueprints.
* *
@@ -83,7 +74,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
} }
if (!empty($messages)) { if (!empty($messages)) {
throw (new ValidationException('', 400))->setMessages($messages); throw (new ValidationException())->setMessages($messages);
} }
} }
@@ -199,7 +190,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
/** @var Config $config */ /** @var Config $config */
$config = Grav::instance()['config']; $config = Grav::instance()['config'];
if (!$config->get('system.strict_mode.blueprint_strict_compat', true)) { if (!$config->get('system.strict_mode.blueprint_strict_compat', true)) {
throw new RuntimeException(sprintf('%s is not defined in blueprints', $key), 400); throw new RuntimeException(sprintf('%s is not defined in blueprints', $key));
} }
user_error(sprintf('Having extra key %s in your data is deprecated with blueprint having \'validation: strict\'', $key), E_USER_DEPRECATED); user_error(sprintf('Having extra key %s in your data is deprecated with blueprint having \'validation: strict\'', $key), E_USER_DEPRECATED);
@@ -326,10 +317,6 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
$toggle = []; $toggle = [];
} }
// Recursively fetch the items. // Recursively fetch the items.
$childData = $data[$key] ?? null;
if (null !== $childData && !is_array($childData)) {
throw new \RuntimeException(sprintf("Bad form data for field collection '%s': %s used instead of an array", $key, gettype($childData)));
}
$data[$key] = $this->processFormRecursive($data[$key] ?? null, $toggle, $value); $data[$key] = $this->processFormRecursive($data[$key] ?? null, $toggle, $value);
} else { } else {
$field = $this->get($value); $field = $this->get($value);
+1 -1
View File
@@ -264,7 +264,7 @@ class Data implements DataInterface, ArrayAccess, \Countable, JsonSerializable,
*/ */
public function blueprints() public function blueprints()
{ {
if (null === $this->blueprints) { if (!$this->blueprints) {
$this->blueprints = new Blueprint(); $this->blueprints = new Blueprint();
} elseif (is_callable($this->blueprints)) { } elseif (is_callable($this->blueprints)) {
// Lazy load blueprints. // Lazy load blueprints.
+10 -33
View File
@@ -519,32 +519,17 @@ class Validation
return false; return false;
} }
$value = (float)$value; if (isset($params['min']) && $value < $params['min']) {
return false;
$min = 0;
if (isset($params['min'])) {
$min = (float)$params['min'];
if ($value < $min) {
return false;
}
} }
if (isset($params['max'])) { if (isset($params['max']) && $value > $params['max']) {
$max = (float)$params['max']; return false;
if ($value > $max) {
return false;
}
} }
if (isset($params['step'])) { $min = $params['min'] ?? 0;
$step = (float)$params['step'];
// Count of how many steps we are above/below the minimum value.
$pos = ($value - $min) / $step;
return is_int(static::filterNumber($pos, $params, $field)); return !(isset($params['step']) && fmod($value - $min, $params['step']) === 0);
}
return true;
} }
/** /**
@@ -608,7 +593,7 @@ class Validation
*/ */
public static function typeColor($value, array $params, array $field) public static function typeColor($value, array $params, array $field)
{ {
return (bool)preg_match('/^\#[0-9a-fA-F]{3}[0-9a-fA-F]{3}?$/u', $value); return preg_match('/^\#[0-9a-fA-F]{3}[0-9a-fA-F]{3}?$/u', $value);
} }
/** /**
@@ -781,22 +766,14 @@ class Validation
} }
// If creating new values is allowed, no further checks are needed. // If creating new values is allowed, no further checks are needed.
$validateOptions = $field['validate']['options'] ?? null; if (!empty($field['selectize']['create'])) {
if (!empty($field['selectize']['create']) || $validateOptions === 'ignore') {
return true; return true;
} }
$options = $field['options'] ?? []; $options = $field['options'] ?? [];
$use = $field['use'] ?? 'values'; $use = $field['use'] ?? 'values';
if ($validateOptions) { if (empty($field['selectize']) || empty($field['multiple'])) {
// Use custom options structure.
foreach ($options as &$option) {
$option = $option[$validateOptions] ?? null;
}
unset($option);
$options = array_values($options);
} elseif (empty($field['selectize']) || empty($field['multiple'])) {
$options = array_keys($options); $options = array_keys($options);
} }
if ($use === 'keys') { if ($use === 'keys') {
@@ -1197,7 +1174,7 @@ class Validation
*/ */
public static function filterItem_List($value, $params) public static function filterItem_List($value, $params)
{ {
return array_values(array_filter($value, static function ($v) { return array_values(array_filter($value, function ($v) {
return !empty($v); return !empty($v);
})); }));
} }
@@ -10,18 +10,16 @@
namespace Grav\Common\Data; namespace Grav\Common\Data;
use Grav\Common\Grav; use Grav\Common\Grav;
use JsonSerializable;
use RuntimeException; use RuntimeException;
/** /**
* Class ValidationException * Class ValidationException
* @package Grav\Common\Data * @package Grav\Common\Data
*/ */
class ValidationException extends RuntimeException implements JsonSerializable class ValidationException extends RuntimeException
{ {
/** @var array */ /** @var array */
protected $messages = []; protected $messages = [];
protected $escape = true;
/** /**
* @param array $messages * @param array $messages
@@ -34,34 +32,21 @@ class ValidationException extends RuntimeException implements JsonSerializable
$language = Grav::instance()['language']; $language = Grav::instance()['language'];
$this->message = $language->translate('GRAV.FORM.VALIDATION_FAIL', null, true) . ' ' . $this->message; $this->message = $language->translate('GRAV.FORM.VALIDATION_FAIL', null, true) . ' ' . $this->message;
foreach ($messages as $list) { foreach ($messages as $variable => &$list) {
$list = array_unique($list); $list = array_unique($list);
foreach ($list as $message) { foreach ($list as $message) {
$this->message .= '<br/>' . htmlspecialchars($message, ENT_QUOTES | ENT_HTML5, 'UTF-8'); $this->message .= "<br/>$message";
} }
} }
return $this; return $this;
} }
public function setSimpleMessage(bool $escape = true): void
{
$first = reset($this->messages);
$message = reset($first);
$this->message = $escape ? htmlspecialchars($message, ENT_QUOTES | ENT_HTML5, 'UTF-8') : $message;
}
/** /**
* @return array * @return array
*/ */
public function getMessages(): array public function getMessages()
{ {
return $this->messages; return $this->messages;
} }
public function jsonSerialize(): array
{
return ['validation' => $this->messages];
}
} }
+1 -1
View File
@@ -332,7 +332,7 @@ class Debugger
return new Response(404, $headers, json_encode($response)); return new Response(404, $headers, json_encode($response));
} }
$data = is_array($data) ? array_map(static function ($item) { $data = is_array($data) ? array_map(function ($item) {
return $item->toArray(); return $item->toArray();
}, $data) : $data->toArray(); }, $data) : $data->toArray();
+3 -4
View File
@@ -197,7 +197,7 @@ abstract class Folder
* Shift first directory out of the path. * Shift first directory out of the path.
* *
* @param string $path * @param string $path
* @return string|null * @return string
*/ */
public static function shift(&$path) public static function shift(&$path)
{ {
@@ -371,7 +371,7 @@ abstract class Folder
return; return;
} }
if (strpos($target, $source . '/') === 0) { if (strpos($target, $source) === 0) {
throw new RuntimeException('Cannot move folder to itself'); throw new RuntimeException('Cannot move folder to itself');
} }
@@ -417,8 +417,7 @@ abstract class Folder
if (!$success) { if (!$success) {
$error = error_get_last(); $error = error_get_last();
throw new RuntimeException($error['message']);
throw new RuntimeException($error['message'] ?? 'Unknown error');
} }
// Make sure that the change will be detected when caching. // Make sure that the change will be detected when caching.
@@ -57,9 +57,7 @@ class ZipArchiver extends Archiver
throw new InvalidArgumentException('ZipArchiver: Zip PHP module not installed...'); throw new InvalidArgumentException('ZipArchiver: Zip PHP module not installed...');
} }
// Get real path for our folder if (!file_exists($source)) {
$rootPath = realpath($source);
if (!$rootPath) {
throw new InvalidArgumentException('ZipArchiver: ' . $source . ' cannot be found...'); throw new InvalidArgumentException('ZipArchiver: ' . $source . ' cannot be found...');
} }
@@ -68,6 +66,9 @@ class ZipArchiver extends Archiver
throw new InvalidArgumentException('ZipArchiver:' . $this->archive_file . ' cannot be created...'); throw new InvalidArgumentException('ZipArchiver:' . $this->archive_file . ' cannot be created...');
} }
// Get real path for our folder
$rootPath = realpath($source);
$files = $this->getArchiveFiles($rootPath); $files = $this->getArchiveFiles($rootPath);
$status && $status([ $status && $status([
+2 -3
View File
@@ -13,7 +13,6 @@ namespace Grav\Common\Flex;
use Grav\Common\Flex\Traits\FlexGravTrait; use Grav\Common\Flex\Traits\FlexGravTrait;
use Grav\Common\Flex\Traits\FlexObjectTrait; use Grav\Common\Flex\Traits\FlexObjectTrait;
use Grav\Common\Media\Interfaces\MediaInterface;
use Grav\Framework\Flex\Traits\FlexMediaTrait; use Grav\Framework\Flex\Traits\FlexMediaTrait;
use function is_array; use function is_array;
@@ -22,7 +21,7 @@ use function is_array;
* *
* @package Grav\Common\Flex * @package Grav\Common\Flex
*/ */
abstract class FlexObject extends \Grav\Framework\Flex\FlexObject implements MediaInterface abstract class FlexObject extends \Grav\Framework\Flex\FlexObject
{ {
use FlexGravTrait; use FlexGravTrait;
use FlexObjectTrait; use FlexObjectTrait;
@@ -43,7 +42,7 @@ abstract class FlexObject extends \Grav\Framework\Flex\FlexObject implements Med
// Handle media fields. // Handle media fields.
$settings = $this->getFieldSettings($name); $settings = $this->getFieldSettings($name);
if (($settings['media_field'] ?? false) === true) { if ($settings['media_field'] ?? false === true) {
return $this->parseFileProperty($value, $settings); return $this->parseFileProperty($value, $settings);
} }
@@ -19,6 +19,7 @@ use Grav\Common\Page\Header;
use Grav\Common\Page\Interfaces\PageCollectionInterface; use Grav\Common\Page\Interfaces\PageCollectionInterface;
use Grav\Common\Page\Interfaces\PageInterface; use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Utils; use Grav\Common\Utils;
use Grav\Framework\Flex\Interfaces\FlexObjectInterface;
use Grav\Framework\Flex\Pages\FlexPageCollection; use Grav\Framework\Flex\Pages\FlexPageCollection;
use Collator; use Collator;
use InvalidArgumentException; use InvalidArgumentException;
@@ -158,7 +159,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa
*/ */
public function addPage(PageInterface $page) public function addPage(PageInterface $page)
{ {
if (!$page instanceof PageObject) { if (!$page instanceof FlexObjectInterface) {
throw new InvalidArgumentException('$page is not a flex page.'); throw new InvalidArgumentException('$page is not a flex page.');
} }
@@ -191,14 +192,6 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa
throw new RuntimeException(__METHOD__ . '(): Not Implemented'); throw new RuntimeException(__METHOD__ . '(): Not Implemented');
} }
/**
* Set current page.
*/
public function setCurrent(string $path): void
{
throw new RuntimeException(__METHOD__ . '(): Not Implemented');
}
/** /**
* Return previous item. * Return previous item.
* *
@@ -399,8 +392,8 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa
$i = count($manual); $i = count($manual);
$new_list = []; $new_list = [];
foreach ($list as $key => $dummy) { foreach ($list as $key => $dummy) {
$child = $this[$key] ?? null; $child = $this[$key];
$order = $child ? array_search($child->slug, $manual, true) : false; $order = array_search($child->slug, $manual, true);
if ($order === false) { if ($order === false) {
$order = $i++; $order = $i++;
} }
@@ -433,20 +426,20 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa
/** /**
* Returns the items between a set of date ranges of either the page date field (default) or * Returns the items between a set of date ranges of either the page date field (default) or
* an arbitrary datetime page field where start date and end date are optional * an arbitrary datetime page field where end date is optional
* Dates must be passed in as text that strtotime() can process * Dates can be passed in as text that strtotime() can process
* http://php.net/manual/en/function.strtotime.php * http://php.net/manual/en/function.strtotime.php
* *
* @param string|null $startDate * @param string $startDate
* @param string|null $endDate * @param string|false $endDate
* @param string|null $field * @param string|null $field
* @return static * @return static
* @throws Exception * @throws Exception
*/ */
public function dateRange($startDate = null, $endDate = null, $field = null) public function dateRange($startDate, $endDate = false, $field = null)
{ {
$start = $startDate ? Utils::date2timestamp($startDate) : null; $start = Utils::date2timestamp($startDate);
$end = $endDate ? Utils::date2timestamp($endDate) : null; $end = $endDate ? Utils::date2timestamp($endDate) : false;
$entries = []; $entries = [];
foreach ($this as $key => $object) { foreach ($this as $key => $object) {
@@ -456,7 +449,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa
$date = $field ? strtotime($object->getNestedProperty($field)) : $object->date(); $date = $field ? strtotime($object->getNestedProperty($field)) : $object->date();
if ((!$start || $date >= $start) && (!$end || $date <= $end)) { if ($date >= $start && (!$end || $date <= $end)) {
$entries[$key] = $object; $entries[$key] = $object;
} }
} }
@@ -109,10 +109,6 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
} }
$element = parent::get($key); $element = parent::get($key);
if (null === $element) {
return null;
}
if (isset($params)) { if (isset($params)) {
$element = $element->getTranslation(ltrim($params, '.')); $element = $element->getTranslation(ltrim($params, '.'));
} }
@@ -335,10 +331,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
*/ */
protected function filterByParent(array $filters) protected function filterByParent(array $filters)
{ {
/** @var static $index */ return parent::filterBy($filters);
$index = parent::filterBy($filters);
return $index;
} }
/** /**
@@ -554,9 +547,6 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
$filters = array_filter($filters, static function($val) { return $val !== null && $val !== ''; }); $filters = array_filter($filters, static function($val) { return $val !== null && $val !== ''; });
if ($page) { if ($page) {
$status = 'success';
$msg = 'PLUGIN_ADMIN.PAGE_ROUTE_FOUND';
if ($page->root() && (!$filter_type || in_array('root', $filter_type, true))) { if ($page->root() && (!$filter_type || in_array('root', $filter_type, true))) {
if ($field) { if ($field) {
$response[] = [ $response[] = [
@@ -596,6 +586,9 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
} }
} }
$status = 'success';
$msg = 'PLUGIN_ADMIN.PAGE_ROUTE_FOUND';
/** @var PageIndex $children */ /** @var PageIndex $children */
$children = $page->children()->getIndex(); $children = $page->children()->getIndex();
$selectedChildren = $children->filterBy($filters, true); $selectedChildren = $children->filterBy($filters, true);
@@ -680,14 +673,13 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
$child_count = $tmp->count(); $child_count = $tmp->count();
$count = $filters ? $tmp->filterBy($filters, true)->count() : null; $count = $filters ? $tmp->filterBy($filters, true)->count() : null;
$route = $child->getRoute(); $route = $child->getRoute();
$route = $route ? ($route->toString(false) ?: '/') : '';
$payload = [ $payload = [
'item-key' => htmlspecialchars(basename($child->rawRoute() ?? $child->getKey())), 'item-key' => basename($child->rawRoute() ?? $child->getKey()),
'icon' => $icon, 'icon' => $icon,
'title' => htmlspecialchars($child->menu()), 'title' => htmlspecialchars($child->menu()),
'route' => [ 'route' => [
'display' => htmlspecialchars($route) ?: null, 'display' => ($route ? ($route->toString(false) ?: '/') : null) ?? '',
'raw' => htmlspecialchars($child->rawRoute()), 'raw' => $child->rawRoute(),
], ],
'modified' => $this->jsDate($child->modified()), 'modified' => $this->jsDate($child->modified()),
'child_count' => $child_count ?: null, 'child_count' => $child_count ?: null,
@@ -721,7 +713,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
$response = Utils::arrayFlatten($sorted); $response = Utils::arrayFlatten($sorted);
} }
return [$status, $msg, $response, $path]; return [$status, $msg ?? 'PLUGIN_ADMIN.NO_ROUTE_PROVIDED', $response, $path];
} }
/** /**
@@ -842,11 +834,12 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
/** /**
* Remove item from the list. * Remove item from the list.
* *
* @param string $key * @param PageInterface|string|null $key
* @return PageObject|null *
* @return $this
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function remove($key) public function remove($key = null)
{ {
return $this->getCollection()->remove($key); return $this->getCollection()->remove($key);
} }
@@ -956,17 +949,17 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
/** /**
* Returns the items between a set of date ranges of either the page date field (default) or * Returns the items between a set of date ranges of either the page date field (default) or
* an arbitrary datetime page field where start date and end date are optional * an arbitrary datetime page field where end date is optional
* Dates must be passed in as text that strtotime() can process * Dates can be passed in as text that strtotime() can process
* http://php.net/manual/en/function.strtotime.php * http://php.net/manual/en/function.strtotime.php
* *
* @param string|null $startDate * @param string $startDate
* @param string|null $endDate * @param bool $endDate
* @param string|null $field * @param string|null $field
* @return static * @return static
* @throws Exception * @throws Exception
*/ */
public function dateRange($startDate = null, $endDate = null, $field = null) public function dateRange($startDate, $endDate = false, $field = null)
{ {
$collection = $this->__call('dateRange', [$startDate, $endDate, $field]); $collection = $this->__call('dateRange', [$startDate, $endDate, $field]);
@@ -104,12 +104,12 @@ class PageObject extends FlexPageObject
*/ */
public function getRoute($query = []): ?Route public function getRoute($query = []): ?Route
{ {
$path = $this->route(); $route = $this->route();
if (null === $path) { if (null === $route) {
return null; return null;
} }
$route = RouteFactory::createFromString($path); $route = RouteFactory::createFromString($route);
if ($lang = $route->getLanguage()) { if ($lang = $route->getLanguage()) {
$grav = Grav::instance(); $grav = Grav::instance();
if (!$grav['config']->get('system.languages.include_default_lang')) { if (!$grav['config']->get('system.languages.include_default_lang')) {
@@ -262,24 +262,6 @@ class PageObject extends FlexPageObject
$this->getFlexDirectory()->reloadIndex(); $this->getFlexDirectory()->reloadIndex();
} }
/**
* @param UserInterface|null $user
*/
public function check(UserInterface $user = null): void
{
parent::check($user);
if ($user && $this->isMoved()) {
$parentKey = $this->getProperty('parent_key');
/** @var PageObject|null $parent */
$parent = $this->getFlexDirectory()->getObject($parentKey, 'storage_key');
if (!$parent || !$parent->isAuthorized('create', null, $user)) {
throw new \RuntimeException('Forbidden', 403);
}
}
}
/** /**
* @param array|bool $reorder * @param array|bool $reorder
* @return FlexObject|FlexObjectInterface * @return FlexObject|FlexObjectInterface
@@ -311,7 +293,7 @@ class PageObject extends FlexPageObject
} }
// Reset original after save events have all been called. // Reset original after save events have all been called.
$this->_originalObject = null; $this->_original = null;
return $instance; return $instance;
} }
@@ -375,19 +357,6 @@ class PageObject extends FlexPageObject
return parent::isAuthorizedOverride($user, $action, $scope, $isMe); return parent::isAuthorizedOverride($user, $action, $scope, $isMe);
} }
/**
* @return bool
*/
protected function isMoved(): bool
{
$storageKey = $this->getMasterKey();
$filesystem = Filesystem::getInstance(false);
$oldParentKey = ltrim($filesystem->dirname("/{$storageKey}"), '/');
$newParentKey = $this->getProperty('parent_key');
return $this->exists() && $oldParentKey !== $newParentKey;
}
/** /**
* @param array $ordering * @param array $ordering
* @return PageCollection|null * @return PageCollection|null
@@ -395,7 +364,10 @@ class PageObject extends FlexPageObject
protected function reorderSiblings(array $ordering) protected function reorderSiblings(array $ordering)
{ {
$storageKey = $this->getMasterKey(); $storageKey = $this->getMasterKey();
$isMoved = $this->isMoved(); $filesystem = Filesystem::getInstance(false);
$oldParentKey = ltrim($filesystem->dirname("/{$storageKey}"), '/');
$newParentKey = $this->getProperty('parent_key');
$isMoved = $oldParentKey !== $newParentKey;
$order = !$isMoved ? $this->order() : false; $order = !$isMoved ? $this->order() : false;
if ($order !== false) { if ($order !== false) {
$order = (int)$order; $order = (int)$order;
@@ -413,12 +385,10 @@ class PageObject extends FlexPageObject
// Handle special case where ordering isn't given. // Handle special case where ordering isn't given.
if ($ordering === []) { if ($ordering === []) {
if ($order >= 999999) { if ($order >= 999999) {
// Set ordering to point to be the last item, ignoring the object itself. // Set ordering to point to be the last item.
$order = 0; $order = 0;
foreach ($siblings as $sibling) { foreach ($siblings as $sibling) {
if ($sibling->getKey() !== $this->getKey()) { $order = max($order, (int)$sibling->order());
$order = max($order, (int)$sibling->order());
}
} }
$this->order($order + 1); $this->order($order + 1);
} }
@@ -441,8 +411,7 @@ class PageObject extends FlexPageObject
// Add missing siblings into the end of the list, keeping the previous ordering between them. // Add missing siblings into the end of the list, keeping the previous ordering between them.
foreach ($siblings as $sibling) { foreach ($siblings as $sibling) {
$folder = (string)$sibling->getProperty('folder'); $basename = preg_replace('|^\d+\.|', '', $sibling->getProperty('folder'));
$basename = preg_replace('|^\d+\.|', '', $folder);
if (!in_array($basename, $ordering, true)) { if (!in_array($basename, $ordering, true)) {
$ordering[] = $basename; $ordering[] = $basename;
} }
@@ -452,8 +421,7 @@ class PageObject extends FlexPageObject
$ordering = array_flip(array_values($ordering)); $ordering = array_flip(array_values($ordering));
$count = count($ordering); $count = count($ordering);
foreach ($siblings as $sibling) { foreach ($siblings as $sibling) {
$folder = (string)$sibling->getProperty('folder'); $basename = preg_replace('|^\d+\.|', '', $sibling->getProperty('folder'));
$basename = preg_replace('|^\d+\.|', '', $folder);
$newOrder = $ordering[$basename] ?? null; $newOrder = $ordering[$basename] ?? null;
$newOrder = null !== $newOrder ? $newOrder + 1 : (int)$sibling->order() + $count; $newOrder = null !== $newOrder ? $newOrder + 1 : (int)$sibling->order() + $count;
$sibling->order($newOrder); $sibling->order($newOrder);
@@ -532,8 +500,6 @@ class PageObject extends FlexPageObject
if ($isNew === true && $name === '') { if ($isNew === true && $name === '') {
// Support onBlueprintCreated event just like in Pages::blueprints($template) // Support onBlueprintCreated event just like in Pages::blueprints($template)
$blueprint->set('initialized', true); $blueprint->set('initialized', true);
$blueprint->setFilename($template);
Grav::instance()->fireEvent('onBlueprintCreated', new Event(['blueprint' => $blueprint, 'type' => $template])); Grav::instance()->fireEvent('onBlueprintCreated', new Event(['blueprint' => $blueprint, 'type' => $template]));
} }
@@ -103,10 +103,7 @@ trait PageLegacyTrait
$parent = $this->parent(); $parent = $this->parent();
$collection = $parent ? $parent->collection('content', false) : null; $collection = $parent ? $parent->collection('content', false) : null;
if (null !== $path && $collection instanceof PageCollectionInterface) { if (null !== $path && $collection instanceof PageCollectionInterface) {
$child = $collection->adjacentSibling($path, $direction); return $collection->adjacentSibling($path, $direction);
if ($child instanceof PageInterface) {
return $child;
}
} }
return false; return false;
@@ -41,14 +41,6 @@ class UserGroupObject extends FlexObject implements UserGroupInterface
] + parent::getCachedMethods(); ] + parent::getCachedMethods();
} }
/**
* @return string
*/
public function getTitle(): string
{
return $this->getProperty('readableName');
}
/** /**
* Checks user authorization to the action. * Checks user authorization to the action.
* *
@@ -92,7 +92,7 @@ class UserCollection extends FlexCollection implements UserCollectionInterface
} else { } else {
$user = parent::find($query, $field); $user = parent::find($query, $field);
} }
if ($user instanceof UserObject) { if ($user) {
return $user; return $user;
} }
} }
@@ -123,7 +123,7 @@ class UserCollection extends FlexCollection implements UserCollectionInterface
* @param string $key * @param string $key
* @return string * @return string
*/ */
protected function filterUsername(string $key): string protected function filterUsername(string $key)
{ {
$storage = $this->getFlexDirectory()->getStorage(); $storage = $this->getFlexDirectory()->getStorage();
if (method_exists($storage, 'normalizeKey')) { if (method_exists($storage, 'normalizeKey')) {
@@ -62,7 +62,7 @@ class UserIndex extends FlexIndex implements UserCollectionInterface
* @param FlexStorageInterface $storage * @param FlexStorageInterface $storage
* @return void * @return void
*/ */
public static function updateObjectMeta(array &$meta, array $data, FlexStorageInterface $storage): void public static function updateObjectMeta(array &$meta, array $data, FlexStorageInterface $storage)
{ {
// Username can also be number and stored as such. // Username can also be number and stored as such.
$key = (string)($data['username'] ?? $meta['key'] ?? $meta['storage_key']); $key = (string)($data['username'] ?? $meta['key'] ?? $meta['storage_key']);
@@ -187,7 +187,7 @@ class UserIndex extends FlexIndex implements UserCollectionInterface
* @param array $updated * @param array $updated
* @param array $removed * @param array $removed
*/ */
protected static function onChanges(array $entries, array $added, array $updated, array $removed): void protected static function onChanges(array $entries, array $added, array $updated, array $removed)
{ {
$message = sprintf('Flex: User index updated, %d objects (%d added, %d updated, %d removed).', count($entries), count($added), count($updated), count($removed)); $message = sprintf('Flex: User index updated, %d objects (%d added, %d updated, %d removed).', count($entries), count($added), count($updated), count($removed));
@@ -11,7 +11,6 @@ declare(strict_types=1);
namespace Grav\Common\Flex\Types\Users; namespace Grav\Common\Flex\Types\Users;
use Closure;
use Countable; use Countable;
use Grav\Common\Config\Config; use Grav\Common\Config\Config;
use Grav\Common\Data\Blueprint; use Grav\Common\Data\Blueprint;
@@ -32,7 +31,6 @@ use Grav\Common\User\Interfaces\UserInterface;
use Grav\Common\User\Traits\UserTrait; use Grav\Common\User\Traits\UserTrait;
use Grav\Framework\File\Formatter\JsonFormatter; use Grav\Framework\File\Formatter\JsonFormatter;
use Grav\Framework\File\Formatter\YamlFormatter; use Grav\Framework\File\Formatter\YamlFormatter;
use Grav\Framework\Filesystem\Filesystem;
use Grav\Framework\Flex\Flex; use Grav\Framework\Flex\Flex;
use Grav\Framework\Flex\FlexDirectory; use Grav\Framework\Flex\FlexDirectory;
use Grav\Framework\Flex\Storage\FileStorage; use Grav\Framework\Flex\Storage\FileStorage;
@@ -77,9 +75,6 @@ class UserObject extends FlexObject implements UserInterface, Countable
use UserTrait; use UserTrait;
use UserObjectLegacyTrait; use UserObjectLegacyTrait;
/** @var Closure|null */
static public $authorizeCallable;
/** @var array|null */ /** @var array|null */
protected $_uploads_original; protected $_uploads_original;
/** @var FileInterface|null */ /** @var FileInterface|null */
@@ -230,16 +225,6 @@ class UserObject extends FlexObject implements UserInterface, Countable
return $this; return $this;
} }
/**
* @return bool
*/
public function isMyself(): bool
{
$me = $this->getActiveUser();
return $me && $me->authenticated && $this->username === $me->username;
}
/** /**
* Checks user authorization to the action. * Checks user authorization to the action.
* *
@@ -274,16 +259,6 @@ class UserObject extends FlexObject implements UserInterface, Countable
} }
} }
// Check custom application access.
$authorizeCallable = static::$authorizeCallable;
if ($authorizeCallable instanceof Closure) {
$authorizeCallable->bindTo($this);
$authorized = $authorizeCallable($action, $scope);
if (is_bool($authorized)) {
return $authorized;
}
}
// Check user access. // Check user access.
$access = $this->getAccess(); $access = $this->getAccess();
$authorized = $access->authorize($action, $scope); $authorized = $access->authorize($action, $scope);
@@ -291,14 +266,13 @@ class UserObject extends FlexObject implements UserInterface, Countable
return $authorized; return $authorized;
} }
// Check group access. // If specific rule isn't hit, check if user is super user.
$authorized = $this->getGroups()->authorize($action, $scope); if ($access->authorize('admin.super') === true) {
if (is_bool($authorized)) { return true;
return $authorized;
} }
// If any specific rule isn't hit, check if user is a superuser. // Check group access.
return $access->authorize('admin.super') === true; return $this->getGroups()->authorize($action, $scope);
} }
/** /**
@@ -318,14 +292,6 @@ class UserObject extends FlexObject implements UserInterface, Countable
return $value; return $value;
} }
/**
* @return UserGroupIndex
*/
public function getRoles(): UserGroupIndex
{
return $this->getGroups();
}
/** /**
* Convert object into an array. * Convert object into an array.
* *
@@ -723,7 +689,6 @@ class UserObject extends FlexObject implements UserInterface, Countable
/** /**
* @param array $files * @param array $files
* @return void
*/ */
protected function setUpdatedMedia(array $files): void protected function setUpdatedMedia(array $files): void
{ {
@@ -731,16 +696,10 @@ class UserObject extends FlexObject implements UserInterface, Countable
$locator = Grav::instance()['locator']; $locator = Grav::instance()['locator'];
$media = $this->getMedia(); $media = $this->getMedia();
if (!$media instanceof MediaUploadInterface) {
return;
}
$filesystem = Filesystem::getInstance(false);
$list = []; $list = [];
$list_original = []; $list_original = [];
foreach ($files as $field => $group) { foreach ($files as $field => $group) {
// Ignore files without a field.
if ($field === '') { if ($field === '') {
continue; continue;
} }
@@ -748,6 +707,7 @@ class UserObject extends FlexObject implements UserInterface, Countable
// Load settings for the field. // Load settings for the field.
$settings = $this->getMediaFieldSettings($field); $settings = $this->getMediaFieldSettings($field);
foreach ($group as $filename => $file) { foreach ($group as $filename => $file) {
if ($file) { if ($file) {
// File upload. // File upload.
@@ -762,8 +722,8 @@ class UserObject extends FlexObject implements UserInterface, Countable
} }
if ($file) { if ($file) {
// Check file upload against media limits (except for max size). // Check file upload against media limits.
$filename = $media->checkUploadedFile($file, $filename, ['filesize' => 0] + $settings); $filename = $media->checkUploadedFile($file, $filename, $settings);
} }
$self = $settings['self']; $self = $settings['self'];
@@ -786,25 +746,19 @@ class UserObject extends FlexObject implements UserInterface, Countable
continue; continue;
} }
// Calculate path without the retina scaling factor.
$realpath = $filesystem->pathname($filepath) . str_replace(['@3x', '@2x'], '', basename($filepath));
$list[$filename] = [$file, $settings]; $list[$filename] = [$file, $settings];
$path = str_replace('.', "\n", $field);
if (null !== $data) { if (null !== $data) {
$data['name'] = $filename; $data['name'] = $filename;
$data['path'] = $filepath; $data['path'] = $filepath;
$this->setNestedProperty("{$path}\n{$realpath}", $data, "\n"); $this->setNestedProperty("{$field}\n{$filepath}", $data, "\n");
} else { } else {
$this->unsetNestedProperty("{$path}\n{$realpath}", "\n"); $this->unsetNestedProperty("{$field}\n{$filepath}", "\n");
} }
} }
} }
$this->clearMediaCache();
$this->_uploads = $list; $this->_uploads = $list;
$this->_uploads_original = $list_original; $this->_uploads_original = $list_original;
} }
+37 -99
View File
@@ -35,11 +35,7 @@ class GPM extends Iterator
/** @var Remote\Packages|null Remote available Packages */ /** @var Remote\Packages|null Remote available Packages */
private $repository; private $repository;
/** @var Remote\GravCore|null Remove Grav Packages */ /** @var Remote\GravCore|null Remove Grav Packages */
private $grav; public $grav;
/** @var bool */
private $refresh;
/** @var callable|null */
private $callback;
/** @var array Internal cache */ /** @var array Internal cache */
protected $cache; protected $cache;
@@ -59,45 +55,13 @@ class GPM extends Iterator
public function __construct($refresh = false, $callback = null) public function __construct($refresh = false, $callback = null)
{ {
parent::__construct(); parent::__construct();
Folder::create(CACHE_DIR . '/gpm');
$this->cache = []; $this->cache = [];
$this->installed = new Local\Packages(); $this->installed = new Local\Packages();
$this->refresh = $refresh; try {
$this->callback = $callback; $this->repository = new Remote\Packages($refresh, $callback);
} $this->grav = new Remote\GravCore($refresh, $callback);
} catch (Exception $e) {
/**
* Magic getter method
*
* @param string $offset Asset name value
* @return mixed Asset value
*/
public function __get($offset)
{
switch ($offset) {
case 'grav':
return $this->getGrav();
} }
return parent::__get($offset);
}
/**
* Magic method to determine if the attribute is set
*
* @param string $offset Asset name value
* @return bool True if the value is set
*/
public function __isset($offset)
{
switch ($offset) {
case 'grav':
return $this->getGrav() !== null;
}
return parent::__isset($offset);
} }
/** /**
@@ -302,12 +266,11 @@ class GPM extends Iterator
{ {
$items = []; $items = [];
$repository = $this->getRepository(); if (null === $this->repository) {
if (null === $repository) {
return $items; return $items;
} }
$plugins = $repository['plugins']; $repository = $this->repository['plugins'];
// local cache to speed things up // local cache to speed things up
if (isset($this->cache[__METHOD__])) { if (isset($this->cache[__METHOD__])) {
@@ -315,18 +278,18 @@ class GPM extends Iterator
} }
foreach ($this->installed['plugins'] as $slug => $plugin) { foreach ($this->installed['plugins'] as $slug => $plugin) {
if (!isset($plugins[$slug]) || $plugin->symlink || !$plugin->version || $plugin->gpm === false) { if (!isset($repository[$slug]) || $plugin->symlink || !$plugin->version || $plugin->gpm === false) {
continue; continue;
} }
$local_version = $plugin->version ?? 'Unknown'; $local_version = $plugin->version ?? 'Unknown';
$remote_version = $plugins[$slug]->version; $remote_version = $repository[$slug]->version;
if (version_compare($local_version, $remote_version) < 0) { if (version_compare($local_version, $remote_version) < 0) {
$plugins[$slug]->available = $remote_version; $repository[$slug]->available = $remote_version;
$plugins[$slug]->version = $local_version; $repository[$slug]->version = $local_version;
$plugins[$slug]->type = $plugins[$slug]->release_type; $repository[$slug]->type = $repository[$slug]->release_type;
$items[$slug] = $plugins[$slug]; $items[$slug] = $repository[$slug];
} }
} }
@@ -343,20 +306,19 @@ class GPM extends Iterator
*/ */
public function getLatestVersionOfPackage($package_name) public function getLatestVersionOfPackage($package_name)
{ {
$repository = $this->getRepository(); if (null === $this->repository) {
if (null === $repository) {
return null; return null;
} }
$plugins = $repository['plugins']; $repository = $this->repository['plugins'];
if (isset($plugins[$package_name])) { if (isset($repository[$package_name])) {
return $plugins[$package_name]->available ?: $plugins[$package_name]->version; return $repository[$package_name]->available ?: $repository[$package_name]->version;
} }
//Not a plugin, it's a theme? //Not a plugin, it's a theme?
$themes = $repository['themes']; $repository = $this->repository['themes'];
if (isset($themes[$package_name])) { if (isset($repository[$package_name])) {
return $themes[$package_name]->available ?: $themes[$package_name]->version; return $repository[$package_name]->available ?: $repository[$package_name]->version;
} }
return null; return null;
@@ -394,12 +356,11 @@ class GPM extends Iterator
{ {
$items = []; $items = [];
$repository = $this->getRepository(); if (null === $this->repository) {
if (null === $repository) {
return $items; return $items;
} }
$themes = $repository['themes']; $repository = $this->repository['themes'];
// local cache to speed things up // local cache to speed things up
if (isset($this->cache[__METHOD__])) { if (isset($this->cache[__METHOD__])) {
@@ -407,18 +368,18 @@ class GPM extends Iterator
} }
foreach ($this->installed['themes'] as $slug => $plugin) { foreach ($this->installed['themes'] as $slug => $plugin) {
if (!isset($themes[$slug]) || $plugin->symlink || !$plugin->version || $plugin->gpm === false) { if (!isset($repository[$slug]) || $plugin->symlink || !$plugin->version || $plugin->gpm === false) {
continue; continue;
} }
$local_version = $plugin->version ?? 'Unknown'; $local_version = $plugin->version ?? 'Unknown';
$remote_version = $themes[$slug]->version; $remote_version = $repository[$slug]->version;
if (version_compare($local_version, $remote_version) < 0) { if (version_compare($local_version, $remote_version) < 0) {
$themes[$slug]->available = $remote_version; $repository[$slug]->available = $remote_version;
$themes[$slug]->version = $local_version; $repository[$slug]->version = $local_version;
$themes[$slug]->type = $themes[$slug]->release_type; $repository[$slug]->type = $repository[$slug]->release_type;
$items[$slug] = $themes[$slug]; $items[$slug] = $repository[$slug];
} }
} }
@@ -446,20 +407,19 @@ class GPM extends Iterator
*/ */
public function getReleaseType($package_name) public function getReleaseType($package_name)
{ {
$repository = $this->getRepository(); if (null === $this->repository) {
if (null === $repository) {
return null; return null;
} }
$plugins = $repository['plugins']; $repository = $this->repository['plugins'];
if (isset($plugins[$package_name])) { if (isset($repository[$package_name])) {
return $plugins[$package_name]->release_type; return $repository[$package_name]->release_type;
} }
//Not a plugin, it's a theme? //Not a plugin, it's a theme?
$themes = $repository['themes']; $repository = $this->repository['themes'];
if (isset($themes[$package_name])) { if (isset($repository[$package_name])) {
return $themes[$package_name]->release_type; return $repository[$package_name]->release_type;
} }
return null; return null;
@@ -510,7 +470,7 @@ class GPM extends Iterator
*/ */
public function getRepositoryPlugins() public function getRepositoryPlugins()
{ {
return $this->getRepository()['plugins'] ?? null; return $this->repository['plugins'] ?? null;
} }
/** /**
@@ -533,7 +493,7 @@ class GPM extends Iterator
*/ */
public function getRepositoryThemes() public function getRepositoryThemes()
{ {
return $this->getRepository()['themes'] ?? null; return $this->repository['themes'] ?? null;
} }
/** /**
@@ -544,31 +504,9 @@ class GPM extends Iterator
*/ */
public function getRepository() public function getRepository()
{ {
if (null === $this->repository) {
try {
$this->repository = new Remote\Packages($this->refresh, $this->callback);
} catch (Exception $e) {}
}
return $this->repository; return $this->repository;
} }
/**
* Returns Grav version available in the repository
*
* @return Remote\GravCore|null
*/
public function getGrav()
{
if (null === $this->grav) {
try {
$this->grav = new Remote\GravCore($this->refresh, $this->callback);
} catch (Exception $e) {}
}
return $this->grav;
}
/** /**
* Searches for a Package in the repository * Searches for a Package in the repository
* *
+142 -2
View File
@@ -1,3 +1,143 @@
<?php <?php
// Create alias for the deprecated class.
class_alias(\Grav\Common\HTTP\Response::class, \Grav\Common\GPM\Response::class); /**
* @package Grav\Common\GPM
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\GPM;
use Exception;
use Grav\Common\Utils;
use Grav\Common\Grav;
use Symfony\Component\HttpClient\CurlHttpClient;
use Symfony\Component\HttpClient\Exception\TransportException;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpClient\HttpOptions;
use Symfony\Component\HttpClient\NativeHttpClient;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use function call_user_func;
use function defined;
use function function_exists;
/**
* Class Response
* @package Grav\Common\GPM
*/
class Response
{
/** @var callable The callback for the progress, either a function or callback in array notation */
public static $callback = null;
/** @var string[] */
private static $headers = [
'User-Agent' => 'Grav CMS'
];
/**
* Makes a request to the URL by using the preferred method
*
* @param string $uri URL to call
* @param array $overrides An array of parameters for both `curl` and `fopen`
* @param callable|null $callback Either a function or callback in array notation
* @return string The response of the request
* @throws TransportExceptionInterface
*/
public static function get($uri = '', $overrides = [], $callback = null)
{
if (empty($uri)) {
throw new TransportException('missing URI');
}
// check if this function is available, if so use it to stop any timeouts
try {
if (Utils::functionExists('set_time_limit')) {
@set_time_limit(0);
}
} catch (Exception $e) {
}
$config = Grav::instance()['config'];
$referer = defined('GRAV_CLI') ? 'grav_cli' : Grav::instance()['uri']->rootUrl(true);
$options = new HttpOptions();
// Set default Headers
$options->setHeaders(array_merge([ 'Referer' => $referer ], self::$headers));
// Disable verify Peer if required
$verify_peer = $config->get('system.gpm.verify_peer', true);
if ($verify_peer !== true) {
$options->verifyPeer($verify_peer);
}
// Set proxy url if provided
$proxy_url = $config->get('system.gpm.proxy_url', false);
if ($proxy_url) {
$options->setProxy($proxy_url);
}
// Use callback if provided
if ($callback) {
self::$callback = $callback;
$options->setOnProgress([Response::class, 'progress']);
}
$preferred_method = $config->get('system.gpm.method', 'auto');
$settings = array_merge_recursive($options->toArray(), $overrides);
switch ($preferred_method) {
case 'curl':
$client = new CurlHttpClient($settings);
break;
case 'fopen':
case 'native':
$client = new NativeHttpClient($settings);
break;
default:
$client = HttpClient::create($settings);
}
$response = $client->request('GET', $uri);
return $response->getContent();
}
/**
* Is this a remote file or not
*
* @param string $file
* @return bool
*/
public static function isRemote($file)
{
return (bool) filter_var($file, FILTER_VALIDATE_URL);
}
/**
* Progress normalized for cURL and Fopen
* Accepts a variable length of arguments passed in by stream method
*
* @return void
*/
public static function progress(int $bytes_transferred, int $filesize, array $info)
{
if ($bytes_transferred > 0) {
$percent = $filesize <= 0 ? 0 : (int)(($bytes_transferred * 100) / $filesize);
$progress = [
'code' => $info['http_code'],
'filesize' => $filesize,
'transferred' => $bytes_transferred,
'percent' => $percent < 100 ? $percent : 100
];
if (self::$callback !== null) {
call_user_func(self::$callback, $progress);
}
}
}
}
-4
View File
@@ -69,7 +69,6 @@ abstract class Getters implements ArrayAccess, Countable
* @param int|string $offset * @param int|string $offset
* @return bool * @return bool
*/ */
#[\ReturnTypeWillChange]
public function offsetExists($offset) public function offsetExists($offset)
{ {
if ($this->gettersVariable) { if ($this->gettersVariable) {
@@ -85,7 +84,6 @@ abstract class Getters implements ArrayAccess, Countable
* @param int|string $offset * @param int|string $offset
* @return mixed * @return mixed
*/ */
#[\ReturnTypeWillChange]
public function offsetGet($offset) public function offsetGet($offset)
{ {
if ($this->gettersVariable) { if ($this->gettersVariable) {
@@ -101,7 +99,6 @@ abstract class Getters implements ArrayAccess, Countable
* @param int|string $offset * @param int|string $offset
* @param mixed $value * @param mixed $value
*/ */
#[\ReturnTypeWillChange]
public function offsetSet($offset, $value) public function offsetSet($offset, $value)
{ {
if ($this->gettersVariable) { if ($this->gettersVariable) {
@@ -115,7 +112,6 @@ abstract class Getters implements ArrayAccess, Countable
/** /**
* @param int|string $offset * @param int|string $offset
*/ */
#[\ReturnTypeWillChange]
public function offsetUnset($offset) public function offsetUnset($offset)
{ {
if ($this->gettersVariable) { if ($this->gettersVariable) {
+16 -24
View File
@@ -9,7 +9,6 @@
namespace Grav\Common; namespace Grav\Common;
use Composer\Autoload\ClassLoader;
use Grav\Common\Config\Config; use Grav\Common\Config\Config;
use Grav\Common\Config\Setup; use Grav\Common\Config\Setup;
use Grav\Common\Helpers\Exif; use Grav\Common\Helpers\Exif;
@@ -135,7 +134,7 @@ class Grav extends Container
* *
* @return void * @return void
*/ */
public static function resetInstance(): void public static function resetInstance()
{ {
if (self::$instance) { if (self::$instance) {
// @phpstan-ignore-next-line // @phpstan-ignore-next-line
@@ -153,13 +152,6 @@ class Grav extends Container
{ {
if (null === self::$instance) { if (null === self::$instance) {
self::$instance = static::load($values); self::$instance = static::load($values);
/** @var ClassLoader|null $loader */
$loader = self::$instance['loader'] ?? null;
if ($loader) {
// Load fix for Deferred Twig Extension
$loader->addPsr4('Phive\\Twig\\Extensions\\Deferred\\', LIB_DIR . 'Phive/Twig/Extensions/Deferred/', true);
}
} elseif ($values) { } elseif ($values) {
$instance = self::$instance; $instance = self::$instance;
foreach ($values as $key => $value) { foreach ($values as $key => $value) {
@@ -242,7 +234,7 @@ class Grav extends Container
* *
* @return void * @return void
*/ */
public function process(): void public function process()
{ {
if (isset($this->initialized['process'])) { if (isset($this->initialized['process'])) {
return; return;
@@ -464,10 +456,6 @@ class Grav extends Container
} }
} }
if ($uri->extension() === 'json') {
return new Response(200, ['Content-Type' => 'application/json'], json_encode(['code' => $code, 'redirect' => $url], JSON_THROW_ON_ERROR));
}
return new Response($code, ['Location' => $url]); return new Response($code, ['Location' => $url]);
} }
@@ -478,7 +466,7 @@ class Grav extends Container
* @param int $code Redirection code (30x) * @param int $code Redirection code (30x)
* @return void * @return void
*/ */
public function redirectLangSafe($route, $code = null): void public function redirectLangSafe($route, $code = null)
{ {
if (!$this['uri']->isExternal($route)) { if (!$this['uri']->isExternal($route)) {
$this->redirect($this['pages']->route($route), $code); $this->redirect($this['pages']->route($route), $code);
@@ -493,7 +481,7 @@ class Grav extends Container
* @param ResponseInterface|null $response * @param ResponseInterface|null $response
* @return void * @return void
*/ */
public function header(ResponseInterface $response = null): void public function header(ResponseInterface $response = null)
{ {
if (null === $response) { if (null === $response) {
/** @var PageInterface $page */ /** @var PageInterface $page */
@@ -518,7 +506,7 @@ class Grav extends Container
* *
* @return void * @return void
*/ */
public function setLocale(): void public function setLocale()
{ {
// Initialize Locale if set and configured. // Initialize Locale if set and configured.
if ($this['language']->enabled() && $this['config']->get('system.languages.override_locale')) { if ($this['language']->enabled() && $this['config']->get('system.languages.override_locale')) {
@@ -579,7 +567,7 @@ class Grav extends Container
* *
* @return void * @return void
*/ */
public function shutdown(): void public function shutdown()
{ {
// Prevent user abort allowing onShutdown event to run without interruptions. // Prevent user abort allowing onShutdown event to run without interruptions.
if (function_exists('ignore_user_abort')) { if (function_exists('ignore_user_abort')) {
@@ -698,7 +686,7 @@ class Grav extends Container
* *
* @return void * @return void
*/ */
protected function registerServices(): void protected function registerServices()
{ {
foreach (self::$diMap as $serviceKey => $serviceClass) { foreach (self::$diMap as $serviceKey => $serviceClass) {
if (is_int($serviceKey)) { if (is_int($serviceKey)) {
@@ -765,10 +753,12 @@ class Grav extends Container
// unsupported media type, try to download it... // unsupported media type, try to download it...
if ($uri_extension) { if ($uri_extension) {
$extension = $uri_extension; $extension = $uri_extension;
} elseif (isset($path_parts['extension'])) {
$extension = $path_parts['extension'];
} else { } else {
$extension = null; if (isset($path_parts['extension'])) {
$extension = $path_parts['extension'];
} else {
$extension = null;
}
} }
if ($extension) { if ($extension) {
@@ -778,9 +768,11 @@ class Grav extends Container
} }
Utils::download($page->path() . DIRECTORY_SEPARATOR . $uri->basename(), $download); Utils::download($page->path() . DIRECTORY_SEPARATOR . $uri->basename(), $download);
} }
// Nothing found
return false;
} }
// Nothing found return $page;
return false;
} }
} }
-130
View File
@@ -1,130 +0,0 @@
<?php
/**
* @package Grav\Common\HTTP
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\HTTP;
use Grav\Common\Grav;
use Symfony\Component\HttpClient\CurlHttpClient;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpClient\HttpOptions;
use Symfony\Component\HttpClient\NativeHttpClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class Client
{
/** @var callable The callback for the progress, either a function or callback in array notation */
public static $callback = null;
/** @var string[] */
private static $headers = [
'User-Agent' => 'Grav CMS'
];
public static function getClient(array $overrides = [], int $connections = 6, callable $callback = null): HttpClientInterface
{
$config = Grav::instance()['config'];
$options = static::getOptions();
// Use callback if provided
if ($callback) {
self::$callback = $callback;
$options->setOnProgress([Client::class, 'progress']);
}
$settings = array_merge($options->toArray(), $overrides);
$preferred_method = $config->get('system.http.method');
// Try old GPM setting if value is the same as system default
if ($preferred_method === 'auto') {
$preferred_method = $config->get('system.gpm.method', 'auto');
}
switch ($preferred_method) {
case 'curl':
$client = new CurlHttpClient($settings, $connections);
break;
case 'fopen':
case 'native':
$client = new NativeHttpClient($settings, $connections);
break;
default:
$client = HttpClient::create($settings, $connections);
}
return $client;
}
/**
* Get HTTP Options
*
* @return HttpOptions
*/
public static function getOptions(): HttpOptions
{
$config = Grav::instance()['config'];
$referer = defined('GRAV_CLI') ? 'grav_cli' : Grav::instance()['uri']->rootUrl(true);
$options = new HttpOptions();
// Set default Headers
$options->setHeaders(array_merge([ 'Referer' => $referer ], self::$headers));
// Disable verify Peer if required
$verify_peer = $config->get('system.http.verify_peer');
// Try old GPM setting if value is default
if ($verify_peer === true) {
$verify_peer = $config->get('system.gpm.verify_peer', null) ?? $verify_peer;
}
$options->verifyPeer($verify_peer);
// Set verify Host
$verify_host = $config->get('system.http.verify_host', true);
$options->verifyHost($verify_host);
// New setting and must be enabled for Proxy to work
if ($config->get('system.http.enable_proxy', true)) {
// Set proxy url if provided
$proxy_url = $config->get('system.http.proxy_url', $config->get('system.gpm.proxy_url', null));
if ($proxy_url !== null) {
$options->setProxy($proxy_url);
}
// Certificate
$proxy_cert = $config->get('system.http.proxy_cert_path', null);
if ($proxy_cert !== null) {
$options->setCaPath($proxy_cert);
}
}
return $options;
}
/**
* Progress normalized for cURL and Fopen
* Accepts a variable length of arguments passed in by stream method
*
* @return void
*/
public static function progress(int $bytes_transferred, int $filesize, array $info)
{
if ($bytes_transferred > 0) {
$percent = $filesize <= 0 ? 0 : (int)(($bytes_transferred * 100) / $filesize);
$progress = [
'code' => $info['http_code'],
'filesize' => $filesize,
'transferred' => $bytes_transferred,
'percent' => $percent < 100 ? $percent : 100
];
if (self::$callback !== null) {
call_user_func(self::$callback, $progress);
}
}
}
}
-96
View File
@@ -1,96 +0,0 @@
<?php
/**
* @package Grav\Common\HTTP
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\HTTP;
use Exception;
use Grav\Common\Utils;
use Grav\Common\Grav;
use Symfony\Component\HttpClient\CurlHttpClient;
use Symfony\Component\HttpClient\Exception\TransportException;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpClient\HttpOptions;
use Symfony\Component\HttpClient\NativeHttpClient;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
use function call_user_func;
use function defined;
/**
* Class Response
* @package Grav\Common\GPM
*/
class Response
{
/**
* Backwards compatible helper method
*
* @param string $uri
* @param array $overrides
* @param callable|null $callback
* @return string
* @throws TransportExceptionInterface|RedirectionExceptionInterface|ServerExceptionInterface|TransportExceptionInterface|ClientExceptionInterface
*/
public static function get(string $uri = '', array $overrides = [], callable $callback = null): string
{
$response = static::request('GET', $uri, $overrides, $callback);
return $response->getContent();
}
/**
* Makes a request to the URL by using the preferred method
*
* @param string $method method to call such as GET, PUT, etc
* @param string $uri URL to call
* @param array $overrides An array of parameters for both `curl` and `fopen`
* @param callable|null $callback Either a function or callback in array notation
* @return ResponseInterface
* @throws TransportExceptionInterface
*/
public static function request(string $method, string $uri, array $overrides = [], callable $callback = null): ResponseInterface
{
if (empty($method)) {
throw new TransportException('missing method (GET, PUT, etc.)');
}
if (empty($uri)) {
throw new TransportException('missing URI');
}
// check if this function is available, if so use it to stop any timeouts
try {
if (Utils::functionExists('set_time_limit')) {
@set_time_limit(0);
}
} catch (Exception $e) {}
$client = Client::getClient($overrides, 6, $callback);
return $client->request($method, $uri);
}
/**
* Is this a remote file or not
*
* @param string $file
* @return bool
*/
public static function isRemote($file): bool
{
return (bool) filter_var($file, FILTER_VALIDATE_URL);
}
}
+1 -6
View File
@@ -33,9 +33,6 @@ class Excerpts
public static function processImageHtml($html, PageInterface $page = null) public static function processImageHtml($html, PageInterface $page = null)
{ {
$excerpt = static::getExcerptFromHtml($html, 'img'); $excerpt = static::getExcerptFromHtml($html, 'img');
if (null === $excerpt) {
return '';
}
$original_src = $excerpt['element']['attributes']['src']; $original_src = $excerpt['element']['attributes']['src'];
$excerpt['element']['attributes']['href'] = $original_src; $excerpt['element']['attributes']['href'] = $original_src;
@@ -64,9 +61,6 @@ class Excerpts
public static function processLinkHtml($html, PageInterface $page = null) public static function processLinkHtml($html, PageInterface $page = null)
{ {
$excerpt = static::getExcerptFromHtml($html, 'a'); $excerpt = static::getExcerptFromHtml($html, 'a');
if (null === $excerpt) {
return '';
}
$original_href = $excerpt['element']['attributes']['href']; $original_href = $excerpt['element']['attributes']['href'];
$excerpt = static::processLinkExcerpt($excerpt, $page, 'link'); $excerpt = static::processLinkExcerpt($excerpt, $page, 'link');
@@ -95,6 +89,7 @@ class Excerpts
$excerpt = null; $excerpt = null;
$inner = []; $inner = [];
/** @var DOMElement $element */
foreach ($elements as $element) { foreach ($elements as $element) {
$attributes = []; $attributes = [];
foreach ($element->attributes as $name => $value) { foreach ($element->attributes as $name => $value) {
+10 -12
View File
@@ -53,6 +53,7 @@ class LogViewer
*/ */
public function tail($filepath, $lines = 1) public function tail($filepath, $lines = 1)
{ {
$f = $filepath ? @fopen($filepath, 'rb') : false; $f = $filepath ? @fopen($filepath, 'rb') : false;
if ($f === false) { if ($f === false) {
return false; return false;
@@ -61,12 +62,13 @@ class LogViewer
$buffer = ($lines < 2 ? 64 : ($lines < 10 ? 512 : 4096)); $buffer = ($lines < 2 ? 64 : ($lines < 10 ? 512 : 4096));
fseek($f, -1, SEEK_END); fseek($f, -1, SEEK_END);
if (fread($f, 1) !== "\n") { if (fread($f, 1) != "\n") {
--$lines; $lines -= 1;
} }
// Start reading // Start reading
$output = ''; $output = '';
$chunk = '';
// While we would like more // While we would like more
while (ftell($f) > 0 && $lines >= 0) { while (ftell($f) > 0 && $lines >= 0) {
// Figure out how far back we should jump // Figure out how far back we should jump
@@ -74,11 +76,7 @@ class LogViewer
// Do the jump (backwards, relative to where we are) // Do the jump (backwards, relative to where we are)
fseek($f, -$seek, SEEK_CUR); fseek($f, -$seek, SEEK_CUR);
// Read a chunk and prepend it to our output // Read a chunk and prepend it to our output
$chunk = fread($f, $seek); $output = ($chunk = fread($f, $seek)) . $output;
if ($chunk === false) {
throw new \RuntimeException('Cannot read file');
}
$output = $chunk . $output;
// Jump back to where we started reading // Jump back to where we started reading
fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR); fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR);
// Decrease our line counter // Decrease our line counter
@@ -125,13 +123,13 @@ class LogViewer
*/ */
public function parse($line) public function parse($line)
{ {
if (!is_string($line) || $line === '') { if (!is_string($line) || strlen($line) === 0) {
return []; return array();
} }
preg_match($this->pattern, $line, $data); preg_match($this->pattern, $line, $data);
if (!isset($data['date'])) { if (!isset($data['date'])) {
return []; return array();
} }
preg_match('/(.*)- Trace:(.*)/', $data['message'], $matches); preg_match('/(.*)- Trace:(.*)/', $data['message'], $matches);
@@ -140,7 +138,7 @@ class LogViewer
$data['trace'] = trim($matches[2]); $data['trace'] = trim($matches[2]);
} }
return [ return array(
'date' => DateTime::createFromFormat('Y-m-d H:i:s', $data['date']), 'date' => DateTime::createFromFormat('Y-m-d H:i:s', $data['date']),
'logger' => $data['logger'], 'logger' => $data['logger'],
'level' => $data['level'], 'level' => $data['level'],
@@ -148,7 +146,7 @@ class LogViewer
'trace' => isset($data['trace']) ? $this->parseTrace($data['trace']) : null, 'trace' => isset($data['trace']) ? $this->parseTrace($data['trace']) : null,
'context' => json_decode($data['context'], true), 'context' => json_decode($data['context'], true),
'extra' => json_decode($data['extra'], true) 'extra' => json_decode($data['extra'], true)
]; );
} }
/** /**
+3 -1
View File
@@ -230,7 +230,9 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
public function filter(callable $callback = null) public function filter(callable $callback = null)
{ {
foreach ($this->items as $key => $value) { foreach ($this->items as $key => $value) {
if ((!$callback && !(bool)$value) || ($callback && !$callback($value, $key))) { if ((!$callback && !(bool)$value) ||
($callback && !$callback($value, $key))
) {
unset($this->items[$key]); unset($this->items[$key]);
} }
} }
@@ -86,14 +86,12 @@ class LanguageCodes
'ja-JP' => [ 'name' => 'Japanese', 'nativeName' => '日本語' ], // not iso-639-1 'ja-JP' => [ 'name' => 'Japanese', 'nativeName' => '日本語' ], // not iso-639-1
'ka' => [ 'name' => 'Georgian', 'nativeName' => 'ქართული' ], 'ka' => [ 'name' => 'Georgian', 'nativeName' => 'ქართული' ],
'kk' => [ 'name' => 'Kazakh', 'nativeName' => 'Қазақ' ], 'kk' => [ 'name' => 'Kazakh', 'nativeName' => 'Қазақ' ],
'km' => [ 'name' => 'Khmer', 'nativeName' => 'Khmer' ],
'kn' => [ 'name' => 'Kannada', 'nativeName' => 'ಕನ್ನಡ' ], 'kn' => [ 'name' => 'Kannada', 'nativeName' => 'ಕನ್ನಡ' ],
'ko' => [ 'name' => 'Korean', 'nativeName' => '한국어' ], 'ko' => [ 'name' => 'Korean', 'nativeName' => '한국어' ],
'ku' => [ 'name' => 'Kurdish', 'nativeName' => 'Kurdî' ], 'ku' => [ 'name' => 'Kurdish', 'nativeName' => 'Kurdî' ],
'la' => [ 'name' => 'Latin', 'nativeName' => 'Latina' ], 'la' => [ 'name' => 'Latin', 'nativeName' => 'Latina' ],
'lb' => [ 'name' => 'Luxembourgish', 'nativeName' => 'Lëtzebuergesch' ], 'lb' => [ 'name' => 'Luxembourgish', 'nativeName' => 'Lëtzebuergesch' ],
'lg' => [ 'name' => 'Luganda', 'nativeName' => 'Luganda' ], 'lg' => [ 'name' => 'Luganda', 'nativeName' => 'Luganda' ],
'lo' => [ 'name' => 'Lao', 'nativeName' => 'Lao' ],
'lt' => [ 'name' => 'Lithuanian', 'nativeName' => 'Lietuvių' ], 'lt' => [ 'name' => 'Lithuanian', 'nativeName' => 'Lietuvių' ],
'lv' => [ 'name' => 'Latvian', 'nativeName' => 'Latviešu' ], 'lv' => [ 'name' => 'Latvian', 'nativeName' => 'Latviešu' ],
'mai' => [ 'name' => 'Maithili', 'nativeName' => 'मैथिली মৈথিলী' ], 'mai' => [ 'name' => 'Maithili', 'nativeName' => 'मैथिली মৈথিলী' ],
@@ -103,7 +101,6 @@ class LanguageCodes
'ml' => [ 'name' => 'Malayalam', 'nativeName' => 'മലയാളം' ], 'ml' => [ 'name' => 'Malayalam', 'nativeName' => 'മലയാളം' ],
'mn' => [ 'name' => 'Mongolian', 'nativeName' => 'Монгол' ], 'mn' => [ 'name' => 'Mongolian', 'nativeName' => 'Монгол' ],
'mr' => [ 'name' => 'Marathi', 'nativeName' => 'मराठी' ], 'mr' => [ 'name' => 'Marathi', 'nativeName' => 'मराठी' ],
'my' => [ 'name' => 'Myanmar (Burmese)', 'nativeName' => 'ဗမာी' ],
'no' => [ 'name' => 'Norwegian', 'nativeName' => 'Norsk' ], 'no' => [ 'name' => 'Norwegian', 'nativeName' => 'Norsk' ],
'nb' => [ 'name' => 'Norwegian', 'nativeName' => 'Norsk' ], 'nb' => [ 'name' => 'Norwegian', 'nativeName' => 'Norsk' ],
'nb-NO' => [ 'name' => 'Norwegian (Bokmål)', 'nativeName' => 'Norsk bokmål' ], 'nb-NO' => [ 'name' => 'Norwegian (Bokmål)', 'nativeName' => 'Norsk bokmål' ],
@@ -135,7 +132,6 @@ class LanguageCodes
'st' => [ 'name' => 'Southern Sotho', 'nativeName' => 'Sesotho' ], 'st' => [ 'name' => 'Southern Sotho', 'nativeName' => 'Sesotho' ],
'sv' => [ 'name' => 'Swedish', 'nativeName' => 'Svenska' ], 'sv' => [ 'name' => 'Swedish', 'nativeName' => 'Svenska' ],
'sv-SE' => [ 'name' => 'Swedish', 'nativeName' => 'Svenska' ], 'sv-SE' => [ 'name' => 'Swedish', 'nativeName' => 'Svenska' ],
'sw' => [ 'name' => 'Swahili', 'nativeName' => 'Swahili' ],
'ta' => [ 'name' => 'Tamil', 'nativeName' => 'தமிழ்' ], 'ta' => [ 'name' => 'Tamil', 'nativeName' => 'தமிழ்' ],
'ta-IN' => [ 'name' => 'Tamil (India)', 'nativeName' => 'தமிழ் (இந்தியா)' ], 'ta-IN' => [ 'name' => 'Tamil (India)', 'nativeName' => 'தமிழ் (இந்தியா)' ],
'ta-LK' => [ 'name' => 'Tamil (Sri Lanka)', 'nativeName' => 'தமிழ் (இலங்கை)' ], 'ta-LK' => [ 'name' => 'Tamil (Sri Lanka)', 'nativeName' => 'தமிழ் (இலங்கை)' ],
@@ -191,7 +187,12 @@ class LanguageCodes
*/ */
public static function getOrientation($code) public static function getOrientation($code)
{ {
return static::$codes[$code]['orientation'] ?? 'ltr'; if (isset(static::$codes[$code])) {
if (isset(static::$codes[$code]['orientation'])) {
return static::get($code, 'orientation');
}
}
return 'ltr';
} }
/** /**
@@ -225,7 +226,11 @@ class LanguageCodes
*/ */
public static function get($code, $type) public static function get($code, $type)
{ {
return static::$codes[$code][$type] ?? false; if (isset(static::$codes[$code][$type])) {
return static::$codes[$code][$type];
}
return false;
} }
/** /**
@@ -50,15 +50,13 @@ trait ImageMediaTrait
/** @var integer */ /** @var integer */
protected $retina_scale; protected $retina_scale;
/** @var bool */
protected $watermark;
/** @var array */ /** @var array */
public static $magic_actions = [ public static $magic_actions = [
'resize', 'forceResize', 'cropResize', 'crop', 'zoomCrop', 'resize', 'forceResize', 'cropResize', 'crop', 'zoomCrop',
'negate', 'brightness', 'contrast', 'grayscale', 'emboss', 'negate', 'brightness', 'contrast', 'grayscale', 'emboss',
'smooth', 'sharp', 'edge', 'colorize', 'sepia', 'enableProgressive', 'smooth', 'sharp', 'edge', 'colorize', 'sepia', 'enableProgressive',
'rotate', 'flip', 'fixOrientation', 'gaussianBlur', 'format', 'create', 'fill', 'merge' 'rotate', 'flip', 'fixOrientation', 'gaussianBlur', 'format'
]; ];
/** @var array */ /** @var array */
@@ -381,8 +379,6 @@ trait ImageMediaTrait
$this->aspect_ratio = $config->get('system.images.cls.aspect_ratio', false); $this->aspect_ratio = $config->get('system.images.cls.aspect_ratio', false);
$this->retina_scale = $config->get('system.images.cls.retina_scale', 1); $this->retina_scale = $config->get('system.images.cls.retina_scale', 1);
$this->watermark = $config->get('system.images.watermark.watermark_all', false);
return $this; return $this;
} }
@@ -419,10 +415,6 @@ trait ImageMediaTrait
$this->image->merge(ImageFile::open($overlay)); $this->image->merge(ImageFile::open($overlay));
} }
if ($this->watermark) {
$this->watermark();
}
return $this->image->cacheFile($this->format, $this->quality, false, [$this->get('width'), $this->get('height'), $this->get('modified')]); return $this->image->cacheFile($this->format, $this->quality, false, [$this->get('width'), $this->get('height'), $this->get('modified')]);
} }
} }
@@ -20,13 +20,11 @@ use Grav\Common\Security;
use Grav\Common\Utils; use Grav\Common\Utils;
use Grav\Framework\Filesystem\Filesystem; use Grav\Framework\Filesystem\Filesystem;
use Grav\Framework\Form\FormFlashFile; use Grav\Framework\Form\FormFlashFile;
use Grav\Framework\Mime\MimeTypes;
use Psr\Http\Message\UploadedFileInterface; use Psr\Http\Message\UploadedFileInterface;
use RocketTheme\Toolbox\File\YamlFile; use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RuntimeException; use RuntimeException;
use function dirname; use function dirname;
use function in_array;
/** /**
* Implements media upload and delete functionality. * Implements media upload and delete functionality.
@@ -108,7 +106,7 @@ trait MediaUploadTrait
* *
* @param array $metadata * @param array $metadata
* @param array|null $settings * @param array|null $settings
* @return string * @return string|null
* @throws RuntimeException * @throws RuntimeException
*/ */
public function checkFileMetadata(array $metadata, string $filename = null, array $settings = null): string public function checkFileMetadata(array $metadata, string $filename = null, array $settings = null): string
@@ -181,20 +179,16 @@ trait MediaUploadTrait
} }
} }
$grav = Grav::instance();
/** @var MimeTypes $mimeChecker */
$mimeChecker = $grav['mime'];
// Handle Accepted file types. Accept can only be mime types (image/png | image/*) or file extensions (.pdf | .jpg) // Handle Accepted file types. Accept can only be mime types (image/png | image/*) or file extensions (.pdf | .jpg)
$accepted = false;
$errors = [];
// Do not trust mime type sent by the browser. // Do not trust mime type sent by the browser.
$mime = $metadata['mime'] ?? $mimeChecker->getMimeType($extension); $mime = Utils::getMimeByFilename($filename);
$validExtensions = $mimeChecker->getExtensions($mime); $mimeTest = $metadata['mime'] ?? $mime;
if (!in_array($extension, $validExtensions, true)) { if ($mime !== $mimeTest) {
throw new RuntimeException('The mime type does not match to file extension', 400); throw new RuntimeException('The mime type does not match to file extension', 400);
} }
$accepted = false;
$errors = [];
foreach ((array)$settings['accept'] as $type) { foreach ((array)$settings['accept'] as $type) {
// Force acceptance of any file when star notation // Force acceptance of any file when star notation
if ($type === '*') { if ($type === '*') {
@@ -424,17 +418,6 @@ trait MediaUploadTrait
$uploadedFile->moveTo($filepath); $uploadedFile->moveTo($filepath);
} }
/**
* Get upload settings.
*
* @param array|null $settings Form field specific settings (override).
* @return array
*/
public function getUploadSettings(?array $settings = null): array
{
return null !== $settings ? $settings + $this->_upload_defaults : $this->_upload_defaults;
}
/** /**
* Internal logic to copy file. * Internal logic to copy file.
* *
@@ -621,6 +604,17 @@ trait MediaUploadTrait
} }
} }
/**
* Get upload settings.
*
* @param array|null $settings Form field specific settings (override).
* @return array
*/
protected function getUploadSettings(?array $settings = null): array
{
return null !== $settings ? $settings + $this->_upload_defaults : $this->_upload_defaults;
}
/** /**
* @param string $filename * @param string $filename
* @param string $path * @param string $path
+13 -28
View File
@@ -47,7 +47,7 @@ class Collection extends Iterator implements PageCollectionInterface
parent::__construct($items); parent::__construct($items);
$this->params = $params; $this->params = $params;
$this->pages = $pages ?: Grav::instance()->offsetGet('pages'); $this->pages = $pages ? $pages : Grav::instance()->offsetGet('pages');
} }
/** /**
@@ -145,18 +145,6 @@ class Collection extends Iterator implements PageCollectionInterface
return $this; return $this;
} }
/**
* Set current page.
*/
public function setCurrent(string $path): void
{
reset($this->items);
while (($key = key($this->items)) !== null && $key !== $path) {
next($this->items);
}
}
/** /**
* Returns current page. * Returns current page.
* *
@@ -187,7 +175,6 @@ class Collection extends Iterator implements PageCollectionInterface
* @param string $offset * @param string $offset
* @return PageInterface|null * @return PageInterface|null
*/ */
#[\ReturnTypeWillChange]
public function offsetGet($offset) public function offsetGet($offset)
{ {
return $this->pages->get($offset) ?: null; return $this->pages->get($offset) ?: null;
@@ -332,32 +319,30 @@ class Collection extends Iterator implements PageCollectionInterface
/** /**
* Returns the items between a set of date ranges of either the page date field (default) or * Returns the items between a set of date ranges of either the page date field (default) or
* an arbitrary datetime page field where start date and end date are optional * an arbitrary datetime page field where end date is optional
* Dates must be passed in as text that strtotime() can process * Dates can be passed in as text that strtotime() can process
* http://php.net/manual/en/function.strtotime.php * http://php.net/manual/en/function.strtotime.php
* *
* @param string|null $startDate * @param string $startDate
* @param string|null $endDate * @param bool $endDate
* @param string|null $field * @param string|null $field
* @return $this * @return $this
* @throws Exception * @throws Exception
*/ */
public function dateRange($startDate = null, $endDate = null, $field = null) public function dateRange($startDate, $endDate = false, $field = null)
{ {
$start = $startDate ? Utils::date2timestamp($startDate) : null; $start = Utils::date2timestamp($startDate);
$end = $endDate ? Utils::date2timestamp($endDate) : null; $end = $endDate ? Utils::date2timestamp($endDate) : false;
$date_range = []; $date_range = [];
foreach ($this->items as $path => $slug) { foreach ($this->items as $path => $slug) {
$page = $this->pages->get($path); $page = $this->pages->get($path);
if (!$page) { if ($page !== null) {
continue; $date = $field ? strtotime($page->value($field)) : $page->date();
}
$date = $field ? strtotime($page->value($field)) : $page->date(); if ($date >= $start && (!$end || $date <= $end)) {
$date_range[$path] = $slug;
if ((!$start || $date >= $start) && (!$end || $date <= $end)) { }
$date_range[$path] = $slug;
} }
} }
@@ -158,17 +158,17 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S
/** /**
* Returns the items between a set of date ranges of either the page date field (default) or * Returns the items between a set of date ranges of either the page date field (default) or
* an arbitrary datetime page field where start date and end date are optional * an arbitrary datetime page field where end date is optional
* Dates must be passed in as text that strtotime() can process * Dates can be passed in as text that strtotime() can process
* http://php.net/manual/en/function.strtotime.php * http://php.net/manual/en/function.strtotime.php
* *
* @param string|null $startDate * @param string $startDate
* @param string|null $endDate * @param bool $endDate
* @param string|null $field * @param string|null $field
* @return PageCollectionInterface * @return PageCollectionInterface
* @throws Exception * @throws Exception
*/ */
public function dateRange($startDate = null, $endDate = null, $field = null); public function dateRange($startDate, $endDate = false, $field = null);
/** /**
* Creates new collection with only visible pages * Creates new collection with only visible pages
@@ -58,7 +58,7 @@ interface PageContentInterface
/** /**
* Needed by the onPageContentProcessed event to set the raw page content * Needed by the onPageContentProcessed event to set the raw page content
* *
* @param string|null $content * @param string $content
*/ */
public function setRawContent($content); public function setRawContent($content);
@@ -157,7 +157,7 @@ class Excerpts
// Handle custom streams. // Handle custom streams.
/** @var UniformResourceLocator $locator */ /** @var UniformResourceLocator $locator */
$locator = $grav['locator']; $locator = $grav['locator'];
if ($type === 'link' && $locator->isStream($url)) { if ($locator->isStream($url)) {
$path = $locator->findResource($url, false) ?: $locator->findResource($url, false, true); $path = $locator->findResource($url, false) ?: $locator->findResource($url, false, true);
$url_parts['path'] = $grav['base_url_relative'] . '/' . $path; $url_parts['path'] = $grav['base_url_relative'] . '/' . $path;
unset($url_parts['stream'], $url_parts['scheme']); unset($url_parts['stream'], $url_parts['scheme']);
+2 -5
View File
@@ -63,7 +63,6 @@ class Media extends AbstractMedia
* @param string $offset * @param string $offset
* @return bool * @return bool
*/ */
#[\ReturnTypeWillChange]
public function offsetExists($offset) public function offsetExists($offset)
{ {
return parent::offsetExists($offset) ?: isset(static::$global[$offset]); return parent::offsetExists($offset) ?: isset(static::$global[$offset]);
@@ -73,7 +72,6 @@ class Media extends AbstractMedia
* @param string $offset * @param string $offset
* @return MediaObjectInterface|null * @return MediaObjectInterface|null
*/ */
#[\ReturnTypeWillChange]
public function offsetGet($offset) public function offsetGet($offset)
{ {
return parent::offsetGet($offset) ?: static::$global[$offset]; return parent::offsetGet($offset) ?: static::$global[$offset];
@@ -104,13 +102,12 @@ class Media extends AbstractMedia
foreach ($iterator as $file => $info) { foreach ($iterator as $file => $info) {
// Ignore folders and Markdown files. // Ignore folders and Markdown files.
$filename = $info->getFilename(); if (!$info->isFile() || $info->getExtension() === 'md' || strpos($info->getFilename(), '.') === 0) {
if (!$info->isFile() || $info->getExtension() === 'md' || $filename === 'frontmatter.yaml' || strpos($filename, '.') === 0) {
continue; continue;
} }
// Find out what type we're dealing with // Find out what type we're dealing with
[$basename, $ext, $type, $extra] = $this->getFileParts($filename); [$basename, $ext, $type, $extra] = $this->getFileParts($info->getFilename());
if (!in_array(strtolower($ext), $media_types, true)) { if (!in_array(strtolower($ext), $media_types, true)) {
continue; continue;
@@ -46,7 +46,6 @@ class GlobalMedia extends AbstractMedia
* @param string $offset * @param string $offset
* @return bool * @return bool
*/ */
#[\ReturnTypeWillChange]
public function offsetExists($offset) public function offsetExists($offset)
{ {
return parent::offsetExists($offset) ?: !empty($this->resolveStream($offset)); return parent::offsetExists($offset) ?: !empty($this->resolveStream($offset));
@@ -56,7 +55,6 @@ class GlobalMedia extends AbstractMedia
* @param string $offset * @param string $offset
* @return MediaObjectInterface|null * @return MediaObjectInterface|null
*/ */
#[\ReturnTypeWillChange]
public function offsetGet($offset) public function offsetGet($offset)
{ {
return parent::offsetGet($offset) ?: $this->addMedium($offset); return parent::offsetGet($offset) ?: $this->addMedium($offset);
@@ -17,7 +17,6 @@ use Grav\Common\Media\Interfaces\MediaLinkInterface;
use Grav\Common\Media\Traits\ImageLoadingTrait; use Grav\Common\Media\Traits\ImageLoadingTrait;
use Grav\Common\Media\Traits\ImageMediaTrait; use Grav\Common\Media\Traits\ImageMediaTrait;
use Grav\Common\Utils; use Grav\Common\Utils;
use Gregwar\Image\Image;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use function func_get_args; use function func_get_args;
use function in_array; use function in_array;
@@ -326,67 +325,6 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate
return $this; return $this;
} }
public function watermark($image = null, $position = null, $scale = null)
{
$grav = $this->getGrav();
$locator = $grav['locator'];
$config = $grav['config'];
$args = func_get_args();
$file = $args[0] ?? '1'; // using '1' because of markdown. doing ![](image.jpg?watermark) returns $args[0]='1';
$file = $file === '1' ? $config->get('system.images.watermark.image') : $args[0];
$watermark = $locator->findResource($file);
$watermark = ImageFile::open($watermark);
// Scaling operations
$scale = ($scale ?? $config->get('system.images.watermark.scale', 100)) / 100;
$wwidth = $this->get('width') * $scale;
$wheight = $this->get('height') * $scale;
$watermark->resize($wwidth, $wheight);
// Position operations
$position = !empty($args[1]) ? explode('-', $args[1]) : ['center', 'center']; // todo change to config
$positionY = $position[0] ?? $config->get('system.images.watermark.position_y', 'center');
$positionX = $position[1] ?? $config->get('system.images.watermark.position_x', 'center');
switch ($positionY)
{
case 'top':
$positionY = 0;
break;
case 'bottom':
$positionY = $this->get('height')-$wheight;
break;
case 'center':
$positionY = ($this->get('height')/2) - ($wheight/2);
break;
}
switch ($positionX)
{
case 'left':
$positionX = 0;
break;
case 'right':
$positionX = $this->get('width')-$wwidth;
break;
case 'center':
$positionX = ($this->get('width')/2) - ($wwidth/2);
break;
}
$this->__call('merge', [$watermark,$positionX, $positionY]);
return $this;
}
/** /**
* Handle this commonly used variant * Handle this commonly used variant
* *
@@ -399,37 +337,6 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate
return $this; return $this;
} }
/**
* Add a frame to image
*
* @return $this
*/
public function addFrame(int $border = 10, string $color = '0x000000')
{
if(is_int(intval($border)) && $border>0 && preg_match('/^0x[a-f0-9]{6}$/i', $color)) { // $border must be an integer and bigger than 0; $color must be formatted as an HEX value (0x??????).
$image = ImageFile::open($this->path());
}
else {
return $this;
}
$dst_width = $image->width()+2*$border;
$dst_height = $image->height()+2*$border;
$frame = ImageFile::create($dst_width, $dst_height);
$frame->__call('fill', [$color]);
$this->image = $frame;
$this->__call('merge', [$image, $border, $border]);
$this->saveImage();
return $this;
}
/** /**
* Forward the call to the image processing method. * Forward the call to the image processing method.
* *
@@ -437,7 +344,6 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate
* @param mixed $args * @param mixed $args
* @return $this|mixed * @return $this|mixed
*/ */
public function __call($method, $args) public function __call($method, $args)
{ {
if (!in_array($method, static::$magic_actions, true)) { if (!in_array($method, static::$magic_actions, true)) {
+7 -10
View File
@@ -41,7 +41,9 @@ class Link implements RenderableInterface, MediaLinkInterface
$this->attributes = $attributes; $this->attributes = $attributes;
$source = $medium->reset()->thumbnail('auto')->display('thumbnail'); $source = $medium->reset()->thumbnail('auto')->display('thumbnail');
if (!$source instanceof MediaObjectInterface) {
// FIXME: Thumbnail can be null, maybe we should not allow that?
if (null === $source) {
throw new RuntimeException('Media has no thumbnail set'); throw new RuntimeException('Media has no thumbnail set');
} }
@@ -87,15 +89,10 @@ class Link implements RenderableInterface, MediaLinkInterface
throw new BadMethodCallException(get_class($object) . '::' . $method . '() not found.'); throw new BadMethodCallException(get_class($object) . '::' . $method . '() not found.');
} }
$object = call_user_func_array($callable, $args); $this->source = call_user_func_array($callable, $args);
if (!$object instanceof MediaLinkInterface) {
// Don't start nesting links, if user has multiple link calls in his
// actions, we will drop the previous links.
return $this;
}
$this->source = $object; // Don't start nesting links, if user has multiple link calls in his
// actions, we will drop the previous links.
return $object; return $this->source instanceof MediaLinkInterface ? $this->source : $this;
} }
} }
+3 -22
View File
@@ -218,25 +218,6 @@ class Page implements PageInterface
return $this; return $this;
} }
public function __clone()
{
$this->initialized = false;
$this->header = $this->header ? clone $this->header : null;
}
/**
* @return void
*/
public function initialize(): void
{
if (!$this->initialized) {
$this->initialized = true;
$this->route = null;
$this->raw_route = null;
$this->_forms = null;
}
}
/** /**
* @return void * @return void
*/ */
@@ -994,7 +975,7 @@ class Page implements PageInterface
/** /**
* Needed by the onPageContentProcessed event to set the raw page content * Needed by the onPageContentProcessed event to set the raw page content
* *
* @param string|null $content * @param string $content
* @return void * @return void
*/ */
public function setRawContent($content) public function setRawContent($content)
@@ -2293,11 +2274,11 @@ class Page implements PageInterface
{ {
if ($var !== null) { if ($var !== null) {
// make sure first level are arrays // make sure first level are arrays
array_walk($var, static function (&$value) { array_walk($var, function (&$value) {
$value = (array) $value; $value = (array) $value;
}); });
// make sure all values are strings // make sure all values are strings
array_walk_recursive($var, static function (&$value) { array_walk_recursive($var, function (&$value) {
$value = (string) $value; $value = (string) $value;
}); });
$this->taxonomy = $var; $this->taxonomy = $var;
+15 -77
View File
@@ -196,58 +196,6 @@ class Pages
return $this->baseRoute($lang) . $route; return $this->baseRoute($lang) . $route;
} }
/**
* Get relative referrer route and language code. Returns null if the route isn't within the current base, language (if set) and route.
*
* @example `$langCode = null; $referrer = $pages->referrerRoute($langCode, '/admin');` returns relative referrer url within /admin and updates $langCode
* @example `$langCode = 'en'; $referrer = $pages->referrerRoute($langCode, '/admin');` returns relative referrer url within the /en/admin
*
* @param string|null $langCode Variable to store the language code. If already set, check only against that language.
* @param string $route Optional route within the site.
* @return string|null
* @since 1.7.23
*/
public function referrerRoute(?string &$langCode, string $route = '/'): ?string
{
$referrer = $_SERVER['HTTP_REFERER'] ?? null;
// Start by checking that referrer came from our site.
$root = $this->grav['base_url_absolute'];
if (!is_string($referrer) || !str_starts_with($referrer, $root)) {
return null;
}
/** @var Language $language */
$language = $this->grav['language'];
// Get all language codes and append no language.
if (null === $langCode) {
$languages = $language->enabled() ? $language->getLanguages() : [];
$languages[] = '';
} else {
$languages[] = $langCode;
}
$path_base = rtrim($this->base(), '/');
$path_route = rtrim($route, '/');
// Try to figure out the language code.
foreach ($languages as $code) {
$path_lang = $code ? "/{$code}" : '';
$base = $path_base . $path_lang . $path_route;
if ($referrer === $base || str_starts_with($referrer, "{$base}/")) {
if (null === $langCode) {
$langCode = $code;
}
return substr($referrer, \strlen($base));
}
}
return null;
}
/** /**
* *
* Get base URL for Grav pages. * Get base URL for Grav pages.
@@ -326,7 +274,7 @@ class Pages
* *
* @return void * @return void
*/ */
public function reset(): void public function reset()
{ {
$this->initialized = false; $this->initialized = false;
@@ -592,9 +540,9 @@ class Pages
} }
if (isset($params['dateRange'])) { if (isset($params['dateRange'])) {
$start = $params['dateRange']['start'] ?? null; $start = $params['dateRange']['start'] ?? 0;
$end = $params['dateRange']['end'] ?? null; $end = $params['dateRange']['end'] ?? false;
$field = $params['dateRange']['field'] ?? null; $field = $params['dateRange']['field'] ?? false;
$collection = $collection->dateRange($start, $end, $field); $collection = $collection->dateRange($start, $end, $field);
} }
@@ -606,7 +554,7 @@ class Pages
if (is_array($sort_flags)) { if (is_array($sort_flags)) {
$sort_flags = array_map('constant', $sort_flags); //transform strings to constant value $sort_flags = array_map('constant', $sort_flags); //transform strings to constant value
$sort_flags = array_reduce($sort_flags, static function ($a, $b) { $sort_flags = array_reduce($sort_flags, function ($a, $b) {
return $a | $b; return $a | $b;
}, 0); //merge constant values using bit or }, 0); //merge constant values using bit or
} }
@@ -649,7 +597,7 @@ class Pages
$cmd = $value; $cmd = $value;
$params = []; $params = [];
} elseif (is_array($value) && count($value) === 1 && !is_int(key($value))) { } elseif (is_array($value) && count($value) === 1 && !is_int(key($value))) {
// Format: @command.param: { attr1: value1, attr2: value2 } // Format: @command.param: { attr1: value1, attr2: value2 }
$cmd = (string)key($value); $cmd = (string)key($value);
$params = (array)current($value); $params = (array)current($value);
} else { } else {
@@ -715,39 +663,29 @@ class Pages
switch ($type) { switch ($type) {
case 'all': case 'all':
$collection = $page->children(); return $page->children();
break;
case 'modules': case 'modules':
case 'modular': case 'modular':
$collection = $page->children()->modules(); return $page->children()->modules();
break;
case 'pages': case 'pages':
case 'children': case 'children':
$collection = $page->children()->pages(); return $page->children()->pages();
break;
case 'page': case 'page':
case 'self': case 'self':
$collection = !$page->root() ? (new Collection())->addPage($page) : new Collection(); return !$page->root() ? (new Collection())->addPage($page) : new Collection();
break;
case 'parent': case 'parent':
$parent = $page->parent(); $parent = $page->parent();
$collection = new Collection(); $collection = new Collection();
$collection = $parent ? $collection->addPage($parent) : $collection; return $parent ? $collection->addPage($parent) : $collection;
break;
case 'siblings': case 'siblings':
$parent = $page->parent(); $parent = $page->parent();
$collection = $parent ? $parent->children()->remove($page->path()) : new Collection(); return $parent ? $parent->children()->remove($page->path()) : new Collection();
break;
case 'descendants': case 'descendants':
$collection = $this->all($page)->remove($page->path())->pages(); return $this->all($page)->remove($page->path())->pages();
break;
default: default:
// Unknown type; return empty collection. // Unknown type; return empty collection.
$collection = new Collection(); return new Collection();
break;
} }
return $collection;
} }
/** /**
@@ -1823,7 +1761,7 @@ class Pages
// Build regular expression for all the allowed page extensions. // Build regular expression for all the allowed page extensions.
$page_extensions = $language->getFallbackPageExtensions(); $page_extensions = $language->getFallbackPageExtensions();
$regex = '/^[^\.]*(' . implode('|', array_map( $regex = '/^[^\.]*(' . implode('|', array_map(
static function ($str) { function ($str) {
return preg_quote($str, '/'); return preg_quote($str, '/');
}, },
$page_extensions $page_extensions
-25
View File
@@ -10,7 +10,6 @@
namespace Grav\Common; namespace Grav\Common;
use ArrayAccess; use ArrayAccess;
use Composer\Autoload\ClassLoader;
use Grav\Common\Data\Blueprint; use Grav\Common\Data\Blueprint;
use Grav\Common\Data\Data; use Grav\Common\Data\Data;
use Grav\Common\Page\Interfaces\PageInterface; use Grav\Common\Page\Interfaces\PageInterface;
@@ -43,8 +42,6 @@ class Plugin implements EventSubscriberInterface, ArrayAccess
protected $active = true; protected $active = true;
/** @var Blueprint|null */ /** @var Blueprint|null */
protected $blueprint; protected $blueprint;
/** @var ClassLoader|null */
protected $loader;
/** /**
* By default assign all methods as listeners using the default priority. * By default assign all methods as listeners using the default priority.
@@ -82,24 +79,6 @@ class Plugin implements EventSubscriberInterface, ArrayAccess
} }
} }
/**
* @return ClassLoader|null
* @internal
*/
final public function getAutoloader(): ?ClassLoader
{
return $this->loader;
}
/**
* @param ClassLoader|null $loader
* @internal
*/
final public function setAutoloader(?ClassLoader $loader): void
{
$this->loader = $loader;
}
/** /**
* @param Config $config * @param Config $config
* @return $this * @return $this
@@ -227,7 +206,6 @@ class Plugin implements EventSubscriberInterface, ArrayAccess
* @param string $offset An offset to check for. * @param string $offset An offset to check for.
* @return bool Returns TRUE on success or FALSE on failure. * @return bool Returns TRUE on success or FALSE on failure.
*/ */
#[\ReturnTypeWillChange]
public function offsetExists($offset) public function offsetExists($offset)
{ {
if ($offset === 'title') { if ($offset === 'title') {
@@ -245,7 +223,6 @@ class Plugin implements EventSubscriberInterface, ArrayAccess
* @param string $offset The offset to retrieve. * @param string $offset The offset to retrieve.
* @return mixed Can return all value types. * @return mixed Can return all value types.
*/ */
#[\ReturnTypeWillChange]
public function offsetGet($offset) public function offsetGet($offset)
{ {
if ($offset === 'title') { if ($offset === 'title') {
@@ -264,7 +241,6 @@ class Plugin implements EventSubscriberInterface, ArrayAccess
* @param mixed $value The value to set. * @param mixed $value The value to set.
* @throws LogicException * @throws LogicException
*/ */
#[\ReturnTypeWillChange]
public function offsetSet($offset, $value) public function offsetSet($offset, $value)
{ {
throw new LogicException(__CLASS__ . ' blueprints cannot be modified.'); throw new LogicException(__CLASS__ . ' blueprints cannot be modified.');
@@ -276,7 +252,6 @@ class Plugin implements EventSubscriberInterface, ArrayAccess
* @param string $offset The offset to unset. * @param string $offset The offset to unset.
* @throws LogicException * @throws LogicException
*/ */
#[\ReturnTypeWillChange]
public function offsetUnset($offset) public function offsetUnset($offset)
{ {
throw new LogicException(__CLASS__ . ' blueprints cannot be modified.'); throw new LogicException(__CLASS__ . ' blueprints cannot be modified.');
+16 -25
View File
@@ -143,7 +143,7 @@ class Plugins extends Iterator
$instance->setConfig($config); $instance->setConfig($config);
// Register autoloader. // Register autoloader.
if (method_exists($instance, 'autoload')) { if (method_exists($instance, 'autoload')) {
$instance->setAutoloader($instance->autoload()); $instance->autoload();
} }
// Register event listeners. // Register event listeners.
$events->addSubscriber($instance); $events->addSubscriber($instance);
@@ -287,42 +287,33 @@ class Plugins extends Iterator
{ {
// NOTE: ALL THE LOCAL VARIABLES ARE USED INSIDE INCLUDED FILE, DO NOT REMOVE THEM! // NOTE: ALL THE LOCAL VARIABLES ARE USED INSIDE INCLUDED FILE, DO NOT REMOVE THEM!
$grav = Grav::instance(); $grav = Grav::instance();
/** @var UniformResourceLocator $locator */
$locator = $grav['locator']; $locator = $grav['locator'];
$class = null;
// Start by attempting to load the plugin_name.php file.
$file = $locator->findResource('plugins://' . $name . DS . $name . PLUGIN_EXT); $file = $locator->findResource('plugins://' . $name . DS . $name . PLUGIN_EXT);
if (is_file($file)) { if (is_file($file)) {
// Local variables available in the file: $grav, $name, $file // Local variables available in the file: $grav, $name, $file
$class = include_once $file; $class = include_once $file;
if (!is_object($class) || !is_subclass_of($class, Plugin::class, true)) {
$class = null;
}
}
// If the class hasn't been initialized yet, guess the class name and create a new instance. if (!$class || !is_subclass_of($class, Plugin::class, true)) {
if (null === $class) { $className = Inflector::camelize($name);
$className = Inflector::camelize($name); $pluginClassFormat = [
$pluginClassFormat = [ 'Grav\\Plugin\\' . ucfirst($name). 'Plugin',
'Grav\\Plugin\\' . ucfirst($name). 'Plugin', 'Grav\\Plugin\\' . $className . 'Plugin',
'Grav\\Plugin\\' . $className . 'Plugin', 'Grav\\Plugin\\' . $className
'Grav\\Plugin\\' . $className ];
];
foreach ($pluginClassFormat as $pluginClass) { foreach ($pluginClassFormat as $pluginClass) {
if (is_subclass_of($pluginClass, Plugin::class, true)) { if (is_subclass_of($pluginClass, Plugin::class, true)) {
$class = new $pluginClass($name, $grav); $class = new $pluginClass($name, $grav);
break; break;
}
} }
} }
} } else {
// Log a warning if plugin cannot be found.
if (null === $class) {
$grav['log']->addWarning( $grav['log']->addWarning(
sprintf("Plugin '%s' enabled but not found! Try clearing cache with `bin/grav clearcache`", $name) sprintf("Plugin '%s' enabled but not found! Try clearing cache with `bin/grav clearcache`", $name)
); );
return null;
} }
return $class; return $class;
@@ -44,7 +44,7 @@ class InitializeProcessor extends ProcessorBase
public $title = 'Initialize'; public $title = 'Initialize';
/** @var bool */ /** @var bool */
protected static $cli_initialized = false; private static $cli_initialized = false;
/** /**
* @param Grav $grav * @param Grav $grav
@@ -105,7 +105,7 @@ class InitializeProcessor extends ProcessorBase
// TODO: remove in 2.0. // TODO: remove in 2.0.
$this->container['accounts']; $this->container['accounts'];
// Initialize session (used by URI, see issue #3269). // Initialize session.
$this->initializeSession($config); $this->initializeSession($config);
// Initialize URI (uses session, see issue #3269). // Initialize URI (uses session, see issue #3269).
@@ -10,8 +10,6 @@
namespace Grav\Common\Processors; namespace Grav\Common\Processors;
use Grav\Common\Page\Interfaces\PageInterface; use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Framework\RequestHandler\Exception\RequestException;
use Grav\Plugin\Form\Forms;
use RocketTheme\Toolbox\Event\Event; use RocketTheme\Toolbox\Event\Event;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
@@ -42,38 +40,15 @@ class PagesProcessor extends ProcessorBase
$this->container['debugger']->addMessage($this->container['cache']->getCacheStatus()); $this->container['debugger']->addMessage($this->container['cache']->getCacheStatus());
$this->container['pages']->init(); $this->container['pages']->init();
$this->container->fireEvent('onPagesInitialized', new Event(['pages' => $this->container['pages']]));
$route = $this->container['route']; $this->container->fireEvent('onPageInitialized', new Event(['page' => $this->container['page']]));
$this->container->fireEvent('onPagesInitialized', new Event(
[
'pages' => $this->container['pages'],
'route' => $route,
'request' => $request
]
));
$this->container->fireEvent('onPageInitialized', new Event(
[
'page' => $this->container['page'],
'route' => $route,
'request' => $request
]
));
/** @var PageInterface $page */ /** @var PageInterface $page */
$page = $this->container['page']; $page = $this->container['page'];
if (!$page->routable()) { if (!$page->routable()) {
$exception = new RequestException($request, 'Page Not Found', 404);
// If no page found, fire event // If no page found, fire event
$event = new Event([ $event = new Event(['page' => $page]);
'page' => $page,
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
'exception' => $exception,
'route' => $route,
'request' => $request
]);
$event->page = null; $event->page = null;
$event = $this->container->fireEvent('onPageNotFound', $event); $event = $this->container->fireEvent('onPageNotFound', $event);
@@ -90,18 +65,12 @@ class PagesProcessor extends ProcessorBase
$task = $this->container['task']; $task = $this->container['task'];
$action = $this->container['action']; $action = $this->container['action'];
/** @var Forms $forms */
$forms = $this->container['forms'] ?? null;
$form = $forms ? $forms->getActiveForm() : null;
$options = ['page' => $page, 'form' => $form, 'request' => $request];
if ($task) { if ($task) {
$event = new Event(['task' => $task] + $options); $event = new Event(['task' => $task, 'page' => $page]);
$this->container->fireEvent('onPageTask', $event); $this->container->fireEvent('onPageTask', $event);
$this->container->fireEvent('onPageTask.' . $task, $event); $this->container->fireEvent('onPageTask.' . $task, $event);
} elseif ($action) { } elseif ($action) {
$event = new Event(['action' => $action] + $options); $event = new Event(['action' => $action, 'page' => $page]);
$this->container->fireEvent('onPageAction', $event); $this->container->fireEvent('onPageAction', $event);
$this->container->fireEvent('onPageAction.' . $action, $event); $this->container->fireEvent('onPageAction.' . $action, $event);
} }
@@ -14,7 +14,6 @@ use Grav\Framework\Psr7\Response;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Server\RequestHandlerInterface;
use RocketTheme\Toolbox\Event\Event;
/** /**
* Class RenderProcessor * Class RenderProcessor
@@ -43,27 +42,23 @@ class RenderProcessor extends ProcessorBase
return $output; return $output;
} }
/** @var PageInterface $page */ ob_start();
$page = $this->container['page'];
// Use internal Grav output. // Use internal Grav output.
$container->output = $output; $container->output = $output;
$container->fireEvent('onOutputGenerated');
ob_start();
$event = new Event(['page' => $page, 'output' => &$container->output]);
$container->fireEvent('onOutputGenerated', $event);
echo $container->output; echo $container->output;
$html = ob_get_clean();
// remove any output // remove any output
$container->output = ''; $container->output = '';
$event = new Event(['page' => $page, 'output' => $html]); $this->container->fireEvent('onOutputRendered');
$this->container->fireEvent('onOutputRendered', $event);
$html = ob_get_clean();
/** @var PageInterface $page */
$page = $this->container['page'];
$this->stopTimer(); $this->stopTimer();
return new Response($page->httpResponseCode(), $page->httpHeaders(), $html); return new Response($page->httpResponseCode(), $page->httpHeaders(), $html);
+2 -4
View File
@@ -271,7 +271,7 @@ class Job
if ($whenOverlapping) { if ($whenOverlapping) {
$this->whenOverlapping = $whenOverlapping; $this->whenOverlapping = $whenOverlapping;
} else { } else {
$this->whenOverlapping = static function () { $this->whenOverlapping = function () {
return false; return false;
}; };
} }
@@ -390,9 +390,7 @@ class Job
if (count($this->outputTo) > 0) { if (count($this->outputTo) > 0) {
foreach ($this->outputTo as $file) { foreach ($this->outputTo as $file) {
$output_mode = $this->outputMode === 'append' ? FILE_APPEND | LOCK_EX : LOCK_EX; $output_mode = $this->outputMode === 'append' ? FILE_APPEND | LOCK_EX : LOCK_EX;
$timestamp = (new DateTime('now'))->format('c'); file_put_contents($file, $this->output, $output_mode);
$output = $timestamp . "\n" . str_pad('', strlen($timestamp), '>') . "\n" . $this->output;
file_put_contents($file, $output, $output_mode);
} }
} }
+15 -19
View File
@@ -9,11 +9,10 @@
namespace Grav\Common; namespace Grav\Common;
use enshrined\svgSanitize\Sanitizer;
use Exception; use Exception;
use Grav\Common\Config\Config; use Grav\Common\Config\Config;
use Grav\Common\Filesystem\Folder;
use Grav\Common\Page\Pages; use Grav\Common\Page\Pages;
use Rhukster\DomSanitizer\DOMSanitizer;
use function chr; use function chr;
use function count; use function count;
use function is_array; use function is_array;
@@ -34,7 +33,7 @@ class Security
public static function sanitizeSvgString(string $svg): string public static function sanitizeSvgString(string $svg): string
{ {
if (Grav::instance()['config']->get('security.sanitize_svg')) { if (Grav::instance()['config']->get('security.sanitize_svg')) {
$sanitizer = new DOMSanitizer(DOMSanitizer::SVG); $sanitizer = new Sanitizer();
$sanitized = $sanitizer->sanitize($svg); $sanitized = $sanitizer->sanitize($svg);
if (is_string($sanitized)) { if (is_string($sanitized)) {
$svg = $sanitized; $svg = $sanitized;
@@ -53,20 +52,13 @@ class Security
public static function sanitizeSVG(string $file): void public static function sanitizeSVG(string $file): void
{ {
if (file_exists($file) && Grav::instance()['config']->get('security.sanitize_svg')) { if (file_exists($file) && Grav::instance()['config']->get('security.sanitize_svg')) {
$sanitizer = new DOMSanitizer(DOMSanitizer::SVG); $sanitizer = new Sanitizer();
$original_svg = file_get_contents($file); $original_svg = file_get_contents($file);
$clean_svg = $sanitizer->sanitize($original_svg); $clean_svg = $sanitizer->sanitize($original_svg);
// Quarantine bad SVG files and throw exception // TODO: what to do with bad SVG files which return false?
if ($clean_svg !== false ) { if ($clean_svg !== false && $clean_svg !== $original_svg) {
file_put_contents($file, $clean_svg); file_put_contents($file, $clean_svg);
} else {
$quarantine_file = basename($file);
$quarantine_dir = 'log://quarantine';
Folder::mkdir($quarantine_dir);
file_put_contents("$quarantine_dir/$quarantine_file", $original_svg);
unlink($file);
throw new Exception('SVG could not be sanitized, it has been moved to the logs/quarantine folder');
} }
} }
} }
@@ -107,7 +99,7 @@ class Security
$content = $page->value('content'); $content = $page->value('content');
$data = ['header' => $header, 'content' => $content]; $data = ['header' => $header, 'content' => $content];
$results = static::detectXssFromArray($data); $results = Security::detectXssFromArray($data);
if (!empty($results)) { if (!empty($results)) {
if ($route) { if ($route) {
@@ -138,7 +130,7 @@ class Security
$options = static::getXssDefaults(); $options = static::getXssDefaults();
} }
$list = [[]]; $list = [];
foreach ($array as $key => $value) { foreach ($array as $key => $value) {
if (is_array($value)) { if (is_array($value)) {
$list[] = static::detectXssFromArray($value, $prefix . $key . '.', $options); $list[] = static::detectXssFromArray($value, $prefix . $key . '.', $options);
@@ -148,7 +140,11 @@ class Security
} }
} }
return array_merge(...$list); if (!empty($list)) {
return array_merge(...$list);
}
return $list;
} }
/** /**
@@ -195,7 +191,7 @@ class Security
$string = urldecode($string); $string = urldecode($string);
// Convert Hexadecimals // Convert Hexadecimals
$string = (string)preg_replace_callback('!(&#|\\\)[xX]([0-9a-fA-F]+);?!u', static function ($m) { $string = (string)preg_replace_callback('!(&#|\\\)[xX]([0-9a-fA-F]+);?!u', function ($m) {
return chr(hexdec($m[2])); return chr(hexdec($m[2]));
}, $string); }, $string);
@@ -203,7 +199,7 @@ class Security
$string = preg_replace('!(&#0+[0-9]+)!u', '$1;', $string); $string = preg_replace('!(&#0+[0-9]+)!u', '$1;', $string);
// Decode entities // Decode entities
$string = html_entity_decode($string, ENT_NOQUOTES | ENT_HTML5, 'UTF-8'); $string = html_entity_decode($string, ENT_NOQUOTES, 'UTF-8');
// Strip whitespace characters // Strip whitespace characters
$string = preg_replace('!\s!u', '', $string); $string = preg_replace('!\s!u', '', $string);
@@ -235,7 +231,7 @@ class Security
} }
} }
return null; return false;
} }
public static function getXssDefaults(): array public static function getXssDefaults(): array
@@ -17,7 +17,6 @@ use Grav\Common\Config\Config;
use Grav\Common\Config\ConfigFileFinder; use Grav\Common\Config\ConfigFileFinder;
use Grav\Common\Config\Setup; use Grav\Common\Config\Setup;
use Grav\Common\Language\Language; use Grav\Common\Language\Language;
use Grav\Framework\Mime\MimeTypes;
use Pimple\Container; use Pimple\Container;
use Pimple\ServiceProviderInterface; use Pimple\ServiceProviderInterface;
use RocketTheme\Toolbox\File\YamlFile; use RocketTheme\Toolbox\File\YamlFile;
@@ -57,19 +56,6 @@ class ConfigServiceProvider implements ServiceProviderInterface
return $config; return $config;
}; };
$container['mime'] = function ($c) {
/** @var Config $config */
$config = $c['config'];
$mimes = $config->get('mime.types', []);
foreach ($config->get('media.types', []) as $ext => $media) {
if (!empty($media['mime'])) {
$mimes[$ext] = array_unique(array_merge([$media['mime']], $mimes[$ext] ?? []));
}
}
return MimeTypes::createFromMimes($mimes);
};
$container['languages'] = function ($c) { $container['languages'] = function ($c) {
return static::languages($c); return static::languages($c);
}; };
@@ -179,7 +165,7 @@ class ConfigServiceProvider implements ServiceProviderInterface
* @param string $folder_path * @param string $folder_path
* @return array * @return array
*/ */
protected static function pluginFolderPaths($plugins, $folder_path) private static function pluginFolderPaths($plugins, $folder_path)
{ {
$paths = []; $paths = [];
@@ -12,7 +12,6 @@ namespace Grav\Common\Service;
use Grav\Common\Grav; use Grav\Common\Grav;
use Pimple\Container; use Pimple\Container;
use Pimple\ServiceProviderInterface; use Pimple\ServiceProviderInterface;
use Psr\Http\Message\ServerRequestInterface;
/** /**
* Class TaskServiceProvider * Class TaskServiceProvider
@@ -27,11 +26,7 @@ class TaskServiceProvider implements ServiceProviderInterface
public function register(Container $container) public function register(Container $container)
{ {
$container['task'] = function (Grav $c) { $container['task'] = function (Grav $c) {
/** @var ServerRequestInterface $request */ $task = $_POST['task'] ?? $c['uri']->param('task');
$request = $c['request'];
$body = $request->getParsedBody();
$task = $body['task'] ?? $c['uri']->param('task');
if (null !== $task) { if (null !== $task) {
$task = filter_var($task, FILTER_SANITIZE_STRING); $task = filter_var($task, FILTER_SANITIZE_STRING);
} }
@@ -40,11 +35,7 @@ class TaskServiceProvider implements ServiceProviderInterface
}; };
$container['action'] = function (Grav $c) { $container['action'] = function (Grav $c) {
/** @var ServerRequestInterface $request */ $action = $_POST['action'] ?? $c['uri']->param('action');
$request = $c['request'];
$body = $request->getParsedBody();
$action = $body['action'] ?? $c['uri']->param('action');
if (null !== $action) { if (null !== $action) {
$action = filter_var($action, FILTER_SANITIZE_STRING); $action = filter_var($action, FILTER_SANITIZE_STRING);
} }
+6 -10
View File
@@ -12,7 +12,6 @@ namespace Grav\Common;
use Grav\Common\Form\FormFlash; use Grav\Common\Form\FormFlash;
use Grav\Events\SessionStartEvent; use Grav\Events\SessionStartEvent;
use Grav\Plugin\Form\Forms; use Grav\Plugin\Form\Forms;
use JsonException;
use function is_string; use function is_string;
/** /**
@@ -129,12 +128,12 @@ class Session extends \Grav\Framework\Session\Session
/** @var Uri $uri */ /** @var Uri $uri */
$uri = $grav['uri']; $uri = $grav['uri'];
/** @var Forms|null $form */ /** @var Forms|null $form */
$form = $grav['forms']->getActiveForm(); // @phpstan-ignore-line (form plugin) $form = $grav['forms']->getActiveForm(); // @phpstan-ignore-line
$sessionField = base64_encode($uri->url); $sessionField = base64_encode($uri->url);
/** @var FormFlash|null $flash */ /** @var FormFlash|null $flash */
$flash = $form ? $form->getFlash() : null; // @phpstan-ignore-line (form plugin) $flash = $form ? $form->getFlash() : null; // @phpstan-ignore-line
$object = $flash && method_exists($flash, 'getLegacyFiles') ? [$sessionField => $flash->getLegacyFiles()] : null; $object = $flash && method_exists($flash, 'getLegacyFiles') ? [$sessionField => $flash->getLegacyFiles()] : null;
} }
} }
@@ -149,11 +148,10 @@ class Session extends \Grav\Framework\Session\Session
* @param mixed $object * @param mixed $object
* @param int $time * @param int $time
* @return $this * @return $this
* @throws JsonException
*/ */
public function setFlashCookieObject($name, $object, $time = 60) public function setFlashCookieObject($name, $object, $time = 60)
{ {
setcookie($name, json_encode($object, JSON_THROW_ON_ERROR), $this->getCookieOptions($time)); setcookie($name, json_encode($object), time() + $time, '/');
return $this; return $this;
} }
@@ -163,15 +161,13 @@ class Session extends \Grav\Framework\Session\Session
* *
* @param string $name * @param string $name
* @return mixed|null * @return mixed|null
* @throws JsonException
*/ */
public function getFlashCookieObject($name) public function getFlashCookieObject($name)
{ {
if (isset($_COOKIE[$name])) { if (isset($_COOKIE[$name])) {
$cookie = $_COOKIE[$name]; $object = json_decode($_COOKIE[$name], false);
setcookie($name, '', $this->getCookieOptions(-42000)); setcookie($name, '', time() - 3600, '/');
return $object;
return json_decode($cookie, false, 512, JSON_THROW_ON_ERROR);
} }
return null; return null;
+1 -1
View File
@@ -105,7 +105,7 @@ class Taxonomy
} }
} elseif (is_string($value)) { } elseif (is_string($value)) {
if (!empty($key)) { if (!empty($key)) {
$taxonomy .= $key; $taxonomy = $taxonomy . $key;
} }
$this->taxonomy_map[$taxonomy][(string) $value][$page->path()] = ['slug' => $page->slug()]; $this->taxonomy_map[$taxonomy][(string) $value][$page->path()] = ['slug' => $page->slug()];
} }
+17 -23
View File
@@ -224,18 +224,28 @@ class Themes extends Iterator
$grav = $this->grav; $grav = $this->grav;
$config = $this->config; $config = $this->config;
$name = $this->current(); $name = $this->current();
$class = null;
/** @var UniformResourceLocator $locator */ /** @var UniformResourceLocator $locator */
$locator = $grav['locator']; $locator = $grav['locator'];
// Start by attempting to load the theme.php file.
$file = $locator('theme://theme.php') ?: $locator("theme://{$name}.php"); $file = $locator('theme://theme.php') ?: $locator("theme://{$name}.php");
if ($file) { if ($file) {
// Local variables available in the file: $grav, $config, $name, $file // Local variables available in the file: $grav, $config, $name, $file
$class = include $file; $class = include $file;
if (!\is_object($class) || !is_subclass_of($class, Theme::class, true)) {
$class = null; if (!$class || !is_subclass_of($class, Plugin::class, true)) {
$className = Inflector::camelize($name);
$themeClassFormat = [
'Grav\\Theme\\' . $className,
'Grav\\Theme\\' . ucfirst($name)
];
foreach ($themeClassFormat as $themeClass) {
if (is_subclass_of($themeClass, Theme::class, true)) {
$class = new $themeClass($grav, $config, $name);
break;
}
}
} }
} elseif (!$locator('theme://') && !defined('GRAV_CLI')) { } elseif (!$locator('theme://') && !defined('GRAV_CLI')) {
$response = new Response(500, [], "Theme '$name' does not exist, unable to display page."); $response = new Response(500, [], "Theme '$name' does not exist, unable to display page.");
@@ -243,28 +253,12 @@ class Themes extends Iterator
$grav->close($response); $grav->close($response);
} }
// If the class hasn't been initialized yet, guess the class name and create a new instance. $this->config->set('theme', $config->get('themes.' . $name));
if (null === $class) {
$themeClassFormat = [
'Grav\\Theme\\' . Inflector::camelize($name),
'Grav\\Theme\\' . ucfirst($name)
];
foreach ($themeClassFormat as $themeClass) { if (empty($class)) {
if (is_subclass_of($themeClass, Theme::class, true)) {
$class = new $themeClass($grav, $config, $name);
break;
}
}
}
// Finally if everything else fails, just create a new instance from the default Theme class.
if (null === $class) {
$class = new Theme($grav, $config, $name); $class = new Theme($grav, $config, $name);
} }
$this->config->set('theme', $config->get('themes.' . $name));
return $class; return $class;
} }
@@ -1,19 +0,0 @@
<?php
/**
* @package Grav\Common\Twig\Exception
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Twig\Exception;
/**
* TwigException gets thrown when you use {% throw code message %} in twig.
*
* This allows Grav to catch 401, 403 and 404 exceptions and display proper error page.
*/
class TwigException extends \RuntimeException
{
}
@@ -13,7 +13,6 @@ use Grav\Common\Grav;
use Grav\Common\Utils; use Grav\Common\Utils;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Twig\Extension\AbstractExtension; use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction; use Twig\TwigFunction;
/** /**
@@ -31,35 +30,11 @@ class FilesystemExtension extends AbstractExtension
} }
/** /**
* @return TwigFilter[] * @return TwigFunction[]
*/ */
public function getFilters() public function getFilters()
{ {
return [ return $this->getFunctions();
new TwigFilter('file_exists', [$this, 'file_exists']),
new TwigFilter('fileatime', [$this, 'fileatime']),
new TwigFilter('filectime', [$this, 'filectime']),
new TwigFilter('filemtime', [$this, 'filemtime']),
new TwigFilter('filesize', [$this, 'filesize']),
new TwigFilter('filetype', [$this, 'filetype']),
new TwigFilter('is_dir', [$this, 'is_dir']),
new TwigFilter('is_file', [$this, 'is_file']),
new TwigFilter('is_link', [$this, 'is_link']),
new TwigFilter('is_readable', [$this, 'is_readable']),
new TwigFilter('is_writable', [$this, 'is_writable']),
new TwigFilter('is_writeable', [$this, 'is_writable']),
new TwigFilter('lstat', [$this, 'lstat']),
new TwigFilter('getimagesize', [$this, 'getimagesize']),
new TwigFilter('exif_read_data', [$this, 'exif_read_data']),
new TwigFilter('read_exif_data', [$this, 'exif_read_data']),
new TwigFilter('exif_imagetype', [$this, 'exif_imagetype']),
new TwigFilter('hash_file', [$this, 'hash_file']),
new TwigFilter('hash_hmac_file', [$this, 'hash_hmac_file']),
new TwigFilter('md5_file', [$this, 'md5_file']),
new TwigFilter('sha1_file', [$this, 'sha1_file']),
new TwigFilter('get_meta_tags', [$this, 'get_meta_tags']),
new TwigFilter('pathinfo', [$this, 'pathinfo']),
];
} }
/** /**
@@ -92,7 +67,6 @@ class FilesystemExtension extends AbstractExtension
new TwigFunction('md5_file', [$this, 'md5_file']), new TwigFunction('md5_file', [$this, 'md5_file']),
new TwigFunction('sha1_file', [$this, 'sha1_file']), new TwigFunction('sha1_file', [$this, 'sha1_file']),
new TwigFunction('get_meta_tags', [$this, 'get_meta_tags']), new TwigFunction('get_meta_tags', [$this, 'get_meta_tags']),
new TwigFunction('pathinfo', [$this, 'pathinfo']),
]; ];
} }
@@ -366,20 +340,6 @@ class FilesystemExtension extends AbstractExtension
return get_meta_tags($filename); return get_meta_tags($filename);
} }
/**
* @param string $path
* @param int|null $flags
* @return string|string[]
*/
public function pathinfo($path, $flags = null)
{
if (null !== $flags) {
return pathinfo($path, (int)$flags);
}
return pathinfo($path);
}
/** /**
* @param string $filename * @param string $filename
* @return bool * @return bool
@@ -90,7 +90,7 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
* *
* @return array * @return array
*/ */
public function getGlobals(): array public function getGlobals()
{ {
return [ return [
'grav' => $this->grav, 'grav' => $this->grav,
@@ -102,7 +102,7 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
* *
* @return array * @return array
*/ */
public function getFilters(): array public function getFilters()
{ {
return [ return [
new TwigFilter('*ize', [$this, 'inflectorFilter']), new TwigFilter('*ize', [$this, 'inflectorFilter']),
@@ -155,15 +155,10 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
new TwigFilter('bool', [$this, 'boolFilter']), new TwigFilter('bool', [$this, 'boolFilter']),
new TwigFilter('float', [$this, 'floatFilter'], ['is_safe' => ['all']]), new TwigFilter('float', [$this, 'floatFilter'], ['is_safe' => ['all']]),
new TwigFilter('array', [$this, 'arrayFilter']), new TwigFilter('array', [$this, 'arrayFilter']),
new TwigFilter('yaml', [$this, 'yamlFilter']),
// Object Types // Object Types
new TwigFilter('get_type', [$this, 'getTypeFunc']), new TwigFilter('get_type', [$this, 'getTypeFunc']),
new TwigFilter('of_type', [$this, 'ofTypeFunc']), new TwigFilter('of_type', [$this, 'ofTypeFunc'])
// PHP methods
new TwigFilter('count', 'count'),
new TwigFilter('array_diff', 'array_diff'),
]; ];
} }
@@ -172,7 +167,7 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
* *
* @return array * @return array
*/ */
public function getFunctions(): array public function getFunctions()
{ {
return [ return [
new TwigFunction('array', [$this, 'arrayFilter']), new TwigFunction('array', [$this, 'arrayFilter']),
@@ -217,7 +212,6 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
new TwigFunction('cron', [$this, 'cronFunc']), new TwigFunction('cron', [$this, 'cronFunc']),
new TwigFunction('svg_image', [$this, 'svgImageFunction']), new TwigFunction('svg_image', [$this, 'svgImageFunction']),
new TwigFunction('xss', [$this, 'xssFunc']), new TwigFunction('xss', [$this, 'xssFunc']),
new TwigFunction('unique_id', [$this, 'uniqueId']),
// Translations // Translations
new TwigFunction('t', [$this, 'translate'], ['needs_environment' => true]), new TwigFunction('t', [$this, 'translate'], ['needs_environment' => true]),
@@ -226,25 +220,14 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
// Object Types // Object Types
new TwigFunction('get_type', [$this, 'getTypeFunc']), new TwigFunction('get_type', [$this, 'getTypeFunc']),
new TwigFunction('of_type', [$this, 'ofTypeFunc']), new TwigFunction('of_type', [$this, 'ofTypeFunc'])
// PHP methods
new TwigFunction('is_numeric', 'is_numeric'),
new TwigFunction('is_iterable', 'is_iterable'),
new TwigFunction('is_countable', 'is_countable'),
new TwigFunction('is_null', 'is_null'),
new TwigFunction('is_string', 'is_string'),
new TwigFunction('is_array', 'is_array'),
new TwigFunction('is_object', 'is_object'),
new TwigFunction('count', 'count'),
new TwigFunction('array_diff', 'array_diff'),
]; ];
} }
/** /**
* @return array * @return array
*/ */
public function getTokenParsers(): array public function getTokenParsers()
{ {
return [ return [
new TwigTokenParserRender(), new TwigTokenParserRender(),
@@ -655,20 +638,6 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
return implode(', ', $results_parts); return implode(', ', $results_parts);
} }
/**
* Generates a random string with configurable length, prefix and suffix.
* Unlike the built-in `uniqid()`, this string is non-conflicting and safe
*
* @param int $length
* @param array $options
* @return string
* @throws \Exception
*/
public function uniqueId(int $length = 9, array $options = ['prefix' => '', 'suffix' => '']): string
{
return Utils::uniqueId($length, $options);
}
/** /**
* @param string $string * @param string $string
* @return string * @return string
@@ -823,23 +792,16 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
return (array)$input; return (array)$input;
} }
/**
* @param array|object $value
* @param int|null $inline
* @param int|null $indent
* @return string
*/
public function yamlFilter($value, $inline = null, $indent = null): string
{
return Yaml::dump($value, $inline, $indent);
}
/** /**
* @param Environment $twig * @param Environment $twig
* @return string * @return string
*/ */
public function translate(Environment $twig, ...$args) public function translate(Environment $twig)
{ {
// shift off the environment
$args = func_get_args();
array_shift($args);
// If admin and tu filter provided, use it // If admin and tu filter provided, use it
if (isset($this->grav['admin'])) { if (isset($this->grav['admin'])) {
$numargs = count($args); $numargs = count($args);
@@ -847,12 +809,6 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
if (($numargs === 3 && is_array($args[1])) || ($numargs === 2 && !is_array($args[1]))) { if (($numargs === 3 && is_array($args[1])) || ($numargs === 2 && !is_array($args[1]))) {
$lang = array_pop($args); $lang = array_pop($args);
/** @var Language $language */
$language = $this->grav['language'];
if (is_string($lang) && !$language->getLanguageCode($lang)) {
$args[] = $lang;
$lang = null;
}
} elseif ($numargs === 2 && is_array($args[1])) { } elseif ($numargs === 2 && is_array($args[1])) {
$subs = array_pop($args); $subs = array_pop($args);
$args = array_merge($args, $subs); $args = array_merge($args, $subs);
@@ -1360,7 +1316,7 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
*/ */
public function vardumpFunc($var) public function vardumpFunc($var)
{ {
dump($var); var_dump($var);
} }
/** /**
@@ -1424,7 +1380,7 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
* @param array $context Twig Context * @param array $context Twig Context
* @param string $var variable to be found (using dot notation) * @param string $var variable to be found (using dot notation)
* @param null $default the default value to be used as last resort * @param null $default the default value to be used as last resort
* @param PageInterface|null $page an optional page to use for the current page * @param null $page an optional page to use for the current page
* @param bool $exists toggle to simply return the page where the variable is set, else null * @param bool $exists toggle to simply return the page where the variable is set, else null
* @return mixed * @return mixed
*/ */
@@ -1528,7 +1484,7 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
} }
//Look for existing class //Look for existing class
$svg = preg_replace_callback('/^<svg[^>]*(class=\"([^"]*)\")[^>]*>/', function($matches) use ($classes, &$matched) { $svg = preg_replace_callback('/^<svg[^>]*(class=\")([^"]*)(\")[^>]*>/', function($matches) use ($classes, &$matched) {
if (isset($matches[2])) { if (isset($matches[2])) {
$new_classes = $matches[2] . $classes; $new_classes = $matches[2] . $classes;
$matched = true; $matched = true;
@@ -1544,7 +1500,7 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
$svg = str_replace('<svg ', "<svg class=\"$classes\" ", $svg); $svg = str_replace('<svg ', "<svg class=\"$classes\" ", $svg);
} }
return trim($svg); return $svg;
} }
return null; return null;
@@ -43,7 +43,7 @@ class TwigNodeThrow extends Node
$compiler->addDebugInfo($this); $compiler->addDebugInfo($this);
$compiler $compiler
->write('throw new \Grav\Common\Twig\Exception\TwigException(') ->write('throw new \RuntimeException(')
->subcompile($this->getNode('message')) ->subcompile($this->getNode('message'))
->write(', ') ->write(', ')
->write($this->getAttribute('code') ?: 500) ->write($this->getAttribute('code') ?: 500)
@@ -49,15 +49,16 @@ class TwigNodeTryCatch extends Node
$compiler $compiler
->indent() ->indent()
->subcompile($this->getNode('try')) ->subcompile($this->getNode('try'));
->outdent()
->write('} catch (\Exception $e) {' . "\n")
->indent()
->write('if (isset($context[\'grav\'][\'debugger\'])) $context[\'grav\'][\'debugger\']->addException($e);' . "\n")
->write('$context[\'e\'] = $e;' . "\n");
if ($this->hasNode('catch')) { if ($this->hasNode('catch')) {
$compiler->subcompile($this->getNode('catch')); $compiler
->outdent()
->write('} catch (\Exception $e) {' . "\n")
->indent()
->write('if (isset($context[\'grav\'][\'debugger\'])) $context[\'grav\'][\'debugger\']->addException($e);' . "\n")
->write('$context[\'e\'] = $e;' . "\n")
->subcompile($this->getNode('catch'));
} }
$compiler $compiler
+49 -71
View File
@@ -16,23 +16,22 @@ use Grav\Common\Language\Language;
use Grav\Common\Language\LanguageCodes; use Grav\Common\Language\LanguageCodes;
use Grav\Common\Page\Interfaces\PageInterface; use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Page\Pages; use Grav\Common\Page\Pages;
use Grav\Common\Twig\Exception\TwigException;
use Grav\Common\Twig\Extension\FilesystemExtension; use Grav\Common\Twig\Extension\FilesystemExtension;
use Grav\Common\Twig\Extension\GravExtension; use Grav\Common\Twig\Extension\GravExtension;
use Grav\Common\Utils; use Grav\Common\Utils;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RocketTheme\Toolbox\Event\Event; use RocketTheme\Toolbox\Event\Event;
use Phive\Twig\Extensions\Deferred\DeferredExtension;
use RuntimeException; use RuntimeException;
use Twig\Cache\FilesystemCache; use Twig\Cache\FilesystemCache;
use Twig\DeferredExtension\DeferredExtension;
use Twig\Environment; use Twig\Environment;
use Twig\Error\LoaderError; use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Extension\CoreExtension; use Twig\Extension\CoreExtension;
use Twig\Extension\DebugExtension; use Twig\Extension\DebugExtension;
use Twig\Extension\StringLoaderExtension; use Twig\Extension\StringLoaderExtension;
use Twig\Loader\ArrayLoader; use Twig\Loader\ArrayLoader;
use Twig\Loader\ChainLoader; use Twig\Loader\ChainLoader;
use Twig\Loader\ExistsLoaderInterface;
use Twig\Loader\FilesystemLoader; use Twig\Loader\FilesystemLoader;
use Twig\Profiler\Profile; use Twig\Profiler\Profile;
use Twig\TwigFilter; use Twig\TwigFilter;
@@ -405,63 +404,38 @@ class Twig
*/ */
public function processSite($format = null, array $vars = []) public function processSite($format = null, array $vars = [])
{ {
// set the page now its been processed
$this->grav->fireEvent('onTwigSiteVariables');
/** @var Pages $pages */
$pages = $this->grav['pages'];
/** @var PageInterface $page */
$page = $this->grav['page'];
$content = $page->content();
$twig_vars = $this->twig_vars;
$twig_vars['theme'] = $this->grav['config']->get('theme');
$twig_vars['pages'] = $pages->root();
$twig_vars['page'] = $page;
$twig_vars['header'] = $page->header();
$twig_vars['media'] = $page->media();
$twig_vars['content'] = $content;
// determine if params are set, if so disable twig cache
$params = $this->grav['uri']->params(null, true);
if (!empty($params)) {
$this->twig->setCache(false);
}
// Get Twig template layout
$template = $this->getPageTwigTemplate($page, $format);
$page->templateFormat($format);
try { try {
$grav = $this->grav;
// set the page now its been processed
$grav->fireEvent('onTwigSiteVariables');
/** @var Pages $pages */
$pages = $grav['pages'];
/** @var PageInterface $page */
$page = $grav['page'];
$twig_vars = $this->twig_vars;
$twig_vars['theme'] = $grav['config']->get('theme');
$twig_vars['pages'] = $pages->root();
$twig_vars['page'] = $page;
$twig_vars['header'] = $page->header();
$twig_vars['media'] = $page->media();
$twig_vars['content'] = $page->content();
// determine if params are set, if so disable twig cache
$params = $grav['uri']->params(null, true);
if (!empty($params)) {
$this->twig->setCache(false);
}
// Get Twig template layout
$template = $this->getPageTwigTemplate($page, $format);
$page->templateFormat($format);
$output = $this->twig->render($template, $vars + $twig_vars); $output = $this->twig->render($template, $vars + $twig_vars);
} catch (LoaderError $e) { } catch (LoaderError $e) {
throw new RuntimeException($e->getMessage(), 400, $e); $error_msg = $e->getMessage();
} catch (RuntimeError $e) { throw new RuntimeException($error_msg, 400, $e);
$prev = $e->getPrevious();
if ($prev instanceof TwigException) {
$code = $prev->getCode() ?: 500;
// Fire onPageNotFound event.
$event = new Event([
'page' => $page,
'code' => $code,
'message' => $prev->getMessage(),
'exception' => $prev,
'route' => $grav['route'],
'request' => $grav['request']
]);
$event = $grav->fireEvent("onDisplayErrorPage.{$code}", $event);
$newPage = $event['page'];
if ($newPage && $newPage !== $page) {
unset($grav['page']);
$grav['page'] = $newPage;
return $this->processSite($newPage->templateFormat(), $vars);
}
}
throw $e;
} }
return $output; return $output;
@@ -514,21 +488,25 @@ class Twig
$twig_extension = $extension ? '.'. $extension .TWIG_EXT : TEMPLATE_EXT; $twig_extension = $extension ? '.'. $extension .TWIG_EXT : TEMPLATE_EXT;
$template_file = $this->template($page->template() . $twig_extension); $template_file = $this->template($page->template() . $twig_extension);
$page_template = null;
$loader = $this->twig->getLoader(); $loader = $this->twig->getLoader();
if ($loader->exists($template_file)) { if ($loader instanceof ExistsLoaderInterface) {
// template.xxx.twig if ($loader->exists($template_file)) {
$page_template = $template_file; // template.xxx.twig
} elseif ($twig_extension !== TEMPLATE_EXT && $loader->exists($template . TEMPLATE_EXT)) { $page_template = $template_file;
// template.html.twig } elseif ($twig_extension !== TEMPLATE_EXT && $loader->exists($template . TEMPLATE_EXT)) {
$page_template = $template . TEMPLATE_EXT; // template.html.twig
$format = 'html'; $page_template = $template . TEMPLATE_EXT;
} elseif ($loader->exists($default . $twig_extension)) { $format = 'html';
// default.xxx.twig } elseif ($loader->exists($default . $twig_extension)) {
$page_template = $default . $twig_extension; // default.xxx.twig
} else { $page_template = $default . $twig_extension;
// default.html.twig } else {
$page_template = $default . TEMPLATE_EXT; // default.html.twig
$format = 'html'; $page_template = $default . TEMPLATE_EXT;
$format = 'html';
}
} }
return $page_template; return $page_template;
+30 -37
View File
@@ -160,8 +160,8 @@ class Uri
$language = $grav['language']; $language = $grav['language'];
// add the port to the base for non-standard ports // add the port to the base for non-standard ports
if ($this->port && $config->get('system.reverse_proxy_setup') === false) { if ($this->port !== null && $config->get('system.reverse_proxy_setup') === false) {
$this->base .= ':' . $this->port; $this->base .= ':' . (string)$this->port;
} }
// Handle custom base // Handle custom base
@@ -176,8 +176,8 @@ class Uri
if (isset($custom_parts['scheme'])) { if (isset($custom_parts['scheme'])) {
$this->base = $custom_parts['scheme'] . '://' . $custom_parts['host']; $this->base = $custom_parts['scheme'] . '://' . $custom_parts['host'];
$this->port = $custom_parts['port'] ?? null; $this->port = $custom_parts['port'] ?? null;
if ($this->port && $config->get('system.reverse_proxy_setup') === false) { if ($this->port !== null && $config->get('system.reverse_proxy_setup') === false) {
$this->base .= ':' . $this->port; $this->base .= ':' . (string)$this->port;
} }
$this->root = $custom_base; $this->root = $custom_base;
} else { } else {
@@ -462,8 +462,8 @@ class Uri
public function port($raw = false) public function port($raw = false)
{ {
$port = $this->port; $port = $this->port;
// If not in raw mode and port is not set or is 0, figure it out from scheme. // If not in raw mode and port is not set, figure it out from scheme.
if (!$raw && !$port) { if (!$raw && $port === null) {
if ($this->scheme === 'http') { if ($this->scheme === 'http') {
$this->port = 80; $this->port = 80;
} elseif ($this->scheme === 'https') { } elseif ($this->scheme === 'https') {
@@ -471,7 +471,7 @@ class Uri
} }
} }
return $this->port ?: null; return $this->port;
} }
/** /**
@@ -586,38 +586,33 @@ class Uri
/** /**
* Return relative path to the referrer defaulting to current or given page. * Return relative path to the referrer defaulting to current or given page.
* *
* You should set the third parameter to `true` for redirects as long as you came from the same sub-site and language.
*
* @param string|null $default * @param string|null $default
* @param string|null $attributes * @param string|null $attributes
* @param bool $withoutBaseRoute
* @return string * @return string
*/ */
public function referrer($default = null, $attributes = null, bool $withoutBaseRoute = false) public function referrer($default = null, $attributes = null)
{ {
$referrer = $_SERVER['HTTP_REFERER'] ?? null; $referrer = $_SERVER['HTTP_REFERER'] ?? null;
// Check that referrer came from our site. // Check that referrer came from our site.
if ($withoutBaseRoute) { $root = $this->rootUrl(true);
/** @var Pages $pages */ if ($referrer) {
$pages = Grav::instance()['pages']; // Referrer should always have host set and it should come from the same base address.
$base = $pages->baseUrl(null, true); if (stripos($referrer, $root) !== 0) {
} else { $referrer = null;
$base = $this->rootUrl(true); }
} }
// Referrer should always have host set and it should come from the same base address. if (!$referrer) {
if (!is_string($referrer) || !str_starts_with($referrer, $base)) {
$referrer = $default ?: $this->route(true, true); $referrer = $default ?: $this->route(true, true);
} }
// Relative path from grav root.
$referrer = substr($referrer, strlen($base));
if ($attributes) { if ($attributes) {
$referrer .= $attributes; $referrer .= $attributes;
} }
return $referrer; // Return relative path.
return substr($referrer, strlen($root));
} }
/** /**
@@ -653,7 +648,7 @@ class Uri
return [ return [
'scheme' => $this->scheme, 'scheme' => $this->scheme,
'host' => $this->host, 'host' => $this->host,
'port' => $this->port ?: null, 'port' => $this->port,
'user' => $this->user, 'user' => $this->user,
'pass' => $this->password, 'pass' => $this->password,
'path' => $path, 'path' => $path,
@@ -670,7 +665,7 @@ class Uri
*/ */
public static function paramsRegex() public static function paramsRegex()
{ {
return '/\/{1,}([^\:\#\/\?]*' . Grav::instance()['config']->get('system.param_sep') . '[^\:\#\/\?]*)/'; return '/\/([^\:\#\/\?]*' . Grav::instance()['config']->get('system.param_sep') . '[^\:\#\/\?]*)/';
} }
/** /**
@@ -680,15 +675,10 @@ class Uri
*/ */
public static function ip() public static function ip()
{ {
$ip = 'UNKNOWN';
if (getenv('HTTP_CLIENT_IP')) { if (getenv('HTTP_CLIENT_IP')) {
$ip = getenv('HTTP_CLIENT_IP'); $ip = getenv('HTTP_CLIENT_IP');
} elseif (getenv('HTTP_CF_CONNECTING_IP')) {
$ip = getenv('HTTP_CF_CONNECTING_IP');
} elseif (getenv('HTTP_X_FORWARDED_FOR') && Grav::instance()['config']->get('system.http_x_forwarded.ip')) { } elseif (getenv('HTTP_X_FORWARDED_FOR') && Grav::instance()['config']->get('system.http_x_forwarded.ip')) {
$ips = array_map('trim', explode(',', getenv('HTTP_X_FORWARDED_FOR'))); $ip = getenv('HTTP_X_FORWARDED_FOR');
$ip = array_shift($ips);
} elseif (getenv('HTTP_X_FORWARDED') && Grav::instance()['config']->get('system.http_x_forwarded.ip')) { } elseif (getenv('HTTP_X_FORWARDED') && Grav::instance()['config']->get('system.http_x_forwarded.ip')) {
$ip = getenv('HTTP_X_FORWARDED'); $ip = getenv('HTTP_X_FORWARDED');
} elseif (getenv('HTTP_FORWARDED_FOR')) { } elseif (getenv('HTTP_FORWARDED_FOR')) {
@@ -697,6 +687,8 @@ class Uri
$ip = getenv('HTTP_FORWARDED'); $ip = getenv('HTTP_FORWARDED');
} elseif (getenv('REMOTE_ADDR')) { } elseif (getenv('REMOTE_ADDR')) {
$ip = getenv('REMOTE_ADDR'); $ip = getenv('REMOTE_ADDR');
} else {
$ip = 'UNKNOWN';
} }
return $ip; return $ip;
@@ -1151,8 +1143,11 @@ class Uri
public static function isValidUrl($url) public static function isValidUrl($url)
{ {
$regex = '/^(?:(https?|ftp|telnet):)?\/\/((?:[a-z0-9@:.-]|%[0-9A-F]{2}){3,})(?::(\d+))?((?:\/(?:[a-z0-9-._~!$&\'\(\)\*\+\,\;\=\:\@]|%[0-9A-F]{2})*)*)(?:\?((?:[a-z0-9-._~!$&\'\(\)\*\+\,\;\=\:\/?@]|%[0-9A-F]{2})*))?(?:#((?:[a-z0-9-._~!$&\'\(\)\*\+\,\;\=\:\/?@]|%[0-9A-F]{2})*))?/'; $regex = '/^(?:(https?|ftp|telnet):)?\/\/((?:[a-z0-9@:.-]|%[0-9A-F]{2}){3,})(?::(\d+))?((?:\/(?:[a-z0-9-._~!$&\'\(\)\*\+\,\;\=\:\@]|%[0-9A-F]{2})*)*)(?:\?((?:[a-z0-9-._~!$&\'\(\)\*\+\,\;\=\:\/?@]|%[0-9A-F]{2})*))?(?:#((?:[a-z0-9-._~!$&\'\(\)\*\+\,\;\=\:\/?@]|%[0-9A-F]{2})*))?/';
if (preg_match($regex, $url)) {
return true;
}
return (bool)preg_match($regex, $url); return false;
} }
/** /**
@@ -1263,7 +1258,7 @@ class Uri
$this->port = null; $this->port = null;
} }
if ($this->port === 0 || $this->hasStandardPort()) { if ($this->hasStandardPort()) {
$this->port = null; $this->port = null;
} }
@@ -1303,7 +1298,7 @@ class Uri
*/ */
protected function hasStandardPort() protected function hasStandardPort()
{ {
return (!$this->port || $this->port === 80 || $this->port === 443); return ($this->port === 80 || $this->port === 443);
} }
/** /**
@@ -1316,13 +1311,11 @@ class Uri
if ($parts === false) { if ($parts === false) {
throw new RuntimeException('Malformed URL: ' . $url); throw new RuntimeException('Malformed URL: ' . $url);
} }
$port = (int)($parts['port'] ?? 0);
$this->scheme = $parts['scheme'] ?? null; $this->scheme = $parts['scheme'] ?? null;
$this->user = $parts['user'] ?? null; $this->user = $parts['user'] ?? null;
$this->password = $parts['pass'] ?? null; $this->password = $parts['pass'] ?? null;
$this->host = $parts['host'] ?? null; $this->host = $parts['host'] ?? null;
$this->port = $port ?: null; $this->port = isset($parts['port']) ? (int)$parts['port'] : null;
$this->path = $parts['path'] ?? ''; $this->path = $parts['path'] ?? '';
$this->query = $parts['query'] ?? ''; $this->query = $parts['query'] ?? '';
$this->fragment = $parts['fragment'] ?? null; $this->fragment = $parts['fragment'] ?? null;
@@ -1505,7 +1498,7 @@ class Uri
* @param string $delimiter * @param string $delimiter
* @return string * @return string
*/ */
private function processParams(string $uri, string $delimiter = ':'): string private function processParams($uri, $delimiter = ':')
{ {
if (strpos($uri, $delimiter) !== false) { if (strpos($uri, $delimiter) !== false) {
preg_match_all(static::paramsRegex(), $uri, $matches, PREG_SET_ORDER); preg_match_all(static::paramsRegex(), $uri, $matches, PREG_SET_ORDER);
@@ -57,7 +57,6 @@ class User extends Data implements UserInterface
* @param string $offset * @param string $offset
* @return bool * @return bool
*/ */
#[\ReturnTypeWillChange]
public function offsetExists($offset) public function offsetExists($offset)
{ {
$value = parent::offsetExists($offset); $value = parent::offsetExists($offset);
@@ -74,7 +73,6 @@ class User extends Data implements UserInterface
* @param string $offset * @param string $offset
* @return mixed * @return mixed
*/ */
#[\ReturnTypeWillChange]
public function offsetGet($offset) public function offsetGet($offset)
{ {
$value = parent::offsetGet($offset); $value = parent::offsetGet($offset);
+1 -1
View File
@@ -27,7 +27,7 @@ class Group extends Data
* @return array * @return array
* @deprecated 1.7, use $grav['user_groups'] Flex UserGroupCollection instead * @deprecated 1.7, use $grav['user_groups'] Flex UserGroupCollection instead
*/ */
protected static function groups() private static function groups()
{ {
user_error(__METHOD__ . '() is deprecated since Grav 1.7, use $grav[\'user_groups\'] Flex UserGroupCollection instead', E_USER_DEPRECATED); user_error(__METHOD__ . '() is deprecated since Grav 1.7, use $grav[\'user_groups\'] Flex UserGroupCollection instead', E_USER_DEPRECATED);
+17 -29
View File
@@ -21,7 +21,6 @@ use Grav\Common\Page\Markdown\Excerpts;
use Grav\Common\Page\Pages; use Grav\Common\Page\Pages;
use Grav\Framework\Flex\Flex; use Grav\Framework\Flex\Flex;
use Grav\Framework\Flex\Interfaces\FlexObjectInterface; use Grav\Framework\Flex\Interfaces\FlexObjectInterface;
use Grav\Framework\Media\Interfaces\MediaInterface;
use InvalidArgumentException; use InvalidArgumentException;
use Negotiation\Accept; use Negotiation\Accept;
use Negotiation\Negotiator; use Negotiation\Negotiator;
@@ -151,7 +150,7 @@ abstract class Utils
$domain = $domain ?: $grav['config']->get('system.absolute_urls', false); $domain = $domain ?: $grav['config']->get('system.absolute_urls', false);
return rtrim($uri->rootUrl($domain), '/') . '/' . ($resource ?: ''); return rtrim($uri->rootUrl($domain), '/') . '/' . ($resource ?? '');
} }
/** /**
@@ -167,9 +166,9 @@ abstract class Utils
if ($locator->isStream($path)) { if ($locator->isStream($path)) {
$path = $locator->findResource($path, true); $path = $locator->findResource($path, true);
} elseif (!static::startsWith($path, GRAV_ROOT)) { } elseif (!Utils::startsWith($path, GRAV_ROOT)) {
$base_url = Grav::instance()['base_url']; $base_url = Grav::instance()['base_url'];
$path = GRAV_ROOT . '/' . ltrim(static::replaceFirstOccurrence($base_url, '', $path), '/'); $path = GRAV_ROOT . '/' . ltrim(Utils::replaceFirstOccurrence($base_url, '', $path), '/');
} }
return $path; return $path;
@@ -629,23 +628,6 @@ abstract class Utils
return substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, $length); return substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, $length);
} }
/**
* Generates a random string with configurable length, prefix and suffix.
* Unlike the built-in `uniqid()`, this string is non-conflicting and safe
*
* @param int $length
* @param array $options
* @return string
* @throws Exception
*/
public static function uniqueId(int $length = 13, array $options = []): string
{
$options = array_merge(['prefix' => '', 'suffix' => ''], $options);
$bytes = random_bytes(ceil($length / 2));
return $options['prefix'] . substr(bin2hex($bytes), 0, $length) . $options['suffix'];
}
/** /**
* Provides the ability to download a file to the browser * Provides the ability to download a file to the browser
* *
@@ -768,13 +750,13 @@ abstract class Utils
if (is_string($http_accept)) { if (is_string($http_accept)) {
$negotiator = new Negotiator(); $negotiator = new Negotiator();
$supported_types = static::getSupportPageTypes(['html', 'json']); $supported_types = Utils::getSupportPageTypes(['html', 'json']);
$priorities = static::getMimeTypes($supported_types); $priorities = Utils::getMimeTypes($supported_types);
$media_type = $negotiator->getBest($http_accept, $priorities); $media_type = $negotiator->getBest($http_accept, $priorities);
$mimetype = $media_type instanceof Accept ? $media_type->getValue() : ''; $mimetype = $media_type instanceof Accept ? $media_type->getValue() : '';
return static::getExtensionByMime($mimetype); return Utils::getExtensionByMime($mimetype);
} }
return 'html'; return 'html';
@@ -809,7 +791,13 @@ abstract class Utils
$media_types = Grav::instance()['config']->get('media.types'); $media_types = Grav::instance()['config']->get('media.types');
return $media_types[$extension]['mime'] ?? $default; if (isset($media_types[$extension])) {
if (isset($media_types[$extension]['mime'])) {
return $media_types[$extension]['mime'];
}
}
return $default;
} }
/** /**
@@ -1567,7 +1555,7 @@ abstract class Utils
switch ($matches[0]) { switch ($matches[0]) {
case 'self': case 'self':
if (!$object instanceof MediaInterface) { if (null === $object) {
throw new RuntimeException(sprintf('Page not available for self@ reference: %s', $path)); throw new RuntimeException(sprintf('Page not available for self@ reference: %s', $path));
} }
@@ -1641,7 +1629,7 @@ abstract class Utils
* @param string $path * @param string $path
* @return string[]|null * @return string[]|null
*/ */
protected static function resolveTokenPath(string $path): ?array private static function resolveTokenPath(string $path): ?array
{ {
if (strpos($path, '@') !== false) { if (strpos($path, '@') !== false) {
$regex = '/^(@\w+|\w+@|@\w+@)([^:]*)(.*)$/u'; $regex = '/^(@\w+|\w+@|@\w+@)([^:]*)(.*)$/u';
@@ -1751,7 +1739,7 @@ abstract class Utils
{ {
$enc_url = preg_replace_callback( $enc_url = preg_replace_callback(
'%[^:/@?&=#]+%usD', '%[^:/@?&=#]+%usD',
static function ($matches) { function ($matches) {
return urlencode($matches[0]); return urlencode($matches[0]);
}, },
$url $url
@@ -1775,7 +1763,7 @@ abstract class Utils
* *
* @param string $string * @param string $string
* @param bool $block Block or Line processing * @param bool $block Block or Line processing
* @param PageInterface|null $page * @param null $page
* @return string * @return string
* @throws Exception * @throws Exception
*/ */

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