Compare commits

...

227 Commits

Author SHA1 Message Date
Andy Miller
2bc6848464 Merge branch 'release/1.6.4' 2019-04-15 14:48:10 -06:00
Andy Miller
abefbfc776 prepare for release 2019-04-15 14:48:01 -06:00
Andy Miller
ad173ca129 Improved redirect_default_route logic as well as Uri::toArray() to take into account root_path and extension 2019-04-15 12:44:48 -06:00
Andy Miller
d69ef0e39c Refactored rounded logic in Utils::parseSize() #2394 2019-04-15 11:00:11 -06:00
Matias Griese
436be17881 Fixed Flex simple storage not being properly initialized if used with caching 2019-04-15 14:26:55 +03:00
Andy Miller
9a5fa7e699 Extra forcing of first level arrays in taxonomy 2019-04-14 13:08:57 -06:00
Andy Miller
d502ff08c1 Attempt to resolve issue in form#332 2019-04-13 17:20:45 -06:00
Andy Miller
14eb1281f9 Fix to force all Page::taxonomy to be treated as strings #2446 2019-04-13 13:53:46 -06:00
Andy Miller
8fd7a5aebe Fixes #2445 pipeline excluded assets with cache on 2019-04-13 13:18:13 -06:00
Andy Miller
08423df547 code tidy 2019-04-13 12:27:19 -06:00
Andy Miller
40563ed2f8 Better Utils::normalizePath() logic #2216 2019-04-13 12:21:54 -06:00
Andy Miller
b639f09ca7 Merge branch 'release/1.6.3' 2019-04-12 19:48:13 -06:00
Andy Miller
dd134ad551 Merge tag '1.6.3' into develop
Release v1.6.3
2019-04-12 19:48:13 -06:00
Andy Miller
3d93d50cf0 prepare for release 2019-04-12 19:48:03 -06:00
Andy Miller
f1da7b6063 Fix for vUndefined errors when upgrading 2019-04-12 09:49:20 -06:00
Andy Miller
ef7b33f9b6 Fixed issue with Utils::normalizePath messing with external URLs #2216 2019-04-12 07:55:51 -06:00
Andy Miller
0f0e6ab1c8 Missed this one! #2442 2019-04-12 07:26:44 -06:00
Matias Griese
5362b312d1 Added Blueprint::addDynamicHandler() method to allow custom dynamic handlers, for example custom-options@: getCustomOptions 2019-04-12 12:23:31 +03:00
Andy Miller
ca4d6a398f Merge branch 'release/1.6.2' 2019-04-11 18:25:47 -06:00
Andy Miller
ed00d480f2 Merge tag '1.6.2' into develop
Release v1.6.2
2019-04-11 18:25:47 -06:00
Andy Miller
057bdd546b prepare for release 2019-04-11 18:25:38 -06:00
Andy Miller
7762f0c85e * Revert renaming of ClearCacheCommand to ensure CLI GPM upgrades go smoothly 2019-04-11 18:22:38 -06:00
Andy Miller
1e6c01ea65 Merge branch 'release/1.6.1' 2019-04-11 16:46:51 -06:00
Andy Miller
4b777f508b Merge tag '1.6.1' into develop
Release v1.6.1
2019-04-11 16:46:51 -06:00
Andy Miller
d15f125964 prepare for release 2019-04-11 16:46:43 -06:00
Andy Miller
32b435b7e6 Revert attempted fixes 2019-04-11 16:45:31 -06:00
Andy Miller
31d301911f Merge branch 'release/1.6.1' 2019-04-11 16:33:08 -06:00
Andy Miller
911eec5e68 Merge tag '1.6.1' into develop
Release v1.6.1
2019-04-11 16:33:08 -06:00
Andy Miller
2e4bb25e2e prepare for release 2019-04-11 16:32:59 -06:00
Andy Miller
d99c80eae9 More backwards compatibility 2019-04-11 16:29:55 -06:00
Andy Miller
0e03240d13 Merge branch 'release/1.6.1' 2019-04-11 15:25:47 -06:00
Andy Miller
02064117bc Merge tag '1.6.1' into develop
Release v1.6.1
2019-04-11 15:25:47 -06:00
Andy Miller
a9e0cc7159 prepare for release 2019-04-11 15:25:31 -06:00
Andy Miller
6b4332db72 not sure if needed, but might be same issue as DebuggerAssetsProcessor 2019-04-11 15:24:54 -06:00
Andy Miller
0f591953a0 update changelog 2019-04-11 15:19:55 -06:00
Andy Miller
0a5e78ccc6 Hopefully fix the DebuggerAssetsProcess error on upgrade of older versions 2019-04-11 15:19:43 -06:00
Andy Miller
fbf7c50a12 no longer needed 2019-04-11 15:06:19 -06:00
Andy Miller
09c1255239 fixes #2440 2019-04-11 15:05:50 -06:00
Andy Miller
75650ceba3 Better CSS for DebugBar 2019-04-11 15:04:00 -06:00
Andy Miller
e2a65004f3 Merge tag '1.6.0' into develop
Release v1.6.0
2019-04-11 12:38:38 -06:00
Andy Miller
d4e1bcc660 Merge branch 'release/1.6.0' 2019-04-11 12:38:37 -06:00
Andy Miller
b1b1670c77 prepare for release 2019-04-11 12:38:27 -06:00
Andy Miller
5a3674f6f6 set releases to stable 2019-04-11 12:36:14 -06:00
Andy Miller
e622e204bf update to include PHP 7.3 next time 2019-04-11 12:31:13 -06:00
Andy Miller
c378b06a90 Merge tag '1.6.0' into develop
Release v1.6.0
2019-04-11 12:23:38 -06:00
Andy Miller
c6ce6e6c32 Merge branch 'release/1.6.0' 2019-04-11 12:23:37 -06:00
Andy Miller
355f7ee748 prepare for release (take 2) 2019-04-11 12:23:27 -06:00
Andy Miller
c19fa22a26 update changelog 2019-04-11 12:22:51 -06:00
Matias Griese
5ba4c8ee5b Merge remote-tracking branch 'origin/1.6' into 1.6 2019-04-11 19:43:12 +03:00
Matias Griese
306df5837d Added FlexStorageInterface::hasKeys() 2019-04-11 19:43:01 +03:00
Andy Miller
66bba376db minor fixes and tests for Utils::url 2019-04-11 07:30:12 -06:00
Andy Miller
ec07d37623 Merge branch '1.6' of github.com:getgrav/grav into 1.6 2019-04-10 16:51:07 -06:00
Andy Miller
cab78d36f3 Missing file 2019-04-10 16:51:02 -06:00
Andy Miller
449682baea Allow reseting of Page::metadtta 2019-04-10 16:50:55 -06:00
Andy Miller
6eb11b9717 Fix for Utils::url and extra base_url 2019-04-10 16:50:30 -06:00
Matias Griese
5a1d138d08 Merge remote-tracking branch 'origin/1.6' into 1.6 2019-04-10 20:59:21 +03:00
Matias Griese
dfd75efbe5 Fixed join() in flex user 2019-04-10 20:59:13 +03:00
Andy Miller
76ed48fc7c escape spaces 2019-04-10 11:27:49 -06:00
Andy Miller
1043b9a189 Added languages.default_lang option to override the default lang 2019-04-09 17:27:35 -06:00
Matias Griese
e401c683f5 Grav 1.6: Fixed bad key in Flex Users if file storage is being used 2019-04-09 12:06:20 +03:00
Matias Griese
9d4fe331fa Fixed error in FlexUserIndex if there are users without an email address 2019-04-09 09:58:55 +03:00
Matias Griese
fbae3fd194 Fixed Flex erroring out with all numeric keys in storage (changelog) 2019-04-08 21:38:25 +03:00
Matias Griese
022f1ce758 Composer update 2019-04-08 21:37:41 +03:00
Andy Miller
b367c664c7 tidy up 2019-04-05 17:42:30 -06:00
Andy Miller
8512968726 this closes #2432 2019-04-05 17:38:45 -06:00
Matias Griese
2ffc110f03 Composer update 2019-04-05 10:21:51 +03:00
Matias Griese
36836c516f Fixed deleting last list item in the form 2019-04-05 10:19:55 +03:00
Andy Miller
1f288c25de typo! 2019-04-04 17:06:12 -06:00
Matias Griese
a4b62d48b2 Fixed minor bugs in Flex 2019-04-04 11:28:30 +03:00
Andy Miller
397f6902f3 Improved Utils::normalizePath() to support non-protocol URLs 2019-04-03 16:52:52 -06:00
Andy Miller
e97bceed56 changelog updated 2019-04-02 12:25:27 -06:00
Andy Miller
7efc0f418e Fixed cached images not being updated when source image is modified 2019-04-02 12:20:38 -06:00
Matias Griese
ac2a4e1c06 Try fixing cache issue with media 2019-04-02 20:28:43 +03:00
Matias Griese
ef43348020 Changelog update 2019-04-01 19:53:14 +03:00
Matias Griese
b6e68bb362 PSR-16, not 15 2019-04-01 19:51:28 +03:00
Matias Griese
8d9ceb5d99 MediaTrait: Use PSR-15 cache 2019-04-01 19:49:15 +03:00
Andy Miller
af47825b76 Merge branch '1.6' of github.com:getgrav/grav into 1.6
# Conflicts:
#	CHANGELOG.md
2019-03-29 11:31:32 -06:00
Andy Miller
a4c88697af set_time_limit(0) for Archiver class 2019-03-29 11:30:55 -06:00
Matias Griese
45fbfb098a Added FlexObjectInterface::getDefaultValue() and FormInterface::getDefaultValue() 2019-03-29 18:19:03 +02:00
Matias Griese
9172e442f2 Blueprint: cache defaults to the object to speed multiple getDefaults() calls up 2019-03-29 18:14:26 +02:00
Matias Griese
3b91f9af8c FlexObjects: Remove null values during save 2019-03-29 13:01:55 +02:00
Matias Griese
d5e9cc4bfe Added RenderInterface 2019-03-28 12:52:34 +02:00
Matias Griese
bb719c5d53 Improve deprecated error logic to better track twig/yaml/markdown issues 2019-03-27 12:06:48 +02:00
Matias Griese
05a6775b08 Minor tweak 2019-03-26 14:07:58 +02:00
Matias Griese
1accbb8edc DebugBar: Fixed some instances where twig template wasn't properly assigned 2019-03-26 14:02:04 +02:00
Matias Griese
a04a7f5714 DebugBar: Resolve twig templates in deprecated backtraces in order to help locating Twig issues 2019-03-26 12:56:53 +02:00
Matias Griese
f31f7f7499 Merge remote-tracking branch 'origin/1.6' into 1.6 2019-03-26 07:51:04 +02:00
Matias Griese
354d6f307c Added Flex[Name]::getFlexType() to all flex classes to have the interface for all Flex classes 2019-03-26 07:50:54 +02:00
Andy Miller
90aa4083ca improved order of page types - fixes #2337 2019-03-25 17:29:35 -07:00
Matias Griese
5fbd252db9 Merge branch 'develop' of github.com:getgrav/grav into 1.6
# Conflicts:
#	CHANGELOG.md
#	composer.json
#	composer.lock
#	system/defines.php
2019-03-25 18:56:09 +02:00
Matias Griese
04d3237a89 Changelog update 2019-03-25 18:53:04 +02:00
Antoine Goutenoir
55aaaeed47 Add the page to the onMarkdownInitialized event. (#2418)
This will allow plugins hooking this event to mergeConfig #2412
2019-03-25 18:51:06 +02:00
Matias Griese
84d995335e Fixed Undefined method closure::fields() when getting avatar for user, thanks @Romarain [#2422] 2019-03-22 18:01:22 +02:00
Djamil Legato
f7d3299ebb Added Awesome Grav link to the Exploring More 2019-03-21 19:06:12 -07:00
Andy Miller
e762c3add9 Merge branch 'release/1.5.10' 2019-03-21 14:16:21 -06:00
Andy Miller
df6bb065d3 Merge tag '1.5.10' into develop
Release v1.5.10
2019-03-21 14:16:21 -06:00
Andy Miller
2fed02affa Prepare for release 2019-03-21 14:16:10 -06:00
Matias Griese
a739ed6825 Improved Exceptions middleware 2019-03-21 12:17:48 +02:00
Matias Griese
90c708db2b Added Content-Type: application/json body support for PSR-7 ServerRequest 2019-03-21 12:01:05 +02:00
Andy Miller
fcbd819f48 Added deferred twig extension 2019-03-20 16:31:19 -06:00
Andy Miller
a25d18bca7 prepare for RC.4 release 2019-03-20 14:06:51 -06:00
Andy Miller
4480077894 Updated with Toolbox 1.4.6 2019-03-20 14:04:35 -06:00
Andy Miller
e9aa338c1e Merge branch 'develop' into 1.6
# Conflicts:
#	composer.json
#	composer.lock
#	system/defines.php
#	system/src/Grav/Common/Page/Media.php
2019-03-20 14:04:00 -06:00
Andy Miller
88a3e874aa Merge branch 'release/1.5.9' 2019-03-20 14:01:21 -06:00
Andy Miller
87814039bc Merge tag '1.5.9' into develop
Release v1.5.9
2019-03-20 14:01:21 -06:00
Andy Miller
af72951671 prepare for release 2019-03-20 14:01:10 -06:00
Andy Miller
5f2dfc8221 Use toolbox 1.4.2 in develop branch for now 2019-03-20 13:58:00 -06:00
Andy Miller
c706756d19 changelog updated 2019-03-20 13:52:27 -06:00
Andy Miller
a4801ead6a Fix for streams with EXIF 2019-03-20 13:52:04 -06:00
Matias Griese
a57cec8404 Merge remote-tracking branch 'origin/1.6' into 1.6 2019-03-20 21:12:41 +02:00
Matias Griese
8b171435a7 Fixed File::save() silently ignoring failures with read only streams 2019-03-20 21:12:33 +02:00
Andy Miller
ab23d071a4 Merge branch 'release/1.5.9' 2019-03-20 12:04:51 -06:00
Andy Miller
3e6c4e9c5e Merge tag '1.5.9' into develop
Release 1.5.9
2019-03-20 12:04:51 -06:00
Andy Miller
28db98c95d Prepare for release 2019-03-20 12:04:26 -06:00
Andy Miller
95583dbbcd updated vendor libs 2019-03-20 11:50:00 -06:00
Andy Miller
9d8fc4a065 New onPageContent() event 2019-03-20 11:16:18 -06:00
Andy Miller
b23cd7bb65 New onPageContent() event 2019-03-20 11:14:38 -06:00
Matias Griese
28d5be982c Merge remote-tracking branch 'origin/1.6' into 1.6 2019-03-20 16:15:28 +02:00
Matias Griese
3836e4d38a Fixed file saving when temporary file cannot be created to the current folder / stream 2019-03-20 16:15:18 +02:00
Andy Miller
4ebfd51e98 Safer stream check 2019-03-20 07:22:05 -06:00
Andy Miller
25ff1241d4 docblock 2019-03-19 13:14:21 -06:00
Matias Griese
0dc3e5806e Minor fix 2019-03-19 16:42:06 +02:00
Matias Griese
982b12a239 Fixed session_start(): Setting option 'session.name' failed [#2408] 2019-03-19 13:47:35 +02:00
Matias Griese
5995515419 Added detection for deprecated features into phpstan 2019-03-19 13:37:02 +02:00
Matias Griese
8c1c813acd Greatly improved Flex interfaces and docblocks 2019-03-19 13:36:12 +02:00
Matias Griese
0e95d7faf6 Class Grav\Common\Page\Medium\AbstractMedia now use array traits instead of extending Grav\Common\Getters 2019-03-19 12:16:02 +02:00
Matias Griese
47c3d4bf9b Improved docblocks for multiple classes 2019-03-18 11:01:41 +02:00
Matias Griese
33282e043b Added composer test-plugins to test plugin issues with the current version of Grav 2019-03-18 10:58:21 +02:00
Andy Miller
81efef7c60 Hopefully fix auto_metadata_exif error #2248 2019-03-15 19:02:17 -06:00
Andy Miller
2678f56499 remove extra stuff 2019-03-15 18:11:49 -06:00
Andy Miller
cd8f578b39 site crashes if header is invalid #2343 2019-03-15 05:10:19 -06:00
Andy Miller
ab535ec8be Better solution for SEO imagename format 2019-03-14 17:14:19 -06:00
Matias Griese
ada6c3f160 Fixed phpstan level 4 and 5 errors in Grav\Framework 2019-03-14 14:06:53 +02:00
Matias Griese
0917850634 Oops, fix a bug 2019-03-14 13:27:13 +02:00
Matias Griese
c09a8fbbc4 Fixed phpstan level 3 errors in Grav\Framework 2019-03-14 13:20:29 +02:00
Matias Griese
2b392055c1 Fixed few phpstan level 3-7 issues 2019-03-14 11:31:26 +02:00
Matias Griese
45acd237f5 Changelog update (2) 2019-03-14 09:18:32 +02:00
Matias Griese
cbf79e38c4 Changelog update 2019-03-14 09:10:38 +02:00
Matias Griese
ed10ce03e2 Twig nicenumber: do not use 0 + string casting hack 2019-03-14 09:04:41 +02:00
Matias Griese
561abc851f Twig tags: use namespaced classes 2019-03-14 09:03:24 +02:00
Matias Griese
8789fd91ee Fixed/silenced the remaining phpstan level 2 issues 2019-03-14 09:01:16 +02:00
Andy Miller
b835db49f4 Added PHPStan badge 2019-03-13 21:35:49 -06:00
Andy Miller
a5e832b0b0 new option for image.prettyname_prefix 2019-03-13 18:04:52 -06:00
Matias Griese
2b16f813ef Merge remote-tracking branch 'origin/1.6' into 1.6 2019-03-13 22:22:14 +02:00
Matias Griese
30b013f5b0 Fixed most phpstan level 2 issues in Grav\Common 2019-03-13 22:22:03 +02:00
Djamil Legato
5eccfaa34b Reverted commit 48816d9 causing issues with GPM cli commands (ref, getgrav/grav-plugin-admin@e3fc4ce) 2019-03-13 13:17:10 -07:00
Matias Griese
fde69ade6b Fixed some phpstan level 2 issues in Grav\Console 2019-03-13 19:46:06 +02:00
Andy Miller
af3d5e9683 phpstan fixes 2019-03-13 11:24:07 -06:00
Andy Miller
c757e00ac9 phpstan fix 2019-03-13 11:16:00 -06:00
Matias Griese
19d61b4013 Merge branch 'develop' of github.com:getgrav/grav into 1.6
# Conflicts:
#	CHANGELOG.md
#	system/src/Grav/Common/User/User.php
2019-03-13 16:13:19 +02:00
Matias Griese
b4a4b60871 Fixed phpdoc generation 2019-03-13 16:00:00 +02:00
Matias Griese
9727f30c61 Fixed broken @inheritdoc in phpdoc blocks 2019-03-13 15:44:53 +02:00
Matias Griese
db8e5b3cbb Merge remote-tracking branch 'origin/1.6' into 1.6 2019-03-13 15:21:18 +02:00
Matias Griese
b0b6c1dd17 Fixed some more phpstan level 2 issues 2019-03-13 15:21:09 +02:00
Sébastien Vanvelthem
47bb35e2e9 Preliminary tests for CsvFormatter::encode + minor fix for empty data (#2405) 2019-03-13 15:02:46 +02:00
Matias Griese
2433e0d2cd Fixed phpstan level 2 issues in Grav\Framework 2019-03-13 14:28:16 +02:00
Matias Griese
cdf891478e Fixed some phpstan level 2 issues 2019-03-13 11:05:50 +02:00
Matias Griese
39b1940f94 Fixed phpstan level 1 issues 2019-03-13 10:16:47 +02:00
Matias Griese
8564524984 Fixed some bugs found by phpstan 2019-03-13 09:49:08 +02:00
Matias Griese
2f74f0f587 Composer update 2019-03-13 08:37:33 +02:00
Matias Griese
6e2e533184 Minor fix for FlexUser, changelog update 2019-03-13 08:30:38 +02:00
Sébastien Vanvelthem
ecd39421d6 Some phpstan issues (#2393)
* Fix bug, method init must be protected
* Added phpstan level 0
* Added exclusions
* Fix: incorrect case in Twig_SimpleFunction
* Fix, no abstract properties, if properties used in methods let's set them
* Fixed Psr\SimpleCache\InvalidArgumentException referenced with incorrect case
* added exclusions
* Fixed: Access to an undefined property Grav\Framework\Form\FormFlash::$uploadObjects.
* Fixed: does not call parent constructor from Grav\Common\Iterator.
* Fixed: does not call parent constructor from Grav\Common\Iterator.
* Fixed: does not call parent constructor
* Fixed: does not call parent constructor
* Minor: correct type for inflector
* Moved phpstan.neon out of public directory
* Added exclusion
* set GRAV_USER_INSTANCE to prevent LogiException in User
* Exlude Stream:create
* Minor: Missing storage property
* Minor: Fixed missing properties (phpstan level 1)
* Added type for $data
* Minor: Ensure $langs is initialized
* Fix possible bug in $http_response_header status code retrieval (PHP 7.1 only)
* Added exclusion for $http_response_header (isset is actually not required)
* Strict null check
2019-03-13 08:15:22 +02:00
Andy Miller
68d43a0c88 update vendor libs 2019-03-12 15:58:33 -06:00
Kirsten Roschanski
c80a3f5568 Update User.php (#2403) 2019-03-12 14:15:03 -06:00
Matias Griese
b3d84a05f5 Merge remote-tracking branch 'origin/1.6' into 1.6 2019-03-12 20:25:35 +02:00
Matias Griese
95495614a8 Simplify grav services and processors 2019-03-12 20:25:17 +02:00
Andy Miller
45d7a164b5 updated changelog 2019-03-12 06:45:39 -06:00
Andy Miller
97b236d117 updated changelog 2019-03-12 06:29:04 -06:00
Andy Miller
8718c5ef31 Remove legacy media.upload_limit references 2019-03-12 06:25:56 -06:00
Andy Miller
7d7418df26 Merge branch '1.6' of github.com:getgrav/grav into 1.6 2019-03-11 12:14:26 -06:00
Andy Miller
dd9aa5bf78 couple of helpers 2019-03-11 12:14:21 -06:00
Matias Griese
7f86f8eb6f Bug fixes 2019-03-11 14:35:44 +02:00
Matias Griese
8b4cac7ba6 Fixed settion caching in FlexIndex 2019-03-11 14:14:20 +02:00
Matias Griese
b388e91177 Implemented Grav\Framework\Pagination classes 2019-03-11 13:25:47 +02:00
Matias Griese
a2ac3f4c8b FlexForm: Fix unique id for new objects 2019-03-08 20:35:54 +02:00
Matias Griese
1db8b93cfa Request object: Include attribute for request time 2019-03-08 20:34:14 +02:00
Matias Griese
6217ac90e0 Improved Flex::getObjects() logic 2019-03-07 14:43:24 +02:00
Matias Griese
bb385490bc Regression: index not working due to missing method 2019-03-07 11:15:16 +02:00
Matias Griese
a5e48ba160 Fixed typo 2019-03-06 12:35:10 +02:00
Matias Griese
c7a4e8e4bb Added FlexObjectInterface::search() and FlexCollectionInterface::search() methods 2019-03-06 12:34:00 +02:00
Matias Griese
85d5b6e889 Generalized flex index creation 2019-03-06 10:19:47 +02:00
Andy Miller
1c725c02f0 Better error checking in bin/plugin for existence and enabled 2019-03-05 12:46:48 -07:00
Matias Griese
5008672a48 Add missing extend to PageInterface 2019-03-05 15:40:34 +02:00
Matias Griese
4e03f19bac Update all classes to rely on PageInterface instead of Page class 2019-03-05 15:29:39 +02:00
Matias Griese
a03c328ee3 Create set of interfaces for all page methods 2019-03-05 15:16:01 +02:00
Andy Miller
77ac68f2e8 Merge branch '1.6' of github.com:getgrav/grav into 1.6 2019-03-04 20:05:21 -07:00
Andy Miller
02acc34461 Move last backup logging into the backup command 2019-03-04 20:05:15 -07:00
Matias Griese
a402a8ef84 Fix FlexForm::getBlueprint() for admin 2019-03-05 00:15:30 +02:00
Matias Griese
60573c1d78 Add parameters for FlexForm::getMediaTaskRoute 2019-03-04 22:59:43 +02:00
Matias Griese
56adf40bb4 Merge remote-tracking branch 'origin/1.6' into 1.6 2019-03-04 18:22:57 +02:00
Matias Griese
d33c45165d Add object.media[filename] support into FlexMediaTrait 2019-03-04 18:22:47 +02:00
Andy Miller
032acedafe Lang updates 2019-03-01 14:32:59 -07:00
Andy Miller
6a48216afb Updated editorconfig to be valid 2019-03-01 14:14:44 -07:00
Matias Griese
85af461eb4 FlexObject: When creating object and key isn't set, use storage key instead 2019-03-01 15:13:21 +02:00
Matias Griese
4b1a129f38 Merge remote-tracking branch 'origin/1.6' into 1.6 2019-03-01 14:48:01 +02:00
Matias Griese
1762ef3d1e Added Medium::size() 2019-03-01 14:47:53 +02:00
Andy Miller
8ffba2d88d fix typo 2019-03-01 05:44:21 -07:00
Andy Miller
f19cb94f89 cleanup 2019-03-01 05:25:29 -07:00
Andy Miller
8fb7caa22a Safer check for admin 2019-02-28 18:44:29 -07:00
Andy Miller
07646860f2 updated toolbox version 2019-02-28 16:11:54 -07:00
Andy Miller
6765c5f594 Merge branch '1.6' of github.com:getgrav/grav into 1.6 2019-02-28 14:59:39 -07:00
Matias Griese
026f9cb3a0 Fixed potential undefined property in onPageNotFound event handling 2019-02-28 22:35:37 +02:00
Andy Miller
516dfd5c26 Merge branch '1.6' of github.com:getgrav/grav into 1.6 2019-02-28 11:30:12 -07:00
Andy Miller
1fbd5e0b38 Fix for static admin::tu() call 2019-02-28 11:29:54 -07:00
Matias Griese
b35a892853 Added Flex::getObjects() and Flex::getMixedCollection() methods for co-mingled collections
Added support to use single Flex key parameter in `Flex::getObject()` method
2019-02-28 16:26:39 +02:00
Matias Griese
578f8e4947 Grav\Framework\File\AbstractFile::save(): Fixed missing directory creation 2019-02-28 10:26:41 +02:00
Andy Miller
a934dd4fff Minor backup things 2019-02-27 12:46:32 -07:00
Andy Miller
18625758e5 Merge branch '1.6' of github.com:getgrav/grav into 1.6 2019-02-26 17:09:15 -07:00
Andy Miller
eb689c417b Updated libs including Toolbox 1.4.3 2019-02-26 17:09:11 -07:00
Matias Griese
df226a1102 Improved File::save() to use a temporary file if file isn't locked 2019-02-26 23:00:43 +02:00
Matias Griese
8947f5ade9 Improved File::save() not to use file lock, but a temporary file, fixed Obtaining write lock failed on file... 2019-02-26 21:21:07 +02:00
Matias Griese
941a5db8de Disable deprecation messages for Page methods for now (used in too many places) 2019-02-26 15:17:03 +02:00
Matias Griese
2fcaaea6ac Fixed mkdir(...) race condition 2019-02-26 15:13:40 +02:00
Matias Griese
5887a396c1 Update changelog by marking fixes/changes between Grav 1.6 beta releases 2019-02-26 12:28:32 +02:00
Matias Griese
f934256e06 Added more deprecation messages 2019-02-26 12:11:51 +02:00
Matias Griese
ccb465e998 Grav 1.6: Renamed $grav['users'] service to $grav['accounts'] 2019-02-26 12:11:15 +02:00
Matias Griese
1b0a6e99f7 Merge branch '1.6' of github.com:getgrav/grav into 1.6 2019-02-26 11:09:52 +02:00
Andy Miller
2e647be565 this is deprecated... 2019-02-25 16:30:31 -07:00
Matias Griese
863123ac92 Changelog update 2019-02-25 19:11:50 +02:00
Matias Griese
434620dea0 Fixed FlexObject::update() removing fields which aren't allowed by ACL 2019-02-25 15:08:33 +02:00
Matias Griese
212d7d24ef Changelog update, minor cleanup 2019-02-25 11:47:24 +02:00
Matias Griese
61cf416df4 Added B/C layer for FormatterInterface 2019-02-25 11:43:42 +02:00
Matias Griese
ff23f6b015 Renamed Grav\Framework\File\Formatter\FormatterInterface to Grav\Framework\File\Interfaces\FileFormatterInterface 2019-02-25 11:29:48 +02:00
Matias Griese
d59d60647c Fixed FlexUser::find() breaking when nothing is found 2019-02-19 23:06:29 +02:00
Matias Griese
cf088d00ff Fixed FlexUser loosing ACL information (apply only for user) 2019-02-19 22:39:47 +02:00
Matias Griese
dd5a10d0e2 Fixed FlexUser loosing ACL information 2019-02-19 22:33:51 +02:00
Matias Griese
0da39aa397 Added method to return FlexIndex from FlexCollection 2019-02-19 12:16:48 +02:00
244 changed files with 7556 additions and 2910 deletions

View File

@@ -13,6 +13,5 @@ indent_style = space
indent_size = 4
# 2 space indentation
[*.yaml, *.yml]
indent_style = space
[*.{yaml,.yml}]
indent_size = 2

View File

@@ -2,6 +2,7 @@ language: php
php:
- '7.1'
- '7.2'
- '7.3'
branches:
only:
- develop

View File

@@ -1,67 +1,62 @@
# v1.6.0-rc.3
## 02/18/2019
# v1.6.4
## 04/15/2019
1. [](#new)
* Implemented `Grav\Framework\Psr7` classes as `Nyholm/psr7` decorators
* Renamed `blueprints/user/accounts.yaml` to `blueprints/user/users.yaml`
* Moved FlexUser index into `user-data://flex/indexes/users.yaml` [#2378](https://github.com/getgrav/grav/issues/2378)
* Added a new `cache-clear` scheduled job to go along with `cache-purge`
1. [](#improved)
* More code cleanup
* Fixed `FlexUser` caching
* Added back missing `page.types` field in system content configuration [admin#1612](https://github.com/getgrav/grav-plugin-admin/issues/1612)
* Console commands: add method for invalidating cache
* Updated languages
1. [](#bugfix)
* Fixed validation for select field type with selectize
* Fixed validation for boolean toggles
* Fixed `Flex[class]::getType()` to return the same value in every class
* Fixed `FlexIndex` keys being lost when `FlexCollection` gets loaded
* Fixed missing `form_nonce` for JS when using `FlexForm`
* Fixed slow loading of `FlexUser` objects on `$grav['users']->find()` and `load()` calls
* Improved `redirect_default_route` logic as well as `Uri::toArray()` to take into account `root_path` and `extension`
* Rework logic to pull out excluded files from pipeline more reliably [#2445](https://github.com/getgrav/grav/issues/2445)
* Better logic in `Utils::normalizePath` to handle externals properly [#2216](https://github.com/getgrav/grav/issues/2216)
* Fixed to force all `Page::taxonomy` to be treated as strings [#2446](https://github.com/getgrav/grav/issues/2446)
* Fixed issue with `Grav['user']` not being available [form#332](https://github.com/getgrav/grav-plugin-form/issues/332)
* Updated rounding logic for `Utils::parseSize()` [#2394](https://github.com/getgrav/grav/issues/2394)
* Fixed Flex simple storage not being properly initialized if used with caching
# v1.6.0-rc.2
## 02/07/2019
# v1.6.3
## 04/12/2019
1. [](#new)
* New experimental **FlexObjects** powered `Users` for increased performance and capability (**disabled** by default)
* New `$grav['users']` service to allow custom user classes implementing `UserInterface`
* Added index file support for Flex Objects
* Added `LogViewer` helper class and CLI command: `bin/grav logviewer`
1. [](#improved)
* Improved error detection for broken Flex Objects
* Removed `apc` and `xcache` support, made `apc` alias of `apcu`
* Support admin and regular translations via the `|t` twig filter and `t()` twig function
* Improved Grav Core installer/updater to run installer script
* Updated vendor libraries including Symfony `4.2.3`
* Renamed old `User` class to `Grav\Common\User\DataUser\User` with multiple improvements and small fixes
* `User` class now acts as a compatibility layer to older versions of Grav
* Deprecated `new User()`, `User::load()`, `User::find()` and `User::delete()` in favor of `$grav['users']` service
* `Media` constructor has now support to not to initialize the media objects
* Cleanly handle session corruption due to changing Flex object types
* Renamed `FlexAuthorizeInterface::authorize()` to `isAuthorized()`
* Added `Blueprint::addDynamicHandler()` method to allow custom dynamic handlers, for example `custom-options@: getCustomOptions`
1. [](#bugfix)
* Fixed non-namespaced exceptions in scheduler
* Fixed trailing slash redirect in multlang environment [#2350](https://github.com/getgrav/grav/issues/2350)
* Fixed Flex from indexing hidden folders/files as objects
* Regression: `$session->getFlashObject('files-upload')` did not work with Form 3.0
* Missed a `CacheCommand` reference in `bin/grav` [#2442](https://github.com/getgrav/grav/issues/2442)
* Fixed issue with `Utils::normalizePath` messing with external URLs [#2216](https://github.com/getgrav/grav/issues/2216)
* Fix for `vUndefined` versions when upgrading
# v1.6.0-rc.1
## 01/30/2019
# v1.6.2
## 04/11/2019
1. [](#bugfix)
* Revert renaming of `ClearCacheCommand` to ensure CLI GPM upgrades go smoothly
# v1.6.1
## 04/11/2019
1. [](#improved)
* Improved `$page->forms()` call, added `$page->addForms()`
* Made `FormFlashFile` more robust against deleted files
* Updated languages from crowdin
* Fixed a bug in `FormFlashFile::moveTo()` not deleting the old file
* Fixed `FlexMediaTrait::getMedia()` trying to include uploaded but already moved media
* Fixed `ImageMedium` constructor warning when file does not exist
* Fixed bad host header in PSR-7 (if using `php -S localhost:8000 system/router.php`)
# v1.6.0-beta.8
## 01/25/2019
* Improved CSS for the bottom filter bar of DebugBar
1. [](#bugfix)
* Fixed issue with `@import` not being added to top of pipelined css [#2440](https://github.com/getgrav/grav/issues/2440)
# v1.6.0
## 04/11/2019
1. [](#new)
* Set minimum requirements to [PHP 7.1.3](https://getgrav.org/blog/raising-php-requirements-2018)
* New `Scheduler` functionality for periodic jobs
* New `Backup` functionality with multiple backup profiles and scheduler integration
* Refactored `Assets Manager` to be more powerful and flexible
* Updated Doctrine Collections to 1.6
* Updated Doctrine Cache to 1.8
* Updated Symfony Components to 4.2
* Added new Cache purge functionality old cache manually via CLI/Admin as well as scheduler integration
* Added new `{% throw 404 'Not Found' %}` twig tag (with custom code/message)
* Added `Grav\Framework\File` classes for handling YAML, Markdown, JSON, INI and PHP serialized files
* Added `Grav\Framework\Collection\AbstractIndexCollection` class
* Added `Grav\Framework\Object\ObjectIndex` class
* Added `Grav\Framework\Flex` classes
* Added support for hiding form fields in blueprints by using dynamic property like `security@: admin.foobar`, `scope@: object` or `scope-ignore@: object` to any field
* New experimental **FlexObjects** powered `Users` for increased performance and capability (**disabled** by default)
* Added PSR-7 and PSR-15 classes
* Added `Grav\Framework\DI\Container` class
* Added `Grav\Framework\RequestHandler\RequestHandler` class
* Added `Page::httpResponseCode()` and `Page::httpHeaders()` methods
* Added `Grav\Framework\Form\Interfaces\FormInterface`
* Added `Grav\Framework\Form\Interfaces\FormFactoryInterface`
* Added `Grav\Framework\Form\FormTrait`
@@ -73,25 +68,8 @@
* Added form preview support for `FlexObject`, including a way to render newly uploaded files before saving them
* Added `FlexObject::getChanges()` to determine what fields change during an update
* Added `arrayDiffMultidimensional`, `arrayIsAssociative`, `arrayCombine` Util functions
1. [](#improved)
* Added method argument `Data::filter($missingValuesAsNull)`, defaulting to `false`
* Improved `Grav\Common\User` class; added `$user->update()` method
* Added trim support for text input fields `validate: trim: true`
1. [](#bugfix)
* Fixed environment getting port added [#2284](https://github.com/getgrav/grav/issues/2284)
* Fixed `FlexForm::updateObject()` to update array values when they are empty in the form
* Fixed some issues related to Medium objects losing query string attributes
* Broke out Medium timestamp so it's not cleared on `reset()s`
* Fixed issue with `redirect_trailing_slash` losing query string [#2269](https://github.com/getgrav/grav/issues/2269)
* Fixed failed login if user attempts to log in with upper case non-english letters
* Removed extra authenticated/authorized fields when saving existing user from a form
* Fixed `Grav\Framework\Route::__toString()` returning relative URL, not relative route
# v1.6.0-beta.7
## 12/14/2018
1. [](#new)
* Updated Symfony Components to 4.2
* New `$grav['users']` service to allow custom user classes implementing `UserInterface`
* Added `LogViewer` helper class and CLI command: `bin/grav logviewer`
* Added `select()` and `unselect()` methods to `CollectionInterface` and its base classes
* Added `orderBy()` and `limit()` methods to `ObjectCollectionInterface` and its base classes
* Added `user-data://` which is a writable stream (`user://data` is not and should be avoided)
@@ -102,46 +80,64 @@
* Added `Grav\Framework\Filesystem\Filesystem` class with methods to manipulate stream URLs
* Added new `$grav['filesystem']` service using an instance of the new `Filesystem` object
* Added `{% render object layout: 'default' with { variable: true } %}` for Flex objects and collections
* Grav 1.6: Flex: Added support for custom object index classes (API compatibility break)
1. [](#improved)
* Improved `Grav\Framework\File\Formatter` classes to have abstract parent class and some useful methods
* Grav 1.6: Improved Flex storage classes
* Grav 1.6: Improved `Grav\Framework\File` classes to use better type hints and the new `Filesystem` class
1. [](#bugfix)
* Fixed handling of `append_url_extension` inside of `Page::templateFormat()` [#2264](https://github.com/getgrav/grav/issues/2264)
* Fixed a broken language string [#2261](https://github.com/getgrav/grav/issues/2261)
* Fixed clearing cache having no effect on Doctrine cache
* Fixed `Medium::relativePath()` for streams
* Fixed `Object` serialization breaking if overriding `jsonSerialize()` method
* Grav 1.6: Fixed `FlexObject::update()` call with partial object update
* Fixed `YamlFormatter::decode()` when calling `init_set()` with integer
* Fixed session throwing error in CLI if initialized
# v1.6.0-beta.6
## 11/12/2018
1. [](#new)
* Added `$grav->setup()` to simplify CLI and custom access points
* Grav 1.6: Added `CsvFormatter` and `CsvFile` classes
* Added `CsvFormatter` and `CsvFile` classes
* Added new system config option to `pages.hide_empty_folders` if a folder has no valid `.md` file available. Default behavior is `false` for compatibility.
* Added new system config option for `languages.pages_fallback_only` forcing only 'fallback' to find page content through supported languages, default behavior is to display any language found if active language is missing
* Added `Utils::arrayFlattenDotNotation()` and `Utils::arrayUnflattenDotNotation()` helper methods
1. [](#improved)
* Add the page to onMarkdownInitialized event [#2412](https://github.com/getgrav/grav/issues/2412)
* Doctrine filecache is now namespaced with prefix to support purging
* Register all page types into `blueprint://pages` stream
* Removed `apc` and `xcache` support, made `apc` alias of `apcu`
* Support admin and regular translations via the `|t` twig filter and `t()` twig function
* Improved Grav Core installer/updater to run installer script
* Updated vendor libraries including Symfony `4.2.3`
* Renamed old `User` class to `Grav\Common\User\DataUser\User` with multiple improvements and small fixes
* `User` class now acts as a compatibility layer to older versions of Grav
* Deprecated `new User()`, `User::load()`, `User::find()` and `User::delete()` in favor of `$grav['users']` service
* `Media` constructor has now support to not to initialize the media objects
* Cleanly handle session corruption due to changing Flex object types
* Added `FlexObjectInterface::getDefaultValue()` and `FormInterface::getDefaultValue()`
* Added new `onPageContent()` event for every call to `Page::content()`
* Added phpstan: PHP Static Analysis Tool [#2393](https://github.com/getgrav/grav/pull/2393)
* Added `composer test-plugins` to test plugin issues with the current version of Grav
* Added `Flex::getObjects()` and `Flex::getMixedCollection()` methods for co-mingled collections
* Added support to use single Flex key parameter in `Flex::getObject()` method
* Added `FlexObjectInterface::search()` and `FlexCollectionInterface::search()` methods
* Override `system.media.upload_limit` with PHP's `post_max_size` or `upload_max_filesize`
* Class `Grav\Common\Page\Medium\AbstractMedia` now use array traits instead of extending `Grav\Common\Getters`
* Implemented `Grav\Framework\Psr7` classes as `Nyholm/psr7` decorators
* Added a new `cache-clear` scheduled job to go along with `cache-purge`
* Renamed `Grav\Framework\File\Formatter\FormatterInterface` to `Grav\Framework\File\Interfaces\FileFormatterInterface`
* Improved `File::save()` to use a temporary file if file isn't locked
* Improved `|t` filter to better support admin `|tu` style filter if in admin
* Update all classes to rely on `PageInterface` instead of `Page` class
* Better error checking in `bin/plugin` for existence and enabled
* Removed `media.upload_limit` references
* Twig `nicenumber`: do not use 0 + string casting hack
* Converted Twig tags to use namespaced Twig classes
* Site shows error on page rather than hard-crash when page has invalid frontmatter [#2343](https://github.com/getgrav/grav/issues/2343)
* Added `languages.default_lang` option to override the default lang (usually first supported language)
* Added `Content-Type: application/json` body support for PSR-7 `ServerRequest`
* Remove PHP time limit in `ZipArchive`
* DebugBar: Resolve twig templates in deprecated backtraces in order to help locating Twig issues
* Added `$grav['cache']->getSimpleCache()` method for getting PSR-16 compatible cache
* MediaTrait: Use PSR-16 cache
* Improved `Utils::normalizePath()` to support non-protocol URLs
* Added ability to reset `Page::metadata` to allow rebuilding from automatically generated values
* Added back missing `page.types` field in system content configuration [admin#1612](https://github.com/getgrav/grav-plugin-admin/issues/1612)
* Console commands: add method for invalidating cache
* Updated languages
* Improved `$page->forms()` call, added `$page->addForms()`
* Updated languages from crowdin
* Fixed `ImageMedium` constructor warning when file does not exist
* Improved `Grav\Common\User` class; added `$user->update()` method
* Added trim support for text input fields `validate: trim: true`
* Improved `Grav\Framework\File\Formatter` classes to have abstract parent class and some useful methods
* Support negotiated content types set via the Request `Accept:` header
* Support negotiated language types set via the Request `Accept-Language:` header
* Cleaned up and sorted the Service `idMap`
* Grav 1.6: Allow custom Flex form views
1. [](#bugfix)
* Fixed `Uri::hasStandardPort()` to support reverse proxy configurations [#1786](https://github.com/getgrav/grav/issues/1786)
* Use `append_url_extension` from page header to set template format if set [#2604](https://github.com/getgrav/grav/pull/2064)
* Fixed some bugs in Grav environment selection logic
# v1.6.0-beta.5
## 11/05/2018
1. [](#new)
* Added PSR-7 and PSR-15 classes
* Added `Grav\Framework\DI\Container` class
* Added `Grav\Framework\RequestHandler\RequestHandler` class
* Added `Page::httpResponseCode()` and `Page::httpHeaders()` methods
1. [](#improved)
* Updated `Grav` container object to implement PSR-11 `ContainerInterface`
* Updated Grav `Processor` classes to implement PSR-15 `MiddlewareInterface`
* Make `Data` class to extend `JsonSerializable`
@@ -150,67 +146,68 @@
* Set session name based on `security.salt` rather than `GRAV_ROOT` [#2242](https://github.com/getgrav/grav/issues/2242)
* Added option to configure list of `xss_invalid_protocols` in `Security` config [#2250](https://github.com/getgrav/grav/issues/2250)
* Smarter `security.salt` checking now we use `security.yaml` for other options
* Grav 1.6: Merged Grav 1.5.4 fixes in
# v1.6.0-beta.4
## 10/24/2018
1. [](#new)
* Added new system config option to `pages.hide_empty_folders` if a folder has no valid `.md` file available. Default behavior is `false` for compatibility.
* Added new system config option for `languages.pages_fallback_only` forcing only 'fallback' to find page content through supported languages, default behavior is to display any language found if active language is missing
* Added `Utils::arrayFlattenDotNotation()` and `Utils::arrayUnflattenDotNotation()` helper methods
1. [](#improved)
* Added apcu autoloader optimization
* Additional helper methods in `Language`, `Languages`, and `LanguageCodes` classes
1. [](#bugfix)
* Use login provider User avatar if set
* Fixed `Folder::doDelete($folder, false)` removing symlink when it should not
# v1.6.0-beta.3
## 10/15/2018
1. [](#improved)
* Call `onFatalException` event also on internal PHP errors
* Built-in PHP Webserver: log requests before handling them
1. [](#bugfix)
* Grav 1.6: Scheduler Fallback for never runs and Windows support [#2202](https://github.com/getgrav/grav/pull/2202)
# v1.6.0-beta.2
## 10/09/2018
1. [](#new)
* Grav 1.6: Added Flex support for custom media tasks
1. [](#improved)
* Built-in PHP Webserver: log requests before handling them
* Added support for syslog and syslog facility logging (default: 'file')
* Improved usability of `System` configuration blueprint with side-tabs
1. [](#bugfix)
1. [](#bugfix)
* Fixed issue with `Truncator::truncateWords` and `Truncator::truncateLetters` when string not wrapped in tags [#2432](https://github.com/getgrav/grav/issues/2432)
* Fixed `Undefined method closure::fields()` when getting avatar for user, thanks @Romarain [#2422](https://github.com/getgrav/grav/issues/2422)
* Fixed cached images not being updated when source image is modified
* Fixed deleting last list item in the form
* Fixed issue with `Utils::url()` method would append extra `base_url` if URL already included it
* Fixed `mkdir(...)` race condition
* Fixed `Obtaining write lock failed on file...`
* Fixed potential undefined property in `onPageNotFound` event handling
* Fixed some potential issues/bugs found by phpstan
* Fixed regression in GPM packages casted to Array (ref, getgrav/grav-plugin-admin@e3fc4ce)
* Fixed session_start(): Setting option 'session.name' failed [#2408](https://github.com/getgrav/grav/issues/2408)
* Fixed validation for select field type with selectize
* Fixed validation for boolean toggles
* Fixed non-namespaced exceptions in scheduler
* Fixed trailing slash redirect in multlang environment [#2350](https://github.com/getgrav/grav/issues/2350)
* Fixed some issues related to Medium objects losing query string attributes
* Broke out Medium timestamp so it's not cleared on `reset()`s
* Fixed issue with `redirect_trailing_slash` losing query string [#2269](https://github.com/getgrav/grav/issues/2269)
* Fixed failed login if user attempts to log in with upper case non-english letters
* Removed extra authenticated/authorized fields when saving existing user from a form
* Fixed `Grav\Framework\Route::__toString()` returning relative URL, not relative route
* Fixed handling of `append_url_extension` inside of `Page::templateFormat()` [#2264](https://github.com/getgrav/grav/issues/2264)
* Fixed a broken language string [#2261](https://github.com/getgrav/grav/issues/2261)
* Fixed clearing cache having no effect on Doctrine cache
* Fixed `Medium::relativePath()` for streams
* Fixed `Object` serialization breaking if overriding `jsonSerialize()` method
* Fixed `YamlFormatter::decode()` when calling `init_set()` with integer
* Fixed session throwing error in CLI if initialized
* Fixed `Uri::hasStandardPort()` to support reverse proxy configurations [#1786](https://github.com/getgrav/grav/issues/1786)
* Use `append_url_extension` from page header to set template format if set [#2604](https://github.com/getgrav/grav/pull/2064)
* Fixed some bugs in Grav environment selection logic
* Use login provider User avatar if set
* Fixed `Folder::doDelete($folder, false)` removing symlink when it should not
* Fixed asset manager to not add empty assets when they don't exist in the filesystem
* Update `script` and `style` Twig tags to use the new `Assets` classes
* Fixed asset pipeline to rewrite remote URLs as well as local [#2216](https://github.com/getgrav/grav/issues/2216)
* Grav 1.6: Regression: Fixed asset manager methods with default legacy attributes
# v1.6.0-beta.1
## 10/01/2018
# v1.5.10
## 03/21/2019
1. [](#new)
* Set minimum requirements to [PHP 7.1.3](https://getgrav.org/blog/raising-php-requirements-2018)
* New `Scheduler` functionality for periodic jobs
* New `Backup` functionality with multiple backup profiles and scheduler integration
* Refactored `Assets Manager` to be more powerful and flexible
* Updated Doctrine Collections to 1.5
* Updated Doctrine Cache to 1.8
* Updated Symfony Components to 4.1
* Added a new Deferred Twig extension to allow adding content to Twig blocks after render
* Added new Cache purge functionality old cache manually via CLI/Admin as well as scheduler integration
* Added new `{% throw 404 'Not Found' %}` twig tag (with custom code/message)
* Added `Grav\Framework\File` classes for handling YAML, Markdown, JSON, INI and PHP serialized files
* Added `Grav\Framework\Collection\AbstractIndexCollection` class
* Added `Grav\Framework\Object\ObjectIndex` class
* Added `Grav\Framework\Flex` classes
* Added support for hiding form fields in blueprints by using dynamic property like `security@: admin.foobar`, `scope@: object` or `scope-ignore@: object` to any field
* Added new `deferred` Twig extension
# v1.5.9
## 03/20/2019
1. [](#new)
* Added new `onPageContent()` event for every call to `Page::content()`
1. [](#improved)
* Doctrine filecache is now namespaced with prefix to support purging
* Register all page types into `blueprint://pages` stream
* Fixed phpdoc generation
* Updated vendor libraries
* Force Toolbox v1.4.2
1. [](#bugfix)
* EXIF fix for streams
* Fix for User avatar not working due to uppercase or spaces in email [#2403](https://github.com/getgrav/grav/pull/2403)
# v1.5.8
## 02/07/2019

View File

@@ -1,5 +1,6 @@
# ![](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)
[![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)
[![Build Status](https://travis-ci.org/getgrav/grav.svg?branch=develop)](https://travis-ci.org/getgrav/grav) [![OpenCollective](https://opencollective.com/grav/backers/badge.svg)](#backers) [![OpenCollective](https://opencollective.com/grav/sponsors/badge.svg)](#sponsors)
@@ -105,6 +106,7 @@ If you discover a possible security issue related to Grav or one of its plugins,
* Dive into more [advanced](https://learn.getgrav.org/advanced) functions
* Learn about the [Grav CLI](https://learn.getgrav.org/cli-console/grav-cli)
* Review examples in the [Grav Cookbook](https://learn.getgrav.org/cookbook)
* More [Awesome Grav Stuff](https://github.com/getgrav/awesome-grav)
# Backers
Support Grav with a monthly donation to help us continue development. [[Become a backer](https://opencollective.com/grav#backer)]

View File

@@ -40,7 +40,7 @@ $app->addCommands(array(
new \Grav\Console\Cli\ComposerCommand(),
new \Grav\Console\Cli\SandboxCommand(),
new \Grav\Console\Cli\CleanCommand(),
new \Grav\Console\Cli\CacheCommand(),
new \Grav\Console\Cli\ClearCacheCommand(),
new \Grav\Console\Cli\BackupCommand(),
new \Grav\Console\Cli\NewProjectCommand(),
new \Grav\Console\Cli\SchedulerCommand(),

View File

@@ -58,7 +58,6 @@ $grav['users'];
$grav['plugins']->init();
$grav['themes']->init();
$app = new Application('Grav Plugins Commands', GRAV_VERSION);
$pattern = '([A-Z]\w+Command\.php)';
@@ -73,12 +72,26 @@ $argv = array_merge([$bin], $argv);
$input = new ArgvInput($argv);
/** @var \Grav\Common\Data\Data $plugin */
$plugin = $grav['plugins']->get($name);
$output = new ConsoleOutput();
$output->getFormatter()->setStyle('red', new OutputFormatterStyle('red', null, array('bold')));
$output->getFormatter()->setStyle('white', new OutputFormatterStyle('white', null, array('bold')));
if (is_null($plugin)) {
$output->writeln('');
$output->writeln("<red>$name plugin not found</red>");
die;
}
if (!$plugin->enabled) {
$output->writeln('');
$output->writeln("<red>$name not enabled</red>");
die;
}
if (!$name) {
$output->writeln('');
$output->writeln('<red>Usage:</red>');

View File

@@ -12,6 +12,7 @@
"ext-openssl": "*",
"ext-curl": "*",
"ext-zip": "*",
"ext-dom": "*",
"symfony/polyfill-iconv": "^1.9",
"symfony/polyfill-php72": "^1.9",
"symfony/polyfill-php73": "^1.9",
@@ -53,10 +54,19 @@
},
"require-dev": {
"codeception/codeception": "^2.4",
"phpstan/phpstan": "^0.11",
"phpstan/phpstan-deprecation-rules": "^0.11.0",
"phpunit/php-code-coverage": "~6.0",
"fzaninotto/faker": "^1.8",
"victorjonsson/markdowndocs": "dev-master"
},
"suggest": {
"ext-zend-opcache": "Recommended for better performance",
"ext-intl": "Recommended for multi-language sites",
"ext-memcache": "Needed to support Memcache servers",
"ext-memcached": "Needed to support Memcached servers",
"ext-redis": "Needed to support Redis servers"
},
"config": {
"apcu-autoloader": true,
"platform": {
@@ -80,6 +90,9 @@
},
"scripts": {
"post-create-project-cmd": "bin/grav install",
"phpstan": "vendor/bin/phpstan analyse -l 2 -c ./tests/phpstan/phpstan.neon system/src --memory-limit=256M",
"phpstan-framework": "vendor/bin/phpstan analyse -l 5 -c ./tests/phpstan/phpstan.neon system/src/Grav/Framework --memory-limit=256M",
"test-plugins": "vendor/bin/phpstan analyse -l 0 -c ./tests/phpstan/plugins.neon user/plugins --memory-limit=256M",
"test": "vendor/bin/codecept run unit",
"test-windows": "vendor\\bin\\codecept run unit"
},

1195
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -30,9 +30,25 @@ div.phpdebugbar {
}
.phpdebugbar .phpdebugbar-widgets-toolbar {
border-top: 1px solid #ddd;
padding-left: 5px;
padding-right: 2px;
padding-top: 2px;
background-color: #fafafa !important;
width: auto !important;
left: 0;
right: 0;
}
.phpdebugbar .phpdebugbar-widgets-toolbar input {
background: transparent !important;
}
.phpdebugbar .phpdebugbar-widgets-toolbar .phpdebugbar-widgets-filter {
}
.phpdebugbar input[type=text] {
padding: 0;
display: inline;

View File

@@ -19,22 +19,22 @@ form:
purge.trigger:
type: select
label: Backup Storage Purge Trigger
label: PLUGIN_ADMIN.BACKUPS_STORAGE_PURGE_TRIGGER
size: medium
default: space
options:
space: Maximum Backup Space
number: Maximum Number of Backups
time: maximum Rention Time
time: maximum Retention Time
validate:
required: true
purge.max_backups_count:
type: number
label: Maximum Number of Backups
label: PLUGIN_ADMIN.BACKUPS_MAX_COUNT
default: 25
size: x-small
help: "0 is unlimited"
help: PLUGIN_ADMIN.BACKUPS_MAX_COUNT
validate:
min: 0
type: number
@@ -43,7 +43,7 @@ form:
purge.max_backups_space:
type: number
label: Maximum Backups Space
label: PLUGIN_ADMIN.BACKUPS_MAX_SPACE
append: in GB
size: x-small
default: 5
@@ -55,8 +55,8 @@ form:
purge.max_backups_time:
type: number
label: Maximum Rention Time
append: in Days
label: PLUGIN_ADMIN.BACKUPS_MAX_RETENTION_TIME
append: PLUGIN_ADMIN.BACKUPS_MAX_RETENTION_TIME_APPEND
size: x-small
default: 365
validate:
@@ -80,35 +80,35 @@ form:
fields:
.name:
type: text
label: Name
placeholder: 'Clear Backup Name'
label: PLUGIN_ADMIN.NAME
placeholder: PLUGIN_ADMIN.BACKUPS_PROFILE_NAME
validate:
max: 20
message: 'Name must be less than 20 characters'
required: true
.root:
type: text
label: Root Folder
help: Can be an absolute path or a stream
label: PLUGIN_ADMIN.BACKUPS_PROFILE_ROOT_FOLDER
help: PLUGIN_ADMIN.BACKUPS_PROFILE_ROOT_FOLDER_HELP
placeholder: '/'
default: '/'
validate:
required: true
.exclude_paths:
type: textarea
label: Exclude Paths
label: PLUGIN_ADMIN.BACKUPS_PROFILE_EXCLUDE_PATHS
rows: 5
placeholder: "/backup\r/cache\r/images\r/logs\r/tmp"
help: Absolute paths to exclude, one per line
help: PLUGIN_ADMIN.BACKUPS_PROFILE_EXCLUDE_PATHS_HELP
.exclude_files:
type: textarea
label: Exclude Files
label: PLUGIN_ADMIN.BACKUPS_PROFILE_EXCLUDE_FILES
rows: 5
placeholder: ".DS_Store\r.git\r.svn\r.hg\r.idea\r.vscode\rnode_modules"
help: Specfic Files or Folders to exclude, one per line
help: PLUGIN_ADMIN.BACKUPS_PROFILE_EXCLUDE_FILES_HELP
.schedule:
type: toggle
label: Enable Scheduled Job
label: PLUGIN_ADMIN.BACKUPS_PROFILE_SCHEDULE
highlight: 1
default: 1
options:
@@ -118,7 +118,7 @@ form:
type: bool
.schedule_at:
type: cron
label: Run Scheduled Job
label: PLUGIN_ADMIN.BACKUPS_PROFILE_SCHEDULE_AT
default: '* 3 * * *'
validate:
required: true

View File

@@ -38,37 +38,37 @@ form:
message: 'ID must be lowercase with dashes/underscores only and less than 20 characters'
.command:
type: text
label: Command
label: PLUGIN_ADMIN.COMMAND
placeholder: 'cd ~;ls -lah;'
validate:
required: true
.args:
type: text
label: Extra Arguments
label: PLUGIN_ADMIN.EXTRA_ARGUMENTS
.at:
type: cron
label: Run At
help: 'Cron formatted "at" syntax'
label: PLUGIN_ADMIN.SCHEDULER_RUNAT
help: PLUGIN_ADMIN.SCHEDULER_RUNAT_HELP
placeholder: '* * * * *'
validate:
required: true
.output:
type: text
label: Output File
help: 'The path/filename of the output file (from the root of the Grav installation)'
label: PLUGIN_ADMIN.SCHEDULER_OUTPUT
help: PLUGIN_ADMIN.SCHEDULER_OUTPUT_HELP
placeholder: 'logs/ls-cron.out'
.output_mode:
type: select
label: Output Type
help: 'Either append to the same file each run, or overwrite the file with each run'
label: PLUGIN_ADMIN.SCHEDULER_OUTPUT_TYPE
help: PLUGIN_ADMIN.SCHEDULER_OUTPUT_TYPE_HELP
default: append
options:
append: Append
overwrite: Overwrite
.email:
type: text
label: Email
help: 'Email to send output to. NOTE: requires output file to be set'
label: PLUGIN_ADMIN.SCHEDULER_EMAIL
help: PLUGIN_ADMIN.SCHEDULER_EMAIL_HELP
placeholder: 'notifications@yoursite.com'

View File

@@ -323,6 +323,12 @@ form:
validate:
type: commalist
languages.default_lang:
type: text
size: x-small
label: PLUGIN_ADMIN.DEFAULT_LANG
help: PLUGIN_ADMIN.DEFAULT_LANG_HELP
languages.include_default_lang:
type: toggle
label: PLUGIN_ADMIN.INCLUDE_DEFAULT_LANG
@@ -1062,6 +1068,17 @@ form:
validate:
type: bool
images.seofriendly:
type: toggle
label: PLUGIN_ADMIN.IMAGES_SEOFRIENDLY
help: PLUGIN_ADMIN.IMAGES_SEOFRIENDLY_HELP
highlight: 0
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: bool
media.enable_media_timestamp:
type: toggle
label: PLUGIN_ADMIN.ENABLE_MEDIA_TIMESTAMP

View File

@@ -107,6 +107,7 @@ form:
fields:
groups:
security@: admin.super
type: select
multiple: true
size: large
@@ -118,6 +119,7 @@ form:
type: commalist
access:
security@: admin.super
type: permissions
label: PLUGIN_ADMIN.PERMISSIONS
ignore_empty: true

View File

@@ -23,7 +23,7 @@ config:
menu:
list:
route: /users
title: Users
route: '/accounts'
title: Accounts
icon: fa-users
authorize: ['admin.users', 'admin.super']
authorize: ['admin.users', 'admin.accounts', 'admin.super']

View File

@@ -13,6 +13,7 @@ intl_enabled: true # Special logic for PHP Interna
languages:
supported: [] # List of languages supported. eg: [en, fr, de]
default_lang: # Default is the first supported language. Must be one of the supported languages
include_default_lang: true # Include the default lang prefix in all URLs
pages_fallback_only: false # Only fallback to find page content through supported languages
translations: true # Enable translations by default
@@ -53,7 +54,7 @@ pages:
special_chars: # List of special characters to automatically convert to entities
'>': 'gt'
'<': 'lt'
types: [html,htm,json,xml,txt,rss,atom] # list of valid page types
types: [html,htm,xml,txt,json,rss,atom] # list of valid page types
append_url_extension: '' # Append page's extension in Page urls (e.g. '.html' results in /path/page.html)
expires: 604800 # Page expires time in seconds (604800 seconds = 7 days)
cache_control: # Can be blank for no setting, or a valid `cache-control` text value
@@ -76,7 +77,7 @@ cache:
enabled: true # Set to true to enable caching
check:
method: file # Method to check for updates in pages: file|folder|hash|none
driver: auto # One of: auto|file|apc|xcache|memcache|wincache
driver: auto # One of: auto|file|apcu|memcache|wincache
prefix: 'g' # Cache prefix string (prevents cache conflicts)
purge_at: '0 4 * * *' # How often to purge old file cache (using new scheduler)
clear_at: '0 3 * * *' # How often to clear cache (using new scheduler)
@@ -133,6 +134,7 @@ images:
cache_perms: '0755' # MUST BE IN QUOTES!! Default cache folder perms. Usually '0755' or '0775'
debug: false # Show an overlay over images indicating the pixel depth of the image when working with retina for example
auto_fix_orientation: false # Automatically fix the image orientation based on the Exif data
seofriendly: false # SEO-friendly processed image names
media:
enable_media_timestamp: false # Enable media timestamps

View File

@@ -8,8 +8,8 @@
// Some standard defines
define('GRAV', true);
define('GRAV_VERSION', '1.6.0-rc.3');
define('GRAV_TESTING', true);
define('GRAV_VERSION', '1.6.4');
define('GRAV_TESTING', false);
define('DS', '/');
if (!defined('GRAV_PHP_MIN')) {

View File

@@ -37,12 +37,6 @@ class Assets extends PropertyObject
/** @const Regex to match JavaScript files */
const JS_REGEX = '/.\.js$/i';
/**
* @const Regex to match <script> or <style> tag when adding inline style/script. Note that this only supports a
* single tag, so the check is greedy to avoid issues in JS.
*/
const HTML_TAG_REGEX = '#(<([A-Z][A-Z0-9]*)>)+(.*)(<\/\2>)#is';
protected $assets_dir;
protected $assets_url;
@@ -51,8 +45,10 @@ class Assets extends PropertyObject
// Config Options
protected $css_pipeline;
protected $css_pipeline_include_externals;
protected $css_pipeline_before_excludes;
protected $js_pipeline;
protected $js_pipeline_include_externals;
protected $js_pipeline_before_excludes;
protected $pipeline_options = [];
@@ -123,7 +119,7 @@ class Assets extends PropertyObject
* It automatically detects the asset type (JavaScript, CSS or collection).
* You may add more than one asset passing an array as argument.
*
* @param $asset
* @param array|string $asset
* @return $this
*/
public function add($asset)
@@ -270,6 +266,21 @@ class Assets extends PropertyObject
protected function filterAssets($assets, $key, $value, $sort = false)
{
$results = array_filter($assets, function($asset) use ($key, $value) {
if ($key === 'position' && $value === 'pipeline') {
$type = $asset->getType();
if ($asset->getRemote() && $this->{$type . '_pipeline_include_externals'} === false) {
if ($this->{$type . '_pipeline_before_excludes'}) {
$asset->setPosition('after');
} else {
$asset->setPosition('before');
}
return false;
}
}
if ($asset[$key] === $value) return true;
return false;
});
@@ -298,11 +309,9 @@ class Assets extends PropertyObject
$before_output = '';
$pipeline_output = '';
$after_output = '';
$no_pipeline = [];
$assets = 'assets_' . $type;
$pipeline_enabled = $type . '_pipeline';
$before_excludes = $type . '_pipeline_before_excludes';
$render_pipeline = 'render' . ucfirst($type);
$group_assets = $this->filterAssets($this->$assets, 'group', $group);
@@ -315,22 +324,13 @@ class Assets extends PropertyObject
$options = array_merge($this->pipeline_options, ['timestamp' => $this->timestamp]);
$pipeline = new Pipeline($options);
$pipeline_output = $pipeline->$render_pipeline($pipeline_assets, $group, $attributes, $no_pipeline);
$pipeline_output = $pipeline->$render_pipeline($pipeline_assets, $group, $attributes);
} else {
foreach ($pipeline_assets as $asset) {
$pipeline_output .= $asset->render();
}
}
// Handle stuff that couldn't be pipelined
if (!empty($no_pipeline)) {
if ($this->{$before_excludes}) {
$after_assets = array_merge($after_assets, $no_pipeline);
} else {
$before_assets = array_merge($before_assets, $no_pipeline);
}
}
// Before Pipeline
foreach ($before_assets as $asset) {
$before_output .= $asset->render();

View File

@@ -129,6 +129,12 @@ abstract class BaseAsset extends PropertyObject
return $this->remote;
}
public function setPosition($position)
{
$this->position = $position;
return $this;
}
/**
*
@@ -153,7 +159,6 @@ abstract class BaseAsset extends PropertyObject
* Build local links including grav asset shortcodes
*
* @param string $asset the asset string reference
* @param bool $absolute build absolute asset link
*
* @return string the final link url to the asset
*/
@@ -175,4 +180,16 @@ abstract class BaseAsset extends PropertyObject
{
return ['type' => $this->getType(), 'elements' => $this->getElements()];
}
/**
* Placeholder for AssetUtilsTrait method
*
* @param string $file
* @param string $dir
* @param bool $local
*/
protected function cssRewrite($file, $dir, $local)
{
return;
}
}

View File

@@ -33,6 +33,8 @@ class Pipeline extends PropertyObject
/** @const Regex to match CSS import content */
protected const CSS_IMPORT_REGEX = '{@import(.*?);}';
protected const FIRST_FORWARDSLASH_REGEX = '{^\/{1}\w}';
protected $css_minify;
protected $css_minify_windows;
protected $css_rewrite;
@@ -48,9 +50,6 @@ class Pipeline extends PropertyObject
protected $query;
protected $asset;
protected $css_pipeline_include_externals;
protected $js_pipeline_include_externals;
/**
* Closure used by the pipeline to fetch assets.
*
@@ -89,11 +88,10 @@ class Pipeline extends PropertyObject
* @param array $assets
* @param string $group
* @param array $attributes
* @param array $no_pipeline
*
* @return bool|string URL or generated content if available, else false
*/
public function renderCss($assets, $group, $attributes = [], &$no_pipeline = [])
public function renderCss($assets, $group, $attributes = [])
{
// temporary list of assets to pipeline
$inline_group = false;
@@ -117,21 +115,13 @@ class Pipeline extends PropertyObject
if (file_exists($this->assets_dir . $file)) {
$buffer = file_get_contents($this->assets_dir . $file) . "\n";
} else {
foreach ($assets as $id => $asset) {
if ($this->css_pipeline_include_externals === false && $asset->getRemote()) {
$no_pipeline[$id] = $asset;
unset($assets[$id]);
}
}
//if nothing found get out of here!
if (empty($assets) && empty($no_pipeline)) {
if (empty($assets)) {
return false;
}
// Concatenate files
$buffer = $this->gatherLinks($assets, self::CSS_ASSET, $no_pipeline);
$buffer = $this->gatherLinks($assets, self::CSS_ASSET);
// Minify if required
if ($this->shouldMinify('css')) {
@@ -162,11 +152,10 @@ class Pipeline extends PropertyObject
* @param array $assets
* @param string $group
* @param array $attributes
* @param array $no_pipeline
*
* @return bool|string URL or generated content if available, else false
*/
public function renderJs($assets, $group, $attributes = [], &$no_pipeline = [])
public function renderJs($assets, $group, $attributes = [])
{
// temporary list of assets to pipeline
$inline_group = false;
@@ -190,21 +179,13 @@ class Pipeline extends PropertyObject
if (file_exists($this->assets_dir . $file)) {
$buffer = file_get_contents($this->assets_dir . $file) . "\n";
} else {
foreach ($assets as $id => $asset) {
if ($this->js_pipeline_include_externals === false && $asset->getRemote()) {
$no_pipeline[$id] = $asset;
unset($assets[$id]);
}
}
//if nothing found get out of here!
if (empty($assets) && empty($no_pipeline)) {
if (empty($assets)) {
return false;
}
// Concatenate files
$buffer = $this->gatherLinks($assets, self::JS_ASSET, $no_pipeline);
$buffer = $this->gatherLinks($assets, self::JS_ASSET);
// Minify if required
if ($this->shouldMinify('js')) {
@@ -235,7 +216,7 @@ class Pipeline extends PropertyObject
*
* @param string $file the css source file
* @param string $dir , $local relative path to the css file
* @param boolean $local is this a local or remote asset
* @param bool $local is this a local or remote asset
*
* @return mixed
*/
@@ -251,13 +232,21 @@ class Pipeline extends PropertyObject
$old_url = $matches[2];
// Ensure link is not rooted to web server, a data URL, or to a remote host
if (Utils::startsWith($old_url, '/') || Utils::startsWith($old_url, 'data:') || $this->isRemoteLink($old_url)) {
if (preg_match(self::FIRST_FORWARDSLASH_REGEX, $old_url) || Utils::startsWith($old_url, 'data:') || $this->isRemoteLink($old_url)) {
return $matches[0];
}
$new_url = ($local ? $this->base_url: '') . ltrim(Utils::normalizePath($dir . '/' . $old_url), '/');
// clean leading /
$old_url = Utils::normalizePath($dir . '/' . $old_url);
if (preg_match(self::FIRST_FORWARDSLASH_REGEX, $old_url)) {
$old_url = ltrim($old_url, '/');
}
return str_replace($old_url, $new_url, $matches[0]);
$new_url = ($local ? $this->base_url: '') . $old_url;
$fixed = str_replace($matches[2], $new_url, $matches[0]);
return $fixed;
}, $file);
return $file;

View File

@@ -38,11 +38,10 @@ trait AssetUtilsTrait
*
* @param array $assets
* @param bool $css
* @param array $no_pipeline
*
* @return string
*/
protected function gatherLinks(array $assets, $css = true, &$no_pipeline = [])
protected function gatherLinks(array $assets, $css = true)
{
$buffer = '';
@@ -74,9 +73,6 @@ trait AssetUtilsTrait
// No file found, skip it...
if ($file === false) {
if (!$local) { // Assume we couldn't download this file for some reason assume it's not pipeline compatible
$no_pipeline[$id] = $asset;
}
continue;
}
@@ -113,7 +109,7 @@ trait AssetUtilsTrait
{
$imports = [];
$file = (string)preg_replace_callback(self::CSS_IMPORT_REGEX, function ($matches) {
$file = (string)preg_replace_callback(self::CSS_IMPORT_REGEX, function ($matches) use (&$imports) {
$imports[] = $matches[0];
return '';

View File

@@ -14,6 +14,11 @@ use Grav\Common\Assets;
trait LegacyAssetsTrait
{
/**
* @param array $args
* @param string $type
* @return array
*/
protected function unifyLegacyArguments($args, $type = Assets::CSS_TYPE)
{
// First argument is always the asset
@@ -77,34 +82,36 @@ trait LegacyAssetsTrait
/**
* Convenience wrapper for async loading of JavaScript
*
* @param $asset
* @param int $priority
* @param bool $pipeline
* @param string $group name of the group
*
* @deprecated Please use dynamic method with ['loading' => 'async']
* @param string|array $asset
* @param int $priority
* @param bool $pipeline
* @param string $group name of the group
*
* @return \Grav\Common\Assets
* @deprecated Please use dynamic method with ['loading' => 'async'].
*/
public function addAsyncJs($asset, $priority = 10, $pipeline = true, $group = 'head')
{
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.6, use dynamic method with [\'loading\' => \'async\']', E_USER_DEPRECATED);
return $this->addJs($asset, $priority, $pipeline, 'async', $group);
}
/**
* Convenience wrapper for deferred loading of JavaScript
*
* @param $asset
* @param int $priority
* @param bool $pipeline
* @param string $group name of the group
*
* @deprecated Please use dynamic method with ['loading' => 'defer']
* @param string|array $asset
* @param int $priority
* @param bool $pipeline
* @param string $group name of the group
*
* @return \Grav\Common\Assets
* @deprecated Please use dynamic method with ['loading' => 'defer'].
*/
public function addDeferJs($asset, $priority = 10, $pipeline = true, $group = 'head')
{
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.6, use dynamic method with [\'loading\' => \'defer\']', E_USER_DEPRECATED);
return $this->addJs($asset, $priority, $pipeline, 'defer', $group);
}

View File

@@ -16,7 +16,7 @@ trait TestingAssetsTrait
/**
* Determines if an asset exists as a collection, CSS or JS reference
*
* @param $asset
* @param string $asset
*
* @return bool
*/
@@ -38,7 +38,7 @@ trait TestingAssetsTrait
/**
* Set the array of collections explicitly
*
* @param $collections
* @param array $collections
*
* @return $this
*/
@@ -90,7 +90,7 @@ trait TestingAssetsTrait
/**
* Set the whole array of CSS assets
*
* @param $css
* @param array $css
*
* @return $this
*/
@@ -104,7 +104,7 @@ trait TestingAssetsTrait
/**
* Set the whole array of JS assets
*
* @param $js
* @param array $js
*
* @return $this
*/
@@ -152,7 +152,7 @@ trait TestingAssetsTrait
/**
* Sets the state of CSS Pipeline
*
* @param boolean $value
* @param bool $value
*
* @return $this
*/
@@ -166,7 +166,7 @@ trait TestingAssetsTrait
/**
* Sets the state of JS Pipeline
*
* @param boolean $value
* @param bool $value
*
* @return $this
*/
@@ -219,7 +219,7 @@ trait TestingAssetsTrait
/**
* Explicitly set's a timestamp for assets
*
* @param $value
* @param string|int $value
*/
public function setTimestamp($value)
{

View File

@@ -18,6 +18,7 @@ use Grav\Common\Utils;
use Grav\Common\Grav;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\Event\EventDispatcher;
use RocketTheme\Toolbox\File\JsonFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Backups
@@ -194,7 +195,7 @@ class Backups
}
// Log the backup
Grav::instance()['log']->error('Backup Created: ' . $destination);
Grav::instance()['log']->notice('Backup Created: ' . $destination);
// Fire Finished event
Grav::instance()->fireEvent('onBackupFinished', new Event(['backup' => $destination]));
@@ -202,6 +203,14 @@ class Backups
// Purge anything required
static::purge();
// Log
$log = JsonFile::instance(Grav::instance()['locator']->findResource("log://backup.log", true, true));
$log->content([
'time' => time(),
'location' => $destination
]);
$log->save();
return $destination;
}

View File

@@ -13,6 +13,7 @@ use \Doctrine\Common\Cache as DoctrineCache;
use Grav\Common\Config\Config;
use Grav\Common\Filesystem\Folder;
use Grav\Common\Scheduler\Scheduler;
use Psr\SimpleCache\CacheInterface;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\Event\EventDispatcher;
@@ -21,8 +22,6 @@ use RocketTheme\Toolbox\Event\EventDispatcher;
* It uses DoctrineCache library and supports a variety of caching mechanisms. Those include:
*
* APCu
* APC
* XCache
* RedisCache
* MemCache
* MemCacheD
@@ -46,6 +45,11 @@ class Cache extends Getters
*/
protected $driver;
/**
* @var CacheInterface
*/
protected $simpleCache;
protected $driver_name;
protected $driver_setting;
@@ -142,6 +146,23 @@ class Cache extends Getters
$dispatcher->addListener('onSchedulerInitialized', [$this, 'onSchedulerInitialized']);
}
/**
* @return CacheInterface
*/
public function getSimpleCache()
{
if (null === $this->simpleCache) {
$cache = new \Grav\Framework\Cache\Adapter\DoctrineCache($this->driver, '', $this->getLifetime());
// Disable cache key validation.
$cache->setValidation(false);
$this->simpleCache = $cache;
}
return $this->simpleCache;
}
/**
* Deletes the old out of date file-based caches
*
@@ -169,7 +190,7 @@ class Cache extends Getters
/**
* Public accessor to set the enabled state of the cache
*
* @param $enabled
* @param bool|int $enabled
*/
public function setEnabled($enabled)
{
@@ -237,40 +258,52 @@ class Cache extends Getters
break;
case 'memcache':
$memcache = new \Memcache();
$memcache->connect($this->config->get('system.cache.memcache.server', 'localhost'),
$this->config->get('system.cache.memcache.port', 11211));
$driver = new DoctrineCache\MemcacheCache();
$driver->setMemcache($memcache);
if (extension_loaded('memcache')) {
$memcache = new \Memcache();
$memcache->connect($this->config->get('system.cache.memcache.server', 'localhost'),
$this->config->get('system.cache.memcache.port', 11211));
$driver = new DoctrineCache\MemcacheCache();
$driver->setMemcache($memcache);
} else {
throw new \LogicException('Memcache PHP extension has not been installed');
}
break;
case 'memcached':
$memcached = new \Memcached();
$memcached->addServer($this->config->get('system.cache.memcached.server', 'localhost'),
$this->config->get('system.cache.memcached.port', 11211));
$driver = new DoctrineCache\MemcachedCache();
$driver->setMemcached($memcached);
if (extension_loaded('memcached')) {
$memcached = new \Memcached();
$memcached->addServer($this->config->get('system.cache.memcached.server', 'localhost'),
$this->config->get('system.cache.memcached.port', 11211));
$driver = new DoctrineCache\MemcachedCache();
$driver->setMemcached($memcached);
} else {
throw new \LogicException('Memcached PHP extension has not been installed');
}
break;
case 'redis':
$redis = new \Redis();
$socket = $this->config->get('system.cache.redis.socket', false);
$password = $this->config->get('system.cache.redis.password', false);
if (extension_loaded('redis')) {
$redis = new \Redis();
$socket = $this->config->get('system.cache.redis.socket', false);
$password = $this->config->get('system.cache.redis.password', false);
if ($socket) {
$redis->connect($socket);
if ($socket) {
$redis->connect($socket);
} else {
$redis->connect($this->config->get('system.cache.redis.server', 'localhost'),
$this->config->get('system.cache.redis.port', 6379));
}
// Authenticate with password if set
if ($password && !$redis->auth($password)) {
throw new \RedisException('Redis authentication failed');
}
$driver = new DoctrineCache\RedisCache();
$driver->setRedis($redis);
} else {
$redis->connect($this->config->get('system.cache.redis.server', 'localhost'),
$this->config->get('system.cache.redis.port', 6379));
throw new \LogicException('Redis PHP extension has not been installed');
}
// Authenticate with password if set
if ($password && !$redis->auth($password)) {
throw new \RedisException('Redis authentication failed');
}
$driver = new DoctrineCache\RedisCache();
$driver->setRedis($redis);
break;
default:
@@ -551,7 +584,7 @@ class Cache extends Getters
/**
* is this driver a volatile driver in that it resides in PHP process memory
*
* @param $setting
* @param string $setting
* @return bool
*/
public function isVolatileDriver($setting)
@@ -578,7 +611,7 @@ class Cache extends Getters
/**
* Static function to call as a scheduled Job to clear Grav cache
*
* @param $type
* @param string $type
*/
public static function clearJob($type)
{

View File

@@ -109,14 +109,13 @@ class Config extends Data
}
}
// Override the media.upload_limit based on PHP values
$upload_limit = Utils::getUploadLimit();
$this->items['system']['media']['upload_limit'] = $upload_limit > 0 ? $upload_limit : 1024*1024*1024;
// Legacy value - Override the media.upload_limit based on PHP values
$this->items['system']['media']['upload_limit'] = Utils::getUploadLimit();
}
/**
* @return mixed
* @deprecated
* @deprecated 1.5 Use Grav::instance()['languages'] instead.
*/
public function getLanguages()
{

View File

@@ -14,6 +14,22 @@ use Grav\Common\Utils;
class Languages extends Data
{
/**
* @var string|null
*/
protected $checksum;
/**
* @var string|null
*/
protected $modified;
/**
* @var string|null
*/
protected $timestamp;
public function checksum($checksum = null)
{
if ($checksum !== null) {

View File

@@ -25,6 +25,18 @@ class Blueprint extends BlueprintForm
/** @var BlueprintSchema */
protected $blueprintSchema;
/** @var array */
protected $defaults;
protected $handlers = [];
public function __clone()
{
if ($this->blueprintSchema) {
$this->blueprintSchema = clone $this->blueprintSchema;
}
}
public function setScope($scope)
{
$this->scope = $scope;
@@ -56,7 +68,60 @@ class Blueprint extends BlueprintForm
{
$this->initInternals();
return $this->blueprintSchema->getDefaults();
if (null === $this->defaults) {
$this->defaults = $this->blueprintSchema->getDefaults();
}
return $this->defaults;
}
/**
* Initialize blueprints with its dynamic fields.
*
* @return $this
*/
public function init()
{
foreach ($this->dynamic as $key => $data) {
// Locate field.
$path = explode('/', $key);
$current = &$this->items;
foreach ($path as $field) {
if (\is_object($current)) {
// Handle objects.
if (!isset($current->{$field})) {
$current->{$field} = [];
}
$current = &$current->{$field};
} else {
// Handle arrays and scalars.
if (!\is_array($current)) {
$current = [$field => []];
} elseif (!isset($current[$field])) {
$current[$field] = [];
}
$current = &$current[$field];
}
}
// Set dynamic property.
foreach ($data as $property => $call) {
$action = $call['action'];
$method = 'dynamic' . ucfirst($action);
if (isset($this->handlers[$action])) {
$callable = $this->handlers[$action];
$callable($current, $property, $call);
} elseif (method_exists($this, $method)) {
$this->{$method}($current, $property, $call);
}
}
}
return $this;
}
/**
@@ -121,15 +186,31 @@ class Blueprint extends BlueprintForm
*
* @param array $data
* @param bool $missingValuesAsNull
* @param bool $keepEmptyValues
* @return array
*/
public function filter(array $data, bool $missingValuesAsNull = false)
public function filter(array $data, bool $missingValuesAsNull = false, bool $keepEmptyValues = false)
{
$this->initInternals();
return $this->blueprintSchema->filter($data, $missingValuesAsNull);
return $this->blueprintSchema->filter($data, $missingValuesAsNull, $keepEmptyValues);
}
/**
* Flatten data by using blueprints.
*
* @param array $data
* @return array
*/
public function flattenData(array $data)
{
$this->initInternals();
return $this->blueprintSchema->flattenData($data);
}
/**
* Return blueprint data schema.
*
@@ -142,6 +223,11 @@ class Blueprint extends BlueprintForm
return $this->blueprintSchema;
}
public function addDynamicHandler(string $name, callable $callable): void
{
$this->handlers[$name] = $callable;
}
/**
* Initialize validator.
*/
@@ -158,6 +244,7 @@ class Blueprint extends BlueprintForm
$this->blueprintSchema->embed('', $this->items);
$this->blueprintSchema->init();
$this->defaults = null;
}
}

View File

@@ -79,11 +79,51 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
*
* @param array $data Incoming data, for example from a form.
* @param bool $missingValuesAsNull Include missing values as nulls.
* @param bool $keepEmptyValues Include empty values.
* @return array
*/
public function filter(array $data, $missingValuesAsNull = false)
public function filter(array $data, $missingValuesAsNull = false, $keepEmptyValues = false)
{
return $this->filterArray($data, $this->nested, $missingValuesAsNull);
return $this->filterArray($data, $this->nested, $missingValuesAsNull, $keepEmptyValues);
}
/**
* Flatten data by using blueprints.
*
* @param array $data Data to be flattened.
* @return array
*/
public function flattenData(array $data)
{
return $this->flattenArray($data, $this->nested, '');
}
/**
* @param array $data
* @param array $rules
* @param string $prefix
* @return array
*/
protected function flattenArray(array $data, array $rules, string $prefix)
{
$array = [];
foreach ($data as $key => $field) {
$val = $rules[$key] ?? $rules['*'] ?? null;
$rule = is_string($val) ? $this->items[$val] : null;
if ($rule || isset($val['*'])) {
// Item has been defined in blueprints.
$array[$prefix.$key] = $field;
} elseif (is_array($field) && is_array($val)) {
// Array has been defined in blueprints.
$array += $this->flattenArray($field, $val, $prefix . $key . '.');
} else {
// Undefined/extra item.
$array[$prefix.$key] = $field;
}
}
return $array;
}
/**
@@ -124,9 +164,10 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
* @param array $data
* @param array $rules
* @param bool $missingValuesAsNull
* @param bool $keepEmptyValues
* @return array
*/
protected function filterArray(array $data, array $rules, $missingValuesAsNull)
protected function filterArray(array $data, array $rules, $missingValuesAsNull, $keepEmptyValues)
{
$results = [];
@@ -138,7 +179,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
$rule = \is_string($val) ? $this->items[$val] : null;
if (empty($rule['validate']['ignore'])) {
$results[$key] = null;
continue;
}
}
}
@@ -152,33 +193,35 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
// Item has been defined in blueprints.
if (!empty($rule['validate']['ignore'])) {
// Skip any data in the ignored field.
unset($results[$key]);
continue;
}
$field = Validation::filter($field, $rule);
} elseif (\is_array($field) && \is_array($val)) {
// Array has been defined in blueprints.
$field = $this->filterArray($field, $val, $missingValuesAsNull);
$field = $this->filterArray($field, $val, $missingValuesAsNull, $keepEmptyValues);
} elseif (isset($rules['validation']) && $rules['validation'] === 'strict') {
$field = null;
// Skip any extra data.
continue;
}
if (null !== $field && (!\is_array($field) || !empty($field))) {
if ($keepEmptyValues || (null !== $field && (!\is_array($field) || !empty($field)))) {
$results[$key] = $field;
}
}
return $results;
return $results ?: null;
}
/**
* @param array $data
* @param array|null $data
* @param array $toggles
* @param array $nested
* @return array
* @return array|null
*/
protected function processFormRecursive(array $data, array $toggles, array $nested)
protected function processFormRecursive(?array $data, array $toggles, array $nested)
{
foreach ($nested as $key => $value) {
if ($key === '') {
@@ -190,11 +233,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
}
if (is_array($value)) {
// Recursively fetch the items.
$array = $this->processFormRecursive($data[$key] ?? [], $toggles[$key] ?? [], $value);
if (!empty($array)) {
$data[$key] = $array;
}
$data[$key] = $this->processFormRecursive($data[$key] ?? null, $toggles[$key] ?? [], $value);
} else {
$field = $this->get($value);
// Do not add the field if:

View File

@@ -26,7 +26,7 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
/** @var array */
protected $items;
/** @var Blueprints */
/** @var Blueprint */
protected $blueprints;
/** @var File */
@@ -197,13 +197,15 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
}
/**
* @param bool $missingValuesAsNull
* @return $this
* Filter all items by using blueprints.
*/
public function filter(bool $missingValuesAsNull = false)
public function filter()
{
$this->items = $this->blueprints()->filter($this->items, $missingValuesAsNull);
$args = func_get_args();
$missingValuesAsNull = (bool)(array_shift($args) ?: false);
$keepEmptyValues = (bool)(array_shift($args) ?: false);
$this->items = $this->blueprints()->filter($this->items, $missingValuesAsNull, $keepEmptyValues);
return $this;
}

View File

@@ -18,7 +18,7 @@ class Validation
/**
* Validate value against a blueprint field definition.
*
* @param $value
* @param mixed $value
* @param array $field
* @return array
*/

View File

@@ -10,6 +10,7 @@
namespace Grav\Common;
use DebugBar\DataCollector\ConfigCollector;
use DebugBar\DataCollector\DataCollectorInterface;
use DebugBar\DataCollector\ExceptionsCollector;
use DebugBar\DataCollector\MemoryCollector;
use DebugBar\DataCollector\MessagesCollector;
@@ -20,6 +21,9 @@ use DebugBar\DebugBar;
use DebugBar\JavascriptRenderer;
use DebugBar\StandardDebugBar;
use Grav\Common\Config\Config;
use Grav\Common\Processors\ProcessorInterface;
use Twig\Template;
use Twig\TemplateWrapper;
class Debugger
{
@@ -43,7 +47,7 @@ class Debugger
/** @var array */
protected $timers = [];
/** @var string[] $deprecations */
/** @var array $deprecations */
protected $deprecations = [];
/** @var callable */
@@ -95,7 +99,7 @@ class Debugger
$this->config = $this->grav['config'];
// Enable/disable debugger based on configuration.
$this->enabled = $this->config->get('system.debugger.enabled');
$this->enabled = (bool)$this->config->get('system.debugger.enabled');
if ($this->enabled()) {
$this->initialized = true;
@@ -120,12 +124,12 @@ class Debugger
*
* @param bool $state If null, the method returns the enabled value. If set, the method sets the enabled state
*
* @return null
* @return bool
*/
public function enabled($state = null)
{
if ($state !== null) {
$this->enabled = $state;
$this->enabled = (bool)$state;
}
return $this->enabled;
@@ -181,7 +185,7 @@ class Debugger
/**
* Adds a data collector
*
* @param $collector
* @param DataCollectorInterface $collector
*
* @return $this
* @throws \DebugBar\DebugBarException
@@ -196,9 +200,9 @@ class Debugger
/**
* Returns a data collector
*
* @param $collector
* @param DataCollectorInterface $collector
*
* @return \DebugBar\DataCollector\DataCollectorInterface
* @return DataCollectorInterface
* @throws \DebugBar\DebugBarException
*/
public function getCollector($collector)
@@ -297,7 +301,7 @@ class Debugger
/**
* Dump variables into the Messages tab of the Debug Bar
*
* @param $message
* @param mixed $message
* @param string $label
* @param bool $isString
*
@@ -355,57 +359,183 @@ class Debugger
return true;
}
$backtrace = debug_backtrace(false);
// Figure out error scope from the error.
$scope = 'unknown';
if (stripos($errstr, 'grav') !== false) {
$scope = 'grav';
} elseif (strpos($errfile, '/twig/') !== false) {
$scope = 'twig';
} elseif (stripos($errfile, '/yaml/') !== false) {
$scope = 'yaml';
} elseif (strpos($errfile, '/vendor/') !== false) {
$scope = 'vendor';
}
// Clean up backtrace to make it more useful.
$backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT);
// Skip current call.
array_shift($backtrace);
// Find yaml file where the error happened.
if ($scope === 'yaml') {
foreach ($backtrace as $current) {
if (isset($current['args'])) {
foreach ($current['args'] as $arg) {
if ($arg instanceof \SplFileInfo) {
$arg = $arg->getPathname();
}
if (\is_string($arg) && preg_match('/.+\.(yaml|md)$/i', $arg)) {
$errfile = $arg;
$errline = 0;
break 2;
}
}
}
}
}
// Filter arguments.
$cut = 0;
$previous = null;
foreach ($backtrace as $i => &$current) {
if (isset($current['args'])) {
$args = [];
foreach ($current['args'] as $arg) {
if (\is_string($arg)) {
$arg = "'" . $arg . "'";
if (mb_strlen($arg) > 100) {
$arg = 'string';
}
} elseif (\is_bool($arg)) {
$arg = $arg ? 'true' : 'false';
} elseif (\is_scalar($arg)) {
$arg = $arg;
} elseif (\is_object($arg)) {
$arg = get_class($arg) . ' $object';
} elseif (\is_array($arg)) {
$arg = '$array';
} else {
$arg = '$object';
}
$args[] = $arg;
}
$current['args'] = $args;
}
$object = $current['object'] ?? null;
unset($current['object']);
$reflection = null;
if ($object instanceof TemplateWrapper) {
$reflection = new \ReflectionObject($object);
$property = $reflection->getProperty('template');
$property->setAccessible(true);
$object = $property->getValue($object);
}
if ($object instanceof Template) {
$file = $current['file'] ?? null;
if (preg_match('`(Template.php|TemplateWrapper.php)$`', $file)) {
$current = null;
continue;
}
$debugInfo = $object->getDebugInfo();
$line = 1;
if (!$reflection) {
foreach ($debugInfo as $codeLine => $templateLine) {
if ($codeLine <= $current['line']) {
$line = $templateLine;
break;
}
}
}
$src = $object->getSourceContext();
//$code = preg_split('/\r\n|\r|\n/', $src->getCode());
//$current['twig']['twig'] = trim($code[$line - 1]);
$current['twig']['file'] = $src->getPath();
$current['twig']['line'] = $line;
$prevFile = $previous['file'] ?? null;
if ($prevFile && $file === $prevFile) {
$prevLine = $previous['line'];
$line = 1;
foreach ($debugInfo as $codeLine => $templateLine) {
if ($codeLine <= $prevLine) {
$line = $templateLine;
break;
}
}
//$previous['twig']['twig'] = trim($code[$line - 1]);
$previous['twig']['file'] = $src->getPath();
$previous['twig']['line'] = $line;
}
$cut = $i;
} elseif ($object instanceof ProcessorInterface) {
$cut = $cut ?: $i;
break;
}
$previous = &$backtrace[$i];
}
unset($current);
if ($cut) {
$backtrace = array_slice($backtrace, 0, $cut + 1);
}
$backtrace = array_values(array_filter($backtrace));
// Skip vendor libraries and the method where error was triggered.
while ($current = array_shift($backtrace)) {
if (isset($current['file']) && strpos($current['file'], 'vendor') !== false) {
foreach ($backtrace as $i => $current) {
if (!isset($current['file'])) {
continue;
}
if (strpos($current['file'], '/vendor/') !== false) {
$cut = $i + 1;
continue;
}
if (isset($current['function']) && ($current['function'] === 'user_error' || $current['function'] === 'trigger_error')) {
$current = array_shift($backtrace);
$cut = $i + 1;
continue;
}
break;
}
// Add back last call.
array_unshift($backtrace, $current);
// Filter arguments.
foreach ($backtrace as &$current) {
if (isset($current['args'])) {
$args = [];
foreach ($current['args'] as $arg) {
if (\is_string($arg)) {
$args[] = "'" . $arg . "'";
} elseif (\is_bool($arg)) {
$args[] = $arg ? 'true' : 'false';
} elseif (\is_scalar($arg)) {
$args[] = $arg;
} elseif (\is_object($arg)) {
$args[] = get_class($arg) . ' $object';
} elseif (\is_array($arg)) {
$args[] = '$array';
} else {
$args[] = '$object';
}
}
$current['args'] = $args;
}
if ($cut) {
$backtrace = array_slice($backtrace, $cut);
}
unset($current);
$backtrace = array_values(array_filter($backtrace));
$this->deprecations[] = [
$current = reset($backtrace);
// If the issue happened inside twig file, change the file and line to match that file.
$file = $current['twig']['file'] ?? '';
if ($file) {
$errfile = $file;
$errline = $current['twig']['line'] ?? 0;
}
$deprecation = [
'scope' => $scope,
'message' => $errstr,
'file' => $errfile,
'line' => $errline,
'trace' => $backtrace,
'count' => 1
];
$this->deprecations[] = $deprecation;
// Do not pass forward.
return true;
}
@@ -430,38 +560,37 @@ class Debugger
protected function getDepracatedMessage($deprecated)
{
$scope = 'unknown';
if (stripos($deprecated['message'], 'grav') !== false) {
$scope = 'grav';
} elseif (!isset($deprecated['file'])) {
$scope = 'unknown';
} elseif (stripos($deprecated['file'], 'twig') !== false) {
$scope = 'twig';
} elseif (stripos($deprecated['file'], 'yaml') !== false) {
$scope = 'yaml';
} elseif (stripos($deprecated['file'], 'vendor') !== false) {
$scope = 'vendor';
}
$scope = $deprecated['scope'];
$trace = [];
foreach ($deprecated['trace'] as $current) {
$class = $current['class'] ?? '';
$type = $current['type'] ?? '';
$function = $this->getFunction($current);
if (isset($current['file'])) {
$current['file'] = str_replace(GRAV_ROOT . '/', '', $current['file']);
if (isset($deprecated['trace'])) {
foreach ($deprecated['trace'] as $current) {
$class = $current['class'] ?? '';
$type = $current['type'] ?? '';
$function = $this->getFunction($current);
if (isset($current['file'])) {
$current['file'] = str_replace(GRAV_ROOT . '/', '', $current['file']);
}
unset($current['class'], $current['type'], $current['function'], $current['args']);
if (isset($current['twig'])) {
$trace[] = $current['twig'];
} else {
$trace[] = ['call' => $class . $type . $function] + $current;
}
}
unset($current['class'], $current['type'], $current['function'], $current['args']);
$trace[] = ['call' => $class . $type . $function] + $current;
}
$array = [
'message' => $deprecated['message'],
'file' => $deprecated['file'],
'line' => $deprecated['line'],
'trace' => $trace
];
return [
[
'message' => $deprecated['message'],
'trace' => $trace
],
array_filter($array),
$scope
];
}
@@ -472,6 +601,6 @@ class Debugger
return '';
}
return $trace['function'] . '(' . implode(', ', $trace['args']) . ')';
return $trace['function'] . '(' . implode(', ', $trace['args'] ?? []) . ')';
}
}

View File

@@ -62,7 +62,7 @@ class Errors
} catch (\Exception $e) {
echo $e;
}
}, 'log');
});
}
$whoops->register();

View File

@@ -59,7 +59,7 @@ class SimplePageHandler extends Handler
}
/**
* @param $resource
* @param string $resource
*
* @return string
* @throws \RuntimeException

View File

@@ -9,6 +9,8 @@
namespace Grav\Common\Filesystem;
use Grav\Common\Utils;
abstract class Archiver
{
protected $options = [
@@ -35,6 +37,11 @@ abstract class Archiver
public function setOptions($options)
{
// Set infinite PHP execution time if possible.
if (function_exists('set_time_limit') && !Utils::isFunctionDisabled('set_time_limit')) {
set_time_limit(0);
}
$this->options = $options + $this->options;
return $this;
}

View File

@@ -49,7 +49,7 @@ abstract class Folder
/**
* Recursively find the last modified time under given path by file.
*
* @param string $path
* @param string $path
* @param string $extensions which files to search for specifically
*
* @return int
@@ -87,7 +87,7 @@ abstract class Folder
/**
* Recursively md5 hash all files in a path
*
* @param $path
* @param string $path
* @return string
*/
public static function hashAllFiles($path)
@@ -418,23 +418,27 @@ abstract class Folder
*/
public static function create($folder)
{
if (is_dir($folder)) {
// Silence error for open_basedir; should fail in mkdir instead.
if (@is_dir($folder)) {
return;
}
$success = @mkdir($folder, 0777, true);
if (!$success) {
$error = error_get_last();
throw new \RuntimeException($error['message']);
// Take yet another look, make sure that the folder doesn't exist.
clearstatcache(true, $folder);
if (!@is_dir($folder)) {
throw new \RuntimeException(sprintf('Unable to create directory: %s', $folder));
}
}
}
/**
* Recursive copy of one directory to another
*
* @param $src
* @param $dest
* @param string $src
* @param string $dest
*
* @return bool
* @throws \RuntimeException
@@ -450,7 +454,7 @@ abstract class Folder
// If the destination directory does not exist create it
if (!is_dir($dest)) {
static::mkdir($dest);
static::create($dest);
}
// Open the source directory to read in files

View File

@@ -39,7 +39,7 @@ class RecursiveDirectoryFilterIterator extends \RecursiveFilterIterator
*/
public function accept()
{
/** @var $file \SplFileInfo */
/** @var \SplFileInfo $file */
$file = $this->current();
$filename = $file->getFilename();
$relative_filename = str_replace($this::$root . '/', '', $file->getPathname());
@@ -57,7 +57,11 @@ class RecursiveDirectoryFilterIterator extends \RecursiveFilterIterator
return false;
}
public function getChildren() {
return new self($this->getInnerIterator()->getChildren(), $this::$root, $this::$ignore_folders, $this::$ignore_files);
public function getChildren()
{
/** @var RecursiveDirectoryFilterIterator $iterator */
$iterator = $this->getInnerIterator();
return new self($iterator->getChildren(), $this::$root, $this::$ignore_folders, $this::$ignore_files);
}
}

View File

@@ -39,7 +39,7 @@ class RecursiveFolderFilterIterator extends \RecursiveFilterIterator
*/
public function accept()
{
/** @var $current \SplFileInfo */
/** @var \SplFileInfo $current */
$current = $this->current();
return $current->isDir() && !in_array($current->getFilename(), $this::$ignore_folders, true);

View File

@@ -18,7 +18,7 @@ class ZipArchiver extends Archiver
$archive = $zip->open($this->archive_file);
if ($archive === true) {
Folder::mkdir($destination);
Folder::create($destination);
if (!$zip->extractTo($destination)) {
throw new \RuntimeException('ZipArchiver: ZIP failed to extract ' . $this->archive_file . ' to ' . $destination);

View File

@@ -60,7 +60,7 @@ class FormFlash extends \Grav\Framework\Form\FormFlash
/**
* @return array
* @deprecated 1.6 For backwards compatibility only, do not use.
* @deprecated 1.6 For backwards compatibility only, do not use
*/
public function getLegacyFiles(): array
{
@@ -85,7 +85,7 @@ class FormFlash extends \Grav\Framework\Form\FormFlash
* @param string $filename
* @param array $upload
* @return bool
* @deprecated 1.6 For backwards compatibility only, do not use.
* @deprecated 1.6 For backwards compatibility only, do not use
*/
public function uploadFile(string $field, string $filename, array $upload): bool
{
@@ -114,7 +114,7 @@ class FormFlash extends \Grav\Framework\Form\FormFlash
* @param array $upload
* @param array $crop
* @return bool
* @deprecated 1.6 For backwards compatibility only, do not use.
* @deprecated 1.6 For backwards compatibility only, do not use
*/
public function cropFile(string $field, string $filename, array $upload, array $crop): bool
{

View File

@@ -17,6 +17,7 @@ class CachedCollection extends Iterator {
public function __construct($items)
{
parent::__construct();
// local cache to speed things up
if (!isset(self::$cache[get_called_class() . __METHOD__])) {
self::$cache[get_called_class() . __METHOD__] = $items;

View File

@@ -13,6 +13,9 @@ use Grav\Common\Data\Data;
class Package {
/**
* @var Data
*/
protected $data;
public function __construct(Data $package, $type = null)
@@ -24,6 +27,9 @@ class Package {
}
}
/**
* @return Data
*/
public function getData()
{
return $this->data;
@@ -54,6 +60,9 @@ class Package {
return $this->data->toJson();
}
/**
* @return array
*/
public function toArray()
{
return $this->data->toArray();

View File

@@ -37,7 +37,7 @@ class GPM extends Iterator
/**
* Internal cache
* @var
* @var array
*/
protected $cache;
@@ -49,11 +49,13 @@ class GPM extends Iterator
/**
* Creates a new GPM instance with Local and Remote packages available
* @param boolean $refresh Applies to Remote Packages only and forces a refetch of data
* @param bool $refresh Applies to Remote Packages only and forces a refetch of data
* @param callable $callback Either a function or callback in array notation
*/
public function __construct($refresh = false, $callback = null)
{
parent::__construct();
$this->cache = [];
$this->installed = new Local\Packages();
try {
$this->repository = new Remote\Packages($refresh, $callback);
@@ -95,7 +97,7 @@ class GPM extends Iterator
/**
* Returns the amount of locally installed packages
* @return integer Amount of installed packages
* @return int Amount of installed packages
*/
public function countInstalled()
{
@@ -145,7 +147,7 @@ class GPM extends Iterator
/**
* Checks if a Plugin is installed
* @param string $slug The slug of the Plugin
* @return boolean True if the Plugin has been installed. False otherwise
* @return bool True if the Plugin has been installed. False otherwise
*/
public function isPluginInstalled($slug)
{
@@ -179,7 +181,7 @@ class GPM extends Iterator
/**
* Checks if a Theme is installed
* @param string $slug The slug of the Theme
* @return boolean True if the Theme has been installed. False otherwise
* @return bool True if the Theme has been installed. False otherwise
*/
public function isThemeInstalled($slug)
{
@@ -188,7 +190,7 @@ class GPM extends Iterator
/**
* Returns the amount of updates available
* @return integer Amount of available updates
* @return int Amount of available updates
*/
public function countUpdates()
{
@@ -250,7 +252,7 @@ class GPM extends Iterator
$repository[$slug]->available = $remote_version;
$repository[$slug]->version = $local_version;
$repository[$slug]->type = $repository[$slug]->release_type;
$items[$slug] = $repository[$slug]->toArray();
$items[$slug] = $repository[$slug];
}
}
@@ -262,7 +264,7 @@ class GPM extends Iterator
/**
* Get the latest release of a package from the GPM
*
* @param $package_name
* @param string $package_name
*
* @return string|null
*/
@@ -285,7 +287,7 @@ class GPM extends Iterator
/**
* Check if a Plugin or Theme is updatable
* @param string $slug The slug of the package
* @return boolean True if updatable. False otherwise or if not found
* @return bool True if updatable. False otherwise or if not found
*/
public function isUpdatable($slug)
{
@@ -295,7 +297,7 @@ class GPM extends Iterator
/**
* Checks if a Plugin is updatable
* @param string $plugin The slug of the Plugin
* @return boolean True if the Plugin is updatable. False otherwise
* @return bool True if the Plugin is updatable. False otherwise
*/
public function isPluginUpdatable($plugin)
{
@@ -329,7 +331,7 @@ class GPM extends Iterator
$repository[$slug]->available = $remote_version;
$repository[$slug]->version = $local_version;
$repository[$slug]->type = $repository[$slug]->release_type;
$items[$slug] = $repository[$slug]->toArray();
$items[$slug] = $repository[$slug];
}
}
@@ -341,7 +343,7 @@ class GPM extends Iterator
/**
* Checks if a Theme is Updatable
* @param string $theme The slug of the Theme
* @return boolean True if the Theme is updatable. False otherwise
* @return bool True if the Theme is updatable. False otherwise
*/
public function isThemeUpdatable($theme)
{
@@ -351,7 +353,7 @@ class GPM extends Iterator
/**
* Get the release type of a package (stable / testing)
*
* @param $package_name
* @param string $package_name
*
* @return string|null
*/
@@ -374,9 +376,9 @@ class GPM extends Iterator
/**
* Returns true if the package latest release is stable
*
* @param $package_name
* @param string $package_name
*
* @return boolean
* @return bool
*/
public function isStableRelease($package_name)
{
@@ -386,9 +388,9 @@ class GPM extends Iterator
/**
* Returns true if the package latest release is testing
*
* @param $package_name
* @param string $package_name
*
* @return boolean
* @return bool
*/
public function isTestingRelease($package_name)
{
@@ -503,8 +505,8 @@ class GPM extends Iterator
/**
* Download the zip package via the URL
*
* @param $package_file
* @param $tmp
* @param string $package_file
* @param string $tmp
* @return null|string
*/
public static function downloadPackage($package_file, $tmp)
@@ -519,7 +521,7 @@ class GPM extends Iterator
$output = Response::get($package_file, []);
if ($output) {
Folder::mkdir($tmp);
Folder::create($tmp);
file_put_contents($tmp . DS . $filename, $output);
return $tmp . DS . $filename;
}
@@ -530,8 +532,8 @@ class GPM extends Iterator
/**
* Copy the local zip package to tmp
*
* @param $package_file
* @param $tmp
* @param string $package_file
* @param string $tmp
* @return null|string
*/
public static function copyPackage($package_file, $tmp)
@@ -540,7 +542,7 @@ class GPM extends Iterator
if (file_exists($package_file)) {
$filename = basename($package_file);
Folder::mkdir($tmp);
Folder::create($tmp);
copy(realpath($package_file), $tmp . DS . $filename);
return $tmp . DS . $filename;
}
@@ -551,7 +553,7 @@ class GPM extends Iterator
/**
* Try to guess the package type from the source files
*
* @param $source
* @param string $source
* @return bool|string
*/
public static function getPackageType($source)
@@ -596,7 +598,7 @@ class GPM extends Iterator
/**
* Try to guess the package name from the source files
*
* @param $source
* @param string $source
* @return bool|string
*/
public static function getPackageName($source)
@@ -616,7 +618,7 @@ class GPM extends Iterator
/**
* Find/Parse the blueprint file
*
* @param $source
* @param string $source
* @return array|bool
*/
public static function getBlueprints($source)
@@ -636,8 +638,8 @@ class GPM extends Iterator
/**
* Get the install path for a name and a particular type of package
*
* @param $type
* @param $name
* @param string $type
* @param string $name
* @return string
*/
public static function getInstallPath($type, $name)
@@ -742,8 +744,8 @@ class GPM extends Iterator
/**
* Get the required version of a dependency of a package
*
* @param $package_slug
* @param $dependency_slug
* @param string $package_slug
* @param string $dependency_slug
*
* @return mixed
*/
@@ -806,7 +808,7 @@ class GPM extends Iterator
/**
* Check the passed packages list can be updated
*
* @param $packages_names_list
* @param array $packages_names_list
*
* @throws \Exception
*/
@@ -1098,7 +1100,7 @@ class GPM extends Iterator
*
* Example: returns true for $version: '~2.0'
*
* @param $version
* @param string $version
*
* @return bool
*/
@@ -1112,7 +1114,7 @@ class GPM extends Iterator
*
* Example: returns true for $version: '>=2.0'
*
* @param $version
* @param string $version
*
* @return bool
*/

View File

@@ -40,12 +40,12 @@ class Installer
protected static $target;
/**
* @var integer Error Code
* @var int Error Code
*/
protected static $error = 0;
/**
* @var integer Zip Error Code
* @var int Zip Error Code
*/
protected static $error_zip = 0;
@@ -75,7 +75,7 @@ class Installer
* @param string $destination The local path to the Grav Instance
* @param array $options Options to use for installing. ie, ['install_path' => 'user/themes/antimatter']
* @param string $extracted The local path to the extacted ZIP package
* @param bool $keepExtracted True if you want to keep the original files
* @param bool $keepExtracted True if you want to keep the original files
* @return bool True if everything went fine, False otherwise.
*/
public static function install($zip, $destination, $options = [], $extracted = null, $keepExtracted = false)
@@ -166,8 +166,8 @@ class Installer
/**
* Unzip a file to somewhere
*
* @param $zip_file
* @param $destination
* @param string $zip_file
* @param string $destination
* @return bool|string
*/
public static function unZip($zip_file, $destination)
@@ -176,7 +176,7 @@ class Installer
$archive = $zip->open($zip_file);
if ($archive === true) {
Folder::mkdir($destination);
Folder::create($destination);
$unzip = $zip->extractTo($destination);
@@ -253,8 +253,8 @@ class Installer
}
/**
* @param $source_path
* @param $install_path
* @param string $source_path
* @param string $install_path
*
* @return bool
*/
@@ -270,8 +270,8 @@ class Installer
}
/**
* @param $source_path
* @param $install_path
* @param string $source_path
* @param string $install_path
*
* @return bool
*/
@@ -287,10 +287,10 @@ class Installer
}
/**
* @param $source_path
* @param $install_path
* @param $ignores
* @param $keep_source
* @param string $source_path
* @param string $install_path
* @param array $ignores
* @param bool $keep_source
*
* @return bool
*/
@@ -332,7 +332,7 @@ class Installer
* @param string $path The slug of the package(s)
* @param array $options Options to use for uninstalling
*
* @return boolean True if everything went fine, False otherwise.
* @return bool True if everything went fine, False otherwise.
*/
public static function uninstall($path, $options = [])
{
@@ -374,7 +374,7 @@ class Installer
* @param string $destination The directory to run validations at
* @param array $exclude An array of constants to exclude from the validation
*
* @return boolean True if validation passed. False otherwise
* @return bool True if validation passed. False otherwise
*/
public static function isValidDestination($destination, $exclude = [])
{
@@ -403,7 +403,7 @@ class Installer
*
* @param string $target The local path to the Grav Instance
*
* @return boolean True if is a Grav Instance. False otherwise
* @return bool True if is a Grav Instance. False otherwise
*/
public static function isGravInstance($target)
{
@@ -523,7 +523,7 @@ class Installer
/**
* Returns the last error code of the occurred error
* @return integer The code of the last error
* @return int|string The code of the last error
*/
public static function lastErrorCode()
{

View File

@@ -33,10 +33,10 @@ class Licenses
/**
* Returns the license for a Premium package
*
* @param $slug
* @param $license
* @param string $slug
* @param string $license
*
* @return boolean
* @return bool
*/
public static function set($slug, $license)
{
@@ -67,7 +67,7 @@ class Licenses
/**
* Returns the license for a Premium package
*
* @param $slug
* @param string $slug
*
* @return array|string
*/
@@ -89,7 +89,7 @@ class Licenses
/**
* Validates the License format
*
* @param $license
* @param string $license
*
* @return bool
*/

View File

@@ -15,6 +15,7 @@ abstract class AbstractPackageCollection extends BaseCollection
{
public function __construct($items)
{
parent::__construct();
foreach ($items as $name => $data) {
$data->set('slug', $name);
$this->items[$name] = new Package($data, $this->type);

View File

@@ -23,11 +23,11 @@ class Package extends BasePackage
$this->settings = $package->toArray();
$html_description = \Parsedown::instance()->line($this->description);
$this->data->set('slug', $package->slug);
$html_description = \Parsedown::instance()->line($this->__get('description'));
$this->data->set('slug', $package->__get('slug'));
$this->data->set('description_html', $html_description);
$this->data->set('description_plain', strip_tags($html_description));
$this->data->set('symlink', is_link(USER_DIR . $package_type . DS . $this->slug));
$this->data->set('symlink', is_link(USER_DIR . $package_type . DS . $this->__get('slug')));
}
/**

View File

@@ -24,7 +24,7 @@ class AbstractPackageCollection extends BaseCollection
/**
* The lifetime to store the entry in seconds
* @var integer
* @var int
*/
private $lifetime = 86400;
@@ -41,6 +41,7 @@ class AbstractPackageCollection extends BaseCollection
*/
public function __construct($repository = null, $refresh = false, $callback = null)
{
parent::__construct();
if ($repository === null) {
throw new \RuntimeException("A repository is required to indicate the origin of the remote collection");
}

View File

@@ -73,7 +73,7 @@ class GravCore extends AbstractPackageCollection
$diffLog = [];
foreach ((array)$this->data['changelog'] as $version => $changelog) {
preg_match("/[\w-\.]+/", $version, $cleanVersion);
preg_match("/[\w\-\.]+/", $version, $cleanVersion);
if (!$cleanVersion || version_compare($diff, $cleanVersion[0], '>=')) {
continue;

View File

@@ -12,11 +12,16 @@ namespace Grav\Common\GPM\Remote;
use Grav\Common\Data\Data;
use Grav\Common\GPM\Common\Package as BasePackage;
class Package extends BasePackage
class Package extends BasePackage implements \JsonSerializable
{
public function __construct($package, $package_type = null)
{
$data = new Data($package);
parent::__construct($data, $package_type);
}
public function jsonSerialize()
{
return $this->data;
}
}

View File

@@ -183,7 +183,7 @@ class Response
/**
* Checks if cURL is available
*
* @return boolean
* @return bool
*/
public static function isCurlAvailable()
{
@@ -193,7 +193,7 @@ class Response
/**
* Checks if the remote fopen request is enabled in PHP
*
* @return boolean
* @return bool
*/
public static function isFopenAvailable()
{
@@ -203,7 +203,7 @@ class Response
/**
* Is this a remote file or not
*
* @param $file
* @param string $file
* @return bool
*/
public static function isRemote($file)
@@ -302,8 +302,9 @@ class Response
if ($content === false) {
$code = null;
// Function file_get_contents() creates local variable $http_response_header, check it
if (isset($http_response_header)) {
$code = explode(' ', $http_response_header[0])[1];
$code = explode(' ', $http_response_header[0] ?? '')[1] ?? null;
}
switch ($code) {
@@ -358,9 +359,9 @@ class Response
}
/**
* @param $ch
* @param $options
* @param $callback
* @param resource $ch
* @param array $options
* @param bool $callback
*
* @return bool|mixed
*/

View File

@@ -11,14 +11,14 @@ namespace Grav\Common;
use Grav\Common\Config\Config;
use Grav\Common\Config\Setup;
use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Page\Medium\ImageMedium;
use Grav\Common\Page\Medium\Medium;
use Grav\Common\Page\Page;
use Grav\Common\Processors\AssetsProcessor;
use Grav\Common\Processors\BackupsProcessor;
use Grav\Common\Processors\ConfigurationProcessor;
use Grav\Common\Processors\DebuggerAssetsProcessor;
use Grav\Common\Processors\DebuggerInitProcessor;
use Grav\Common\Processors\DebuggerProcessor;
use Grav\Common\Processors\ErrorsProcessor;
use Grav\Common\Processors\InitializeProcessor;
use Grav\Common\Processors\LoggerProcessor;
@@ -38,6 +38,11 @@ use Psr\Http\Message\ServerRequestInterface;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\Event\EventDispatcher;
/**
* Grav container is the heart of Grav.
*
* @package Grav\Common
*/
class Grav extends Container
{
/**
@@ -55,28 +60,24 @@ class Grav extends Container
* to the dependency injection container.
*/
protected static $diMap = [
'Grav\Common\Service\AccountsServiceProvider',
'Grav\Common\Service\AssetsServiceProvider',
'Grav\Common\Service\BackupsServiceProvider',
'Grav\Common\Service\ConfigServiceProvider',
'Grav\Common\Service\ErrorServiceProvider',
'Grav\Common\Service\FilesystemServiceProvider',
'Grav\Common\Service\InflectorServiceProvider',
'Grav\Common\Service\LoggerServiceProvider',
'Grav\Common\Service\OutputServiceProvider',
'Grav\Common\Service\PageServiceProvider',
'Grav\Common\Service\PagesServiceProvider',
'Grav\Common\Service\RequestServiceProvider',
'Grav\Common\Service\SessionServiceProvider',
'Grav\Common\Service\SetupServiceProvider',
'Grav\Common\Service\StreamsServiceProvider',
'Grav\Common\Service\TaskServiceProvider',
'Grav\Common\Service\UserServiceProvider',
'browser' => 'Grav\Common\Browser',
'cache' => 'Grav\Common\Cache',
'events' => 'RocketTheme\Toolbox\Event\EventDispatcher',
'exif' => 'Grav\Common\Helpers\Exif',
'filesystem' => 'Grav\Framework\Filesystem\Filesystem',
'inflector' => 'Grav\Common\Inflector',
'language' => 'Grav\Common\Language\Language',
'pages' => 'Grav\Common\Page\Pages',
'plugins' => 'Grav\Common\Plugins',
'scheduler' => 'Grav\Common\Scheduler\Scheduler',
'taxonomy' => 'Grav\Common\Taxonomy',
@@ -92,7 +93,7 @@ class Grav extends Container
'configurationProcessor',
'loggerProcessor',
'errorsProcessor',
'debuggerInitProcessor',
'debuggerProcessor',
'initializeProcessor',
'pluginsProcessor',
'themesProcessor',
@@ -194,8 +195,8 @@ class Grav extends Container
'errorsProcessor' => function () {
return new ErrorsProcessor($this);
},
'debuggerInitProcessor' => function () {
return new DebuggerInitProcessor($this);
'debuggerProcessor' => function () {
return new DebuggerProcessor($this);
},
'initializeProcessor' => function () {
return new InitializeProcessor($this);
@@ -335,7 +336,7 @@ class Grav extends Container
public function header(ResponseInterface $response = null)
{
if (null === $response) {
/** @var Page $page */
/** @var PageInterface $page */
$page = $this['page'];
$response = new Response($page->httpResponseCode(), $page->httpHeaders(), '');
}
@@ -470,7 +471,7 @@ class Grav extends Container
};
$container->measureTime('_services', 'Services', function () use ($container) {
$container->registerServices($container);
$container->registerServices();
});
return $container;
@@ -500,7 +501,7 @@ class Grav extends Container
/**
* This attempts to find media, other files, and download them
*
* @param $path
* @param string $path
*/
public function fallbackUrl($path)
{
@@ -526,7 +527,7 @@ class Grav extends Container
$path_parts = pathinfo($path);
/** @var Page $page */
/** @var PageInterface $page */
$page = $this['pages']->dispatch($path_parts['dirname'], true);
if ($page) {

View File

@@ -10,7 +10,7 @@
namespace Grav\Common;
/**
* @deprecated 1.4 Use Grav::instance() instead
* @deprecated 1.4 Use Grav::instance() instead.
*/
trait GravTrait
{
@@ -18,15 +18,16 @@ trait GravTrait
/**
* @return Grav
* @deprecated 1.4 Use Grav::instance() instead.
*/
public static function getGrav()
{
user_error(__TRAIT__ . ' is deprecated since Grav 1.4, use Grav::instance() instead', E_USER_DEPRECATED);
if (!self::$grav) {
self::$grav = Grav::instance();
}
user_error(__TRAIT__ . ' is deprecated since Grav 1.4, use Grav::instance() instead', E_USER_DEPRECATED);
return self::$grav;
}
}

View File

@@ -28,7 +28,7 @@ class Base32
/**
* Encode in Base32
*
* @param $bytes
* @param string $bytes
* @return string
*/
public static function encode($bytes)
@@ -69,7 +69,7 @@ class Base32
/**
* Decode in Base32
*
* @param $base32
* @param string $base32
* @return string
*/
public static function decode($base32)

View File

@@ -10,7 +10,7 @@
namespace Grav\Common\Helpers;
use Grav\Common\Grav;
use Grav\Common\Page\Page;
use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Uri;
use Grav\Common\Page\Medium\Medium;
use Grav\Common\Utils;
@@ -23,10 +23,10 @@ class Excerpts
* Process Grav image media URL from HTML tag
*
* @param string $html HTML tag e.g. `<img src="image.jpg" />`
* @param Page $page The current page object
* @param PageInterface $page The current page object
* @return string Returns final HTML string
*/
public static function processImageHtml($html, Page $page)
public static function processImageHtml($html, PageInterface $page)
{
$excerpt = static::getExcerptFromHtml($html, 'img');
@@ -80,7 +80,7 @@ class Excerpts
/**
* Rebuild HTML tag from an excerpt array
*
* @param $excerpt
* @param array $excerpt
* @return string
*/
public static function getHtmlFromExcerpt($excerpt)
@@ -111,12 +111,12 @@ class Excerpts
/**
* Process a Link excerpt
*
* @param $excerpt
* @param Page $page
* @param array $excerpt
* @param PageInterface $page
* @param string $type
* @return mixed
*/
public static function processLinkExcerpt($excerpt, Page $page, $type = 'link')
public static function processLinkExcerpt($excerpt, PageInterface $page, $type = 'link')
{
$url = htmlspecialchars_decode(rawurldecode($excerpt['element']['attributes']['href']));
@@ -192,10 +192,10 @@ class Excerpts
* Process an image excerpt
*
* @param array $excerpt
* @param Page $page
* @return mixed
* @param PageInterface $page
* @return array
*/
public static function processImageExcerpt(array $excerpt, Page $page)
public static function processImageExcerpt(array $excerpt, PageInterface $page)
{
$url = htmlspecialchars_decode(urldecode($excerpt['element']['attributes']['src']));
$url_parts = static::parseUrl($url);
@@ -206,13 +206,15 @@ class Excerpts
if (!empty($url_parts['stream'])) {
$filename = $url_parts['scheme'] . '://' . ($url_parts['path'] ?? '');
$media = $page->media();
$media = $page->getMedia();
} else {
$grav = Grav::instance();
// File is also local if scheme is http(s) and host matches.
$local_file = isset($url_parts['path'])
&& (empty($url_parts['scheme']) || in_array($url_parts['scheme'], ['http', 'https'], true))
&& (empty($url_parts['host']) || $url_parts['host'] === Grav::instance()['uri']->host());
&& (empty($url_parts['host']) || $url_parts['host'] === $grav['uri']->host());
if ($local_file) {
$filename = basename($url_parts['path']);
@@ -221,18 +223,18 @@ class Excerpts
// Get the local path to page media if possible.
if ($folder === $page->url(false, false, false)) {
// Get the media objects for this page.
$media = $page->media();
$media = $page->getMedia();
} else {
// see if this is an external page to this one
$base_url = rtrim(Grav::instance()['base_url_relative'] . Grav::instance()['pages']->base(), '/');
$base_url = rtrim($grav['base_url_relative'] . $grav['pages']->base(), '/');
$page_route = '/' . ltrim(str_replace($base_url, '', $folder), '/');
/** @var Page $ext_page */
$ext_page = Grav::instance()['pages']->dispatch($page_route, true);
/** @var PageInterface $ext_page */
$ext_page = $grav['pages']->dispatch($page_route, true);
if ($ext_page) {
$media = $ext_page->media();
$media = $ext_page->getMedia();
} else {
Grav::instance()->fireEvent('onMediaLocate', new Event(['route' => $page_route, 'media' => &$media]));
$grav->fireEvent('onMediaLocate', new Event(['route' => $page_route, 'media' => &$media]));
}
}
}
@@ -266,9 +268,9 @@ class Excerpts
/**
* Process media actions
*
* @param $medium
* @param $url
* @return mixed
* @param Medium $medium
* @param string|array $url
* @return Medium
*/
public static function processMediaActions($medium, $url)
{
@@ -353,6 +355,10 @@ class Excerpts
return $url_parts;
}
/**
* @param string $url
* @return bool|string
*/
protected static function resolveStream($url)
{
/** @var UniformResourceLocator $locator */

View File

@@ -16,7 +16,7 @@ class LogViewer
/**
* Get the objects of a tailed file
*
* @param $filepath
* @param string $filepath
* @param int $lines
* @param bool $desc
* @return array
@@ -37,7 +37,7 @@ class LogViewer
/**
* Optimized way to get just the last few entries of a log file
*
* @param $filepath
* @param string $filepath
* @param int $lines
* @return bool|string
*/
@@ -82,7 +82,7 @@ class LogViewer
/**
* Helper class to get level color
*
* @param $level
* @param string $level
* @return mixed|string
*/
public static function levelColor($level)
@@ -103,7 +103,7 @@ class LogViewer
/**
* Parse a monolog row into array bits
*
* @param $line
* @param string $line
* @return array
*/
public function parse($line)
@@ -136,8 +136,8 @@ class LogViewer
/**
* Parse text of trace into an array of lines
*
* @param $trace
* @param $rows
* @param string $trace
* @param int $rows
* @return array
*/
public static function parseTrace($trace, $rows = 10)

View File

@@ -31,7 +31,7 @@ class Truncator {
/**
* Safely truncates HTML by a given number of words.
* @param string $html Input HTML.
* @param integer $limit Limit to how many words we preserve.
* @param int $limit Limit to how many words we preserve.
* @param string $ellipsis String to use as ellipsis (if any).
* @return string Safe truncated HTML.
*/
@@ -41,13 +41,12 @@ class Truncator {
return $html;
}
$dom = self::htmlToDomDocument($html);
// Grab the body of our DOM.
$body = $dom->getElementsByTagName('body')->item(0);
$doc = self::htmlToDomDocument($html);
$container = $doc->getElementsByTagName('div')->item(0);
$container = $container->parentNode->removeChild($container);
// Iterate over words.
$words = new DOMWordsIterator($body);
$words = new DOMWordsIterator($container);
$truncated = false;
foreach ($words as $word) {
@@ -66,7 +65,7 @@ class Truncator {
$words[$offset][1] + strlen($words[$offset][0])
);
self::removeProceedingNodes($curNode, $body);
self::removeProceedingNodes($curNode, $container);
if (!empty($ellipsis)) {
self::insertEllipsis($curNode, $ellipsis);
@@ -81,7 +80,7 @@ class Truncator {
// Return original HTML if not truncated.
if ($truncated) {
return self::innerHTML($body);
$html = self::getCleanedHtml($doc, $container);
}
return $html;
@@ -90,7 +89,7 @@ class Truncator {
/**
* Safely truncates HTML by a given number of letters.
* @param string $html Input HTML.
* @param integer $limit Limit to how many letters we preserve.
* @param int $limit Limit to how many letters we preserve.
* @param string $ellipsis String to use as ellipsis (if any).
* @return string Safe truncated HTML.
*/
@@ -100,13 +99,12 @@ class Truncator {
return $html;
}
$dom = self::htmlToDomDocument($html);
// Grab the body of our DOM.
$body = $dom->getElementsByTagName('body')->item(0);
$doc = self::htmlToDomDocument($html);
$container = $doc->getElementsByTagName('div')->item(0);
$container = $container->parentNode->removeChild($container);
// Iterate over letters.
$letters = new DOMLettersIterator($body);
$letters = new DOMLettersIterator($container);
$truncated = false;
foreach ($letters as $letter) {
@@ -115,7 +113,7 @@ class Truncator {
$currentText = $letters->currentTextPosition();
$currentText[0]->nodeValue = mb_substr($currentText[0]->nodeValue, 0, $currentText[1] + 1);
self::removeProceedingNodes($currentText[0], $body);
self::removeProceedingNodes($currentText[0], $container);
if (!empty($ellipsis)) {
self::insertEllipsis($currentText[0], $ellipsis);
@@ -129,7 +127,7 @@ class Truncator {
// Return original HTML if not truncated.
if ($truncated) {
return self::innerHTML($body);
$html = self::getCleanedHtml($doc, $container);
}
return $html;
@@ -143,7 +141,7 @@ class Truncator {
public static function htmlToDomDocument($html)
{
if (!$html) {
$html = '<p></p>';
$html = '';
}
// Transform multibyte entities which otherwise display incorrectly.
@@ -155,7 +153,7 @@ class Truncator {
// Instantiate new DOMDocument object, and then load in UTF-8 HTML.
$dom = new DOMDocument();
$dom->encoding = 'UTF-8';
$dom->loadHTML($html);
$dom->loadHTML("<div>$html</div>");
return $dom;
}
@@ -188,6 +186,27 @@ class Truncator {
}
}
/**
* Clean extra code
*
* @param DOMDocument $doc
* @param $container
* @return string
*/
private static function getCleanedHTML(DOMDocument $doc, $container)
{
while ($doc->firstChild) {
$doc->removeChild($doc->firstChild);
}
while ($container->firstChild ) {
$doc->appendChild($container->firstChild);
}
$html = trim($doc->saveHTML());
return $html;
}
/**
* Inserts an ellipsis
* @param DOMNode|DOMElement $domNode Element to insert after.
@@ -215,21 +234,102 @@ class Truncator {
}
/**
* Returns the innerHTML of a particular DOMElement
*
* @param $element
* @return string
* @inheritDoc
*/
private static function innerHTML($element) {
$innerHTML = '';
$children = $element->childNodes;
foreach ($children as $child)
{
$tmp_dom = new DOMDocument();
$tmp_dom->appendChild($tmp_dom->importNode($child, true));
$innerHTML.=trim($tmp_dom->saveHTML());
public function truncate(
$text,
$length = 100,
$ending = '...',
$exact = false,
$considerHtml = true
) {
if ($considerHtml) {
// if the plain text is shorter than the maximum length, return the whole text
if (strlen(preg_replace('/<.*?>/', '', $text)) <= $length) {
return $text;
}
// splits all html-tags to scanable lines
preg_match_all('/(<.+?>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER);
$total_length = strlen($ending);
$open_tags = array();
$truncate = '';
foreach ($lines as $line_matchings) {
// if there is any html-tag in this line, handle it and add it (uncounted) to the output
if (!empty($line_matchings[1])) {
// if it's an "empty element" with or without xhtml-conform closing slash
if (preg_match('/^<(\s*.+?\/\s*|\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\s.+?)?)>$/is', $line_matchings[1])) {
// do nothing
// if tag is a closing tag
} else if (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $line_matchings[1], $tag_matchings)) {
// delete tag from $open_tags list
$pos = array_search($tag_matchings[1], $open_tags);
if ($pos !== false) {
unset($open_tags[$pos]);
}
// if tag is an opening tag
} else if (preg_match('/^<\s*([^\s>!]+).*?>$/s', $line_matchings[1], $tag_matchings)) {
// add tag to the beginning of $open_tags list
array_unshift($open_tags, strtolower($tag_matchings[1]));
}
// add html-tag to $truncate'd text
$truncate .= $line_matchings[1];
}
// calculate the length of the plain text part of the line; handle entities as one character
$content_length = strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|[0-9a-f]{1,6};/i', ' ', $line_matchings[2]));
if ($total_length+$content_length> $length) {
// the number of characters which are left
$left = $length - $total_length;
$entities_length = 0;
// search for html entities
if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|[0-9a-f]{1,6};/i', $line_matchings[2], $entities, PREG_OFFSET_CAPTURE)) {
// calculate the real length of all entities in the legal range
foreach ($entities[0] as $entity) {
if ($entity[1]+1-$entities_length <= $left) {
$left--;
$entities_length += strlen($entity[0]);
} else {
// no more characters left
break;
}
}
}
$truncate .= substr($line_matchings[2], 0, $left+$entities_length);
// maximum lenght is reached, so get off the loop
break;
} else {
$truncate .= $line_matchings[2];
$total_length += $content_length;
}
// if the maximum length is reached, get off the loop
if($total_length>= $length) {
break;
}
}
} else {
if (strlen($text) <= $length) {
return $text;
} else {
$truncate = substr($text, 0, $length - strlen($ending));
}
}
return $innerHTML;
// if the words shouldn't be cut in the middle...
if (!$exact) {
// ...search the last occurance of a space...
$spacepos = strrpos($truncate, ' ');
if (isset($spacepos)) {
// ...and cut the text in this position
$truncate = substr($truncate, 0, $spacepos);
}
}
// add the defined ending to the text
$truncate .= $ending;
if($considerHtml) {
// close all unclosed html-tags
foreach ($open_tags as $tag) {
$truncate .= '</' . $tag . '>';
}
}
return $truncate;
}
}

View File

@@ -278,7 +278,7 @@ class Inflector
*
* This method converts 13 to 13th, 2 to 2nd ...
*
* @param integer $number Number to get its ordinal value
* @param int $number Number to get its ordinal value
*
* @return string Ordinal representation of given string.
*/

View File

@@ -63,7 +63,7 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
/**
* Remove item from the list.
*
* @param $key
* @param string $key
*/
public function remove($key)
{

View File

@@ -11,12 +11,16 @@ namespace Grav\Common\Language;
use Grav\Common\Grav;
use Grav\Common\Config\Config;
use Negotiation\AcceptLanguage;
use Negotiation\LanguageNegotiator;
class Language
{
protected $grav;
protected $enabled = true;
/**
* @var array
*/
protected $languages = [];
protected $page_extensions = [];
protected $fallback_languages = [];
@@ -47,7 +51,13 @@ class Language
*/
public function init()
{
$this->default = reset($this->languages);
$default = $this->config->get('system.languages.default_lang');
if (isset($default) && $this->validate($default)) {
$this->default = $default;
} else {
$this->default = reset($this->languages);
}
$this->page_extensions = null;
if (empty($this->languages)) {
@@ -78,7 +88,7 @@ class Language
/**
* Sets the current supported languages manually
*
* @param $langs
* @param array $langs
*/
public function setLanguages($langs)
{
@@ -102,7 +112,7 @@ class Language
/**
* Gets language, active if set, else default
*
* @return mixed
* @return string
*/
public function getLanguage()
{
@@ -122,7 +132,7 @@ class Language
/**
* Sets default language manually
*
* @param $lang
* @param string $lang
*
* @return bool
*/
@@ -140,7 +150,7 @@ class Language
/**
* Gets current active language
*
* @return mixed
* @return string
*/
public function getActive()
{
@@ -150,9 +160,9 @@ class Language
/**
* Sets active language manually
*
* @param $lang
* @param string $lang
*
* @return bool
* @return string|bool
*/
public function setActive($lang)
{
@@ -168,9 +178,9 @@ class Language
/**
* Sets the active language based on the first part of the URL
*
* @param $uri
* @param string $uri
*
* @return mixed
* @return string
*/
public function setActiveFromUri($uri)
{
@@ -205,7 +215,7 @@ class Language
$negotiator = new LanguageNegotiator();
$best_language = $negotiator->getBest($accept, $this->languages);
if ($best_language) {
if ($best_language instanceof AcceptLanguage) {
$this->active = $best_language->getType();
} else {
$this->active = $this->getDefault();
@@ -221,7 +231,7 @@ class Language
/**
* Get's a URL prefix based on configuration
*
* @param null $lang
* @param string|null $lang
* @return string
*/
public function getLanguageURLPrefix($lang = null)
@@ -237,7 +247,7 @@ class Language
/**
* Test to see if language is default and language should be included in the URL
*
* @param null $lang
* @param string|null $lang
* @return bool
*/
public function isIncludeDefaultLanguage($lang = null)
@@ -349,7 +359,7 @@ class Language
/**
* Ensures the language is valid and supported
*
* @param $lang
* @param string $lang
*
* @return bool
*/
@@ -361,7 +371,7 @@ class Language
/**
* Translate a key and possibly arguments into a string using current lang and fallbacks
*
* @param mixed $args The first argument is the lookup key value
* @param string|array $args The first argument is the lookup key value
* Other arguments can be passed and replaced in the translation with sprintf syntax
* @param array $languages
* @param bool $array_support
@@ -414,9 +424,9 @@ class Language
/**
* Translate Array
*
* @param $key
* @param $index
* @param null $languages
* @param string $key
* @param string $index
* @param array|null $languages
* @param bool $html_out
*
* @return string
@@ -473,10 +483,9 @@ class Language
/**
* Get the browser accepted languages
*
* @deprecated 1.6.0 no longer used - using content negotiation
*
* @param array $accept_langs
* @return array
* @deprecated 1.6 No longer used - using content negotiation.
*/
public function getBrowserLanguages($accept_langs = [])
{
@@ -489,6 +498,8 @@ class Language
return $accept_langs;
}
$langs = [];
foreach (explode(',', $accept_langs) as $k => $pref) {
// split $pref again by ';q='
// and decorate the language entries by inverted position
@@ -509,7 +520,7 @@ class Language
/**
* Accessible wrapper to LanguageCodes
*
* @param $code
* @param string $code
* @param string $type
* @return bool
*/

View File

@@ -9,6 +9,8 @@
namespace Grav\Common\Markdown;
use Grav\Common\Page\Interfaces\PageInterface;
class Parsedown extends \Parsedown
{
use ParsedownGravTrait;
@@ -16,8 +18,8 @@ class Parsedown extends \Parsedown
/**
* Parsedown constructor.
*
* @param $page
* @param $defaults
* @param PageInterface $page
* @param array|null $defaults
*/
public function __construct($page, $defaults)
{

View File

@@ -9,6 +9,8 @@
namespace Grav\Common\Markdown;
use Grav\Common\Page\Interfaces\PageInterface;
class ParsedownExtra extends \ParsedownExtra
{
use ParsedownGravTrait;
@@ -16,8 +18,8 @@ class ParsedownExtra extends \ParsedownExtra
/**
* ParsedownExtra constructor.
*
* @param $page
* @param $defaults
* @param PageInterface $page
* @param array|null $defaults
* @throws \Exception
*/
public function __construct($page, $defaults)

View File

@@ -11,12 +11,12 @@ namespace Grav\Common\Markdown;
use Grav\Common\Grav;
use Grav\Common\Helpers\Excerpts;
use Grav\Common\Page\Page;
use Grav\Common\Page\Interfaces\PageInterface;
use RocketTheme\Toolbox\Event\Event;
trait ParsedownGravTrait
{
/** @var Page $page */
/** @var PageInterface $page */
protected $page;
protected $special_chars;
@@ -28,8 +28,8 @@ trait ParsedownGravTrait
/**
* Initialization function to setup key variables needed by the MarkdownGravLinkTrait
*
* @param $page
* @param $defaults
* @param PageInterface $page
* @param array|null $defaults
*/
protected function init($page, $defaults)
{
@@ -40,7 +40,7 @@ trait ParsedownGravTrait
$this->special_chars = ['>' => 'gt', '<' => 'lt', '"' => 'quot'];
if ($defaults === null) {
$defaults = Grav::instance()['config']->get('system.pages.markdown');
$defaults = (array)Grav::instance()['config']->get('system.pages.markdown');
}
$this->setBreaksEnabled($defaults['auto_line_breaks']);
@@ -48,18 +48,18 @@ trait ParsedownGravTrait
$this->setMarkupEscaped($defaults['escape_markup']);
$this->setSpecialChars($defaults['special_chars']);
$grav->fireEvent('onMarkdownInitialized', new Event(['markdown' => $this]));
$grav->fireEvent('onMarkdownInitialized', new Event(['markdown' => $this, 'page' => $page]));
}
/**
* Be able to define a new Block type or override an existing one
*
* @param $type
* @param $tag
* @param string $type
* @param string $tag
* @param bool $continuable
* @param bool $completable
* @param $index
* @param int|null $index
*/
public function addBlockType($type, $tag, $continuable = false, $completable = false, $index = null)
{
@@ -88,9 +88,9 @@ trait ParsedownGravTrait
/**
* Be able to define a new Inline type or override an existing one
*
* @param $type
* @param $tag
* @param $index
* @param string $type
* @param string $tag
* @param int|null $index
*/
public function addInlineType($type, $tag, $index = null)
{
@@ -108,7 +108,7 @@ trait ParsedownGravTrait
/**
* Overrides the default behavior to allow for plugin-provided blocks to be continuable
*
* @param $Type
* @param string $Type
*
* @return bool
*/
@@ -122,7 +122,7 @@ trait ParsedownGravTrait
/**
* Overrides the default behavior to allow for plugin-provided blocks to be completable
*
* @param $Type
* @param string $Type
*
* @return bool
*/
@@ -149,7 +149,7 @@ trait ParsedownGravTrait
/**
* Setter for special chars
*
* @param $special_chars
* @param array $special_chars
*
* @return $this
*/

View File

@@ -14,4 +14,10 @@ namespace Grav\Common\Media\Interfaces;
*/
interface MediaCollectionInterface extends \Grav\Framework\Media\Interfaces\MediaCollectionInterface
{
/**
* Return media path.
*
* @return string|null
*/
public function getPath();
}

View File

@@ -13,6 +13,7 @@ use Grav\Common\Cache;
use Grav\Common\Grav;
use Grav\Common\Media\Interfaces\MediaCollectionInterface;
use Grav\Common\Page\Media;
use Psr\SimpleCache\CacheInterface;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
trait MediaTrait
@@ -71,9 +72,9 @@ trait MediaTrait
// Use cached media if possible.
$cacheKey = md5('media' . $this->getCacheKey());
if (!$media = $cache->fetch($cacheKey)) {
if (!$media = $cache->get($cacheKey)) {
$media = new Media($this->getMediaFolder(), $this->getMediaOrder());
$cache->save($cacheKey, $media);
$cache->set($cacheKey, $media);
}
$this->media = $media;
}
@@ -91,7 +92,7 @@ trait MediaTrait
{
$cache = $this->getMediaCache();
$cacheKey = md5('media' . $this->getCacheKey());
$cache->save($cacheKey, $media);
$cache->set($cacheKey, $media);
$this->media = $media;
@@ -111,11 +112,14 @@ trait MediaTrait
}
/**
* @return Cache
* @return CacheInterface
*/
protected function getMediaCache()
{
return Grav::instance()['cache'];
/** @var Cache $cache */
$cache = Grav::instance()['cache'];
return $cache->getSimpleCache();
}
/**

View File

@@ -11,6 +11,7 @@ namespace Grav\Common\Page;
use Grav\Common\Grav;
use Grav\Common\Iterator;
use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Utils;
class Collection extends Iterator
@@ -53,11 +54,11 @@ class Collection extends Iterator
/**
* Add a single page to a collection
*
* @param Page $page
* @param PageInterface $page
*
* @return $this
*/
public function addPage(Page $page)
public function addPage(PageInterface $page)
{
$this->items[$page->path()] = ['slug' => $page->slug()];
@@ -67,8 +68,8 @@ class Collection extends Iterator
/**
* Add a page with path and slug
*
* @param $path
* @param $slug
* @param string $path
* @param string $slug
* @return $this
*/
public function add($path, $slug)
@@ -140,7 +141,7 @@ class Collection extends Iterator
/**
* Returns current page.
*
* @return Page
* @return PageInterface
*/
public function current()
{
@@ -176,7 +177,7 @@ class Collection extends Iterator
/**
* Split collection into array of smaller collections.
*
* @param $size
* @param int $size
* @return array|Collection[]
*/
public function batch($size)
@@ -194,14 +195,14 @@ class Collection extends Iterator
/**
* Remove item from the list.
*
* @param Page|string|null $key
* @param PageInterface|string|null $key
*
* @return $this
* @throws \InvalidArgumentException
*/
public function remove($key = null)
{
if ($key instanceof Page) {
if ($key instanceof PageInterface) {
$key = $key->path();
} elseif (null === $key) {
$key = (string)key($this->items);
@@ -237,7 +238,7 @@ class Collection extends Iterator
*
* @param string $path
*
* @return boolean True if item is first.
* @return bool True if item is first.
*/
public function isFirst($path)
{
@@ -249,7 +250,7 @@ class Collection extends Iterator
*
* @param string $path
*
* @return boolean True if item is last.
* @return bool True if item is last.
*/
public function isLast($path)
{
@@ -261,7 +262,7 @@ class Collection extends Iterator
*
* @param string $path
*
* @return Page The previous item.
* @return PageInterface The previous item.
*/
public function prevSibling($path)
{
@@ -273,7 +274,7 @@ class Collection extends Iterator
*
* @param string $path
*
* @return Page The next item.
* @return PageInterface The next item.
*/
public function nextSibling($path)
{
@@ -284,9 +285,9 @@ class Collection extends Iterator
* Returns the adjacent sibling based on a direction.
*
* @param string $path
* @param integer $direction either -1 or +1
* @param int $direction either -1 or +1
*
* @return Page|Collection The sibling item.
* @return PageInterface|Collection The sibling item.
*/
public function adjacentSibling($path, $direction = 1)
{
@@ -308,7 +309,7 @@ class Collection extends Iterator
*
* @param string $path the path the item
*
* @return Integer the index of the current page.
* @return int the index of the current page.
*/
public function currentPosition($path)
{
@@ -321,14 +322,14 @@ class Collection extends Iterator
* Dates can be passed in as text that strtotime() can process
* http://php.net/manual/en/function.strtotime.php
*
* @param $startDate
* @param string $startDate
* @param bool $endDate
* @param $field
* @param string|null $field
*
* @return $this
* @throws \Exception
*/
public function dateRange($startDate, $endDate = false, $field = false)
public function dateRange($startDate, $endDate = false, $field = null)
{
$start = Utils::date2timestamp($startDate);
$end = $endDate ? Utils::date2timestamp($endDate) : false;

View File

@@ -0,0 +1,267 @@
<?php
/**
* @package Grav\Common\Page
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Page\Interfaces;
use Grav\Common\Page\Media;
/**
* Methods currently implemented in Flex Page emulation layer.
*/
interface PageContentInterface
{
/**
* Gets and Sets the header based on the YAML configuration at the top of the .md file
*
* @param object|array $var a YAML object representing the configuration for the file
*
* @return object the current YAML configuration
*/
public function header($var = null);
/**
* Get the summary.
*
* @param int $size Max summary size.
*
* @param bool $textOnly Only count text size.
*
* @return string
*/
public function summary($size = null, $textOnly = false);
/**
* Gets and Sets the content based on content portion of the .md file
*
* @param string $var Content
*
* @return string Content
*/
public function content($var = null);
/**
* Needed by the onPageContentProcessed event to get the raw page content
*
* @return string the current page content
*/
public function getRawContent();
/**
* Needed by the onPageContentProcessed event to set the raw page content
*
* @param string $content
*/
public function setRawContent($content);
/**
* Gets and Sets the Page raw content
*
* @param string|null $var
*
* @return null
*/
public function rawMarkdown($var = null);
/**
* Get value from a page variable (used mostly for creating edit forms).
*
* @param string $name Variable name.
* @param mixed $default
*
* @return mixed
*/
public function value($name, $default = null);
/**
* Gets and sets the associated media as found in the page folder.
*
* @param Media $var Representation of associated media.
*
* @return Media Representation of associated media.
*/
public function media($var = null);
/**
* Gets and sets the title for this Page. If no title is set, it will use the slug() to get a name
*
* @param string $var the title of the Page
*
* @return string the title of the Page
*/
public function title($var = null);
/**
* Gets and sets the menu name for this Page. This is the text that can be used specifically for navigation.
* If no menu field is set, it will use the title()
*
* @param string $var the menu field for the page
*
* @return string the menu field for the page
*/
public function menu($var = null);
/**
* Gets and Sets whether or not this Page is visible for navigation
*
* @param bool $var true if the page is visible
*
* @return bool true if the page is visible
*/
public function visible($var = null);
/**
* Gets and Sets whether or not this Page is considered published
*
* @param bool $var true if the page is published
*
* @return bool true if the page is published
*/
public function published($var = null);
/**
* Gets and Sets the Page publish date
*
* @param string $var string representation of a date
*
* @return int unix timestamp representation of the date
*/
public function publishDate($var = null);
/**
* Gets and Sets the Page unpublish date
*
* @param string $var string representation of a date
*
* @return int|null unix timestamp representation of the date
*/
public function unpublishDate($var = null);
/**
* Gets and Sets the process setup for this Page. This is multi-dimensional array that consists of
* a simple array of arrays with the form array("markdown"=>true) for example
*
* @param array $var an Array of name value pairs where the name is the process and value is true or false
*
* @return array an Array of name value pairs where the name is the process and value is true or false
*/
public function process($var = null);
/**
* Gets and Sets the slug for the Page. The slug is used in the URL routing. If not set it uses
* the parent folder from the path
*
* @param string $var the slug, e.g. 'my-blog'
*
* @return string the slug
*/
public function slug($var = null);
/**
* Get/set order number of this page.
*
* @param int $var
*
* @return int|bool
*/
public function order($var = null);
/**
* Gets and sets the identifier for this Page object.
*
* @param string $var the identifier
*
* @return string the identifier
*/
public function id($var = null);
/**
* Gets and sets the modified timestamp.
*
* @param int $var modified unix timestamp
*
* @return int modified unix timestamp
*/
public function modified($var = null);
/**
* Gets and sets the option to show the last_modified header for the page.
*
* @param boolean $var show last_modified header
*
* @return boolean show last_modified header
*/
public function lastModified($var = null);
/**
* Get/set the folder.
*
* @param string $var Optional path
*
* @return string|null
*/
public function folder($var = null);
/**
* Gets and sets the date for this Page object. This is typically passed in via the page headers
*
* @param string $var string representation of a date
*
* @return int unix timestamp representation of the date
*/
public function date($var = null);
/**
* Gets and sets the date format for this Page object. This is typically passed in via the page headers
* using typical PHP date string structure - http://php.net/manual/en/function.date.php
*
* @param string $var string representation of a date format
*
* @return string string representation of a date format
*/
public function dateformat($var = null);
/**
* Gets and sets the taxonomy array which defines which taxonomies this page identifies itself with.
*
* @param array $var an array of taxonomies
*
* @return array an array of taxonomies
*/
public function taxonomy($var = null);
/**
* Gets the configured state of the processing method.
*
* @param string $process the process, eg "twig" or "markdown"
*
* @return bool whether or not the processing method is enabled for this Page
*/
public function shouldProcess($process);
/**
* Returns whether or not this Page object has a .md file associated with it or if its just a directory.
*
* @return bool True if its a page with a .md file associated
*/
public function isPage();
/**
* Returns whether or not this Page object is a directory or a page.
*
* @return bool True if its a directory
*/
public function isDir();
/**
* Returns whether the page exists in the filesystem.
*
* @return bool
*/
public function exists();
}

View File

@@ -9,9 +9,11 @@
namespace Grav\Common\Page\Interfaces;
use Grav\Common\Media\Interfaces\MediaInterface;
/**
* Class implements page interface.
*/
interface PageInterface
interface PageInterface extends PageContentInterface, PageRoutableInterface, PageTranslateInterface, MediaInterface, PageLegacyInterface
{
}

View File

@@ -0,0 +1,496 @@
<?php
namespace Grav\Common\Page\Interfaces;
use Exception;
use Grav\Common\Data\Blueprint;
use Grav\Common\Page\Collection;
use RocketTheme\Toolbox\File\MarkdownFile;
interface PageLegacyInterface
{
/**
* Initializes the page instance variables based on a file
*
* @param \SplFileInfo $file The file information for the .md file that the page represents
* @param string $extension
*
* @return $this
*/
public function init(\SplFileInfo $file, $extension = null);
/**
* Gets and Sets the raw data
*
* @param string $var Raw content string
*
* @return string Raw content string
*/
public function raw($var = null);
/**
* Gets and Sets the page frontmatter
*
* @param string|null $var
*
* @return string
*/
public function frontmatter($var = null);
/**
* Modify a header value directly
*
* @param string $key
* @param mixed $value
*/
public function modifyHeader($key, $value);
/**
* @return int
*/
public function httpResponseCode();
public function httpHeaders();
/**
* Sets the summary of the page
*
* @param string $summary Summary
*/
public function setSummary($summary);
/**
* Get the contentMeta array and initialize content first if it's not already
*
* @return mixed
*/
public function contentMeta();
/**
* Add an entry to the page's contentMeta array
*
* @param string $name
* @param string $value
*/
public function addContentMeta($name, $value);
/**
* Return the whole contentMeta array as it currently stands
*
* @param string|null $name
*
* @return mixed
*/
public function getContentMeta($name = null);
/**
* Sets the whole content meta array in one shot
*
* @param array $content_meta
*
* @return array
*/
public function setContentMeta($content_meta);
/**
* Fires the onPageContentProcessed event, and caches the page content using a unique ID for the page
*/
public function cachePageContent();
/**
* Get file object to the page.
*
* @return MarkdownFile|null
*/
public function file();
/**
* Save page if there's a file assigned to it.
*
* @param bool|mixed $reorder Internal use.
*/
public function save($reorder = true);
/**
* Prepare move page to new location. Moves also everything that's under the current page.
*
* You need to call $this->save() in order to perform the move.
*
* @param PageInterface $parent New parent page.
*
* @return $this
*/
public function move(PageInterface $parent);
/**
* Prepare a copy from the page. Copies also everything that's under the current page.
*
* Returns a new Page object for the copy.
* You need to call $this->save() in order to perform the move.
*
* @param PageInterface $parent New parent page.
*
* @return $this
*/
public function copy(PageInterface $parent);
/**
* Get blueprints for the page.
*
* @return Blueprint
*/
public function blueprints();
/**
* Get the blueprint name for this page. Use the blueprint form field if set
*
* @return string
*/
public function blueprintName();
/**
* Validate page header.
*
* @throws Exception
*/
public function validate();
/**
* Filter page header from illegal contents.
*/
public function filter();
/**
* Get unknown header variables.
*
* @return array
*/
public function extra();
/**
* Convert page to an array.
*
* @return array
*/
public function toArray();
/**
* Convert page to YAML encoded string.
*
* @return string
*/
public function toYaml();
/**
* Convert page to JSON encoded string.
*
* @return string
*/
public function toJson();
/**
* Returns normalized list of name => form pairs.
*
* @return array
*/
public function forms();
/**
* @param array $new
*/
public function addForms(array $new);
/**
* Gets and sets the name field. If no name field is set, it will return 'default.md'.
*
* @param string $var The name of this page.
*
* @return string The name of this page.
*/
public function name($var = null);
/**
* Returns child page type.
*
* @return string
*/
public function childType();
/**
* Gets and sets the template field. This is used to find the correct Twig template file to render.
* If no field is set, it will return the name without the .md extension
*
* @param string $var the template name
*
* @return string the template name
*/
public function template($var = null);
/**
* Allows a page to override the output render format, usually the extension provided
* in the URL. (e.g. `html`, `json`, `xml`, etc).
*
* @param null $var
*
* @return null
*/
public function templateFormat($var = null);
/**
* Gets and sets the extension field.
*
* @param null $var
*
* @return null|string
*/
public function extension($var = null);
/**
* Gets and sets the expires field. If not set will return the default
*
* @param int $var The new expires value.
*
* @return int The expires value
*/
public function expires($var = null);
/**
* Gets and sets the cache-control property. If not set it will return the default value (null)
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control for more details on valid options
*
* @param null $var
* @return null
*/
public function cacheControl($var = null);
public function ssl($var = null);
/**
* Returns the state of the debugger override etting for this page
*
* @return mixed
*/
public function debugger();
/**
* Function to merge page metadata tags and build an array of Metadata objects
* that can then be rendered in the page.
*
* @param array $var an Array of metadata values to set
*
* @return array an Array of metadata values for the page
*/
public function metadata($var = null);
/**
* Gets and sets the option to show the etag header for the page.
*
* @param bool $var show etag header
*
* @return bool show etag header
*/
public function eTag($var = null);
/**
* Gets and sets the path to the .md file for this Page object.
*
* @param string $var the file path
*
* @return string|null the file path
*/
public function filePath($var = null);
/**
* Gets the relative path to the .md file
*
* @return string The relative file path
*/
public function filePathClean();
/**
* Gets and sets the order by which any sub-pages should be sorted.
*
* @param string $var the order, either "asc" or "desc"
*
* @return string the order, either "asc" or "desc"
* @deprecated 1.6
*/
public function orderDir($var = null);
/**
* Gets and sets the order by which the sub-pages should be sorted.
*
* default - is the order based on the file system, ie 01.Home before 02.Advark
* title - is the order based on the title set in the pages
* date - is the order based on the date set in the pages
* folder - is the order based on the name of the folder with any numerics omitted
*
* @param string $var supported options include "default", "title", "date", and "folder"
*
* @return string supported options include "default", "title", "date", and "folder"
* @deprecated 1.6
*/
public function orderBy($var = null);
/**
* Gets the manual order set in the header.
*
* @param string $var supported options include "default", "title", "date", and "folder"
*
* @return array
* @deprecated 1.6
*/
public function orderManual($var = null);
/**
* Gets and sets the maxCount field which describes how many sub-pages should be displayed if the
* sub_pages header property is set for this page object.
*
* @param int $var the maximum number of sub-pages
*
* @return int the maximum number of sub-pages
* @deprecated 1.6
*/
public function maxCount($var = null);
/**
* Gets and sets the modular var that helps identify this page is a modular child
*
* @param bool $var true if modular_twig
*
* @return bool true if modular_twig
*/
public function modular($var = null);
/**
* Gets and sets the modular_twig var that helps identify this page as a modular child page that will need
* twig processing handled differently from a regular page.
*
* @param bool $var true if modular_twig
*
* @return bool true if modular_twig
*/
public function modularTwig($var = null);
/**
* Returns children of this page.
*
* @return \Grav\Common\Page\Collection
*/
public function children();
/**
* Check to see if this item is the first in an array of sub-pages.
*
* @return bool True if item is first.
*/
public function isFirst();
/**
* Check to see if this item is the last in an array of sub-pages.
*
* @return bool True if item is last
*/
public function isLast();
/**
* Gets the previous sibling based on current position.
*
* @return PageInterface the previous Page item
*/
public function prevSibling();
/**
* Gets the next sibling based on current position.
*
* @return PageInterface the next Page item
*/
public function nextSibling();
/**
* Returns the adjacent sibling based on a direction.
*
* @param int $direction either -1 or +1
*
* @return PageInterface|bool the sibling page
*/
public function adjacentSibling($direction = 1);
/**
* Helper method to return an ancestor page.
*
* @param bool $lookup Name of the parent folder
*
* @return PageInterface page you were looking for if it exists
*/
public function ancestor($lookup = null);
/**
* Helper method to return an ancestor page to inherit from. The current
* page object is returned.
*
* @param string $field Name of the parent folder
*
* @return PageInterface
*/
public function inherited($field);
/**
* Helper method to return an ancestor field only to inherit from. The
* first occurrence of an ancestor field will be returned if at all.
*
* @param string $field Name of the parent folder
*
* @return array
*/
public function inheritedField($field);
/**
* Helper method to return a page.
*
* @param string $url the url of the page
* @param bool $all
*
* @return PageInterface page you were looking for if it exists
*/
public function find($url, $all = false);
/**
* Get a collection of pages in the current context.
*
* @param string|array $params
* @param bool $pagination
*
* @return Collection
* @throws \InvalidArgumentException
*/
public function collection($params = 'content', $pagination = true);
/**
* @param string|array $value
* @param bool $only_published
* @return mixed
* @internal
*/
public function evaluate($value, $only_published = true);
/**
* Returns whether or not the current folder exists
*
* @return bool
*/
public function folderExists();
/**
* Gets the Page Unmodified (original) version of the page.
*
* @return PageInterface The original version of the page.
*/
public function getOriginal();
/**
* Gets the action.
*
* @return string The Action string.
*/
public function getAction();
}

View File

@@ -0,0 +1,188 @@
<?php
namespace Grav\Common\Page\Interfaces;
interface PageRoutableInterface
{
/**
* Returns the page extension, got from the page `url_extension` config and falls back to the
* system config `system.pages.append_url_extension`.
*
* @return string The extension of this page. For example `.html`
*/
public function urlExtension();
/**
* Gets and Sets whether or not this Page is routable, ie you can reach it
* via a URL.
* The page must be *routable* and *published*
*
* @param bool $var true if the page is routable
*
* @return bool true if the page is routable
*/
public function routable($var = null);
/**
* Gets the URL for a page - alias of url().
*
* @param bool $include_host
*
* @return string the permalink
*/
public function link($include_host = false);
/**
* Gets the URL with host information, aka Permalink.
* @return string The permalink.
*/
public function permalink();
/**
* Returns the canonical URL for a page
*
* @param bool $include_lang
*
* @return string
*/
public function canonical($include_lang = true);
/**
* Gets the url for the Page.
*
* @param bool $include_host Defaults false, but true would include http://yourhost.com
* @param bool $canonical true to return the canonical URL
* @param bool $include_lang
* @param bool $raw_route
*
* @return string The url.
*/
public function url($include_host = false, $canonical = false, $include_lang = true, $raw_route = false);
/**
* Gets the route for the page based on the route headers if available, else from
* the parents route and the current Page's slug.
*
* @param string $var Set new default route.
*
* @return string The route for the Page.
*/
public function route($var = null);
/**
* Helper method to clear the route out so it regenerates next time you use it
*/
public function unsetRouteSlug();
/**
* Gets and Sets the page raw route
*
* @param string|null $var
*
* @return string
*/
public function rawRoute($var = null);
/**
* Gets the route aliases for the page based on page headers.
*
* @param array $var list of route aliases
*
* @return array The route aliases for the Page.
*/
public function routeAliases($var = null);
/**
* Gets the canonical route for this page if its set. If provided it will use
* that value, else if it's `true` it will use the default route.
*
* @param string|null $var
*
* @return bool|string
*/
public function routeCanonical($var = null);
/**
* Gets the redirect set in the header.
*
* @param string $var redirect url
*
* @return string
*/
public function redirect($var = null);
/**
* Returns the clean path to the page file
*/
public function relativePagePath();
/**
* Gets and sets the path to the folder where the .md for this Page object resides.
* This is equivalent to the filePath but without the filename.
*
* @param string $var the path
*
* @return string|null the path
*/
public function path($var = null);
/**
* Get/set the folder.
*
* @param string $var Optional path
*
* @return string|null
*/
public function folder($var = null);
/**
* Gets and Sets the parent object for this page
*
* @param PageInterface $var the parent page object
*
* @return PageInterface|null the parent page object if it exists.
*/
public function parent(PageInterface $var = null);
/**
* Gets the top parent object for this page
*
* @return PageInterface|null the top parent page object if it exists.
*/
public function topParent();
/**
* Returns the item in the current position.
*
* @return int the index of the current page.
*/
public function currentPosition();
/**
* Returns whether or not this page is the currently active page requested via the URL.
*
* @return bool True if it is active
*/
public function active();
/**
* Returns whether or not this URI's URL contains the URL of the active page.
* Or in other words, is this page's URL in the current URL
*
* @return bool True if active child exists
*/
public function activeChild();
/**
* Returns whether or not this page is the currently configured home page.
*
* @return bool True if it is the homepage
*/
public function home();
/**
* Returns whether or not this page is the root node of the pages tree.
*
* @return bool True if it is the root
*/
public function root();
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Grav\Common\Page\Interfaces;
interface PageTranslateInterface
{
/**
* Return an array with the routes of other translated languages
*
* @param bool $onlyPublished only return published translations
*
* @return array the page translated languages
*/
public function translatedLanguages($onlyPublished = false);
/**
* Return an array listing untranslated languages available
*
* @param bool $includeUnpublished also list unpublished translations
*
* @return array the page untranslated languages
*/
public function untranslatedLanguages($includeUnpublished = false);
/**
* Get page language
*
* @param string $var
*
* @return mixed
*/
public function language($var = null);
}

View File

@@ -15,13 +15,12 @@ use Grav\Common\Page\Medium\AbstractMedia;
use Grav\Common\Page\Medium\GlobalMedia;
use Grav\Common\Page\Medium\MediumFactory;
use RocketTheme\Toolbox\File\File;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Media extends AbstractMedia
{
protected static $global;
protected $path;
protected $standard_exif = ['FileSize', 'MimeType', 'height', 'width'];
/**
@@ -31,7 +30,7 @@ class Media extends AbstractMedia
*/
public function __construct($path, array $media_order = null, $load = true)
{
$this->path = $path;
$this->setPath($path);
$this->media_order = $media_order;
$this->__wakeup();
@@ -76,16 +75,19 @@ class Media extends AbstractMedia
*/
protected function init()
{
/** @var UniformResourceLocator $locator */
$locator = Grav::instance()['locator'];
$config = Grav::instance()['config'];
$locator = Grav::instance()['locator'];
$exif_reader = isset(Grav::instance()['exif']) ? Grav::instance()['exif']->getReader() : false;
$media_types = array_keys(Grav::instance()['config']->get('media.types'));
// Handle special cases where page doesn't exist in filesystem.
if (!is_dir($this->path)) {
if (!is_dir($this->getPath())) {
return;
}
$iterator = new \FilesystemIterator($this->path, \FilesystemIterator::UNIX_PATHS | \FilesystemIterator::SKIP_DOTS);
$iterator = new \FilesystemIterator($this->getPath(), \FilesystemIterator::UNIX_PATHS | \FilesystemIterator::SKIP_DOTS);
$media = [];
@@ -159,7 +161,11 @@ class Media extends AbstractMedia
$meta_data = $meta->getData();
$meta_trimmed = array_diff_key($meta_data, array_flip($this->standard_exif));
if ($meta_trimmed) {
$file = File::instance($meta_path);
if ($locator->isStream($meta_path)) {
$file = File::instance($locator->findResource($meta_path, true, true));
} else {
$file = File::instance($meta_path);
}
$file->save(Yaml::dump($meta_trimmed));
$types['meta']['file'] = $meta_path;
}
@@ -206,12 +212,11 @@ class Media extends AbstractMedia
}
/**
* Enable accessing the media path
*
* @return mixed
* @return string
* @deprecated 1.6 Use $this->getPath() instead.
*/
public function path()
{
return $this->path;
return $this->getPath();
}
}

View File

@@ -9,23 +9,47 @@
namespace Grav\Common\Page\Medium;
use Grav\Common\Getters;
use Grav\Common\Grav;
use Grav\Common\Media\Interfaces\MediaCollectionInterface;
use Grav\Common\Media\Interfaces\MediaObjectInterface;
use Grav\Common\Page\Page;
use Grav\Common\Utils;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
use RocketTheme\Toolbox\ArrayTraits\Countable;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
abstract class AbstractMedia extends Getters implements MediaCollectionInterface
abstract class AbstractMedia implements ExportInterface, MediaCollectionInterface
{
protected $gettersVariable = 'instances';
use ArrayAccess;
use Countable;
use Iterator;
use Export;
protected $instances = [];
protected $items = [];
protected $path;
protected $images = [];
protected $videos = [];
protected $audios = [];
protected $files = [];
protected $media_order;
/**
* Return media path.
*
* @return string
*/
public function getPath()
{
return $this->path;
}
public function setPath(?string $path)
{
$this->path = $path;
}
/**
* Get medium by filename.
*
@@ -48,21 +72,6 @@ abstract class AbstractMedia extends Getters implements MediaCollectionInterface
return $this->offsetGet($filename);
}
/**
* @param mixed $offset
*
* @return mixed
*/
public function offsetGet($offset)
{
$object = parent::offsetGet($offset);
// It would be nice if previous image modification would not affect the later ones.
//$object = $object ? clone($object) : null;
return $object;
}
/**
* Set file modification timestamps (query params) for all the media files.
*
@@ -72,7 +81,7 @@ abstract class AbstractMedia extends Getters implements MediaCollectionInterface
public function setTimestamps($timestamp = null)
{
/** @var Medium $instance */
foreach ($this->instances as $instance) {
foreach ($this->items as $instance) {
$instance->setTimestamp($timestamp);
}
@@ -82,56 +91,60 @@ abstract class AbstractMedia extends Getters implements MediaCollectionInterface
/**
* Get a list of all media.
*
* @return array|MediaObjectInterface[]
* @return MediaObjectInterface[]
*/
public function all()
{
$this->instances = $this->orderMedia($this->instances);
$this->items = $this->orderMedia($this->items);
return $this->instances;
return $this->items;
}
/**
* Get a list of all image media.
*
* @return array|MediaObjectInterface[]
* @return MediaObjectInterface[]
*/
public function images()
{
$this->images = $this->orderMedia($this->images);
return $this->images;
}
/**
* Get a list of all video media.
*
* @return array|MediaObjectInterface[]
* @return MediaObjectInterface[]
*/
public function videos()
{
$this->videos = $this->orderMedia($this->videos);
return $this->videos;
}
/**
* Get a list of all audio media.
*
* @return array|MediaObjectInterface[]
* @return MediaObjectInterface[]
*/
public function audios()
{
$this->audios = $this->orderMedia($this->audios);
return $this->audios;
}
/**
* Get a list of all file media.
*
* @return array|MediaObjectInterface[]
* @return MediaObjectInterface[]
*/
public function files()
{
$this->files = $this->orderMedia($this->files);
return $this->files;
}
@@ -141,7 +154,7 @@ abstract class AbstractMedia extends Getters implements MediaCollectionInterface
*/
public function add($name, $file)
{
$this->instances[$name] = $file;
$this->offsetSet($name, $file);
switch ($file->type) {
case 'image':
$this->images[$name] = $file;
@@ -160,13 +173,14 @@ abstract class AbstractMedia extends Getters implements MediaCollectionInterface
/**
* Order the media based on the page's media_order
*
* @param $media
* @param array $media
* @return array
*/
protected function orderMedia($media)
{
if (null === $this->media_order) {
$page = Grav::instance()['pages']->get($this->path);
/** @var Page $page */
$page = Grav::instance()['pages']->get($this->getPath());
if ($page && isset($page->header()->media_order)) {
$this->media_order = array_map('trim', explode(',', $page->header()->media_order));

View File

@@ -17,7 +17,7 @@ class AudioMedium extends Medium
* Parsedown element for source display mode
*
* @param array $attributes
* @param boolean $reset
* @param bool $reset
* @return array
*/
protected function sourceParsedownElement(array $attributes, $reset = true)
@@ -51,7 +51,7 @@ class AudioMedium extends Medium
/**
* Allows to set the preload behaviour
*
* @param $preload
* @param string $preload
* @return $this
*/
public function preload($preload)
@@ -69,7 +69,7 @@ class AudioMedium extends Medium
* Allows to set the controlsList behaviour
* Separate multiple values with a hyphen
*
* @param $controlsList
* @param string $controlsList
* @return $this
*/
public function controlsList($controlsList)

View File

@@ -14,6 +14,16 @@ use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class GlobalMedia extends AbstractMedia
{
/**
* Return media path.
*
* @return null
*/
public function getPath()
{
return null;
}
/**
* @param mixed $offset
*

View File

@@ -33,13 +33,14 @@ class ImageFile extends Image
/**
* This is the same as the Gregwar Image class except this one fires a Grav Event on creation of new cached file
*
* @param string $type the image type
* @param int $quality the quality (for JPEG)
* @param bool $actual
* @param string $type the image type
* @param int $quality the quality (for JPEG)
* @param bool $actual
* @param array $extras
*
* @return string
*/
public function cacheFile($type = 'jpg', $quality = 80, $actual = false)
public function cacheFile($type = 'jpg', $quality = 80, $actual = false, $extras = [])
{
if ($type === 'guess') {
$type = $this->guessType();
@@ -50,21 +51,16 @@ class ImageFile extends Image
}
// Computes the hash
$this->hash = $this->getHash($type, $quality);
$this->hash = $this->getHash($type, $quality, $extras);
// Generates the cache file
$cacheFile = '';
// Seo friendly image names
$seofriendly = Grav::instance()['config']->get('system.images.seofriendly', false);
if (!$this->prettyName || $this->prettyPrefix) {
$cacheFile .= $this->hash;
}
if ($this->prettyPrefix) {
$cacheFile .= '-';
}
if ($this->prettyName) {
$cacheFile .= $this->prettyName;
if ($seofriendly) {
$mini_hash = substr($this->hash, 0, 4) . substr($this->hash, -4);
$cacheFile = "{$this->prettyName}-{$mini_hash}";
} else {
$cacheFile = "{$this->hash}-{$this->prettyName}";
}
$cacheFile .= '.' . $type;
@@ -108,4 +104,42 @@ class ImageFile extends Image
return $this->getFilename($file);
}
/**
* Gets the hash.
* @param string $type
* @param int $quality
* @param [] $extras
* @return null
*/
public function getHash($type = 'guess', $quality = 80, $extras = [])
{
if (null === $this->hash) {
$this->generateHash($type, $quality, $extras);
}
return $this->hash;
}
/**
* Generates the hash.
* @param string $type
* @param int $quality
* @param array $extras
*/
public function generateHash($type = 'guess', $quality = 80, $extras = [])
{
$inputInfos = $this->source->getInfos();
$datas = array(
$inputInfos,
$this->serializeOperations(),
$type,
$quality,
$extras
);
$this->hash = sha1(serialize($datas));
}
}

View File

@@ -42,7 +42,7 @@ class ImageMedium extends Medium
protected $default_quality;
/**
* @var boolean
* @var bool
*/
protected $debug_watermarked = false;
@@ -122,7 +122,7 @@ class ImageMedium extends Medium
/**
* Add meta file for the medium.
*
* @param $filepath
* @param string $filepath
* @return $this
*/
public function addMetaFile($filepath)
@@ -234,7 +234,7 @@ class ImageMedium extends Medium
/**
* Allows the ability to override the Inmage's Pretty name stored in cache
*
* @param $name
* @param string $name
*/
public function setImagePrettyName($name)
{
@@ -263,8 +263,8 @@ class ImageMedium extends Medium
* widths. Existing image alternatives won't be overwritten.
*
* @param int|int[] $min_width
* @param int [$max_width=2500]
* @param int [$step=200]
* @param int $max_width
* @param int $step
* @return $this
*/
public function derivatives($min_width, $max_width = 2500, $step = 200)
@@ -335,7 +335,7 @@ class ImageMedium extends Medium
* Parsedown element for source display mode
*
* @param array $attributes
* @param boolean $reset
* @param bool $reset
* @return array
*/
public function sourceParsedownElement(array $attributes, $reset = true)
@@ -378,7 +378,7 @@ class ImageMedium extends Medium
/**
* Turn the current Medium into a Link
*
* @param boolean $reset
* @param bool $reset
* @param array $attributes
* @return Link
*/
@@ -398,7 +398,7 @@ class ImageMedium extends Medium
*
* @param int $width
* @param int $height
* @param boolean $reset
* @param bool $reset
* @return Link
*/
public function lightbox($width = null, $height = null, $reset = true)
@@ -408,7 +408,7 @@ class ImageMedium extends Medium
}
if ($width && $height) {
$this->cropResize($width, $height);
$this->__call('cropResize', [$width, $height]);
}
return parent::lightbox($width, $height, $reset);
@@ -418,7 +418,7 @@ class ImageMedium extends Medium
* Sets or gets the quality of the image
*
* @param int $quality 0-100 quality
* @return Medium
* @return int|$this
*/
public function quality($quality = null)
{
@@ -619,7 +619,7 @@ class ImageMedium extends Medium
$this->image->merge(ImageFile::open($overlay));
}
return $this->image->cacheFile($this->format, $this->quality);
return $this->image->cacheFile($this->format, $this->quality, false, [$this->get('width'), $this->get('height'), $this->get('modified')]);
}
/**

View File

@@ -38,7 +38,7 @@ class Link implements RenderableInterface
* @param string $alt
* @param string $class
* @param string $id
* @param boolean $reset
* @param bool $reset
* @return array
*/
public function parsedownElement($title = null, $alt = null, $class = null, $id = null, $reset = true)

View File

@@ -16,6 +16,12 @@ use Grav\Common\Data\Blueprint;
use Grav\Common\Media\Interfaces\MediaObjectInterface;
use Grav\Common\Utils;
/**
* Class Medium
* @package Grav\Common\Page\Medium
*
* @property string $mime
*/
class Medium extends Data implements RenderableInterface, MediaObjectInterface
{
use ParsedownHtmlTrait;
@@ -137,6 +143,20 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
return filemtime($path) ?: null;
}
/**
* @return int
*/
public function size()
{
$path = $this->get('filepath');
if (!file_exists($path)) {
return 0;
}
return filesize($path) ?: 0;
}
/**
* Set querystring to file modification timestamp (or value provided as a parameter).
*
@@ -163,7 +183,7 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
/**
* Add meta file for the medium.
*
* @param $filepath
* @param string $filepath
*/
public function addMetaFile($filepath)
{
@@ -174,7 +194,7 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
/**
* Add alternative Medium to this Medium.
*
* @param $ratio
* @param int|float $ratio
* @param Medium $alternative
*/
public function addAlternative($ratio, Medium $alternative)
@@ -262,7 +282,7 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
* Get/set querystring for the file's url
*
* @param string $querystring
* @param boolean $withQuestionmark
* @param bool $withQuestionmark
* @return string
*/
public function querystring($querystring = null, $withQuestionmark = true)
@@ -291,7 +311,7 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
/**
* Get the URL with full querystring
*
* @param $url
* @param string $url
* @return string
*/
public function urlQuerystring($url)
@@ -308,7 +328,7 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
* Get/set hash for the file's url
*
* @param string $hash
* @param boolean $withHash
* @param bool $withHash
* @return string
*/
public function urlHash($hash = null, $withHash = true)
@@ -329,7 +349,7 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
* @param string $alt
* @param string $class
* @param string $id
* @param boolean $reset
* @param bool $reset
* @return array
*/
public function parsedownElement($title = null, $alt = null, $class = null, $id = null, $reset = true)
@@ -393,6 +413,8 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
case 'source':
$element = $this->sourceParsedownElement($attributes, false);
break;
default:
$element = [];
}
if ($reset) {
@@ -408,7 +430,7 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
* Parsedown element for source display mode
*
* @param array $attributes
* @param boolean $reset
* @param bool $reset
* @return array
*/
protected function sourceParsedownElement(array $attributes, $reset = true)
@@ -420,7 +442,7 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
* Parsedown element for text display mode
*
* @param array $attributes
* @param boolean $reset
* @param bool $reset
* @return array
*/
protected function textParsedownElement(array $attributes, $reset = true)
@@ -512,7 +534,7 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
/**
* Turn the current Medium into a Link
*
* @param boolean $reset
* @param bool $reset
* @param array $attributes
* @return Link
*/
@@ -536,7 +558,7 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
*
* @param int $width
* @param int $height
* @param boolean $reset
* @param bool $reset
* @return Link
*/
public function lightbox($width = null, $height = null, $reset = true)
@@ -571,7 +593,7 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
* Add an id to the element from Markdown or Twig
* Example: ![Example](myimg.png?id=primary-img)
*
* @param $id
* @param string $id
* @return $this
*/
public function id($id)

View File

@@ -37,14 +37,14 @@ class MediumFactory
$config = Grav::instance()['config'];
$media_params = $config->get('media.types.' . strtolower($ext));
if (!$media_params) {
if (!\is_array($media_params)) {
return null;
}
$params += $media_params;
// Add default settings for undefined variables.
$params += $config->get('media.types.defaults');
$params += (array)$config->get('media.types.defaults');
$params += [
'type' => 'file',
'thumb' => 'media/thumb.png',
@@ -87,14 +87,14 @@ class MediumFactory
$config = Grav::instance()['config'];
$media_params = $config->get('media.types.' . strtolower($ext));
if (!$media_params) {
if (!\is_array($media_params)) {
return null;
}
$params += $media_params;
// Add default settings for undefined variables.
$params += $config->get('media.types.defaults');
$params += (array)$config->get('media.types.defaults');
$params += [
'type' => 'file',
'thumb' => 'media/thumb.png',

View File

@@ -17,7 +17,7 @@ class StaticImageMedium extends Medium
* Parsedown element for source display mode
*
* @param array $attributes
* @param boolean $reset
* @param bool $reset
* @return array
*/
protected function sourceParsedownElement(array $attributes, $reset = true)

View File

@@ -17,7 +17,7 @@ class ThumbnailImageMedium extends ImageMedium
public $parent = null;
/**
* @var boolean
* @var bool
*/
public $linked = false;
@@ -39,7 +39,7 @@ class ThumbnailImageMedium extends ImageMedium
* @param string $alt
* @param string $class
* @param string $id
* @param boolean $reset
* @param bool $reset
* @return array
*/
public function parsedownElement($title = null, $alt = null, $class = null, $id = null, $reset = true)
@@ -91,7 +91,7 @@ class ThumbnailImageMedium extends ImageMedium
/**
* Turn the current Medium into a Link
*
* @param boolean $reset
* @param bool $reset
* @param array $attributes
* @return Link
*/
@@ -105,7 +105,7 @@ class ThumbnailImageMedium extends ImageMedium
*
* @param int $width
* @param int $height
* @param boolean $reset
* @param bool $reset
* @return Link
*/
public function lightbox($width = null, $height = null, $reset = true)
@@ -118,7 +118,7 @@ class ThumbnailImageMedium extends ImageMedium
*
* @param string $method
* @param array $arguments
* @param boolean $testLinked
* @param bool $testLinked
* @return Medium
*/
protected function bubble($method, array $arguments = [], $testLinked = true)

View File

@@ -17,7 +17,7 @@ class VideoMedium extends Medium
* Parsedown element for source display mode
*
* @param array $attributes
* @param boolean $reset
* @param bool $reset
* @return array
*/
protected function sourceParsedownElement(array $attributes, $reset = true)
@@ -51,7 +51,7 @@ class VideoMedium extends Medium
/**
* Allows to set the video's poster image
*
* @param $urlImage
* @param string $urlImage
* @return $this
*/
public function poster($urlImage)

View File

@@ -23,6 +23,7 @@ use Grav\Common\Taxonomy;
use Grav\Common\Uri;
use Grav\Common\Utils;
use Grav\Common\Yaml;
use Negotiation\Accept;
use Negotiation\Negotiator;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\MarkdownFile;
@@ -96,7 +97,7 @@ class Page implements PageInterface
protected $forms;
/**
* @var Page Unmodified (original) version of the page. Used for copying and moving the page.
* @var PageInterface Unmodified (original) version of the page. Used for copying and moving the page.
*/
private $_original;
@@ -345,7 +346,7 @@ class Page implements PageInterface
$this->processFrontmatter();
}
}
} catch (ParseException $e) {
} catch (\Exception $e) {
$file->raw(Grav::instance()['language']->translate([
'GRAV.FRONTMATTER_ERROR_PAGE',
$this->slug(),
@@ -413,9 +414,7 @@ class Page implements PageInterface
$this->markdown_extra = (bool)$this->header->markdown_extra;
}
if (isset($this->header->taxonomy)) {
foreach ((array)$this->header->taxonomy as $taxonomy => $taxitems) {
$this->taxonomy[$taxonomy] = (array)$taxitems;
}
$this->taxonomy($this->header->taxonomy);
}
if (isset($this->header->max_count)) {
$this->max_count = (int)$this->header->max_count;
@@ -466,7 +465,7 @@ class Page implements PageInterface
/**
* Get page language
*
* @param $var
* @param string $var
*
* @return mixed
*/
@@ -482,8 +481,8 @@ class Page implements PageInterface
/**
* Modify a header value directly
*
* @param $key
* @param $value
* @param string $key
* @param mixed $value
*/
public function modifyHeader($key, $value)
{
@@ -548,7 +547,7 @@ class Page implements PageInterface
*
* @param int $size Max summary size.
*
* @param boolean $textOnly Only count text size.
* @param bool $textOnly Only count text size.
*
* @return string
*/
@@ -748,6 +747,8 @@ class Page implements PageInterface
$this->content = str_replace("<p>{$delimiter}</p>", '', $this->content);
}
// Fire event when Page::content() is called
Grav::instance()->fireEvent('onPageContent', new Event(['page' => $this]));
}
return $this->content;
@@ -770,8 +771,8 @@ class Page implements PageInterface
/**
* Add an entry to the page's contentMeta array
*
* @param $name
* @param $value
* @param string $name
* @param string $value
*/
public function addContentMeta($name, $value)
{
@@ -781,9 +782,9 @@ class Page implements PageInterface
/**
* Return the whole contentMeta array as it currently stands
*
* @param null $name
* @param string|null $name
*
* @return mixed
* @return string
*/
public function getContentMeta($name = null)
{
@@ -801,9 +802,9 @@ class Page implements PageInterface
/**
* Sets the whole content meta array in one shot
*
* @param $content_meta
* @param array $content_meta
*
* @return mixed
* @return array
*/
public function setContentMeta($content_meta)
{
@@ -873,7 +874,7 @@ class Page implements PageInterface
/**
* Needed by the onPageContentProcessed event to set the raw page content
*
* @param $content
* @param string $content
*/
public function setRawContent($content)
{
@@ -965,9 +966,9 @@ class Page implements PageInterface
/**
* Gets and Sets the Page raw content
*
* @param null $var
* @param string|null $var
*
* @return null
* @return string
*/
public function rawMarkdown($var = null)
{
@@ -1023,11 +1024,11 @@ class Page implements PageInterface
*
* You need to call $this->save() in order to perform the move.
*
* @param Page $parent New parent page.
* @param PageInterface $parent New parent page.
*
* @return $this
*/
public function move(Page $parent)
public function move(PageInterface $parent)
{
if (!$this->_original) {
$clone = clone $this;
@@ -1067,11 +1068,11 @@ class Page implements PageInterface
* Returns a new Page object for the copy.
* You need to call $this->save() in order to perform the move.
*
* @param Page $parent New parent page.
* @param PageInterface $parent New parent page.
*
* @return $this
*/
public function copy($parent)
public function copy(PageInterface $parent)
{
$this->move($parent);
$this->_action = 'copy';
@@ -1240,6 +1241,9 @@ class Page implements PageInterface
return $this->forms;
}
/**
* @param array $new
*/
public function addForms(array $new)
{
// Initialize forms.
@@ -1402,7 +1406,8 @@ class Page implements PageInterface
$priorities = Utils::getMimeTypes($supported_types);
$media_type = $negotiator->getBest($http_accept, $priorities);
$mimetype = $media_type ? $media_type->getValue() : '';
$mimetype = $media_type instanceof Accept ? $media_type->getValue() : '';
$this->template_format = Utils::getExtensionByMime($mimetype);
return $this->template_format;
@@ -1445,7 +1450,7 @@ class Page implements PageInterface
}
// if not set in the page get the value from system config
if (empty($this->url_extension)) {
if (null === $this->url_extension) {
$this->url_extension = Grav::instance()['config']->get('system.pages.append_url_extension', '');
}
@@ -1737,6 +1742,14 @@ class Page implements PageInterface
return $this->metadata;
}
/**
* Reset the metadata and pull from header again
*/
public function resetMetadata()
{
$this->metadata = null;
}
/**
* Gets and Sets the slug for the Page. The slug is used in the URL routing. If not set it uses
* the parent folder from the path
@@ -2033,9 +2046,9 @@ class Page implements PageInterface
/**
* Gets and sets the option to show the etag header for the page.
*
* @param boolean $var show etag header
* @param bool $var show etag header
*
* @return boolean show etag header
* @return bool show etag header
*/
public function eTag($var = null)
{
@@ -2052,9 +2065,9 @@ class Page implements PageInterface
/**
* Gets and sets the option to show the last_modified header for the page.
*
* @param boolean $var show last_modified header
* @param bool $var show last_modified header
*
* @return boolean show last_modified header
* @return bool show last_modified header
*/
public function lastModified($var = null)
{
@@ -2186,9 +2199,12 @@ class Page implements PageInterface
* @param string $var the order, either "asc" or "desc"
*
* @return string the order, either "asc" or "desc"
* @deprecated 1.6
*/
public function orderDir($var = null)
{
//user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.6', E_USER_DEPRECATED);
if ($var !== null) {
$this->order_dir = $var;
}
@@ -2211,9 +2227,12 @@ class Page implements PageInterface
* @param string $var supported options include "default", "title", "date", and "folder"
*
* @return string supported options include "default", "title", "date", and "folder"
* @deprecated 1.6
*/
public function orderBy($var = null)
{
//user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.6', E_USER_DEPRECATED);
if ($var !== null) {
$this->order_by = $var;
}
@@ -2227,9 +2246,12 @@ class Page implements PageInterface
* @param string $var supported options include "default", "title", "date", and "folder"
*
* @return array
* @deprecated 1.6
*/
public function orderManual($var = null)
{
//user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.6', E_USER_DEPRECATED);
if ($var !== null) {
$this->order_manual = $var;
}
@@ -2244,9 +2266,12 @@ class Page implements PageInterface
* @param int $var the maximum number of sub-pages
*
* @return int the maximum number of sub-pages
* @deprecated 1.6
*/
public function maxCount($var = null)
{
//user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.6', E_USER_DEPRECATED);
if ($var !== null) {
$this->max_count = (int)$var;
}
@@ -2269,6 +2294,14 @@ class Page implements PageInterface
public function taxonomy($var = null)
{
if ($var !== null) {
// make sure first level are arrays
array_walk($var, function(&$value) {
$value = (array) $value;
});
// make sure all values are strings
array_walk_recursive($var, function(&$value) {
$value = (string) $value;
});
$this->taxonomy = $var;
}
@@ -2326,11 +2359,11 @@ class Page implements PageInterface
/**
* Gets and Sets the parent object for this page
*
* @param Page $var the parent page object
* @param PageInterface $var the parent page object
*
* @return Page|null the parent page object if it exists.
* @return PageInterface|null the parent page object if it exists.
*/
public function parent(Page $var = null)
public function parent(PageInterface $var = null)
{
if ($var) {
$this->parent = $var->path();
@@ -2347,7 +2380,7 @@ class Page implements PageInterface
/**
* Gets the top parent object for this page
*
* @return Page|null the top parent page object if it exists.
* @return PageInterface|null the top parent page object if it exists.
*/
public function topParent()
{
@@ -2372,7 +2405,7 @@ class Page implements PageInterface
/**
* Returns children of this page.
*
* @return \Grav\Common\Page\Collection
* @return Collection
*/
public function children()
{
@@ -2386,7 +2419,7 @@ class Page implements PageInterface
/**
* Check to see if this item is the first in an array of sub-pages.
*
* @return boolean True if item is first.
* @return bool True if item is first.
*/
public function isFirst()
{
@@ -2402,7 +2435,7 @@ class Page implements PageInterface
/**
* Check to see if this item is the last in an array of sub-pages.
*
* @return boolean True if item is last
* @return bool True if item is last
*/
public function isLast()
{
@@ -2418,7 +2451,7 @@ class Page implements PageInterface
/**
* Gets the previous sibling based on current position.
*
* @return Page the previous Page item
* @return PageInterface the previous Page item
*/
public function prevSibling()
{
@@ -2428,7 +2461,7 @@ class Page implements PageInterface
/**
* Gets the next sibling based on current position.
*
* @return Page the next Page item
* @return PageInterface the next Page item
*/
public function nextSibling()
{
@@ -2438,9 +2471,9 @@ class Page implements PageInterface
/**
* Returns the adjacent sibling based on a direction.
*
* @param integer $direction either -1 or +1
* @param int $direction either -1 or +1
*
* @return Page|bool the sibling page
* @return PageInterface|bool the sibling page
*/
public function adjacentSibling($direction = 1)
{
@@ -2456,9 +2489,7 @@ class Page implements PageInterface
/**
* Returns the item in the current position.
*
* @param string $path the path the item
*
* @return Integer the index of the current page.
* @return int the index of the current page.
*/
public function currentPosition()
{
@@ -2468,7 +2499,7 @@ class Page implements PageInterface
return $collection->currentPosition($this->path());
}
return true;
return 1;
}
/**
@@ -2498,7 +2529,7 @@ class Page implements PageInterface
$routes = Grav::instance()['pages']->routes();
if (isset($routes[$uri_path])) {
/** @var Page $child_page */
/** @var PageInterface $child_page */
$child_page = $pages->dispatch($uri->route())->parent();
if ($child_page) {
while (!$child_page->root()) {
@@ -2538,10 +2569,9 @@ class Page implements PageInterface
/**
* Helper method to return an ancestor page.
*
* @param string $url The url of the page
* @param bool $lookup Name of the parent folder
*
* @return \Grav\Common\Page\Page page you were looking for if it exists
* @return PageInterface page you were looking for if it exists
*/
public function ancestor($lookup = null)
{
@@ -2557,7 +2587,7 @@ class Page implements PageInterface
*
* @param string $field Name of the parent folder
*
* @return Page
* @return PageInterface
*/
public function inherited($field)
{
@@ -2611,7 +2641,7 @@ class Page implements PageInterface
* @param string $url the url of the page
* @param bool $all
*
* @return \Grav\Common\Page\Page page you were looking for if it exists
* @return PageInterface page you were looking for if it exists
*/
public function find($url, $all = false)
{
@@ -2625,7 +2655,7 @@ class Page implements PageInterface
* Get a collection of pages in the current context.
*
* @param string|array $params
* @param boolean $pagination
* @param bool $pagination
*
* @return Collection
* @throws \InvalidArgumentException
@@ -3016,7 +3046,7 @@ class Page implements PageInterface
/**
* Reorders all siblings according to a defined order
*
* @param $new_order
* @param array|null $new_order
*/
protected function doReorder($new_order)
{
@@ -3122,8 +3152,7 @@ class Page implements PageInterface
/**
* Gets the Page Unmodified (original) version of the page.
*
* @return Page
* The original version of the page.
* @return PageInterface The original version of the page.
*/
public function getOriginal()
{
@@ -3133,8 +3162,7 @@ class Page implements PageInterface
/**
* Gets the action.
*
* @return string
* The Action string.
* @return string The Action string.
*/
public function getAction()
{

View File

@@ -16,6 +16,7 @@ use Grav\Common\Data\Blueprints;
use Grav\Common\Filesystem\Folder;
use Grav\Common\Grav;
use Grav\Common\Language\Language;
use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Taxonomy;
use Grav\Common\Uri;
use Grav\Common\Utils;
@@ -33,7 +34,7 @@ class Pages
protected $grav;
/**
* @var array|Page[]
* @var array|PageInterface[]
*/
protected $instances;
@@ -261,7 +262,7 @@ class Pages
/**
* Returns a list of all pages.
*
* @return array|Page[]
* @return array|PageInterface[]
*/
public function instances()
{
@@ -281,10 +282,10 @@ class Pages
/**
* Adds a page and assigns a route to it.
*
* @param Page $page Page to be added.
* @param PageInterface $page Page to be added.
* @param string $route Optional route (uses route from the object if not set).
*/
public function addPage(Page $page, $route = null)
public function addPage(PageInterface $page, $route = null)
{
if (!isset($this->instances[$page->path()])) {
$this->instances[$page->path()] = $page;
@@ -301,13 +302,13 @@ class Pages
/**
* Sort sub-pages in a page.
*
* @param Page $page
* @param PageInterface $page
* @param string $order_by
* @param string $order_dir
*
* @return array
*/
public function sort(Page $page, $order_by = null, $order_dir = null, $sort_flags = null)
public function sort(PageInterface $page, $order_by = null, $order_dir = null, $sort_flags = null)
{
if ($order_by === null) {
$order_by = $page->orderBy();
@@ -338,10 +339,10 @@ class Pages
/**
* @param Collection $collection
* @param $orderBy
* @param string|int $orderBy
* @param string $orderDir
* @param null $orderManual
* @param null $sort_flags
* @param array|null $orderManual
* @param int|null $sort_flags
*
* @return array
* @internal
@@ -373,7 +374,7 @@ class Pages
*
* @param string $path The filesystem full path of the page
*
* @return Page
* @return PageInterface
* @throws \Exception
*/
public function get($path)
@@ -401,7 +402,7 @@ class Pages
* @param string $route The relative URL of the page
* @param string $path The relative path of the ancestor folder
*
* @return Page|null
* @return PageInterface|null
*/
public function ancestor($route, $path = null)
{
@@ -427,7 +428,7 @@ class Pages
* @param string $route The relative route of the page
* @param string $field The field name of the ancestor to query for
*
* @return Page|null
* @return PageInterface|null
*/
public function inherited($route, $field = null)
{
@@ -453,7 +454,7 @@ class Pages
* @param string $route The relative URL of the page
* @param bool $all
*
* @return Page|null
* @return PageInterface|null
*/
public function find($route, $all = false)
{
@@ -467,7 +468,7 @@ class Pages
* @param bool $all
*
* @param bool $redirect
* @return Page|null
* @return PageInterface|null
* @throws \Exception
*/
public function dispatch($route, $all = false, $redirect = true)
@@ -549,7 +550,7 @@ class Pages
/**
* Get root page.
*
* @return Page
* @return PageInterface
*/
public function root()
{
@@ -589,15 +590,15 @@ class Pages
/**
* Get all pages
*
* @param \Grav\Common\Page\Page $current
* @param PageInterface $current
*
* @return \Grav\Common\Page\Collection
*/
public function all(Page $current = null)
public function all(PageInterface $current = null)
{
$all = new Collection();
/** @var Page $current */
/** @var PageInterface $current */
$current = $current ?: $this->root();
if (!$current->root()) {
@@ -659,7 +660,7 @@ class Pages
/**
* Get list of route/title of all pages.
*
* @param Page $current
* @param PageInterface $current
* @param int $level
* @param bool $rawRoutes
*
@@ -670,7 +671,7 @@ class Pages
* @param bool $limitLevels
* @return array
*/
public function getList(Page $current = null, $level = 0, $rawRoutes = false, $showAll = true, $showFullpath = false, $showSlug = false, $showModular = false, $limitLevels = false)
public function getList(PageInterface $current = null, $level = 0, $rawRoutes = false, $showAll = true, $showFullpath = false, $showSlug = false, $showModular = false, $limitLevels = false)
{
if (!$current) {
if ($level) {
@@ -811,7 +812,7 @@ class Pages
/** @var Admin $admin */
$admin = Grav::instance()['admin'];
/** @var Page $page */
/** @var PageInterface $page */
$page = $admin->getPage($admin->route);
if ($page && $page->modular()) {
@@ -984,7 +985,7 @@ class Pages
/**
* Accessible method to manually reset the pages cache
*
* @param $pages_dir
* @param string $pages_dir
*/
public function resetPages($pages_dir)
{
@@ -1007,13 +1008,13 @@ class Pages
* Recursive function to load & build page relationships.
*
* @param string $directory
* @param Page|null $parent
* @param PageInterface|null $parent
*
* @return Page
* @return PageInterface
* @throws \RuntimeException
* @internal
*/
protected function recurse($directory, Page $parent = null)
protected function recurse($directory, PageInterface $parent = null)
{
$directory = rtrim($directory, DS);
$page = new Page;
@@ -1179,14 +1180,14 @@ class Pages
*/
protected function buildRoutes()
{
/** @var $taxonomy Taxonomy */
/** @var Taxonomy $taxonomy */
$taxonomy = $this->grav['taxonomy'];
// Get the home route
$home = self::resetHomeRoute();
// Build routes and taxonomy map.
/** @var $page Page */
/** @var PageInterface $page */
foreach ($this->instances as $page) {
if (!$page->root()) {
// process taxonomy
@@ -1232,8 +1233,8 @@ class Pages
* @param string $path
* @param array $pages
* @param string $order_by
* @param array $manual
* @param int $sort_flags
* @param array|null $manual
* @param int|null $sort_flags
*
* @throws \RuntimeException
* @internal

View File

@@ -9,8 +9,9 @@
namespace Grav\Common;
use Grav\Common\Data\Blueprint;
use Grav\Common\Data\Data;
use Grav\Common\Page\Page;
use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Config\Config;
use RocketTheme\Toolbox\Event\EventDispatcher;
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
@@ -120,7 +121,7 @@ class Plugin implements EventSubscriberInterface, \ArrayAccess
/**
* Determine if this route is in Admin and active for the plugin
*
* @param $plugin_route
* @param string $plugin_route
* @return bool
*/
protected function isPluginActiveAdmin($plugin_route)
@@ -183,7 +184,7 @@ class Plugin implements EventSubscriberInterface, \ArrayAccess
/**
* Whether or not an offset exists.
*
* @param mixed $offset An offset to check for.
* @param string $offset An offset to check for.
* @return bool Returns TRUE on success or FALSE on failure.
*/
public function offsetExists($offset)
@@ -199,7 +200,7 @@ class Plugin implements EventSubscriberInterface, \ArrayAccess
/**
* Returns the value at specified offset.
*
* @param mixed $offset The offset to retrieve.
* @param string $offset The offset to retrieve.
* @return mixed Can return all value types.
*/
public function offsetGet($offset)
@@ -215,7 +216,7 @@ class Plugin implements EventSubscriberInterface, \ArrayAccess
/**
* Assigns a value to the specified offset.
*
* @param mixed $offset The offset to assign the value to.
* @param string $offset The offset to assign the value to.
* @param mixed $value The value to set.
* @throws \LogicException
*/
@@ -227,7 +228,7 @@ class Plugin implements EventSubscriberInterface, \ArrayAccess
/**
* Unsets an offset.
*
* @param mixed $offset The offset to unset.
* @param string $offset The offset to unset.
* @throws \LogicException
*/
public function offsetUnset($offset)
@@ -258,7 +259,7 @@ class Plugin implements EventSubscriberInterface, \ArrayAccess
/**
* Merge global and page configurations.
*
* @param Page $page The page to merge the configurations with the
* @param PageInterface $page The page to merge the configurations with the
* plugin settings.
* @param mixed $deep false = shallow|true = recursive|merge = recursive+unique
* @param array $params Array of additional configuration options to
@@ -267,7 +268,7 @@ class Plugin implements EventSubscriberInterface, \ArrayAccess
*
* @return Data
*/
protected function mergeConfig(Page $page, $deep = false, $params = [], $type = 'plugins')
protected function mergeConfig(PageInterface $page, $deep = false, $params = [], $type = 'plugins')
{
$class_name = $this->name;
$class_name_merged = $class_name . '.merged';
@@ -304,12 +305,12 @@ class Plugin implements EventSubscriberInterface, \ArrayAccess
/**
* Merge arrays based on deepness
*
* @param bool $deep
* @param $array1
* @param $array2
* @return array|mixed
* @param string|bool $deep
* @param array $array1
* @param array $array2
* @return array
*/
private function mergeArrays($deep = false, $array1, $array2)
private function mergeArrays($deep, $array1, $array2)
{
if ($deep === 'merge') {
return Utils::arrayMergeRecursiveUnique($array1, $array2);
@@ -326,7 +327,7 @@ class Plugin implements EventSubscriberInterface, \ArrayAccess
*
* @param string $plugin_name The name of the plugin whose config it should store.
*
* @return true
* @return bool
*/
public static function saveConfig($plugin_name)
{
@@ -348,7 +349,7 @@ class Plugin implements EventSubscriberInterface, \ArrayAccess
/**
* Simpler getter for the plugin blueprint
*
* @return mixed
* @return Blueprint
*/
public function getBlueprint()
{

View File

@@ -90,7 +90,7 @@ class Plugins extends Iterator
/**
* Registers all plugins.
*
* @return array|Plugin[] array of Plugin objects
* @return Plugin[] array of Plugin objects
* @throws \RuntimeException
*/
public function init()
@@ -117,7 +117,7 @@ class Plugins extends Iterator
/**
* Add a plugin
*
* @param $plugin
* @param Plugin $plugin
*/
public function add($plugin)
{

View File

@@ -9,6 +9,7 @@
namespace Grav\Common\Processors;
use Grav\Framework\Psr7\Response;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
@@ -25,5 +26,6 @@ class DebuggerAssetsProcessor extends ProcessorBase
$this->stopTimer();
return $handler->handle($request);
}
}

View File

@@ -13,7 +13,7 @@ use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
class DebuggerInitProcessor extends ProcessorBase
class DebuggerProcessor extends ProcessorBase
{
public $id = '_debugger';
public $title = 'Init Debugger';

View File

@@ -46,7 +46,7 @@ class InitializeProcessor extends ProcessorBase
// FIXME: Initialize session should happen later after plugins have been loaded. This is a workaround to fix session issues in AWS.
if (isset($this->container['session']) && $config->get('system.session.initialize', true)) {
// TODO: remove in 2.0.
$this->container['users'];
$this->container['accounts'];
try {
$this->container['session']->init();

View File

@@ -9,7 +9,7 @@
namespace Grav\Common\Processors;
use Grav\Common\Page\Page;
use Grav\Common\Page\Interfaces\PageInterface;
use RocketTheme\Toolbox\Event\Event;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
@@ -31,12 +31,14 @@ class PagesProcessor extends ProcessorBase
$this->container->fireEvent('onPagesInitialized', new Event(['pages' => $this->container['pages']]));
$this->container->fireEvent('onPageInitialized', new Event(['page' => $this->container['page']]));
/** @var Page $page */
/** @var PageInterface $page */
$page = $this->container['page'];
if (!$page->routable()) {
// If no page found, fire event
$event = $this->container->fireEvent('onPageNotFound', new Event(['page' => $page]));
$event = new Event(['page' => $page]);
$event->page = null;
$event = $this->container->fireEvent('onPageNotFound', $event);
if (isset($event->page)) {
unset ($this->container['page']);

View File

@@ -22,7 +22,7 @@ class PluginsProcessor extends ProcessorBase
{
$this->startTimer();
// TODO: remove in 2.0.
$this->container['users'];
$this->container['accounts'];
$this->container['plugins']->init();
$this->container->fireEvent('onPluginsInitialized');
$this->stopTimer();

View File

@@ -17,6 +17,9 @@ abstract class ProcessorBase implements ProcessorInterface
/** @var Grav */
protected $container;
public $id = 'processorbase';
public $title = 'ProcessorBase';
public function __construct(Grav $container)
{
$this->container = $container;

View File

@@ -9,7 +9,7 @@
namespace Grav\Common\Processors;
use Grav\Common\Page\Page;
use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Framework\Psr7\Response;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
@@ -37,9 +37,6 @@ class RenderProcessor extends ProcessorBase
$container->output = $output;
$container->fireEvent('onOutputGenerated');
// Set the header type
$container->header();
echo $container->output;
// remove any output
@@ -49,7 +46,7 @@ class RenderProcessor extends ProcessorBase
$html = ob_get_clean();
/** @var Page $page */
/** @var PageInterface $page */
$page = $this->container['page'];
$this->stopTimer();

View File

@@ -23,8 +23,16 @@ class RequestProcessor extends ProcessorBase
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
{
$this->startTimer();
$header = $request->getHeaderLine('Content-Type');
$type = trim(strstr($header, ';', true) ?: $header);
if ($type === 'application/json') {
$request = $request->withParsedBody(json_decode($request->getBody()->getContents(), true));
}
$request = $request
->withAttribute('grav', $this->container)
->withAttribute('time', $_SERVER['REQUEST_TIME_FLOAT'] ?? GRAV_REQUEST_TIME)
->withAttribute('route', Uri::getCurrentRoute())
->withAttribute('referrer', $this->container['uri']->referrer());

View File

@@ -128,7 +128,7 @@ class Cron
/**
*
* @param string $cron
* @param string|null $cron
*/
public function __construct($cron = null)
{
@@ -459,7 +459,7 @@ class Cron
/**
*
* @param int|string|\Datetime $date
* @param int|string|\DateTime $date
*/
public function matchExact($date)
{
@@ -476,7 +476,7 @@ class Cron
/**
*
* @param int|string|\Datetime $date
* @param int|string|\DateTime $date
* @param int $minuteBefore
* @param int $minuteAfter
*/

View File

@@ -175,7 +175,7 @@ class Job
/**
* Force the Job to run in foreground.
*
* @return self
* @return $this
*/
public function inForeground()
{
@@ -187,7 +187,7 @@ class Job
/**
* Sets/Gets an option backlink
*
* @param $link string
* @param string $link
*
* @return null|string
*/
@@ -298,7 +298,6 @@ class Job
if (is_callable($this->command)) {
$this->output = $this->exec();
} else {
/** @var Process process */
$args = \is_string($this->args) ? $this->args : implode(' ', $this->args);
$command = $this->command . ' ' . $args;
$process = new Process($command);

View File

@@ -106,7 +106,6 @@ class Scheduler
/**
* Get all jobs if they are disabled or not as one array
*
* @param bool $all
* @return array
*/
public function getAllJobs()
@@ -151,10 +150,9 @@ class Scheduler
/**
* Run the scheduler.
*
* @param \DateTime $runTime Optional, run at specific moment
* @return array Executed jobs
* @param \DateTime|null $runTime Optional, run at specific moment
*/
public function run(\Datetime $runTime = null)
public function run(\DateTime $runTime = null)
{
$this->loadSavedJobs();
@@ -236,7 +234,7 @@ class Scheduler
{
$phpBinaryFinder = new PhpExecutableFinder();
$php = $phpBinaryFinder->find();
$command = 'cd ' . GRAV_ROOT . ';' . $php . ' bin/grav scheduler';
$command = 'cd ' . str_replace(' ', '\ ', GRAV_ROOT) . ';' . $php . ' bin/grav scheduler';
return "(crontab -l; echo \"* * * * * {$command} 1>> /dev/null 2>&1\") | crontab -";
}

View File

@@ -93,7 +93,7 @@ class Security
* their content.
*
* @param string $string The string to run XSS detection logic on
* @return boolean|string Type of XSS vector if the given `$string` may contain XSS, false otherwise.
* @return bool|string Type of XSS vector if the given `$string` may contain XSS, false otherwise.
*
* Copies the code from: https://github.com/symphonycms/xssfilter/blob/master/extension.driver.php#L138
*/

View File

@@ -10,6 +10,7 @@
namespace Grav\Common\Service;
use Grav\Common\Config\Config;
use Grav\Common\Debugger;
use Grav\Common\User\DataUser;
use Grav\Common\User\FlexUser;
use Grav\Common\User\User;
@@ -21,20 +22,29 @@ use Pimple\ServiceProviderInterface;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\Event\EventDispatcher;
class UserServiceProvider implements ServiceProviderInterface
class AccountsServiceProvider implements ServiceProviderInterface
{
public function register(Container $container)
{
$container['users'] = function (Container $container) {
$container['accounts'] = function (Container $container) {
/** @var Debugger $debugger */
$debugger = $container['debugger'];
if ($container['config']->get('system.accounts.type') === 'flex') {
return $this->flexUsers($container);
$debugger->addMessage('User Accounts: Flex Directory');
return $this->flexAccounts($container);
}
return $this->dataUsers($container);
return $this->dataAccounts($container);
};
$container['users'] = $container->factory(function (Container $container) {
user_error('Grav::instance()[\'users\'] is deprecated since Grav 1.6, use Grav::instance()[\'accounts\'] instead', E_USER_DEPRECATED);
return $container['accounts'];
});
}
protected function dataUsers(Container $container)
protected function dataAccounts(Container $container)
{
define('GRAV_USER_INSTANCE', 'DATA');
@@ -42,7 +52,7 @@ class UserServiceProvider implements ServiceProviderInterface
return new DataUser\UserCollection(User::class);
}
protected function flexUsers(Container $container)
protected function flexAccounts(Container $container)
{
define('GRAV_USER_INSTANCE', 'FLEX');
@@ -55,11 +65,20 @@ class UserServiceProvider implements ServiceProviderInterface
'object' => User::class, // Use User class for backwards compatibility.
'collection' => FlexUser\UserCollection::class,
'index' => FlexUser\UserIndex::class,
'storage' => $this->getFlexStorage($config->get('system.accounts.storage', 'file'))
'storage' => $this->getFlexStorage($config->get('system.accounts.storage', 'file')),
'search' => [
'options' => [
'contains' => 1
],
'fields' => [
'key',
'email'
]
]
]
] + ($config->get('plugins.flex-objects.object') ?: []);
$directory = new FlexDirectory('users', 'blueprints://user/users.yaml', $options);
$directory = new FlexDirectory('accounts', 'blueprints://user/accounts.yaml', $options);
/** @var EventDispatcher $dispatcher */
$dispatcher = $container['events'];
@@ -85,8 +104,9 @@ class UserServiceProvider implements ServiceProviderInterface
'formatter' => ['class' => YamlFormatter::class],
'folder' => 'account://',
'pattern' => '{FOLDER}/{KEY:2}/{KEY}/user.yaml',
'key' => 'username',
'indexed' => true
]
],
];
}
@@ -96,8 +116,9 @@ class UserServiceProvider implements ServiceProviderInterface
'formatter' => ['class' => YamlFormatter::class],
'folder' => 'account://',
'pattern' => '{FOLDER}/{KEY}.yaml',
'key' => 'storage_key',
'indexed' => true
]
],
];
}
}

View File

@@ -15,6 +15,7 @@ use Grav\Common\Config\CompiledLanguages;
use Grav\Common\Config\Config;
use Grav\Common\Config\ConfigFileFinder;
use Grav\Common\Config\Setup;
use Grav\Common\Language\Language;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use RocketTheme\Toolbox\File\YamlFile;
@@ -25,7 +26,10 @@ class ConfigServiceProvider implements ServiceProviderInterface
public function register(Container $container)
{
$container['setup'] = function ($c) {
return static::setup($c);
$setup = new Setup($c);
$setup->init();
return $setup;
};
$container['blueprints'] = function ($c) {
@@ -46,11 +50,10 @@ class ConfigServiceProvider implements ServiceProviderInterface
$container['languages'] = function ($c) {
return static::languages($c);
};
}
public static function setup(Container $container)
{
return new Setup($container);
$container['language'] = function ($c) {
return new Language($c);
};
}
public static function blueprints(Container $container)
@@ -137,8 +140,8 @@ class ConfigServiceProvider implements ServiceProviderInterface
/**
* Find specific paths in plugins
*
* @param $plugins
* @param $folder_path
* @param array $plugins
* @param string $folder_path
* @return array
*/
private static function pluginFolderPaths($plugins, $folder_path)

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