mirror of
https://github.com/getgrav/grav.git
synced 2025-12-05 23:39:58 +01:00
Compare commits
224 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aabc8aeb31 | ||
|
|
ca4f6f3c5c | ||
|
|
3a645ab4a2 | ||
|
|
25ba6198e5 | ||
|
|
6564ea98b1 | ||
|
|
8fe018a7dd | ||
|
|
5c40337ff0 | ||
|
|
41e7142dfc | ||
|
|
f45362b5b5 | ||
|
|
f8fbc82196 | ||
|
|
c153039457 | ||
|
|
88ccc25c47 | ||
|
|
b8164d3fe6 | ||
|
|
a1d4199e68 | ||
|
|
30f8eca905 | ||
|
|
ef7ff9ec4e | ||
|
|
0d471599d7 | ||
|
|
13bc19f1e3 | ||
|
|
da61196b7b | ||
|
|
04e1710de1 | ||
|
|
330a90b0ab | ||
|
|
5c34556246 | ||
|
|
10a15ef33f | ||
|
|
4eebc81808 | ||
|
|
cd37ea0689 | ||
|
|
7e50c340c7 | ||
|
|
701f18e782 | ||
|
|
66a21db504 | ||
|
|
0290ffd26e | ||
|
|
b5cfb53d78 | ||
|
|
252b2f71a0 | ||
|
|
262951ece4 | ||
|
|
874160480f | ||
|
|
79d6b8ab22 | ||
|
|
04228ecee9 | ||
|
|
3cf8bd5928 | ||
|
|
c86d791d44 | ||
|
|
ec9342ced1 | ||
|
|
23ba8a1386 | ||
|
|
4726873b57 | ||
|
|
90ea2fc067 | ||
|
|
175fcb9415 | ||
|
|
da2a0f507b | ||
|
|
e771938672 | ||
|
|
08e2bb558a | ||
|
|
af304f14f4 | ||
|
|
389cc296c2 | ||
|
|
9f193904b5 | ||
|
|
17e6e3ec11 | ||
|
|
b969ab7deb | ||
|
|
50b355aaea | ||
|
|
b9424922a2 | ||
|
|
14bde9f31f | ||
|
|
a3ccae5915 | ||
|
|
e2cc55a4e3 | ||
|
|
724f24335a | ||
|
|
33a63de4f1 | ||
|
|
7ab0aee44a | ||
|
|
3e786e0ea9 | ||
|
|
994793acea | ||
|
|
5b03125150 | ||
|
|
c36b26878f | ||
|
|
f8822de6fe | ||
|
|
97fe8095bd | ||
|
|
212d35221a | ||
|
|
570b02a760 | ||
|
|
c89914e63d | ||
|
|
929fefbef4 | ||
|
|
030b020add | ||
|
|
9100c54244 | ||
|
|
cab21f9834 | ||
|
|
b707007e7d | ||
|
|
a4c5d570ae | ||
|
|
3dc0e84351 | ||
|
|
993d93b067 | ||
|
|
d269989c50 | ||
|
|
e17721b4bd | ||
|
|
f37bebaacd | ||
|
|
f287dab16d | ||
|
|
31fe300a1e | ||
|
|
f1faea26e5 | ||
|
|
422b9907cf | ||
|
|
24bffcde80 | ||
|
|
b7c76b5a53 | ||
|
|
c7619e5b52 | ||
|
|
3d05574d08 | ||
|
|
6e8455fde6 | ||
|
|
c6c8577b6f | ||
|
|
108312e8c0 | ||
|
|
060f21f83d | ||
|
|
4d904e6f70 | ||
|
|
6968e2edff | ||
|
|
300f65c22e | ||
|
|
5ecf240c26 | ||
|
|
9536f2e418 | ||
|
|
645285ca5c | ||
|
|
c7fd01a644 | ||
|
|
61c0c31992 | ||
|
|
e2ee02a71d | ||
|
|
4e283322ea | ||
|
|
b5b452e585 | ||
|
|
138abdcab1 | ||
|
|
bf661c4355 | ||
|
|
085ca323e2 | ||
|
|
c22b28f312 | ||
|
|
9b5ef4c263 | ||
|
|
3cffe74965 | ||
|
|
759ba5143f | ||
|
|
9ebff2287c | ||
|
|
e492fbde21 | ||
|
|
7255556819 | ||
|
|
c718b8f32a | ||
|
|
07b66dd5d0 | ||
|
|
2e4686fada | ||
|
|
e2544feeaf | ||
|
|
970bf77492 | ||
|
|
0145f454b7 | ||
|
|
e984d9b68f | ||
|
|
1cd6773ded | ||
|
|
0445aa707b | ||
|
|
cc96d160a4 | ||
|
|
4331ab374e | ||
|
|
61005360a5 | ||
|
|
335c44385a | ||
|
|
88e9ad3df2 | ||
|
|
2b19414598 | ||
|
|
b00cd00259 | ||
|
|
6097431021 | ||
|
|
55a9356681 | ||
|
|
8aee946682 | ||
|
|
523d3a331a | ||
|
|
397107b611 | ||
|
|
342af3deba | ||
|
|
dc92498cd0 | ||
|
|
364209a27d | ||
|
|
46d741a2ed | ||
|
|
b5be9ee3f0 | ||
|
|
e0f17a48d5 | ||
|
|
fa27856bc0 | ||
|
|
befaf5d387 | ||
|
|
9571e992d9 | ||
|
|
e40bed5be2 | ||
|
|
442249c3a1 | ||
|
|
8b8d8bcc5b | ||
|
|
d2152cb48e | ||
|
|
81fc0d47ac | ||
|
|
8450f77443 | ||
|
|
0ccc34d860 | ||
|
|
5b6452d89e | ||
|
|
d61d260ef1 | ||
|
|
1125b51f27 | ||
|
|
bf552e22f1 | ||
|
|
b740142668 | ||
|
|
d23f829559 | ||
|
|
85bf215dc6 | ||
|
|
9db04abd1c | ||
|
|
156f645576 | ||
|
|
d3b654bdb0 | ||
|
|
7ed078ce31 | ||
|
|
3c6df48b8b | ||
|
|
833cd497bb | ||
|
|
625f3d3a34 | ||
|
|
380157f9cc | ||
|
|
74c005d39c | ||
|
|
7b2716dab1 | ||
|
|
906c090bd4 | ||
|
|
27ad9a24eb | ||
|
|
83fdecbdd1 | ||
|
|
6c1a76b901 | ||
|
|
4a5847784a | ||
|
|
896fb8138b | ||
|
|
678c445799 | ||
|
|
36428e4735 | ||
|
|
10da784d53 | ||
|
|
c64cdb5dad | ||
|
|
52b68a0a1b | ||
|
|
ee1742af1f | ||
|
|
6315283a3a | ||
|
|
d8b3f215a2 | ||
|
|
3838de1d97 | ||
|
|
f6ddba52d8 | ||
|
|
f7b35c3b79 | ||
|
|
8dd65b709d | ||
|
|
3064fe8ad9 | ||
|
|
287a329a4d | ||
|
|
923b2469f9 | ||
|
|
55bb4cf2fa | ||
|
|
5105be338a | ||
|
|
20e36c8a00 | ||
|
|
9dd4f690a8 | ||
|
|
9a15b5ebdc | ||
|
|
5c003d38be | ||
|
|
95ab80b8f9 | ||
|
|
079468c609 | ||
|
|
7c98ca7134 | ||
|
|
fbf9c345b9 | ||
|
|
491e73eade | ||
|
|
e73773672b | ||
|
|
98d022ee49 | ||
|
|
abca8ce433 | ||
|
|
2dcd4aeaad | ||
|
|
58d4e3384e | ||
|
|
76aed8a119 | ||
|
|
37b8ffb7d2 | ||
|
|
af53d79e5e | ||
|
|
c3aa11abeb | ||
|
|
8e1454b3ab | ||
|
|
1c32f4eaee | ||
|
|
cd15b9197b | ||
|
|
afc18236c2 | ||
|
|
9bac4df02a | ||
|
|
fb1c8eb80d | ||
|
|
19f3a24257 | ||
|
|
5bf95d8b87 | ||
|
|
7d7bb0d52a | ||
|
|
0a459d256d | ||
|
|
6185edcc1b | ||
|
|
814c726323 | ||
|
|
db4c9c1844 | ||
|
|
a96820af36 | ||
|
|
f2c6829cd9 | ||
|
|
790429e286 | ||
|
|
b42366cad2 | ||
|
|
122db6330e |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -26,6 +26,7 @@ user/plugins/*
|
||||
user/themes/*
|
||||
!user/themes/.*
|
||||
user/localhost/config/security.yaml
|
||||
user/config/security.yaml
|
||||
|
||||
# OS Generated
|
||||
.DS_Store*
|
||||
|
||||
226
CHANGELOG.md
226
CHANGELOG.md
@@ -1,3 +1,225 @@
|
||||
# v1.2.0
|
||||
## 03/31/2017
|
||||
|
||||
1. [](#new)
|
||||
* Added file upload for user avatar in user/admin blueprint
|
||||
1. [](#improved)
|
||||
* Analysis fixes
|
||||
* Switched to stable composer lib versions
|
||||
|
||||
# v1.2.0-rc.3
|
||||
## 03/22/2017
|
||||
|
||||
1. [](#new)
|
||||
* Refactored Page re-ordering to handle all siblings at once
|
||||
* Added `language_codes` to Twig init to allow for easy language name/code/native-name lookup
|
||||
1. [](#improved)
|
||||
* Added an _Admin Overrides_ section with option to choose the order of children in Pages Management
|
||||
1. [](#bugfix)
|
||||
* Fixed loading issues with improperly named themes (use old broken method first) [#1373](https://github.com/getgrav/grav/issues/1373)
|
||||
* Simplified modular/twig processing logic and fixed an issue with system process config [#1351](https://github.com/getgrav/grav/issues/1351)
|
||||
* Cleanup package files via GPM install to make them more windows-friendly [#1361](https://github.com/getgrav/grav/pull/1361)
|
||||
* Fix for page-level debugger override changing the option site-wide
|
||||
* Allow `url()` twig function to pass-through external links
|
||||
|
||||
# v1.2.0-rc.2
|
||||
## 03/17/2017
|
||||
|
||||
1. [](#improved)
|
||||
* Updated vendor libraries to latest
|
||||
* Added the ability to disable debugger on a per-page basis with `debugger: false` in page frontmatter
|
||||
1. [](#bugfix)
|
||||
* Fixed an issue with theme inheritance and hyphenated base themes [#1353](https://github.com/getgrav/grav/issues/1353)
|
||||
* Fixed an issue when trying to use an `@2x` derivative on a non-image media file [#1341](https://github.com/getgrav/grav/issues/1341)
|
||||
|
||||
# v1.2.0-rc.1
|
||||
## 03/13/2017
|
||||
|
||||
1. [](#new)
|
||||
* Added default setting to only allow `direct-installs` from official GPM. Can be configured in `system.yaml`
|
||||
* Added a new `Utils::isValidUrl()` method
|
||||
* Added optional parameter to `|markdown(false)` filter to toggle block/line processing (default|true = `block`)
|
||||
* Added new `Page::folderExists()` method
|
||||
1. [](#improved)
|
||||
* `Twig::evaluate()` now takes current environment and context into account
|
||||
* Genericized `direct-install` so it can be called via Admin plugin
|
||||
1. [](#bugfix)
|
||||
* Fixed a minor bug in Number validation [#1329](https://github.com/getgrav/grav/issues/1329)
|
||||
* Fixed exception when trying to find user account and there is no `user://accounts` folder
|
||||
* Fixed issue when setting `Page::expires(0)` [Admin #1009](https://github.com/getgrav/grav-plugin-admin/issues/1009)
|
||||
* Removed ID from `nonce_field()` Twig function causing validation errors [Form #115](https://github.com/getgrav/grav-plugin-form/issues/115)
|
||||
|
||||
# v1.1.17
|
||||
## 02/17/2017
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fix for double extensions getting added during some redirects [#1307](https://github.com/getgrav/grav/issues/1307)
|
||||
* Fix syntax error in PHP 5.3. Move the version check before requiring the autoloaded deps
|
||||
* Fix Whoops displaying error page if there is PHP core warning or error [Admin #980](https://github.com/getgrav/grav-plugin-admin/issues/980)
|
||||
|
||||
# v1.1.16
|
||||
## 02/10/2017
|
||||
|
||||
1. [](#new)
|
||||
* Exposed the Pages cache ID for use by plugins (e.g. Form) via `Pages::getPagesCacheId()`
|
||||
* Added `Languages::resetFallbackPageExtensions()` regarding [#1276](https://github.com/getgrav/grav/pull/1276)
|
||||
1. [](#improved)
|
||||
* Allowed CLI to use non-volatile cache drivers for better integration with CLI and Web caches
|
||||
* Added Gantry5-compatible query information to Caddy configuration
|
||||
* Added some missing docblocks and type-hints
|
||||
* Various code cleanups (return types, missing variables in doclbocks, etc.)
|
||||
1. [](#bugfix)
|
||||
* Fix blueprints slug validation [https://github.com/getgrav/grav-plugin-admin/issues/955](https://github.com/getgrav/grav-plugin-admin/issues/955)
|
||||
|
||||
# v1.1.15
|
||||
## 01/30/2017
|
||||
|
||||
1. [](#new)
|
||||
* Added a new `Collection::merge()` method to allow merging of multiple collections [#1258](https://github.com/getgrav/grav/pull/1258)
|
||||
* Added [OpenCollective](https://opencollective.com/grav) backer/sponsor info to `README.md`
|
||||
1. [](#improved)
|
||||
* Add an additional parameter to GPM::findPackage to avoid throwing an exception, for use in Twig [#1008](https://github.com/getgrav/grav/issues/1008)
|
||||
* Skip symlinks if found while clearing cache [#1269](https://github.com/getgrav/grav/issues/1269)
|
||||
1. [](#bugfix)
|
||||
* Fixed an issue when page collection with header-based `sort.by` returns an array [#1264](https://github.com/getgrav/grav/issues/1264)
|
||||
* Fix `Response` object to handle `303` redirects when `open_basedir` in effect [#1267](https://github.com/getgrav/grav/issues/1267)
|
||||
* Silence `E_WARNING: Zend OPcache API is restricted by "restrict_api" configuration directive`
|
||||
|
||||
# v1.1.14
|
||||
## 01/18/2017
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed `Page::collection()` returning array and not Collection object when header variable did not exist
|
||||
* Revert `Content-Encoding: identity` fix, and let you set `cache: allow_webserver_gzip:` option to switch to `identity` [#548](https://github.com/getgrav/grav/issues/548)
|
||||
|
||||
# v1.1.13
|
||||
## 01/17/2017
|
||||
|
||||
1. [](#new)
|
||||
* Added new `never_cache_twig` page option in `system.yaml` and frontmatter. Allows dynamic Twig logic in regular and modular Twig templates [#1244](https://github.com/getgrav/grav/pull/1244)
|
||||
1. [](#improved)
|
||||
* Several improvements to aid theme development [#232](https://github.com/getgrav/grav/pull/1232)
|
||||
* Added `hash` cache check option and made dropdown more descriptive [Admin #923](https://github.com/getgrav/grav-plugin-admin/issues/923)
|
||||
1. [](#bugfix)
|
||||
* Fixed cross volume file system operations [#635](https://github.com/getgrav/grav/issues/635)
|
||||
* Fix issue with pages folders validation not accepting uppercase letters
|
||||
* Fix renaming the folder name if the page, in the default language, had a custom slug set in its header
|
||||
* Fixed issue with `Content-Encoding: none`. It should really be `Content-Encoding: identity` instead
|
||||
* Fixed broken `hash` method on page modifications detection
|
||||
* Fixed issue with multi-lang pages not caching independently without unique `.md` file [#1211](https://github.com/getgrav/grav/issues/1211)
|
||||
* Fixed all `$_GET` parameters missing in Nginx (please update your nginx.conf) [#1245](https://github.com/getgrav/grav/issues/1245)
|
||||
* Fixed issue in trying to process broken symlink [#1254](https://github.com/getgrav/grav/issues/1254)
|
||||
|
||||
# v1.1.12
|
||||
## 12/26/2016
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed issue with JSON calls throwing errors due to debugger enabled [#1227](https://github.com/getgrav/grav/issues/1227)
|
||||
|
||||
# v1.1.11
|
||||
## 12/22/2016
|
||||
|
||||
1. [](#improved)
|
||||
* Fall back properly to HTML if template type not found
|
||||
1. [](#bugfix)
|
||||
* Fix issue with modular pages folders validation [#900](https://github.com/getgrav/grav-plugin-admin/issues/900)
|
||||
|
||||
# v1.1.10
|
||||
## 12/21/2016
|
||||
|
||||
1. [](#improved)
|
||||
* Improve detection of home path. Also allow `~/.grav` on Windows, drop `ConsoleTrait::isWindows()` method, used only for that [#1204](https://github.com/getgrav/grav/pull/1204)
|
||||
* Reworked PHP CLI router [#1219](https://github.com/getgrav/grav/pull/1219)
|
||||
* More robust theme/plugin logic in `bin/gpm direct-install`
|
||||
1. [](#bugfix)
|
||||
* Fixed case where extracting a package would cause an error during rename
|
||||
* Fix issue with using `Yaml::parse` direcly on a filename, now deprecated
|
||||
* Add pattern for frontend validation of folder slugs [#891](https://github.com/getgrav/grav-plugin-admin/issues/891)
|
||||
* Fix issue with Inflector when translation is disabled [SimpleSearch #87](https://github.com/getgrav/grav-plugin-simplesearch/issues/87)
|
||||
* Explicitly expose `array_unique` Twig filter [Admin #897](https://github.com/getgrav/grav-plugin-admin/issues/897)
|
||||
|
||||
# v1.1.9
|
||||
## 12/13/2016
|
||||
|
||||
1. [](#new)
|
||||
* RC released as stable
|
||||
1. [](#improved)
|
||||
* Better error handling in cache clear
|
||||
* YAML syntax fixes for the future compatibility
|
||||
* Added new parameter `remove` for `onBeforeCacheClear` event
|
||||
* Add support for calling Media object as function to get medium by filename
|
||||
1. [](#bugfix)
|
||||
* Added checks before accessing admin reference during `Page::blueprints()` call. Allows to access `page.blueprints` from Twig in the frontend
|
||||
|
||||
# v1.1.9-rc.3
|
||||
## 12/07/2016
|
||||
|
||||
1. [](#new)
|
||||
* Add `ignore_empty` property to be used on array fields, if positive only save options with a value
|
||||
* Use new `permissions` field in user account
|
||||
* Add `range(int start, int end, int step)` twig function to generate an array of numbers between start and end, inclusive
|
||||
* New retina Media image derivatives array support (``) [#1147](https://github.com/getgrav/grav/pull/1147)
|
||||
* Added stream support for images (``)
|
||||
* Added stream support for links (`[Download PDF](user://data/pdf/my.pdf)`)
|
||||
* Added new `onBeforeCacheClear` event to add custom paths to cache clearing process
|
||||
1. [](#improved)
|
||||
* Added alias `selfupdate` to the `self-upgrade` `bin/gpm` CLI command
|
||||
* Synced `webserver-configs/htaccess.txt` with `.htaccess`
|
||||
* Use permissions field in group details.
|
||||
* Updated vendor libraries
|
||||
* Added a warning on GPM update to update Grav first if needed [#1194](https://github.com/getgrav/grav/pull/1194)
|
||||
1. [](#bugfix)
|
||||
* Fix page collections problem with `@page.modular` [#1178](https://github.com/getgrav/grav/pull/1178)
|
||||
* Fix issue with using a multiple taxonomy filter of which one had no results, thanks to @hughbris [#1184](https://github.com/getgrav/grav/issues/1184)
|
||||
* Fix saving permissions in group
|
||||
* Fixed issue with redirect of a page getting moved to a different location
|
||||
|
||||
# v1.1.9-rc.2
|
||||
## 11/26/2016
|
||||
|
||||
1. [](#new)
|
||||
* Added two new sort order options for pages: `publish_date` and `unpublish_date` [#1173](https://github.com/getgrav/grav/pull/1173))
|
||||
1. [](#improved)
|
||||
* Multisite: Create image cache folder if it doesn't exist
|
||||
* Add 2 new language values for French [#1174](https://github.com/getgrav/grav/issues/1174)
|
||||
1. [](#bugfix)
|
||||
* Fixed issue when we have a meta file without corresponding media [#1179](https://github.com/getgrav/grav/issues/1179)
|
||||
* Update class namespace for Admin class [Admin #874](https://github.com/getgrav/grav-plugin-admin/issues/874)
|
||||
|
||||
# v1.1.9-rc.1
|
||||
## 11/09/2016
|
||||
|
||||
1. [](#new)
|
||||
* Added a `CompiledJsonFile` object to better handle Json files.
|
||||
* Added Base32 encode/decode class
|
||||
* Added a new `User::find()` method
|
||||
1. [](#improved)
|
||||
* Moved `messages` object into core Grav from login plugin
|
||||
* Added `getTaxonomyItemKeys` to the Taxonomy object [#1124](https://github.com/getgrav/grav/issues/1124)
|
||||
* Added a `redirect_me` Twig function [#1124](https://github.com/getgrav/grav/issues/1124)
|
||||
* Added a Caddyfile for newer Caddy versions [#1115](https://github.com/getgrav/grav/issues/1115)
|
||||
* Allow to override sorting flags for page header-based or default ordering. If the `intl` PHP extension is loaded, only these flags are available: https://secure.php.net/manual/en/collator.asort.php. Otherwise, you can use the PHP standard sorting flags (https://secure.php.net/manual/en/array.constants.php) [#1169](https://github.com/getgrav/grav/issues/1169)
|
||||
1. [](#bugfix)
|
||||
* Fixed an issue with site redirects/routes, not processing with extension (.html, .json, etc.)
|
||||
* Don't truncate HTML if content length is less than summary size [#1125](https://github.com/getgrav/grav/issues/1125)
|
||||
* Return max available number when calling random() on a collection passing an int > available items [#1135](https://github.com/getgrav/grav/issues/1135)
|
||||
* Use correct ratio when applying image filters to image alternatives [#1147](https://github.com/getgrav/grav/issues/1147)
|
||||
* Fixed URI path in multi-site when query parameters were used in front page
|
||||
|
||||
# v1.1.8
|
||||
## 10/22/2016
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed warning with unset `ssl` option when using GPM [#1132](https://github.com/getgrav/grav/issues/1132)
|
||||
|
||||
# v1.1.7
|
||||
## 10/22/2016
|
||||
|
||||
1. [](#improved)
|
||||
* Improved the capabilities of Image derivatives [#1107](https://github.com/getgrav/grav/pull/1107)
|
||||
1. [](#bugfix)
|
||||
* Only pass verify_peer settings to cURL and fopen if the setting is disabled [#1120](https://github.com/getgrav/grav/issues/1120)
|
||||
|
||||
# v1.1.6
|
||||
## 10/19/2016
|
||||
|
||||
@@ -25,7 +247,7 @@
|
||||
1. [](#bugfix)
|
||||
* Fixed missing `progress` method in the DirectInstall Command
|
||||
* `Response` class now handles better unsuccessful requests such as 404 and 401
|
||||
* Fixed saving of `external` page types [admin #789](https://github.com/getgrav/grav-plugin-admin/issues/789)
|
||||
* Fixed saving of `external` page types [Admin #789](https://github.com/getgrav/grav-plugin-admin/issues/789)
|
||||
* Fixed issue deleting parent folder of folder with `param_sep` in the folder name [admin #796](https://github.com/getgrav/grav-plugin-admin/issues/796)
|
||||
* Fixed an issue with streams in `bin/plugin`
|
||||
* Fixed `jpeg` file format support in Media
|
||||
@@ -830,7 +1052,7 @@
|
||||
* Added new `onImageMediumSaved()` event (useful for post-image processing)
|
||||
* Added `Vary: Accept-Encoding` option
|
||||
2. [](#improved)
|
||||
* Multilang-safe delimeter position
|
||||
* Multilang-safe delimiter position
|
||||
* Refactored Twig classes and added optional umask setting
|
||||
* Removed `pageinit()` timing
|
||||
* `Page->routable()` now takes `published()` state into account
|
||||
|
||||
@@ -110,7 +110,7 @@ Good pull requests - patches, improvements, new features - are a fantastic
|
||||
help. They should remain focused in scope and avoid containing unrelated
|
||||
commits.
|
||||
|
||||
**Please ask first** in Gitter or in the Forum before embarking on any significant pull request (e.g.
|
||||
**Please ask first** in [Slack](https://getgrav.org/slack) or in the Forum before embarking on any significant pull request (e.g.
|
||||
implementing features, refactoring code..),
|
||||
otherwise you risk spending a lot of time working on something that the
|
||||
project's developers might not want to merge into the project.
|
||||
|
||||
99
README.md
99
README.md
@@ -1,6 +1,6 @@
|
||||
#  Grav
|
||||
|
||||
[](https://insight.sensiolabs.com/projects/cfd20465-d0f8-4a0a-8444-467f5b5f16ad) [](https://gitter.im/getgrav/grav?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://travis-ci.org/getgrav/grav)
|
||||
[](https://insight.sensiolabs.com/projects/cfd20465-d0f8-4a0a-8444-467f5b5f16ad) [](https://chat.getgrav.org) [](https://travis-ci.org/getgrav/grav) [](#backers) [](#sponsors)
|
||||
|
||||
Grav is a **Fast**, **Simple**, and **Flexible**, file-based Web-platform. There is **Zero** installation required. Just extract the ZIP archive, and you are already up and running. It follows similar principles to other flat-file CMS platforms, but has a different design philosophy than most. Grav comes with a powerful **Package Management System** to allow for simple installation and upgrading of plugins and themes, as well as simple updating of Grav itself.
|
||||
|
||||
@@ -18,8 +18,8 @@ The underlying architecture of Grav is designed to use well-established and _bes
|
||||
|
||||
# Requirements
|
||||
|
||||
- PHP 5.5.9 or higher. Check the [required modules list](http://learn.getgrav.org/basics/requirements#php-requirements)
|
||||
- Check the [Apache](http://learn.getgrav.org/basics/requirements#apache-requirements) or [IIS](http://learn.getgrav.org/basics/requirements#iis-requirements) requirements
|
||||
- PHP 5.5.9 or higher. Check the [required modules list](https://learn.getgrav.org/basics/requirements#php-requirements)
|
||||
- Check the [Apache](https://learn.getgrav.org/basics/requirements#apache-requirements) or [IIS](https://learn.getgrav.org/basics/requirements#iis-requirements) requirements
|
||||
|
||||
# QuickStart
|
||||
|
||||
@@ -27,7 +27,7 @@ These are the options to get Grav:
|
||||
|
||||
### Downloading a Grav Package
|
||||
|
||||
You can download a **ready-built** package from the [Downloads page on http://getgrav.org](http://getgrav.org/downloads)
|
||||
You can download a **ready-built** package from the [Downloads page on https://getgrav.org](https://getgrav.org/downloads)
|
||||
|
||||
### With Composer
|
||||
|
||||
@@ -45,17 +45,17 @@ $ composer create-project getgrav/grav ~/webroot/grav
|
||||
$ git clone https://github.com/getgrav/grav.git
|
||||
```
|
||||
|
||||
2. Install the **plugin** and **theme dependencies** by using the [Grav CLI application](http://learn.getgrav.org/advanced/grav-cli) `bin/grav`:
|
||||
2. Install the **plugin** and **theme dependencies** by using the [Grav CLI application](https://learn.getgrav.org/advanced/grav-cli) `bin/grav`:
|
||||
```
|
||||
$ cd ~/webroot/grav
|
||||
$ bin/grav install
|
||||
```
|
||||
|
||||
Check out the [install procedures](http://learn.getgrav.org/basics/installation) for more information.
|
||||
Check out the [install procedures](https://learn.getgrav.org/basics/installation) for more information.
|
||||
|
||||
# Adding Functionality
|
||||
|
||||
You can download [plugins](http://getgrav.org/downloads/plugins) or [themes](http://getgrav.org/downloads/themes) manually from the appropriate tab on the [Downloads page on http://getgrav.org](http://getgrav.org/downloads), but the preferred solution is to use the [Grav Package Manager](http://learn.getgrav.org/advanced/grav-gpm) or `GPM`:
|
||||
You can download [plugins](https://getgrav.org/downloads/plugins) or [themes](https://getgrav.org/downloads/themes) manually from the appropriate tab on the [Downloads page on https://getgrav.org](https://getgrav.org/downloads), but the preferred solution is to use the [Grav Package Manager](https://learn.getgrav.org/advanced/grav-gpm) or `GPM`:
|
||||
|
||||
```
|
||||
$ bin/gpm index
|
||||
@@ -69,7 +69,7 @@ $ bin/gpm install <plugin/theme>
|
||||
|
||||
# Updating
|
||||
|
||||
To update Grav you should use the [Grav Package Manager](http://learn.getgrav.org/advanced/grav-gpm) or `GPM`:
|
||||
To update Grav you should use the [Grav Package Manager](https://learn.getgrav.org/advanced/grav-gpm) or `GPM`:
|
||||
|
||||
```
|
||||
$ bin/gpm selfupgrade
|
||||
@@ -97,17 +97,86 @@ What you mainly want to know is that:
|
||||
|
||||
# Getting Started
|
||||
|
||||
* [What is Grav?](http://learn.getgrav.org/basics/what-is-grav)
|
||||
* [Install](http://learn.getgrav.org/basics/installation) Grav in few seconds
|
||||
* Understand the [Configuration](http://learn.getgrav.org/basics/grav-configuration)
|
||||
* Take a peek at our available free [Skeletons](http://getgrav.org/downloads/skeletons)
|
||||
* If you have questions, jump on our [Gitter Room](https://gitter.im/getgrav/grav)!
|
||||
* [What is Grav?](https://learn.getgrav.org/basics/what-is-grav)
|
||||
* [Install](https://learn.getgrav.org/basics/installation) Grav in few seconds
|
||||
* Understand the [Configuration](https://learn.getgrav.org/basics/grav-configuration)
|
||||
* Take a peek at our available free [Skeletons](https://getgrav.org/downloads/skeletons)
|
||||
* If you have questions, jump on our [Slack Room](https://getgrav.org/slack)!
|
||||
* Have fun!
|
||||
|
||||
# Exploring More
|
||||
|
||||
* Have a look at our [Basic Tutorial](http://learn.getgrav.org/basics/basic-tutorial)
|
||||
* Dive into more [advanced](http://learn.getgrav.org/advanced) functions
|
||||
* Have a look at our [Basic Tutorial](https://learn.getgrav.org/basics/basic-tutorial)
|
||||
* Dive into more [advanced](https://learn.getgrav.org/advanced) functions
|
||||
|
||||
# Backers
|
||||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/grav#backer)]
|
||||
|
||||
<a href="https://opencollective.com/grav/backer/0/website" target="_blank"><img src="https://opencollective.com/grav/backer/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/1/website" target="_blank"><img src="https://opencollective.com/grav/backer/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/2/website" target="_blank"><img src="https://opencollective.com/grav/backer/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/3/website" target="_blank"><img src="https://opencollective.com/grav/backer/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/4/website" target="_blank"><img src="https://opencollective.com/grav/backer/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/5/website" target="_blank"><img src="https://opencollective.com/grav/backer/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/6/website" target="_blank"><img src="https://opencollective.com/grav/backer/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/7/website" target="_blank"><img src="https://opencollective.com/grav/backer/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/8/website" target="_blank"><img src="https://opencollective.com/grav/backer/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/9/website" target="_blank"><img src="https://opencollective.com/grav/backer/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/10/website" target="_blank"><img src="https://opencollective.com/grav/backer/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/11/website" target="_blank"><img src="https://opencollective.com/grav/backer/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/12/website" target="_blank"><img src="https://opencollective.com/grav/backer/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/13/website" target="_blank"><img src="https://opencollective.com/grav/backer/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/14/website" target="_blank"><img src="https://opencollective.com/grav/backer/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/15/website" target="_blank"><img src="https://opencollective.com/grav/backer/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/16/website" target="_blank"><img src="https://opencollective.com/grav/backer/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/17/website" target="_blank"><img src="https://opencollective.com/grav/backer/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/18/website" target="_blank"><img src="https://opencollective.com/grav/backer/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/19/website" target="_blank"><img src="https://opencollective.com/grav/backer/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/20/website" target="_blank"><img src="https://opencollective.com/grav/backer/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/21/website" target="_blank"><img src="https://opencollective.com/grav/backer/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/22/website" target="_blank"><img src="https://opencollective.com/grav/backer/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/23/website" target="_blank"><img src="https://opencollective.com/grav/backer/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/24/website" target="_blank"><img src="https://opencollective.com/grav/backer/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/25/website" target="_blank"><img src="https://opencollective.com/grav/backer/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/26/website" target="_blank"><img src="https://opencollective.com/grav/backer/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/27/website" target="_blank"><img src="https://opencollective.com/grav/backer/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/28/website" target="_blank"><img src="https://opencollective.com/grav/backer/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/29/website" target="_blank"><img src="https://opencollective.com/grav/backer/29/avatar.svg"></a>
|
||||
|
||||
|
||||
# Sponsors
|
||||
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/grav#sponsor)]
|
||||
|
||||
<a href="https://opencollective.com/grav/sponsor/0/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/1/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/2/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/3/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/4/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/5/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/6/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/7/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/8/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/9/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/10/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/11/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/12/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/13/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/14/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/15/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/16/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/17/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/18/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/19/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/20/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/21/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/22/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/23/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/24/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/25/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/26/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/27/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/28/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/sponsor/29/website" target="_blank"><img src="https://opencollective.com/grav/sponsor/29/avatar.svg"></a>
|
||||
|
||||
# License
|
||||
|
||||
|
||||
Binary file not shown.
@@ -8,7 +8,7 @@
|
||||
"require": {
|
||||
"php": ">=5.5.9",
|
||||
"twig/twig": "~1.24",
|
||||
"erusev/parsedown": "dev-master as 1.6.0",
|
||||
"erusev/parsedown": "~1.6",
|
||||
"erusev/parsedown-extra": "~0.7",
|
||||
"symfony/yaml": "~2.8",
|
||||
"symfony/console": "~2.8",
|
||||
@@ -19,7 +19,7 @@
|
||||
"filp/whoops": "~2.0",
|
||||
"matthiasmullie/minify": "^1.3",
|
||||
"monolog/monolog": "~1.0",
|
||||
"gregwar/image": "dev-master#72568cfbeb77515278f2ccb386fc344e874b7ae8",
|
||||
"gregwar/image": "~2.0",
|
||||
"donatj/phpuseragentparser": "~0.3",
|
||||
"pimple/pimple": "~3.0",
|
||||
"rockettheme/toolbox": "~1.0",
|
||||
|
||||
619
composer.lock
generated
619
composer.lock
generated
File diff suppressed because it is too large
Load Diff
15
index.php
15
index.php
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
namespace Grav;
|
||||
define('GRAV_PHP_MIN', '5.5.9');
|
||||
|
||||
// Ensure vendor libraries exist
|
||||
$autoload = __DIR__ . '/vendor/autoload.php';
|
||||
@@ -14,16 +15,22 @@ if (!is_file($autoload)) {
|
||||
die("Please run: <i>bin/grav install</i>");
|
||||
}
|
||||
|
||||
if (PHP_SAPI == 'cli-server') {
|
||||
if (!isset($_SERVER['PHP_CLI_ROUTER'])) {
|
||||
die("PHP webserver requires a router to run Grav, please use: <pre>php -S {$_SERVER["SERVER_NAME"]}:{$_SERVER["SERVER_PORT"]} system/router.php</pre>");
|
||||
}
|
||||
}
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
|
||||
// Register the auto-loader.
|
||||
$loader = require_once $autoload;
|
||||
|
||||
if (version_compare($ver = PHP_VERSION, $req = GRAV_PHP_MIN, '<')) {
|
||||
die(sprintf('You are running PHP %s, but Grav needs at least <strong>PHP %s</strong> to run.', $ver, $req));
|
||||
}
|
||||
|
||||
// Register the auto-loader.
|
||||
$loader = require_once $autoload;
|
||||
|
||||
// Set timezone to default, falls back to system if php.ini not set
|
||||
date_default_timezone_set(@date_default_timezone_get());
|
||||
|
||||
@@ -44,6 +51,6 @@ $grav = Grav::instance(
|
||||
try {
|
||||
$grav->process();
|
||||
} catch (\Exception $e) {
|
||||
$grav->fireEvent('onFatalException', new Event(['exception' => $e]));
|
||||
$grav->fireEvent('onFatalException', new Event(array('exception' => $e)));
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,13 @@ form:
|
||||
placeholder: PLUGIN_ADMIN.SITE_TITLE_PLACEHOLDER
|
||||
help: PLUGIN_ADMIN.SITE_TITLE_HELP
|
||||
|
||||
default_lang:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.SITE_DEFAULT_LANG
|
||||
size: x-small
|
||||
placeholder: PLUGIN_ADMIN.SITE_DEFAULT_LANG_PLACEHOLDER
|
||||
help: PLUGIN_ADMIN.SITE_DEFAULT_LANG_HELP
|
||||
|
||||
author.name:
|
||||
type: text
|
||||
size: large
|
||||
|
||||
@@ -102,7 +102,7 @@ form:
|
||||
|
||||
pages.order.by:
|
||||
type: select
|
||||
size: long
|
||||
size: large
|
||||
classes: fancy
|
||||
label: PLUGIN_ADMIN.DEFAULT_ORDERING
|
||||
help: PLUGIN_ADMIN.DEFAULT_ORDERING_HELP
|
||||
@@ -155,6 +155,7 @@ form:
|
||||
|
||||
pages.append_url_extension:
|
||||
type: text
|
||||
size: x-small
|
||||
placeholder: "e.g. .html"
|
||||
label: PLUGIN_ADMIN.APPEND_URL_EXT
|
||||
help: PLUGIN_ADMIN.APPEND_URL_EXT_HELP
|
||||
@@ -244,6 +245,17 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
pages.never_cache_twig:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.NEVER_CACHE_TWIG
|
||||
help: PLUGIN_ADMIN.NEVER_CACHE_TWIG_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
pages.frontmatter.process_twig:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.FRONTMATTER_PROCESS_TWIG
|
||||
@@ -461,14 +473,15 @@ form:
|
||||
|
||||
cache.check.method:
|
||||
type: select
|
||||
size: small
|
||||
size: medium
|
||||
classes: fancy
|
||||
label: PLUGIN_ADMIN.CACHE_CHECK_METHOD
|
||||
help: PLUGIN_ADMIN.CACHE_CHECK_METHOD_HELP
|
||||
options:
|
||||
file: File
|
||||
folder: Folder
|
||||
none: None
|
||||
file: Markdown + Yaml file timestamps
|
||||
folder: Folder timestamps
|
||||
hash: All files timestamps
|
||||
none: No timestamp checking
|
||||
|
||||
cache.driver:
|
||||
type: select
|
||||
@@ -487,6 +500,55 @@ form:
|
||||
wincache: WinCache
|
||||
redis: Redis
|
||||
|
||||
cache.prefix:
|
||||
type: text
|
||||
size: x-small
|
||||
label: PLUGIN_ADMIN.CACHE_PREFIX
|
||||
help: PLUGIN_ADMIN.CACHE_PREFIX_HELP
|
||||
placeholder: PLUGIN_ADMIN.CACHE_PREFIX_PLACEHOLDER
|
||||
|
||||
cache.cli_compatibility:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.CLI_COMPATIBILITY
|
||||
help: PLUGIN_ADMIN.CLI_COMPATIBILITY_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
cache.lifetime:
|
||||
type: text
|
||||
size: small
|
||||
append: NICETIME.SECOND_PLURAL
|
||||
label: PLUGIN_ADMIN.LIFETIME
|
||||
help: PLUGIN_ADMIN.LIFETIME_HELP
|
||||
validate:
|
||||
type: number
|
||||
|
||||
cache.gzip:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.GZIP_COMPRESSION
|
||||
help: PLUGIN_ADMIN.GZIP_COMPRESSION_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
cache.allow_webserver_gzip:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.ALLOW_WEBSERVER_GZIP
|
||||
help: PLUGIN_ADMIN.ALLOW_WEBSERVER_GZIP_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
cache.memcache.server:
|
||||
type: text
|
||||
size: medium
|
||||
@@ -536,32 +598,6 @@ form:
|
||||
help: PLUGIN_ADMIN.REDIS_PORT_HELP
|
||||
placeholder: "6379"
|
||||
|
||||
cache.prefix:
|
||||
type: text
|
||||
size: x-small
|
||||
label: PLUGIN_ADMIN.CACHE_PREFIX
|
||||
help: PLUGIN_ADMIN.CACHE_PREFIX_HELP
|
||||
placeholder: PLUGIN_ADMIN.CACHE_PREFIX_PLACEHOLDER
|
||||
|
||||
cache.lifetime:
|
||||
type: text
|
||||
size: small
|
||||
append: NICETIME.SECOND_PLURAL
|
||||
label: PLUGIN_ADMIN.LIFETIME
|
||||
help: PLUGIN_ADMIN.LIFETIME_HELP
|
||||
validate:
|
||||
type: number
|
||||
|
||||
cache.gzip:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.GZIP_COMPRESSION
|
||||
help: PLUGIN_ADMIN.GZIP_COMPRESSION_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
twig:
|
||||
type: section
|
||||
@@ -821,13 +857,12 @@ form:
|
||||
|
||||
fields:
|
||||
images.default_image_quality:
|
||||
type: text
|
||||
type: range
|
||||
append: '%'
|
||||
label: PLUGIN_ADMIN.DEFAULT_IMAGE_QUALITY
|
||||
help: PLUGIN_ADMIN.DEFAULT_IMAGE_QUALITY_HELP
|
||||
classes: x-small
|
||||
validate:
|
||||
type: number
|
||||
min: 1
|
||||
max: 100
|
||||
|
||||
@@ -1022,6 +1057,19 @@ form:
|
||||
fopen: PLUGIN_ADMIN.FOPEN
|
||||
curl: PLUGIN_ADMIN.CURL
|
||||
|
||||
gpm.official_gpm_only:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.GPM_OFFICIAL_ONLY
|
||||
highlight: auto
|
||||
help: PLUGIN_ADMIN.GPM_OFFICIAL_ONLY_HELP
|
||||
highlight: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
default: true
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
gpm.verify_peer:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.GPM_VERIFY_PEER
|
||||
@@ -1091,6 +1139,6 @@ form:
|
||||
custom_base_url:
|
||||
type: text
|
||||
size: medium
|
||||
placeholder: "e.g. http://localhost:8080"
|
||||
placeholder: "e.g. http://yoursite.com/yourpath"
|
||||
label: PLUGIN_ADMIN.CUSTOM_BASE_URL
|
||||
help: PLUGIN_ADMIN.CUSTOM_BASE_URL_HELP
|
||||
|
||||
@@ -2,9 +2,9 @@ title: PLUGIN_ADMIN.DEFAULT
|
||||
|
||||
rules:
|
||||
slug:
|
||||
pattern: "[a-zа-я][a-zа-я0-9_\-]+"
|
||||
min: 2
|
||||
max: 80
|
||||
pattern: '[a-zA-Zа-яA-Я0-9_\-]+'
|
||||
min: 1
|
||||
max: 200
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
@@ -117,29 +117,18 @@ form:
|
||||
title: PLUGIN_ADMIN.SETTINGS
|
||||
underline: true
|
||||
|
||||
ordering:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.FOLDER_NUMERIC_PREFIX
|
||||
help: PLUGIN_ADMIN.FOLDER_NUMERIC_PREFIX_HELP
|
||||
highlight: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
folder:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.FOLDER_NAME
|
||||
validate:
|
||||
type: slug
|
||||
rule: slug
|
||||
|
||||
route:
|
||||
type: select
|
||||
label: PLUGIN_ADMIN.PARENT
|
||||
classes: fancy
|
||||
data-options@: '\Grav\Common\Page\Pages::parentsRawRoutes'
|
||||
data-default@: '\Grav\Plugin\admin::rawRoute'
|
||||
data-default@: '\Grav\Plugin\Admin\Admin::rawRoute'
|
||||
options:
|
||||
'/': PLUGIN_ADMIN.DEFAULT_OPTION_ROOT
|
||||
|
||||
@@ -165,9 +154,20 @@ form:
|
||||
title: PLUGIN_ADMIN.ORDERING
|
||||
underline: true
|
||||
|
||||
ordering:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.FOLDER_NUMERIC_PREFIX
|
||||
help: PLUGIN_ADMIN.FOLDER_NUMERIC_PREFIX_HELP
|
||||
highlight: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
order:
|
||||
type: order
|
||||
label: PLUGIN_ADMIN.PAGE_ORDER
|
||||
label: PLUGIN_ADMIN.SORTABLE_PAGES
|
||||
sitemap:
|
||||
|
||||
overrides:
|
||||
@@ -224,6 +224,30 @@ form:
|
||||
twig: Twig
|
||||
use: keys
|
||||
|
||||
header.twig_first:
|
||||
type: toggle
|
||||
toggleable: true
|
||||
label: PLUGIN_ADMIN.TWIG_FIRST
|
||||
help: PLUGIN_ADMIN.TWIG_FIRST_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
header.never_cache_twig:
|
||||
type: toggle
|
||||
toggleable: true
|
||||
label: PLUGIN_ADMIN.NEVER_CACHE_TWIG
|
||||
help: PLUGIN_ADMIN.NEVER_CACHE_TWIG_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
header.child_type:
|
||||
type: select
|
||||
toggleable: true
|
||||
@@ -267,6 +291,18 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
header.debugger:
|
||||
type: toggle
|
||||
toggleable: true
|
||||
label: PLUGIN_ADMIN.DEBUGGER
|
||||
help: PLUGIN_ADMIN.DEBUGGER_HELP
|
||||
highlight: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
header.template:
|
||||
type: text
|
||||
toggleable: true
|
||||
@@ -278,6 +314,25 @@ form:
|
||||
toggleable: true
|
||||
help: PLUGIN_ADMIN.APPEND_URL_EXT_HELP
|
||||
|
||||
admin_only:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.ADMIN_SPECIFIC_OVERRIDES
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
|
||||
header.admin.children_display_order:
|
||||
type: select
|
||||
label: PLUGIN_ADMIN.ADMIN_CHILDREN_DISPLAY_ORDER
|
||||
help: PLUGIN_ADMIN.ADMIN_CHILDREN_DISPLAY_ORDER_HELP
|
||||
toggleable: true
|
||||
classes: fancy
|
||||
default: 'collection'
|
||||
options:
|
||||
'default': 'Ordered by Folder name (default)'
|
||||
'collection': 'Ordered by Collection definition'
|
||||
|
||||
|
||||
header.order_by:
|
||||
type: hidden
|
||||
|
||||
|
||||
@@ -3,12 +3,6 @@ title: PLUGIN_ADMIN:EXTERNAL
|
||||
type: default
|
||||
context: blueprints://pages
|
||||
|
||||
rules:
|
||||
slug:
|
||||
pattern: "[a-zа-я][a-zа-я0-9_\-]+"
|
||||
min: 2
|
||||
max: 80
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
rules:
|
||||
slug:
|
||||
pattern: "[a-z][a-z0-9_\-]+"
|
||||
min: 2
|
||||
max: 80
|
||||
pattern: '[a-zA-Zа-яA-Я0-9_\-]+'
|
||||
min: 1
|
||||
max: 200
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
@@ -22,7 +22,7 @@ form:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.FOLDER_NAME
|
||||
validate:
|
||||
type: slug
|
||||
rule: slug
|
||||
required: true
|
||||
|
||||
route:
|
||||
@@ -30,7 +30,7 @@ form:
|
||||
label: PLUGIN_ADMIN.PAGE
|
||||
classes: fancy
|
||||
data-options@: '\Grav\Common\Page\Pages::parentsRawRoutes'
|
||||
data-default@: '\Grav\Plugin\admin::rawRoute'
|
||||
data-default@: '\Grav\Plugin\Admin\Admin::rawRoute'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
rules:
|
||||
slug:
|
||||
pattern: "[a-z][a-z0-9_\-]+"
|
||||
min: 2
|
||||
max: 80
|
||||
pattern: '[a-zA-Zа-яA-Я0-9_\-]+'
|
||||
min: 1
|
||||
max: 200
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
@@ -71,7 +71,7 @@ form:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.FILENAME
|
||||
validate:
|
||||
type: slug
|
||||
rule: slug
|
||||
required: true
|
||||
|
||||
route:
|
||||
@@ -79,7 +79,7 @@ form:
|
||||
label: PLUGIN_ADMIN.PARENT
|
||||
classes: fancy
|
||||
data-options@: '\Grav\Common\Page\Pages::parentsRawRoutes'
|
||||
data-default@: '\Grav\Plugin\admin::rawRoute'
|
||||
data-default@: '\Grav\Plugin\Admin\Admin::rawRoute'
|
||||
options:
|
||||
'': PLUGIN_ADMIN.DEFAULT_OPTION_SELECT
|
||||
validate:
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
rules:
|
||||
slug:
|
||||
pattern: "[a-z][a-z0-9_\-]+"
|
||||
min: 2
|
||||
max: 80
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
@@ -12,6 +6,6 @@ form:
|
||||
label: PLUGIN_ADMIN.PARENT
|
||||
classes: fancy
|
||||
data-options@: '\Grav\Common\Page\Pages::parentsRawRoutes'
|
||||
data-default@: '\Grav\Plugin\admin::rawRoute'
|
||||
data-default@: '\Grav\Plugin\Admin\Admin::rawRoute'
|
||||
options:
|
||||
'/': PLUGIN_ADMIN.DEFAULT_OPTION_ROOT
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
rules:
|
||||
slug:
|
||||
pattern: "[a-z][a-z0-9_\-]+"
|
||||
min: 2
|
||||
max: 80
|
||||
pattern: '[a-zA-Zа-яA-Я0-9_\-]+'
|
||||
min: 1
|
||||
max: 200
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
@@ -24,7 +24,7 @@ form:
|
||||
label: PLUGIN_ADMIN.FOLDER_NAME
|
||||
help: PLUGIN_ADMIN.FOLDER_NAME_HELP
|
||||
validate:
|
||||
type: slug
|
||||
rule: slug
|
||||
required: true
|
||||
|
||||
route:
|
||||
@@ -32,7 +32,7 @@ form:
|
||||
label: PLUGIN_ADMIN.PARENT_PAGE
|
||||
classes: fancy
|
||||
data-options@: '\Grav\Common\Page\Pages::parentsRawRoutes'
|
||||
data-default@: '\Grav\Plugin\admin::getLastPageRoute'
|
||||
data-default@: '\Grav\Plugin\Admin\Admin::getLastPageRoute'
|
||||
options:
|
||||
'/': PLUGIN_ADMIN.DEFAULT_OPTION_ROOT
|
||||
validate:
|
||||
@@ -44,7 +44,7 @@ form:
|
||||
label: PLUGIN_ADMIN.PAGE_FILE
|
||||
help: PLUGIN_ADMIN.PAGE_FILE_HELP
|
||||
data-options@: '\Grav\Common\Page\Pages::types'
|
||||
data-default@: '\Grav\Plugin\admin::getLastPageName'
|
||||
data-default@: '\Grav\Plugin\Admin\Admin::getLastPageName'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
rules:
|
||||
slug:
|
||||
pattern: "[a-z][a-z0-9_\-]+"
|
||||
min: 2
|
||||
max: 80
|
||||
pattern: '[a-zA-Zа-яA-Я0-9_\-]+'
|
||||
min: 1
|
||||
max: 200
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
@@ -17,7 +17,7 @@ form:
|
||||
label: PLUGIN_ADMIN.FOLDER_NAME
|
||||
help: PLUGIN_ADMIN.FOLDER_NAME_HELP
|
||||
validate:
|
||||
type: slug
|
||||
rule: slug
|
||||
required: true
|
||||
|
||||
route:
|
||||
@@ -25,7 +25,7 @@ form:
|
||||
label: PLUGIN_ADMIN.PARENT_PAGE
|
||||
classes: fancy
|
||||
data-options@: '\Grav\Common\Page\Pages::parentsRawRoutes'
|
||||
data-default@: '\Grav\Plugin\admin::getLastPageRoute'
|
||||
data-default@: '\Grav\Plugin\Admin\Admin::getLastPageRoute'
|
||||
options:
|
||||
'/': PLUGIN_ADMIN.DEFAULT_OPTION_ROOT
|
||||
validate:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
rules:
|
||||
slug:
|
||||
pattern: "[a-z][a-z0-9_\-]+"
|
||||
min: 2
|
||||
max: 80
|
||||
pattern: '[a-zA-Zа-яA-Я0-9_\-]+'
|
||||
min: 1
|
||||
max: 200
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
@@ -71,7 +71,7 @@ form:
|
||||
label: PLUGIN_ADMIN.FOLDER_NAME
|
||||
help: PLUGIN_ADMIN.FOLDER_NAME_HELP
|
||||
validate:
|
||||
type: slug
|
||||
rule: slug
|
||||
required: true
|
||||
|
||||
route:
|
||||
@@ -79,7 +79,7 @@ form:
|
||||
label: PLUGIN_ADMIN.PARENT
|
||||
classes: fancy
|
||||
data-options@: '\Grav\Common\Page\Pages::parentsRawRoutes'
|
||||
data-default@: '\Grav\Plugin\admin::rawRoute'
|
||||
data-default@: '\Grav\Plugin\Admin\Admin::rawRoute'
|
||||
options:
|
||||
'/': PLUGIN_ADMIN.DEFAULT_OPTION_ROOT
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
title: Site
|
||||
title: Account
|
||||
form:
|
||||
validation: loose
|
||||
|
||||
@@ -8,6 +8,13 @@ form:
|
||||
type: userinfo
|
||||
size: large
|
||||
|
||||
avatar:
|
||||
type: file
|
||||
size: large
|
||||
destination: 'user://accounts/avatars'
|
||||
multiple: false
|
||||
random_name: true
|
||||
|
||||
content:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.ACCOUNT
|
||||
@@ -56,7 +63,7 @@ form:
|
||||
label: PLUGIN_ADMIN.LANGUAGE
|
||||
size: medium
|
||||
classes: fancy
|
||||
data-options@: '\Grav\Plugin\admin::adminLanguages'
|
||||
data-options@: '\Grav\Plugin\Admin\Admin::adminLanguages'
|
||||
default: 'en'
|
||||
help: PLUGIN_ADMIN.LANGUAGE_HELP
|
||||
|
||||
@@ -77,16 +84,9 @@ form:
|
||||
validate:
|
||||
type: commalist
|
||||
|
||||
access.admin:
|
||||
type: array
|
||||
label: PLUGIN_ADMIN.ADMIN_ACCESS
|
||||
multiple: false
|
||||
validate:
|
||||
type: array
|
||||
|
||||
access.site:
|
||||
type: array
|
||||
label: PLUGIN_ADMIN.SITE_ACCESS
|
||||
multiple: false
|
||||
access:
|
||||
type: permissions
|
||||
label: PLUGIN_ADMIN.PERMISSIONS
|
||||
ignore_empty: true
|
||||
validate:
|
||||
type: array
|
||||
|
||||
@@ -29,16 +29,9 @@ form:
|
||||
size: small
|
||||
label: PLUGIN_ADMIN_PRO.ICON
|
||||
|
||||
access.admin:
|
||||
type: array
|
||||
label: PLUGIN_ADMIN.ADMIN_ACCESS
|
||||
multiple: false
|
||||
validate:
|
||||
type: array
|
||||
|
||||
access.site:
|
||||
type: array
|
||||
label: PLUGIN_ADMIN.SITE_ACCESS
|
||||
multiple: false
|
||||
access:
|
||||
type: permissions
|
||||
label: PLUGIN_ADMIN.PERMISSIONS
|
||||
ignore_empty: true
|
||||
validate:
|
||||
type: array
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
title: Grav # Name of the site
|
||||
default_lang: en # Default language for site (potentially used by theme)
|
||||
|
||||
author:
|
||||
name: John Appleseed # Default author name
|
||||
|
||||
@@ -5,7 +5,7 @@ param_sep: ':' # Parameter separator, use ';' for A
|
||||
wrapped_site: false # For themes/plugins to know if Grav is wrapped by another platform
|
||||
reverse_proxy_setup: false # Running in a reverse proxy scenario with different webserver ports than proxy
|
||||
force_ssl: false # If enabled, Grav forces to be accessed via HTTPS (NOTE: Not an ideal solution)
|
||||
custom_base_url: '' # Set the base_url manually
|
||||
custom_base_url: '' # Set the base_url manually, e.g. http://yoursite.com/yourpath
|
||||
|
||||
languages:
|
||||
supported: [] # List of languages supported. eg: [en, fr, de]
|
||||
@@ -36,6 +36,7 @@ pages:
|
||||
markdown: true # Process Markdown
|
||||
twig: false # Process Twig
|
||||
twig_first: false # Process Twig before markdown when processing both on a page
|
||||
never_cache_twig: false # Only cache content, never cache twig processed in content (incompatible with `twig_first: true`)
|
||||
events:
|
||||
page: true # Enable page level events
|
||||
twig: true # Enable Twig level events
|
||||
@@ -50,7 +51,7 @@ pages:
|
||||
types: [txt,xml,html,htm,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)
|
||||
last_modified: false # Set the last modified date header based on file modifcation timestamp
|
||||
last_modified: false # Set the last modified date header based on file modification timestamp
|
||||
etag: false # Set the etag header tag
|
||||
vary_accept_encoding: false # Add `Vary: Accept-Encoding` header
|
||||
redirect_default_route: false # Automatically redirect to a page's default route
|
||||
@@ -70,8 +71,10 @@ cache:
|
||||
method: file # Method to check for updates in pages: file|folder|hash|none
|
||||
driver: auto # One of: auto|file|apc|xcache|memcache|wincache
|
||||
prefix: 'g' # Cache prefix string (prevents cache conflicts)
|
||||
cli_compatibility: false # Ensures only non-volatile drivers are used (file, redis, memcache, etc.)
|
||||
lifetime: 604800 # Lifetime of cached data in seconds (0 = infinite)
|
||||
gzip: false # GZip compress the page output
|
||||
allow_webserver_gzip: false # If true, `content-encoding: identity` but connection isn't closed before `onShutDown()` event
|
||||
redis:
|
||||
socket: false # Path to redis unix socket (e.g. /var/run/redis/redis.sock), false = use server and port to connect
|
||||
|
||||
@@ -135,3 +138,4 @@ gpm:
|
||||
proxy_url: # Configure a manual proxy URL for GPM (eg 127.0.0.1:3128)
|
||||
method: 'auto' # Either 'curl', 'fopen' or 'auto'. 'auto' will try fopen first and if not available cURL
|
||||
verify_peer: true # Sometimes on some systems (Windows most commonly) GPM is unable to connect because the SSL certificate cannot be verified. Disabling this setting might help.
|
||||
official_gpm_only: true # By default GPM direct-install will only allow URLs via the official GPM proxy to ensure security
|
||||
|
||||
@@ -8,10 +8,13 @@
|
||||
|
||||
// Some standard defines
|
||||
define('GRAV', true);
|
||||
define('GRAV_VERSION', '1.1.6');
|
||||
define('GRAV_VERSION', '1.2.0');
|
||||
define('GRAV_TESTING', false);
|
||||
define('DS', '/');
|
||||
define('GRAV_PHP_MIN', '5.5.9');
|
||||
|
||||
if (!defined('GRAV_PHP_MIN')) {
|
||||
define('GRAV_PHP_MIN', '5.5.9');
|
||||
}
|
||||
|
||||
// Directories and Paths
|
||||
if (!defined('GRAV_ROOT')) {
|
||||
|
||||
26
system/router.php
Normal file
26
system/router.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Core
|
||||
*
|
||||
* @copyright Copyright (C) 2014 - 2016 RocketTheme, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
if (PHP_SAPI !== 'cli-server') {
|
||||
exit('This script cannot be run from browser. Run it from a CLI.');
|
||||
}
|
||||
|
||||
$_SERVER['PHP_CLI_ROUTER'] = true;
|
||||
|
||||
if (is_file($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . $_SERVER['SCRIPT_NAME'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$_SERVER = array_merge($_SERVER, $_ENV);
|
||||
$_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . 'index.php';
|
||||
$_SERVER['SCRIPT_NAME'] = DIRECTORY_SEPARATOR . 'index.php';
|
||||
$_SERVER['PHP_SELF'] = DIRECTORY_SEPARATOR . 'index.php';
|
||||
|
||||
require 'index.php';
|
||||
|
||||
error_log(sprintf('%s:%d [%d]: %s', $_SERVER['REMOTE_ADDR'], $_SERVER['REMOTE_PORT'], http_response_code(), $_SERVER['REQUEST_URI']), 4);
|
||||
@@ -889,7 +889,7 @@ class Assets
|
||||
/**
|
||||
* Removes an item from the CSS array if set
|
||||
*
|
||||
* @param $key the asset key
|
||||
* @param string $key The asset key
|
||||
*/
|
||||
public function removeCss($key)
|
||||
{
|
||||
@@ -902,7 +902,7 @@ class Assets
|
||||
/**
|
||||
* Removes an item from the JS array if set
|
||||
*
|
||||
* @param $key the asset key
|
||||
* @param string $key The asset key
|
||||
*/
|
||||
public function removeJs($key)
|
||||
{
|
||||
@@ -1255,13 +1255,8 @@ class Assets
|
||||
|
||||
$old_url = $matches[2];
|
||||
|
||||
// ensure this is not a data url
|
||||
if (strpos($old_url, 'data:') === 0) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
// ensure this is not a remote url
|
||||
if ($this->isRemoteLink($old_url)) {
|
||||
// Ensure link is not rooted to webserver, a data URL, or to a remote host
|
||||
if (Utils::startsWith($old_url, '/') || Utils::startsWith($old_url, 'data:') || $this->isRemoteLink($old_url)) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Grav\Common;
|
||||
use \Doctrine\Common\Cache as DoctrineCache;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Grav;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
|
||||
/**
|
||||
* The GravCache object is used throughout Grav to store and retrieve cached data.
|
||||
@@ -173,6 +173,12 @@ class Cache extends Getters
|
||||
$setting = $this->driver_setting;
|
||||
$driver_name = 'file';
|
||||
|
||||
// CLI compatibility requires a non-volatile cache driver
|
||||
if ($this->config->get('system.cache.cli_compatibility') && (
|
||||
$setting == 'auto' || $this->isVolatileDriver($setting))) {
|
||||
$setting = $driver_name;
|
||||
}
|
||||
|
||||
if (!$setting || $setting == 'auto') {
|
||||
if (extension_loaded('apcu')) {
|
||||
$driver_name = 'apcu';
|
||||
@@ -250,7 +256,7 @@ class Cache extends Getters
|
||||
*
|
||||
* @param string $id the id of the cached entry
|
||||
*
|
||||
* @return object returns the cached entry, can be any type, or false if doesn't exist
|
||||
* @return object|bool returns the cached entry, can be any type, or false if doesn't exist
|
||||
*/
|
||||
public function fetch($id)
|
||||
{
|
||||
@@ -356,36 +362,40 @@ class Cache extends Getters
|
||||
$remove_paths = self::$standard_remove;
|
||||
}
|
||||
|
||||
// Clearing cache event to add paths to clear
|
||||
Grav::instance()->fireEvent('onBeforeCacheClear', new Event(['remove' => $remove, 'paths' => &$remove_paths]));
|
||||
|
||||
foreach ($remove_paths as $stream) {
|
||||
|
||||
// Convert stream to a real path
|
||||
try {
|
||||
$path = $locator->findResource($stream, true, true);
|
||||
} catch (\Exception $e) {
|
||||
// stream not found..
|
||||
continue;
|
||||
}
|
||||
|
||||
$anything = false;
|
||||
$files = glob($path . '/*');
|
||||
$anything = false;
|
||||
$files = glob($path . '/*');
|
||||
|
||||
if (is_array($files)) {
|
||||
foreach ($files as $file) {
|
||||
if (is_file($file)) {
|
||||
if (@unlink($file)) {
|
||||
$anything = true;
|
||||
}
|
||||
} elseif (is_dir($file)) {
|
||||
if (Folder::delete($file)) {
|
||||
$anything = true;
|
||||
if (is_array($files)) {
|
||||
foreach ($files as $file) {
|
||||
if (is_link($file)) {
|
||||
$output[] = '<yellow>Skipping symlink: </yellow>' . $file;
|
||||
} elseif (is_file($file)) {
|
||||
if (@unlink($file)) {
|
||||
$anything = true;
|
||||
}
|
||||
} elseif (is_dir($file)) {
|
||||
if (Folder::delete($file)) {
|
||||
$anything = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($anything) {
|
||||
$output[] = '<red>Cleared: </red>' . $path . '/*';
|
||||
if ($anything) {
|
||||
$output[] = '<red>Cleared: </red>' . $path . '/*';
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// stream not found or another error while deleting files.
|
||||
$output[] = '<red>ERROR: </red>' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,4 +443,39 @@ class Cache extends Getters
|
||||
|
||||
return $this->lifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current driver name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDriverName()
|
||||
{
|
||||
return $this->driver_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current driver setting
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDriverSetting()
|
||||
{
|
||||
return $this->driver_setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* is this driver a volatile driver in that it resides in PHP process memory
|
||||
*
|
||||
* @param $setting
|
||||
* @return bool
|
||||
*/
|
||||
public function isVolatileDriver($setting)
|
||||
{
|
||||
if (in_array($setting, ['apc', 'apcu', 'xcache', 'wincache'])) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ class CompiledLanguages extends CompiledBase
|
||||
{
|
||||
$file = CompiledYamlFile::instance($filename);
|
||||
if (preg_match('|languages\.yaml$|', $filename)) {
|
||||
$this->object->mergeRecursive($file->content());
|
||||
$this->object->mergeRecursive((array)$file->content());
|
||||
} else {
|
||||
$this->object->join($name, $file->content(), '/');
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Utils;
|
||||
use Symfony\Component\Yaml\Exception\ParseException;
|
||||
use Symfony\Component\Yaml\Parser;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
@@ -338,7 +339,7 @@ class Validation
|
||||
|
||||
protected static function filterNumber($value, array $params, array $field)
|
||||
{
|
||||
return (int) $value;
|
||||
return (string)(int)$value !== (string)(float)$value ? (float) $value : (int) $value;
|
||||
}
|
||||
|
||||
protected static function filterDateTime($value, array $params, array $field)
|
||||
@@ -569,6 +570,7 @@ class Validation
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
if ($options) {
|
||||
$useKey = isset($field['use']) && $field['use'] == 'keys';
|
||||
foreach ($values as $key => $value) {
|
||||
@@ -580,9 +582,22 @@ class Validation
|
||||
foreach ($values as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$value = implode(',', $value);
|
||||
$values[$key] = array_map('trim', explode(',', $value));
|
||||
} else {
|
||||
$values[$key] = trim($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($field['ignore_empty']) && Utils::isPositive($field['ignore_empty'])) {
|
||||
foreach ($values as $key => $value) {
|
||||
foreach ($value as $inner_key => $inner_value) {
|
||||
if ($inner_value == '') {
|
||||
unset($value[$inner_key]);
|
||||
}
|
||||
}
|
||||
|
||||
$values[$key] = array_map('trim', explode(',', $value));
|
||||
$values[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,14 @@ class Debugger
|
||||
public function addAssets()
|
||||
{
|
||||
if ($this->enabled()) {
|
||||
|
||||
// Only add assets if Page is HTML
|
||||
$page = $this->grav['page'];
|
||||
if ($page->templateFormat() != 'html') {
|
||||
$this->enabled = false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @var Assets $assets */
|
||||
$assets = $this->grav['assets'];
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@ class Errors
|
||||
$jsonRequest = $_SERVER && isset($_SERVER['HTTP_ACCEPT']) && $_SERVER['HTTP_ACCEPT'] == 'application/json';
|
||||
|
||||
// Setup Whoops-based error handler
|
||||
$whoops = new \Whoops\Run;
|
||||
$system = new SystemFacade;
|
||||
$whoops = new \Whoops\Run($system);
|
||||
|
||||
$verbosity = 1;
|
||||
|
||||
|
||||
39
system/src/Grav/Common/Errors/SystemFacade.php
Normal file
39
system/src/Grav/Common/Errors/SystemFacade.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Errors
|
||||
*
|
||||
* @copyright Copyright (C) 2014 - 2016 RocketTheme, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Errors;
|
||||
|
||||
class SystemFacade extends \Whoops\Util\SystemFacade
|
||||
{
|
||||
protected $whoopsShutdownHandler;
|
||||
|
||||
/**
|
||||
* @param callable $function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerShutdownFunction(callable $function)
|
||||
{
|
||||
$this->whoopsShutdownHandler = $function;
|
||||
register_shutdown_function([$this, 'handleShutdown']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special case to deal with Fatal errors and the like.
|
||||
*/
|
||||
public function handleShutdown()
|
||||
{
|
||||
$error = $this->getLastError();
|
||||
|
||||
// Ignore core warnings and errors.
|
||||
if ($error && !($error['type'] & (E_CORE_WARNING | E_CORE_ERROR))) {
|
||||
$handler = $this->whoopsShutdownHandler;
|
||||
$handler();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,58 +23,64 @@ trait CompiledFile
|
||||
// Set some options
|
||||
$this->settings(['native' => true, 'compat' => true]);
|
||||
|
||||
// If nothing has been loaded, attempt to get pre-compiled version of the file first.
|
||||
if ($var === null && $this->raw === null && $this->content === null) {
|
||||
$key = md5($this->filename);
|
||||
$file = PhpFile::instance(CACHE_DIR . DS . "compiled/files/{$key}{$this->extension}.php");
|
||||
try {
|
||||
// If nothing has been loaded, attempt to get pre-compiled version of the file first.
|
||||
if ($var === null && $this->raw === null && $this->content === null) {
|
||||
$key = md5($this->filename);
|
||||
$file = PhpFile::instance(CACHE_DIR . DS . "compiled/files/{$key}{$this->extension}.php");
|
||||
|
||||
$modified = $this->modified();
|
||||
$modified = $this->modified();
|
||||
|
||||
if (!$modified) {
|
||||
return $this->decode($this->raw());
|
||||
}
|
||||
|
||||
$class = get_class($this);
|
||||
|
||||
$cache = $file->exists() ? $file->content() : null;
|
||||
|
||||
// Load real file if cache isn't up to date (or is invalid).
|
||||
if (
|
||||
!isset($cache['@class'])
|
||||
|| $cache['@class'] != $class
|
||||
|| $cache['modified'] != $modified
|
||||
|| $cache['filename'] != $this->filename
|
||||
) {
|
||||
// Attempt to lock the file for writing.
|
||||
try {
|
||||
$file->lock(false);
|
||||
} catch (\Exception $e) {
|
||||
// Another process has locked the file; we will check this in a bit.
|
||||
if (!$modified) {
|
||||
return $this->decode($this->raw());
|
||||
}
|
||||
|
||||
// Decode RAW file into compiled array.
|
||||
$data = (array) $this->decode($this->raw());
|
||||
$cache = [
|
||||
'@class' => $class,
|
||||
'filename' => $this->filename,
|
||||
'modified' => $modified,
|
||||
'data' => $data
|
||||
];
|
||||
$class = get_class($this);
|
||||
|
||||
// If compiled file wasn't already locked by another process, save it.
|
||||
if ($file->locked() !== false) {
|
||||
$file->save($cache);
|
||||
$file->unlock();
|
||||
$cache = $file->exists() ? $file->content() : null;
|
||||
|
||||
// Compile cached file into bytecode cache
|
||||
if (function_exists('opcache_invalidate')) {
|
||||
opcache_invalidate($file->filename(), true);
|
||||
// Load real file if cache isn't up to date (or is invalid).
|
||||
if (
|
||||
!isset($cache['@class'])
|
||||
|| $cache['@class'] != $class
|
||||
|| $cache['modified'] != $modified
|
||||
|| $cache['filename'] != $this->filename
|
||||
) {
|
||||
// Attempt to lock the file for writing.
|
||||
try {
|
||||
$file->lock(false);
|
||||
} catch (\Exception $e) {
|
||||
// Another process has locked the file; we will check this in a bit.
|
||||
}
|
||||
|
||||
// Decode RAW file into compiled array.
|
||||
$data = (array)$this->decode($this->raw());
|
||||
$cache = [
|
||||
'@class' => $class,
|
||||
'filename' => $this->filename,
|
||||
'modified' => $modified,
|
||||
'data' => $data
|
||||
];
|
||||
|
||||
// If compiled file wasn't already locked by another process, save it.
|
||||
if ($file->locked() !== false) {
|
||||
$file->save($cache);
|
||||
$file->unlock();
|
||||
|
||||
// Compile cached file into bytecode cache
|
||||
if (function_exists('opcache_invalidate')) {
|
||||
// Silence error if function exists, but is restricted.
|
||||
@opcache_invalidate($file->filename(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$file->free();
|
||||
$file->free();
|
||||
|
||||
$this->content = $cache['data'];
|
||||
$this->content = $cache['data'];
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
throw new \RuntimeException(sprintf('Failed to read %s: %s', basename($this->filename), $e->getMessage()), 500, $e);
|
||||
}
|
||||
|
||||
return parent::content($var);
|
||||
|
||||
28
system/src/Grav/Common/File/CompiledJsonFile.php
Normal file
28
system/src/Grav/Common/File/CompiledJsonFile.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.File
|
||||
*
|
||||
* @copyright Copyright (C) 2014 - 2016 RocketTheme, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\File;
|
||||
|
||||
use RocketTheme\Toolbox\File\JsonFile;
|
||||
|
||||
class CompiledJsonFile extends JsonFile
|
||||
{
|
||||
use CompiledFile;
|
||||
|
||||
/**
|
||||
* Decode RAW string into contents.
|
||||
*
|
||||
* @param string $var
|
||||
* @param bool $assoc
|
||||
* @return array mixed
|
||||
*/
|
||||
protected function decode($var, $assoc = true)
|
||||
{
|
||||
return (array) json_decode($var, $assoc);
|
||||
}
|
||||
}
|
||||
@@ -70,9 +70,13 @@ abstract class Folder
|
||||
|
||||
/** @var \RecursiveDirectoryIterator $file */
|
||||
foreach ($iterator as $filepath => $file) {
|
||||
$file_modified = $file->getMTime();
|
||||
if ($file_modified > $last_modified) {
|
||||
$last_modified = $file_modified;
|
||||
try {
|
||||
$file_modified = $file->getMTime();
|
||||
if ($file_modified > $last_modified) {
|
||||
$last_modified = $file_modified;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Grav::instance()['log']->error('Could not process file: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,12 +344,10 @@ abstract class Folder
|
||||
// Make sure that path to the target exists before moving.
|
||||
self::create(dirname($target));
|
||||
|
||||
// Just rename the directory.
|
||||
$success = @rename($source, $target);
|
||||
|
||||
if (!$success) {
|
||||
$error = error_get_last();
|
||||
throw new \RuntimeException($error['message']);
|
||||
self::copy($source, $target);
|
||||
self::delete($source);
|
||||
}
|
||||
|
||||
// Make sure that the change will be detected when caching.
|
||||
@@ -386,7 +388,6 @@ abstract class Folder
|
||||
/**
|
||||
* @param string $folder
|
||||
* @throws \RuntimeException
|
||||
* @internal
|
||||
*/
|
||||
public static function mkdir($folder)
|
||||
{
|
||||
@@ -396,7 +397,6 @@ abstract class Folder
|
||||
/**
|
||||
* @param string $folder
|
||||
* @throws \RuntimeException
|
||||
* @internal
|
||||
*/
|
||||
public static function create($folder)
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ class RecursiveFolderFilterIterator extends \RecursiveFilterIterator
|
||||
/**
|
||||
* Create a RecursiveFilterIterator from a RecursiveIterator
|
||||
*
|
||||
* @param RecursiveIterator $iterator
|
||||
* @param \RecursiveIterator $iterator
|
||||
*/
|
||||
public function __construct(\RecursiveIterator $iterator)
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Inflector;
|
||||
use Grav\Common\Iterator;
|
||||
use Grav\Common\Utils;
|
||||
@@ -74,7 +75,7 @@ class GPM extends Iterator
|
||||
* Returns the Locally installable packages
|
||||
*
|
||||
* @param array $list_type_installed
|
||||
* @return Iterator The installed packages
|
||||
* @return array The installed packages
|
||||
*/
|
||||
public function getInstallable($list_type_installed = ['plugins' => true, 'themes' => true])
|
||||
{
|
||||
@@ -117,6 +118,8 @@ class GPM extends Iterator
|
||||
if (isset($this->installed['themes'][$slug])) {
|
||||
return $this->installed['themes'][$slug];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,7 +202,7 @@ class GPM extends Iterator
|
||||
/**
|
||||
* Returns an array of Plugins and Themes that can be updated.
|
||||
* Plugins and Themes are extended with the `available` property that relies to the remote version
|
||||
* @param $list_type_update specifies what type of package to update
|
||||
* @param array $list_type_update specifies what type of package to update
|
||||
* @return array Array of updatable Plugins and Themes.
|
||||
* Format: ['total' => int, 'plugins' => array, 'themes' => array]
|
||||
*/
|
||||
@@ -222,7 +225,7 @@ class GPM extends Iterator
|
||||
/**
|
||||
* Returns an array of Plugins that can be updated.
|
||||
* The Plugins are extended with the `available` property that relies to the remote version
|
||||
* @return Iterator Array of updatable Plugins
|
||||
* @return array Array of updatable Plugins
|
||||
*/
|
||||
public function getUpdatablePlugins()
|
||||
{
|
||||
@@ -302,7 +305,7 @@ class GPM extends Iterator
|
||||
/**
|
||||
* Returns an array of Themes that can be updated.
|
||||
* The Themes are extended with the `available` property that relies to the remote version
|
||||
* @return Iterator Array of updatable Themes
|
||||
* @return array Array of updatable Themes
|
||||
*/
|
||||
public function getUpdatableThemes()
|
||||
{
|
||||
@@ -435,7 +438,7 @@ class GPM extends Iterator
|
||||
|
||||
/**
|
||||
* Returns the list of Plugins and Themes available in the repository
|
||||
* @return array Array of available Plugins and Themes
|
||||
* @return Remote\Packages Available Plugins and Themes
|
||||
* Format: ['plugins' => array, 'themes' => array]
|
||||
*/
|
||||
public function getRepository()
|
||||
@@ -446,9 +449,10 @@ class GPM extends Iterator
|
||||
/**
|
||||
* Searches for a Package in the repository
|
||||
* @param string $search Can be either the slug or the name
|
||||
* @return Remote\Package Package if found, FALSE if not
|
||||
* @param bool $ignore_exception True if should not fire an exception (for use in Twig)
|
||||
* @return Remote\Package|bool Package if found, FALSE if not
|
||||
*/
|
||||
public function findPackage($search)
|
||||
public function findPackage($search, $ignore_exception = false)
|
||||
{
|
||||
$search = strtolower($search);
|
||||
|
||||
@@ -470,6 +474,10 @@ class GPM extends Iterator
|
||||
throw new \RuntimeException("The cache/gpm folder is not writable. Please check the folder permissions.");
|
||||
}
|
||||
|
||||
if ($ignore_exception) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new \RuntimeException("GPM not reachable. Please check your internet connection or check the Grav site is reachable");
|
||||
}
|
||||
|
||||
@@ -492,6 +500,151 @@ class GPM extends Iterator
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the zip package via the URL
|
||||
*
|
||||
* @param $package_file
|
||||
* @param $tmp
|
||||
* @return null|string
|
||||
*/
|
||||
public static function downloadPackage($package_file, $tmp)
|
||||
{
|
||||
$package = parse_url($package_file);
|
||||
$filename = basename($package['path']);
|
||||
|
||||
if (Grav::instance()['config']->get('system.gpm.official_gpm_only') && $package['host'] !== 'getgrav.org') {
|
||||
throw new \RuntimeException("Only offical GPM URLs are allowed. You can modify this behavior in the System configuration.");
|
||||
}
|
||||
|
||||
$output = Response::get($package_file, []);
|
||||
|
||||
if ($output) {
|
||||
Folder::mkdir($tmp);
|
||||
file_put_contents($tmp . DS . $filename, $output);
|
||||
return $tmp . DS . $filename;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the local zip package to tmp
|
||||
*
|
||||
* @param $package_file
|
||||
* @param $tmp
|
||||
* @return null|string
|
||||
*/
|
||||
public static function copyPackage($package_file, $tmp)
|
||||
{
|
||||
$package_file = realpath($package_file);
|
||||
|
||||
if (file_exists($package_file)) {
|
||||
$filename = basename($package_file);
|
||||
Folder::mkdir($tmp);
|
||||
copy(realpath($package_file), $tmp . DS . $filename);
|
||||
return $tmp . DS . $filename;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to guess the package type from the source files
|
||||
*
|
||||
* @param $source
|
||||
* @return bool|string
|
||||
*/
|
||||
public static function getPackageType($source)
|
||||
{
|
||||
$plugin_regex = '/^class\\s{1,}[a-zA-Z0-9]{1,}\\s{1,}extends.+Plugin/m';
|
||||
$theme_regex = '/^class\\s{1,}[a-zA-Z0-9]{1,}\\s{1,}extends.+Theme/m';
|
||||
|
||||
if (
|
||||
file_exists($source . 'system/defines.php') &&
|
||||
file_exists($source . 'system/config/system.yaml')
|
||||
) {
|
||||
return 'grav';
|
||||
} else {
|
||||
// must have a blueprint
|
||||
if (!file_exists($source . 'blueprints.yaml')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// either theme or plugin
|
||||
$name = basename($source);
|
||||
if (Utils::contains($name, 'theme')) {
|
||||
return 'theme';
|
||||
} elseif (Utils::contains($name, 'plugin')) {
|
||||
return 'plugin';
|
||||
}
|
||||
foreach (glob($source . "*.php") as $filename) {
|
||||
$contents = file_get_contents($filename);
|
||||
if (preg_match($theme_regex, $contents)) {
|
||||
return 'theme';
|
||||
} elseif (preg_match($plugin_regex, $contents)) {
|
||||
return 'plugin';
|
||||
}
|
||||
}
|
||||
|
||||
// Assume it's a theme
|
||||
return 'theme';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to guess the package name from the source files
|
||||
*
|
||||
* @param $source
|
||||
* @return bool|string
|
||||
*/
|
||||
public static function getPackageName($source)
|
||||
{
|
||||
foreach (glob($source . "*.yaml") as $filename) {
|
||||
$name = strtolower(basename($filename, '.yaml'));
|
||||
if ($name == 'blueprints') {
|
||||
continue;
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find/Parse the blueprint file
|
||||
*
|
||||
* @param $source
|
||||
* @return array|bool
|
||||
*/
|
||||
public static function getBlueprints($source)
|
||||
{
|
||||
$blueprint_file = $source . 'blueprints.yaml';
|
||||
if (!file_exists($blueprint_file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$blueprint = (array)Yaml::parse(file_get_contents($blueprint_file));
|
||||
return $blueprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the install path for a name and a particular type of package
|
||||
*
|
||||
* @param $type
|
||||
* @param $name
|
||||
* @return string
|
||||
*/
|
||||
public static function getInstallPath($type, $name)
|
||||
{
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
if ($type == 'theme') {
|
||||
$install_path = $locator->findResource('themes://', false) . DS . $name;
|
||||
} else {
|
||||
$install_path = $locator->findResource('plugins://', false) . DS . $name;
|
||||
}
|
||||
return $install_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a list of Packages in the repository
|
||||
* @param array $searches An array of either slugs or names
|
||||
@@ -595,6 +748,8 @@ class GPM extends Iterator
|
||||
return $dependency[$dependency_slug];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -181,7 +181,7 @@ class Installer
|
||||
return false;
|
||||
}
|
||||
|
||||
$package_folder_name = $zip->getNameIndex(0);
|
||||
$package_folder_name = preg_replace('#\./$#', '', $zip->getNameIndex(0));
|
||||
$zip->close();
|
||||
$extracted_folder = $destination . '/' . $package_folder_name;
|
||||
|
||||
|
||||
@@ -115,23 +115,25 @@ class Response
|
||||
// SSL Verify Peer and Proxy Setting
|
||||
$settings = [
|
||||
'method' => $config->get('system.gpm.method', self::$method),
|
||||
'verify_peer' => $config->get('system.gpm.verify_peer'),
|
||||
'verify_peer' => $config->get('system.gpm.verify_peer', true),
|
||||
// `system.proxy_url` is for fallback
|
||||
// introduced with 1.1.0-beta.1 probably safe to remove at some point
|
||||
'proxy_url' => $config->get('system.gpm.proxy_url', $config->get('system.proxy_url', false)),
|
||||
];
|
||||
|
||||
$overrides = array_replace_recursive([], $overrides, [
|
||||
'curl' => [
|
||||
CURLOPT_SSL_VERIFYPEER => $settings['verify_peer']
|
||||
],
|
||||
'fopen' => [
|
||||
'ssl' => [
|
||||
'verify_peer' => $settings['verify_peer'],
|
||||
'verify_peer_name' => $settings['verify_peer'],
|
||||
if (!$settings['verify_peer']) {
|
||||
$overrides = array_replace_recursive([], $overrides, [
|
||||
'curl' => [
|
||||
CURLOPT_SSL_VERIFYPEER => $settings['verify_peer']
|
||||
],
|
||||
'fopen' => [
|
||||
'ssl' => [
|
||||
'verify_peer' => $settings['verify_peer'],
|
||||
'verify_peer_name' => $settings['verify_peer'],
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
]);
|
||||
}
|
||||
|
||||
// Proxy Setting
|
||||
if ($settings['proxy_url']) {
|
||||
@@ -188,11 +190,19 @@ class Response
|
||||
}
|
||||
|
||||
/**
|
||||
* Progress normalized for cURL and Fopen
|
||||
* Accepts a vsariable length of arguments passed in by stream method
|
||||
* Is this a remote file or not
|
||||
*
|
||||
* @return array Normalized array with useful data.
|
||||
* Format: ['code' => int|false, 'filesize' => bytes, 'transferred' => bytes, 'percent' => int]
|
||||
* @param $file
|
||||
* @return bool
|
||||
*/
|
||||
public static function isRemote($file)
|
||||
{
|
||||
return (bool) filter_var($file, FILTER_VALIDATE_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Progress normalized for cURL and Fopen
|
||||
* Accepts a variable length of arguments passed in by stream method
|
||||
*/
|
||||
public static function progress()
|
||||
{
|
||||
@@ -241,6 +251,8 @@ class Response
|
||||
if (self::isCurlAvailable()) {
|
||||
return self::getCurl(func_get_args());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -262,13 +274,18 @@ class Response
|
||||
$options['fopen']['notification'] = ['self', 'progress'];
|
||||
}
|
||||
|
||||
$ssl = $options['fopen']['ssl'];
|
||||
unset($options['fopen']['ssl']);
|
||||
if (isset($options['fopen']['ssl'])) {
|
||||
$ssl = $options['fopen']['ssl'];
|
||||
unset($options['fopen']['ssl']);
|
||||
|
||||
$stream = stream_context_create([
|
||||
'http' => $options['fopen'],
|
||||
'ssl' => $ssl
|
||||
], $options['fopen']);
|
||||
} else {
|
||||
$stream = stream_context_create(['http' => $options['fopen']], $options['fopen']);
|
||||
}
|
||||
|
||||
$stream = stream_context_create([
|
||||
'http' => $options['fopen'],
|
||||
'ssl' => $ssl
|
||||
], $options['fopen']);
|
||||
|
||||
$content = @file_get_contents($uri, false, $stream);
|
||||
|
||||
@@ -284,7 +301,7 @@ class Response
|
||||
case '401':
|
||||
throw new \RuntimeException("Invalid LICENSE");
|
||||
default:
|
||||
throw new \RuntimeException("Error while trying to download '$uri'\n");
|
||||
throw new \RuntimeException("Error while trying to download (code: $code): $uri \n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,7 +337,7 @@ class Response
|
||||
case '401':
|
||||
throw new \RuntimeException("Invalid LICENSE");
|
||||
default:
|
||||
throw new \RuntimeException("Error while trying to download '$uri'\nMessage: $error_message");
|
||||
throw new \RuntimeException("Error while trying to download (code: $code): $uri \nMessage: $error_message");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,7 +371,7 @@ class Response
|
||||
return curl_exec($ch);
|
||||
}
|
||||
|
||||
$max_redirects = isset($options['curl'][CURLOPT_MAXREDIRS]) ? $options['curl'][CURLOPT_MAXREDIRS] : 3;
|
||||
$max_redirects = isset($options['curl'][CURLOPT_MAXREDIRS]) ? $options['curl'][CURLOPT_MAXREDIRS] : 5;
|
||||
$options['curl'][CURLOPT_FOLLOWLOCATION] = false;
|
||||
|
||||
// open_basedir set but no redirects to follow, we can disable followlocation and proceed normally
|
||||
@@ -379,7 +396,7 @@ class Response
|
||||
$code = 0;
|
||||
} else {
|
||||
$code = curl_getinfo($rch, CURLINFO_HTTP_CODE);
|
||||
if ($code == 301 || $code == 302) {
|
||||
if ($code == 301 || $code == 302 || $code == 303) {
|
||||
preg_match('/Location:(.*?)\n/', $header, $matches);
|
||||
$uri = trim(array_pop($matches));
|
||||
} else {
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\GPM\Remote\GravCore;
|
||||
|
||||
/**
|
||||
* Class Upgrader
|
||||
*
|
||||
@@ -18,17 +20,10 @@ class Upgrader
|
||||
/**
|
||||
* Remote details about latest Grav version
|
||||
*
|
||||
* @var Packages
|
||||
* @var GravCore
|
||||
*/
|
||||
private $remote;
|
||||
|
||||
/**
|
||||
* Internal cache
|
||||
*
|
||||
* @var Iterator
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* Creates a new GPM instance with Local and Remote packages available
|
||||
*
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Language\Language;
|
||||
use Grav\Common\Page\Medium\ImageMedium;
|
||||
use Grav\Common\Page\Medium\Medium;
|
||||
use Grav\Common\Page\Page;
|
||||
use RocketTheme\Toolbox\DI\Container;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
|
||||
@@ -29,6 +33,7 @@ class Grav extends Container
|
||||
'events' => 'RocketTheme\Toolbox\Event\EventDispatcher',
|
||||
'cache' => 'Grav\Common\Cache',
|
||||
'session' => 'Grav\Common\Session',
|
||||
'Grav\Common\Service\MessagesServiceProvider',
|
||||
'plugins' => 'Grav\Common\Plugins',
|
||||
'themes' => 'Grav\Common\Themes',
|
||||
'twig' => 'Grav\Common\Twig\Twig',
|
||||
@@ -305,7 +310,12 @@ class Grav extends Container
|
||||
} else {
|
||||
// Without gzip we have no other choice than to prevent server from compressing the output.
|
||||
// This action turns off mod_deflate which would prevent us from closing the connection.
|
||||
header('Content-Encoding: none');
|
||||
if ($this['config']->get('system.cache.allow_webserver_gzip')) {
|
||||
header('Content-Encoding: identity');
|
||||
} else {
|
||||
header('Content-Encoding: none');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
103
system/src/Grav/Common/Helpers/Base32.php
Normal file
103
system/src/Grav/Common/Helpers/Base32.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Helpers
|
||||
*
|
||||
* @copyright Copyright (C) 2014 - 2016 RocketTheme, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Helpers;
|
||||
|
||||
class Base32 {
|
||||
protected static $base32Chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
protected static $base32Lookup = array(
|
||||
0xFF,0xFF,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, // '0', '1', '2', '3', '4', '5', '6', '7'
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, // '8', '9', ':', ';', '<', '=', '>', '?'
|
||||
0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06, // '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G'
|
||||
0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E, // 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'
|
||||
0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16, // 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W'
|
||||
0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF, // 'X', 'Y', 'Z', '[', '\', ']', '^', '_'
|
||||
0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06, // '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g'
|
||||
0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E, // 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o'
|
||||
0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16, // 'p', 'q', 'r', 's', 't', 'u', 'v', 'w'
|
||||
0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF // 'x', 'y', 'z', '{', '|', '}', '~', 'DEL'
|
||||
);
|
||||
|
||||
/**
|
||||
* Encode in Base32
|
||||
*
|
||||
* @param $bytes
|
||||
* @return string
|
||||
*/
|
||||
public static function encode( $bytes ) {
|
||||
$i = 0; $index = 0; $digit = 0;
|
||||
$base32 = "";
|
||||
$bytes_len = strlen($bytes);
|
||||
while( $i < $bytes_len ) {
|
||||
$currByte = ord($bytes{$i});
|
||||
/* Is the current digit going to span a byte boundary? */
|
||||
if( $index > 3 ) {
|
||||
if( ($i + 1) < $bytes_len ) {
|
||||
$nextByte = ord($bytes{$i+1});
|
||||
} else {
|
||||
$nextByte = 0;
|
||||
}
|
||||
$digit = $currByte & (0xFF >> $index);
|
||||
$index = ($index + 5) % 8;
|
||||
$digit <<= $index;
|
||||
$digit |= $nextByte >> (8 - $index);
|
||||
$i++;
|
||||
} else {
|
||||
$digit = ($currByte >> (8 - ($index + 5))) & 0x1F;
|
||||
$index = ($index + 5) % 8;
|
||||
if( $index == 0 ) $i++;
|
||||
}
|
||||
$base32 .= self::$base32Chars{$digit};
|
||||
}
|
||||
return $base32;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode in Base32
|
||||
*
|
||||
* @param $base32
|
||||
* @return string
|
||||
*/
|
||||
public static function decode( $base32 ) {
|
||||
$bytes = array();
|
||||
$base32_len = strlen($base32);
|
||||
for( $i=$base32_len*5/8-1; $i>=0; --$i ) {
|
||||
$bytes[] = 0;
|
||||
}
|
||||
for( $i = 0, $index = 0, $offset = 0; $i < $base32_len; $i++ ) {
|
||||
$lookup = ord($base32{$i}) - ord('0');
|
||||
/* Skip chars outside the lookup table */
|
||||
if( $lookup < 0 || $lookup >= count(self::$base32Lookup) ) {
|
||||
continue;
|
||||
}
|
||||
$digit = self::$base32Lookup[$lookup];
|
||||
/* If this digit is not in the table, ignore it */
|
||||
if( $digit == 0xFF ) continue;
|
||||
if( $index <= 3 ) {
|
||||
$index = ($index + 5) % 8;
|
||||
if( $index == 0) {
|
||||
$bytes[$offset] |= $digit;
|
||||
$offset++;
|
||||
if( $offset >= count($bytes) ) break;
|
||||
} else {
|
||||
$bytes[$offset] |= $digit << (8 - $index);
|
||||
}
|
||||
} else {
|
||||
$index = ($index + 5) % 8;
|
||||
$bytes[$offset] |= ($digit >> $index);
|
||||
$offset++;
|
||||
if ($offset >= count($bytes) ) break;
|
||||
$bytes[$offset] |= $digit << (8 - $index);
|
||||
}
|
||||
}
|
||||
$bites = "";
|
||||
foreach( $bytes as $byte ) $bites .= chr($byte);
|
||||
return $bites;
|
||||
}
|
||||
}
|
||||
@@ -9,21 +9,22 @@
|
||||
namespace Grav\Common\Helpers;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Page\Page;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Common\Page\Medium\Medium;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
class Excerpts
|
||||
{
|
||||
/**
|
||||
* Process Grav image media URL from HTML tag
|
||||
*
|
||||
* @param $html HTML tag e.g. `<img src="image.jpg" />`
|
||||
* @param $page The current page object
|
||||
* @return string Returns final HTML string
|
||||
* @param string $html HTML tag e.g. `<img src="image.jpg" />`
|
||||
* @param Page $page The current page object
|
||||
* @return string Returns final HTML string
|
||||
*/
|
||||
public static function processImageHtml($html, $page)
|
||||
public static function processImageHtml($html, Page $page)
|
||||
{
|
||||
$excerpt = static::getExcerptFromHtml($html, 'img');
|
||||
|
||||
@@ -47,8 +48,8 @@ class Excerpts
|
||||
/**
|
||||
* Get an Excerpt array from a chunk of HTML
|
||||
*
|
||||
* @param $html Chunk of HTML
|
||||
* @param $tag a tag, for example `img`
|
||||
* @param string $html Chunk of HTML
|
||||
* @param string $tag A tag, for example `img`
|
||||
* @return array|null returns nested array excerpt
|
||||
*/
|
||||
public static function getExcerptFromHtml($html, $tag)
|
||||
@@ -109,17 +110,17 @@ class Excerpts
|
||||
* Process a Link excerpt
|
||||
*
|
||||
* @param $excerpt
|
||||
* @param $page
|
||||
* @param Page $page
|
||||
* @param string $type
|
||||
* @return mixed
|
||||
*/
|
||||
public static function processLinkExcerpt($excerpt, $page, $type = 'link')
|
||||
public static function processLinkExcerpt($excerpt, Page $page, $type = 'link')
|
||||
{
|
||||
$url = $excerpt['element']['attributes']['href'];
|
||||
$url = htmlspecialchars_decode(urldecode($excerpt['element']['attributes']['href']));
|
||||
|
||||
$url_parts = parse_url(htmlspecialchars_decode(urldecode($url)));
|
||||
$url_parts = static::parseUrl($url);
|
||||
|
||||
// if there is a query, then parse it and build action calls
|
||||
// If there is a query, then parse it and build action calls.
|
||||
if (isset($url_parts['query'])) {
|
||||
$actions = array_reduce(explode('&', $url_parts['query']), function ($carry, $item) {
|
||||
$parts = explode('=', $item, 2);
|
||||
@@ -129,19 +130,19 @@ class Excerpts
|
||||
return $carry;
|
||||
}, []);
|
||||
|
||||
// valid attributes supported
|
||||
// Valid attributes supported.
|
||||
$valid_attributes = ['rel', 'target', 'id', 'class', 'classes'];
|
||||
|
||||
// Unless told to not process, go through actions
|
||||
// Unless told to not process, go through actions.
|
||||
if (array_key_exists('noprocess', $actions)) {
|
||||
unset($actions['noprocess']);
|
||||
} else {
|
||||
// loop through actions for the image and call them
|
||||
// Loop through actions for the image and call them.
|
||||
foreach ($actions as $attrib => $value) {
|
||||
$key = $attrib;
|
||||
|
||||
if (in_array($attrib, $valid_attributes)) {
|
||||
// support both class and classes
|
||||
// support both class and classes.
|
||||
if ($attrib == 'classes') {
|
||||
$attrib = 'class';
|
||||
}
|
||||
@@ -154,25 +155,33 @@ class Excerpts
|
||||
$url_parts['query'] = http_build_query($actions, null, '&', PHP_QUERY_RFC3986);
|
||||
}
|
||||
|
||||
// if no query elements left, unset query
|
||||
// If no query elements left, unset query.
|
||||
if (empty($url_parts['query'])) {
|
||||
unset ($url_parts['query']);
|
||||
}
|
||||
|
||||
// set path to / if not set
|
||||
// Set path to / if not set.
|
||||
if (empty($url_parts['path'])) {
|
||||
$url_parts['path'] = '';
|
||||
}
|
||||
|
||||
// if special scheme, just return
|
||||
if(isset($url_parts['scheme']) && !Utils::startsWith($url_parts['scheme'], 'http')) {
|
||||
// If scheme isn't http(s)..
|
||||
if (!empty($url_parts['scheme']) && !in_array($url_parts['scheme'], ['http', 'https'])) {
|
||||
// Handle custom streams.
|
||||
if ($type !== 'image' && !empty($url_parts['stream']) && !empty($url_parts['path'])) {
|
||||
$url_parts['path'] = Grav::instance()['base_url_relative'] . '/' . static::resolveStream("{$url_parts['scheme']}://{$url_parts['path']}");
|
||||
unset($url_parts['stream'], $url_parts['scheme']);
|
||||
|
||||
$excerpt['element']['attributes']['href'] = Uri::buildUrl($url_parts);
|
||||
}
|
||||
|
||||
return $excerpt;
|
||||
}
|
||||
|
||||
// handle paths and such
|
||||
// Handle paths and such.
|
||||
$url_parts = Uri::convertUrl($page, $url_parts, $type);
|
||||
|
||||
// build the URL from the component parts and set it on the element
|
||||
// Build the URL from the component parts and set it on the element.
|
||||
$excerpt['element']['attributes']['href'] = Uri::buildUrl($url_parts);
|
||||
|
||||
return $excerpt;
|
||||
@@ -181,68 +190,72 @@ class Excerpts
|
||||
/**
|
||||
* Process an image excerpt
|
||||
*
|
||||
* @param $excerpt
|
||||
* @param $page
|
||||
* @param array $excerpt
|
||||
* @param Page $page
|
||||
* @return mixed
|
||||
*/
|
||||
public static function processImageExcerpt($excerpt, $page)
|
||||
public static function processImageExcerpt(array $excerpt, Page $page)
|
||||
{
|
||||
$url = $excerpt['element']['attributes']['src'];
|
||||
$url = htmlspecialchars_decode(urldecode($excerpt['element']['attributes']['src']));
|
||||
$url_parts = static::parseUrl($url);
|
||||
|
||||
$url_parts = parse_url(htmlspecialchars_decode(urldecode($url)));
|
||||
$media = null;
|
||||
$filename = null;
|
||||
|
||||
if (isset($url_parts['scheme']) && !Utils::startsWith($url_parts['scheme'], 'http')) {
|
||||
$stream_path = $url_parts['scheme'] . '://' . $url_parts['host'] . $url_parts['path'];
|
||||
$url_parts['path'] = $stream_path;
|
||||
unset($url_parts['host']);
|
||||
unset($url_parts['scheme']);
|
||||
}
|
||||
if (!empty($url_parts['stream'])) {
|
||||
$filename = $url_parts['scheme'] . '://' . (isset($url_parts['path']) ? $url_parts['path'] : '');
|
||||
|
||||
$this_host = isset($url_parts['host']) && $url_parts['host'] == Grav::instance()['uri']->host();
|
||||
$media = $page->media();
|
||||
|
||||
// if there is no host set but there is a path, the file is local
|
||||
if ((!isset($url_parts['host']) || $this_host) && isset($url_parts['path'])) {
|
||||
} else {
|
||||
// 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']))
|
||||
&& (empty($url_parts['host']) || $url_parts['host'] == Grav::instance()['uri']->host());
|
||||
|
||||
$path_parts = pathinfo($url_parts['path']);
|
||||
$media = null;
|
||||
if ($local_file) {
|
||||
$filename = basename($url_parts['path']);
|
||||
$folder = dirname($url_parts['path']);
|
||||
|
||||
// get the local path to page media if possible
|
||||
if ($path_parts['dirname'] == $page->url(false, false, false)) {
|
||||
// get the media objects for this page
|
||||
$media = $page->media();
|
||||
} else {
|
||||
// see if this is an external page to this one
|
||||
$base_url = rtrim(Grav::instance()['base_url_relative'] . Grav::instance()['pages']->base(), '/');
|
||||
$page_route = '/' . ltrim(str_replace($base_url, '', $path_parts['dirname']), '/');
|
||||
|
||||
$ext_page = Grav::instance()['pages']->dispatch($page_route, true);
|
||||
if ($ext_page) {
|
||||
$media = $ext_page->media();
|
||||
// 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();
|
||||
} else {
|
||||
Grav::instance()->fireEvent('onMediaLocate', new Event(['route' => $page_route, 'media' => &$media]));
|
||||
// see if this is an external page to this one
|
||||
$base_url = rtrim(Grav::instance()['base_url_relative'] . Grav::instance()['pages']->base(), '/');
|
||||
$page_route = '/' . ltrim(str_replace($base_url, '', $folder), '/');
|
||||
|
||||
/** @var Page $ext_page */
|
||||
$ext_page = Grav::instance()['pages']->dispatch($page_route, true);
|
||||
if ($ext_page) {
|
||||
$media = $ext_page->media();
|
||||
} else {
|
||||
Grav::instance()->fireEvent('onMediaLocate', new Event(['route' => $page_route, 'media' => &$media]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if there is a media file that matches the path referenced..
|
||||
if ($media && isset($media->all()[$path_parts['basename']])) {
|
||||
// get the medium object
|
||||
/** @var Medium $medium */
|
||||
$medium = $media->all()[$path_parts['basename']];
|
||||
// If there is a media file that matches the path referenced..
|
||||
if ($media && $filename && isset($media[$filename])) {
|
||||
// Get the medium object.
|
||||
/** @var Medium $medium */
|
||||
$medium = $media[$filename];
|
||||
|
||||
// Process operations
|
||||
$medium = static::processMediaActions($medium, $url_parts);
|
||||
// Process operations
|
||||
$medium = static::processMediaActions($medium, $url_parts);
|
||||
|
||||
$alt = isset($excerpt['element']['attributes']['alt']) ? $excerpt['element']['attributes']['alt'] : '';
|
||||
$title = isset($excerpt['element']['attributes']['title']) ? $excerpt['element']['attributes']['title'] : '';
|
||||
$class = isset($excerpt['element']['attributes']['class']) ? $excerpt['element']['attributes']['class'] : '';
|
||||
$id = isset($excerpt['element']['attributes']['id']) ? $excerpt['element']['attributes']['id'] : '';
|
||||
$alt = isset($excerpt['element']['attributes']['alt']) ? $excerpt['element']['attributes']['alt'] : '';
|
||||
$title = isset($excerpt['element']['attributes']['title']) ? $excerpt['element']['attributes']['title'] : '';
|
||||
$class = isset($excerpt['element']['attributes']['class']) ? $excerpt['element']['attributes']['class'] : '';
|
||||
$id = isset($excerpt['element']['attributes']['id']) ? $excerpt['element']['attributes']['id'] : '';
|
||||
|
||||
$excerpt['element'] = $medium->parseDownElement($title, $alt, $class, $id, true);
|
||||
$excerpt['element'] = $medium->parseDownElement($title, $alt, $class, $id, true);
|
||||
|
||||
} else {
|
||||
// not a current page media file, see if it needs converting to relative
|
||||
$excerpt['element']['attributes']['src'] = Uri::buildUrl($url_parts);
|
||||
}
|
||||
} else {
|
||||
// Not a current page media file, see if it needs converting to relative.
|
||||
$excerpt['element']['attributes']['src'] = Uri::buildUrl($url_parts);
|
||||
}
|
||||
|
||||
return $excerpt;
|
||||
@@ -282,8 +295,15 @@ class Excerpts
|
||||
|
||||
// loop through actions for the image and call them
|
||||
foreach ($actions as $action) {
|
||||
$medium = call_user_func_array([$medium, $action['method']],
|
||||
explode(',', $action['params']));
|
||||
$matches = [];
|
||||
|
||||
if (preg_match('/\[(.*)\]/', $action['params'], $matches)) {
|
||||
$args = [explode(',', $matches[1])];
|
||||
} else {
|
||||
$args = explode(',', $action['params']);
|
||||
}
|
||||
|
||||
$medium = call_user_func_array([$medium, $action['method']], $args);
|
||||
}
|
||||
|
||||
if (isset($url_parts['fragment'])) {
|
||||
@@ -293,4 +313,40 @@ class Excerpts
|
||||
return $medium;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variation of parse_url() which works also with local streams.
|
||||
*
|
||||
* @param string $url
|
||||
* @return array|bool
|
||||
*/
|
||||
protected static function parseUrl($url)
|
||||
{
|
||||
$url_parts = parse_url($url);
|
||||
|
||||
if (isset($url_parts['scheme'])) {
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
// Special handling for the streams.
|
||||
if ($locator->schemeExists($url_parts['scheme'])) {
|
||||
if (isset($url_parts['host'])) {
|
||||
// Merge host and path into a path.
|
||||
$url_parts['path'] = $url_parts['host'] . (isset($url_parts['path']) ? '/' . $url_parts['path'] : '');
|
||||
unset($url_parts['host']);
|
||||
}
|
||||
|
||||
$url_parts['stream'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $url_parts;
|
||||
}
|
||||
|
||||
protected static function resolveStream($url)
|
||||
{
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
return $locator->isStream($url) ? ($locator->findResource($url, false) ?: $locator->findResource($url, false, true)) : $url;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace Grav\Common\Helpers;
|
||||
|
||||
use DOMText;
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use DOMNode;
|
||||
use DOMWordsIterator;
|
||||
use DOMLettersIterator;
|
||||
|
||||
@@ -118,7 +120,7 @@ class Truncator {
|
||||
|
||||
/**
|
||||
* Builds a DOMDocument object from a string containing HTML.
|
||||
* @param string HTML to load
|
||||
* @param string $html HTML to load
|
||||
* @returns DOMDocument Returns a DOMDocument object.
|
||||
*/
|
||||
public static function htmlToDomDocument($html)
|
||||
|
||||
@@ -26,11 +26,11 @@ class Inflector
|
||||
{
|
||||
if (empty($this->plural)) {
|
||||
$language = Grav::instance()['language'];
|
||||
$this->plural = $language->translate('INFLECTOR_PLURALS', null, true);
|
||||
$this->singular = $language->translate('INFLECTOR_SINGULAR', null, true);
|
||||
$this->uncountable = $language->translate('INFLECTOR_UNCOUNTABLE', null, true);
|
||||
$this->irregular = $language->translate('INFLECTOR_IRREGULAR', null, true);
|
||||
$this->ordinals = $language->translate('INFLECTOR_ORDINALS', null, true);
|
||||
$this->plural = $language->translate('INFLECTOR_PLURALS', null, true) ?: [];
|
||||
$this->singular = $language->translate('INFLECTOR_SINGULAR', null, true) ?: [];
|
||||
$this->uncountable = $language->translate('INFLECTOR_UNCOUNTABLE', null, true) ?: [];
|
||||
$this->irregular = $language->translate('INFLECTOR_IRREGULAR', null, true) ?: [];
|
||||
$this->ordinals = $language->translate('INFLECTOR_ORDINALS', null, true) ?: [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -189,6 +189,10 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
|
||||
*/
|
||||
public function random($num = 1)
|
||||
{
|
||||
if ($num > count($this->items)) {
|
||||
$num = count($this->items);
|
||||
}
|
||||
|
||||
$this->items = array_intersect_key($this->items, array_flip((array)array_rand($this->items, $num)));
|
||||
|
||||
return $this;
|
||||
|
||||
@@ -293,6 +293,21 @@ class Language
|
||||
return $this->page_extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the page_extensions value.
|
||||
*
|
||||
* Useful to re-initialize the pages and change site language at runtime, example:
|
||||
*
|
||||
* ```
|
||||
* $this->grav['language']->setActive('it');
|
||||
* $this->grav['language']->resetFallbackPageExtensions();
|
||||
* $this->grav['pages']->init();
|
||||
* ```
|
||||
*/
|
||||
public function resetFallbackPageExtensions() {
|
||||
$this->page_extensions = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of languages with active first, then fallback languages
|
||||
*
|
||||
|
||||
@@ -52,6 +52,8 @@ class LanguageCodes
|
||||
'fi' => [ 'name' => 'Finnish', 'nativeName' => 'Suomi' ],
|
||||
'fj-FJ' => [ 'name' => 'Fijian', 'nativeName' => 'Vosa vaka-Viti' ],
|
||||
'fr' => [ 'name' => 'French', 'nativeName' => 'Français' ],
|
||||
'fr-CA' => [ 'name' => 'French (Canada)', 'nativeName' => 'Français (Canada)' ],
|
||||
'fr-FR' => [ 'name' => 'French (France)', 'nativeName' => 'Français (France)' ],
|
||||
'fur' => [ 'name' => 'Friulian', 'nativeName' => 'Furlan' ],
|
||||
'fur-IT' => [ 'name' => 'Friulian', 'nativeName' => 'Furlan' ],
|
||||
'fy' => [ 'name' => 'Frisian', 'nativeName' => 'Frysk' ],
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace Grav\Common\Markdown;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Helpers\Excerpts;
|
||||
use Grav\Common\Page\Page;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
|
||||
trait ParsedownGravTrait
|
||||
@@ -166,6 +167,8 @@ trait ParsedownGravTrait
|
||||
|
||||
return $Block;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function inlineSpecialCharacter($Excerpt)
|
||||
@@ -183,6 +186,8 @@ trait ParsedownGravTrait
|
||||
'extent' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function inlineImage($excerpt)
|
||||
@@ -243,5 +248,7 @@ trait ParsedownGravTrait
|
||||
|
||||
return call_user_func_array($func, $args);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,21 @@ class Collection extends Iterator
|
||||
return new static($this->items, $this->params, $this->pages);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Merge another collection with the current collection
|
||||
*
|
||||
* @param Collection $collection
|
||||
* @return $this
|
||||
*/
|
||||
public function merge(Collection $collection)
|
||||
{
|
||||
foreach($collection as $page) {
|
||||
$this->addPage($page);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set parameters to the Collection
|
||||
*
|
||||
@@ -84,7 +99,6 @@ class Collection extends Iterator
|
||||
public function setParams(array $params)
|
||||
{
|
||||
$this->params = array_merge($this->params, $params);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -147,7 +161,7 @@ class Collection extends Iterator
|
||||
*
|
||||
* @param Page|string|null $key
|
||||
*
|
||||
* @return $this|void
|
||||
* @return $this
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function remove($key = null)
|
||||
@@ -172,12 +186,13 @@ class Collection extends Iterator
|
||||
* @param string $by
|
||||
* @param string $dir
|
||||
* @param array $manual
|
||||
* @param string $sort_flags
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function order($by, $dir = 'asc', $manual = null)
|
||||
public function order($by, $dir = 'asc', $manual = null, $sort_flags = null)
|
||||
{
|
||||
$this->items = $this->pages->sortCollection($this, $by, $dir, $manual);
|
||||
$this->items = $this->pages->sortCollection($this, $by, $dir, $manual, $sort_flags);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -8,51 +8,77 @@
|
||||
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
use Grav\Common\Getters;
|
||||
use Grav\Common\Page\Medium\Medium;
|
||||
use Grav\Common\Page\Medium\AbstractMedia;
|
||||
use Grav\Common\Page\Medium\GlobalMedia;
|
||||
use Grav\Common\Page\Medium\MediumFactory;
|
||||
|
||||
class Media extends Getters
|
||||
class Media extends AbstractMedia
|
||||
{
|
||||
protected $gettersVariable = 'instances';
|
||||
protected $path;
|
||||
protected static $global;
|
||||
|
||||
protected $instances = [];
|
||||
protected $images = [];
|
||||
protected $videos = [];
|
||||
protected $audios = [];
|
||||
protected $files = [];
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
*/
|
||||
public function __construct($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
if (!isset(static::$global)) {
|
||||
// Add fallback to global media.
|
||||
static::$global = new GlobalMedia($path);
|
||||
}
|
||||
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return parent::offsetExists($offset) ?: isset(static::$global[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return parent::offsetGet($offset) ?: static::$global[$offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize class.
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
|
||||
// Handle special cases where page doesn't exist in filesystem.
|
||||
if (!is_dir($path)) {
|
||||
if (!is_dir($this->path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->path = $path;
|
||||
|
||||
$iterator = new \FilesystemIterator($path, \FilesystemIterator::UNIX_PATHS | \FilesystemIterator::SKIP_DOTS);
|
||||
$iterator = new \FilesystemIterator($this->path, \FilesystemIterator::UNIX_PATHS | \FilesystemIterator::SKIP_DOTS);
|
||||
|
||||
$media = [];
|
||||
|
||||
/** @var \DirectoryIterator $info */
|
||||
foreach ($iterator as $path => $info) {
|
||||
// Ignore folders and Markdown files.
|
||||
if (!$info->isFile() || $info->getExtension() == 'md' || $info->getBasename() === '.DS_Store') {
|
||||
if (!$info->isFile() || $info->getExtension() == 'md' || $info->getBasename()[0] === '.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find out what type we're dealing with
|
||||
list($basename, $ext, $type, $extra) = $this->getFileParts($info->getFilename());
|
||||
|
||||
$media["{$basename}.{$ext}"] = isset($media["{$basename}.{$ext}"]) ? $media["{$basename}.{$ext}"] : [];
|
||||
|
||||
if ($type === 'alternative') {
|
||||
$media["{$basename}.{$ext}"][$type] = isset($media["{$basename}.{$ext}"][$type]) ? $media["{$basename}.{$ext}"][$type] : [];
|
||||
$media["{$basename}.{$ext}"][$type][$extra] = [ 'file' => $path, 'size' => $info->getSize() ];
|
||||
} else {
|
||||
$media["{$basename}.{$ext}"][$type] = [ 'file' => $path, 'size' => $info->getSize() ];
|
||||
@@ -74,16 +100,16 @@ class Media extends Getters
|
||||
}
|
||||
|
||||
// Create the base medium
|
||||
if (!empty($types['base'])) {
|
||||
if (empty($types['base'])) {
|
||||
if (!isset($types['alternative'])) {
|
||||
continue;
|
||||
}
|
||||
$max = max(array_keys($types['alternative']));
|
||||
$medium = $types['alternative'][$max]['file'];
|
||||
$medium = MediumFactory::scaledFromMedium($medium, $max, 1)['file'];
|
||||
} else {
|
||||
$medium = MediumFactory::fromFile($types['base']['file']);
|
||||
$medium && $medium->set('size', $types['base']['size']);
|
||||
} else if (!empty($types['alternative'])) {
|
||||
$altMedium = reset($types['alternative']);
|
||||
$ratio = key($types['alternative']);
|
||||
|
||||
$altMedium = $altMedium['file'];
|
||||
|
||||
$medium = MediumFactory::scaledFromMedium($altMedium, $ratio, 1)['file'];
|
||||
}
|
||||
|
||||
if (empty($medium)) {
|
||||
@@ -103,10 +129,9 @@ class Media extends Getters
|
||||
// Build missing alternatives
|
||||
if (!empty($types['alternative'])) {
|
||||
$alternatives = $types['alternative'];
|
||||
|
||||
$max = max(array_keys($alternatives));
|
||||
|
||||
for ($i=2; $i < $max; $i++) {
|
||||
for ($i=$max; $i > 1; $i--) {
|
||||
if (isset($alternatives[$i])) {
|
||||
continue;
|
||||
}
|
||||
@@ -114,142 +139,19 @@ class Media extends Getters
|
||||
$types['alternative'][$i] = MediumFactory::scaledFromMedium($alternatives[$max]['file'], $max, $i);
|
||||
}
|
||||
|
||||
foreach ($types['alternative'] as $ratio => $altMedium) {
|
||||
$medium->addAlternative($ratio, $altMedium['file']);
|
||||
foreach ($types['alternative'] as $altMedium) {
|
||||
if ($altMedium['file'] != $medium) {
|
||||
$altWidth = $altMedium['file']->get('width');
|
||||
$medWidth = $medium->get('width');
|
||||
if ($altWidth && $medWidth) {
|
||||
$ratio = $altWidth / $medWidth;
|
||||
$medium->addAlternative($ratio, $altMedium['file']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->add($name, $medium);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get medium by filename.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return Medium|null
|
||||
*/
|
||||
public function get($filename)
|
||||
{
|
||||
return isset($this->instances[$filename]) ? $this->instances[$filename] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all media.
|
||||
*
|
||||
* @return array|Medium[]
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
ksort($this->instances, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
return $this->instances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all image media.
|
||||
*
|
||||
* @return array|Medium[]
|
||||
*/
|
||||
public function images()
|
||||
{
|
||||
ksort($this->images, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
return $this->images;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all video media.
|
||||
*
|
||||
* @return array|Medium[]
|
||||
*/
|
||||
public function videos()
|
||||
{
|
||||
ksort($this->videos, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
return $this->videos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all audio media.
|
||||
*
|
||||
* @return array|Medium[]
|
||||
*/
|
||||
public function audios()
|
||||
{
|
||||
ksort($this->audios, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
return $this->audios;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all file media.
|
||||
*
|
||||
* @return array|Medium[]
|
||||
*/
|
||||
public function files()
|
||||
{
|
||||
ksort($this->files, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function add($name, $file)
|
||||
{
|
||||
$this->instances[$name] = $file;
|
||||
switch ($file->type) {
|
||||
case 'image':
|
||||
$this->images[$name] = $file;
|
||||
break;
|
||||
case 'video':
|
||||
$this->videos[$name] = $file;
|
||||
break;
|
||||
case 'audio':
|
||||
$this->audios[$name] = $file;
|
||||
break;
|
||||
default:
|
||||
$this->files[$name] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filename, extension and meta part.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return array
|
||||
*/
|
||||
protected function getFileParts($filename)
|
||||
{
|
||||
$fileParts = explode('.', $filename);
|
||||
|
||||
$name = array_shift($fileParts);
|
||||
$type = 'base';
|
||||
$extra = null;
|
||||
|
||||
if (preg_match('/(.*)@(\d+)x\.(.*)$/', $filename, $matches)) {
|
||||
$name = $matches[1];
|
||||
$extension = $matches[3];
|
||||
$extra = (int) $matches[2];
|
||||
$type = 'alternative';
|
||||
|
||||
if ($extra === 1) {
|
||||
$type = 'base';
|
||||
$extra = null;
|
||||
}
|
||||
} else {
|
||||
$extension = null;
|
||||
while (($part = array_shift($fileParts)) !== null) {
|
||||
if ($part != 'meta' && $part != 'thumb') {
|
||||
if (isset($extension)) {
|
||||
$name .= '.' . $extension;
|
||||
}
|
||||
$extension = $part;
|
||||
} else {
|
||||
$type = $part;
|
||||
$extra = '.' . $part . '.' . implode('.', $fileParts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array($name, $extension, $type, $extra);
|
||||
}
|
||||
}
|
||||
|
||||
164
system/src/Grav/Common/Page/Medium/AbstractMedia.php
Normal file
164
system/src/Grav/Common/Page/Medium/AbstractMedia.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2014 - 2016 RocketTheme, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\Getters;
|
||||
|
||||
abstract class AbstractMedia extends Getters
|
||||
{
|
||||
protected $gettersVariable = 'instances';
|
||||
|
||||
protected $instances = [];
|
||||
protected $images = [];
|
||||
protected $videos = [];
|
||||
protected $audios = [];
|
||||
protected $files = [];
|
||||
|
||||
/**
|
||||
* Get medium by filename.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return Medium|null
|
||||
*/
|
||||
public function get($filename)
|
||||
{
|
||||
return $this->offsetGet($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call object as function to get medium by filename.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return mixed
|
||||
*/
|
||||
public function __invoke($filename)
|
||||
{
|
||||
return $this->offsetGet($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all media.
|
||||
*
|
||||
* @return array|Medium[]
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
ksort($this->instances, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
return $this->instances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all image media.
|
||||
*
|
||||
* @return array|Medium[]
|
||||
*/
|
||||
public function images()
|
||||
{
|
||||
ksort($this->images, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
return $this->images;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all video media.
|
||||
*
|
||||
* @return array|Medium[]
|
||||
*/
|
||||
public function videos()
|
||||
{
|
||||
ksort($this->videos, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
return $this->videos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all audio media.
|
||||
*
|
||||
* @return array|Medium[]
|
||||
*/
|
||||
public function audios()
|
||||
{
|
||||
ksort($this->audios, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
return $this->audios;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all file media.
|
||||
*
|
||||
* @return array|Medium[]
|
||||
*/
|
||||
public function files()
|
||||
{
|
||||
ksort($this->files, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param Medium $file
|
||||
*/
|
||||
protected function add($name, $file)
|
||||
{
|
||||
$this->instances[$name] = $file;
|
||||
switch ($file->type) {
|
||||
case 'image':
|
||||
$this->images[$name] = $file;
|
||||
break;
|
||||
case 'video':
|
||||
$this->videos[$name] = $file;
|
||||
break;
|
||||
case 'audio':
|
||||
$this->audios[$name] = $file;
|
||||
break;
|
||||
default:
|
||||
$this->files[$name] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filename, extension and meta part.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return array
|
||||
*/
|
||||
protected function getFileParts($filename)
|
||||
{
|
||||
if (preg_match('/(.*)@(\d+)x\.(.*)$/', $filename, $matches)) {
|
||||
$name = $matches[1];
|
||||
$extension = $matches[3];
|
||||
$extra = (int) $matches[2];
|
||||
$type = 'alternative';
|
||||
|
||||
if ($extra === 1) {
|
||||
$type = 'base';
|
||||
$extra = null;
|
||||
}
|
||||
} else {
|
||||
$fileParts = explode('.', $filename);
|
||||
|
||||
$name = array_shift($fileParts);
|
||||
$extension = null;
|
||||
$extra = null;
|
||||
$type = 'base';
|
||||
|
||||
while (($part = array_shift($fileParts)) !== null) {
|
||||
if ($part != 'meta' && $part != 'thumb') {
|
||||
if (isset($extension)) {
|
||||
$name .= '.' . $extension;
|
||||
}
|
||||
$extension = $part;
|
||||
} else {
|
||||
$type = $part;
|
||||
$extra = '.' . $part . '.' . implode('.', $fileParts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array($name, $extension, $type, $extra);
|
||||
}
|
||||
}
|
||||
117
system/src/Grav/Common/Page/Medium/GlobalMedia.php
Normal file
117
system/src/Grav/Common/Page/Medium/GlobalMedia.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2014 - 2016 RocketTheme, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
class GlobalMedia extends AbstractMedia
|
||||
{
|
||||
/**
|
||||
* @param mixed $offset
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return parent::offsetExists($offset) ?: !empty($this->resolveStream($offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return parent::offsetGet($offset) ?: $this->addMedium($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return string|null
|
||||
*/
|
||||
protected function resolveStream($filename)
|
||||
{
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
return $locator->isStream($filename) ? ($locator->findResource($filename) ?: null) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $stream
|
||||
* @return Medium|null
|
||||
*/
|
||||
protected function addMedium($stream)
|
||||
{
|
||||
$filename = $this->resolveStream($stream);
|
||||
if (!$filename) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$path = dirname($filename);
|
||||
list($basename, $ext,, $extra) = $this->getFileParts(basename($filename));
|
||||
$medium = MediumFactory::fromFile($filename);
|
||||
|
||||
if (empty($medium)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$medium->set('size', filesize($filename));
|
||||
$scale = (int) ($extra ?: 1);
|
||||
|
||||
if ($scale !== 1) {
|
||||
$altMedium = $medium;
|
||||
|
||||
// Create scaled down regular sized image.
|
||||
$medium = MediumFactory::scaledFromMedium($altMedium, $scale, 1)['file'];
|
||||
|
||||
if (empty($medium)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add original sized image as alternative.
|
||||
$medium->addAlternative($scale, $altMedium['file']);
|
||||
|
||||
// Locate or generate smaller retina images.
|
||||
for ($i = $scale-1; $i > 1; $i--) {
|
||||
$altFilename = "{$path}/{$basename}@{$i}x.{$ext}";
|
||||
|
||||
if (file_exists($altFilename)) {
|
||||
$scaled = MediumFactory::fromFile($altFilename);
|
||||
} else {
|
||||
$scaled = MediumFactory::scaledFromMedium($altMedium, $scale, $i)['file'];
|
||||
}
|
||||
|
||||
if ($scaled) {
|
||||
$medium->addAlternative($i, $scaled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$meta = "{$path}/{$basename}.{$ext}.yaml";
|
||||
if (file_exists($meta)) {
|
||||
$medium->addMetaFile($meta);
|
||||
}
|
||||
$meta = "{$path}/{$basename}.{$ext}.meta.yaml";
|
||||
if (file_exists($meta)) {
|
||||
$medium->addMetaFile($meta);
|
||||
}
|
||||
|
||||
$thumb = "{$path}/{$basename}.thumb.{$ext}";
|
||||
if (file_exists($thumb)) {
|
||||
$medium->set('thumbnails.page', $thumb);
|
||||
}
|
||||
|
||||
$this->add($stream, $medium);
|
||||
|
||||
return $medium;
|
||||
}
|
||||
}
|
||||
@@ -65,11 +65,6 @@ class ImageMedium extends Medium
|
||||
'zoomCrop' => [0, 1]
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $derivatives = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
@@ -197,27 +192,19 @@ class ImageMedium extends Medium
|
||||
*/
|
||||
public function srcset($reset = true)
|
||||
{
|
||||
if (empty($this->alternatives) && empty($this->derivatives)) {
|
||||
if (empty($this->alternatives)) {
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!empty($this->derivatives)) {
|
||||
asort($this->derivatives);
|
||||
|
||||
foreach ($this->derivatives as $url => $width) {
|
||||
$srcset[] = $url . ' ' . $width . 'w';
|
||||
}
|
||||
|
||||
$srcset[] = $this->url($reset) . ' ' . $this->get('width') . 'w';
|
||||
} else {
|
||||
$srcset = [$this->url($reset) . ' ' . $this->get('width') . 'w'];
|
||||
foreach ($this->alternatives as $ratio => $medium) {
|
||||
$srcset[] = $medium->url($reset) . ' ' . $medium->get('width') . 'w';
|
||||
}
|
||||
$srcset = [];
|
||||
foreach ($this->alternatives as $ratio => $medium) {
|
||||
$srcset[] = $medium->url($reset) . ' ' . $medium->get('width') . 'w';
|
||||
}
|
||||
$srcset[] = $this->url($reset) . ' ' . $this->get('width') . 'w';
|
||||
|
||||
return implode(', ', $srcset);
|
||||
}
|
||||
@@ -249,39 +236,76 @@ class ImageMedium extends Medium
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate derivatives
|
||||
* Generate alternative image widths, using either an array of integers, or
|
||||
* a min width, a max width, and a step parameter to fill out the necessary
|
||||
* widths. Existing image alternatives won't be overwritten.
|
||||
*
|
||||
* @param int $min_width
|
||||
* @param int $max_width
|
||||
* @param int $step
|
||||
* @param int|int[] $min_width
|
||||
* @param int [$max_width=2500]
|
||||
* @param int [$step=200]
|
||||
* @return $this
|
||||
*/
|
||||
public function derivatives($min_width, $max_width, $step = 200) {
|
||||
$width = $min_width;
|
||||
|
||||
// Do not upscale images.
|
||||
if ($max_width > $this->get('width')) {
|
||||
$max_width = $this->get('width');
|
||||
}
|
||||
|
||||
while ($width <= $max_width) {
|
||||
$ratio = $width / $this->get('width');
|
||||
$derivative = MediumFactory::scaledFromMedium($this, 1, $ratio);
|
||||
if (is_array($derivative)) {
|
||||
$this->addDerivative($derivative['file']);
|
||||
public function derivatives($min_width, $max_width = 2500, $step = 200) {
|
||||
if (!empty($this->alternatives)) {
|
||||
$max = max(array_keys($this->alternatives));
|
||||
$base = $this->alternatives[$max];
|
||||
} else {
|
||||
$base = $this;
|
||||
}
|
||||
$width += $step;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a derivative
|
||||
*
|
||||
* @param ImageMedium $image
|
||||
*/
|
||||
public function addDerivative(ImageMedium $image) {
|
||||
$this->derivatives[$image->url()] = $image->get('width');
|
||||
$widths = [];
|
||||
|
||||
if (func_num_args() === 1) {
|
||||
foreach ((array) func_get_arg(0) as $width) {
|
||||
if ($width < $base->get('width')) {
|
||||
$widths[] = $width;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$max_width = min($max_width, $base->get('width'));
|
||||
|
||||
for ($width = $min_width; $width < $max_width; $width = $width + $step) {
|
||||
$widths[] = $width;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($widths as $width) {
|
||||
// Only generate image alternatives that don't already exist
|
||||
if (array_key_exists((int) $width, $this->alternatives)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$derivative = MediumFactory::fromFile($base->get('filepath'));
|
||||
|
||||
// It's possible that MediumFactory::fromFile returns null if the
|
||||
// original image file no longer exists and this class instance was
|
||||
// retrieved from the page cache
|
||||
if (isset($derivative)) {
|
||||
$index = 2;
|
||||
$alt_widths = array_keys($this->alternatives);
|
||||
sort($alt_widths);
|
||||
|
||||
foreach ($alt_widths as $i => $key) {
|
||||
if ($width > $key) {
|
||||
$index += max($i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
$basename = preg_replace('/(@\d+x){0,1}$/', "@{$width}w", $base->get('basename'), 1);
|
||||
$derivative->setImagePrettyName($basename);
|
||||
|
||||
$ratio = $base->get('width') / $width;
|
||||
$height = $derivative->get('height') / $ratio;
|
||||
|
||||
$derivative->resize($width, $height);
|
||||
$derivative->set('width', $width);
|
||||
$derivative->set('height', $height);
|
||||
|
||||
$this->addAlternative($ratio, $derivative);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -488,7 +512,11 @@ class ImageMedium extends Medium
|
||||
try {
|
||||
call_user_func_array([$this->image, $method], $args);
|
||||
|
||||
foreach ($this->alternatives as $ratio => $medium) {
|
||||
foreach ($this->alternatives as $medium) {
|
||||
if (!$medium->image) {
|
||||
$medium->image();
|
||||
}
|
||||
|
||||
$args_copy = $args;
|
||||
|
||||
// regular image: resize 400x400 -> 200x200
|
||||
@@ -496,7 +524,7 @@ class ImageMedium extends Medium
|
||||
if (isset(self::$magic_resize_actions[$method])) {
|
||||
foreach (self::$magic_resize_actions[$method] as $param) {
|
||||
if (isset($args_copy[$param])) {
|
||||
$args_copy[$param] = (int) $args_copy[$param] * $ratio;
|
||||
$args_copy[$param] *= $medium->get('ratio');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -519,7 +547,9 @@ class ImageMedium extends Medium
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
$file = $this->get('filepath');
|
||||
$cacheDir = $locator->findResource('cache://images', true);
|
||||
|
||||
// Use existing cache folder or if it doesn't exist, create it.
|
||||
$cacheDir = $locator->findResource('cache://images', true) ?: $locator->findResource('cache://images', true, true);
|
||||
|
||||
$this->image = ImageFile::open($file)
|
||||
->setCacheDir($cacheDir)
|
||||
|
||||
@@ -57,7 +57,7 @@ class Link implements RenderableInterface
|
||||
*
|
||||
* @param string $method
|
||||
* @param mixed $args
|
||||
* @return $this|mixed
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
|
||||
@@ -70,7 +70,7 @@ class Medium extends Data implements RenderableInterface
|
||||
/**
|
||||
* Return just metadata from the Medium object
|
||||
*
|
||||
* @return $this
|
||||
* @return Data
|
||||
*/
|
||||
public function meta()
|
||||
{
|
||||
@@ -84,7 +84,7 @@ class Medium extends Data implements RenderableInterface
|
||||
*/
|
||||
public function addMetaFile($filepath)
|
||||
{
|
||||
$this->merge(CompiledYamlFile::instance($filepath)->content());
|
||||
$this->merge((array)CompiledYamlFile::instance($filepath)->content());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +100,9 @@ class Medium extends Data implements RenderableInterface
|
||||
}
|
||||
|
||||
$alternative->set('ratio', $ratio);
|
||||
$this->alternatives[(float) $ratio] = $alternative;
|
||||
$width = $alternative->get('width');
|
||||
|
||||
$this->alternatives[$width] = $alternative;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,8 +150,8 @@ class Medium extends Data implements RenderableInterface
|
||||
/**
|
||||
* Get/set querystring for the file's url
|
||||
*
|
||||
* @param string $hash
|
||||
* @param boolean $withHash
|
||||
* @param string $querystring
|
||||
* @param boolean $withQuestionmark
|
||||
* @return string
|
||||
*/
|
||||
public function querystring($querystring = null, $withQuestionmark = true)
|
||||
@@ -227,7 +229,7 @@ class Medium extends Data implements RenderableInterface
|
||||
}
|
||||
|
||||
if (empty($attributes['alt'])) {
|
||||
if (!empty($alt)) {
|
||||
if (!empty($alt) || $alt === '') {
|
||||
$attributes['alt'] = $alt;
|
||||
} elseif (!empty($this->items['alt'])) {
|
||||
$attributes['alt'] = $this->items['alt'];
|
||||
|
||||
@@ -119,8 +119,8 @@ class MediumFactory
|
||||
}
|
||||
|
||||
$ratio = $to / $from;
|
||||
$width = (int) ($medium->get('width') * $ratio);
|
||||
$height = (int) ($medium->get('height') * $ratio);
|
||||
$width = $medium->get('width') * $ratio;
|
||||
$height = $medium->get('height') * $ratio;
|
||||
|
||||
$prev_basename = $medium->get('basename');
|
||||
$basename = str_replace('@'.$from.'x', '@'.$to.'x', $prev_basename);
|
||||
|
||||
@@ -15,7 +15,7 @@ trait StaticResizeTrait
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @return Medium
|
||||
* @return $this
|
||||
*/
|
||||
public function resize($width = null, $height = null)
|
||||
{
|
||||
|
||||
@@ -86,6 +86,7 @@ class Page
|
||||
protected $hide_home_route;
|
||||
protected $ssl;
|
||||
protected $template_format;
|
||||
protected $debugger;
|
||||
|
||||
/**
|
||||
* @var Page Unmodified (original) version of the page. Used for copying and moving the page.
|
||||
@@ -224,7 +225,7 @@ class Page
|
||||
*
|
||||
* @param string $var Raw content string
|
||||
*
|
||||
* @return Object Raw content string
|
||||
* @return string Raw content string
|
||||
*/
|
||||
public function raw($var = null)
|
||||
{
|
||||
@@ -424,6 +425,9 @@ class Page
|
||||
if (isset($this->header->template_format)) {
|
||||
$this->template_format = $this->header->template_format;
|
||||
}
|
||||
if (isset($this->header->debugger)) {
|
||||
$this->debugger = (bool)$this->header->debugger;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->header;
|
||||
@@ -566,22 +570,23 @@ class Page
|
||||
|
||||
|
||||
$process_markdown = $this->shouldProcess('markdown');
|
||||
$process_twig = $this->shouldProcess('twig');
|
||||
$process_twig = $this->shouldProcess('twig') || $this->modularTwig() ;
|
||||
|
||||
$cache_enable = isset($this->header->cache_enable) ? $this->header->cache_enable : $config->get('system.cache.enabled',
|
||||
true);
|
||||
$twig_first = isset($this->header->twig_first) ? $this->header->twig_first : $config->get('system.pages.twig_first',
|
||||
true);
|
||||
|
||||
// never cache twig means it's always run after content
|
||||
$never_cache_twig = isset($this->header->never_cache_twig) ? $this->header->never_cache_twig : $config->get('system.pages.never_cache_twig',
|
||||
false);
|
||||
|
||||
// if no cached-content run everything
|
||||
if ($this->content === false || $cache_enable === false) {
|
||||
$this->content = $this->raw_content;
|
||||
Grav::instance()->fireEvent('onPageContentRaw', new Event(['page' => $this]));
|
||||
if ($never_cache_twig) {
|
||||
if ($this->content === false || $cache_enable === false) {
|
||||
$this->content = $this->raw_content;
|
||||
Grav::instance()->fireEvent('onPageContentRaw', new Event(['page' => $this]));
|
||||
|
||||
if ($twig_first) {
|
||||
if ($process_twig) {
|
||||
$this->processTwig();
|
||||
}
|
||||
if ($process_markdown) {
|
||||
$this->processMarkdown();
|
||||
}
|
||||
@@ -589,21 +594,47 @@ class Page
|
||||
// Content Processed but not cached yet
|
||||
Grav::instance()->fireEvent('onPageContentProcessed', new Event(['page' => $this]));
|
||||
|
||||
} else {
|
||||
if ($process_markdown) {
|
||||
$this->processMarkdown();
|
||||
}
|
||||
|
||||
// Content Processed but not cached yet
|
||||
Grav::instance()->fireEvent('onPageContentProcessed', new Event(['page' => $this]));
|
||||
|
||||
if ($process_twig) {
|
||||
$this->processTwig();
|
||||
if ($cache_enable) {
|
||||
$this->cachePageContent();
|
||||
}
|
||||
}
|
||||
|
||||
if ($cache_enable) {
|
||||
$this->cachePageContent();
|
||||
if ($process_twig) {
|
||||
$this->processTwig();
|
||||
}
|
||||
|
||||
} else {
|
||||
if ($this->content === false || $cache_enable === false) {
|
||||
$this->content = $this->raw_content;
|
||||
Grav::instance()->fireEvent('onPageContentRaw', new Event(['page' => $this]));
|
||||
|
||||
if ($twig_first) {
|
||||
if ($process_twig) {
|
||||
$this->processTwig();
|
||||
}
|
||||
if ($process_markdown) {
|
||||
$this->processMarkdown();
|
||||
}
|
||||
|
||||
// Content Processed but not cached yet
|
||||
Grav::instance()->fireEvent('onPageContentProcessed', new Event(['page' => $this]));
|
||||
|
||||
} else {
|
||||
if ($process_markdown) {
|
||||
$this->processMarkdown();
|
||||
}
|
||||
|
||||
// Content Processed but not cached yet
|
||||
Grav::instance()->fireEvent('onPageContentProcessed', new Event(['page' => $this]));
|
||||
|
||||
if ($process_twig) {
|
||||
$this->processTwig();
|
||||
}
|
||||
}
|
||||
|
||||
if ($cache_enable) {
|
||||
$this->cachePageContent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,6 +804,9 @@ class Page
|
||||
if ($name == 'folder') {
|
||||
return preg_replace(PAGE_ORDER_PREFIX_REGEX, '', $this->folder);
|
||||
}
|
||||
if ($name == 'slug') {
|
||||
return $this->slug();
|
||||
}
|
||||
if ($name == 'name') {
|
||||
$language = $this->language() ? '.' . $this->language() : '';
|
||||
$name_val = str_replace($language . '.md', '', $this->name());
|
||||
@@ -856,12 +890,12 @@ class Page
|
||||
/**
|
||||
* Save page if there's a file assigned to it.
|
||||
*
|
||||
* @param bool $reorder Internal use.
|
||||
* @param bool|mixed $reorder Internal use.
|
||||
*/
|
||||
public function save($reorder = true)
|
||||
{
|
||||
// Perform move, copy or reordering if needed.
|
||||
$this->doRelocation($reorder);
|
||||
// Perform move, copy [or reordering] if needed.
|
||||
$this->doRelocation();
|
||||
|
||||
$file = $this->file();
|
||||
if ($file) {
|
||||
@@ -870,6 +904,13 @@ class Page
|
||||
$file->markdown($this->raw_content);
|
||||
$file->save();
|
||||
}
|
||||
|
||||
// Perform reorder if required
|
||||
if ($reorder && is_array($reorder)) {
|
||||
$this->doReorder($reorder);
|
||||
}
|
||||
|
||||
$this->_original = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -902,6 +943,8 @@ class Page
|
||||
$this->route(Grav::instance()['pages']->root()->route() . '/' . $this->slug());
|
||||
}
|
||||
|
||||
$this->raw_route = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -1121,7 +1164,7 @@ class Page
|
||||
}
|
||||
|
||||
if (empty($this->template_format)) {
|
||||
$this->template_format = Grav::instance()['uri']->extension();
|
||||
$this->template_format = Grav::instance()['uri']->extension('html');
|
||||
}
|
||||
|
||||
return $this->template_format;
|
||||
@@ -1180,7 +1223,7 @@ class Page
|
||||
$this->expires = $var;
|
||||
}
|
||||
|
||||
return empty($this->expires) ? Grav::instance()['config']->get('system.pages.expires') : $this->expires;
|
||||
return !isset($this->expires) ? Grav::instance()['config']->get('system.pages.expires') : $this->expires;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1345,6 +1388,20 @@ class Page
|
||||
return $this->process;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of the debugger override etting for this page
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function debugger()
|
||||
{
|
||||
if (isset($this->debugger) && $this->debugger === false) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to merge page metadata tags and build an array of Metadata objects
|
||||
* that can then be rendered in the page.
|
||||
@@ -1456,22 +1513,16 @@ class Page
|
||||
{
|
||||
if ($var !== null) {
|
||||
$order = !empty($var) ? sprintf('%02d.', (int)$var) : '';
|
||||
$this->folder($order . $this->slug());
|
||||
$this->folder($order . preg_replace(PAGE_ORDER_PREFIX_REGEX, '', $this->folder));
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
preg_match(PAGE_ORDER_PREFIX_REGEX, $this->folder, $order);
|
||||
|
||||
return isset($order[0]) ? $order[0] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL with host information, aka Permalink.
|
||||
* @return string The permalink.
|
||||
*/
|
||||
public function permalink()
|
||||
{
|
||||
return $this->url(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL for a page - alias of url().
|
||||
*
|
||||
@@ -1484,16 +1535,36 @@ class Page
|
||||
return $this->url($include_host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL with host information, aka Permalink.
|
||||
* @return string The permalink.
|
||||
*/
|
||||
public function permalink()
|
||||
{
|
||||
return $this->url(true, false, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the canonical URL for a page
|
||||
*
|
||||
* @param bool $include_lang
|
||||
* @return string
|
||||
*/
|
||||
public function canonical($include_lang = true)
|
||||
{
|
||||
return $this->url(true, true, $include_lang);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $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)
|
||||
public function url($include_host = false, $canonical = false, $include_lang = true, $raw_route = false)
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
|
||||
@@ -1529,6 +1600,8 @@ class Page
|
||||
// get canonical route if requested
|
||||
if ($canonical) {
|
||||
$route = $pre_route . $this->routeCanonical();
|
||||
} elseif ($raw_route) {
|
||||
$route = $pre_route . $this->rawRoute();
|
||||
} else {
|
||||
$route = $pre_route . $this->route();
|
||||
}
|
||||
@@ -1669,7 +1742,10 @@ class Page
|
||||
public function id($var = null)
|
||||
{
|
||||
if ($var !== null) {
|
||||
$this->id = $var;
|
||||
// store unique per language
|
||||
$active_lang = Grav::instance()['language']->getLanguage() ?: '';
|
||||
$id = $active_lang . $var;
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
return $this->id;
|
||||
@@ -1696,7 +1772,7 @@ class Page
|
||||
*
|
||||
* @param string $var redirect url
|
||||
*
|
||||
* @return array
|
||||
* @return string
|
||||
*/
|
||||
public function redirect($var = null)
|
||||
{
|
||||
@@ -1980,7 +2056,6 @@ class Page
|
||||
if ($var !== null) {
|
||||
$this->modular_twig = (bool)$var;
|
||||
if ($var) {
|
||||
$this->process['twig'] = true;
|
||||
$this->visible(false);
|
||||
// some routable logic
|
||||
if (empty($this->header->routable)) {
|
||||
@@ -2119,7 +2194,7 @@ class Page
|
||||
*
|
||||
* @param integer $direction either -1 or +1
|
||||
*
|
||||
* @return Page the sibling page
|
||||
* @return Page|bool the sibling page
|
||||
*/
|
||||
public function adjacentSibling($direction = 1)
|
||||
{
|
||||
@@ -2241,7 +2316,7 @@ class Page
|
||||
}
|
||||
|
||||
if (!isset($params['items'])) {
|
||||
return [];
|
||||
return new Collection();
|
||||
}
|
||||
|
||||
$collection = $this->evaluate($params['items']);
|
||||
@@ -2269,6 +2344,7 @@ class Page
|
||||
continue;
|
||||
}
|
||||
foreach ($items as $item) {
|
||||
$item = rawurldecode($item);
|
||||
if (empty($page->taxonomy[$taxonomy]) || !in_array(htmlspecialchars_decode($item,
|
||||
ENT_QUOTES), $page->taxonomy[$taxonomy])
|
||||
) {
|
||||
@@ -2291,7 +2367,16 @@ class Page
|
||||
$by = isset($params['order']['by']) ? $params['order']['by'] : 'default';
|
||||
$dir = isset($params['order']['dir']) ? $params['order']['dir'] : 'asc';
|
||||
$custom = isset($params['order']['custom']) ? $params['order']['custom'] : null;
|
||||
$collection->order($by, $dir, $custom);
|
||||
$sort_flags = isset($params['order']['sort_flags']) ? $params['order']['sort_flags'] : null;
|
||||
|
||||
if (is_array($sort_flags)) {
|
||||
$sort_flags = array_map('constant', $sort_flags); //transform strings to constant value
|
||||
$sort_flags = array_reduce($sort_flags, function ($a, $b) {
|
||||
return $a | $b;
|
||||
}, 0); //merge constant values using bit or
|
||||
}
|
||||
|
||||
$collection->order($by, $dir, $custom, $sort_flags);
|
||||
}
|
||||
|
||||
/** @var Grav $grav */
|
||||
@@ -2411,7 +2496,10 @@ class Page
|
||||
switch ($parts[0]) {
|
||||
case 'modular':
|
||||
$results = new Collection();
|
||||
$results = $results->addPage($page)->Modular();
|
||||
foreach ($page->children() as $child) {
|
||||
$results = $results->addPage($child);
|
||||
}
|
||||
$results->modular();
|
||||
break;
|
||||
case 'page':
|
||||
case 'self':
|
||||
@@ -2500,6 +2588,16 @@ class Page
|
||||
return $file && $file->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the current folder exists
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function folderExists()
|
||||
{
|
||||
return file_exists($this->path());
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans the path.
|
||||
*
|
||||
@@ -2518,65 +2616,62 @@ class Page
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves or copies the page in filesystem.
|
||||
* Reorders all siblings according to a defined order
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @param bool $reorder
|
||||
*
|
||||
* @throws Exception
|
||||
* @param $new_order
|
||||
*/
|
||||
protected function doRelocation($reorder)
|
||||
protected function doReorder($new_order)
|
||||
{
|
||||
if (!$this->_original) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do reordering.
|
||||
if ($reorder && $this->order() != $this->_original->order()) {
|
||||
/** @var Pages $pages */
|
||||
$pages = Grav::instance()['pages'];
|
||||
$pages = Grav::instance()['pages'];
|
||||
$pages->init();
|
||||
|
||||
$parent = $this->parent();
|
||||
$this->_original->path($this->path());
|
||||
|
||||
// Extract visible children from the parent page.
|
||||
$list = [];
|
||||
/** @var Page $page */
|
||||
foreach ($parent->children()->visible() as $page) {
|
||||
if ($page->order()) {
|
||||
$list[$page->slug] = $page->path();
|
||||
}
|
||||
}
|
||||
$siblings = $this->parent()->children();
|
||||
$siblings->order('slug', 'asc', $new_order);
|
||||
|
||||
// If page was moved, take it out of the list.
|
||||
if ($this->_action == 'move') {
|
||||
unset($list[$this->slug()]);
|
||||
}
|
||||
$counter = 0;
|
||||
|
||||
$list = array_values($list);
|
||||
// Reorder all moved pages.
|
||||
foreach ($siblings as $slug => $page) {
|
||||
$order = intval(trim($page->order(),'.'));
|
||||
$counter++;
|
||||
|
||||
// Then add it back to the new location (if needed).
|
||||
if ($this->order()) {
|
||||
array_splice($list, min($this->order() - 1, count($list)), 0, [$this->path()]);
|
||||
}
|
||||
|
||||
// Reorder all moved pages.
|
||||
foreach ($list as $order => $path) {
|
||||
if ($path == $this->path()) {
|
||||
if ($order) {
|
||||
if ($page->path() == $this->path() && $this->folderExists()) {
|
||||
// Handle current page; we do want to change ordering number, but nothing else.
|
||||
$this->order($order + 1);
|
||||
$this->order($counter);
|
||||
$this->save(false);
|
||||
} else {
|
||||
// Handle all the other pages.
|
||||
$page = $pages->get($path);
|
||||
|
||||
if ($page && $page->exists() && !$page->_action && $page->order() != $order + 1) {
|
||||
$page = $page->move($parent);
|
||||
$page->order($order + 1);
|
||||
$page = $pages->get($page->path());
|
||||
if ($page && $page->folderExists() && !$page->_action) {
|
||||
$page = $page->move($this->parent());
|
||||
$page->order($counter);
|
||||
$page->save(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves or copies the page in filesystem.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function doRelocation()
|
||||
{
|
||||
if (!$this->_original) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_dir($this->_original->path())) {
|
||||
if ($this->_action == 'move') {
|
||||
Folder::move($this->_original->path(), $this->path());
|
||||
@@ -2592,12 +2687,11 @@ class Page
|
||||
}
|
||||
}
|
||||
|
||||
$this->_original = null;
|
||||
}
|
||||
|
||||
protected function setPublishState()
|
||||
{
|
||||
// Handle publishing dates if no explict published option set
|
||||
// Handle publishing dates if no explicit published option set
|
||||
if (Grav::instance()['config']->get('system.pages.publish_dates') && !isset($this->header->published)) {
|
||||
// unpublish if required, if not clear cache right before page should be unpublished
|
||||
if ($this->unpublishDate()) {
|
||||
|
||||
@@ -21,6 +21,7 @@ use Grav\Plugin\Admin;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use Whoops\Exception\ErrorException;
|
||||
use Collator as Collator;
|
||||
|
||||
class Pages
|
||||
{
|
||||
@@ -89,6 +90,8 @@ class Pages
|
||||
*/
|
||||
static protected $home_route;
|
||||
|
||||
protected $pages_cache_id;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@@ -153,7 +156,7 @@ class Pages
|
||||
/**
|
||||
* Returns a list of all pages.
|
||||
*
|
||||
* @return Page
|
||||
* @return array|Page[]
|
||||
*/
|
||||
public function instances()
|
||||
{
|
||||
@@ -197,7 +200,7 @@ class Pages
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function sort(Page $page, $order_by = null, $order_dir = null)
|
||||
public function sort(Page $page, $order_by = null, $order_dir = null, $sort_flags = null)
|
||||
{
|
||||
if ($order_by === null) {
|
||||
$order_by = $page->orderBy();
|
||||
@@ -214,7 +217,7 @@ class Pages
|
||||
}
|
||||
|
||||
if (!isset($this->sort[$path][$order_by])) {
|
||||
$this->buildSort($path, $children, $order_by, $page->orderManual());
|
||||
$this->buildSort($path, $children, $order_by, $page->orderManual(), $sort_flags);
|
||||
}
|
||||
|
||||
$sort = $this->sort[$path][$order_by];
|
||||
@@ -235,7 +238,7 @@ class Pages
|
||||
* @return array
|
||||
* @internal
|
||||
*/
|
||||
public function sortCollection(Collection $collection, $orderBy, $orderDir = 'asc', $orderManual = null)
|
||||
public function sortCollection(Collection $collection, $orderBy, $orderDir = 'asc', $orderManual = null, $sort_flags = null)
|
||||
{
|
||||
$items = $collection->toArray();
|
||||
if (!$items) {
|
||||
@@ -244,7 +247,7 @@ class Pages
|
||||
|
||||
$lookup = md5(json_encode($items) . json_encode($orderManual) . $orderBy . $orderDir);
|
||||
if (!isset($this->sort[$lookup][$orderBy])) {
|
||||
$this->buildSort($lookup, $items, $orderBy, $orderManual);
|
||||
$this->buildSort($lookup, $items, $orderBy, $orderManual, $sort_flags);
|
||||
}
|
||||
|
||||
$sort = $this->sort[$lookup][$orderBy];
|
||||
@@ -342,13 +345,20 @@ class Pages
|
||||
$page = $this->dispatch($route, $all);
|
||||
} else {
|
||||
// Try Regex style redirects
|
||||
$uri = $this->grav['uri'];
|
||||
$source_url = $url;
|
||||
$extension = $uri->extension();
|
||||
if (isset($extension) && !Utils::endsWith($uri->url(), $extension)) {
|
||||
$source_url.= '.' . $extension;
|
||||
}
|
||||
|
||||
$site_redirects = $config->get("site.redirects");
|
||||
if (is_array($site_redirects)) {
|
||||
foreach ((array)$site_redirects as $pattern => $replace) {
|
||||
$pattern = '#' . $pattern . '#';
|
||||
try {
|
||||
$found = preg_replace($pattern, $replace, $url);
|
||||
if ($found != $url) {
|
||||
$found = preg_replace($pattern, $replace, $source_url);
|
||||
if ($found != $source_url) {
|
||||
$this->grav->redirectLangSafe($found);
|
||||
}
|
||||
} catch (ErrorException $e) {
|
||||
@@ -363,8 +373,8 @@ class Pages
|
||||
foreach ((array)$site_routes as $pattern => $replace) {
|
||||
$pattern = '#' . $pattern . '#';
|
||||
try {
|
||||
$found = preg_replace($pattern, $replace, $url);
|
||||
if ($found != $url) {
|
||||
$found = preg_replace($pattern, $replace, $source_url);
|
||||
if ($found != $source_url) {
|
||||
$page = $this->dispatch($found, $all);
|
||||
}
|
||||
} catch (ErrorException $e) {
|
||||
@@ -564,17 +574,21 @@ class Pages
|
||||
*/
|
||||
public static function pageTypes()
|
||||
{
|
||||
/** @var Admin $admin */
|
||||
$admin = Grav::instance()['admin'];
|
||||
if (isset(Grav::instance()['admin'])) {
|
||||
/** @var Admin $admin */
|
||||
$admin = Grav::instance()['admin'];
|
||||
|
||||
/** @var Page $page */
|
||||
$page = $admin->getPage($admin->route);
|
||||
/** @var Page $page */
|
||||
$page = $admin->getPage($admin->route);
|
||||
|
||||
if ($page && $page->modular()) {
|
||||
return static::modularTypes();
|
||||
if ($page && $page->modular()) {
|
||||
return static::modularTypes();
|
||||
}
|
||||
|
||||
return static::types();
|
||||
}
|
||||
|
||||
return static::types();
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -647,11 +661,12 @@ class Pages
|
||||
|
||||
$parents = $pages->getList(null, 0, $rawRoutes);
|
||||
|
||||
/** @var Admin $admin */
|
||||
$admin = $grav['admin'];
|
||||
if (isset($grav['admin'])) {
|
||||
// Remove current route from parents
|
||||
|
||||
/** @var Admin $admin */
|
||||
$admin = $grav['admin'];
|
||||
|
||||
// Remove current route from parents
|
||||
if (isset($admin)) {
|
||||
$page = $admin->getPage($admin->route);
|
||||
$page_route = $page->route();
|
||||
if (isset($parents[$page_route])) {
|
||||
@@ -752,18 +767,19 @@ class Pages
|
||||
break;
|
||||
case 'hash':
|
||||
$hash = Folder::hashAllFiles($pages_dir);
|
||||
break;
|
||||
default:
|
||||
$hash = Folder::lastModifiedFile($pages_dir);
|
||||
}
|
||||
|
||||
$page_cache_id = md5($pages_dir . $hash . $language->getActive() . $config->checksum());
|
||||
$this->pages_cache_id = md5($pages_dir . $hash . $language->getActive() . $config->checksum());
|
||||
|
||||
list($this->instances, $this->routes, $this->children, $taxonomy_map, $this->sort) = $cache->fetch($page_cache_id);
|
||||
list($this->instances, $this->routes, $this->children, $taxonomy_map, $this->sort) = $cache->fetch($this->pages_cache_id);
|
||||
if (!$this->instances) {
|
||||
$this->grav['debugger']->addMessage('Page cache missed, rebuilding pages..');
|
||||
|
||||
// recurse pages and cache result
|
||||
$this->resetPages($pages_dir, $page_cache_id);
|
||||
$this->resetPages($pages_dir, $this->pages_cache_id);
|
||||
|
||||
} else {
|
||||
// If pages was found in cache, set the taxonomy
|
||||
@@ -780,9 +796,8 @@ class Pages
|
||||
* Accessible method to manually reset the pages cache
|
||||
*
|
||||
* @param $pages_dir
|
||||
* @param $page_cache_id
|
||||
*/
|
||||
public function resetPages($pages_dir, $page_cache_id)
|
||||
public function resetPages($pages_dir)
|
||||
{
|
||||
$this->recurse($pages_dir);
|
||||
$this->buildRoutes();
|
||||
@@ -795,7 +810,7 @@ class Pages
|
||||
$taxonomy = $this->grav['taxonomy'];
|
||||
|
||||
// save pages, routes, taxonomy, and sort to cache
|
||||
$cache->save($page_cache_id, [$this->instances, $this->routes, $this->children, $taxonomy->taxonomy(), $this->sort]);
|
||||
$cache->save($this->pages_cache_id, [$this->instances, $this->routes, $this->children, $taxonomy->taxonomy(), $this->sort]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1017,12 +1032,11 @@ class Pages
|
||||
* @throws \RuntimeException
|
||||
* @internal
|
||||
*/
|
||||
protected function buildSort($path, array $pages, $order_by = 'default', $manual = null)
|
||||
protected function buildSort($path, array $pages, $order_by = 'default', $manual = null, $sort_flags = null)
|
||||
{
|
||||
$list = [];
|
||||
$header_default = null;
|
||||
$header_query = null;
|
||||
$sort_flags = SORT_NATURAL | SORT_FLAG_CASE;
|
||||
|
||||
// do this header query work only once
|
||||
if (strpos($order_by, 'header.') === 0) {
|
||||
@@ -1050,6 +1064,14 @@ class Pages
|
||||
$list[$key] = $child->modified();
|
||||
$sort_flags = SORT_REGULAR;
|
||||
break;
|
||||
case 'publish_date':
|
||||
$list[$key] = $child->publishDate();
|
||||
$sort_flags = SORT_REGULAR;
|
||||
break;
|
||||
case 'unpublish_date':
|
||||
$list[$key] = $child->unpublishDate();
|
||||
$sort_flags = SORT_REGULAR;
|
||||
break;
|
||||
case 'slug':
|
||||
$list[$key] = $child->slug();
|
||||
break;
|
||||
@@ -1059,21 +1081,27 @@ class Pages
|
||||
case (is_string($header_query[0])):
|
||||
$child_header = new Header((array)$child->header());
|
||||
$header_value = $child_header->get($header_query[0]);
|
||||
if ($header_value) {
|
||||
if (is_array($header_value)) {
|
||||
$list[$key] = implode(',',$header_value);
|
||||
} elseif ($header_value) {
|
||||
$list[$key] = $header_value;
|
||||
} else {
|
||||
$list[$key] = $header_default ?: $key;
|
||||
}
|
||||
$sort_flags = SORT_REGULAR;
|
||||
$sort_flags = $sort_flags ?: SORT_REGULAR;
|
||||
break;
|
||||
case 'manual':
|
||||
case 'default':
|
||||
default:
|
||||
$list[$key] = $key;
|
||||
$sort_flags = SORT_REGULAR;
|
||||
$sort_flags = $sort_flags ?: SORT_REGULAR;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$sort_flags) {
|
||||
$sort_flags = SORT_NATURAL | SORT_FLAG_CASE;
|
||||
}
|
||||
|
||||
// handle special case when order_by is random
|
||||
if ($order_by == 'random') {
|
||||
$list = $this->arrayShuffle($list);
|
||||
@@ -1081,7 +1109,7 @@ class Pages
|
||||
// else just sort the list according to specified key
|
||||
if (extension_loaded('intl')) {
|
||||
$locale = setlocale(LC_COLLATE, 0); //`setlocale` with a 0 param returns the current locale set
|
||||
$col = \Collator::create($locale);
|
||||
$col = Collator::create($locale);
|
||||
if ($col) {
|
||||
$col->asort($list, $sort_flags);
|
||||
} else {
|
||||
@@ -1138,4 +1166,17 @@ class Pages
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Pages cache ID
|
||||
*
|
||||
* this is particularly useful to know if pages have changed and you want
|
||||
* to sync another cache with pages cache - works best in `onPagesInitialized()`
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPagesCacheId()
|
||||
{
|
||||
return $this->pages_cache_id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,21 +246,23 @@ class Plugin implements EventSubscriberInterface, \ArrayAccess
|
||||
/**
|
||||
* Merge global and page configurations.
|
||||
*
|
||||
* @param Page $page The page to merge the configurations with the
|
||||
* @param Page $page The page to merge the configurations with the
|
||||
* plugin settings.
|
||||
* @param bool $deep Should you use deep or shallow merging
|
||||
* @param array $params Array of additional configuration options to
|
||||
* @param mixed $deep false = shallow|true = recursive|merge = recursive+unique
|
||||
* @param array $params Array of additional configuration options to
|
||||
* merge with the plugin settings.
|
||||
* @param string $type Is this 'plugins' or 'themes'
|
||||
*
|
||||
* @return \Grav\Common\Data\Data
|
||||
* @return Data
|
||||
*/
|
||||
protected function mergeConfig(Page $page, $deep = false, $params = [])
|
||||
protected function mergeConfig(Page $page, $deep = false, $params = [], $type = 'plugins')
|
||||
{
|
||||
$class_name = $this->name;
|
||||
$class_name_merged = $class_name . '.merged';
|
||||
$defaults = $this->config->get('plugins.' . $class_name, []);
|
||||
$defaults = $this->config->get($type . '.' . $class_name, []);
|
||||
$page_header = $page->header();
|
||||
$header = [];
|
||||
|
||||
if (!isset($page_header->$class_name_merged) && isset($page_header->$class_name)) {
|
||||
// Get default plugin configurations and retrieve page header configuration
|
||||
$config = $page_header->$class_name;
|
||||
@@ -269,11 +271,8 @@ class Plugin implements EventSubscriberInterface, \ArrayAccess
|
||||
$config = ['enabled' => $config];
|
||||
}
|
||||
// Merge page header settings using deep or shallow merging technique
|
||||
if ($deep) {
|
||||
$header = array_replace_recursive($defaults, $config);
|
||||
} else {
|
||||
$header = array_merge($defaults, $config);
|
||||
}
|
||||
$header = $this->mergeArrays($deep, $defaults, $config);
|
||||
|
||||
// Create new config object and set it on the page object so it's cached for next time
|
||||
$page->modifyHeader($class_name_merged, new Data($header));
|
||||
} else if (isset($page_header->$class_name_merged)) {
|
||||
@@ -284,16 +283,31 @@ class Plugin implements EventSubscriberInterface, \ArrayAccess
|
||||
$header = $defaults;
|
||||
}
|
||||
// Merge additional parameter with configuration options
|
||||
if ($deep) {
|
||||
$header = array_replace_recursive($header, $params);
|
||||
} else {
|
||||
$header = array_merge($header, $params);
|
||||
}
|
||||
$header = $this->mergeArrays($deep, $header, $params);
|
||||
|
||||
// Return configurations as a new data config class
|
||||
return new Data($header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge arrays based on deepness
|
||||
*
|
||||
* @param bool $deep
|
||||
* @param $array1
|
||||
* @param $array2
|
||||
* @return array|mixed
|
||||
*/
|
||||
private function mergeArrays($deep = false, $array1, $array2)
|
||||
{
|
||||
if ($deep == 'merge') {
|
||||
return Utils::arrayMergeRecursiveUnique($array1, $array2);
|
||||
} elseif ($deep === true) {
|
||||
return array_replace_recursive($array1, $array2);
|
||||
} else {
|
||||
return array_merge($array1, $array2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists to disk the plugin parameters currently stored in the Grav Config object
|
||||
*
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
|
||||
namespace Grav\Common\Processors;
|
||||
|
||||
class AssetsProcessor extends ProcessorBase implements ProcessorInterface {
|
||||
|
||||
class AssetsProcessor extends ProcessorBase implements ProcessorInterface
|
||||
{
|
||||
public $id = 'assets';
|
||||
public $title = 'Assets';
|
||||
|
||||
public function process() {
|
||||
$this->container['assets']->init();
|
||||
$this->container->fireEvent('onAssetsInitialized');
|
||||
public function process()
|
||||
{
|
||||
$this->container['assets']->init();
|
||||
$this->container->fireEvent('onAssetsInitialized');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
|
||||
namespace Grav\Common\Processors;
|
||||
|
||||
class ConfigurationProcessor extends ProcessorBase implements ProcessorInterface {
|
||||
|
||||
class ConfigurationProcessor extends ProcessorBase implements ProcessorInterface
|
||||
{
|
||||
public $id = '_config';
|
||||
public $title = 'Configuration';
|
||||
|
||||
public function process() {
|
||||
$this->container['config']->init();
|
||||
return $this->container['plugins']->setup();
|
||||
public function process()
|
||||
{
|
||||
$this->container['config']->init();
|
||||
return $this->container['plugins']->setup();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
|
||||
namespace Grav\Common\Processors;
|
||||
|
||||
class DebuggerAssetsProcessor extends ProcessorBase implements ProcessorInterface {
|
||||
|
||||
class DebuggerAssetsProcessor extends ProcessorBase implements ProcessorInterface
|
||||
{
|
||||
public $id = 'debugger_assets';
|
||||
public $title = 'Debugger Assets';
|
||||
|
||||
public function process() {
|
||||
$this->container['debugger']->addAssets();
|
||||
public function process()
|
||||
{
|
||||
$this->container['debugger']->addAssets();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
|
||||
namespace Grav\Common\Processors;
|
||||
|
||||
class DebuggerInitProcessor extends ProcessorBase implements ProcessorInterface {
|
||||
|
||||
class DebuggerInitProcessor extends ProcessorBase implements ProcessorInterface
|
||||
{
|
||||
public $id = '_debugger';
|
||||
public $title = 'Init Debugger';
|
||||
|
||||
public function process() {
|
||||
$this->container['debugger']->init();
|
||||
public function process()
|
||||
{
|
||||
$this->container['debugger']->init();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
|
||||
namespace Grav\Common\Processors;
|
||||
|
||||
class ErrorsProcessor extends ProcessorBase implements ProcessorInterface {
|
||||
|
||||
class ErrorsProcessor extends ProcessorBase implements ProcessorInterface
|
||||
{
|
||||
public $id = '_errors';
|
||||
public $title = 'Error Handlers Reset';
|
||||
|
||||
public function process() {
|
||||
$this->container['errors']->resetHandlers();
|
||||
public function process()
|
||||
{
|
||||
$this->container['errors']->resetHandlers();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,12 +8,13 @@
|
||||
|
||||
namespace Grav\Common\Processors;
|
||||
|
||||
class InitializeProcessor extends ProcessorBase implements ProcessorInterface {
|
||||
|
||||
class InitializeProcessor extends ProcessorBase implements ProcessorInterface
|
||||
{
|
||||
public $id = 'init';
|
||||
public $title = 'Initialize';
|
||||
|
||||
public function process() {
|
||||
public function process()
|
||||
{
|
||||
$this->container['config']->debug();
|
||||
|
||||
// Use output buffering to prevent headers from being sent too early.
|
||||
@@ -36,5 +37,4 @@ class InitializeProcessor extends ProcessorBase implements ProcessorInterface {
|
||||
|
||||
$this->container->setLocale();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ use Grav\Common\Page\Page;
|
||||
|
||||
class PagesProcessor extends ProcessorBase implements ProcessorInterface
|
||||
{
|
||||
|
||||
public $id = 'pages';
|
||||
public $title = 'Pages';
|
||||
|
||||
@@ -41,5 +40,4 @@ class PagesProcessor extends ProcessorBase implements ProcessorInterface
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
|
||||
namespace Grav\Common\Processors;
|
||||
|
||||
class PluginsProcessor extends ProcessorBase implements ProcessorInterface {
|
||||
|
||||
class PluginsProcessor extends ProcessorBase implements ProcessorInterface
|
||||
{
|
||||
public $id = 'plugins';
|
||||
public $title = 'Plugins';
|
||||
|
||||
public function process() {
|
||||
$this->container['plugins']->init();
|
||||
$this->container->fireEvent('onPluginsInitialized');
|
||||
public function process()
|
||||
{
|
||||
$this->container['plugins']->init();
|
||||
$this->container->fireEvent('onPluginsInitialized');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,10 +8,18 @@
|
||||
|
||||
namespace Grav\Common\Processors;
|
||||
|
||||
class ProcessorBase {
|
||||
use Grav\Common\Grav;
|
||||
|
||||
public function __construct($container) {
|
||||
$this->container = $container;
|
||||
}
|
||||
class ProcessorBase
|
||||
{
|
||||
/**
|
||||
* @var Grav
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
public function __construct(Grav $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
namespace Grav\Common\Processors;
|
||||
|
||||
interface ProcessorInterface {
|
||||
public function process();
|
||||
interface ProcessorInterface
|
||||
{
|
||||
public function process();
|
||||
}
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
|
||||
namespace Grav\Common\Processors;
|
||||
|
||||
class RenderProcessor extends ProcessorBase implements ProcessorInterface {
|
||||
|
||||
class RenderProcessor extends ProcessorBase implements ProcessorInterface
|
||||
{
|
||||
public $id = 'render';
|
||||
public $title = 'Render';
|
||||
|
||||
public function process() {
|
||||
$this->container->output = $this->container['output'];
|
||||
$this->container->fireEvent('onOutputGenerated');
|
||||
public function process()
|
||||
{
|
||||
$this->container->output = $this->container['output'];
|
||||
$this->container->fireEvent('onOutputGenerated');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
|
||||
namespace Grav\Common\Processors;
|
||||
|
||||
class SiteSetupProcessor extends ProcessorBase implements ProcessorInterface {
|
||||
|
||||
class SiteSetupProcessor extends ProcessorBase implements ProcessorInterface
|
||||
{
|
||||
public $id = '_setup';
|
||||
public $title = 'Site Setup';
|
||||
|
||||
public function process() {
|
||||
$this->container['setup']->init();
|
||||
$this->container['streams'];
|
||||
public function process()
|
||||
{
|
||||
$this->container['setup']->init();
|
||||
$this->container['streams'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,16 +8,16 @@
|
||||
|
||||
namespace Grav\Common\Processors;
|
||||
|
||||
class TasksProcessor extends ProcessorBase implements ProcessorInterface {
|
||||
|
||||
class TasksProcessor extends ProcessorBase implements ProcessorInterface
|
||||
{
|
||||
public $id = 'tasks';
|
||||
public $title = 'Tasks';
|
||||
|
||||
public function process() {
|
||||
public function process()
|
||||
{
|
||||
$task = $this->container['task'];
|
||||
if ($task) {
|
||||
$this->container->fireEvent('onTask.' . $task);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
|
||||
namespace Grav\Common\Processors;
|
||||
|
||||
class ThemesProcessor extends ProcessorBase implements ProcessorInterface {
|
||||
|
||||
class ThemesProcessor extends ProcessorBase implements ProcessorInterface
|
||||
{
|
||||
public $id = 'themes';
|
||||
public $title = 'Themes';
|
||||
|
||||
public function process() {
|
||||
$this->container['themes']->init();
|
||||
public function process()
|
||||
{
|
||||
$this->container['themes']->init();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,13 +8,14 @@
|
||||
|
||||
namespace Grav\Common\Processors;
|
||||
|
||||
class TwigProcessor extends ProcessorBase implements ProcessorInterface {
|
||||
|
||||
class TwigProcessor extends ProcessorBase implements ProcessorInterface
|
||||
{
|
||||
public $id = 'twig';
|
||||
public $title = 'Twig';
|
||||
|
||||
public function process() {
|
||||
$this->container['twig']->init();
|
||||
public function process()
|
||||
{
|
||||
$this->container['twig']->init();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ use Grav\Common\Assets;
|
||||
|
||||
class AssetsServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
public function register(Container $container) {
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container['assets'] = new Assets();
|
||||
}
|
||||
}
|
||||
|
||||
30
system/src/Grav/Common/Service/MessagesServiceProvider.php
Normal file
30
system/src/Grav/Common/Service/MessagesServiceProvider.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Service
|
||||
*
|
||||
* @copyright Copyright (C) 2014 - 2016 RocketTheme, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Service;
|
||||
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use RocketTheme\Toolbox\Session\Message;
|
||||
|
||||
class MessagesServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
public function register(Container $container)
|
||||
{
|
||||
// Define session message service.
|
||||
$container['messages'] = function ($c) {
|
||||
$session = $c['session'];
|
||||
|
||||
if (!isset($session->messages)) {
|
||||
$session->messages = new Message;
|
||||
}
|
||||
|
||||
return $session->messages;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,8 @@ use Pimple\ServiceProviderInterface;
|
||||
|
||||
class OutputServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
public function register(Container $container) {
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container['output'] = function ($c) {
|
||||
return $c['twig']->processSite($c['page']->templateFormat());
|
||||
};
|
||||
|
||||
@@ -8,21 +8,23 @@
|
||||
|
||||
namespace Grav\Common\Service;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Language\Language;
|
||||
use Grav\Common\Page\Page;
|
||||
use Grav\Common\Page\Pages;
|
||||
use Grav\Common\Uri;
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
|
||||
class PageServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
public function register(Container $container) {
|
||||
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container['page'] = function ($c) {
|
||||
/** @var Grav $c */
|
||||
|
||||
/** @var Pages $pages */
|
||||
$pages = $c['pages'];
|
||||
/** @var Language $language */
|
||||
$language = $c['language'];
|
||||
|
||||
/** @var Uri $uri */
|
||||
$uri = $c['uri'];
|
||||
@@ -34,6 +36,14 @@ class PageServiceProvider implements ServiceProviderInterface
|
||||
|
||||
// Redirection tests
|
||||
if ($page) {
|
||||
/** @var Language $language */
|
||||
$language = $c['language'];
|
||||
|
||||
// some debugger override logic
|
||||
if ($page->debugger() === false) {
|
||||
Grav::instance()['debugger']->enabled(false);
|
||||
}
|
||||
|
||||
if ($c['config']->get('system.force_ssl')) {
|
||||
if (!isset($_SERVER['HTTPS']) || $_SERVER["HTTPS"] != "on") {
|
||||
$url = "https://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];
|
||||
|
||||
@@ -8,12 +8,14 @@
|
||||
|
||||
namespace Grav\Common\Service;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
|
||||
class TaskServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
public function register(Container $container) {
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container['task'] = function ($c) {
|
||||
/** @var Grav $c */
|
||||
return !empty($_POST['task']) ? $_POST['task'] : $c['uri']->param('task');
|
||||
|
||||
@@ -93,6 +93,8 @@ class Taxonomy
|
||||
foreach ((array)$items as $item) {
|
||||
if (isset($this->taxonomy_map[$taxonomy][$item])) {
|
||||
$matches[] = $this->taxonomy_map[$taxonomy][$item];
|
||||
} else {
|
||||
$matches[] = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,4 +128,22 @@ class Taxonomy
|
||||
|
||||
return $this->taxonomy_map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets item keys per taxonomy
|
||||
*
|
||||
* @param string $taxonomy taxonomy name
|
||||
*
|
||||
* @return array keys of this taxonomy
|
||||
*/
|
||||
public function getTaxonomyItemKeys($taxonomy) {
|
||||
if (isset($this->taxonomy_map[$taxonomy])) {
|
||||
|
||||
$results = array_keys($this->taxonomy_map[$taxonomy]);
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Page\Page;
|
||||
use Grav\Common\Config\Config;
|
||||
use RocketTheme\Toolbox\File\YamlFile;
|
||||
|
||||
@@ -59,6 +60,13 @@ class Theme extends Plugin
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the mergeConfig method to work for themes
|
||||
*/
|
||||
protected function mergeConfig(Page $page, $deep = 'merge', $params = [], $type = 'themes') {
|
||||
return parent::mergeConfig($page, $deep, $params, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simpler getter for the theme blueprint
|
||||
*
|
||||
|
||||
@@ -311,17 +311,23 @@ class Themes extends Iterator
|
||||
*/
|
||||
protected function autoloadTheme($class)
|
||||
{
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $this->grav['locator'];
|
||||
|
||||
$prefix = "Grav\\Theme";
|
||||
if (false !== strpos($class, $prefix)) {
|
||||
// Remove prefix from class
|
||||
$class = substr($class, strlen($prefix));
|
||||
|
||||
// Replace namespace tokens to directory separators
|
||||
// Try Old style theme classes
|
||||
$path = strtolower(ltrim(preg_replace('#\\\|_(?!.+\\\)#', '/', $class), '/'));
|
||||
$file = $locator->findResource("themes://{$path}/{$path}.php");
|
||||
$file = $this->grav['locator']->findResource("themes://{$path}/{$path}.php");
|
||||
|
||||
// Load class
|
||||
if (file_exists($file)) {
|
||||
return include_once($file);
|
||||
}
|
||||
|
||||
// Replace namespace tokens to directory separators
|
||||
$path = $this->grav['inflector']->hyphenize(ltrim($class,"\\"));
|
||||
$file = $this->grav['locator']->findResource("themes://{$path}/{$path}.php");
|
||||
|
||||
// Load class
|
||||
if (file_exists($file)) {
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Grav\Common\Twig;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Language\Language;
|
||||
use Grav\Common\Language\LanguageCodes;
|
||||
use Grav\Common\Page\Page;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
@@ -152,21 +153,28 @@ class Twig
|
||||
|
||||
$this->grav->fireEvent('onTwigExtensions');
|
||||
|
||||
$base_url = $this->grav['base_url'] . $path_append;
|
||||
|
||||
// Set some standard variables for twig
|
||||
$this->twig_vars = $this->twig_vars + [
|
||||
'config' => $config,
|
||||
'uri' => $this->grav['uri'],
|
||||
'base_dir' => rtrim(ROOT_DIR, '/'),
|
||||
'base_url' => $this->grav['base_url'] . $path_append,
|
||||
'base_url_simple' => $this->grav['base_url'],
|
||||
'base_url_absolute' => $this->grav['base_url_absolute'] . $path_append,
|
||||
'base_url_relative' => $this->grav['base_url_relative'] . $path_append,
|
||||
'theme_dir' => $locator->findResource('theme://'),
|
||||
'theme_url' => $this->grav['base_url'] . '/' . $locator->findResource('theme://', false),
|
||||
'system' => $config->get('system'),
|
||||
'theme' => $config->get('theme'),
|
||||
'site' => $config->get('site'),
|
||||
'uri' => $this->grav['uri'],
|
||||
'assets' => $this->grav['assets'],
|
||||
'taxonomy' => $this->grav['taxonomy'],
|
||||
'browser' => $this->grav['browser'],
|
||||
'base_dir' => rtrim(ROOT_DIR, '/'),
|
||||
'base_url' => $base_url,
|
||||
'base_url_simple' => $this->grav['base_url'],
|
||||
'base_url_absolute' => $this->grav['base_url_absolute'] . $path_append,
|
||||
'base_url_relative' => $this->grav['base_url_relative'] . $path_append,
|
||||
'home_url' => $base_url == '' ? '/' : $base_url,
|
||||
'theme_dir' => $locator->findResource('theme://'),
|
||||
'theme_url' => $this->grav['base_url'] . '/' . $locator->findResource('theme://', false),
|
||||
'html_lang' => $this->grav['language']->getActive() ?: $config->get('site.default_lang', 'en'),
|
||||
'language_codes' => new LanguageCodes,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -222,25 +230,22 @@ class Twig
|
||||
$twig_vars['header'] = $item->header();
|
||||
|
||||
$local_twig = clone($this->twig);
|
||||
$modular_twig = $item->modularTwig();
|
||||
$process_twig = isset($item->header()->process['twig']) ? $item->header()->process['twig'] : false;
|
||||
|
||||
$output = '';
|
||||
|
||||
try {
|
||||
// Process Modular Twig
|
||||
if ($modular_twig) {
|
||||
if ($item->modularTwig()) {
|
||||
$twig_vars['content'] = $content;
|
||||
$template = $item->template() . TEMPLATE_EXT;
|
||||
$output = $content = $local_twig->render($template, $twig_vars);
|
||||
}
|
||||
|
||||
// Process in-page Twig
|
||||
if (!$modular_twig || ($modular_twig && $process_twig)) {
|
||||
if ($item->shouldProcess('twig')) {
|
||||
$name = '@Page:' . $item->path();
|
||||
$this->setTemplate($name, $content);
|
||||
$output = $local_twig->render($name, $twig_vars);
|
||||
}
|
||||
|
||||
} catch (\Twig_Error_Loader $e) {
|
||||
throw new \RuntimeException($e->getRawMessage(), 404, $e);
|
||||
}
|
||||
@@ -320,6 +325,7 @@ class Twig
|
||||
|
||||
$twig_vars = $this->twig_vars;
|
||||
|
||||
$twig_vars['theme'] = $this->grav['config']->get('theme');
|
||||
$twig_vars['pages'] = $pages->root();
|
||||
$twig_vars['page'] = $page;
|
||||
$twig_vars['header'] = $page->header();
|
||||
@@ -343,6 +349,7 @@ class Twig
|
||||
// Try html version of this template if initial template was NOT html
|
||||
if ($ext != '.html' . TWIG_EXT) {
|
||||
try {
|
||||
$page->templateFormat('html');
|
||||
$output = $this->twig->render($page->template() . '.html' . TWIG_EXT, $twig_vars);
|
||||
} catch (\Twig_Error_Loader $e) {
|
||||
throw new \RuntimeException($error_msg, 400, $e);
|
||||
|
||||
@@ -87,6 +87,7 @@ class TwigExtension extends \Twig_Extension
|
||||
new \Twig_SimpleFilter('truncate', ['\Grav\Common\Utils', 'truncate']),
|
||||
new \Twig_SimpleFilter('truncate_html', ['\Grav\Common\Utils', 'truncateHTML']),
|
||||
new \Twig_SimpleFilter('json_decode', [$this, 'jsonDecodeFilter']),
|
||||
new \Twig_SimpleFilter('array_unique', 'array_unique'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -103,7 +104,8 @@ class TwigExtension extends \Twig_Extension
|
||||
new \Twig_simpleFunction('authorize', [$this, 'authorize']),
|
||||
new \Twig_SimpleFunction('debug', [$this, 'dump'], ['needs_context' => true, 'needs_environment' => true]),
|
||||
new \Twig_SimpleFunction('dump', [$this, 'dump'], ['needs_context' => true, 'needs_environment' => true]),
|
||||
new \Twig_SimpleFunction('evaluate', [$this, 'evaluateFunc']),
|
||||
new \Twig_SimpleFunction('evaluate', [$this, 'evaluateStringFunc'], ['needs_context' => true, 'needs_environment' => true]),
|
||||
new \Twig_SimpleFunction('evaluate_twig', [$this, 'evaluateTwigFunc'], ['needs_context' => true, 'needs_environment' => true]),
|
||||
new \Twig_SimpleFunction('gist', [$this, 'gistFunc']),
|
||||
new \Twig_SimpleFunction('nonce_field', [$this, 'nonceFieldFunc']),
|
||||
new \Twig_simpleFunction('random_string', [$this, 'randomStringFunc']),
|
||||
@@ -115,6 +117,8 @@ class TwigExtension extends \Twig_Extension
|
||||
new \Twig_SimpleFunction('url', [$this, 'urlFunc']),
|
||||
new \Twig_SimpleFunction('json_decode', [$this, 'jsonDecodeFilter']),
|
||||
new \Twig_SimpleFunction('get_cookie', [$this, 'getCookie']),
|
||||
new \Twig_SimpleFunction('redirect_me', [$this, 'redirectFunc']),
|
||||
new \Twig_SimpleFunction('range', [$this, 'rangeFunc']),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -429,9 +433,10 @@ class TwigExtension extends \Twig_Extension
|
||||
/**
|
||||
* @param $string
|
||||
*
|
||||
* @param bool $block Block or Line processing
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function markdownFilter($string)
|
||||
public function markdownFilter($string, $block = true)
|
||||
{
|
||||
$page = $this->grav['page'];
|
||||
$defaults = $this->config->get('system.pages.markdown');
|
||||
@@ -443,7 +448,12 @@ class TwigExtension extends \Twig_Extension
|
||||
$parsedown = new Parsedown($page, $defaults);
|
||||
}
|
||||
|
||||
$string = $parsedown->text($string);
|
||||
if ($block) {
|
||||
$string = $parsedown->text($string);
|
||||
} else {
|
||||
$string = $parsedown->line($string);
|
||||
}
|
||||
|
||||
|
||||
return $string;
|
||||
}
|
||||
@@ -560,15 +570,22 @@ class TwigExtension extends \Twig_Extension
|
||||
$domain = true;
|
||||
}
|
||||
|
||||
if (Grav::instance()['uri']->isExternal($input)) {
|
||||
return $input;
|
||||
}
|
||||
|
||||
if (strpos((string)$input, '://')) {
|
||||
$input = ltrim((string)$input, '/');
|
||||
|
||||
if (Utils::contains((string)$input, '://')) {
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $this->grav['locator'];
|
||||
|
||||
|
||||
|
||||
// Get relative path to the resource (or false if not found).
|
||||
$resource = $locator->findResource((string)$input, false);
|
||||
$resource = $locator->findResource($input, false);
|
||||
} else {
|
||||
$resource = (string)$input;
|
||||
$resource = $input;
|
||||
}
|
||||
|
||||
/** @var Uri $uri */
|
||||
@@ -578,22 +595,51 @@ class TwigExtension extends \Twig_Extension
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate a string
|
||||
* This function will evaluate Twig $twig through the $environment, and return its results.
|
||||
*
|
||||
* @example {{ evaluate('grav.language.getLanguage') }}
|
||||
*
|
||||
* @param string $input String to be evaluated
|
||||
*
|
||||
* @return string Returns the evaluated string
|
||||
* @param \Twig_Environment $environment
|
||||
* @param array $context
|
||||
* @param string $twig
|
||||
* @return mixed
|
||||
*/
|
||||
public function evaluateFunc($input)
|
||||
{
|
||||
if (!$input) { //prevent an obscure Twig error if $input is not set
|
||||
$input = '""';
|
||||
}
|
||||
return $this->grav['twig']->processString("{{ $input }}");
|
||||
public function evaluateTwigFunc( \Twig_Environment $environment, $context, $twig ) {
|
||||
$loader = $environment->getLoader( );
|
||||
|
||||
$parsed = $this->parseString( $environment, $context, $twig );
|
||||
|
||||
$environment->setLoader( $loader );
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will evaluate a $string through the $environment, and return its results.
|
||||
*
|
||||
* @param \Twig_Environment $environment
|
||||
* @param $context
|
||||
* @param $string
|
||||
* @return mixed
|
||||
*/
|
||||
public function evaluateStringFunc(\Twig_Environment $environment, $context, $string )
|
||||
{
|
||||
$parsed = $this->evaluateTwigFunc($environment, $context, "{{ $string }}");
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parser for the environment to Twig_Loader_String, and parsed the string $string.
|
||||
*
|
||||
* @param \Twig_Environment $environment
|
||||
* @param array $context
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
protected function parseString( \Twig_Environment $environment, $context, $string ) {
|
||||
$environment->setLoader( new \Twig_Loader_String( ) );
|
||||
return $environment->render( $string, $context );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Based on Twig_Extension_Debug / twig_var_dump
|
||||
* (c) 2011 Fabien Potencier
|
||||
@@ -775,7 +821,7 @@ class TwigExtension extends \Twig_Extension
|
||||
*/
|
||||
public function nonceFieldFunc($action, $nonceParamName = 'nonce')
|
||||
{
|
||||
$string = '<input type="hidden" id="' . $nonceParamName . '" name="' . $nonceParamName . '" value="' . Utils::getNonce($action) . '" />';
|
||||
$string = '<input type="hidden" name="' . $nonceParamName . '" value="' . Utils::getNonce($action) . '" />';
|
||||
|
||||
return $string;
|
||||
}
|
||||
@@ -820,4 +866,30 @@ class TwigExtension extends \Twig_Extension
|
||||
{
|
||||
return preg_replace($pattern, $replace, $subject, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* redirect browser from twig
|
||||
*
|
||||
* @param string $url the url to redirect to
|
||||
* @param int $statusCode statusCode, default 303
|
||||
*/
|
||||
public function redirectFunc($url, $statusCode = 303)
|
||||
{
|
||||
header('Location: ' . $url, true, $statusCode);
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an array containing a range of elements, optionally stepped
|
||||
*
|
||||
* @param int $start Minimum number, default 0
|
||||
* @param int $end Maximum number, default `getrandmax()`
|
||||
* @param int $step Increment between elements in the sequence, default 1
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rangeFunc($start = 0, $end = 100, $step = 1)
|
||||
{
|
||||
return range($start, $end, $step);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +277,13 @@ class Uri
|
||||
}
|
||||
|
||||
// Set some defaults
|
||||
$this->root = $grav['config']->get('system.custom_base_url') ?: $this->base . $this->root_path;
|
||||
if ($grav['config']->get('system.custom_base_url')) {
|
||||
$this->root_path = parse_url($grav['config']->get('system.custom_base_url'), PHP_URL_PATH);
|
||||
$this->root = $grav['config']->get('system.custom_base_url');
|
||||
} else {
|
||||
$this->root = $this->base . $this->root_path;
|
||||
}
|
||||
|
||||
$this->url = $this->base . $this->uri;
|
||||
|
||||
// get any params and remove them
|
||||
@@ -304,11 +310,10 @@ class Uri
|
||||
$bits = parse_url($uri);
|
||||
|
||||
// process query string
|
||||
if (isset($bits['query']) && isset($bits['path'])) {
|
||||
if (isset($bits['query'])) {
|
||||
if (!$this->query) {
|
||||
$this->query = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING);
|
||||
}
|
||||
$uri = $bits['path'];
|
||||
}
|
||||
|
||||
//process fragment
|
||||
@@ -316,8 +321,11 @@ class Uri
|
||||
$this->fragment = $bits['fragment'];
|
||||
}
|
||||
|
||||
// Get the path. If there's no path, make sure pathinfo() still returns dirname variable
|
||||
$path = isset($bits['path']) ? $bits['path'] : '/';
|
||||
|
||||
// remove the extension if there is one set
|
||||
$parts = pathinfo($uri);
|
||||
$parts = pathinfo($path);
|
||||
|
||||
// set the original basename
|
||||
$this->basename = $parts['basename'];
|
||||
@@ -331,12 +339,12 @@ class Uri
|
||||
|
||||
// Strip the file extension for valid page types
|
||||
if (preg_match('/\.(' . $valid_page_types . ')$/', $parts['basename'])) {
|
||||
$uri = rtrim(str_replace(DIRECTORY_SEPARATOR, DS, $parts['dirname']), DS) . '/' . $parts['filename'];
|
||||
$path = rtrim(str_replace(DIRECTORY_SEPARATOR, DS, $parts['dirname']), DS) . '/' . $parts['filename'];
|
||||
}
|
||||
|
||||
// set the new url
|
||||
$this->url = $this->root . $uri;
|
||||
$this->path = $uri;
|
||||
$this->url = $this->root . $path;
|
||||
$this->path = $path;
|
||||
$this->content_path = trim(str_replace($this->base, '', $this->path), '/');
|
||||
if ($this->content_path != '') {
|
||||
$this->paths = explode('/', $this->content_path);
|
||||
@@ -719,7 +727,7 @@ class Uri
|
||||
*
|
||||
* @return boolean is eternal state
|
||||
*/
|
||||
public function isExternal($url)
|
||||
public static function isExternal($url)
|
||||
{
|
||||
if (Utils::startsWith($url, 'http')) {
|
||||
return true;
|
||||
@@ -1078,4 +1086,20 @@ class Uri
|
||||
|
||||
return $urlWithNonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the passed in URL a valid URL?
|
||||
*
|
||||
* @param $url
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidUrl($url)
|
||||
{
|
||||
$regex = '/^(?:(https?|ftp|telnet):)?\/\/((?:[a-z0-9@:.-]|%[0-9A-F]{2}){3,})(?::(\d+))?((?:\/(?:[a-z0-9-._~!$&\'\(\)\*\+\,\;\=\:\@]|%[0-9A-F]{2})*)*)(?:\?((?:[a-z0-9-._~!$&\'\(\)\*\+\,\;\=\:\/?@]|%[0-9A-F]{2})*))?(?:#((?:[a-z0-9-._~!$&\'\(\)\*\+\,\;\=\:\/?@]|%[0-9A-F]{2})*))?/';
|
||||
if (preg_match($regex, $url)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ class Group extends Data
|
||||
$config->set("groups.$this->groupname.$value", $this->items['data'][$value]);
|
||||
}
|
||||
}
|
||||
if ($field['type'] == 'array') {
|
||||
if ($field['type'] == 'array' || $field['type'] == 'permissions') {
|
||||
$value = $field['name'];
|
||||
$arrayValues = Utils::getDotNotation($this->items['data'], $field['name']);
|
||||
|
||||
|
||||
@@ -54,6 +54,42 @@ class User extends Data
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a user by username, email, etc
|
||||
*
|
||||
* @param string $query the query to search for
|
||||
* @param array $fields the fields to search
|
||||
* @return User
|
||||
*/
|
||||
public static function find($query, $fields = ['username', 'email'])
|
||||
{
|
||||
$account_dir = Grav::instance()['locator']->findResource('account://');
|
||||
$files = $account_dir ? array_diff(scandir($account_dir), ['.', '..']) : [];
|
||||
|
||||
// Try with username first, you never know!
|
||||
if (in_array('username', $fields)) {
|
||||
$user = User::load($query);
|
||||
unset($fields[array_search('username', $fields)]);
|
||||
} else {
|
||||
$user = User::load('');
|
||||
}
|
||||
|
||||
// If not found, try the fields
|
||||
if (!$user->exists()) {
|
||||
foreach ($files as $file) {
|
||||
if (Utils::endsWith($file, YAML_EXT)) {
|
||||
$find_user = User::load(trim(pathinfo($file, PATHINFO_FILENAME)));
|
||||
foreach ($fields as $field) {
|
||||
if ($find_user[$field] == $query) {
|
||||
return $find_user;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove user account.
|
||||
*
|
||||
@@ -64,7 +100,7 @@ class User extends Data
|
||||
public static function remove($username)
|
||||
{
|
||||
$file_path = Grav::instance()['locator']->findResource('account://' . $username . YAML_EXT);
|
||||
if (file_exists($file_path) && unlink($file_path)) {
|
||||
if ($file_path && unlink($file_path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -203,4 +239,20 @@ class User extends Data
|
||||
{
|
||||
return $this->authorize($action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the User's avatar URL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function avatarUrl()
|
||||
{
|
||||
if ($this->avatar) {
|
||||
$avatar = $this->avatar;
|
||||
$avatar = array_shift($avatar);
|
||||
return Grav::instance()['base_url'] . '/' . $avatar['path'];
|
||||
} else {
|
||||
return 'https://www.gravatar.com/avatar/' . md5($this->email);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,26 @@ abstract class Utils
|
||||
return (object)array_merge((array)$obj1, (array)$obj2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive Merge with uniqueness
|
||||
*
|
||||
* @param $array1
|
||||
* @param $array2
|
||||
* @return mixed
|
||||
*/
|
||||
public static function arrayMergeRecursiveUnique($array1, $array2)
|
||||
{
|
||||
if (empty($array1)) return $array2; //optimize the base case
|
||||
|
||||
foreach ($array2 as $key => $value) {
|
||||
if (is_array($value) && is_array(@$array1[$key])) {
|
||||
$value = static::arrayMergeRecursiveUnique($array1[$key], $value);
|
||||
}
|
||||
$array1[$key] = $value;
|
||||
}
|
||||
return $array1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Grav date formats allowed
|
||||
*
|
||||
@@ -188,7 +208,11 @@ abstract class Utils
|
||||
*/
|
||||
public static function truncateHtml($text, $length = 100, $ellipsis = '...')
|
||||
{
|
||||
return Truncator::truncateLetters($text, $length, $ellipsis);
|
||||
if (mb_strlen($text) <= $length) {
|
||||
return $text;
|
||||
} else {
|
||||
return Truncator::truncateLetters($text, $length, $ellipsis);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -809,4 +833,22 @@ abstract class Utils
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to determine if the current OS is Windows
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isWindows() {
|
||||
return strncasecmp(PHP_OS, 'WIN', 3) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility to determine if the server running PHP is Apache
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isApache() {
|
||||
return strpos($_SERVER["SERVER_SOFTWARE"], 'Apache') !== false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,20 +65,15 @@ class InstallCommand extends ConsoleCommand
|
||||
// fix trailing slash
|
||||
$this->destination = rtrim($this->destination, DS) . DS;
|
||||
$this->user_path = $this->destination . USER_PATH;
|
||||
|
||||
if (false === $this->isWindows()) {
|
||||
$local_config_file = exec('eval echo ~/.grav/config');
|
||||
if (file_exists($local_config_file)) {
|
||||
$this->local_config = Yaml::parse($local_config_file);
|
||||
$this->output->writeln('Read local config from <cyan>' . $local_config_file . '</cyan>');
|
||||
}
|
||||
if ($local_config_file = $this->loadLocalConfig()) {
|
||||
$this->output->writeln('Read local config from <cyan>' . $local_config_file . '</cyan>');
|
||||
}
|
||||
|
||||
// Look for dependencies file in ROOT and USER dir
|
||||
if (file_exists($this->user_path . $dependencies_file)) {
|
||||
$this->config = Yaml::parse($this->user_path . $dependencies_file);
|
||||
$this->config = Yaml::parse(file_get_contents($this->user_path . $dependencies_file));
|
||||
} elseif (file_exists($this->destination . $dependencies_file)) {
|
||||
$this->config = Yaml::parse($this->destination . $dependencies_file);
|
||||
$this->config = Yaml::parse(file_get_contents($this->destination . $dependencies_file));
|
||||
} else {
|
||||
$this->output->writeln('<red>ERROR</red> Missing .dependencies file in <cyan>user/</cyan> folder');
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ class SandboxCommand extends ConsoleCommand
|
||||
* @var array
|
||||
*/
|
||||
protected $mappings = [
|
||||
'/.editorconfig' => '/.editorconfig',
|
||||
'/.gitignore' => '/.gitignore',
|
||||
'/CHANGELOG.md' => '/CHANGELOG.md',
|
||||
'/LICENSE.txt' => '/LICENSE.txt',
|
||||
@@ -59,7 +58,6 @@ class SandboxCommand extends ConsoleCommand
|
||||
'/system' => '/system',
|
||||
'/vendor' => '/vendor',
|
||||
'/webserver-configs' => '/webserver-configs',
|
||||
'/codeception.yml' => '/codeception.yml',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,7 @@ use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
trait ConsoleTrait
|
||||
{
|
||||
@@ -40,12 +41,11 @@ trait ConsoleTrait
|
||||
*/
|
||||
public function setupConsole(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if (Grav::instance()) {
|
||||
Grav::instance()['config']->set('system.cache.driver', 'default');
|
||||
}
|
||||
// Initialize cache with CLI compatibility
|
||||
Grav::instance()['config']->set('system.cache.cli_compatibility', true);
|
||||
Grav::instance()['cache'];
|
||||
|
||||
$this->argv = $_SERVER['argv'][0];
|
||||
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
|
||||
@@ -113,19 +113,20 @@ trait ConsoleTrait
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if the system is based on windows or not.
|
||||
* Load the local config file
|
||||
*
|
||||
* @return bool
|
||||
* @return mixed string the local config file name. false if local config does not exist
|
||||
*/
|
||||
public function isWindows()
|
||||
public function loadLocalConfig()
|
||||
{
|
||||
$keys = [
|
||||
'CYGWIN_NT-5.1',
|
||||
'WIN32',
|
||||
'WINNT',
|
||||
'Windows'
|
||||
];
|
||||
$home_folder = getenv('HOME') ?: getenv('HOMEDRIVE') . getenv('HOMEPATH');
|
||||
$local_config_file = $home_folder . '/.grav/config';
|
||||
|
||||
return array_key_exists(PHP_OS, $keys);
|
||||
if (file_exists($local_config_file)) {
|
||||
$this->local_config = Yaml::parse(file_get_contents($local_config_file));
|
||||
return $local_config_file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,14 +9,13 @@
|
||||
namespace Grav\Console\Gpm;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\GPM\GPM;
|
||||
use Grav\Common\GPM\Installer;
|
||||
use Grav\Common\GPM\Response;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class DirectInstallCommand extends ConsoleCommand
|
||||
{
|
||||
@@ -32,14 +31,14 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
->addArgument(
|
||||
'package-file',
|
||||
InputArgument::REQUIRED,
|
||||
'The local location or remote URL to an installable package file'
|
||||
'Installable package local <path> or remote <URL>. Can install specific version'
|
||||
)
|
||||
->setDescription("Installs Grav, plugin, or theme directly from a file or a URL")
|
||||
->setHelp('The <info>direct-install</info> command installs Grav, plugin, or theme directly from a file or a URL');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null|void
|
||||
* @return bool
|
||||
*/
|
||||
protected function serve()
|
||||
{
|
||||
@@ -63,10 +62,30 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
$this->output->writeln("Preparing to install <cyan>" . $package_file . "</cyan>");
|
||||
|
||||
|
||||
if ($this->isRemote($package_file)) {
|
||||
$zip = $this->downloadPackage($package_file, $tmp_zip);
|
||||
if (Response::isRemote($package_file)) {
|
||||
$this->output->write(" |- Downloading package... 0%");
|
||||
try {
|
||||
$zip = GPM::downloadPackage($package_file, $tmp_zip);
|
||||
} catch (\RuntimeException $e) {
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln(" `- <red>ERROR: " . $e->getMessage() . "</red>");
|
||||
$this->output->writeln('');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($zip) {
|
||||
$this->output->write("\x0D");
|
||||
$this->output->write(" |- Downloading package... 100%");
|
||||
$this->output->writeln('');
|
||||
}
|
||||
} else {
|
||||
$zip = $this->copyPackage($package_file, $tmp_zip);
|
||||
$this->output->write(" |- Copying package... 0%");
|
||||
$zip = GPM::copyPackage($package_file, $tmp_zip);
|
||||
if ($zip) {
|
||||
$this->output->write("\x0D");
|
||||
$this->output->write(" |- Copying package... 100%");
|
||||
$this->output->writeln('');
|
||||
}
|
||||
}
|
||||
|
||||
if (file_exists($zip)) {
|
||||
@@ -84,7 +103,8 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
$this->output->write("\x0D");
|
||||
$this->output->writeln(" |- Extracting package... <green>ok</green>");
|
||||
|
||||
$type = $this->getPackageType($extracted);
|
||||
|
||||
$type = GPM::getPackageType($extracted);
|
||||
|
||||
if (!$type) {
|
||||
$this->output->writeln(" '- <red>ERROR: Not a valid Grav package</red>");
|
||||
@@ -92,7 +112,7 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
exit;
|
||||
}
|
||||
|
||||
$blueprint = $this->getBlueprints($extracted);
|
||||
$blueprint = GPM::getBlueprints($extracted);
|
||||
if ($blueprint) {
|
||||
if (isset($blueprint['dependencies'])) {
|
||||
$depencencies = [];
|
||||
@@ -136,7 +156,7 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
$this->output->write(" |- Installing package... ");
|
||||
Installer::install($zip, GRAV_ROOT, ['sophisticated' => true, 'overwrite' => true, 'ignore_symlinks' => true], $extracted);
|
||||
} else {
|
||||
$name = $this->getPackageName($extracted);
|
||||
$name = GPM::getPackageName($extracted);
|
||||
|
||||
if (!$name) {
|
||||
$this->output->writeln("<red>ERROR: Name could not be determined.</red> Please specify with --name|-n");
|
||||
@@ -144,7 +164,7 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
exit;
|
||||
}
|
||||
|
||||
$install_path = $this->getInstallPath($type, $name);
|
||||
$install_path = GPM::getInstallPath($type, $name);
|
||||
$is_update = file_exists($install_path);
|
||||
|
||||
$this->output->write(" |- Checking destination... ");
|
||||
@@ -172,7 +192,7 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
$this->output->write("\x0D");
|
||||
|
||||
if(Installer::lastErrorCode()) {
|
||||
$this->output->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
$this->output->writeln(" '- <red>" . Installer::lastErrorMsg() . "</red>");
|
||||
$this->output->writeln('');
|
||||
} else {
|
||||
$this->output->writeln(" |- Installing package... <green>ok</green>");
|
||||
@@ -192,183 +212,4 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the install path for a name and a particular type of package
|
||||
*
|
||||
* @param $type
|
||||
* @param $name
|
||||
* @return string
|
||||
*/
|
||||
protected function getInstallPath($type, $name)
|
||||
{
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
if ($type == 'theme') {
|
||||
$install_path = $locator->findResource('themes://', false) . DS . $name;
|
||||
} else {
|
||||
$install_path = $locator->findResource('plugins://', false) . DS . $name;
|
||||
}
|
||||
return $install_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to guess the package name from the source files
|
||||
*
|
||||
* @param $source
|
||||
* @return bool|string
|
||||
*/
|
||||
protected function getPackageName($source)
|
||||
{
|
||||
foreach (glob($source . "*.yaml") as $filename) {
|
||||
$name = strtolower(basename($filename, '.yaml'));
|
||||
if ($name == 'blueprints') {
|
||||
continue;
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to guess the package type from the source files
|
||||
*
|
||||
* @param $source
|
||||
* @return bool|string
|
||||
*/
|
||||
protected function getPackageType($source)
|
||||
{
|
||||
if (
|
||||
file_exists($source . 'system/defines.php') &&
|
||||
file_exists($source . 'system/config/system.yaml')
|
||||
) {
|
||||
return 'grav';
|
||||
} else {
|
||||
// must have a blueprint
|
||||
if (!file_exists($source . 'blueprints.yaml')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// either theme or plugin
|
||||
$name = basename($source);
|
||||
if (Utils::contains($name, 'theme')) {
|
||||
return 'theme';
|
||||
} elseif (Utils::contains($name, 'plugin')) {
|
||||
return 'plugin';
|
||||
}
|
||||
foreach (glob($source . "*.php") as $filename) {
|
||||
$contents = file_get_contents($filename);
|
||||
if (Utils::contains($contents, 'Grav\Common\Theme')) {
|
||||
return 'theme';
|
||||
} elseif (Utils::contains($contents, 'Grav\Common\Plugin')) {
|
||||
return 'plugin';
|
||||
}
|
||||
}
|
||||
|
||||
// Assume it's a theme
|
||||
return 'theme';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this is a local or a remote file
|
||||
*
|
||||
* @param $file
|
||||
* @return bool
|
||||
*/
|
||||
protected function isRemote($file)
|
||||
{
|
||||
return (bool) filter_var($file, FILTER_VALIDATE_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find/Parse the blueprint file
|
||||
*
|
||||
* @param $source
|
||||
* @return array|bool
|
||||
*/
|
||||
protected function getBlueprints($source)
|
||||
{
|
||||
$blueprint_file = $source . 'blueprints.yaml';
|
||||
if (!file_exists($blueprint_file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$blueprint = (array)Yaml::parse(file_get_contents($blueprint_file));
|
||||
return $blueprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the zip package via the URL
|
||||
*
|
||||
* @param $package_file
|
||||
* @param $tmp
|
||||
* @return null|string
|
||||
*/
|
||||
private function downloadPackage($package_file, $tmp)
|
||||
{
|
||||
$this->output->write(" |- Downloading package... 0%");
|
||||
|
||||
$package = parse_url($package_file);
|
||||
|
||||
|
||||
$filename = basename($package['path']);
|
||||
$output = Response::get($package_file, [], [$this, 'progress']);
|
||||
|
||||
if ($output) {
|
||||
Folder::mkdir($tmp);
|
||||
|
||||
$this->output->write("\x0D");
|
||||
$this->output->write(" |- Downloading package... 100%");
|
||||
$this->output->writeln('');
|
||||
|
||||
file_put_contents($tmp . DS . $filename, $output);
|
||||
|
||||
return $tmp . DS . $filename;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the local zip package to tmp
|
||||
*
|
||||
* @param $package_file
|
||||
* @param $tmp
|
||||
* @return null|string
|
||||
*/
|
||||
private function copyPackage($package_file, $tmp)
|
||||
{
|
||||
$this->output->write(" |- Copying package... 0%");
|
||||
|
||||
$package_file = realpath($package_file);
|
||||
|
||||
if (file_exists($package_file)) {
|
||||
$filename = basename($package_file);
|
||||
|
||||
Folder::mkdir($tmp);
|
||||
|
||||
$this->output->write("\x0D");
|
||||
$this->output->write(" |- Copying package... 100%");
|
||||
$this->output->writeln('');
|
||||
|
||||
copy(realpath($package_file), $tmp . DS . $filename);
|
||||
|
||||
return $tmp . DS . $filename;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $progress
|
||||
*/
|
||||
public function progress($progress)
|
||||
{
|
||||
$this->output->write("\x0D");
|
||||
$this->output->write(" |- Downloading package... " . str_pad($progress['percent'], 5, " ",
|
||||
STR_PAD_LEFT) . '%');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ class InfoCommand extends ConsoleCommand
|
||||
$this->output->writeln("");
|
||||
foreach ($changelog as $version => $log) {
|
||||
$title = $version . ' [' . $log['date'] . ']';
|
||||
$content = preg_replace_callback("/\d\.\s\[\]\(#(.*)\)/", function ($match) {
|
||||
$content = preg_replace_callback('/\d\.\s\[\]\(#(.*)\)/', function ($match) {
|
||||
return "\n" . ucfirst($match[1]) . ":";
|
||||
}, $log['content']);
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ use Grav\Console\ConsoleCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
define('GIT_REGEX', '/http[s]?:\/\/(?:.*@)?(github|bitbucket)(?:.org|.com)\/.*\/(.*)/');
|
||||
|
||||
@@ -81,7 +80,7 @@ class InstallCommand extends ConsoleCommand
|
||||
->addArgument(
|
||||
'package',
|
||||
InputArgument::IS_ARRAY | InputArgument::REQUIRED,
|
||||
'The package(s) that are desired to be installed. Use the "index" command for a list of packages'
|
||||
'Package(s) to install. Use "bin/gpm index" to list packages. Use "bin/gpm direct-install" to install a specific version'
|
||||
)
|
||||
->setDescription("Performs the installation of plugins and themes")
|
||||
->setHelp('The <info>install</info> command allows to install plugins and themes');
|
||||
@@ -98,7 +97,7 @@ class InstallCommand extends ConsoleCommand
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null|void|bool
|
||||
* @return bool
|
||||
*/
|
||||
protected function serve()
|
||||
{
|
||||
@@ -112,13 +111,7 @@ class InstallCommand extends ConsoleCommand
|
||||
|
||||
$packages = array_map('strtolower', $this->input->getArgument('package'));
|
||||
$this->data = $this->gpm->findPackages($packages);
|
||||
|
||||
if (false === $this->isWindows() && @is_file(getenv("HOME") . '/.grav/config')) {
|
||||
$local_config_file = exec('eval echo ~/.grav/config');
|
||||
if (file_exists($local_config_file)) {
|
||||
$this->local_config = Yaml::parse($local_config_file);
|
||||
}
|
||||
}
|
||||
$this->loadLocalConfig();
|
||||
|
||||
if (
|
||||
!Installer::isGravInstance($this->destination) ||
|
||||
@@ -427,7 +420,7 @@ class InstallCommand extends ConsoleCommand
|
||||
/**
|
||||
* @param $package
|
||||
*
|
||||
* @return array
|
||||
* @return array|bool
|
||||
*/
|
||||
private function getGitRegexMatches($package)
|
||||
{
|
||||
@@ -550,8 +543,12 @@ class InstallCommand extends ConsoleCommand
|
||||
} else {
|
||||
$this->output->writeln(" '- <green>Success!</green> ");
|
||||
$this->output->writeln('');
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -566,6 +563,7 @@ class InstallCommand extends ConsoleCommand
|
||||
$tmp_dir = Grav::instance()['locator']->findResource('tmp://', true, true);
|
||||
$this->tmp = $tmp_dir . '/Grav-' . uniqid();
|
||||
$filename = $package->slug . basename($package->zipball_url);
|
||||
$filename = preg_replace('/[\\\\\/:"*?&<>|]+/mi', '-', $filename);
|
||||
$query = '';
|
||||
|
||||
if ($package->premium) {
|
||||
|
||||
@@ -59,7 +59,7 @@ class SelfupgradeCommand extends ConsoleCommand
|
||||
{
|
||||
$this
|
||||
->setName("self-upgrade")
|
||||
->setAliases(['selfupgrade'])
|
||||
->setAliases(['selfupgrade', 'selfupdate'])
|
||||
->addOption(
|
||||
'force',
|
||||
'f',
|
||||
@@ -143,7 +143,7 @@ class SelfupgradeCommand extends ConsoleCommand
|
||||
$this->output->writeln("");
|
||||
foreach ($changelog as $version => $log) {
|
||||
$title = $version . ' [' . $log['date'] . ']';
|
||||
$content = preg_replace_callback("/\d\.\s\[\]\(#(.*)\)/", function ($match) {
|
||||
$content = preg_replace_callback('/\d\.\s\[\]\(#(.*)\)/', function ($match) {
|
||||
return "\n" . ucfirst($match[1]) . ":";
|
||||
}, $log['content']);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Grav\Console\Gpm;
|
||||
use Grav\Common\GPM\GPM;
|
||||
use Grav\Common\GPM\Installer;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Common\GPM\Upgrader;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
@@ -51,6 +52,11 @@ class UpdateCommand extends ConsoleCommand
|
||||
|
||||
protected $overwrite;
|
||||
|
||||
/**
|
||||
* @var Upgrader
|
||||
*/
|
||||
protected $upgrader;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -109,6 +115,22 @@ class UpdateCommand extends ConsoleCommand
|
||||
*/
|
||||
protected function serve()
|
||||
{
|
||||
$this->upgrader = new Upgrader($this->input->getOption('force'));
|
||||
$local = $this->upgrader->getLocalVersion();
|
||||
$remote = $this->upgrader->getRemoteVersion();
|
||||
if ($local !== $remote) {
|
||||
$this->output->writeln("<yellow>WARNING</yellow>: A new version of Grav is available. You should update Grav before updating plugins and themes. If you continue without updating Grav, some plugins or themes may stop working.");
|
||||
$this->output->writeln("");
|
||||
$questionHelper = $this->getHelper('question');
|
||||
$question = new ConfirmationQuestion("Continue with the update process? [Y|n] ", true);
|
||||
$answer = $questionHelper->ask($this->input, $this->output, $question);
|
||||
|
||||
if (!$answer) {
|
||||
$this->output->writeln("<red>Update aborted. Exiting...</red>");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$this->gpm = new GPM($this->input->getOption('force'));
|
||||
|
||||
$this->all_yes = $this->input->getOption('all-yes');
|
||||
|
||||
@@ -18,7 +18,7 @@ use Symfony\Component\Yaml\Yaml;
|
||||
class VersionCommand extends ConsoleCommand
|
||||
{
|
||||
/**
|
||||
* @var
|
||||
* @var GPM
|
||||
*/
|
||||
protected $gpm;
|
||||
|
||||
|
||||
@@ -78,24 +78,27 @@ class ParsedownTest extends \Codeception\TestCase\Test
|
||||
$this->grav['language'] = new Language($this->grav);
|
||||
$this->uri->initializeWithURL('http://testing.dev/fr/item2/item2-2')->init();
|
||||
|
||||
$this->assertSame('<p><img src="/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->assertSame('<p><img alt="" src="/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertRegexp('|<p><img src="\/images\/.*-cache-image.jpe?g\?foo=1" \/><\/p>|',
|
||||
$this->assertRegexp('|<p><img alt="" src="\/images\/.*-cache-image.jpe?g\?foo=1" \/><\/p>|',
|
||||
$this->parsedown->text(''));
|
||||
|
||||
$this->uri->initializeWithURL('http://testing.dev/item2/item2-2')->init();
|
||||
|
||||
$this->assertSame('<p><img src="/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->assertSame('<p><img alt="" src="/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertRegexp('|<p><img src="\/images\/.*-cache-image.jpe?g\?foo=1" \/><\/p>|',
|
||||
$this->assertRegexp('|<p><img alt="" src="\/images\/.*-cache-image.jpe?g\?foo=1" \/><\/p>|',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertRegexp('|<p><img src="\/images\/.*-home-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->assertRegexp('|<p><img alt="" src="\/images\/.*-home-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertSame('<p><img src="/item2/item2-2/missing-image.jpg" alt="" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertSame('<p><img src="/home-missing-image.jpg" alt="" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
|
||||
$this->assertSame('<p><img src="/home-missing-image.jpg" alt="" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertSame('<p><img src="https://getgrav-grav.netdna-ssl.com/user/pages/media/grav-logo.svg" alt="" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
|
||||
}
|
||||
|
||||
@@ -103,11 +106,11 @@ class ParsedownTest extends \Codeception\TestCase\Test
|
||||
{
|
||||
$this->uri->initializeWithUrlAndRootPath('http://testing.dev/subdir/item2/item2-2', '/subdir')->init();
|
||||
|
||||
$this->assertRegexp('|<p><img src="\/subdir\/images\/.*-home-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->assertRegexp('|<p><img alt="" src="\/subdir\/images\/.*-home-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertSame('<p><img src="/subdir/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->assertSame('<p><img alt="" src="/subdir/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertRegexp('|<p><img src="\/subdir\/images\/.*-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->assertRegexp('|<p><img alt="" src="\/subdir\/images\/.*-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertSame('<p><img src="/subdir/item2/item2-2/missing-image.jpg" alt="" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
@@ -121,11 +124,11 @@ class ParsedownTest extends \Codeception\TestCase\Test
|
||||
$this->config->set('system.absolute_urls', true);
|
||||
$this->uri->initializeWithURL('http://testing.dev/item2/item2-2')->init();
|
||||
|
||||
$this->assertSame('<p><img src="http://testing.dev/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->assertSame('<p><img alt="" src="http://testing.dev/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertRegexp('|<p><img src="http:\/\/testing.dev\/images\/.*-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->assertRegexp('|<p><img alt="" src="http:\/\/testing.dev\/images\/.*-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertRegexp('|<p><img src="http:\/\/testing.dev\/images\/.*-home-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->assertRegexp('|<p><img alt="" src="http:\/\/testing.dev\/images\/.*-home-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertSame('<p><img src="http://testing.dev/item2/item2-2/missing-image.jpg" alt="" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
@@ -138,11 +141,11 @@ class ParsedownTest extends \Codeception\TestCase\Test
|
||||
$this->config->set('system.absolute_urls', true);
|
||||
$this->uri->initializeWithUrlAndRootPath('http://testing.dev/subdir/item2/item2-2', '/subdir')->init();
|
||||
|
||||
$this->assertSame('<p><img src="http://testing.dev/subdir/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->assertSame('<p><img alt="" src="http://testing.dev/subdir/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertRegexp('|<p><img src="http:\/\/testing.dev\/subdir\/images\/.*-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->assertRegexp('|<p><img alt="" src="http:\/\/testing.dev\/subdir\/images\/.*-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertRegexp('|<p><img src="http:\/\/testing.dev\/subdir\/images\/.*-home-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->assertRegexp('|<p><img alt="" src="http:\/\/testing.dev\/subdir\/images\/.*-home-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertSame('<p><img src="http://testing.dev/subdir/item2/item2-2/missing-image.jpg" alt="" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
@@ -154,13 +157,13 @@ class ParsedownTest extends \Codeception\TestCase\Test
|
||||
{
|
||||
$this->uri->initializeWithURL('http://testing.dev/item2/item2-2')->init();
|
||||
|
||||
$this->assertSame('<p><img title="My Title" src="/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->assertSame('<p><img title="My Title" alt="" src="/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertSame('<p><img class="foo" src="/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->assertSame('<p><img alt="" class="foo" src="/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertSame('<p><img class="foo bar" src="/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->assertSame('<p><img alt="" class="foo bar" src="/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertSame('<p><img id="foo" src="/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->assertSame('<p><img alt="" id="foo" src="/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertSame('<p><img alt="Alt Text" id="foo" src="/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
@@ -185,11 +188,11 @@ class ParsedownTest extends \Codeception\TestCase\Test
|
||||
$page = $this->pages->dispatch('/');
|
||||
$this->parsedown = new Parsedown($page, $defaults);
|
||||
|
||||
$this->assertSame('<p><img src="/tests/fake/nested-site/user/pages/01.item1/home-sample-image.jpg" /></p>',
|
||||
$this->assertSame('<p><img alt="" src="/tests/fake/nested-site/user/pages/01.item1/home-sample-image.jpg" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertRegexp('|<p><img src="\/images\/.*-home-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->assertRegexp('|<p><img alt="" src="\/images\/.*-home-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertRegexp('|<p><img src="\/images\/.*-home-cache-image.jpe?g\?foo=1" \/><\/p>|',
|
||||
$this->assertRegexp('|<p><img alt="" src="\/images\/.*-home-cache-image.jpe?g\?foo=1" \/><\/p>|',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertSame('<p><img src="/home-missing-image.jpg" alt="" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
@@ -199,7 +202,7 @@ class ParsedownTest extends \Codeception\TestCase\Test
|
||||
$this->grav['language'] = new Language($this->grav);
|
||||
$this->uri->initializeWithURL('http://testing.dev/fr/item2/item2-2')->init();
|
||||
|
||||
$this->assertSame('<p><img src="/tests/fake/nested-site/user/pages/01.item1/home-sample-image.jpg" /></p>',
|
||||
$this->assertSame('<p><img alt="" src="/tests/fake/nested-site/user/pages/01.item1/home-sample-image.jpg" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
|
||||
|
||||
@@ -210,11 +213,11 @@ class ParsedownTest extends \Codeception\TestCase\Test
|
||||
$this->config->set('system.absolute_urls', true);
|
||||
$this->uri->initializeWithUrlAndRootPath('http://testing.dev/subdir/item2/item2-2', '/subdir')->init();
|
||||
|
||||
$this->assertSame('<p><img src="http://testing.dev/subdir/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->assertSame('<p><img alt="" src="http://testing.dev/subdir/tests/fake/nested-site/user/pages/02.item2/02.item2-2/sample-image.jpg" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertRegexp('|<p><img src="http:\/\/testing.dev\/subdir\/images\/.*-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->assertRegexp('|<p><img alt="" src="http:\/\/testing.dev\/subdir\/images\/.*-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertRegexp('|<p><img src="http:\/\/testing.dev\/subdir\/images\/.*-home-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->assertRegexp('|<p><img alt="" src="http:\/\/testing.dev\/subdir\/images\/.*-home-cache-image.jpe?g" \/><\/p>|',
|
||||
$this->parsedown->text(''));
|
||||
$this->assertSame('<p><img src="http://testing.dev/subdir/item2/item2-2/missing-image.jpg" alt="" /></p>',
|
||||
$this->parsedown->text(''));
|
||||
@@ -672,6 +675,8 @@ class ParsedownTest extends \Codeception\TestCase\Test
|
||||
{
|
||||
$this->uri->initializeWithURL('http://testing.dev/item2/item2-2')->init();
|
||||
|
||||
$this->assertSame('<p><a href="#something" class="button">Anchor Class</a></p>',
|
||||
$this->parsedown->text('[Anchor Class](?classes=button#something)'));
|
||||
$this->assertSame('<p><a href="/item2/item2-3" class="button">Relative Class</a></p>',
|
||||
$this->parsedown->text('[Relative Class](../item2-3?classes=button)'));
|
||||
$this->assertSame('<p><a href="/item2/item2-3" id="unique">Relative ID</a></p>',
|
||||
|
||||
@@ -183,4 +183,26 @@ class TwigExtensionTest extends \Codeception\TestCase\Test
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function testRangeFunc()
|
||||
{
|
||||
$hundred = [];
|
||||
for($i = 0; $i <= 100; $i++) { $hundred[] = $i; }
|
||||
|
||||
|
||||
$this->assertSame([0], $this->twig_ext->rangeFunc(0, 0));
|
||||
$this->assertSame([0, 1, 2], $this->twig_ext->rangeFunc(0, 2));
|
||||
|
||||
$this->assertSame([0, 5, 10, 15], $this->twig_ext->rangeFunc(0, 16, 5));
|
||||
|
||||
// default (min 0, max 100, step 1)
|
||||
$this->assertSame($hundred, $this->twig_ext->rangeFunc());
|
||||
|
||||
// 95 items, starting from 5, (min 5, max 100, step 1)
|
||||
$this->assertSame(array_slice($hundred, 5), $this->twig_ext->rangeFunc(5));
|
||||
|
||||
// reversed range
|
||||
$this->assertSame(array_reverse($hundred), $this->twig_ext->rangeFunc(100, 0));
|
||||
$this->assertSame([4, 2, 0], $this->twig_ext->rangeFunc(4, 0, 2));
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user