mirror of
https://github.com/getgrav/grav.git
synced 2025-12-05 15:29:57 +01:00
Compare commits
324 Commits
1.0.0-rc.2
...
1.0.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a5a400b89 | ||
|
|
96d7c4790f | ||
|
|
c0b9ada21c | ||
|
|
7b116b41ae | ||
|
|
fcdd0bc0e9 | ||
|
|
0619f7c656 | ||
|
|
f7696b61d3 | ||
|
|
48083b203a | ||
|
|
f8aa9fed91 | ||
|
|
c55521ac4a | ||
|
|
2bb23efa7c | ||
|
|
cdcb76cc55 | ||
|
|
def726a012 | ||
|
|
7d1291e2b9 | ||
|
|
a0297e9d65 | ||
|
|
e4e0c06ea8 | ||
|
|
f46ad2d032 | ||
|
|
0c807b6108 | ||
|
|
3a47d6a580 | ||
|
|
52947b3a2c | ||
|
|
eacfc798f4 | ||
|
|
5c0e5f3c01 | ||
|
|
985141b842 | ||
|
|
1aac2ab95d | ||
|
|
aa7d5ddf59 | ||
|
|
e87505378d | ||
|
|
53f097c2b3 | ||
|
|
6ea7fe9dba | ||
|
|
0206f1b0c7 | ||
|
|
ef9f2c77dd | ||
|
|
822292f541 | ||
|
|
ec08cd9374 | ||
|
|
8fb4063cf2 | ||
|
|
8a2b444c48 | ||
|
|
9c6f243902 | ||
|
|
d5bd99b363 | ||
|
|
5554c07cbf | ||
|
|
acd95aac6f | ||
|
|
8ad5f2624d | ||
|
|
8adc0e1c17 | ||
|
|
fab66cf3a5 | ||
|
|
5df48e7a68 | ||
|
|
079a8c5728 | ||
|
|
d9196426a1 | ||
|
|
2f686f5b74 | ||
|
|
c4b5df20a9 | ||
|
|
06608a6d3c | ||
|
|
fd63911faf | ||
|
|
66a1e55867 | ||
|
|
af2eb2e75d | ||
|
|
9b4f32cafd | ||
|
|
9179fbd1a2 | ||
|
|
24ea7f1f55 | ||
|
|
9b95053110 | ||
|
|
71ffb9c72f | ||
|
|
f29f698f61 | ||
|
|
f1f8579a0b | ||
|
|
4ca8fab750 | ||
|
|
b80ed731b0 | ||
|
|
23a9a73600 | ||
|
|
dc8c0b6522 | ||
|
|
e695b1942c | ||
|
|
3f543e7e84 | ||
|
|
7f6f9e82e3 | ||
|
|
51529eb0ce | ||
|
|
05b24a4b75 | ||
|
|
ca5819489f | ||
|
|
1d2acf8096 | ||
|
|
98278e965b | ||
|
|
7bc990688c | ||
|
|
b86aa6d473 | ||
|
|
dba7347c1e | ||
|
|
fa52e18e3f | ||
|
|
b605753a6d | ||
|
|
0af4fb351c | ||
|
|
90edf95077 | ||
|
|
ab3843442a | ||
|
|
023b9dd708 | ||
|
|
0ac882314e | ||
|
|
0fe9264582 | ||
|
|
b1e16b2206 | ||
|
|
a67c1780c1 | ||
|
|
1170f2f58d | ||
|
|
199c0a08ea | ||
|
|
84ad152536 | ||
|
|
5b0f905ae3 | ||
|
|
a2bba8f09d | ||
|
|
2007975428 | ||
|
|
e484997515 | ||
|
|
af5c52c52f | ||
|
|
f3d0e10378 | ||
|
|
b33ab43ff9 | ||
|
|
c580399db6 | ||
|
|
00d8717d7c | ||
|
|
6e2f4607a6 | ||
|
|
3a7abeb18b | ||
|
|
84a5984c65 | ||
|
|
1465c26b1f | ||
|
|
b399d8e3b9 | ||
|
|
b5c04bdc9b | ||
|
|
b4725800c3 | ||
|
|
2b1a102efa | ||
|
|
500c548af4 | ||
|
|
fc7017f822 | ||
|
|
5b254f4cf8 | ||
|
|
43783f3ce6 | ||
|
|
ae39aabee1 | ||
|
|
df7a94148b | ||
|
|
63890661fe | ||
|
|
793ac1a1bb | ||
|
|
77db54c50d | ||
|
|
b259927348 | ||
|
|
e8972a6aa5 | ||
|
|
8d8420c0d6 | ||
|
|
7a6707f597 | ||
|
|
b0ec66cce8 | ||
|
|
edfd7db88b | ||
|
|
fb3e68e16e | ||
|
|
080ab9e289 | ||
|
|
f67e441b83 | ||
|
|
9c07d69c45 | ||
|
|
13207f13ad | ||
|
|
34f83ebde2 | ||
|
|
14ed805656 | ||
|
|
385233c508 | ||
|
|
8a3b987cd5 | ||
|
|
bdd17fc56a | ||
|
|
29b3c081ee | ||
|
|
b1d80b6c5f | ||
|
|
e00560f81a | ||
|
|
20f17130a2 | ||
|
|
583156d2f3 | ||
|
|
88f36f4987 | ||
|
|
965c8cfbe9 | ||
|
|
b8f00243e6 | ||
|
|
1d97f98515 | ||
|
|
9764cf3f65 | ||
|
|
1f5df81496 | ||
|
|
5f76a0255c | ||
|
|
17f3ca6eba | ||
|
|
b29d79738b | ||
|
|
020cdd7324 | ||
|
|
1e39f3b22d | ||
|
|
37035a488d | ||
|
|
745b418cd7 | ||
|
|
2a02c8bc4f | ||
|
|
a15e063b92 | ||
|
|
2051fed5b7 | ||
|
|
268714863e | ||
|
|
60c6532307 | ||
|
|
50c6e81c09 | ||
|
|
2a0a9a225c | ||
|
|
aee92b58c7 | ||
|
|
16db950009 | ||
|
|
bde33e7188 | ||
|
|
267efbe164 | ||
|
|
f9e137c994 | ||
|
|
e7f9751403 | ||
|
|
9adf81294d | ||
|
|
3033818589 | ||
|
|
06d663680c | ||
|
|
1bbdca5032 | ||
|
|
c1654a988e | ||
|
|
18a540c867 | ||
|
|
99fc8df322 | ||
|
|
2d21cb8b1e | ||
|
|
d8008654b9 | ||
|
|
146295fb1e | ||
|
|
09ed480628 | ||
|
|
5dd1554e5d | ||
|
|
748f329c8e | ||
|
|
7228b25393 | ||
|
|
63e083ea37 | ||
|
|
b1630feb5d | ||
|
|
1185a91c90 | ||
|
|
dce6d7894b | ||
|
|
375ee0d1fa | ||
|
|
ce0574f897 | ||
|
|
c515111446 | ||
|
|
7d6393628e | ||
|
|
24fde7261a | ||
|
|
3d922abf1a | ||
|
|
7f1d3a94fe | ||
|
|
3d774b7585 | ||
|
|
b0083548b6 | ||
|
|
905dae3b16 | ||
|
|
d79979371b | ||
|
|
c4bff94f7d | ||
|
|
7c4fd3858c | ||
|
|
3b9af8883d | ||
|
|
29b34d7de0 | ||
|
|
90fcf448c7 | ||
|
|
5193551d04 | ||
|
|
d2660e0755 | ||
|
|
2d8ac27fdd | ||
|
|
dd2ddfeb40 | ||
|
|
ac3396e6c4 | ||
|
|
49a5b38589 | ||
|
|
6e2f792bb9 | ||
|
|
da098fd46a | ||
|
|
3e081b340f | ||
|
|
698015a03d | ||
|
|
000a10f936 | ||
|
|
c764e31c8a | ||
|
|
c18021d52a | ||
|
|
3d0cc67415 | ||
|
|
3f94a6fda9 | ||
|
|
7f0eefbde5 | ||
|
|
280377985f | ||
|
|
d5b3f070a5 | ||
|
|
3505ef046d | ||
|
|
a6bc565356 | ||
|
|
a1ee3cf4e4 | ||
|
|
e96445abe3 | ||
|
|
c22fae0d3d | ||
|
|
d888dcd085 | ||
|
|
184cb9ea3a | ||
|
|
3cf6e8762c | ||
|
|
f0cdd7c03e | ||
|
|
5e40201888 | ||
|
|
f2c2debb28 | ||
|
|
997c772b7c | ||
|
|
bc4a09f80d | ||
|
|
0e3e7497ac | ||
|
|
72313ac9ec | ||
|
|
dc80228f0b | ||
|
|
a83642a7e3 | ||
|
|
00d8403095 | ||
|
|
e1ec8e9742 | ||
|
|
0725af5367 | ||
|
|
65e543af02 | ||
|
|
b49e8315eb | ||
|
|
c5a89112b4 | ||
|
|
94ec474ffa | ||
|
|
da4593fdc1 | ||
|
|
b8413cefaf | ||
|
|
d3097e4fd0 | ||
|
|
51753f0716 | ||
|
|
b7ada873b8 | ||
|
|
ed8b08a9e4 | ||
|
|
e1fdb6803d | ||
|
|
5cb9f2f42f | ||
|
|
fcf48ed2e5 | ||
|
|
050512536a | ||
|
|
99207fca13 | ||
|
|
55890b4fd8 | ||
|
|
6fdfaccc92 | ||
|
|
76e01e7aea | ||
|
|
87378562ea | ||
|
|
77d80f12f3 | ||
|
|
e400207a65 | ||
|
|
4b68036a1b | ||
|
|
a95b716aa7 | ||
|
|
dc8efded34 | ||
|
|
e016b17276 | ||
|
|
b99876f0b4 | ||
|
|
66abc842b7 | ||
|
|
9f36158c67 | ||
|
|
18c1ca3919 | ||
|
|
0c729e5b0a | ||
|
|
55f3b78ab1 | ||
|
|
a73b796ca7 | ||
|
|
bb0bca7ef1 | ||
|
|
b3144ee921 | ||
|
|
a86ce7cb28 | ||
|
|
ccf2a780b6 | ||
|
|
b0c1dbe4b7 | ||
|
|
ec73eef695 | ||
|
|
467d68344e | ||
|
|
8899b3ebb8 | ||
|
|
5478cfaf9f | ||
|
|
4b6a85f30a | ||
|
|
e62ff07726 | ||
|
|
a045107cc7 | ||
|
|
c97edb60a5 | ||
|
|
695793b752 | ||
|
|
c953ffb471 | ||
|
|
3d7fa06129 | ||
|
|
49d4fbcf3d | ||
|
|
fc18a40c35 | ||
|
|
1e81d5e38c | ||
|
|
daf8b53c0d | ||
|
|
8de55a745d | ||
|
|
6bf669815d | ||
|
|
8ba49e163d | ||
|
|
26918d90ab | ||
|
|
929b0806dc | ||
|
|
3e32e61db1 | ||
|
|
038693bffb | ||
|
|
9445aa43e6 | ||
|
|
bb16dbab78 | ||
|
|
658212e7be | ||
|
|
e91554770c | ||
|
|
8f9671ad32 | ||
|
|
7f134e39f4 | ||
|
|
c87e3f419d | ||
|
|
07b2767ac9 | ||
|
|
0ca24a9786 | ||
|
|
c84c1366e7 | ||
|
|
ebf9bb5c18 | ||
|
|
70b67a0805 | ||
|
|
545b97716f | ||
|
|
7e540e0623 | ||
|
|
f7140522f6 | ||
|
|
bd2f7088e9 | ||
|
|
5260c181a1 | ||
|
|
a9538adf2b | ||
|
|
b0c171f453 | ||
|
|
67fefb53ad | ||
|
|
0ff5dc0016 | ||
|
|
ae17a77789 | ||
|
|
5f11ae7482 | ||
|
|
4d33eb2173 | ||
|
|
fe7873ddbe | ||
|
|
f95a4f5cc6 | ||
|
|
f973b61b5e | ||
|
|
f1d4192ae7 | ||
|
|
fb500d3e1c | ||
|
|
14347ebf88 | ||
|
|
3e0188e40b | ||
|
|
b82f17f367 | ||
|
|
3f28dc59ea | ||
|
|
77deea8ad4 | ||
|
|
e27f638fe3 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -16,12 +16,15 @@ logs/*
|
||||
!logs/.*
|
||||
images/*
|
||||
!images/.*
|
||||
user/accounts/*
|
||||
!user/accounts/.*
|
||||
user/data/*
|
||||
!user/data/.*
|
||||
user/plugins/*
|
||||
!user/plugins/.*
|
||||
user/themes/*
|
||||
!user/themes/.*
|
||||
user/localhost/config/security.yaml
|
||||
|
||||
# OS Generated
|
||||
.DS_Store*
|
||||
|
||||
12
.htaccess
12
.htaccess
@@ -44,15 +44,17 @@ RewriteRule .* index.php [L]
|
||||
|
||||
## Begin - Security
|
||||
# Block all direct access for these folders
|
||||
RewriteRule ^(.git|cache|bin|logs|backup)/(.*) error [L]
|
||||
# Block access to specific file types for these folders
|
||||
RewriteRule ^(system|user|vendor)/(.*)\.(txt|md|html|yaml|php|twig|sh|bat)$ error [L]
|
||||
RewriteRule ^(.git|cache|bin|logs|backup)/(.*) error [F]
|
||||
# Block access to specific file types for these system folders
|
||||
RewriteRule ^(system|vendor)/(.*)\.(txt|xml|md|html|yaml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
# Block access to specific file types for these user folders
|
||||
RewriteRule ^(user)/(.*)\.(txt|md|yaml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
# Block all direct access to .md files:
|
||||
RewriteRule \.md$ error [L]
|
||||
RewriteRule \.md$ error [F]
|
||||
# Block all direct access to files and folders beginning with a dot
|
||||
RewriteRule (^\.|/\.) - [F]
|
||||
# Block access to specific files in the root folder
|
||||
RewriteRule ^(LICENSE|composer.lock|composer.json|nginx.conf|web.config)$ error [F]
|
||||
RewriteRule ^(LICENSE.txt|composer.lock|composer.json|nginx.conf|web.config|htaccess.txt|\.htaccess)$ error [F]
|
||||
## End - Security
|
||||
|
||||
</IfModule>
|
||||
|
||||
140
CHANGELOG.md
140
CHANGELOG.md
@@ -1,3 +1,141 @@
|
||||
# v1.0.3
|
||||
## 12/11/2015
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed issue when saving config causing incorrect image cache folder perms
|
||||
|
||||
# v1.0.2
|
||||
## 12/11/2015
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fix for timing display in debugbar
|
||||
|
||||
# v1.0.1
|
||||
## 12/11/2015
|
||||
|
||||
1. [](#improved)
|
||||
* Reduced package sizes by removing extra vendor dev bits
|
||||
1. [](#bugfix)
|
||||
* Fix issue when you enable debugger from admin plugin
|
||||
|
||||
# v1.0.0
|
||||
## 12/11/2015
|
||||
|
||||
1. [](#new)
|
||||
* Add new link attributes via markdown media
|
||||
* Added setters to set state of CSS/JS pipelining
|
||||
* Added `user/accounts` to `.gitignore`
|
||||
* Added configurable permissions option for Image cache
|
||||
1. [](#improved)
|
||||
* Hungarian translation updated
|
||||
* Refactored Theme initialization for improved flexibility
|
||||
* Wrapped security section of account blueprints in an 'super user' authorize check
|
||||
* Minor performance optimizations
|
||||
* Updated core page blueprints with markdown preview option
|
||||
* Added useful cache info output to Debugbar
|
||||
* Added `iconv` polyfill library used by Symfony 2.8
|
||||
* Force lowercase of username in a few places for case sensitive filesystems
|
||||
1. [](#bugfix)
|
||||
* Fix for GPM problems "Call to a member function set() on null"
|
||||
* Fix for individual asset pipeline values not functioning
|
||||
* Fix `Page::copy()` and `Page::move()` to support multiple moves at once
|
||||
* Fixed page moving of a page with no content
|
||||
* Fix for wrong ordering when moving many pages
|
||||
* Escape root path in page medium files to work with special characters
|
||||
* Add missing parent constructor to Themes class
|
||||
* Fix missing file error in `bin/grav sandbox` command
|
||||
* Fixed changelog differ when upgrading Grav
|
||||
* Fixed a logic error in `Validation->validate()`
|
||||
* Make `$container` available in `setup.php` to fix multi-site
|
||||
|
||||
# v1.0.0-rc.6
|
||||
## 12/01/2015
|
||||
|
||||
1. [](#new)
|
||||
* Refactor Config classes for improved performance!
|
||||
* Refactor Data classes to use `NestedArrayAccess` instead of `DataMutatorTrait`
|
||||
* Added support for `classes` and `id` on medium objects to set CSS values
|
||||
* Data objects: Allow function call chaining
|
||||
* Data objects: Lazy load blueprints only if needed
|
||||
* Automatically create unique security salt for each configuration
|
||||
* Added Hungarian translation
|
||||
* Added support for User groups
|
||||
1. [](#improved)
|
||||
* Improved robots.txt to disallow crawling of non-user folders
|
||||
* Nonces only generated once per action and process
|
||||
* Added IP into Nonce string calculation
|
||||
* Nonces now use random string with random salt to improve performance
|
||||
* Improved list form handling #475
|
||||
* Vendor library updates
|
||||
1. [](#bugfix)
|
||||
* Fixed help output for `bin/plugin`
|
||||
* Fix for nested logic for lists and form parsing #273
|
||||
* Fix for array form fields and last entry not getting deleted
|
||||
* Should not be able to set parent to self #308
|
||||
|
||||
# v1.0.0-rc.5
|
||||
## 11/20/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added **nonce** functionality for all admin forms for improved security
|
||||
* Implemented the ability for Plugins to provide their own CLI commands through `bin/plugin`
|
||||
* Added Croatian translation
|
||||
* Added missing `umask_fix` property to `system.yaml`
|
||||
* Added current theme's config to global config. E.g. `config.theme.dropdown_enabled`
|
||||
* Added `append_url_extension` option to system config & page headers
|
||||
* Users have a new `state` property to allow disabling/banning
|
||||
* Added new `Page.relativePagePath()` helper method
|
||||
* Added new `|pad` Twig filter for strings (uses `str_pad()`)
|
||||
* Added `lighttpd.conf` for Lightly web server
|
||||
1. [](#improved)
|
||||
* Clear previously applied operations when doing a reset on image media
|
||||
* Password no longer required when editing user
|
||||
* Improved support for trailing `/` URLs
|
||||
* Improved `.nginx.conf` configuration file
|
||||
* Improved `.htaccess` security
|
||||
* Updated vendor libs
|
||||
* Updated `composer.phar`
|
||||
* Use streams instead of paths for `clearCache()`
|
||||
* Use PCRE_UTF8 so unicode strings can be regexed in Truncator
|
||||
* Handle case when login plugin is disabled
|
||||
* Improved `quality` functionality in media handling
|
||||
* Added some missing translation strings
|
||||
* Deprecated `bin/grav newuser` in favor of `bin/plugin login new-user`
|
||||
* Moved fallback types to use any valid media type
|
||||
* Renamed `system.pages.fallback_types` to `system.media.allowed_fallback_types`
|
||||
* Removed version number in default `generator` meta tag
|
||||
* Disable time limit in case of slow downloads
|
||||
* Removed default hash in `system.yaml`
|
||||
1. [](#bugfix)
|
||||
* Fix for media using absolute URLs causing broken links
|
||||
* Fix theme auto-loading #432
|
||||
* Don't create empty `<style>` or `<script>` scripts if no data
|
||||
* Code cleanups
|
||||
* Fix undefined variable in Config class
|
||||
* Fix exception message when label is not set
|
||||
* Check in `Plugins::get()` to ensure plugins exists
|
||||
* Fixed GZip compression making output buffering work correctly with all servers and browsers
|
||||
* Fixed date representation in system config
|
||||
|
||||
# v1.0.0-rc.4
|
||||
## 10/29/2015
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed a fatal error if you have a collection with missing or invalid `@page: /route`
|
||||
|
||||
# v1.0.0-rc.3
|
||||
## 10/29/2015
|
||||
|
||||
1. [](#new)
|
||||
* New Page collection options! `@self.parent, @self.siblings, @self.descendants` + more
|
||||
* White list of file types for fallback route functionality (images by default)
|
||||
1. [](#improved)
|
||||
* Assets switched from defines to streams
|
||||
1. [](#bugfix)
|
||||
* README.md typos fixed
|
||||
* Fixed issue with routes that have lang string in them (`/en/english`)
|
||||
* Trim strings before validation so whitespace is not satisfy 'required'
|
||||
|
||||
# v1.0.0-rc.2
|
||||
## 10/27/2015
|
||||
|
||||
@@ -15,7 +153,7 @@
|
||||
* German language improvements
|
||||
* Updated bundled composer
|
||||
1. [](#bugfix)
|
||||
* Accept variety of `true` values in `User.authorize()` method
|
||||
* Accept variety of `true` values in `User.authorize()` method
|
||||
* Fix for `Validation` throwing an error if no label set
|
||||
|
||||
# v1.0.0-rc.1
|
||||
|
||||
10
README.md
10
README.md
@@ -9,10 +9,10 @@ The underlying architecture of Grav is designed to use well-established and _bes
|
||||
* [Twig Templating](http://twig.sensiolabs.org/): for powerful control of the user interface
|
||||
* [Markdown](http://en.wikipedia.org/wiki/Markdown): for easy content creation
|
||||
* [YAML](http://yaml.org): for simple configuration
|
||||
* [Parsedown](http://parsedown.org/): for fast Markdown and Mardown Extra support
|
||||
* [Parsedown](http://parsedown.org/): for fast Markdown and Markdown Extra support
|
||||
* [Doctrine Cache](http://docs.doctrine-project.org/en/2.0.x/reference/caching.html): layer for performance
|
||||
* [Pimple Dependency Injection Container](http://pimple.sensiolabs.org/): for extensibility and maintainability
|
||||
* [Symfony Event Dispacher](http://symfony.com/doc/current/components/event_dispatcher/introduction.html): for plugin event handling
|
||||
* [Symfony Event Dispatcher](http://symfony.com/doc/current/components/event_dispatcher/introduction.html): for plugin event handling
|
||||
* [Symfony Console](http://symfony.com/doc/current/components/console/introduction.html): for CLI interface
|
||||
* [Gregwar Image Library](https://github.com/Gregwar/Image): for dynamic image manipulation
|
||||
|
||||
@@ -53,7 +53,7 @@ You can download [plugins](http://getgrav.org/downloads/plugins) or [themes](htt
|
||||
$ bin/gpm index
|
||||
```
|
||||
|
||||
This will display all the available plugins and then you can install one ore more with:
|
||||
This will display all the available plugins and then you can install one or more with:
|
||||
|
||||
```
|
||||
$ bin/gpm install <plugin/theme>
|
||||
@@ -76,7 +76,7 @@ $ bin/gpm update
|
||||
|
||||
# Contributing
|
||||
We appreciate any contribution to Grav, whether it is related to bugs, grammar, or simply a suggestion or improvement.
|
||||
However, we ask that any contribution follow our simple guidelines in order to be properly received.
|
||||
However, we ask that any contributions follow our simple guidelines in order to be properly received.
|
||||
|
||||
All our projects follow the [GitFlow branching model][gitflow-model], from development to release. If you are not familiar with it, there are several guides and tutorials to make you understand what it is about.
|
||||
|
||||
@@ -103,7 +103,7 @@ What you mainly want to know is that:
|
||||
|
||||
# License
|
||||
|
||||
See [LICENSE](LICENSE)
|
||||
See [LICENSE](LICENSE.txt)
|
||||
|
||||
|
||||
[gitflow-model]: http://nvie.com/posts/a-successful-git-branching-model/
|
||||
|
||||
Binary file not shown.
2
bin/gpm
2
bin/gpm
@@ -40,8 +40,6 @@ if (!function_exists('curl_version')) {
|
||||
$grav = Grav::instance(array('loader' => $autoload));
|
||||
$grav['config']->init();
|
||||
$grav['streams'];
|
||||
$grav['plugins']->init();
|
||||
$grav['themes']->init();
|
||||
|
||||
$app = new Application('Grav Package Manager', GRAV_VERSION);
|
||||
$app->addCommands(array(
|
||||
|
||||
116
bin/plugin
Executable file
116
bin/plugin
Executable file
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
define('GRAV_CLI', true);
|
||||
|
||||
if (version_compare($ver = PHP_VERSION, $req = '5.4.0', '<')) {
|
||||
exit(sprintf("You are running PHP %s, but Grav needs at least PHP %s to run.\n", $ver, $req));
|
||||
}
|
||||
|
||||
if (!file_exists(__DIR__ . '/../vendor')) {
|
||||
require_once __DIR__ . '/../system/src/Grav/Common/Composer.php';
|
||||
}
|
||||
|
||||
use Grav\Common\Composer;
|
||||
|
||||
if (!file_exists(__DIR__ . '/../vendor')) {
|
||||
// Before we can even start, we need to run composer first
|
||||
$composer = Composer::getComposerExecutor();
|
||||
echo "Preparing to install vendor dependencies...\n\n";
|
||||
echo system($composer . ' --working-dir="' . __DIR__ . '/../" --no-interaction --no-dev --prefer-dist -o install');
|
||||
echo "\n\n";
|
||||
}
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
|
||||
$autoload = require_once(__DIR__ . '/../vendor/autoload.php');
|
||||
|
||||
if (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('UTC');
|
||||
}
|
||||
|
||||
if (!file_exists(ROOT_DIR . 'index.php')) {
|
||||
exit('FATAL: Must be run from ROOT directory of Grav!');
|
||||
}
|
||||
|
||||
$grav = Grav::instance(array('loader' => $autoload));
|
||||
$grav['config']->init();
|
||||
$grav['streams'];
|
||||
$grav['plugins']->init();
|
||||
$grav['themes']->init();
|
||||
|
||||
$app = new Application('Grav Plugins Commands', GRAV_VERSION);
|
||||
$pattern = '([A-Z]\w+Command\.php)';
|
||||
|
||||
// get arguments and strip the application name
|
||||
if (null === $argv) {
|
||||
$argv = $_SERVER['argv'];
|
||||
}
|
||||
|
||||
$bin = array_shift($argv);
|
||||
$name = array_shift($argv);
|
||||
$argv = array_merge([$bin], $argv);
|
||||
|
||||
$input = new ArgvInput($argv);
|
||||
|
||||
$plugin = $grav['plugins']->get($name);
|
||||
|
||||
$output = new ConsoleOutput();
|
||||
$output->getFormatter()->setStyle('red', new OutputFormatterStyle('red', null, array('bold')));
|
||||
$output->getFormatter()->setStyle('white', new OutputFormatterStyle('white', null, array('bold')));
|
||||
|
||||
if (!$name) {
|
||||
$output->writeln('');
|
||||
$output->writeln("<red>Usage:</red>");
|
||||
$output->writeln(" {$bin} [slug] [command] [arguments]");
|
||||
$output->writeln('');
|
||||
$output->writeln("<red>Example:</red>");
|
||||
$output->writeln(" {$bin} error log -l 1 --trace");
|
||||
$list = Folder::all('plugins://', ['compare' => 'Pathname', 'pattern' => '/\/cli\/' . $pattern . '$/usm']);
|
||||
|
||||
if (count($list)) {
|
||||
$available = [];
|
||||
$output->writeln('');
|
||||
$output->writeln('<red>Plugins with CLI available:</red>');
|
||||
foreach ($list as $index => $entry) {
|
||||
$split = explode('/', $entry);
|
||||
$entry = array_shift($split);
|
||||
$index = str_pad($index++ + 1, 2, '0', STR_PAD_LEFT);
|
||||
|
||||
if (in_array($entry, $available)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$available[] = $entry;
|
||||
$output->writeln(' ' . $index . ". <red>" . str_pad($entry, 15) . "</red> <white>${bin} ${entry} list</white>");
|
||||
}
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($plugin === null) {
|
||||
$output->writeln("<red>Grav Plugin <white>'{$name}'</white> is not installed</red>");
|
||||
exit;
|
||||
}
|
||||
|
||||
$path = 'plugins://' . $name . '/cli';
|
||||
|
||||
try {
|
||||
$commands = Folder::all($path, ['compare' => 'Filename', 'pattern' => '/' . $pattern . '$/usm']);
|
||||
} catch (\RuntimeException $e) {
|
||||
$output->writeln("<red>No Console Commands for <white>'{$name}'</white> where found in <white>'{$path}'</white></red>");
|
||||
exit;
|
||||
}
|
||||
|
||||
foreach ($commands as $command) {
|
||||
require_once "plugins://{$name}/cli/{$command}";
|
||||
$command = 'Grav\Plugin\Console\\' . preg_replace('/.php$/', '', $command);
|
||||
$app->add(new $command());
|
||||
}
|
||||
|
||||
$app->run($input);
|
||||
@@ -7,21 +7,22 @@
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=5.4.0",
|
||||
"twig/twig": "~1.16",
|
||||
"twig/twig": "~1.23",
|
||||
"erusev/parsedown-extra": "~0.7",
|
||||
"symfony/yaml": "~2.7",
|
||||
"symfony/console": "~2.7",
|
||||
"symfony/event-dispatcher": "~2.7",
|
||||
"symfony/var-dumper": "~2.7",
|
||||
"doctrine/cache": "~1.4",
|
||||
"filp/whoops": "1.2.*@dev",
|
||||
"symfony/yaml": "~2.8",
|
||||
"symfony/console": "~2.8",
|
||||
"symfony/event-dispatcher": "~2.8",
|
||||
"symfony/var-dumper": "~2.8",
|
||||
"symfony/polyfill-iconv": "~1.0",
|
||||
"doctrine/cache": "~1.5",
|
||||
"filp/whoops": "1.1.10",
|
||||
"monolog/monolog": "~1.0",
|
||||
"gregwar/image": "~2.0",
|
||||
"ircmaxell/password-compat": "1.0.*",
|
||||
"mrclay/minify": "~2.2",
|
||||
"donatj/phpuseragentparser": "~0.3",
|
||||
"pimple/pimple": "~3.0",
|
||||
"rockettheme/toolbox": "1.1.*",
|
||||
"rockettheme/toolbox": "~1.2",
|
||||
"maximebf/debugbar": "~1.10"
|
||||
},
|
||||
"autoload": {
|
||||
|
||||
303
composer.lock
generated
303
composer.lock
generated
@@ -1,23 +1,24 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "e1db721096772d41f16003b39b47c85a",
|
||||
"hash": "09fcc6b4528be7d9c8af68a66e85f0b2",
|
||||
"content-hash": "69bee250cbc5160401d50cc47c8d6aba",
|
||||
"packages": [
|
||||
{
|
||||
"name": "doctrine/cache",
|
||||
"version": "v1.4.2",
|
||||
"version": "v1.5.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/cache.git",
|
||||
"reference": "8c434000f420ade76a07c64cbe08ca47e5c101ca"
|
||||
"reference": "47c7128262da274f590ae6f86eb137a7a64e82af"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/cache/zipball/8c434000f420ade76a07c64cbe08ca47e5c101ca",
|
||||
"reference": "8c434000f420ade76a07c64cbe08ca47e5c101ca",
|
||||
"url": "https://api.github.com/repos/doctrine/cache/zipball/47c7128262da274f590ae6f86eb137a7a64e82af",
|
||||
"reference": "47c7128262da274f590ae6f86eb137a7a64e82af",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -38,8 +39,8 @@
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Doctrine\\Common\\Cache\\": "lib/"
|
||||
"psr-4": {
|
||||
"Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@@ -74,7 +75,7 @@
|
||||
"cache",
|
||||
"caching"
|
||||
],
|
||||
"time": "2015-08-31 12:36:41"
|
||||
"time": "2015-12-03 10:50:37"
|
||||
},
|
||||
{
|
||||
"name": "donatj/phpuseragentparser",
|
||||
@@ -168,16 +169,16 @@
|
||||
},
|
||||
{
|
||||
"name": "erusev/parsedown-extra",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/erusev/parsedown-extra.git",
|
||||
"reference": "11a44e076d02ffcc4021713398a60cd73f78b6f5"
|
||||
"reference": "0db5cce7354e4b76f155d092ab5eb3981c21258c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/erusev/parsedown-extra/zipball/11a44e076d02ffcc4021713398a60cd73f78b6f5",
|
||||
"reference": "11a44e076d02ffcc4021713398a60cd73f78b6f5",
|
||||
"url": "https://api.github.com/repos/erusev/parsedown-extra/zipball/0db5cce7354e4b76f155d092ab5eb3981c21258c",
|
||||
"reference": "0db5cce7354e4b76f155d092ab5eb3981c21258c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -208,20 +209,20 @@
|
||||
"parsedown",
|
||||
"parser"
|
||||
],
|
||||
"time": "2015-01-25 14:52:34"
|
||||
"time": "2015-11-01 10:19:22"
|
||||
},
|
||||
{
|
||||
"name": "filp/whoops",
|
||||
"version": "dev-master",
|
||||
"version": "1.1.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filp/whoops.git",
|
||||
"reference": "9a393ceb80f7497b6513feb574638e87048fed55"
|
||||
"reference": "72538eeb70bbfb11964412a3d098d109efd012f7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filp/whoops/zipball/9a393ceb80f7497b6513feb574638e87048fed55",
|
||||
"reference": "9a393ceb80f7497b6513feb574638e87048fed55",
|
||||
"url": "https://api.github.com/repos/filp/whoops/zipball/72538eeb70bbfb11964412a3d098d109efd012f7",
|
||||
"reference": "72538eeb70bbfb11964412a3d098d109efd012f7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -266,7 +267,7 @@
|
||||
"whoops",
|
||||
"zf2"
|
||||
],
|
||||
"time": "2015-09-27 09:47:06"
|
||||
"time": "2015-06-29 05:42:04"
|
||||
},
|
||||
{
|
||||
"name": "gregwar/cache",
|
||||
@@ -403,25 +404,25 @@
|
||||
},
|
||||
{
|
||||
"name": "maximebf/debugbar",
|
||||
"version": "v1.10.5",
|
||||
"version": "v1.11.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/maximebf/php-debugbar.git",
|
||||
"reference": "30e53e8a28284b69dd223c9f5ee8957befd72636"
|
||||
"reference": "07741d84d39d10f00551c94284cdefcc69703e77"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/30e53e8a28284b69dd223c9f5ee8957befd72636",
|
||||
"reference": "30e53e8a28284b69dd223c9f5ee8957befd72636",
|
||||
"url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/07741d84d39d10f00551c94284cdefcc69703e77",
|
||||
"reference": "07741d84d39d10f00551c94284cdefcc69703e77",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0",
|
||||
"psr/log": "~1.0",
|
||||
"symfony/var-dumper": "~2.6"
|
||||
"psr/log": "^1.0",
|
||||
"symfony/var-dumper": "^2.6|^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0"
|
||||
"phpunit/phpunit": "^4.0|^5.0"
|
||||
},
|
||||
"suggest": {
|
||||
"kriswallsmith/assetic": "The best way to manage assets",
|
||||
@@ -431,12 +432,12 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.10-dev"
|
||||
"dev-master": "1.11-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"DebugBar": "src/"
|
||||
"psr-4": {
|
||||
"DebugBar\\": "src/DebugBar/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@@ -448,14 +449,19 @@
|
||||
"name": "Maxime Bouroumeau-Fuseau",
|
||||
"email": "maxime.bouroumeau@gmail.com",
|
||||
"homepage": "http://maximebf.com"
|
||||
},
|
||||
{
|
||||
"name": "Barry vd. Heuvel",
|
||||
"email": "barryvdh@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Debug bar in the browser for php application",
|
||||
"homepage": "https://github.com/maximebf/php-debugbar",
|
||||
"keywords": [
|
||||
"debug"
|
||||
"debug",
|
||||
"debugbar"
|
||||
],
|
||||
"time": "2015-10-19 20:35:12"
|
||||
"time": "2015-12-10 09:50:24"
|
||||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
@@ -665,16 +671,16 @@
|
||||
},
|
||||
{
|
||||
"name": "rockettheme/toolbox",
|
||||
"version": "1.1.4",
|
||||
"version": "1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/rockettheme/toolbox.git",
|
||||
"reference": "ff677d8f66d1addd3590d0cb85bcbaff4174d9c9"
|
||||
"reference": "0c7a3b4b6e4d73be8512e89f7acde6899334b7f2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/rockettheme/toolbox/zipball/ff677d8f66d1addd3590d0cb85bcbaff4174d9c9",
|
||||
"reference": "ff677d8f66d1addd3590d0cb85bcbaff4174d9c9",
|
||||
"url": "https://api.github.com/repos/rockettheme/toolbox/zipball/0c7a3b4b6e4d73be8512e89f7acde6899334b7f2",
|
||||
"reference": "0c7a3b4b6e4d73be8512e89f7acde6899334b7f2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -710,30 +716,30 @@
|
||||
"php",
|
||||
"rockettheme"
|
||||
],
|
||||
"time": "2015-10-15 23:27:40"
|
||||
"time": "2015-11-24 17:04:24"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v2.7.5",
|
||||
"version": "v2.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "06cb17c013a82f94a3d840682b49425cd00a2161"
|
||||
"reference": "d232bfc100dfd32b18ccbcab4bcc8f28697b7e41"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/06cb17c013a82f94a3d840682b49425cd00a2161",
|
||||
"reference": "06cb17c013a82f94a3d840682b49425cd00a2161",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/d232bfc100dfd32b18ccbcab4bcc8f28697b7e41",
|
||||
"reference": "d232bfc100dfd32b18ccbcab4bcc8f28697b7e41",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.9"
|
||||
"php": ">=5.3.9",
|
||||
"symfony/polyfill-mbstring": "~1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/log": "~1.0",
|
||||
"symfony/event-dispatcher": "~2.1",
|
||||
"symfony/phpunit-bridge": "~2.7",
|
||||
"symfony/process": "~2.1"
|
||||
"symfony/event-dispatcher": "~2.1|~3.0.0",
|
||||
"symfony/process": "~2.1|~3.0.0"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "For using the console logger",
|
||||
@@ -743,13 +749,16 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.7-dev"
|
||||
"dev-master": "2.8-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Console\\": ""
|
||||
}
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
@@ -767,20 +776,20 @@
|
||||
],
|
||||
"description": "Symfony Console Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-09-25 08:32:23"
|
||||
"time": "2015-11-30 12:35:10"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
"version": "v2.7.5",
|
||||
"version": "v2.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||
"reference": "ae4dcc2a8d3de98bd794167a3ccda1311597c5d9"
|
||||
"reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ae4dcc2a8d3de98bd794167a3ccda1311597c5d9",
|
||||
"reference": "ae4dcc2a8d3de98bd794167a3ccda1311597c5d9",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a5eb815363c0388e83247e7e9853e5dbc14999cc",
|
||||
"reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -788,11 +797,10 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/log": "~1.0",
|
||||
"symfony/config": "~2.0,>=2.0.5",
|
||||
"symfony/dependency-injection": "~2.6",
|
||||
"symfony/expression-language": "~2.6",
|
||||
"symfony/phpunit-bridge": "~2.7",
|
||||
"symfony/stopwatch": "~2.3"
|
||||
"symfony/config": "~2.0,>=2.0.5|~3.0.0",
|
||||
"symfony/dependency-injection": "~2.6|~3.0.0",
|
||||
"symfony/expression-language": "~2.6|~3.0.0",
|
||||
"symfony/stopwatch": "~2.3|~3.0.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/dependency-injection": "",
|
||||
@@ -801,13 +809,16 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.7-dev"
|
||||
"dev-master": "2.8-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\EventDispatcher\\": ""
|
||||
}
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
@@ -825,27 +836,140 @@
|
||||
],
|
||||
"description": "Symfony EventDispatcher Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-09-22 13:49:29"
|
||||
"time": "2015-10-30 20:15:42"
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
"version": "v2.7.5",
|
||||
"name": "symfony/polyfill-iconv",
|
||||
"version": "v1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-dumper.git",
|
||||
"reference": "ba8c9a0edf18f70a7efcb8d3eb35323a10263338"
|
||||
"url": "https://github.com/symfony/polyfill-iconv.git",
|
||||
"reference": "21a18998764e569c1675efc7191887130b319605"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/ba8c9a0edf18f70a7efcb8d3eb35323a10263338",
|
||||
"reference": "ba8c9a0edf18f70a7efcb8d3eb35323a10263338",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/21a18998764e569c1675efc7191887130b319605",
|
||||
"reference": "21a18998764e569c1675efc7191887130b319605",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.9"
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Iconv\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for the Iconv extension",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"iconv",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2015-11-04 20:28:58"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "0b6a8940385311a24e060ec1fe35680e17c74497"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0b6a8940385311a24e060ec1fe35680e17c74497",
|
||||
"reference": "0b6a8940385311a24e060ec1fe35680e17c74497",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for the Mbstring extension",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"mbstring",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2015-11-04 20:28:58"
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
"version": "v2.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-dumper.git",
|
||||
"reference": "e6f3855005f2bfad7d7e72431d374a6478893fe3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/e6f3855005f2bfad7d7e72431d374a6478893fe3",
|
||||
"reference": "e6f3855005f2bfad7d7e72431d374a6478893fe3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.9",
|
||||
"symfony/polyfill-mbstring": "~1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "~2.7"
|
||||
"twig/twig": "~1.20|~2.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-symfony_debug": ""
|
||||
@@ -853,7 +977,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.7-dev"
|
||||
"dev-master": "2.8-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -862,7 +986,10 @@
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\VarDumper\\": ""
|
||||
}
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
@@ -884,38 +1011,38 @@
|
||||
"debug",
|
||||
"dump"
|
||||
],
|
||||
"time": "2015-09-22 14:41:01"
|
||||
"time": "2015-11-18 13:45:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v2.7.5",
|
||||
"version": "v2.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "31cb2ad0155c95b88ee55fe12bc7ff92232c1770"
|
||||
"reference": "f79824187de95064a2f5038904c4d7f0227fedb5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/31cb2ad0155c95b88ee55fe12bc7ff92232c1770",
|
||||
"reference": "31cb2ad0155c95b88ee55fe12bc7ff92232c1770",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/f79824187de95064a2f5038904c4d7f0227fedb5",
|
||||
"reference": "f79824187de95064a2f5038904c4d7f0227fedb5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.9"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "~2.7"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.7-dev"
|
||||
"dev-master": "2.8-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Yaml\\": ""
|
||||
}
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
@@ -933,20 +1060,20 @@
|
||||
],
|
||||
"description": "Symfony Yaml Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-09-14 14:14:09"
|
||||
"time": "2015-11-30 12:35:10"
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v1.22.3",
|
||||
"version": "v1.23.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/Twig.git",
|
||||
"reference": "ebfc36b7e77b0c1175afe30459cf943010245540"
|
||||
"reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/ebfc36b7e77b0c1175afe30459cf943010245540",
|
||||
"reference": "ebfc36b7e77b0c1175afe30459cf943010245540",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/d9b6333ae8dd2c8e3fd256e127548def0bc614c6",
|
||||
"reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -959,7 +1086,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.22-dev"
|
||||
"dev-master": "1.23-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -994,15 +1121,13 @@
|
||||
"keywords": [
|
||||
"templating"
|
||||
],
|
||||
"time": "2015-10-13 07:07:02"
|
||||
"time": "2015-11-05 12:49:06"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"filp/whoops": 20
|
||||
},
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
||||
12
htaccess.txt
12
htaccess.txt
@@ -44,15 +44,17 @@ RewriteRule .* index.php [L]
|
||||
|
||||
## Begin - Security
|
||||
# Block all direct access for these folders
|
||||
RewriteRule ^(.git|cache|bin|logs|backup)/(.*) error [L]
|
||||
# Block access to specific file types for these folders
|
||||
RewriteRule ^(system|user|vendor)/(.*)\.(txt|md|html|yaml|php|twig|sh|bat)$ error [L]
|
||||
RewriteRule ^(.git|cache|bin|logs|backup)/(.*) error [F]
|
||||
# Block access to specific file types for these system folders
|
||||
RewriteRule ^(system|vendor)/(.*)\.(txt|xml|md|html|yaml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
# Block access to specific file types for these user folders
|
||||
RewriteRule ^(user)/(.*)\.(txt|md|yaml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
# Block all direct access to .md files:
|
||||
RewriteRule \.md$ error [L]
|
||||
RewriteRule \.md$ error [F]
|
||||
# Block all direct access to files and folders beginning with a dot
|
||||
RewriteRule (^\.|/\.) - [F]
|
||||
# Block access to specific files in the root folder
|
||||
RewriteRule ^(LICENSE|composer.lock|composer.json|nginx.conf|web.config)$ error [F]
|
||||
RewriteRule ^(LICENSE.txt|composer.lock|composer.json|nginx.conf|web.config|htaccess.txt|\.htaccess)$ error [F]
|
||||
## End - Security
|
||||
|
||||
</IfModule>
|
||||
|
||||
48
lighttpd.conf
Normal file
48
lighttpd.conf
Normal file
@@ -0,0 +1,48 @@
|
||||
############# DO NOT FORGET TO CHANGE "grav_path" BY YOUR ACTUAL GRAV INSTALLATION FOLDER #############
|
||||
############# IF GRAV IS AT THE ROOT OF YOUR WEBSITE, ie http://yoursite.tld POINTS TO #############
|
||||
############# GRAV DIRECTLY, THEN JUST REMOVE ANY "/grav_path/" MENTION BELOW. OTHERWISE #############
|
||||
############# WE ASSUME YOU RUN AN INSTALLATION SUCH AS http://yoursite.tld/grav_path/ #############
|
||||
#######################################################################################################
|
||||
### GRAV RULES FOR LIGHTTPD ###
|
||||
### By Mr3ase ###
|
||||
### Last Rev. 2015/11/20 ###
|
||||
|
||||
#PREVENTING EXPLOITS
|
||||
$HTTP["querystring"] =~ "base64_encode[^(]*\([^)]*\)" {
|
||||
url.redirect = (".*" => "/grav_path/index.php" )
|
||||
}
|
||||
$HTTP["querystring"] =~ "(<|%3C)([^s]*s)+cript.*(>|%3E)" {
|
||||
url.redirect = (".*" => "/grav_path/index.php" )
|
||||
}
|
||||
$HTTP["querystring"] =~ "GLOBALS(=|\[|\%[0-9A-Z])" {
|
||||
url.redirect = (".*" => "/grav_path/index.php" )
|
||||
}
|
||||
$HTTP["querystring"] =~ "_REQUEST(=|\[|\%[0-9A-Z])" {
|
||||
url.redirect = (".*" => "/grav_path/index.php" )
|
||||
}
|
||||
|
||||
#REROUTING TO THE INDEX PAGE
|
||||
url.rewrite-if-not-file = (
|
||||
"^/grav_path/(.*)$" => "/grav_path/index.php"
|
||||
)
|
||||
|
||||
#IMPROVING SECURITY
|
||||
$HTTP["url"] =~ "^/grav_path/(LICENSE.txt|composer.json|composer.lock|nginx.conf|web.config)$" {
|
||||
url.access-deny = ("")
|
||||
}
|
||||
$HTTP["url"] =~ "^/grav_path/(.git|cache|bin|logs|backup)/(.*)" {
|
||||
url.access-deny = ("")
|
||||
}
|
||||
$HTTP["url"] =~ "^/grav_path/(system|user|vendor)/(.*)\.(txt|md|html|yaml|php|twig|sh|bat)$" {
|
||||
url.access-deny = ("")
|
||||
}
|
||||
$HTTP["url"] =~ "^/grav_path/(\.(.*))|(\.(.*)/)" {
|
||||
url.access-deny = ("")
|
||||
}
|
||||
url.access-deny = (".md","~",".inc")
|
||||
|
||||
#PREVENT BROWSING AND SET INDEXES
|
||||
$HTTP["url"] =~ "^/grav_path($|/)" {
|
||||
dir-listing.activate = "disable"
|
||||
index-file.names = ( "index.php", "index.html" , "index.htm" )
|
||||
}
|
||||
121
nginx.conf
121
nginx.conf
@@ -1,87 +1,44 @@
|
||||
worker_processes 1;
|
||||
server {
|
||||
#listen 80;
|
||||
index index.html index.php;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root html;
|
||||
}
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.php;
|
||||
if (!-e $request_filename){ rewrite ^(.*)$ /index.php last; }
|
||||
}
|
||||
|
||||
# if you want grav in a sub-directory of your main site
|
||||
# (for example, example.com/mygrav) then you need this rewrite:
|
||||
location /mygrav {
|
||||
index index.php;
|
||||
if (!-e $request_filename){ rewrite ^(.*)$ /mygrav/$2 last; }
|
||||
try_files $uri $uri/ /index.php?$args;
|
||||
}
|
||||
|
||||
# if using grav in a sub-directory of your site,
|
||||
# prepend the actual path to each location
|
||||
# for example: /mygrav/images
|
||||
# and: /mygrav/user
|
||||
# and: /mygrav/cache
|
||||
# and so on
|
||||
|
||||
location /images/ {
|
||||
# Serve images as static
|
||||
}
|
||||
|
||||
location /user {
|
||||
rewrite ^/user/accounts/(.*)$ /error redirect;
|
||||
rewrite ^/user/config/(.*)$ /error redirect;
|
||||
rewrite ^/user/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
|
||||
}
|
||||
|
||||
location /cache {
|
||||
rewrite ^/cache/(.*) /error redirect;
|
||||
}
|
||||
|
||||
location /bin {
|
||||
rewrite ^/bin/(.*)$ /error redirect;
|
||||
}
|
||||
|
||||
location /backup {
|
||||
rewrite ^/backup/(.*) /error redirect;
|
||||
}
|
||||
|
||||
location /system {
|
||||
rewrite ^/system/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
|
||||
}
|
||||
|
||||
location /vendor {
|
||||
rewrite ^/vendor/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
|
||||
}
|
||||
|
||||
# Remember to change 127.0.0.1:9000 to the Ip/port
|
||||
# you configured php-cgi.exe to run from
|
||||
|
||||
location ~ \.php$ {
|
||||
try_files $uri =404;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass 127.0.0.1:9000;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
}
|
||||
## Begin - Server Info
|
||||
root /home/user/www/html;
|
||||
server_name localhost;
|
||||
## End - Server Info
|
||||
|
||||
## Begin - Index
|
||||
# for subfolders, simply adjust:
|
||||
# `location /subfolder {`
|
||||
# and the rewrite to use `/subfolder/index.php`
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
if (!-e $request_filename){ rewrite ^(.*)$ /index.php last; }
|
||||
}
|
||||
## End - Index
|
||||
|
||||
## Begin - PHP
|
||||
location ~ \.php$ {
|
||||
# Choose either a socket or TCP/IP address
|
||||
fastcgi_pass unix:/var/run/php5-fpm.sock;
|
||||
# fastcgi_pass 127.0.0.1:9000;
|
||||
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
|
||||
}
|
||||
## End - PHP
|
||||
|
||||
## Begin - Security
|
||||
# deny all direct access for these folders
|
||||
location ~* /(.git|cache|bin|logs|backups)/.*$ { return 403; }
|
||||
# deny running scripts inside core system folders
|
||||
location ~* /(system|vendor)/.*\.(txt|xml|md|html|yaml|php|pl|py|cgi|twig|sh|bat)$ { return 403; }
|
||||
# deny running scripts inside user folder
|
||||
location ~* /user/.*\.(txt|md|yaml|php|pl|py|cgi|twig|sh|bat)$ { return 403; }
|
||||
# deny access to specific files in the root folder
|
||||
location ~ /(LICENSE.txt|composer.lock|composer.json|nginx.conf|web.config|htaccess.txt|\.htaccess) { return 403; }
|
||||
## End - Security
|
||||
}
|
||||
|
||||
|
||||
11
robots.txt
11
robots.txt
@@ -1,2 +1,11 @@
|
||||
User-agent: *
|
||||
Disallow:
|
||||
Disallow: /backup/
|
||||
Disallow: /bin/
|
||||
Disallow: /cache/
|
||||
Disallow: /grav/
|
||||
Disallow: /logs/
|
||||
Disallow: /system/
|
||||
Disallow: /vendor/
|
||||
Disallow: /user/
|
||||
Allow: /user/pages/
|
||||
Allow: /user/themes/
|
||||
|
||||
@@ -73,7 +73,7 @@ form:
|
||||
options:
|
||||
"F jS \\a\\t g:ia": Date1
|
||||
"l jS \\of F g:i A": Date2
|
||||
"D, m M Y G:i:s": Date3
|
||||
"D, d M Y G:i:s": Date3
|
||||
"d-m-y G:i": Date4
|
||||
"jS M Y": Date5
|
||||
|
||||
@@ -86,7 +86,7 @@ form:
|
||||
options:
|
||||
"F jS \\a\\t g:ia": Date1
|
||||
"l jS \\of F g:i A": Date2
|
||||
"D, m M Y G:i:s": Date3
|
||||
"D, d M Y G:i:s": Date3
|
||||
"d-m-y G:i": Date4
|
||||
"jS M Y": Date5
|
||||
|
||||
@@ -142,6 +142,12 @@ form:
|
||||
twig: Twig Events
|
||||
use: keys
|
||||
|
||||
pages.append_url_extension:
|
||||
type: text
|
||||
placeholder: "e.g. .html"
|
||||
label: PLUGIN_ADMIN.APPEND_URL_EXT
|
||||
help: PLUGIN_ADMIN.APPEND_URL_EXT_HELP
|
||||
|
||||
pages.redirect_default_route:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.REDIRECT_DEFAULT_ROUTE
|
||||
@@ -225,6 +231,7 @@ form:
|
||||
languages.supported:
|
||||
type: selectize
|
||||
size: large
|
||||
placeholder: "e.g. en, fr"
|
||||
label: PLUGIN_ADMIN.SUPPORTED
|
||||
help: PLUGIN_ADMIN.SUPPORTED_HELP
|
||||
classes: fancy
|
||||
@@ -509,6 +516,17 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
twig.umask_fix:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.TWIG_UMASK_FIX
|
||||
help: PLUGIN_ADMIN.TWIG_UMASK_FIX_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
assets:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.ASSETS
|
||||
@@ -681,6 +699,17 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
images.cache_perms:
|
||||
type: select
|
||||
size: small
|
||||
label: PLUGIN_ADMIN.CACHE_PERMS
|
||||
help: PLUGIN_ADMIN.CACHE_PERMS_HELP
|
||||
highlight: '0755'
|
||||
options:
|
||||
'0755': '0755'
|
||||
'0775': '0775'
|
||||
|
||||
|
||||
images.debug:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.IMAGES_DEBUG
|
||||
@@ -711,6 +740,26 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
|
||||
|
||||
media.allowed_fallback_types:
|
||||
type: selectize
|
||||
size: large
|
||||
label: PLUGIN_ADMIN.FALLBACK_TYPES
|
||||
help: PLUGIN_ADMIN.FALLBACK_TYPES_HELP
|
||||
classes: fancy
|
||||
validate:
|
||||
type: commalist
|
||||
|
||||
media.unsupported_inline_types:
|
||||
type: selectize
|
||||
size: large
|
||||
label: PLUGIN_ADMIN.INLINE_TYPES
|
||||
help: PLUGIN_ADMIN.INLINE_TYPES_HELP
|
||||
classes: fancy
|
||||
validate:
|
||||
type: commalist
|
||||
|
||||
session:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.SESSION
|
||||
@@ -718,13 +767,14 @@ form:
|
||||
|
||||
fields:
|
||||
session.enabled:
|
||||
type: toggle
|
||||
type: hidden
|
||||
label: PLUGIN_ADMIN.ENABLED
|
||||
help: PLUGIN_ADMIN.SESSION_ENABLED_HELP
|
||||
highlight: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
default: true
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
@@ -774,6 +824,7 @@ form:
|
||||
|
||||
param_sep:
|
||||
type: select
|
||||
size: medium
|
||||
label: PLUGIN_ADMIN.PARAMETER_SEPARATOR
|
||||
classes: fancy
|
||||
help: PLUGIN_ADMIN.PARAMETER_SEPARATOR_HELP
|
||||
|
||||
7
system/blueprints/media/meta.yaml
Normal file
7
system/blueprints/media/meta.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
alt_text:
|
||||
type: string
|
||||
label: Alt Text
|
||||
8
system/blueprints/media/move.yaml
Normal file
8
system/blueprints/media/move.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
route:
|
||||
type: select
|
||||
label: PLUGIN_ADMIN.PAGE
|
||||
classes: fancy
|
||||
'@data-options': '\Grav\Common\Page\Pages::parents'
|
||||
8
system/blueprints/media/rename.yaml
Normal file
8
system/blueprints/media/rename.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
new_file_name:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN_PRO.NEW_FILE_NAME
|
||||
validate:
|
||||
required: true
|
||||
@@ -29,7 +29,7 @@ form:
|
||||
|
||||
content:
|
||||
type: markdown
|
||||
label: PLUGIN_ADMIN.CONTENT
|
||||
showPreview: true
|
||||
validate:
|
||||
type: textarea
|
||||
|
||||
@@ -88,7 +88,6 @@ form:
|
||||
placeholder_key: PLUGIN_ADMIN.METADATA_KEY
|
||||
placeholder_value: PLUGIN_ADMIN.METADATA_VALUE
|
||||
|
||||
|
||||
taxonomies:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.TAXONOMIES
|
||||
@@ -264,6 +263,12 @@ form:
|
||||
default: default
|
||||
'@data-options': '\Grav\Common\Page\Pages::types'
|
||||
|
||||
header.append_url_extension:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.APPEND_URL_EXT
|
||||
toggleable: true
|
||||
help: PLUGIN_ADMIN.APPEND_URL_EXT_HELP
|
||||
|
||||
header.order_by:
|
||||
type: hidden
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ form:
|
||||
|
||||
content:
|
||||
type: markdown
|
||||
label: PLUGIN_ADMIN.CONTENT
|
||||
showPreview: true
|
||||
|
||||
uploads:
|
||||
type: pagemedia
|
||||
|
||||
@@ -25,7 +25,7 @@ form:
|
||||
|
||||
content:
|
||||
type: markdown
|
||||
label: PLUGIN_ADMIN.CONTENT
|
||||
showPreview: true
|
||||
|
||||
uploads:
|
||||
type: pagemedia
|
||||
|
||||
@@ -29,7 +29,7 @@ form:
|
||||
size: large
|
||||
label: PLUGIN_ADMIN.PASSWORD
|
||||
validate:
|
||||
required: true
|
||||
required: false
|
||||
message: PLUGIN_ADMIN.PASSWORD_VALIDATION_MESSAGE
|
||||
pattern: '(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}'
|
||||
|
||||
@@ -54,3 +54,32 @@ form:
|
||||
default: 'en'
|
||||
help: PLUGIN_ADMIN.LANGUAGE_HELP
|
||||
|
||||
security:
|
||||
title: Security
|
||||
type: section
|
||||
security: admin.super
|
||||
|
||||
fields:
|
||||
groups:
|
||||
type: selectize
|
||||
size: large
|
||||
label: PLUGIN_ADMIN.GROUPS
|
||||
'@data-options': '\Grav\User\Groups::groups'
|
||||
classes: fancy
|
||||
help: PLUGIN_ADMIN.GROUPS_HELP
|
||||
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
|
||||
validate:
|
||||
type: array
|
||||
44
system/blueprints/user/group.yaml
Normal file
44
system/blueprints/user/group.yaml
Normal file
@@ -0,0 +1,44 @@
|
||||
title: Group
|
||||
form:
|
||||
validation: loose
|
||||
|
||||
fields:
|
||||
spacer:
|
||||
type: spacer
|
||||
text: '<br>'
|
||||
|
||||
groupname:
|
||||
type: text
|
||||
size: large
|
||||
label: PLUGIN_ADMIN.NAME
|
||||
disabled: true
|
||||
readonly: true
|
||||
|
||||
readableName:
|
||||
type: text
|
||||
size: large
|
||||
label: PLUGIN_ADMIN_PRO.READABLE_NAME
|
||||
|
||||
description:
|
||||
type: text
|
||||
size: large
|
||||
label: PLUGIN_ADMIN.DESCRIPTION
|
||||
|
||||
icon:
|
||||
type: text
|
||||
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
|
||||
validate:
|
||||
type: array
|
||||
16
system/blueprints/user/group_new.yaml
Normal file
16
system/blueprints/user/group_new.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
title: PLUGIN_ADMIN_PRO.ADD_GROUP
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
content:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN_PRO.ADD_GROUP
|
||||
|
||||
groupname:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN_PRO.GROUP_NAME
|
||||
help: PLUGIN_ADMIN_PRO.GROUP_NAME_HELP
|
||||
validate:
|
||||
required: true
|
||||
@@ -1,9 +1,4 @@
|
||||
schemes:
|
||||
asset:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- assets
|
||||
|
||||
image:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
|
||||
@@ -1,111 +1,114 @@
|
||||
absolute_urls: false # Absolute or relative URLs for `base_url`
|
||||
timezone: '' # Valid values: http://php.net/manual/en/timezones.php
|
||||
default_locale: # Default locale (defaults to system)
|
||||
param_sep: ':' # Parameter separator, use ';' for Apache on windows
|
||||
wrapped_site: false # For themes/plugins to know if Grav is wrapped by another platform
|
||||
absolute_urls: false # Absolute or relative URLs for `base_url`
|
||||
timezone: '' # Valid values: http://php.net/manual/en/timezones.php
|
||||
default_locale: # Default locale (defaults to system)
|
||||
param_sep: ':' # Parameter separator, use ';' for Apache on windows
|
||||
wrapped_site: false # For themes/plugins to know if Grav is wrapped by another platform
|
||||
|
||||
languages:
|
||||
supported: [] # List of languages supported. eg: [en, fr, de]
|
||||
include_default_lang: true # Include the default lang prefix in all URLs
|
||||
translations: true # Enable translations by default
|
||||
translations_fallback: true # Fallback through supported translations if active lang doesn't exist
|
||||
session_store_active: false # Store active language in session
|
||||
http_accept_language: false # Attempt to set the language based on http_accept_language header in the browser
|
||||
override_locale: false # Override the default or system locale with language specific one
|
||||
supported: [] # List of languages supported. eg: [en, fr, de]
|
||||
include_default_lang: true # Include the default lang prefix in all URLs
|
||||
translations: true # Enable translations by default
|
||||
translations_fallback: true # Fallback through supported translations if active lang doesn't exist
|
||||
session_store_active: false # Store active language in session
|
||||
http_accept_language: false # Attempt to set the language based on http_accept_language header in the browser
|
||||
override_locale: false # Override the default or system locale with language specific one
|
||||
|
||||
home:
|
||||
alias: '/home' # Default path for home, ie /
|
||||
alias: '/home' # Default path for home, ie /
|
||||
|
||||
pages:
|
||||
theme: antimatter # Default theme (defaults to "antimatter" theme)
|
||||
theme: antimatter # Default theme (defaults to "antimatter" theme)
|
||||
order:
|
||||
by: default # Order pages by "default", "alpha" or "date"
|
||||
dir: asc # Default ordering direction, "asc" or "desc"
|
||||
by: default # Order pages by "default", "alpha" or "date"
|
||||
dir: asc # Default ordering direction, "asc" or "desc"
|
||||
list:
|
||||
count: 20 # Default item count per page
|
||||
count: 20 # Default item count per page
|
||||
dateformat:
|
||||
default: # The default date format Grav expects in the `date: ` field
|
||||
short: 'jS M Y' # Short date format
|
||||
long: 'F jS \a\t g:ia' # Long date format
|
||||
publish_dates: true # automatically publish/unpublish based on dates
|
||||
default: # The default date format Grav expects in the `date: ` field
|
||||
short: 'jS M Y' # Short date format
|
||||
long: 'F jS \a\t g:ia' # Long date format
|
||||
publish_dates: true # automatically publish/unpublish based on dates
|
||||
process:
|
||||
markdown: true # Process Markdown
|
||||
twig: false # Process Twig
|
||||
markdown: true # Process Markdown
|
||||
twig: false # Process Twig
|
||||
events:
|
||||
page: true # Enable page level events
|
||||
twig: true # Enable twig level events
|
||||
page: true # Enable page level events
|
||||
twig: true # Enable twig level events
|
||||
markdown:
|
||||
extra: false # Enable support for Markdown Extra support (GFM by default)
|
||||
auto_line_breaks: false # Enable automatic line breaks
|
||||
auto_url_links: false # Enable automatic HTML links
|
||||
escape_markup: false # Escape markup tags into entities
|
||||
special_chars: # List of special characters to automatically convert to entities
|
||||
extra: false # Enable support for Markdown Extra support (GFM by default)
|
||||
auto_line_breaks: false # Enable automatic line breaks
|
||||
auto_url_links: false # Enable automatic HTML links
|
||||
escape_markup: false # Escape markup tags into entities
|
||||
special_chars: # List of special characters to automatically convert to entities
|
||||
'>': 'gt'
|
||||
'<': 'lt'
|
||||
types: [txt,xml,html,json,rss,atom] # list of valid page types
|
||||
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
|
||||
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
|
||||
redirect_default_code: 301 # Default code to use for redirects
|
||||
redirect_trailing_slash: true # Handle automatically or 301 redirect a trailing / URL
|
||||
ignore_files: [.DS_Store] # Files to ignore in Pages
|
||||
ignore_folders: [.git, .idea] # Folders to ignore in Pages
|
||||
ignore_hidden: true # Ignore all Hidden files and folders
|
||||
url_taxonomy_filters: true # Enable auto-magic URL-based taxonomy filters for page collections
|
||||
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
|
||||
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
|
||||
redirect_default_code: 301 # Default code to use for redirects
|
||||
redirect_trailing_slash: true # Handle automatically or 301 redirect a trailing / URL
|
||||
ignore_files: [.DS_Store] # Files to ignore in Pages
|
||||
ignore_folders: [.git, .idea] # Folders to ignore in Pages
|
||||
ignore_hidden: true # Ignore all Hidden files and folders
|
||||
url_taxonomy_filters: true # Enable auto-magic URL-based taxonomy filters for page collections
|
||||
|
||||
cache:
|
||||
enabled: true # Set to true to enable caching
|
||||
enabled: true # Set to true to enable caching
|
||||
check:
|
||||
method: file # Method to check for updates in pages: file|folder|none
|
||||
driver: auto # One of: auto|file|apc|xcache|memcache|wincache
|
||||
prefix: 'g' # Cache prefix string (prevents cache conflicts)
|
||||
lifetime: 604800 # Lifetime of cached data in seconds (0 = infinite)
|
||||
gzip: false # GZip compress the page output
|
||||
method: file # Method to check for updates in pages: file|folder|none
|
||||
driver: auto # One of: auto|file|apc|xcache|memcache|wincache
|
||||
prefix: 'g' # Cache prefix string (prevents cache conflicts)
|
||||
lifetime: 604800 # Lifetime of cached data in seconds (0 = infinite)
|
||||
gzip: false # GZip compress the page output
|
||||
|
||||
twig:
|
||||
cache: true # Set to true to enable twig caching
|
||||
debug: false # Enable Twig debug
|
||||
auto_reload: true # Refresh cache on changes
|
||||
autoescape: false # Autoescape Twig vars
|
||||
undefined_functions: true # Allow undefined functions
|
||||
undefined_filters: true # Allow undefined filters
|
||||
cache: true # Set to true to enable twig caching
|
||||
debug: false # Enable Twig debug
|
||||
auto_reload: true # Refresh cache on changes
|
||||
autoescape: false # Autoescape Twig vars
|
||||
undefined_functions: true # Allow undefined functions
|
||||
undefined_filters: true # Allow undefined filters
|
||||
umask_fix: false # By default Twig creates cached files as 755, fix switches this to 775
|
||||
|
||||
assets: # Configuration for Assets Manager (JS, CSS)
|
||||
css_pipeline: false # The CSS pipeline is the unification of multiple CSS resources into one file
|
||||
css_minify: true # Minify the CSS during pipelining
|
||||
css_minify_windows: false # Minify Override for Windows platforms. False by default due to ThreadStackSize
|
||||
css_rewrite: true # Rewrite any CSS relative URLs during pipelining
|
||||
js_pipeline: false # The JS pipeline is the unification of multiple JS resources into one file
|
||||
js_minify: true # Minify the JS during pipelining
|
||||
enable_asset_timestamp: false # Enable asset timestamps
|
||||
assets: # Configuration for Assets Manager (JS, CSS)
|
||||
css_pipeline: false # The CSS pipeline is the unification of multiple CSS resources into one file
|
||||
css_minify: true # Minify the CSS during pipelining
|
||||
css_minify_windows: false # Minify Override for Windows platforms. False by default due to ThreadStackSize
|
||||
css_rewrite: true # Rewrite any CSS relative URLs during pipelining
|
||||
js_pipeline: false # The JS pipeline is the unification of multiple JS resources into one file
|
||||
js_minify: true # Minify the JS during pipelining
|
||||
enable_asset_timestamp: false # Enable asset timestamps
|
||||
collections:
|
||||
jquery: system://assets/jquery/jquery-2.1.4.min.js
|
||||
|
||||
errors:
|
||||
display: false # Display full backtrace-style error page
|
||||
log: true # Log errors to /logs folder
|
||||
display: false # Display full backtrace-style error page
|
||||
log: true # Log errors to /logs folder
|
||||
|
||||
debugger:
|
||||
enabled: false # Enable Grav debugger and following settings
|
||||
enabled: false # Enable Grav debugger and following settings
|
||||
shutdown:
|
||||
close_connection: true # Close the connection before calling onShutdown(). false for debugging
|
||||
close_connection: true # Close the connection before calling onShutdown(). false for debugging
|
||||
|
||||
images:
|
||||
default_image_quality: 85 # Default image quality to use when resampling images (85%)
|
||||
cache_all: false # Cache all image by default
|
||||
debug: false # Show an overlay over images indicating the pixel depth of the image when working with retina for example
|
||||
default_image_quality: 85 # Default image quality to use when resampling images (85%)
|
||||
cache_all: false # Cache all image by default
|
||||
cache_perms: 0755 # Default cache folder perms. Usually 0755 or 0775 depending on setup
|
||||
debug: false # Show an overlay over images indicating the pixel depth of the image when working with retina for example
|
||||
|
||||
media:
|
||||
enable_media_timestamp: false # Enable media timetsamps
|
||||
upload_limit: 0 # Set maximum upload size in bytes (0 is unlimited)
|
||||
unsupported_inline_types: [] # Array of unsupported media file types to try to display inline
|
||||
enable_media_timestamp: false # Enable media timetsamps
|
||||
upload_limit: 0 # Set maximum upload size in bytes (0 is unlimited)
|
||||
unsupported_inline_types: [] # Array of supported media types to try to display inline
|
||||
allowed_fallback_types: [] # Array of allowed media types of files found if accessed via Page route
|
||||
|
||||
session:
|
||||
enabled: true # Enable Session support
|
||||
timeout: 1800 # Timeout in seconds
|
||||
name: grav-site # Name prefix of the session cookie
|
||||
enabled: true # Enable Session support
|
||||
timeout: 1800 # Timeout in seconds
|
||||
name: grav-site # Name prefix of the session cookie. Use alphanumeric, dashes or underscores only. Do not use dots in the session name
|
||||
|
||||
|
||||
security:
|
||||
default_hash: $2y$10$kwsyMVwM8/7j0K/6LHT.g.Fs49xOCTp2b8hh/S5.dPJuJcJB6T.UK
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
// Some standard defines
|
||||
define('GRAV', true);
|
||||
define('GRAV_VERSION', '1.0.0-rc.2');
|
||||
define('GRAV_VERSION', '1.0.3');
|
||||
define('DS', '/');
|
||||
|
||||
// Directories and Paths
|
||||
@@ -13,14 +13,14 @@ define('ROOT_DIR', GRAV_ROOT . '/');
|
||||
define('USER_PATH', 'user/');
|
||||
define('USER_DIR', ROOT_DIR . USER_PATH);
|
||||
define('SYSTEM_DIR', ROOT_DIR .'system/');
|
||||
define('ASSETS_DIR', ROOT_DIR . 'assets/');
|
||||
define('CACHE_DIR', ROOT_DIR . 'cache/');
|
||||
define('IMAGES_DIR', ROOT_DIR . 'images/');
|
||||
define('LOG_DIR', ROOT_DIR .'logs/');
|
||||
define('ACCOUNTS_DIR', USER_DIR .'accounts/');
|
||||
define('PAGES_DIR', USER_DIR .'pages/');
|
||||
|
||||
// DEPRECATED: Do not use!
|
||||
define('ASSETS_DIR', ROOT_DIR . 'assets/');
|
||||
define('IMAGES_DIR', ROOT_DIR . 'images/');
|
||||
define('ACCOUNTS_DIR', USER_DIR .'accounts/');
|
||||
define('PAGES_DIR', USER_DIR .'pages/');
|
||||
define('DATA_DIR', USER_DIR .'data/');
|
||||
define('LIB_DIR', SYSTEM_DIR .'src/');
|
||||
define('PLUGINS_DIR', USER_DIR .'plugins/');
|
||||
|
||||
@@ -92,3 +92,7 @@ NICETIME:
|
||||
MO_PLURAL: mos
|
||||
YR_PLURAL: yrs
|
||||
DEC_PLURAL: decs
|
||||
FORM:
|
||||
VALIDATION_FAIL: <b>Validation failed:</b>
|
||||
INVALID_INPUT: Invalid input in
|
||||
MISSING_REQUIRED_FIELD: Missing required field:
|
||||
|
||||
42
system/languages/es.yaml
Normal file
42
system/languages/es.yaml
Normal file
@@ -0,0 +1,42 @@
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: No se proporcionó fecha
|
||||
BAD_DATE: Fecha erronea
|
||||
AGO: antes
|
||||
FROM_NOW: desde ahora
|
||||
SECOND: segundo
|
||||
MINUTE: minuto
|
||||
HOUR: hora
|
||||
DAY: dia
|
||||
WEEK: semana
|
||||
MONTH: mes
|
||||
YEAR: año
|
||||
DECADE: decada
|
||||
SEC: seg
|
||||
MIN: min
|
||||
HR: hr
|
||||
DAY: dia
|
||||
WK: sem
|
||||
MO: mes
|
||||
YR: yr
|
||||
DEC: dec
|
||||
SECOND_PLURAL: segundos
|
||||
MINUTE_PLURAL: minutos
|
||||
HOUR_PLURAL: horas
|
||||
DAY_PLURAL: días
|
||||
WEEK_PLURAL: semanas
|
||||
MONTH_PLURAL: meses
|
||||
YEAR_PLURAL: años
|
||||
DECADE_PLURAL: decadas
|
||||
SEC_PLURAL: segs
|
||||
MIN_PLURAL: mins
|
||||
HR_PLURAL: hrs
|
||||
DAY_PLURAL: dias
|
||||
WK_PLURAL: sem
|
||||
MO_PLURAL: mes
|
||||
YR_PLURAL: años
|
||||
DEC_PLURAL: decs
|
||||
FORM:
|
||||
VALIDATION_FAIL: <b>Falló la validación. </b>
|
||||
INVALID_INPUT: "Dato inválido en: "
|
||||
MISSING_REQUIRED_FIELD: "Falta el campo requerido: "
|
||||
|
||||
@@ -1,26 +1,60 @@
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Erreur : Frontmatter invalide\n\nPath: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_PLURALS:
|
||||
'/$/': 's'
|
||||
'/(bijou|caillou|chou|genou|hibou|joujou|pou|au|eu|eau)$/': '\1x'
|
||||
'/(bleu|émeu|landau|lieu|pneu|sarrau)$/': '\1s'
|
||||
'/(b|cor|ém|gemm|soupir|trav|vant|vitr)ail$/': '\1aux'
|
||||
'/(s|x|z)$/': '\1'
|
||||
'/ail$/': 'ails'
|
||||
'/al$/': 'aux'
|
||||
'/(quiz)$/i': '\1zes'
|
||||
'/^(ox)$/i': '\1en'
|
||||
'/([m|l])ouse$/i': '\1ice'
|
||||
'/(matr|vert|ind)ix|ex$/i': '\1ices'
|
||||
'/(x|ch|ss|sh)$/i': '\1es'
|
||||
'/([^aeiouy]|qu)ies$/i': '\1y'
|
||||
'/([^aeiouy]|qu)y$/i': '\1ies'
|
||||
'/(hive)$/i': '\1s'
|
||||
'/(?:([^f])fe|([lr])f)$/i': '\1\2ves'
|
||||
'/sis$/i': 'ses'
|
||||
'/([ti])um$/i': '\1a'
|
||||
'/(buffal|tomat)o$/i': '\1oes'
|
||||
'/(bu)s$/i': '\1ses'
|
||||
'/(alias|status)/i': '\1es'
|
||||
'/(octop|vir)us$/i': '\1i'
|
||||
'/(ax|test)is$/i': '\1es'
|
||||
'/s$/i': 's'
|
||||
'/$/': 's'
|
||||
INFLECTOR_SINGULAR:
|
||||
'/(bijou|caillou|chou|genou|hibou|joujou|pou|au|eu|eau)x$/': '\1'
|
||||
'/(b|cor|ém|gemm|soupir|trav|vant|vitr)aux$/': '\1ail'
|
||||
'/(journ|chev)aux$/': '\1al'
|
||||
'/ails$/': 'ail'
|
||||
'/(quiz)zes$/i': '\1'
|
||||
'/(matr)ices$/i': '\1ix'
|
||||
'/(vert|ind)ices$/i': '\1ex'
|
||||
'/^(ox)en/i': '\1'
|
||||
'/(alias|status)es$/i': '\1'
|
||||
'/([octop|vir])i$/i': '\1us'
|
||||
'/(cris|ax|test)es$/i': '\1is'
|
||||
'/(shoe)s$/i': '\1'
|
||||
'/(o)es$/i': '\1'
|
||||
'/(bus)es$/i': '\1'
|
||||
'/([m|l])ice$/i': '\1ouse'
|
||||
'/(x|ch|ss|sh)es$/i': '\1'
|
||||
'/(m)ovies$/i': '\1ovie'
|
||||
'/(s)eries$/i': '\1eries'
|
||||
'/([^aeiouy]|qu)ies$/i': '\1y'
|
||||
'/([lr])ves$/i': '\1f'
|
||||
'/(tive)s$/i': '\1'
|
||||
'/(hive)s$/i': '\1'
|
||||
'/([^f])ves$/i': '\1fe'
|
||||
'/(^analy)ses$/i': '\1sis'
|
||||
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2sis'
|
||||
'/([ti])a$/i': '\1um'
|
||||
'/(n)ews$/i': '\1ews'
|
||||
'/s$/i': ''
|
||||
INFLECTOR_UNCOUNTABLE: ['équipment', 'information', 'riz', 'argent', 'espèces', 'séries', 'poisson', 'mouton']
|
||||
INFLECTOR_IRREGULAR:
|
||||
'madame': 'mesdames'
|
||||
'mademoiselle': 'mesdemoiselles'
|
||||
'monsieur': 'messieurs'
|
||||
'person': 'personnes'
|
||||
'man': 'Hommes'
|
||||
'child': 'enfants'
|
||||
'sex': 'sexes'
|
||||
'move': 'déplacemements'
|
||||
INFLECTOR_ORDINALS:
|
||||
'default': 'ème'
|
||||
'first': 'er'
|
||||
'second': 'nd'
|
||||
'third': 'ème'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Aucune date
|
||||
BAD_DATE: Date erronée
|
||||
@@ -48,7 +82,7 @@ NICETIME:
|
||||
DAY_PLURAL: jours
|
||||
WEEK_PLURAL: semaines
|
||||
MONTH_PLURAL: mois
|
||||
YEAR_PLURAL: ans
|
||||
YEAR_PLURAL: années
|
||||
DECADE_PLURAL: décennies
|
||||
SEC_PLURAL: s
|
||||
MIN_PLURAL: m
|
||||
@@ -58,3 +92,7 @@ NICETIME:
|
||||
MO_PLURAL: m
|
||||
YR_PLURAL: a
|
||||
DEC_PLURAL: d
|
||||
FORM:
|
||||
VALIDATION_FAIL: <b>La validation a échoué :</b>
|
||||
INVALID_INPUT: Saisie non valide
|
||||
MISSING_REQUIRED_FIELD: Champ obligatoire manquant :
|
||||
|
||||
52
system/languages/hr.yaml
Normal file
52
system/languages/hr.yaml
Normal file
@@ -0,0 +1,52 @@
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'Osoba'
|
||||
'man': 'Čovjek'
|
||||
'child': 'Dijete'
|
||||
'sex': 'Spol'
|
||||
'move': 'Pomakni'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Datum nije upisan
|
||||
BAD_DATE: Pogrešan datum
|
||||
AGO: prije
|
||||
FROM_NOW: od sad
|
||||
SECOND: sekundi
|
||||
MINUTE: minuta
|
||||
HOUR: godina
|
||||
DAY: dan
|
||||
WEEK: tjedan
|
||||
MONTH: mjesec
|
||||
YEAR: godina
|
||||
DECADE: desetljeće
|
||||
SEC: sek
|
||||
MIN: min
|
||||
HR: sat
|
||||
DAY: dan
|
||||
WK: t
|
||||
MO: m
|
||||
YR: g
|
||||
DEC: des
|
||||
SECOND_PLURAL: sekundi
|
||||
SECOND_PLURAL_MORE_THAN_TWO: sekunde
|
||||
MINUTE_PLURAL: minuta
|
||||
MINUTE_PLURAL_MORE_THAN_TWO: minute
|
||||
HOUR_PLURAL: sati
|
||||
HOUR_PLURAL_MORE_THAN_TWO: sata
|
||||
DAY_PLURAL: dana
|
||||
WEEK_PLURAL: tjedana
|
||||
WEEK_PLURAL_MORE_THAN_TWO: tjedna
|
||||
MONTH_PLURAL: mjeseci
|
||||
MONTH_PLURAL_MORE_THAN_TWO: mjeseca
|
||||
YEAR_PLURAL: godina
|
||||
YEAR_PLURAL_MORE_THAN_TWO: godine
|
||||
DECADE_PLURAL: desetljeća
|
||||
SEC_PLURAL: sek
|
||||
MIN_PLURAL: min
|
||||
HR_PLURAL: sat
|
||||
DAY_PLURAL: dan
|
||||
WK_PLURAL: t
|
||||
MO_PLURAL: m
|
||||
YR_PLURAL: g
|
||||
DEC_PLURAL: des
|
||||
FORM:
|
||||
VALIDATION_FAIL: <b>Validacija nije uspjela:</b>
|
||||
INVALID_INPUT: Unos nije valjan
|
||||
53
system/languages/hu.yaml
Normal file
53
system/languages/hu.yaml
Normal file
@@ -0,0 +1,53 @@
|
||||
FRONTMATTER_ERROR_PAGE: "---\ncím: %1$s\n---\n\n# Hiba: Érvénytelen Frontmatter\n\nElérési út: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'személyek'
|
||||
'man': 'férfiak'
|
||||
'child': 'gyerekek'
|
||||
'sex': 'nemek'
|
||||
'move': 'lépések'
|
||||
INFLECTOR_ORDINALS:
|
||||
'default': '.'
|
||||
'first': '.'
|
||||
'second': '.'
|
||||
'third': '.'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Nincs dátum megadva
|
||||
BAD_DATE: Hibás dátum
|
||||
AGO: elteltével
|
||||
FROM_NOW: mostantól
|
||||
SECOND: másodperc
|
||||
MINUTE: perc
|
||||
HOUR: óra
|
||||
DAY: nap
|
||||
WEEK: hét
|
||||
MONTH: hónap
|
||||
YEAR: év
|
||||
DECADE: évtized
|
||||
SEC: mp
|
||||
MIN: p
|
||||
HR: ó
|
||||
DAY: nap
|
||||
WK: hét
|
||||
MO: hó
|
||||
YR: év
|
||||
DEC: évt
|
||||
SECOND_PLURAL: másodperc
|
||||
MINUTE_PLURAL: perc
|
||||
HOUR_PLURAL: óra
|
||||
DAY_PLURAL: nap
|
||||
WEEK_PLURAL: hét
|
||||
MONTH_PLURAL: hónap
|
||||
YEAR_PLURAL: év
|
||||
DECADE_PLURAL: évtized
|
||||
SEC_PLURAL: mp
|
||||
MIN_PLURAL: perc
|
||||
HR_PLURAL: ó
|
||||
DAY_PLURAL: nap
|
||||
WK_PLURAL: hét
|
||||
MO_PLURAL: hó
|
||||
YR_PLURAL: év
|
||||
DEC_PLURAL: évt
|
||||
FORM:
|
||||
VALIDATION_FAIL: <b>A validáció hibát talált:</b>
|
||||
INVALID_INPUT: Az itt megadott érték érvénytelen:
|
||||
MISSING_REQUIRED_FIELD: Ez a kötelező mező nincs kitöltve:
|
||||
@@ -19,3 +19,7 @@ NICETIME:
|
||||
MONTH_PLURAL: mesi
|
||||
YEAR_PLURAL: anni
|
||||
DECADE_PLURAL: decadi
|
||||
FORM:
|
||||
VALIDATION_FAIL: <b>Validazione fallita:</b>
|
||||
INVALID_INPUT: Input invalido in
|
||||
MISSING_REQUIRED_FIELD: Campo richiesto mancante:
|
||||
|
||||
37
system/languages/tr.yaml
Normal file
37
system/languages/tr.yaml
Normal file
@@ -0,0 +1,37 @@
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Tarih yok
|
||||
BAD_DATE: Yanlış tarih
|
||||
AGO: önce
|
||||
FROM_NOW: (şimdiden)
|
||||
SECOND: saniye
|
||||
MINUTE: dakika
|
||||
HOUR: saat
|
||||
DAY: gün
|
||||
WEEK: hafta
|
||||
MONTH: ay
|
||||
YEAR: yıl
|
||||
DECADE: onyıl
|
||||
SEC: sn
|
||||
MIN: dk
|
||||
HR: sa
|
||||
DAY: gün
|
||||
WK: hft
|
||||
MO: ay
|
||||
YR: yl
|
||||
DEC: onyl
|
||||
SECOND_PLURAL: saniye
|
||||
MINUTE_PLURAL: dakika
|
||||
HOUR_PLURAL: saat
|
||||
DAY_PLURAL: gün
|
||||
WEEK_PLURAL: hafta
|
||||
MONTH_PLURAL: ay
|
||||
YEAR_PLURAL: yıl
|
||||
DECADE_PLURAL: onyıl
|
||||
SEC_PLURAL: sn
|
||||
MIN_PLURAL: dk
|
||||
HR_PLURAL: sa
|
||||
DAY_PLURAL: gün
|
||||
WK_PLURAL: hft
|
||||
MO_PLURAL: ay
|
||||
YR_PLURAL: yl
|
||||
DEC_PLURAL: onyl
|
||||
@@ -74,6 +74,8 @@ class Assets
|
||||
protected $config;
|
||||
protected $base_url;
|
||||
protected $timestamp = '';
|
||||
protected $assets_dir;
|
||||
protected $assets_url;
|
||||
|
||||
// Default values for pipeline settings
|
||||
protected $css_minify = true;
|
||||
@@ -117,7 +119,7 @@ class Assets
|
||||
}
|
||||
|
||||
// Pipeline requires public dir
|
||||
if (($this->js_pipeline || $this->css_pipeline) && !is_dir(ASSETS_DIR)) {
|
||||
if (($this->js_pipeline || $this->css_pipeline) && !is_dir($this->assets_dir)) {
|
||||
throw new \Exception('Assets: Public dir not found');
|
||||
}
|
||||
|
||||
@@ -175,6 +177,11 @@ class Assets
|
||||
$base_url = self::getGrav()['base_url'];
|
||||
$asset_config = (array)$config->get('system.assets');
|
||||
|
||||
/** @var Locator $locator */
|
||||
$locator = self::$grav['locator'];
|
||||
$this->assets_dir = self::getGrav()['locator']->findResource('asset://') . DS;
|
||||
$this->assets_url = self::getGrav()['locator']->findResource('asset://', false);
|
||||
|
||||
$this->config($asset_config);
|
||||
$this->base_url = $base_url . '/';
|
||||
|
||||
@@ -196,7 +203,7 @@ class Assets
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function add($asset, $priority = null, $pipeline = null)
|
||||
public function add($asset, $priority = null, $pipeline = true)
|
||||
{
|
||||
// More than one asset
|
||||
if (is_array($asset)) {
|
||||
@@ -236,7 +243,7 @@ class Assets
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addCss($asset, $priority = null, $pipeline = null, $group = null)
|
||||
public function addCss($asset, $priority = null, $pipeline = true, $group = null)
|
||||
{
|
||||
if (is_array($asset)) {
|
||||
foreach ($asset as $a) {
|
||||
@@ -252,16 +259,20 @@ class Assets
|
||||
$asset = $this->buildLocalLink($asset);
|
||||
}
|
||||
|
||||
// Check for existence
|
||||
if ($asset === false) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'asset' => $asset,
|
||||
'priority' => intval($priority ?: 10),
|
||||
'order' => count($this->css),
|
||||
'pipeline' => $pipeline ?: true,
|
||||
'pipeline' => (bool) $pipeline,
|
||||
'group' => $group ?: 'head'
|
||||
];
|
||||
|
||||
// check for dynamic array and merge with defaults
|
||||
$count_args = func_num_args();
|
||||
if (func_num_args() == 2) {
|
||||
$dynamic_arg = func_get_arg(1);
|
||||
if (is_array($dynamic_arg)) {
|
||||
@@ -290,7 +301,7 @@ class Assets
|
||||
* @param string $group name of the group
|
||||
* @return $this
|
||||
*/
|
||||
public function addJs($asset, $priority = null, $pipeline = null, $loading = null, $group = null)
|
||||
public function addJs($asset, $priority = null, $pipeline = true, $loading = null, $group = null)
|
||||
{
|
||||
if (is_array($asset)) {
|
||||
foreach ($asset as $a) {
|
||||
@@ -306,17 +317,21 @@ class Assets
|
||||
$asset = $this->buildLocalLink($asset);
|
||||
}
|
||||
|
||||
// Check for existence
|
||||
if ($asset === false) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'asset' => $asset,
|
||||
'priority' => intval($priority ?: 10),
|
||||
'order' => count($this->js),
|
||||
'pipeline' => $pipeline ?: true,
|
||||
'pipeline' => (bool) $pipeline,
|
||||
'loading' => $loading ?: '',
|
||||
'group' => $group ?: 'head'
|
||||
];
|
||||
|
||||
// check for dynamic array and merge with defaults
|
||||
$count_args = func_num_args();
|
||||
if (func_num_args() == 2) {
|
||||
$dynamic_arg = func_get_arg(1);
|
||||
if (is_array($dynamic_arg)) {
|
||||
@@ -344,7 +359,7 @@ class Assets
|
||||
*
|
||||
* @return \Grav\Common\Assets
|
||||
*/
|
||||
public function addAsyncJs($asset, $priority = null, $pipeline = null, $group = null)
|
||||
public function addAsyncJs($asset, $priority = null, $pipeline = true, $group = null)
|
||||
{
|
||||
return $this->addJs($asset, $priority, $pipeline, 'async', $group);
|
||||
}
|
||||
@@ -361,7 +376,7 @@ class Assets
|
||||
*
|
||||
* @return \Grav\Common\Assets
|
||||
*/
|
||||
public function addDeferJs($asset, $priority = null, $pipeline = null, $group = null)
|
||||
public function addDeferJs($asset, $priority = null, $pipeline = true, $group = null)
|
||||
{
|
||||
return $this->addJs($asset, $priority, $pipeline, 'defer', $group);
|
||||
}
|
||||
@@ -406,7 +421,7 @@ class Assets
|
||||
}
|
||||
|
||||
$key = md5($asset);
|
||||
if (is_string($asset) && !array_key_exists($key, $this->inline_css)) {
|
||||
if ($asset && is_string($asset) && !array_key_exists($key, $this->inline_css)) {
|
||||
$this->inline_css[$key] = $data;
|
||||
}
|
||||
|
||||
@@ -453,7 +468,7 @@ class Assets
|
||||
}
|
||||
|
||||
$key = md5($asset);
|
||||
if (is_string($asset) && !array_key_exists($key, $this->inline_js)) {
|
||||
if ($asset && is_string($asset) && !array_key_exists($key, $this->inline_js)) {
|
||||
$this->inline_js[$key] = $data;
|
||||
}
|
||||
|
||||
@@ -621,8 +636,8 @@ class Assets
|
||||
|
||||
$file = md5(json_encode($this->css) . $this->css_minify . $this->css_rewrite . $group) . '.css';
|
||||
|
||||
$relative_path = "{$this->base_url}" . basename(ASSETS_DIR) . "/{$file}";
|
||||
$absolute_path = ASSETS_DIR . $file;
|
||||
$relative_path = "{$this->base_url}{$this->assets_url}/{$file}";
|
||||
$absolute_path = $this->assets_dir . $file;
|
||||
|
||||
// If pipeline exist return it
|
||||
if (file_exists($absolute_path)) {
|
||||
@@ -689,8 +704,8 @@ class Assets
|
||||
|
||||
$file = md5(json_encode($this->js) . $this->js_minify . $group) . '.js';
|
||||
|
||||
$relative_path = "{$this->base_url}" . basename(ASSETS_DIR) . "/{$file}";
|
||||
$absolute_path = ASSETS_DIR . $file;
|
||||
$relative_path = "{$this->base_url}{$this->assets_url}/{$file}";
|
||||
$absolute_path = $this->assets_dir . $file;
|
||||
|
||||
// If pipeline exist return it
|
||||
if (file_exists($absolute_path)) {
|
||||
@@ -852,12 +867,12 @@ class Assets
|
||||
public function addDir($directory, $pattern = self::DEFAULT_REGEX)
|
||||
{
|
||||
// Check if public_dir exists
|
||||
if (!is_dir(ASSETS_DIR)) {
|
||||
if (!is_dir($this->assets_dir)) {
|
||||
throw new Exception('Assets: Public dir not found');
|
||||
}
|
||||
|
||||
// Get files
|
||||
$files = $this->rglob(ASSETS_DIR . DIRECTORY_SEPARATOR . $directory, $pattern, ASSETS_DIR);
|
||||
$files = $this->rglob($this->assets_dir . DIRECTORY_SEPARATOR . $directory, $pattern, $this->assets_dir);
|
||||
|
||||
// No luck? Nothing to do
|
||||
if (!$files) {
|
||||
@@ -1117,6 +1132,36 @@ class Assets
|
||||
return $this->addDir($directory, self::JS_REGEX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state of CSS Pipeline
|
||||
*
|
||||
* @param boolean $value
|
||||
*/
|
||||
public function setCssPipeline($value)
|
||||
{
|
||||
$this->css_pipeline = (bool) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state of JS Pipeline
|
||||
*
|
||||
* @param boolean $value
|
||||
*/
|
||||
public function setJsPipeline($value)
|
||||
{
|
||||
$this->js_pipeline = (bool) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly set's a timestamp for assets
|
||||
*
|
||||
* @param $value
|
||||
*/
|
||||
public function setTimestamp($value)
|
||||
{
|
||||
$this->timestamp = '?'.$value;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return '';
|
||||
|
||||
@@ -21,6 +21,8 @@ use Grav\Common\Filesystem\Folder;
|
||||
*/
|
||||
class Cache extends Getters
|
||||
{
|
||||
use GravTrait;
|
||||
|
||||
/**
|
||||
* @var string Cache key.
|
||||
*/
|
||||
@@ -36,6 +38,8 @@ class Cache extends Getters
|
||||
*/
|
||||
protected $driver;
|
||||
|
||||
protected $driver_name;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
@@ -44,30 +48,30 @@ class Cache extends Getters
|
||||
protected $cache_dir;
|
||||
|
||||
protected static $standard_remove = [
|
||||
'cache/twig/',
|
||||
'cache/doctrine/',
|
||||
'cache/compiled/',
|
||||
'cache/validated-',
|
||||
'images/',
|
||||
'assets/',
|
||||
'cache://twig/',
|
||||
'cache://doctrine/',
|
||||
'cache://compiled/',
|
||||
'cache://validated-',
|
||||
'cache://images',
|
||||
'asset://',
|
||||
];
|
||||
|
||||
protected static $all_remove = [
|
||||
'cache/',
|
||||
'images/',
|
||||
'assets/'
|
||||
'cache://',
|
||||
'cache://images',
|
||||
'asset://'
|
||||
];
|
||||
|
||||
protected static $assets_remove = [
|
||||
'assets/'
|
||||
'asset://'
|
||||
];
|
||||
|
||||
protected static $images_remove = [
|
||||
'images/'
|
||||
'cache://images'
|
||||
];
|
||||
|
||||
protected static $cache_remove = [
|
||||
'cache/'
|
||||
'cache://'
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -108,6 +112,10 @@ class Cache extends Getters
|
||||
|
||||
// Set the cache namespace to our unique key
|
||||
$this->driver->setNamespace($this->key);
|
||||
|
||||
// Dump Cache state
|
||||
$grav['debugger']->addMessage('Cache: [' . ($this->enabled ? 'true' : 'false') . '] Driver: [' . $this->driver_name . ']');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,6 +142,8 @@ class Cache extends Getters
|
||||
$driver_name = $setting;
|
||||
}
|
||||
|
||||
$this->driver_name = $driver_name;
|
||||
|
||||
switch ($driver_name) {
|
||||
case 'apc':
|
||||
$driver = new \Doctrine\Common\Cache\ApcCache();
|
||||
@@ -221,7 +231,7 @@ class Cache extends Getters
|
||||
*/
|
||||
public static function clearCache($remove = 'standard')
|
||||
{
|
||||
|
||||
$locator = self::getGrav()['locator'];
|
||||
$output = [];
|
||||
$user_config = USER_DIR . 'config/system.yaml';
|
||||
|
||||
@@ -243,10 +253,16 @@ class Cache extends Getters
|
||||
}
|
||||
|
||||
|
||||
foreach ($remove_paths as $path) {
|
||||
foreach ($remove_paths as $stream) {
|
||||
|
||||
// Convert stream to a real path
|
||||
$path = $locator->findResource($stream, true, true);
|
||||
// Make sure path exists before proceeding, otherwise we would wipe ROOT_DIR
|
||||
if (!$path)
|
||||
throw new \RuntimeException("Stream '{$stream}' not found", 500);
|
||||
|
||||
$anything = false;
|
||||
$files = glob(ROOT_DIR . $path . '*');
|
||||
$files = glob($path . '/*');
|
||||
|
||||
if (is_array($files)) {
|
||||
foreach ($files as $file) {
|
||||
@@ -263,7 +279,7 @@ class Cache extends Getters
|
||||
}
|
||||
|
||||
if ($anything) {
|
||||
$output[] = '<red>Cleared: </red>' . $path . '*';
|
||||
$output[] = '<red>Cleared: </red>' . $path . '/*';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use RocketTheme\Toolbox\Blueprints\Blueprints as BaseBlueprints;
|
||||
use RocketTheme\Toolbox\File\PhpFile;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
/**
|
||||
* The Blueprints class contains configuration rules.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Blueprints extends BaseBlueprints
|
||||
{
|
||||
protected $grav;
|
||||
protected $files = [];
|
||||
protected $blueprints;
|
||||
|
||||
public function __construct(array $serialized = null, Grav $grav = null)
|
||||
{
|
||||
parent::__construct($serialized);
|
||||
$this->grav = $grav ?: Grav::instance();
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $this->grav['locator'];
|
||||
|
||||
$blueprints = $locator->findResources('blueprints://config');
|
||||
$plugins = $locator->findResources('plugins://');
|
||||
|
||||
$blueprintFiles = $this->getBlueprintFiles($blueprints, $plugins);
|
||||
|
||||
$this->loadCompiledBlueprints($plugins + $blueprints, $blueprintFiles);
|
||||
}
|
||||
|
||||
protected function loadCompiledBlueprints($blueprints, $blueprintFiles)
|
||||
{
|
||||
$checksum = md5(serialize($blueprints));
|
||||
$filename = CACHE_DIR . 'compiled/blueprints/' . $checksum .'.php';
|
||||
$checksum .= ':'.md5(serialize($blueprintFiles));
|
||||
$class = get_class($this);
|
||||
$file = PhpFile::instance($filename);
|
||||
|
||||
if ($file->exists()) {
|
||||
$cache = $file->exists() ? $file->content() : null;
|
||||
} else {
|
||||
$cache = null;
|
||||
}
|
||||
|
||||
|
||||
// Load real file if cache isn't up to date (or is invalid).
|
||||
if (
|
||||
!is_array($cache)
|
||||
|| empty($cache['checksum'])
|
||||
|| empty($cache['$class'])
|
||||
|| $cache['checksum'] != $checksum
|
||||
|| $cache['@class'] != $class
|
||||
) {
|
||||
// Attempt to lock the file for writing.
|
||||
$file->lock(false);
|
||||
|
||||
// Load blueprints.
|
||||
$this->blueprints = new Blueprints();
|
||||
foreach ($blueprintFiles as $key => $files) {
|
||||
$this->loadBlueprints($key);
|
||||
}
|
||||
|
||||
$cache = [
|
||||
'@class' => $class,
|
||||
'checksum' => $checksum,
|
||||
'files' => $blueprintFiles,
|
||||
'data' => $this->blueprints->toArray()
|
||||
];
|
||||
|
||||
// If compiled file wasn't already locked by another process, save it.
|
||||
if ($file->locked() !== false) {
|
||||
$file->save($cache);
|
||||
$file->unlock();
|
||||
}
|
||||
} else {
|
||||
$this->blueprints = new Blueprints($cache['data']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load global blueprints.
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $files
|
||||
*/
|
||||
public function loadBlueprints($key, array $files = null)
|
||||
{
|
||||
if (is_null($files)) {
|
||||
$files = $this->files[$key];
|
||||
}
|
||||
foreach ($files as $name => $item) {
|
||||
$file = CompiledYamlFile::instance($item['file']);
|
||||
$this->blueprints->embed($name, $file->content(), '/');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all blueprint files (including plugins).
|
||||
*
|
||||
* @param array $blueprints
|
||||
* @param array $plugins
|
||||
* @return array
|
||||
*/
|
||||
protected function getBlueprintFiles(array $blueprints, array $plugins)
|
||||
{
|
||||
$list = [];
|
||||
foreach (array_reverse($plugins) as $folder) {
|
||||
$list += $this->detectPlugins($folder, true);
|
||||
}
|
||||
foreach (array_reverse($blueprints) as $folder) {
|
||||
$list += $this->detectConfig($folder, true);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects all plugins with a configuration file and returns last modification time.
|
||||
*
|
||||
* @param string $lookup Location to look up from.
|
||||
* @param bool $blueprints
|
||||
* @return array
|
||||
* @internal
|
||||
*/
|
||||
protected function detectPlugins($lookup = SYSTEM_DIR, $blueprints = false)
|
||||
{
|
||||
$find = $blueprints ? 'blueprints.yaml' : '.yaml';
|
||||
$location = $blueprints ? 'blueprintFiles' : 'configFiles';
|
||||
$path = trim(Folder::getRelativePath($lookup), '/');
|
||||
if (isset($this->{$location}[$path])) {
|
||||
return [$path => $this->{$location}[$path]];
|
||||
}
|
||||
|
||||
$list = [];
|
||||
|
||||
if (is_dir($lookup)) {
|
||||
$iterator = new \DirectoryIterator($lookup);
|
||||
|
||||
/** @var \DirectoryIterator $directory */
|
||||
foreach ($iterator as $directory) {
|
||||
if (!$directory->isDir() || $directory->isDot()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $directory->getBasename();
|
||||
$filename = "{$path}/{$name}/" . ($find && $find[0] != '.' ? $find : $name . $find);
|
||||
|
||||
if (is_file($filename)) {
|
||||
$list["plugins/{$name}"] = ['file' => $filename, 'modified' => filemtime($filename)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->{$location}[$path] = $list;
|
||||
|
||||
return [$path => $list];
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects all plugins with a configuration file and returns last modification time.
|
||||
*
|
||||
* @param string $lookup Location to look up from.
|
||||
* @param bool $blueprints
|
||||
* @return array
|
||||
* @internal
|
||||
*/
|
||||
protected function detectConfig($lookup = SYSTEM_DIR, $blueprints = false)
|
||||
{
|
||||
$location = $blueprints ? 'blueprintFiles' : 'configFiles';
|
||||
$path = trim(Folder::getRelativePath($lookup), '/');
|
||||
if (isset($this->{$location}[$path])) {
|
||||
return [$path => $this->{$location}[$path]];
|
||||
}
|
||||
|
||||
if (is_dir($lookup)) {
|
||||
// Find all system and user configuration files.
|
||||
$options = [
|
||||
'compare' => 'Filename',
|
||||
'pattern' => '|\.yaml$|',
|
||||
'filters' => [
|
||||
'key' => '|\.yaml$|',
|
||||
'value' => function (\RecursiveDirectoryIterator $file) use ($path) {
|
||||
return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()];
|
||||
}],
|
||||
'key' => 'SubPathname'
|
||||
];
|
||||
|
||||
$list = Folder::all($lookup, $options);
|
||||
} else {
|
||||
$list = [];
|
||||
}
|
||||
|
||||
$this->{$location}[$path] = $list;
|
||||
|
||||
return [$path => $list];
|
||||
}
|
||||
}
|
||||
236
system/src/Grav/Common/Config/CompiledBase.php
Normal file
236
system/src/Grav/Common/Config/CompiledBase.php
Normal file
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use RocketTheme\Toolbox\File\PhpFile;
|
||||
|
||||
/**
|
||||
* The Compiled base class.
|
||||
*/
|
||||
abstract class CompiledBase
|
||||
{
|
||||
/**
|
||||
* @var int Version number for the compiled file.
|
||||
*/
|
||||
public $version = 1;
|
||||
|
||||
/**
|
||||
* @var string Filename (base name) of the compiled configuration.
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @var string|bool Configuration checksum.
|
||||
*/
|
||||
public $checksum;
|
||||
|
||||
/**
|
||||
* @var string Cache folder to be used.
|
||||
*/
|
||||
protected $cacheFolder;
|
||||
|
||||
/**
|
||||
* @var array List of files to load.
|
||||
*/
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var mixed Configuration object.
|
||||
*/
|
||||
protected $object;
|
||||
|
||||
/**
|
||||
* @param string $cacheFolder Cache folder to be used.
|
||||
* @param array $files List of files as returned from ConfigFileFinder class.
|
||||
* @param string $path Base path for the file list.
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function __construct($cacheFolder, array $files, $path)
|
||||
{
|
||||
if (!$cacheFolder) {
|
||||
throw new \BadMethodCallException('Cache folder not defined.');
|
||||
}
|
||||
|
||||
$this->cacheFolder = $cacheFolder;
|
||||
$this->files = $files;
|
||||
$this->path = $path ? rtrim($path, '\\/') . '/' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filename for the compiled PHP file.
|
||||
*
|
||||
* @param string $name
|
||||
* @return $this
|
||||
*/
|
||||
public function name($name = null)
|
||||
{
|
||||
if (!$this->name) {
|
||||
$this->name = $name ?: md5(json_encode(array_keys($this->files)));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function gets called when cached configuration is saved.
|
||||
*/
|
||||
public function modified() {}
|
||||
|
||||
/**
|
||||
* Load the configuration.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
if ($this->object) {
|
||||
return $this->object;
|
||||
}
|
||||
|
||||
$filename = $this->createFilename();
|
||||
if (!$this->loadCompiledFile($filename) && $this->loadFiles()) {
|
||||
$this->saveCompiledFile($filename);
|
||||
}
|
||||
|
||||
return $this->object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns checksum from the configuration files.
|
||||
*
|
||||
* You can set $this->checksum = false to disable this check.
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
public function checksum()
|
||||
{
|
||||
if (!isset($this->checksum)) {
|
||||
$this->checksum = md5(json_encode($this->files) . $this->version);
|
||||
}
|
||||
|
||||
return $this->checksum;
|
||||
}
|
||||
|
||||
protected function createFilename()
|
||||
{
|
||||
return "{$this->cacheFolder}/{$this->name()->name}.php";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create configuration object.
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
abstract protected function createObject(array $data = []);
|
||||
|
||||
/**
|
||||
* Finalize configuration object.
|
||||
*/
|
||||
abstract protected function finalizeObject();
|
||||
|
||||
/**
|
||||
* Load single configuration file and append it to the correct position.
|
||||
*
|
||||
* @param string $name Name of the position.
|
||||
* @param string $filename File to be loaded.
|
||||
*/
|
||||
abstract protected function loadFile($name, $filename);
|
||||
|
||||
/**
|
||||
* Load and join all configuration files.
|
||||
*
|
||||
* @return bool
|
||||
* @internal
|
||||
*/
|
||||
protected function loadFiles()
|
||||
{
|
||||
$this->createObject();
|
||||
|
||||
$list = array_reverse($this->files);
|
||||
foreach ($list as $files) {
|
||||
foreach ($files as $name => $item) {
|
||||
$this->loadFile($name, $this->path . $item['file']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->finalizeObject();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load compiled file.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return bool
|
||||
* @internal
|
||||
*/
|
||||
protected function loadCompiledFile($filename)
|
||||
{
|
||||
if (!file_exists($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cache = include $filename;
|
||||
if (
|
||||
!is_array($cache)
|
||||
|| !isset($cache['checksum'])
|
||||
|| !isset($cache['data'])
|
||||
|| !isset($cache['@class'])
|
||||
|| $cache['@class'] != get_class($this)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load real file if cache isn't up to date (or is invalid).
|
||||
if ($cache['checksum'] !== $this->checksum()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->createObject($cache['data']);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save compiled file.
|
||||
*
|
||||
* @param string $filename
|
||||
* @throws \RuntimeException
|
||||
* @internal
|
||||
*/
|
||||
protected function saveCompiledFile($filename)
|
||||
{
|
||||
$file = PhpFile::instance($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 ($file->locked() === false) {
|
||||
// File was already locked by another process.
|
||||
return;
|
||||
}
|
||||
|
||||
$cache = [
|
||||
'@class' => get_class($this),
|
||||
'timestamp' => time(),
|
||||
'checksum' => $this->checksum(),
|
||||
'files' => $this->files,
|
||||
'data' => $this->object->toArray()
|
||||
];
|
||||
|
||||
$file->save($cache);
|
||||
$file->unlock();
|
||||
$file->free();
|
||||
|
||||
$this->modified();
|
||||
}
|
||||
}
|
||||
49
system/src/Grav/Common/Config/CompiledBlueprints.php
Normal file
49
system/src/Grav/Common/Config/CompiledBlueprints.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use RocketTheme\Toolbox\Blueprints\Blueprints;
|
||||
|
||||
/**
|
||||
* The Compiled Blueprints class.
|
||||
*/
|
||||
class CompiledBlueprints extends CompiledBase
|
||||
{
|
||||
/**
|
||||
* @var int Version number for the compiled file.
|
||||
*/
|
||||
public $version = 1;
|
||||
|
||||
/**
|
||||
* @var Blueprints Blueprints object.
|
||||
*/
|
||||
protected $object;
|
||||
|
||||
/**
|
||||
* Create configuration object.
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
protected function createObject(array $data = [])
|
||||
{
|
||||
$this->object = new Blueprints($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize configuration object.
|
||||
*/
|
||||
protected function finalizeObject() {}
|
||||
|
||||
/**
|
||||
* Load single configuration file and append it to the correct position.
|
||||
*
|
||||
* @param string $name Name of the position.
|
||||
* @param string $filename File to be loaded.
|
||||
*/
|
||||
protected function loadFile($name, $filename)
|
||||
{
|
||||
$file = CompiledYamlFile::instance($filename);
|
||||
$this->object->embed($name, $file->content(), '/');
|
||||
$file->free();
|
||||
}
|
||||
}
|
||||
98
system/src/Grav/Common/Config/CompiledConfig.php
Normal file
98
system/src/Grav/Common/Config/CompiledConfig.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
|
||||
/**
|
||||
* The Compiled Configuration class.
|
||||
*/
|
||||
class CompiledConfig extends CompiledBase
|
||||
{
|
||||
/**
|
||||
* @var int Version number for the compiled file.
|
||||
*/
|
||||
public $version = 1;
|
||||
|
||||
/**
|
||||
* @var Config Configuration object.
|
||||
*/
|
||||
protected $object;
|
||||
|
||||
/**
|
||||
* @var callable Blueprints loader.
|
||||
*/
|
||||
protected $callable;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $withDefaults;
|
||||
|
||||
/**
|
||||
* Set blueprints for the configuration.
|
||||
*
|
||||
* @param callable $blueprints
|
||||
* @return $this
|
||||
*/
|
||||
public function setBlueprints(callable $blueprints)
|
||||
{
|
||||
$this->callable = $blueprints;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $withDefaults
|
||||
* @return mixed
|
||||
*/
|
||||
public function load($withDefaults = false)
|
||||
{
|
||||
$this->withDefaults = $withDefaults;
|
||||
|
||||
return parent::load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create configuration object.
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
protected function createObject(array $data = [])
|
||||
{
|
||||
if ($this->withDefaults && empty($data) && is_callable($this->callable)) {
|
||||
$blueprints = $this->callable;
|
||||
$data = $blueprints()->getDefaults();
|
||||
}
|
||||
|
||||
$this->object = new Config($data, $this->callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize configuration object.
|
||||
*/
|
||||
protected function finalizeObject()
|
||||
{
|
||||
$this->object->checksum($this->checksum());
|
||||
}
|
||||
|
||||
/**
|
||||
* Function gets called when cached configuration is saved.
|
||||
*/
|
||||
public function modified()
|
||||
{
|
||||
$this->object->modified(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load single configuration file and append it to the correct position.
|
||||
*
|
||||
* @param string $name Name of the position.
|
||||
* @param string $filename File to be loaded.
|
||||
*/
|
||||
protected function loadFile($name, $filename)
|
||||
{
|
||||
$file = CompiledYamlFile::instance($filename);
|
||||
$this->object->join($name, $file->content(), '/');
|
||||
$file->free();
|
||||
}
|
||||
}
|
||||
64
system/src/Grav/Common/Config/CompiledLanguages.php
Normal file
64
system/src/Grav/Common/Config/CompiledLanguages.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
|
||||
/**
|
||||
* The Compiled Languages class.
|
||||
*/
|
||||
class CompiledLanguages extends CompiledBase
|
||||
{
|
||||
/**
|
||||
* @var int Version number for the compiled file.
|
||||
*/
|
||||
public $version = 1;
|
||||
|
||||
/**
|
||||
* @var Languages Configuration object.
|
||||
*/
|
||||
protected $object;
|
||||
|
||||
/**
|
||||
* Create configuration object.
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
protected function createObject(array $data = [])
|
||||
{
|
||||
$this->object = new Languages($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize configuration object.
|
||||
*/
|
||||
protected function finalizeObject()
|
||||
{
|
||||
$this->object->checksum($this->checksum());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function gets called when cached configuration is saved.
|
||||
*/
|
||||
public function modified()
|
||||
{
|
||||
$this->object->modified(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load single configuration file and append it to the correct position.
|
||||
*
|
||||
* @param string $name Name of the position.
|
||||
* @param string $filename File to be loaded.
|
||||
*/
|
||||
protected function loadFile($name, $filename)
|
||||
{
|
||||
$file = CompiledYamlFile::instance($filename);
|
||||
if (preg_match('|languages\.yaml$|', $filename)) {
|
||||
$this->object->mergeRecursive($file->content());
|
||||
} else {
|
||||
$this->object->join($name, $file->content(), '/');
|
||||
}
|
||||
$file->free();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
<?php
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Debugger;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Data\Data;
|
||||
use RocketTheme\Toolbox\Blueprints\Blueprints;
|
||||
use RocketTheme\Toolbox\File\PhpFile;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use Grav\Common\Service\ConfigServiceProvider;
|
||||
|
||||
/**
|
||||
* The Config class contains configuration information.
|
||||
@@ -16,464 +14,84 @@ use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
*/
|
||||
class Config extends Data
|
||||
{
|
||||
protected $grav;
|
||||
protected $streams = [
|
||||
'system' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['system'],
|
||||
]
|
||||
],
|
||||
'user' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user'],
|
||||
]
|
||||
],
|
||||
'blueprints' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user://blueprints', 'system/blueprints'],
|
||||
]
|
||||
],
|
||||
'config' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user://config', 'system/config'],
|
||||
]
|
||||
],
|
||||
'plugins' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user://plugins'],
|
||||
]
|
||||
],
|
||||
'plugin' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user://plugins'],
|
||||
]
|
||||
],
|
||||
'themes' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user://themes'],
|
||||
]
|
||||
],
|
||||
'languages' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user://languages', 'system/languages'],
|
||||
]
|
||||
],
|
||||
'cache' => [
|
||||
'type' => 'Stream',
|
||||
'prefixes' => [
|
||||
'' => ['cache'],
|
||||
'images' => ['images']
|
||||
]
|
||||
],
|
||||
'log' => [
|
||||
'type' => 'Stream',
|
||||
'prefixes' => [
|
||||
'' => ['logs']
|
||||
]
|
||||
],
|
||||
'backup' => [
|
||||
'type' => 'Stream',
|
||||
'prefixes' => [
|
||||
'' => ['backup']
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
protected $setup = [];
|
||||
|
||||
protected $blueprintFiles = [];
|
||||
protected $configFiles = [];
|
||||
protected $languageFiles = [];
|
||||
protected $checksum;
|
||||
protected $timestamp;
|
||||
|
||||
protected $configLookup;
|
||||
protected $blueprintLookup;
|
||||
protected $pluginLookup;
|
||||
protected $languagesLookup;
|
||||
|
||||
protected $finder;
|
||||
protected $environment;
|
||||
protected $messages = [];
|
||||
|
||||
protected $languages;
|
||||
|
||||
public function __construct(array $setup = array(), Grav $grav = null, $environment = null)
|
||||
{
|
||||
$this->grav = $grav ?: Grav::instance();
|
||||
$this->finder = new ConfigFinder;
|
||||
$this->environment = $environment ?: 'localhost';
|
||||
$this->messages[] = 'Environment Name: ' . $this->environment;
|
||||
|
||||
// Make sure that
|
||||
if (!isset($setup['streams']['schemes'])) {
|
||||
$setup['streams']['schemes'] = [];
|
||||
}
|
||||
$setup['streams']['schemes'] += $this->streams;
|
||||
|
||||
$setup = $this->autoDetectEnvironmentConfig($setup);
|
||||
|
||||
$this->setup = $setup;
|
||||
parent::__construct($setup);
|
||||
|
||||
$this->check();
|
||||
}
|
||||
protected $modified = false;
|
||||
|
||||
public function key()
|
||||
{
|
||||
return $this->checksum();
|
||||
}
|
||||
|
||||
public function reload()
|
||||
public function checksum($checksum = null)
|
||||
{
|
||||
$this->items = $this->setup;
|
||||
$this->check();
|
||||
$this->init();
|
||||
$this->debug();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function check()
|
||||
{
|
||||
$streams = isset($this->items['streams']['schemes']) ? $this->items['streams']['schemes'] : null;
|
||||
if (!is_array($streams)) {
|
||||
throw new \InvalidArgumentException('Configuration is missing streams.schemes!');
|
||||
}
|
||||
$diff = array_keys(array_diff_key($this->streams, $streams));
|
||||
if ($diff) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('Configuration is missing keys %s from streams.schemes!', implode(', ', $diff))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function debug()
|
||||
{
|
||||
foreach ($this->messages as $message) {
|
||||
$this->grav['debugger']->addMessage($message);
|
||||
}
|
||||
$this->messages = [];
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $this->grav['locator'];
|
||||
|
||||
$this->configLookup = $locator->findResources('config://');
|
||||
$this->blueprintLookup = $locator->findResources('blueprints://config');
|
||||
$this->pluginLookup = $locator->findResources('plugins://');
|
||||
|
||||
|
||||
$this->loadCompiledBlueprints($this->blueprintLookup, $this->pluginLookup, 'master');
|
||||
$this->loadCompiledConfig($this->configLookup, $this->pluginLookup, 'master');
|
||||
|
||||
// process languages if supported
|
||||
if ($this->get('system.languages.translations', true)) {
|
||||
$this->languagesLookup = $locator->findResources('languages://');
|
||||
$this->loadCompiledLanguages($this->languagesLookup, $this->pluginLookup, 'master');
|
||||
}
|
||||
|
||||
$this->initializeLocator($locator);
|
||||
}
|
||||
|
||||
public function checksum()
|
||||
{
|
||||
if (empty($this->checksum)) {
|
||||
$checkBlueprints = $this->get('system.cache.check.blueprints', false);
|
||||
$checkLanguages = $this->get('system.cache.check.languages', false);
|
||||
$checkConfig = $this->get('system.cache.check.config', true);
|
||||
$checkSystem = $this->get('system.cache.check.system', true);
|
||||
|
||||
if (!$checkBlueprints && !!$checkLanguages && $checkConfig && !$checkSystem) {
|
||||
$this->messages[] = 'Skip configuration timestamp check.';
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate checksum according to the configuration settings.
|
||||
if (!$checkConfig) {
|
||||
// Just check changes in system.yaml files and ignore all the other files.
|
||||
$cc = $checkSystem ? $this->finder->locateConfigFile($this->configLookup, 'system') : [];
|
||||
} else {
|
||||
// Check changes in all configuration files.
|
||||
$cc = $this->finder->locateConfigFiles($this->configLookup, $this->pluginLookup);
|
||||
}
|
||||
|
||||
if ($checkBlueprints) {
|
||||
$cb = $this->finder->locateBlueprintFiles($this->blueprintLookup, $this->pluginLookup);
|
||||
} else {
|
||||
$cb = [];
|
||||
}
|
||||
|
||||
if ($checkLanguages) {
|
||||
$cl = $this->finder->locateLanguageFiles($this->languagesLookup, $this->pluginLookup);
|
||||
} else {
|
||||
$cl = [];
|
||||
}
|
||||
|
||||
$this->checksum = md5(json_encode([$cc, $cb, $cl]));
|
||||
if ($checksum !== null) {
|
||||
$this->checksum = $checksum;
|
||||
}
|
||||
|
||||
return $this->checksum;
|
||||
}
|
||||
|
||||
protected function autoDetectEnvironmentConfig($items)
|
||||
public function modified($modified = null)
|
||||
{
|
||||
$environment = $this->environment;
|
||||
$env_stream = 'user://'.$environment.'/config';
|
||||
|
||||
if (file_exists(USER_DIR.$environment.'/config')) {
|
||||
array_unshift($items['streams']['schemes']['config']['prefixes'][''], $env_stream);
|
||||
if ($modified !== null) {
|
||||
$this->modified = $modified;
|
||||
}
|
||||
|
||||
return $items;
|
||||
return $this->modified;
|
||||
}
|
||||
|
||||
protected function loadCompiledBlueprints($blueprints, $plugins, $filename = null)
|
||||
public function reload()
|
||||
{
|
||||
$checksum = md5(json_encode($blueprints));
|
||||
$filename = $filename
|
||||
? CACHE_DIR . 'compiled/blueprints/' . $filename . '-' . $this->environment . '.php'
|
||||
: CACHE_DIR . 'compiled/blueprints/' . $checksum . '-' . $this->environment . '.php';
|
||||
$file = PhpFile::instance($filename);
|
||||
$cache = $file->exists() ? $file->content() : null;
|
||||
$blueprintFiles = $this->finder->locateBlueprintFiles($blueprints, $plugins);
|
||||
$checksum .= ':'.md5(json_encode($blueprintFiles));
|
||||
$class = get_class($this);
|
||||
$grav = Grav::instance();
|
||||
|
||||
// Load real file if cache isn't up to date (or is invalid).
|
||||
if (
|
||||
!is_array($cache)
|
||||
|| !isset($cache['checksum'])
|
||||
|| !isset($cache['@class'])
|
||||
|| $cache['checksum'] != $checksum
|
||||
|| $cache['@class'] != $class
|
||||
) {
|
||||
// Attempt to lock the file for writing.
|
||||
$file->lock(false);
|
||||
// Load new configuration.
|
||||
$config = ConfigServiceProvider::load($grav);
|
||||
|
||||
// Load blueprints.
|
||||
$this->blueprints = new Blueprints;
|
||||
foreach ($blueprintFiles as $files) {
|
||||
$this->loadBlueprintFiles($files);
|
||||
}
|
||||
/** @var Debugger $debugger */
|
||||
$debugger = $grav['debugger'];
|
||||
|
||||
$cache = [
|
||||
'@class' => $class,
|
||||
'checksum' => $checksum,
|
||||
'files' => $blueprintFiles,
|
||||
'data' => $this->blueprints->toArray()
|
||||
];
|
||||
// If compiled file wasn't already locked by another process, save it.
|
||||
if ($file->locked() !== false) {
|
||||
$this->messages[] = 'Saving compiled blueprints.';
|
||||
$file->save($cache);
|
||||
$file->unlock();
|
||||
}
|
||||
} else {
|
||||
$this->blueprints = new Blueprints($cache['data']);
|
||||
if ($config->modified()) {
|
||||
// Update current configuration.
|
||||
$this->items = $config->toArray();
|
||||
$this->checksum($config->checksum());
|
||||
$this->modified(true);
|
||||
|
||||
$debugger->addMessage('Configuration was changed and saved.');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function debug()
|
||||
{
|
||||
/** @var Debugger $debugger */
|
||||
$debugger = Grav::instance()['debugger'];
|
||||
|
||||
$debugger->addMessage('Environment Name: ' . $this->environment);
|
||||
if ($this->modified()) {
|
||||
$debugger->addMessage('Configuration reloaded and cached.');
|
||||
}
|
||||
}
|
||||
|
||||
protected function loadCompiledConfig($configs, $plugins, $filename = null)
|
||||
public function init()
|
||||
{
|
||||
$filename = $filename
|
||||
? CACHE_DIR . 'compiled/config/' . $filename . '-' . $this->environment . '.php'
|
||||
: CACHE_DIR . 'compiled/config/' . $checksum . '-' . $this->environment . '.php';
|
||||
$file = PhpFile::instance($filename);
|
||||
$cache = $file->exists() ? $file->content() : null;
|
||||
$class = get_class($this);
|
||||
$checksum = $this->checksum();
|
||||
|
||||
if (
|
||||
!is_array($cache)
|
||||
|| !isset($cache['checksum'])
|
||||
|| !isset($cache['@class'])
|
||||
|| $cache['@class'] != $class
|
||||
) {
|
||||
$this->messages[] = 'No cached configuration, compiling new configuration..';
|
||||
} else if ($cache['checksum'] !== $checksum) {
|
||||
$this->messages[] = 'Configuration checksum mismatch, reloading configuration..';
|
||||
} else {
|
||||
$this->messages[] = 'Configuration checksum matches, using cached version.';
|
||||
|
||||
$this->items = $cache['data'];
|
||||
return;
|
||||
}
|
||||
|
||||
$configFiles = $this->finder->locateConfigFiles($configs, $plugins);
|
||||
|
||||
// Attempt to lock the file for writing.
|
||||
$file->lock(false);
|
||||
|
||||
// Load configuration.
|
||||
foreach ($configFiles as $files) {
|
||||
$this->loadConfigFiles($files);
|
||||
}
|
||||
$cache = [
|
||||
'@class' => $class,
|
||||
'timestamp' => time(),
|
||||
'checksum' => $checksum,
|
||||
'data' => $this->toArray()
|
||||
];
|
||||
|
||||
// If compiled file wasn't already locked by another process, save it.
|
||||
if ($file->locked() !== false) {
|
||||
$this->messages[] = 'Saving compiled configuration.';
|
||||
$file->save($cache);
|
||||
$file->unlock();
|
||||
}
|
||||
|
||||
$this->items = $cache['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $languages
|
||||
* @param $plugins
|
||||
* @param null $filename
|
||||
*/
|
||||
protected function loadCompiledLanguages($languages, $plugins, $filename = null)
|
||||
{
|
||||
$checksum = md5(json_encode($languages));
|
||||
$filename = $filename
|
||||
? CACHE_DIR . 'compiled/languages/' . $filename . '-' . $this->environment . '.php'
|
||||
: CACHE_DIR . 'compiled/languages/' . $checksum . '-' . $this->environment . '.php';
|
||||
$file = PhpFile::instance($filename);
|
||||
$cache = $file->exists() ? $file->content() : null;
|
||||
$languageFiles = $this->finder->locateLanguageFiles($languages, $plugins);
|
||||
$checksum .= ':' . md5(json_encode($languageFiles));
|
||||
$class = get_class($this);
|
||||
|
||||
// Load real file if cache isn't up to date (or is invalid).
|
||||
if (
|
||||
!is_array($cache)
|
||||
|| !isset($cache['checksum'])
|
||||
|| !isset($cache['@class'])
|
||||
|| $cache['checksum'] != $checksum
|
||||
|| $cache['@class'] != $class
|
||||
) {
|
||||
// Attempt to lock the file for writing.
|
||||
$file->lock(false);
|
||||
|
||||
// Load languages.
|
||||
$this->languages = new Languages;
|
||||
$pluginPaths = str_ireplace(GRAV_ROOT . '/', '', array_reverse($plugins));
|
||||
foreach ($pluginPaths as $path) {
|
||||
if (isset($languageFiles[$path])) {
|
||||
foreach ((array) $languageFiles[$path] as $plugin => $item) {
|
||||
$lang_file = CompiledYamlFile::instance($item['file']);
|
||||
$content = $lang_file->content();
|
||||
$this->languages->mergeRecursive($content);
|
||||
}
|
||||
unset($languageFiles[$path]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($languageFiles as $location) {
|
||||
foreach ($location as $lang => $item) {
|
||||
$lang_file = CompiledYamlFile::instance($item['file']);
|
||||
$content = $lang_file->content();
|
||||
$this->languages->join($lang, $content, '/');
|
||||
}
|
||||
}
|
||||
|
||||
$cache = [
|
||||
'@class' => $class,
|
||||
'checksum' => $checksum,
|
||||
'files' => $languageFiles,
|
||||
'data' => $this->languages->toArray()
|
||||
];
|
||||
// If compiled file wasn't already locked by another process, save it.
|
||||
if ($file->locked() !== false) {
|
||||
$this->messages[] = 'Saving compiled languages.';
|
||||
$file->save($cache);
|
||||
$file->unlock();
|
||||
}
|
||||
} else {
|
||||
$this->languages = new Languages($cache['data']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load blueprints.
|
||||
*
|
||||
* @param array $files
|
||||
*/
|
||||
public function loadBlueprintFiles(array $files)
|
||||
{
|
||||
foreach ($files as $name => $item) {
|
||||
$file = CompiledYamlFile::instance($item['file']);
|
||||
$this->blueprints->embed($name, $file->content(), '/');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load configuration.
|
||||
*
|
||||
* @param array $files
|
||||
*/
|
||||
public function loadConfigFiles(array $files)
|
||||
{
|
||||
foreach ($files as $name => $item) {
|
||||
$file = CompiledYamlFile::instance($item['file']);
|
||||
$this->join($name, $file->content(), '/');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize resource locator by using the configuration.
|
||||
*
|
||||
* @param UniformResourceLocator $locator
|
||||
*/
|
||||
public function initializeLocator(UniformResourceLocator $locator)
|
||||
{
|
||||
$locator->reset();
|
||||
|
||||
$schemes = (array) $this->get('streams.schemes', []);
|
||||
|
||||
foreach ($schemes as $scheme => $config) {
|
||||
if (isset($config['paths'])) {
|
||||
$locator->addPath($scheme, '', $config['paths']);
|
||||
}
|
||||
if (isset($config['prefixes'])) {
|
||||
foreach ($config['prefixes'] as $prefix => $paths) {
|
||||
$locator->addPath($scheme, $prefix, $paths);
|
||||
}
|
||||
$setup = Grav::instance()['setup']->toArray();
|
||||
foreach ($setup as $key => $value) {
|
||||
if ($key === 'streams' || !is_array($value)) {
|
||||
// Optimized as streams and simple values are fully defined in setup.
|
||||
$this->items[$key] = $value;
|
||||
} else {
|
||||
$this->joinDefaults($key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available streams and their types from the configuration.
|
||||
*
|
||||
* @return array
|
||||
* @return mixed
|
||||
* @deprecated
|
||||
*/
|
||||
public function getStreams()
|
||||
{
|
||||
$schemes = [];
|
||||
foreach ((array) $this->get('streams.schemes') as $scheme => $config) {
|
||||
$type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream';
|
||||
if ($type[0] != '\\') {
|
||||
$type = '\\RocketTheme\\Toolbox\\StreamWrapper\\' . $type;
|
||||
}
|
||||
|
||||
$schemes[$scheme] = $type;
|
||||
}
|
||||
|
||||
return $schemes;
|
||||
}
|
||||
|
||||
public function getLanguages()
|
||||
{
|
||||
return $this->languages;
|
||||
return Grav::instance()['languages'];
|
||||
}
|
||||
}
|
||||
|
||||
258
system/src/Grav/Common/Config/ConfigFileFinder.php
Normal file
258
system/src/Grav/Common/Config/ConfigFileFinder.php
Normal file
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
|
||||
/**
|
||||
* The Configuration & Blueprints Finder class.
|
||||
*/
|
||||
class ConfigFileFinder
|
||||
{
|
||||
protected $base = '';
|
||||
|
||||
/**
|
||||
* @param string $base
|
||||
* @return $this
|
||||
*/
|
||||
public function setBase($base)
|
||||
{
|
||||
$this->base = $base ? "{$base}/" : '';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all locations for all the files with a timestamp.
|
||||
*
|
||||
* @param array $paths List of folders to look from.
|
||||
* @param string $pattern Pattern to match the file. Pattern will also be removed from the key.
|
||||
* @param int $levels Maximum number of recursive directories.
|
||||
* @return array
|
||||
*/
|
||||
public function locateFiles(array $paths, $pattern = '|\.yaml$|', $levels = -1)
|
||||
{
|
||||
$list = [];
|
||||
foreach ($paths as $folder) {
|
||||
$list += $this->detectRecursive($folder, $pattern, $levels);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all locations for all the files with a timestamp.
|
||||
*
|
||||
* @param array $paths List of folders to look from.
|
||||
* @param string $pattern Pattern to match the file. Pattern will also be removed from the key.
|
||||
* @param int $levels Maximum number of recursive directories.
|
||||
* @return array
|
||||
*/
|
||||
public function getFiles(array $paths, $pattern = '|\.yaml$|', $levels = -1)
|
||||
{
|
||||
$list = [];
|
||||
foreach ($paths as $folder) {
|
||||
$path = trim(Folder::getRelativePath($folder), '/');
|
||||
|
||||
$files = $this->detectRecursive($folder, $pattern, $levels);
|
||||
|
||||
$list += $files[trim($path, '/')];
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all paths for all the files with a timestamp.
|
||||
*
|
||||
* @param array $paths List of folders to look from.
|
||||
* @param string $pattern Pattern to match the file. Pattern will also be removed from the key.
|
||||
* @param int $levels Maximum number of recursive directories.
|
||||
* @return array
|
||||
*/
|
||||
public function listFiles(array $paths, $pattern = '|\.yaml$|', $levels = -1)
|
||||
{
|
||||
$list = [];
|
||||
foreach ($paths as $folder) {
|
||||
$list = array_merge_recursive($list, $this->detectAll($folder, $pattern, $levels));
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find filename from a list of folders.
|
||||
*
|
||||
* Note: Only finds the last override.
|
||||
*
|
||||
* @param string $filename
|
||||
* @param array $folders
|
||||
* @return array
|
||||
*/
|
||||
public function locateFileInFolder($filename, array $folders)
|
||||
{
|
||||
$list = [];
|
||||
foreach ($folders as $folder) {
|
||||
$list += $this->detectInFolder($folder, $filename);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find filename from a list of folders.
|
||||
*
|
||||
* @param array $folders
|
||||
* @param string $filename
|
||||
* @return array
|
||||
*/
|
||||
public function locateInFolders(array $folders, $filename = null)
|
||||
{
|
||||
$list = [];
|
||||
foreach ($folders as $folder) {
|
||||
$path = trim(Folder::getRelativePath($folder), '/');
|
||||
$list[$path] = $this->detectInFolder($folder, $filename);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all existing locations for a single file with a timestamp.
|
||||
*
|
||||
* @param array $paths Filesystem paths to look up from.
|
||||
* @param string $name Configuration file to be located.
|
||||
* @param string $ext File extension (optional, defaults to .yaml).
|
||||
* @return array
|
||||
*/
|
||||
public function locateFile(array $paths, $name, $ext = '.yaml')
|
||||
{
|
||||
$filename = preg_replace('|[.\/]+|', '/', $name) . $ext;
|
||||
|
||||
$list = [];
|
||||
foreach ($paths as $folder) {
|
||||
$path = trim(Folder::getRelativePath($folder), '/');
|
||||
|
||||
if (is_file("{$folder}/{$filename}")) {
|
||||
$modified = filemtime("{$folder}/{$filename}");
|
||||
} else {
|
||||
$modified = 0;
|
||||
}
|
||||
$basename = $this->base . $name;
|
||||
$list[$path] = [$basename => ['file' => "{$path}/{$filename}", 'modified' => $modified]];
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects all directories with a configuration file and returns them with last modification time.
|
||||
*
|
||||
* @param string $folder Location to look up from.
|
||||
* @param string $pattern Pattern to match the file. Pattern will also be removed from the key.
|
||||
* @param int $levels Maximum number of recursive directories.
|
||||
* @return array
|
||||
* @internal
|
||||
*/
|
||||
protected function detectRecursive($folder, $pattern, $levels)
|
||||
{
|
||||
$path = trim(Folder::getRelativePath($folder), '/');
|
||||
|
||||
if (is_dir($folder)) {
|
||||
// Find all system and user configuration files.
|
||||
$options = [
|
||||
'levels' => $levels,
|
||||
'compare' => 'Filename',
|
||||
'pattern' => $pattern,
|
||||
'filters' => [
|
||||
'pre-key' => $this->base,
|
||||
'key' => $pattern,
|
||||
'value' => function (\RecursiveDirectoryIterator $file) use ($path) {
|
||||
return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()];
|
||||
}
|
||||
],
|
||||
'key' => 'SubPathname'
|
||||
];
|
||||
|
||||
$list = Folder::all($folder, $options);
|
||||
|
||||
ksort($list);
|
||||
} else {
|
||||
$list = [];
|
||||
}
|
||||
|
||||
return [$path => $list];
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects all directories with the lookup file and returns them with last modification time.
|
||||
*
|
||||
* @param string $folder Location to look up from.
|
||||
* @param string $lookup Filename to be located (defaults to directory name).
|
||||
* @return array
|
||||
* @internal
|
||||
*/
|
||||
protected function detectInFolder($folder, $lookup = null)
|
||||
{
|
||||
$folder = rtrim($folder, '/');
|
||||
$path = trim(Folder::getRelativePath($folder), '/');
|
||||
$base = $path === $folder ? '' : ($path ? substr($folder, 0, -strlen($path)) : $folder . '/');
|
||||
|
||||
$list = [];
|
||||
|
||||
if (is_dir($folder)) {
|
||||
$iterator = new \DirectoryIterator($folder);
|
||||
|
||||
/** @var \DirectoryIterator $directory */
|
||||
foreach ($iterator as $directory) {
|
||||
if (!$directory->isDir() || $directory->isDot()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $directory->getBasename();
|
||||
$find = ($lookup ?: $name) . '.yaml';
|
||||
$filename = "{$path}/{$name}/{$find}";
|
||||
|
||||
if (file_exists($base . $filename)) {
|
||||
$basename = $this->base . $name;
|
||||
$list[$basename] = ['file' => $filename, 'modified' => filemtime($base . $filename)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects all plugins with a configuration file and returns them with last modification time.
|
||||
*
|
||||
* @param string $folder Location to look up from.
|
||||
* @param string $pattern Pattern to match the file. Pattern will also be removed from the key.
|
||||
* @param int $levels Maximum number of recursive directories.
|
||||
* @return array
|
||||
* @internal
|
||||
*/
|
||||
protected function detectAll($folder, $pattern, $levels)
|
||||
{
|
||||
$path = trim(Folder::getRelativePath($folder), '/');
|
||||
|
||||
if (is_dir($folder)) {
|
||||
// Find all system and user configuration files.
|
||||
$options = [
|
||||
'levels' => $levels,
|
||||
'compare' => 'Filename',
|
||||
'pattern' => $pattern,
|
||||
'filters' => [
|
||||
'pre-key' => $this->base,
|
||||
'key' => $pattern,
|
||||
'value' => function (\RecursiveDirectoryIterator $file) use ($path) {
|
||||
return ["{$path}/{$file->getSubPathname()}" => $file->getMTime()];
|
||||
}
|
||||
],
|
||||
'key' => 'SubPathname'
|
||||
];
|
||||
|
||||
$list = Folder::all($folder, $options);
|
||||
|
||||
ksort($list);
|
||||
} else {
|
||||
$list = [];
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
|
||||
/**
|
||||
* The Configuration Finder class.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class ConfigFinder
|
||||
{
|
||||
/**
|
||||
* Get all locations for blueprint files (including plugins).
|
||||
*
|
||||
* @param array $blueprints
|
||||
* @param array $plugins
|
||||
* @return array
|
||||
*/
|
||||
public function locateBlueprintFiles(array $blueprints, array $plugins)
|
||||
{
|
||||
$list = [];
|
||||
foreach (array_reverse($plugins) as $folder) {
|
||||
$list += $this->detectInFolder($folder, 'blueprints');
|
||||
}
|
||||
foreach (array_reverse($blueprints) as $folder) {
|
||||
$list += $this->detectRecursive($folder);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all locations for configuration files (including plugins).
|
||||
*
|
||||
* @param array $configs
|
||||
* @param array $plugins
|
||||
* @return array
|
||||
*/
|
||||
public function locateConfigFiles(array $configs, array $plugins)
|
||||
{
|
||||
$list = [];
|
||||
foreach (array_reverse($plugins) as $folder) {
|
||||
$list += $this->detectInFolder($folder);
|
||||
}
|
||||
foreach (array_reverse($configs) as $folder) {
|
||||
$list += $this->detectRecursive($folder);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function locateLanguageFiles(array $languages, array $plugins)
|
||||
{
|
||||
$list = [];
|
||||
foreach (array_reverse($plugins) as $folder) {
|
||||
$list += $this->detectLanguagesInFolder($folder, 'languages');
|
||||
}
|
||||
foreach (array_reverse($languages) as $folder) {
|
||||
$list += $this->detectRecursive($folder);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all locations for a single configuration file.
|
||||
*
|
||||
* @param array $folders Locations to look up from.
|
||||
* @param string $name Filename to be located.
|
||||
* @return array
|
||||
*/
|
||||
public function locateConfigFile(array $folders, $name)
|
||||
{
|
||||
$filename = "{$name}.yaml";
|
||||
|
||||
$list = [];
|
||||
foreach ($folders as $folder) {
|
||||
$path = trim(Folder::getRelativePath($folder), '/');
|
||||
|
||||
if (is_file("{$folder}/{$filename}")) {
|
||||
$modified = filemtime("{$folder}/{$filename}");
|
||||
} else {
|
||||
$modified = 0;
|
||||
}
|
||||
$list[$path] = [$name => ['file' => "{$path}/{$filename}", 'modified' => $modified]];
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects all plugins with a configuration file and returns them with last modification time.
|
||||
*
|
||||
* @param string $folder Location to look up from.
|
||||
* @param string $lookup Filename to be located.
|
||||
* @return array
|
||||
* @internal
|
||||
*/
|
||||
protected function detectInFolder($folder, $lookup = null)
|
||||
{
|
||||
$path = trim(Folder::getRelativePath($folder), '/');
|
||||
|
||||
$list = [];
|
||||
|
||||
if (is_dir($folder)) {
|
||||
$iterator = new \FilesystemIterator($folder);
|
||||
|
||||
/** @var \DirectoryIterator $directory */
|
||||
foreach ($iterator as $directory) {
|
||||
if (!$directory->isDir()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $directory->getBasename();
|
||||
$find = ($lookup ?: $name) . '.yaml';
|
||||
$filename = "{$path}/{$name}/$find";
|
||||
|
||||
if (file_exists($filename)) {
|
||||
$list["plugins/{$name}"] = ['file' => $filename, 'modified' => filemtime($filename)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [$path => $list];
|
||||
}
|
||||
|
||||
protected function detectLanguagesInFolder($folder, $lookup = null)
|
||||
{
|
||||
$path = trim(Folder::getRelativePath($folder), '/');
|
||||
|
||||
$list = [];
|
||||
|
||||
if (is_dir($folder)) {
|
||||
$iterator = new \FilesystemIterator($folder);
|
||||
|
||||
/** @var \DirectoryIterator $directory */
|
||||
foreach ($iterator as $directory) {
|
||||
if (!$directory->isDir()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $directory->getBasename();
|
||||
$find = ($lookup ?: $name) . '.yaml';
|
||||
$filename = "{$path}/{$name}/$find";
|
||||
|
||||
if (file_exists($filename)) {
|
||||
$list[$name] = ['file' => $filename, 'modified' => filemtime($filename)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [$path => $list];
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects all plugins with a configuration file and returns them with last modification time.
|
||||
*
|
||||
* @param string $folder Location to look up from.
|
||||
* @return array
|
||||
* @internal
|
||||
*/
|
||||
protected function detectRecursive($folder)
|
||||
{
|
||||
$path = trim(Folder::getRelativePath($folder), '/');
|
||||
|
||||
if (is_dir($folder)) {
|
||||
// Find all system and user configuration files.
|
||||
$options = [
|
||||
'compare' => 'Filename',
|
||||
'pattern' => '|\.yaml$|',
|
||||
'filters' => [
|
||||
'key' => '|\.yaml$|',
|
||||
'value' => function (\RecursiveDirectoryIterator $file) use ($path) {
|
||||
return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()];
|
||||
}
|
||||
],
|
||||
'key' => 'SubPathname'
|
||||
];
|
||||
|
||||
$list = Folder::all($folder, $options);
|
||||
} else {
|
||||
$list = [];
|
||||
}
|
||||
|
||||
return [$path => $list];
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,23 @@ use Grav\Common\Data\Data;
|
||||
*/
|
||||
class Languages extends Data
|
||||
{
|
||||
public function checksum($checksum = null)
|
||||
{
|
||||
if ($checksum !== null) {
|
||||
$this->checksum = $checksum;
|
||||
}
|
||||
|
||||
return $this->checksum;
|
||||
}
|
||||
|
||||
public function modified($modified = null)
|
||||
{
|
||||
if ($modified !== null) {
|
||||
$this->modified = $modified;
|
||||
}
|
||||
|
||||
return $this->modified;
|
||||
}
|
||||
|
||||
public function reformat()
|
||||
{
|
||||
|
||||
254
system/src/Grav/Common/Config/Setup.php
Normal file
254
system/src/Grav/Common/Config/Setup.php
Normal file
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Utils;
|
||||
use RocketTheme\Toolbox\File\YamlFile;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
/**
|
||||
* The Config class contains configuration information.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Setup extends Data
|
||||
{
|
||||
protected $streams = [
|
||||
'system' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['system'],
|
||||
]
|
||||
],
|
||||
'user' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user'],
|
||||
]
|
||||
],
|
||||
'environment' => [
|
||||
'type' => 'ReadOnlyStream'
|
||||
// If not defined, environment will be set up in the constructor.
|
||||
],
|
||||
'asset' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['assets'],
|
||||
]
|
||||
],
|
||||
'blueprints' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['environment://blueprints', 'user://blueprints', 'system/blueprints'],
|
||||
]
|
||||
],
|
||||
'config' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['environment://config', 'user://config', 'system/config'],
|
||||
]
|
||||
],
|
||||
'plugins' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user://plugins'],
|
||||
]
|
||||
],
|
||||
'plugin' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user://plugins'],
|
||||
]
|
||||
],
|
||||
'themes' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user://themes'],
|
||||
]
|
||||
],
|
||||
'languages' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['environment://languages', 'user://languages', 'system/languages'],
|
||||
]
|
||||
],
|
||||
'cache' => [
|
||||
'type' => 'Stream',
|
||||
'prefixes' => [
|
||||
'' => ['cache'],
|
||||
'images' => ['images']
|
||||
]
|
||||
],
|
||||
'log' => [
|
||||
'type' => 'Stream',
|
||||
'prefixes' => [
|
||||
'' => ['logs']
|
||||
]
|
||||
],
|
||||
'backup' => [
|
||||
'type' => 'Stream',
|
||||
'prefixes' => [
|
||||
'' => ['backup']
|
||||
]
|
||||
],
|
||||
'image' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user://images', 'system://images']
|
||||
]
|
||||
],
|
||||
'page' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user://pages']
|
||||
]
|
||||
],
|
||||
'account' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user://accounts']
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
public function __construct($container)
|
||||
{
|
||||
$environment = $container['uri']->environment();
|
||||
if (!$environment) {
|
||||
$environment = 'localhost';
|
||||
}
|
||||
|
||||
// Pre-load setup.php which contains our initial configuration.
|
||||
// Configuration may contain dynamic parts, which is why we need to always load it.
|
||||
$file = GRAV_ROOT . '/setup.php';
|
||||
$setup = is_file($file) ? (array) include $file : [];
|
||||
|
||||
// Add default streams defined in beginning of the class.
|
||||
if (!isset($setup['streams']['schemes'])) {
|
||||
$setup['streams']['schemes'] = [];
|
||||
}
|
||||
$setup['streams']['schemes'] += $this->streams;
|
||||
|
||||
// Initialize class.
|
||||
parent::__construct($setup);
|
||||
|
||||
// Set up environment.
|
||||
$this->def('environment', $environment);
|
||||
$this->def('streams.schemes.environment.prefixes', ['' => ["user://{$this->environment}"]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$locator = new UniformResourceLocator(GRAV_ROOT);
|
||||
$files = [];
|
||||
|
||||
$guard = 5;
|
||||
do {
|
||||
$check = $files;
|
||||
$this->initializeLocator($locator);
|
||||
$files = $locator->findResources('config://streams.yaml');
|
||||
|
||||
if ($check === $files) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Update streams.
|
||||
foreach ($files as $path) {
|
||||
$file = CompiledYamlFile::instance($path);
|
||||
$content = $file->content();
|
||||
if (!empty($content['schemes'])) {
|
||||
$this->items['streams']['schemes'] = $content['schemes'] + $this->items['streams']['schemes'];
|
||||
}
|
||||
}
|
||||
} while (--$guard);
|
||||
|
||||
if (!$guard) {
|
||||
throw new \RuntimeException('Setup: Configuration reload loop detected!');
|
||||
}
|
||||
|
||||
// Make sure we have valid setup.
|
||||
$this->check($locator);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize resource locator by using the configuration.
|
||||
*
|
||||
* @param UniformResourceLocator $locator
|
||||
*/
|
||||
public function initializeLocator(UniformResourceLocator $locator)
|
||||
{
|
||||
$locator->reset();
|
||||
|
||||
$schemes = (array) $this->get('streams.schemes', []);
|
||||
|
||||
foreach ($schemes as $scheme => $config) {
|
||||
if (isset($config['paths'])) {
|
||||
$locator->addPath($scheme, '', $config['paths']);
|
||||
}
|
||||
if (isset($config['prefixes'])) {
|
||||
foreach ($config['prefixes'] as $prefix => $paths) {
|
||||
$locator->addPath($scheme, $prefix, $paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available streams and their types from the configuration.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getStreams()
|
||||
{
|
||||
$schemes = [];
|
||||
foreach ((array) $this->get('streams.schemes') as $scheme => $config) {
|
||||
$type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream';
|
||||
if ($type[0] != '\\') {
|
||||
$type = '\\RocketTheme\\Toolbox\\StreamWrapper\\' . $type;
|
||||
}
|
||||
|
||||
$schemes[$scheme] = $type;
|
||||
}
|
||||
|
||||
return $schemes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UniformResourceLocator $locator
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function check(UniformResourceLocator $locator)
|
||||
{
|
||||
$streams = isset($this->items['streams']['schemes']) ? $this->items['streams']['schemes'] : null;
|
||||
if (!is_array($streams)) {
|
||||
throw new \InvalidArgumentException('Configuration is missing streams.schemes!');
|
||||
}
|
||||
$diff = array_keys(array_diff_key($this->streams, $streams));
|
||||
if ($diff) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('Configuration is missing keys %s from streams.schemes!', implode(', ', $diff))
|
||||
);
|
||||
}
|
||||
|
||||
if (!$locator->findResource('environment://config', true)) {
|
||||
// If environment does not have its own directory, remove it from the lookup.
|
||||
$this->set('streams.schemes.environment.prefixes', ['config' => []]);
|
||||
$this->initializeLocator($locator);
|
||||
}
|
||||
|
||||
// Create security.yaml if it doesn't exist.
|
||||
$filename = $locator->findResource('config://security.yaml', true, true);
|
||||
$file = YamlFile::instance($filename);
|
||||
if (!$file->exists()) {
|
||||
$file->save(['salt' => Utils::generateRandomString(14)]);
|
||||
$file->free();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ namespace Grav\Common\Data;
|
||||
|
||||
use Grav\Common\GravTrait;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
|
||||
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
|
||||
|
||||
/**
|
||||
* Blueprint handles the inside logic of blueprints.
|
||||
@@ -10,9 +12,9 @@ use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Blueprint
|
||||
class Blueprint implements \ArrayAccess, ExportInterface
|
||||
{
|
||||
use Export, DataMutatorTrait, GravTrait;
|
||||
use Export, NestedArrayAccessWithGetters, GravTrait;
|
||||
|
||||
public $name;
|
||||
|
||||
@@ -76,7 +78,9 @@ class Blueprint
|
||||
try {
|
||||
$this->validateArray($data, $this->nested);
|
||||
} catch (\RuntimeException $e) {
|
||||
throw new \RuntimeException(sprintf('<b>Validation failed:</b> %s', $e->getMessage()));
|
||||
$language = self::getGrav()['language'];
|
||||
$message = sprintf($language->translate('FORM.VALIDATION_FAIL', null, true) . ' %s', $e->getMessage());
|
||||
throw new \RuntimeException($message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,9 +242,6 @@ class Blueprint
|
||||
|
||||
if ($rule) {
|
||||
// Item has been defined in blueprints.
|
||||
if (is_array($field) && count($field) == 1 && reset($field) == '') {
|
||||
continue;
|
||||
}
|
||||
$field = Validation::filter($field, $rule);
|
||||
} elseif (is_array($field) && is_array($val)) {
|
||||
// Array has been defined in blueprints.
|
||||
@@ -310,91 +311,105 @@ class Blueprint
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all field definitions from the blueprints.
|
||||
*
|
||||
* @param array $fields
|
||||
* @param array $params
|
||||
* @param string $prefix
|
||||
* @param array $current
|
||||
* @internal
|
||||
*/
|
||||
protected function parseFormFields(array &$fields, $params, $prefix, array &$current)
|
||||
{
|
||||
// Go though all the fields in current level.
|
||||
foreach ($fields as $key => &$field) {
|
||||
$current[$key] = &$field;
|
||||
// Set name from the array key.
|
||||
$field['name'] = $prefix . $key;
|
||||
$field += $params;
|
||||
* Gets all field definitions from the blueprints.
|
||||
*
|
||||
* @param array $fields
|
||||
* @param array $params
|
||||
* @param string $prefix
|
||||
* @param array $current
|
||||
* @internal
|
||||
*/
|
||||
protected function parseFormFields(array &$fields, $params, $prefix, array &$current)
|
||||
{
|
||||
// Go though all the fields in current level.
|
||||
foreach ($fields as $key => &$field) {
|
||||
$current[$key] = &$field;
|
||||
// Set name from the array key.
|
||||
$field['name'] = $prefix . $key;
|
||||
$field += $params;
|
||||
|
||||
if (isset($field['fields']) && (!isset($field['type']) || $field['type'] !== 'list')) {
|
||||
// Recursively get all the nested fields.
|
||||
$newParams = array_intersect_key($this->filter, $field);
|
||||
$this->parseFormFields($field['fields'], $newParams, $prefix, $current[$key]['fields']);
|
||||
} else if ($field['type'] !== 'ignore') {
|
||||
// Add rule.
|
||||
if (isset($field['fields']) && (!isset($field['type']) || $field['type'] !== 'list')) {
|
||||
// Recursively get all the nested fields.
|
||||
$newParams = array_intersect_key($this->filter, $field);
|
||||
$this->parseFormFields($field['fields'], $newParams, $prefix, $current[$key]['fields']);
|
||||
} else if ($field['type'] !== 'ignore') {
|
||||
$this->rules[$prefix . $key] = &$field;
|
||||
$this->addProperty($prefix . $key);
|
||||
|
||||
foreach ($field as $name => $value) {
|
||||
// Support nested blueprints.
|
||||
if ($this->context && $name == '@import') {
|
||||
$values = (array) $value;
|
||||
if (!isset($field['fields'])) {
|
||||
$field['fields'] = array();
|
||||
}
|
||||
foreach ($values as $bname) {
|
||||
$b = $this->context->get($bname);
|
||||
$field['fields'] = array_merge($field['fields'], $b->fields());
|
||||
}
|
||||
}
|
||||
|
||||
// Support for callable data values.
|
||||
elseif (substr($name, 0, 6) == '@data-') {
|
||||
$property = substr($name, 6);
|
||||
if (is_array($value)) {
|
||||
$func = array_shift($value);
|
||||
} else {
|
||||
$func = $value;
|
||||
$value = array();
|
||||
}
|
||||
list($o, $f) = preg_split('/::/', $func);
|
||||
if (!$f && function_exists($o)) {
|
||||
$data = call_user_func_array($o, $value);
|
||||
} elseif ($f && method_exists($o, $f)) {
|
||||
$data = call_user_func_array(array($o, $f), $value);
|
||||
}
|
||||
|
||||
// If function returns a value,
|
||||
if (isset($data)) {
|
||||
if (isset($field[$property]) && is_array($field[$property]) && is_array($data)) {
|
||||
// Combine field and @data-field together.
|
||||
$field[$property] += $data;
|
||||
} else {
|
||||
// Or create/replace field with @data-field.
|
||||
$field[$property] = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elseif (substr($name, 0, 8) == '@config-') {
|
||||
$property = substr($name, 8);
|
||||
$default = isset($field[$property]) ? $field[$property] : null;
|
||||
$config = self::getGrav()['config']->get($value, $default);
|
||||
|
||||
if (!is_null($config)) {
|
||||
$field[$property] = $config;
|
||||
}
|
||||
if ($field['type'] === 'list') {
|
||||
// we loop through list to get the actual field
|
||||
foreach($field['fields'] as $subName => &$subField) {
|
||||
$this->parseFormField($subField);
|
||||
}
|
||||
} else {
|
||||
$this->parseFormField($field);
|
||||
}
|
||||
|
||||
// Initialize predefined validation rule.
|
||||
if (isset($field['validate']['rule']) && $field['type'] !== 'ignore') {
|
||||
$field['validate'] += $this->getRule($field['validate']['rule']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Parses individual field definition
|
||||
*
|
||||
* @param array $field
|
||||
* @internal
|
||||
*/
|
||||
protected function parseFormField(&$field) {
|
||||
foreach ($field as $name => $value) {
|
||||
// Support nested blueprints.
|
||||
if ($this->context && $name == '@import') {
|
||||
$values = (array) $value;
|
||||
if (!isset($field['fields'])) {
|
||||
$field['fields'] = array();
|
||||
}
|
||||
foreach ($values as $bname) {
|
||||
$b = $this->context->get($bname);
|
||||
$field['fields'] = array_merge($field['fields'], $b->fields());
|
||||
}
|
||||
}
|
||||
|
||||
// Support for callable data values.
|
||||
elseif (substr($name, 0, 6) == '@data-') {
|
||||
$property = substr($name, 6);
|
||||
if (is_array($value)) {
|
||||
$func = array_shift($value);
|
||||
} else {
|
||||
$func = $value;
|
||||
$value = array();
|
||||
}
|
||||
list($o, $f) = preg_split('/::/', $func);
|
||||
if (!$f && function_exists($o)) {
|
||||
$data = call_user_func_array($o, $value);
|
||||
} elseif ($f && method_exists($o, $f)) {
|
||||
$data = call_user_func_array(array($o, $f), $value);
|
||||
}
|
||||
|
||||
// If function returns a value,
|
||||
if (isset($data)) {
|
||||
if (isset($field[$property]) && is_array($field[$property]) && is_array($data)) {
|
||||
// Combine field and @data-field together.
|
||||
$field[$property] += $data;
|
||||
} else {
|
||||
// Or create/replace field with @data-field.
|
||||
$field[$property] = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elseif (substr($name, 0, 8) == '@config-') {
|
||||
$property = substr($name, 8);
|
||||
$default = isset($field[$property]) ? $field[$property] : null;
|
||||
$config = self::getGrav()['config']->get($value, $default);
|
||||
|
||||
if (!is_null($config)) {
|
||||
$field[$property] = $config;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add property to the definition.
|
||||
@@ -449,7 +464,10 @@ class Blueprint
|
||||
if (isset($field['validate']['required'])
|
||||
&& $field['validate']['required'] === true
|
||||
&& empty($data[$name])) {
|
||||
throw new \RuntimeException("Missing required field: {$field['name']}");
|
||||
$value = isset($field['label']) ? $field['label'] : $field['name'];
|
||||
$language = self::getGrav()['language'];
|
||||
$message = sprintf($language->translate('FORM.MISSING_REQUIRED_FIELD', null, true) . ' %s', $value);
|
||||
throw new \RuntimeException($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<?php
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use RocketTheme\Toolbox\ArrayTraits\ArrayAccessWithGetters;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Countable;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
|
||||
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
|
||||
use RocketTheme\Toolbox\Blueprints\Blueprints;
|
||||
use RocketTheme\Toolbox\File\File;
|
||||
use RocketTheme\Toolbox\File\FileInterface;
|
||||
|
||||
@@ -13,9 +15,9 @@ use RocketTheme\Toolbox\File\FileInterface;
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Data implements DataInterface
|
||||
class Data implements DataInterface, \ArrayAccess, \Countable, ExportInterface
|
||||
{
|
||||
use ArrayAccessWithGetters, Countable, Export, DataMutatorTrait;
|
||||
use NestedArrayAccessWithGetters, Countable, Export;
|
||||
|
||||
protected $gettersVariable = 'items';
|
||||
protected $items;
|
||||
@@ -32,12 +34,11 @@ class Data implements DataInterface
|
||||
|
||||
/**
|
||||
* @param array $items
|
||||
* @param Blueprint $blueprints
|
||||
* @param Blueprint|callable $blueprints
|
||||
*/
|
||||
public function __construct(array $items = array(), Blueprint $blueprints = null)
|
||||
public function __construct(array $items = array(), $blueprints = null)
|
||||
{
|
||||
$this->items = $items;
|
||||
|
||||
$this->blueprints = $blueprints;
|
||||
}
|
||||
|
||||
@@ -57,126 +58,150 @@ class Data implements DataInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
* @example $data->def('this.is.my.nested.variable', 'default');
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $default Default value (or null).
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
*/
|
||||
public function def($name, $default = null, $separator = '.')
|
||||
{
|
||||
$this->set($name, $this->get($name, $default, $separator), $separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join two values together by using blueprints if available.
|
||||
* Join nested values together by using blueprints.
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $value Value to be joined.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return $this
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function join($name, $value, $separator = '.')
|
||||
{
|
||||
$old = $this->get($name, null, $separator);
|
||||
if ($old === null) {
|
||||
// Variable does not exist yet: just use the incoming value.
|
||||
} elseif ($this->blueprints) {
|
||||
// Blueprints: join values by using blueprints.
|
||||
$value = $this->blueprints->mergeData($old, $value, $name, $separator);
|
||||
} else {
|
||||
// No blueprints: replace existing top level variables with the new ones.
|
||||
$value = array_merge($old, $value);
|
||||
if ($old !== null) {
|
||||
if (!is_array($old)) {
|
||||
throw new \RuntimeException('Value ' . $old);
|
||||
}
|
||||
if (is_object($value)) {
|
||||
$value = (array) $value;
|
||||
} elseif (!is_array($value)) {
|
||||
throw new \RuntimeException('Value ' . $value);
|
||||
}
|
||||
$value = $this->blueprints()->mergeData($old, $value, $name, $separator);
|
||||
}
|
||||
|
||||
$this->set($name, $value, $separator);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join two values together by using blueprints if available.
|
||||
* Get nested structure containing default values defined in the blueprints.
|
||||
*
|
||||
* Fields without default value are ignored in the list.
|
||||
|
||||
* @return array
|
||||
*/
|
||||
public function getDefaults()
|
||||
{
|
||||
return $this->blueprints()->getDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default values by using blueprints.
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $value Value to be joined.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return $this
|
||||
*/
|
||||
public function joinDefaults($name, $value, $separator = '.')
|
||||
{
|
||||
if (is_object($value)) {
|
||||
$value = (array) $value;
|
||||
}
|
||||
$old = $this->get($name, null, $separator);
|
||||
if ($old === null) {
|
||||
// Variable does not exist yet: just use the incoming value.
|
||||
} elseif ($this->blueprints) {
|
||||
// Blueprints: join values by using blueprints.
|
||||
$value = $this->blueprints->mergeData($value, $old, $name, $separator);
|
||||
} else {
|
||||
// No blueprints: replace existing top level variables with the new ones.
|
||||
$value = array_merge($value, $old);
|
||||
if ($old !== null) {
|
||||
$value = $this->blueprints()->mergeData($value, $old, $name, $separator);
|
||||
}
|
||||
|
||||
$this->set($name, $value, $separator);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value from the configuration and join it with given data.
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param array $value Value to be joined.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function getJoined($name, $value, $separator = '.')
|
||||
{
|
||||
if (is_object($value)) {
|
||||
$value = (array) $value;
|
||||
} elseif (!is_array($value)) {
|
||||
throw new \RuntimeException('Value ' . $value);
|
||||
}
|
||||
|
||||
$old = $this->get($name, null, $separator);
|
||||
|
||||
if ($old === null) {
|
||||
// No value set; no need to join data.
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (!is_array($old)) {
|
||||
throw new \RuntimeException('Value ' . $old);
|
||||
}
|
||||
|
||||
// Return joined data.
|
||||
return $this->blueprints()->mergeData($old, $value, $name, $separator);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Merge two sets of data together.
|
||||
* Merge two configurations together.
|
||||
*
|
||||
* @param array $data
|
||||
* @return void
|
||||
* @return $this
|
||||
*/
|
||||
public function merge(array $data)
|
||||
{
|
||||
if ($this->blueprints) {
|
||||
$this->items = $this->blueprints->mergeData($this->items, $data);
|
||||
} else {
|
||||
$this->items = array_merge($this->items, $data);
|
||||
}
|
||||
$this->items = $this->blueprints()->mergeData($this->items, $data);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add default data to the set.
|
||||
* Set default values to the configuration if variables were not set.
|
||||
*
|
||||
* @param array $data
|
||||
* @return void
|
||||
* @return $this
|
||||
*/
|
||||
public function setDefaults(array $data)
|
||||
{
|
||||
if ($this->blueprints) {
|
||||
$this->items = $this->blueprints->mergeData($data, $this->items);
|
||||
} else {
|
||||
$this->items = array_merge($data, $this->items);
|
||||
}
|
||||
}
|
||||
$this->items = $this->blueprints()->mergeData($data, $this->items);
|
||||
|
||||
/**
|
||||
* Return blueprints.
|
||||
*
|
||||
* @return Blueprint
|
||||
*/
|
||||
public function blueprints()
|
||||
{
|
||||
return $this->blueprints;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate by blueprints.
|
||||
*
|
||||
* @return $this
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if ($this->blueprints) {
|
||||
$this->blueprints->validate($this->items);
|
||||
}
|
||||
$this->blueprints()->validate($this->items);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
* Filter all items by using blueprints.
|
||||
*/
|
||||
public function filter()
|
||||
{
|
||||
if ($this->blueprints) {
|
||||
$this->items = $this->blueprints->filter($this->items);
|
||||
}
|
||||
$this->items = $this->blueprints()->filter($this->items);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,7 +211,24 @@ class Data implements DataInterface
|
||||
*/
|
||||
public function extra()
|
||||
{
|
||||
return $this->blueprints ? $this->blueprints->extra($this->items) : array();
|
||||
return $this->blueprints()->extra($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return blueprints.
|
||||
*
|
||||
* @return Blueprints
|
||||
*/
|
||||
public function blueprints()
|
||||
{
|
||||
if (!$this->blueprints){
|
||||
$this->blueprints = new Blueprints;
|
||||
} elseif (is_callable($this->blueprints)) {
|
||||
// Lazy load blueprints.
|
||||
$blueprints = $this->blueprints;
|
||||
$this->blueprints = $blueprints();
|
||||
}
|
||||
return $this->blueprints;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,16 +266,6 @@ class Data implements DataInterface
|
||||
return $this->file()->raw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the data items.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function items()
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or get the data storage.
|
||||
*
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
trait DataMutatorTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* Get value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
* @example $value = $data->get('this.is.my.nested.variable');
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $default Default value (or null).
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return mixed Value.
|
||||
*/
|
||||
public function get($name, $default = null, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = $this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current) && isset($current->{$field})) {
|
||||
$current = $current->{$field};
|
||||
} elseif (is_array($current) && isset($current[$field])) {
|
||||
$current = $current[$field];
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
* @example $value = $data->set('this.is.my.nested.variable', true);
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $value New value.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
*/
|
||||
public function set($name, $value, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = &$this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current)) {
|
||||
// Handle objects.
|
||||
if (!isset($current->{$field})) {
|
||||
$current->{$field} = array();
|
||||
}
|
||||
$current = &$current->{$field};
|
||||
} else {
|
||||
// Handle arrays and scalars.
|
||||
if (!is_array($current)) {
|
||||
$current = array($field => array());
|
||||
} elseif (!isset($current[$field])) {
|
||||
$current[$field] = array();
|
||||
}
|
||||
$current = &$current[$field];
|
||||
}
|
||||
}
|
||||
|
||||
$current = $value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use Grav\Common\GravTrait;
|
||||
use Symfony\Component\Yaml\Exception\ParseException;
|
||||
use Symfony\Component\Yaml\Parser;
|
||||
@@ -30,6 +31,11 @@ class Validation
|
||||
return;
|
||||
}
|
||||
|
||||
// special case for files, value is never empty and errors with code 4 instead
|
||||
if (empty($validate['required']) && $field['type'] == 'file' && (isset($value['error']) && ($value['error'] == UPLOAD_ERR_NO_FILE || in_array(UPLOAD_ERR_NO_FILE, $value['error'])))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get language class
|
||||
$language = self::getGrav()['language'];
|
||||
|
||||
@@ -37,7 +43,7 @@ class Validation
|
||||
$type = (string) isset($field['validate']['type']) ? $field['validate']['type'] : $field['type'];
|
||||
$method = 'type'.strtr($type, '-', '_');
|
||||
$name = ucfirst(isset($field['label']) ? $field['label'] : $field['name']);
|
||||
$message = (string) isset($field['validate']['message']) ? $field['validate']['message'] : 'Invalid input in "' . $language->translate($name) . '""';
|
||||
$message = (string) isset($field['validate']['message']) ? $language->translate($field['validate']['message']) : $language->translate('FORM.INVALID_INPUT', null, true) . ' "' . $language->translate($name) . '"';
|
||||
|
||||
if (method_exists(__CLASS__, $method)) {
|
||||
$success = self::$method($value, $validate, $field);
|
||||
@@ -77,6 +83,11 @@ class Validation
|
||||
return null;
|
||||
}
|
||||
|
||||
// special case for files, value is never empty and errors with code 4 instead
|
||||
if (empty($validate['required']) && $field['type'] == 'file' && (isset($value['error']) && ($value['error'] == UPLOAD_ERR_NO_FILE || in_array(UPLOAD_ERR_NO_FILE, $value['error'])))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// if this is a YAML field, simply parse it and return the value
|
||||
if (isset($field['yaml']) && $field['yaml'] === true) {
|
||||
try {
|
||||
@@ -257,6 +268,24 @@ class Validation
|
||||
return self::typeArray((array) $value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom input: file
|
||||
*
|
||||
* @param mixed $value Value to be validated.
|
||||
* @param array $params Validation parameters.
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeFile($value, array $params, array $field)
|
||||
{
|
||||
return self::typeArray((array) $value, $params, $field);
|
||||
}
|
||||
|
||||
protected static function filterFile($value, array $params, array $field)
|
||||
{
|
||||
return (array) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 input: select
|
||||
*
|
||||
@@ -588,6 +617,10 @@ class Validation
|
||||
|
||||
public static function validateRequired($value, $params)
|
||||
{
|
||||
if (is_string($value)) {
|
||||
$value = trim($value);
|
||||
}
|
||||
|
||||
return (bool) $params !== true || !empty($value);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ class Debugger
|
||||
protected $renderer;
|
||||
protected $enabled;
|
||||
|
||||
protected $timers = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->debugbar = new StandardDebugBar();
|
||||
@@ -98,15 +100,17 @@ class Debugger
|
||||
{
|
||||
if ($name[0] == '_' || $this->grav['config']->get('system.debugger.enabled')) {
|
||||
$this->debugbar['time']->startMeasure($name, $description);
|
||||
$this->timers[] = $name;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function stopTimer($name)
|
||||
{
|
||||
if ($name[0] == '_' || $this->grav['config']->get('system.debugger.enabled')) {
|
||||
if (in_array($name, $this->timers) && ($name[0] == '_' || $this->grav['config']->get('system.debugger.enabled'))) {
|
||||
$this->debugbar['time']->stopMeasure($name);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use Whoops\Run;
|
||||
* Class Debugger
|
||||
* @package Grav\Common
|
||||
*/
|
||||
class Errors extends \Whoops\Run
|
||||
class Errors extends Run
|
||||
{
|
||||
|
||||
public function pushHandler($handler, $key = null)
|
||||
|
||||
@@ -47,7 +47,11 @@ trait CompiledFile
|
||||
|| $cache['filename'] != $this->filename
|
||||
) {
|
||||
// Attempt to lock the file for writing.
|
||||
$file->lock(false);
|
||||
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());
|
||||
@@ -64,6 +68,7 @@ trait CompiledFile
|
||||
$file->unlock();
|
||||
}
|
||||
}
|
||||
$file->free();
|
||||
|
||||
$this->content = $cache['data'];
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@ abstract class Folder
|
||||
{
|
||||
$last_modified = 0;
|
||||
|
||||
$dirItr = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
$filterItr = new RecursiveFolderFilterIterator($dirItr);
|
||||
$itr = new \RecursiveIteratorIterator($filterItr, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
$directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
$filter = new RecursiveFolderFilterIterator($directory);
|
||||
$iterator = new \RecursiveIteratorIterator($filter, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
|
||||
/** @var \RecursiveDirectoryIterator $file */
|
||||
foreach ($itr as $dir) {
|
||||
foreach ($iterator as $dir) {
|
||||
$dir_modified = $dir->getMTime();
|
||||
if ($dir_modified > $last_modified) {
|
||||
$last_modified = $dir_modified;
|
||||
@@ -46,12 +46,12 @@ abstract class Folder
|
||||
{
|
||||
$last_modified = 0;
|
||||
|
||||
$dirItr = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
$itrItr = new \RecursiveIteratorIterator($dirItr, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
$itr = new \RegexIterator($itrItr, '/^.+\.'.$extensions.'$/i');
|
||||
$directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
$recursive = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
$iterator = new \RegexIterator($recursive, '/^.+\.'.$extensions.'$/i');
|
||||
|
||||
/** @var \RecursiveDirectoryIterator $file */
|
||||
foreach ($itr as $filepath => $file) {
|
||||
foreach ($iterator as $filepath => $file) {
|
||||
$file_modified = $file->getMTime();
|
||||
if ($file_modified > $last_modified) {
|
||||
$last_modified = $file_modified;
|
||||
@@ -81,6 +81,43 @@ abstract class Folder
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get relative path between target and base path. If path isn't relative, return full path.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $base
|
||||
* @return string
|
||||
*/
|
||||
public static function getRelativePathDotDot($path, $base)
|
||||
{
|
||||
$base = preg_replace('![\\\/]+!', '/', $base);
|
||||
$path = preg_replace('![\\\/]+!', '/', $path);
|
||||
|
||||
if ($path === $base) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$baseParts = explode('/', isset($base[0]) && '/' === $base[0] ? substr($base, 1) : $base);
|
||||
$pathParts = explode('/', isset($path[0]) && '/' === $path[0] ? substr($path, 1) : $path);
|
||||
|
||||
array_pop($baseParts);
|
||||
$lastPart = array_pop($pathParts);
|
||||
foreach ($baseParts as $i => $directory) {
|
||||
if (isset($pathParts[$i]) && $pathParts[$i] === $directory) {
|
||||
unset($baseParts[$i], $pathParts[$i]);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$pathParts[] = $lastPart;
|
||||
$path = str_repeat('../', count($baseParts)) . implode('/', $pathParts);
|
||||
|
||||
return '' === $path
|
||||
|| '/' === $path[0]
|
||||
|| false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos)
|
||||
? "./$path" : $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shift first directory out of the path.
|
||||
*
|
||||
@@ -96,8 +133,6 @@ abstract class Folder
|
||||
return $result ?: null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return recursive list of all files and directories under given path.
|
||||
*
|
||||
@@ -116,13 +151,17 @@ abstract class Folder
|
||||
$pattern = isset($params['pattern']) ? $params['pattern'] : null;
|
||||
$filters = isset($params['filters']) ? $params['filters'] : null;
|
||||
$recursive = isset($params['recursive']) ? $params['recursive'] : true;
|
||||
$levels = isset($params['levels']) ? $params['levels'] : -1;
|
||||
$key = isset($params['key']) ? 'get' . $params['key'] : null;
|
||||
$value = isset($params['value']) ? 'get' . $params['value'] : ($recursive ? 'getSubPathname' : 'getFilename');
|
||||
$folders = isset($params['folders']) ? $params['folders'] : true;
|
||||
$files = isset($params['files']) ? $params['files'] : true;
|
||||
|
||||
if ($recursive) {
|
||||
$directory = new \RecursiveDirectoryIterator($path,
|
||||
\RecursiveDirectoryIterator::SKIP_DOTS + \FilesystemIterator::UNIX_PATHS + \FilesystemIterator::CURRENT_AS_SELF);
|
||||
$iterator = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
$iterator->setMaxDepth(max($levels, -1));
|
||||
} else {
|
||||
$iterator = new \FilesystemIterator($path);
|
||||
}
|
||||
@@ -131,6 +170,16 @@ abstract class Folder
|
||||
|
||||
/** @var \RecursiveDirectoryIterator $file */
|
||||
foreach ($iterator as $file) {
|
||||
// Ignore hidden files.
|
||||
if ($file->getFilename()[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
if (!$folders && $file->isDir()) {
|
||||
continue;
|
||||
}
|
||||
if (!$files && $file->isFile()) {
|
||||
continue;
|
||||
}
|
||||
if ($compare && $pattern && !preg_match($pattern, $file->{$compare}())) {
|
||||
continue;
|
||||
}
|
||||
@@ -138,7 +187,8 @@ abstract class Folder
|
||||
$filePath = $file->{$value}();
|
||||
if ($filters) {
|
||||
if (isset($filters['key'])) {
|
||||
$fileKey = preg_replace($filters['key'], '', $fileKey);
|
||||
$pre = !empty($filters['pre-key']) ? $filters['pre-key'] : '';
|
||||
$fileKey = $pre . preg_replace($filters['key'], '', $fileKey);
|
||||
}
|
||||
if (isset($filters['value'])) {
|
||||
$filter = $filters['value'];
|
||||
@@ -146,12 +196,12 @@ abstract class Folder
|
||||
$filePath = call_user_func($filter, $file);
|
||||
} else {
|
||||
$filePath = preg_replace($filter, '', $filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($fileKey !== null) {
|
||||
$results[$fileKey] = $filePath;
|
||||
$results[$fileKey] = $filePath;
|
||||
} else {
|
||||
$results[] = $filePath;
|
||||
}
|
||||
@@ -163,11 +213,12 @@ abstract class Folder
|
||||
/**
|
||||
* Recursively copy directory in filesystem.
|
||||
*
|
||||
* @param string $source
|
||||
* @param string $target
|
||||
* @param string $source
|
||||
* @param string $target
|
||||
* @param string $ignore Ignore files matching pattern (regular expression).
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function copy($source, $target)
|
||||
public static function copy($source, $target, $ignore = null)
|
||||
{
|
||||
$source = rtrim($source, '\\/');
|
||||
$target = rtrim($target, '\\/');
|
||||
@@ -177,19 +228,24 @@ abstract class Folder
|
||||
}
|
||||
|
||||
// Make sure that path to the target exists before copying.
|
||||
self::mkdir($target);
|
||||
self::create($target);
|
||||
|
||||
$success = true;
|
||||
|
||||
// Go through all sub-directories and copy everything.
|
||||
$files = self::all($source);
|
||||
foreach ($files as $file) {
|
||||
if ($ignore && preg_match($ignore, $file)) {
|
||||
continue;
|
||||
}
|
||||
$src = $source .'/'. $file;
|
||||
$dst = $target .'/'. $file;
|
||||
|
||||
if (is_dir($src)) {
|
||||
// Create current directory.
|
||||
$success &= @mkdir($dst);
|
||||
// Create current directory (if it doesn't exist).
|
||||
if (!is_dir($dst)) {
|
||||
$success &= @mkdir($dst, 0777, true);
|
||||
}
|
||||
} else {
|
||||
// Or copy current file.
|
||||
$success &= @copy($src, $dst);
|
||||
@@ -208,8 +264,8 @@ abstract class Folder
|
||||
/**
|
||||
* Move directory in filesystem.
|
||||
*
|
||||
* @param string $source
|
||||
* @param string $target
|
||||
* @param string $source
|
||||
* @param string $target
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function move($source, $target)
|
||||
@@ -218,8 +274,13 @@ abstract class Folder
|
||||
throw new \RuntimeException('Cannot move non-existing folder.');
|
||||
}
|
||||
|
||||
// Don't do anything if the source is the same as the new target
|
||||
if ($source == $target) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure that path to the target exists before moving.
|
||||
self::mkdir(dirname($target));
|
||||
self::create(dirname($target));
|
||||
|
||||
// Just rename the directory.
|
||||
$success = @rename($source, $target);
|
||||
@@ -238,16 +299,16 @@ abstract class Folder
|
||||
* Recursively delete directory from filesystem.
|
||||
*
|
||||
* @param string $target
|
||||
* @throws \RuntimeException
|
||||
* @param bool $include_target
|
||||
* @return bool
|
||||
*/
|
||||
public static function delete($target)
|
||||
public static function delete($target, $include_target = true)
|
||||
{
|
||||
if (!is_dir($target)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
$success = self::doDelete($target);
|
||||
$success = self::doDelete($target, $include_target);
|
||||
|
||||
if (!$success) {
|
||||
$error = error_get_last();
|
||||
@@ -255,16 +316,31 @@ abstract class Folder
|
||||
}
|
||||
|
||||
// Make sure that the change will be detected when caching.
|
||||
@touch(dirname($target));
|
||||
if ($include_target) {
|
||||
@touch(dirname($target));
|
||||
} else {
|
||||
@touch($target);
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $folder
|
||||
* @param string $folder
|
||||
* @throws \RuntimeException
|
||||
* @internal
|
||||
*/
|
||||
public static function mkdir($folder)
|
||||
{
|
||||
self::create($folder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $folder
|
||||
* @throws \RuntimeException
|
||||
* @internal
|
||||
*/
|
||||
public static function create($folder)
|
||||
{
|
||||
if (is_dir($folder)) {
|
||||
return;
|
||||
@@ -320,10 +396,11 @@ abstract class Folder
|
||||
|
||||
/**
|
||||
* @param string $folder
|
||||
* @param bool $include_target
|
||||
* @return bool
|
||||
* @internal
|
||||
*/
|
||||
protected static function doDelete($folder)
|
||||
protected static function doDelete($folder, $include_target = true)
|
||||
{
|
||||
// Special case for symbolic links.
|
||||
if (is_link($folder)) {
|
||||
@@ -338,16 +415,16 @@ abstract class Folder
|
||||
/** @var \DirectoryIterator $fileinfo */
|
||||
foreach ($files as $fileinfo) {
|
||||
if ($fileinfo->isDir()) {
|
||||
if (false === rmdir($fileinfo->getRealPath())) {
|
||||
if (false === @rmdir($fileinfo->getRealPath())) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (false === unlink($fileinfo->getRealPath())) {
|
||||
if (false === @unlink($fileinfo->getRealPath())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rmdir($folder);
|
||||
return $include_target ? @rmdir($folder) : true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
use Grav\Common\GPM\Common\AbstractPackageCollection as BaseCollection;
|
||||
use Grav\Common\GPM\Local\Package;
|
||||
|
||||
abstract class AbstractPackageCollection extends BaseCollection {
|
||||
|
||||
abstract class AbstractPackageCollection extends BaseCollection
|
||||
{
|
||||
public function __construct($items)
|
||||
{
|
||||
foreach ($items as $name => $data) {
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\Data\Data;
|
||||
|
||||
/**
|
||||
* Interface Package
|
||||
* @package Grav\Common\GPM
|
||||
*/
|
||||
class Package
|
||||
{
|
||||
/**
|
||||
* @var Data
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @var \Grav\Common\Data\Blueprint
|
||||
*/
|
||||
protected $blueprints;
|
||||
|
||||
/**
|
||||
* @param Data $package
|
||||
* @param bool $package_type
|
||||
*/
|
||||
public function __construct(Data $package, $package_type = false);
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function isEnabled();
|
||||
|
||||
/**
|
||||
* @return Data
|
||||
*/
|
||||
public function getData();
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($key);
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function toJson();
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray();
|
||||
}
|
||||
@@ -3,7 +3,6 @@ namespace Grav\Common\GPM\Remote;
|
||||
|
||||
use Grav\Common\GPM\Common\AbstractPackageCollection as BaseCollection;
|
||||
use Grav\Common\GPM\Response;
|
||||
|
||||
use \Doctrine\Common\Cache\FilesystemCache;
|
||||
|
||||
class AbstractPackageCollection extends BaseCollection
|
||||
|
||||
@@ -55,7 +55,7 @@ class Grav extends AbstractPackageCollection
|
||||
|
||||
$diffLog = [];
|
||||
foreach ($this->data['changelog'] as $version => $changelog) {
|
||||
preg_match("/[\d\.]+/", $version, $cleanVersion);
|
||||
preg_match("/[\w-\.]+/", $version, $cleanVersion);
|
||||
|
||||
if (!$cleanVersion || version_compare($diff, $cleanVersion[0], ">=")) { continue; }
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ class Plugins extends AbstractPackageCollection
|
||||
|
||||
/**
|
||||
* Local Plugins Constructor
|
||||
* @param bool $refresh
|
||||
* @param callable $callback Either a function or callback in array notation
|
||||
*/
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
|
||||
@@ -16,6 +16,8 @@ class Themes extends AbstractPackageCollection
|
||||
|
||||
/**
|
||||
* Local Themes Constructor
|
||||
* @param bool $refresh
|
||||
* @param callable $callback Either a function or callback in array notation
|
||||
*/
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\Utils;
|
||||
|
||||
class Response
|
||||
{
|
||||
/**
|
||||
@@ -50,6 +52,7 @@ class Response
|
||||
/**
|
||||
* Sets the preferred method to use for making HTTP calls.
|
||||
* @param string $method Default is `auto`
|
||||
* @return Response
|
||||
*/
|
||||
public static function setMethod($method = 'auto')
|
||||
{
|
||||
@@ -64,8 +67,9 @@ class Response
|
||||
|
||||
/**
|
||||
* Makes a request to the URL by using the preferred method
|
||||
* @param string $uri URL to call
|
||||
* @param array $options An array of parameters for both `curl` and `fopen`
|
||||
* @param string $uri URL to call
|
||||
* @param array $options An array of parameters for both `curl` and `fopen`
|
||||
* @param callable $callback Either a function or callback in array notation
|
||||
* @return string The response of the request
|
||||
*/
|
||||
public static function get($uri = '', $options = [], $callback = null)
|
||||
@@ -74,6 +78,13 @@ class Response
|
||||
throw new \RuntimeException('Could not start an HTTP request. `allow_url_open` is disabled and `cURL` is not available');
|
||||
}
|
||||
|
||||
// check if this function is available, if so use it to stop any timeouts
|
||||
try {
|
||||
if (!Utils::isFunctionDisabled('set_time_limit') && !ini_get('safe_mode') && function_exists('set_time_limit')) {
|
||||
set_time_limit(0);
|
||||
}
|
||||
} catch (\Exception $e) {}
|
||||
|
||||
$options = array_replace_recursive(self::$defaults, $options);
|
||||
$method = 'get' . ucfirst(strtolower(self::$method));
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ class Grav extends Container
|
||||
/** @var Uri $uri */
|
||||
$uri = $c['uri'];
|
||||
|
||||
$path = rtrim($uri->path(), '/');
|
||||
$path = $uri->path(); // Don't trim to support trailing slash default routes
|
||||
$path = $path ?: '/';
|
||||
|
||||
$page = $pages->dispatch($path);
|
||||
@@ -201,7 +201,10 @@ class Grav extends Container
|
||||
// Use output buffering to prevent headers from being sent too early.
|
||||
ob_start();
|
||||
if ($this['config']->get('system.cache.gzip')) {
|
||||
ob_start('ob_gzhandler');
|
||||
// Enable zip/deflate with a fallback in case of if browser does not support compressing.
|
||||
if(!ob_start("ob_gzhandler")) {
|
||||
ob_start();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the timezone
|
||||
@@ -227,7 +230,6 @@ class Grav extends Container
|
||||
|
||||
$debugger->startTimer('themes', 'Themes');
|
||||
$this['themes']->init();
|
||||
$this->fireEvent('onThemeInitialized');
|
||||
$debugger->stopTimer('themes');
|
||||
|
||||
$task = $this['task'];
|
||||
@@ -296,7 +298,10 @@ class Grav extends Container
|
||||
if ($uri->isExternal($route)) {
|
||||
$url = $route;
|
||||
} else {
|
||||
$url = rtrim($uri->rootUrl(), '/') .'/'. trim($route, '/');
|
||||
if ($this['config']->get('system.pages.redirect_trailing_slash', true))
|
||||
$url = rtrim($uri->rootUrl(), '/') .'/'. trim($route, '/'); // Remove trailing slash
|
||||
else
|
||||
$url = rtrim($uri->rootUrl(), '/') .'/'. ltrim($route, '/'); // Support trailing slash default routes
|
||||
}
|
||||
|
||||
header("Location: {$url}", true, $code);
|
||||
@@ -413,22 +418,25 @@ class Grav extends Container
|
||||
public function shutdown()
|
||||
{
|
||||
if ($this['config']->get('system.debugger.shutdown.close_connection')) {
|
||||
//stop user abort
|
||||
// Prevent user abort.
|
||||
if (function_exists('ignore_user_abort')) {
|
||||
@ignore_user_abort(true);
|
||||
}
|
||||
|
||||
// close the session
|
||||
// Close the session.
|
||||
if (isset($this['session'])) {
|
||||
$this['session']->close();
|
||||
}
|
||||
|
||||
// flush buffer if gzip buffer was started
|
||||
if ($this['config']->get('system.cache.gzip')) {
|
||||
ob_end_flush(); // gzhandler buffer
|
||||
// Flush gzhandler buffer if gzip was enabled.
|
||||
ob_end_flush();
|
||||
} else {
|
||||
// Otherwise prevent server from compressing the output.
|
||||
header('Content-Encoding: none');
|
||||
}
|
||||
|
||||
// get lengh and close the connection
|
||||
// Get length and close the connection.
|
||||
header('Content-Length: ' . ob_get_length());
|
||||
header("Connection: close");
|
||||
|
||||
@@ -437,7 +445,7 @@ class Grav extends Container
|
||||
@ob_flush();
|
||||
flush();
|
||||
|
||||
// fix for fastcgi close connection issue
|
||||
// Fix for fastcgi close connection issue.
|
||||
if (function_exists('fastcgi_finish_request')) {
|
||||
@fastcgi_finish_request();
|
||||
}
|
||||
@@ -457,6 +465,20 @@ class Grav extends Container
|
||||
/** @var Uri $uri */
|
||||
$uri = $this['uri'];
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $this['config'];
|
||||
|
||||
$uri_extension = $uri->extension();
|
||||
$fallback_types = $config->get('system.media.allowed_fallback_types', null);
|
||||
$supported_types = $config->get('media');
|
||||
|
||||
// Check whitelist first, then ensure extension is a valid media type
|
||||
if (!empty($fallback_types) && !in_array($uri_extension, $fallback_types)) {
|
||||
return;
|
||||
} elseif (!array_key_exists($uri_extension, $supported_types)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$path_parts = pathinfo($path);
|
||||
$page = $this['pages']->dispatch($path_parts['dirname'], true);
|
||||
if ($page) {
|
||||
@@ -478,7 +500,6 @@ class Grav extends Container
|
||||
}
|
||||
|
||||
// unsupported media type, try to download it...
|
||||
$uri_extension = $uri->extension();
|
||||
if ($uri_extension) {
|
||||
$extension = $uri_extension;
|
||||
} else {
|
||||
@@ -491,7 +512,7 @@ class Grav extends Container
|
||||
|
||||
if ($extension) {
|
||||
$download = true;
|
||||
if (in_array(ltrim($extension, '.'), $this['config']->get('system.media.unsupported_inline_types', []))) {
|
||||
if (in_array(ltrim($extension, '.'), $config->get('system.media.unsupported_inline_types', []))) {
|
||||
$download = false;
|
||||
}
|
||||
Utils::download($page->path() . DIRECTORY_SEPARATOR . $uri->basename(), $download);
|
||||
|
||||
@@ -131,7 +131,7 @@ class Truncator {
|
||||
$inner .= $txt;
|
||||
if ($remaining < 0) {
|
||||
if (static::ellipsable($node)) {
|
||||
$inner = preg_replace('/(?:[\s\pP]+|(?:&(?:[a-z]+|#[0-9]+);?))*$/', '', $inner).$opts['ellipsis'];
|
||||
$inner = preg_replace('/(?:[\s\pP]+|(?:&(?:[a-z]+|#[0-9]+);?))*$/u', '', $inner).$opts['ellipsis'];
|
||||
$opts['ellipsis'] = '';
|
||||
$opts['was_truncated'] = true;
|
||||
}
|
||||
|
||||
@@ -215,4 +215,26 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sorts elements from the list and returns a copy of the list in the proper order
|
||||
*
|
||||
* @param callable|null $callback
|
||||
*
|
||||
* @param bool $desc
|
||||
*
|
||||
* @return $this|array
|
||||
* @internal param bool $asc
|
||||
*
|
||||
*/
|
||||
public function sort(callable $callback = null, $desc = false)
|
||||
{
|
||||
if (!$callback || !is_callable($callback)) { return $this; }
|
||||
|
||||
$items = $this->items;
|
||||
uasort($items, $callback);
|
||||
|
||||
return !$desc ? $items : array_reverse($items, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ class Language
|
||||
*/
|
||||
public function setActiveFromUri($uri)
|
||||
{
|
||||
$regex = '/(^\/(' . $this->getAvailable() . ')).*/';
|
||||
$regex = '/(^\/(' . $this->getAvailable() . '))(?:\/.*|$)/i';
|
||||
|
||||
// if languages set
|
||||
if ($this->enabled()) {
|
||||
@@ -352,7 +352,7 @@ class Language
|
||||
if ($this->config->get('system.languages.translations_fallback', true)) {
|
||||
$languages = $this->getFallbackLanguages();
|
||||
} else {
|
||||
$languages = (array)$this->getDefault();
|
||||
$languages = (array)$this->getLanguage();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -193,7 +193,7 @@ class LanguageCodes
|
||||
],
|
||||
"fr" => [
|
||||
"name" => "French",
|
||||
"nativeName" => "français"
|
||||
"nativeName" => "Français"
|
||||
],
|
||||
"ff" => [
|
||||
"name" => "Fula",
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
<?php
|
||||
namespace Grav\Common\Markdown;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Debugger;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Page\Medium\Medium;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Utils;
|
||||
|
||||
/**
|
||||
* A trait to add some custom processing to the identifyLink() method in Parsedown and ParsedownExtra
|
||||
@@ -208,10 +204,52 @@ trait ParsedownGravTrait
|
||||
if (isset($excerpt['element']['attributes']['href'])) {
|
||||
$url = parse_url(htmlspecialchars_decode($excerpt['element']['attributes']['href']));
|
||||
|
||||
// if there is a query, then parse it and build action calls
|
||||
if (isset($url['query'])) {
|
||||
$actions = array_reduce(explode('&', $url['query']), function ($carry, $item) {
|
||||
$parts = explode('=', $item, 2);
|
||||
$value = isset($parts[1]) ? $parts[1] : null;
|
||||
$carry[$parts[0]] = $value;
|
||||
|
||||
return $carry;
|
||||
}, []);
|
||||
|
||||
// valid attributes supported
|
||||
$valid_attributes = ['rel', 'target', 'id', 'class', 'classes'];
|
||||
|
||||
// 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
|
||||
foreach ($actions as $attrib => $value) {
|
||||
$key = $attrib;
|
||||
|
||||
if (in_array($attrib, $valid_attributes)) {
|
||||
// support both class and classes
|
||||
if ($attrib == 'classes') {
|
||||
$attrib = 'class';
|
||||
}
|
||||
$excerpt['element']['attributes'][$attrib] = $value;
|
||||
unset($actions[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$url['query']= http_build_query($actions, null, '&', PHP_QUERY_RFC3986);
|
||||
}
|
||||
|
||||
// if no query elements left, unset query
|
||||
if (empty($url['query'])) {
|
||||
unset ($url['query']);
|
||||
}
|
||||
|
||||
|
||||
// if there is no scheme, the file is local
|
||||
if (!isset($url['scheme']) && (count($url) > 0)) {
|
||||
// convert the URl is required
|
||||
$excerpt['element']['attributes']['href'] = Uri::convertUrl($this->page, Uri::buildUrl($url), $type);
|
||||
$excerpt['element']['attributes']['href'] = Uri::convertUrl($this->page, Uri::buildUrl($url), $type, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,18 @@ class Collection extends Iterator
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single page to a collection
|
||||
*
|
||||
* @param Page $page
|
||||
* @return $this
|
||||
*/
|
||||
public function addPage(Page $page)
|
||||
{
|
||||
$this->items[$page->path()] = ['slug' => $page->slug()];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Create a copy of this collection
|
||||
@@ -96,6 +108,7 @@ class Collection extends Iterator
|
||||
* Remove item from the list.
|
||||
*
|
||||
* @param Page|string|null $key
|
||||
* @return $this|void
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function remove($key = null)
|
||||
@@ -110,6 +123,7 @@ class Collection extends Iterator
|
||||
}
|
||||
|
||||
parent::remove($key);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -439,6 +453,55 @@ class Collection extends Iterator
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only pages of one of the specified access levels
|
||||
*
|
||||
* @return Collection The collection
|
||||
*/
|
||||
public function ofOneOfTheseAccessLevels($accessLevels)
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
|
||||
if ($page !== null && isset($page->header()->access)) {
|
||||
if (is_array($page->header()->access)) {
|
||||
//Multiple values for access
|
||||
$valid = false;
|
||||
|
||||
foreach ($page->header()->access as $index => $accessLevel) {
|
||||
if (is_array($accessLevel)) {
|
||||
foreach($accessLevel as $innerIndex => $innerAccessLevel) {
|
||||
if (in_array($innerAccessLevel, $accessLevels)) {
|
||||
$valid = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (in_array($index, $accessLevels)) {
|
||||
$valid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($valid) {
|
||||
$items[$path] = $slug;
|
||||
}
|
||||
} else {
|
||||
//Single value for access
|
||||
if (in_array($page->header()->access, $accessLevels)) {
|
||||
$items[$path] = $slug;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$this->items = $items;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
use Grav\Common\Getters;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Page\Medium\Medium;
|
||||
use Grav\Common\Page\Medium\MediumFactory;
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Data\Data;
|
||||
|
||||
class AudioMedium extends Medium
|
||||
{
|
||||
use StaticResizeTrait;
|
||||
|
||||
@@ -4,11 +4,20 @@ namespace Grav\Common\Page\Medium;
|
||||
use Grav\Common\GravTrait;
|
||||
use Gregwar\Image\Exceptions\GenerationError;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use Gregwar\Image\Image;
|
||||
|
||||
class ImageFile extends \Gregwar\Image\Image
|
||||
class ImageFile extends Image
|
||||
{
|
||||
use GravTrait;
|
||||
|
||||
/**
|
||||
* Clear previously applied operations
|
||||
*/
|
||||
public function clearOperations()
|
||||
{
|
||||
$this->operations = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the same as the Gregwar Image class except this one fires a Grav Event on creation of new cached file
|
||||
*
|
||||
@@ -46,6 +55,7 @@ class ImageFile extends \Gregwar\Image\Image
|
||||
$cacheFile .= $this->prettyName;
|
||||
}
|
||||
|
||||
|
||||
$cacheFile .= '.'.$type;
|
||||
|
||||
// If the files does not exists, save it
|
||||
@@ -70,7 +80,9 @@ class ImageFile extends \Gregwar\Image\Image
|
||||
|
||||
// Asking the cache for the cacheFile
|
||||
try {
|
||||
$file = $this->cache->getOrCreateFile($cacheFile, $conditions, $generate, $actual);
|
||||
$perms = self::getGrav()['config']->get('system.images.cache_perms', '0755');
|
||||
$perms = octdec($perms);
|
||||
$file = $this->cache->setDirectoryMode($perms)->getOrCreateFile($cacheFile, $conditions, $generate, $actual);
|
||||
} catch (GenerationError $e) {
|
||||
$file = $e->getNewFile();
|
||||
}
|
||||
|
||||
@@ -52,7 +52,6 @@ class ImageMedium extends Medium
|
||||
'forceResize' => [ 0, 1 ],
|
||||
'cropResize' => [ 0, 1 ],
|
||||
'crop' => [ 0, 1, 2, 3 ],
|
||||
'cropResize' => [ 0, 1 ],
|
||||
'zoomCrop' => [ 0, 1 ]
|
||||
];
|
||||
|
||||
@@ -136,7 +135,7 @@ class ImageMedium extends Medium
|
||||
*/
|
||||
public function url($reset = true)
|
||||
{
|
||||
$output = preg_replace('|^' . GRAV_ROOT . '|', '', $this->saveImage());
|
||||
$output = preg_replace('|^' . preg_quote(GRAV_ROOT) . '|', '', $this->saveImage());
|
||||
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
@@ -260,6 +259,7 @@ class ImageMedium extends Medium
|
||||
|
||||
if ($this->image) {
|
||||
$this->image();
|
||||
$this->image->clearOperations(); // Clear previously applied operations
|
||||
$this->filter();
|
||||
}
|
||||
|
||||
@@ -311,19 +311,23 @@ class ImageMedium extends Medium
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the quality of the image
|
||||
* Sets or gets the quality of the image
|
||||
*
|
||||
* @param int $quality 0-100 quality
|
||||
* @return Medium
|
||||
*/
|
||||
public function quality($quality)
|
||||
public function quality($quality = null)
|
||||
{
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
if ($quality) {
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
}
|
||||
|
||||
$this->quality = $quality;
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->quality = $quality;
|
||||
return $this;
|
||||
return $this->quality;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -359,6 +363,48 @@ class ImageMedium extends Medium
|
||||
return empty($this->sizes) ? '100vw' : $this->sizes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the width attribute from Markdown or Twig
|
||||
* Examples: 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* {{ page.media['myimg.png'].width().height().html }}
|
||||
* {{ page.media['myimg.png'].resize(100,200).width(100).height(200).html }}
|
||||
*
|
||||
* @param mixed $value A value or 'auto' or empty to use the width of the image
|
||||
* @return $this
|
||||
*/
|
||||
public function width($value = 'auto')
|
||||
{
|
||||
if (!$value || $value == 'auto')
|
||||
$this->attributes['width'] = $this->get('width');
|
||||
else
|
||||
$this->attributes['width'] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the height attribute from Markdown or Twig
|
||||
* Examples: 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* {{ page.media['myimg.png'].width().height().html }}
|
||||
* {{ page.media['myimg.png'].resize(100,200).width(100).height(200).html }}
|
||||
*
|
||||
* @param mixed $value A value or 'auto' or empty to use the height of the image
|
||||
* @return $this
|
||||
*/
|
||||
public function height($value = 'auto')
|
||||
{
|
||||
if (!$value || $value == 'auto')
|
||||
$this->attributes['height'] = $this->get('height');
|
||||
else
|
||||
$this->attributes['height'] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward the call to the image processing method.
|
||||
*
|
||||
@@ -470,4 +516,28 @@ class ImageMedium extends Medium
|
||||
$this->__call($method, $params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the image higher quality version
|
||||
*
|
||||
* @return ImageMedium the alternative version with higher quality
|
||||
*/
|
||||
public function higherQualityAlternative()
|
||||
{
|
||||
if ($this->alternatives) {
|
||||
$max = reset($this->alternatives);
|
||||
foreach($this->alternatives as $alternative)
|
||||
{
|
||||
if($alternative->quality() > $max->quality())
|
||||
{
|
||||
$max = $alternative;
|
||||
}
|
||||
}
|
||||
|
||||
return $max;
|
||||
} else {
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ class Medium extends Data implements RenderableInterface
|
||||
*/
|
||||
public function url($reset = true)
|
||||
{
|
||||
$output = preg_replace('|^' . GRAV_ROOT . '|', '', $this->get('filepath'));
|
||||
$output = preg_replace('|^' . preg_quote(GRAV_ROOT) . '|', '', $this->get('filepath'));
|
||||
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
@@ -209,9 +209,14 @@ class Medium extends Data implements RenderableInterface
|
||||
|
||||
$style = '';
|
||||
foreach ($this->styleAttributes as $key => $value) {
|
||||
$style .= $key . ': ' . $value . ';';
|
||||
if (is_numeric($key)) // Special case for inline style attributes, refer to style() method
|
||||
$style .= $value;
|
||||
else
|
||||
$style .= $key . ': ' . $value . ';';
|
||||
}
|
||||
if ($style) {
|
||||
$attributes['style'] = $style;
|
||||
}
|
||||
$attributes['style'] = $style;
|
||||
|
||||
if (empty($attributes['title'])) {
|
||||
if (!empty($title)) {
|
||||
@@ -388,6 +393,51 @@ class Medium extends Data implements RenderableInterface
|
||||
return $this->link($reset, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class to the element from Markdown or Twig
|
||||
* Example:  or 
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function classes()
|
||||
{
|
||||
$classes = func_get_args();
|
||||
if (!empty($classes)) {
|
||||
$this->attributes['class'] = implode(',', (array)$classes);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an id to the element from Markdown or Twig
|
||||
* Example: 
|
||||
*
|
||||
* @param $id
|
||||
* @return $this
|
||||
*/
|
||||
public function id($id)
|
||||
{
|
||||
if (is_string($id)) {
|
||||
$this->attributes['id'] = trim($id);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to add an inline style attribute from Markdown or Twig
|
||||
* Example: 
|
||||
*
|
||||
* @param string $style
|
||||
* @return $this
|
||||
*/
|
||||
public function style($style)
|
||||
{
|
||||
$this->styleAttributes[] = rtrim($style, ';') . ';';
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow any action to be called on this medium from twig or markdown
|
||||
*
|
||||
@@ -440,4 +490,5 @@ class Medium extends Data implements RenderableInterface
|
||||
|
||||
return $this->_thumbnail;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Data\Data;
|
||||
|
||||
class VideoMedium extends Medium
|
||||
{
|
||||
use StaticResizeTrait;
|
||||
|
||||
@@ -5,6 +5,7 @@ use Exception;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Language\Language;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Common\Cache;
|
||||
use Grav\Common\Twig;
|
||||
@@ -41,6 +42,7 @@ class Page
|
||||
protected $folder;
|
||||
protected $path;
|
||||
protected $extension;
|
||||
protected $url_extension;
|
||||
|
||||
protected $id;
|
||||
protected $parent;
|
||||
@@ -128,6 +130,7 @@ class Page
|
||||
$this->modularTwig($this->slug[0] == '_');
|
||||
$this->setPublishState();
|
||||
$this->published();
|
||||
$this->urlExtension();
|
||||
|
||||
// some extension logic
|
||||
if (empty($extension)) {
|
||||
@@ -136,6 +139,7 @@ class Page
|
||||
$this->extension($extension);
|
||||
}
|
||||
|
||||
|
||||
// extract page language from page extension
|
||||
$language = trim(basename($this->extension(), 'md'), '.') ?: null;
|
||||
$this->language($language);
|
||||
@@ -349,7 +353,6 @@ class Page
|
||||
if (isset($this->header->last_modified)) {
|
||||
$this->last_modified = (bool) $this->header->last_modified;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $this->header;
|
||||
@@ -379,7 +382,7 @@ class Page
|
||||
*/
|
||||
public function modifyHeader($key, $value)
|
||||
{
|
||||
$this->header->$key = $value;
|
||||
$this->header->{$key} = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -390,8 +393,7 @@ class Page
|
||||
*/
|
||||
public function summary($size = null)
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::getGrav()['config']->get('site.summary');
|
||||
$config = (array) self::getGrav()['config']->get('site.summary');
|
||||
if (isset($this->header->summary)) {
|
||||
$config = array_merge($config, $this->header->summary);
|
||||
}
|
||||
@@ -726,27 +728,30 @@ class Page
|
||||
* You need to call $this->save() in order to perform the move.
|
||||
*
|
||||
* @param Page $parent New parent page.
|
||||
* @return Page
|
||||
* @return $this
|
||||
*/
|
||||
public function move(Page $parent)
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->_action = 'move';
|
||||
$clone->_original = $this;
|
||||
$clone->parent($parent);
|
||||
$clone->id(time().md5($clone->filePath()));
|
||||
if (!$this->_original) {
|
||||
$clone = clone $this;
|
||||
$this->_original = $clone;
|
||||
}
|
||||
|
||||
$this->_action = 'move';
|
||||
$this->parent($parent);
|
||||
$this->id(time().md5($this->filePath()));
|
||||
|
||||
if ($parent->path()) {
|
||||
$clone->path($parent->path() . '/' . $clone->folder());
|
||||
$this->path($parent->path() . '/' . $this->folder());
|
||||
}
|
||||
|
||||
if ($parent->route()) {
|
||||
$clone->route($parent->route() . '/'. $clone->slug());
|
||||
$this->route($parent->route() . '/'. $this->slug());
|
||||
} else {
|
||||
$clone->route(self::getGrav()['pages']->root()->route() . '/'. $clone->slug());
|
||||
$this->route(self::getGrav()['pages']->root()->route() . '/'. $this->slug());
|
||||
}
|
||||
|
||||
return $clone;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -756,14 +761,14 @@ class Page
|
||||
* You need to call $this->save() in order to perform the move.
|
||||
*
|
||||
* @param Page $parent New parent page.
|
||||
* @return Page
|
||||
* @return $this
|
||||
*/
|
||||
public function copy($parent)
|
||||
{
|
||||
$clone = $this->move($parent);
|
||||
$clone->_action = 'copy';
|
||||
$this->move($parent);
|
||||
$this->_action = 'copy';
|
||||
|
||||
return $clone;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -824,7 +829,7 @@ class Page
|
||||
$blueprints = $this->blueprints();
|
||||
$values = $blueprints->filter($this->toArray());
|
||||
if ($values && isset($values['header'])) {
|
||||
$this->header($values['header']);
|
||||
$this->header($values['header']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -957,6 +962,17 @@ class Page
|
||||
return $this->extension;
|
||||
}
|
||||
|
||||
public function urlExtension()
|
||||
{
|
||||
// if not set in the page get the value from system config
|
||||
if (empty($this->url_extension)) {
|
||||
$this->url_extension = trim(isset($this->header->append_url_extension) ? $this->header->append_url_extension : self::getGrav()['config']->get('system.pages.append_url_extension', false));
|
||||
}
|
||||
|
||||
return $this->url_extension;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and sets the expires field. If not set will return the default
|
||||
*
|
||||
@@ -1137,7 +1153,7 @@ class Page
|
||||
$this->metadata = [];
|
||||
|
||||
// Set the Generator tag
|
||||
$this->metadata['generator'] = array('name'=>'generator', 'content'=>'GravCMS ' . GRAV_VERSION);
|
||||
$this->metadata['generator'] = array('name'=>'generator', 'content'=>'GravCMS');
|
||||
|
||||
// Get initial metadata for the page
|
||||
$metadata = self::getGrav()['config']->get('site.metadata');
|
||||
@@ -1262,7 +1278,7 @@ class Page
|
||||
|
||||
$rootUrl = $uri->rootUrl($include_host) . $pages->base();
|
||||
|
||||
$url = $rootUrl.'/'.trim($route, '/');
|
||||
$url = $rootUrl.'/'.trim($route, '/') . $this->urlExtension();
|
||||
|
||||
// trim trailing / if not root
|
||||
if ($url !== '/') {
|
||||
@@ -1471,7 +1487,17 @@ class Page
|
||||
*/
|
||||
public function filePathClean()
|
||||
{
|
||||
return str_replace(ROOT_DIR, '', $this->filePath());
|
||||
$path = str_replace(ROOT_DIR, '', $this->filePath());
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the clean path to the page file
|
||||
*/
|
||||
public function relativePagePath()
|
||||
{
|
||||
$path = str_replace('/'.$this->name, '', $this->filePathClean());
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1782,11 +1808,13 @@ class Page
|
||||
|
||||
if (isset($routes[$uri_path])) {
|
||||
$child_page = $pages->dispatch($uri->route())->parent();
|
||||
if ($child_page) while (!$child_page->root()) {
|
||||
if ($this->path() == $child_page->path()) {
|
||||
return true;
|
||||
if ($child_page) {
|
||||
while (!$child_page->root()) {
|
||||
if ($this->path() == $child_page->path()) {
|
||||
return true;
|
||||
}
|
||||
$child_page = $child_page->parent();
|
||||
}
|
||||
$child_page = $child_page->parent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1820,7 +1848,7 @@ class Page
|
||||
/**
|
||||
* Helper method to return a page.
|
||||
*
|
||||
* @param string $url the url of the page
|
||||
* @param string $url the url of the page
|
||||
* @param bool $all
|
||||
*
|
||||
* @return \Grav\Common\Page\Page page you were looking for if it exists
|
||||
@@ -1928,14 +1956,14 @@ class Page
|
||||
* @return mixed
|
||||
* @internal
|
||||
*/
|
||||
protected function evaluate($value)
|
||||
public function evaluate($value)
|
||||
{
|
||||
// Parse command.
|
||||
if (is_string($value)) {
|
||||
// Format: @command.param
|
||||
$cmd = $value;
|
||||
$params = array();
|
||||
} elseif (is_array($value) && count($value) == 1) {
|
||||
} elseif (is_array($value) && count($value) == 1 && !is_int(key($value))) {
|
||||
// Format: @command.param: { attr1: value1, attr2: value2 }
|
||||
$cmd = (string) key($value);
|
||||
$params = (array) current($value);
|
||||
@@ -1957,51 +1985,92 @@ class Page
|
||||
return $value;
|
||||
}
|
||||
|
||||
/** @var Pages $pages */
|
||||
$pages = self::getGrav()['pages'];
|
||||
|
||||
$parts = explode('.', $cmd);
|
||||
$current = array_shift($parts);
|
||||
|
||||
$results = null;
|
||||
$results = new Collection();
|
||||
switch ($current) {
|
||||
case '@self':
|
||||
if (!empty($parts)) {
|
||||
switch ($parts[0]) {
|
||||
case 'modular':
|
||||
// @self.modular: false (alternative to @self.children)
|
||||
if (!empty($params) && $params[0] === false) {
|
||||
$results = $this->children()->nonModular()->published();
|
||||
$results = $this->children()->nonModular();
|
||||
break;
|
||||
}
|
||||
$results = $this->children()->modular()->published();
|
||||
$results = $this->children()->modular();
|
||||
break;
|
||||
case 'children':
|
||||
$results = $this->children()->nonModular()->published();
|
||||
$results = $this->children()->nonModular();
|
||||
break;
|
||||
case 'all':
|
||||
$results = $this->children();
|
||||
break;
|
||||
case 'parent':
|
||||
$collection = new Collection();
|
||||
$results = $collection->addPage($this->parent());
|
||||
break;
|
||||
case 'siblings':
|
||||
$results = $this->parent()->children()->remove($this->path());
|
||||
break;
|
||||
case 'descendants':
|
||||
$results = $pages->all($this)->remove($this->path())->nonModular();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$results = $results->published();
|
||||
break;
|
||||
|
||||
case '@page':
|
||||
$page = null;
|
||||
|
||||
if (!empty($params)) {
|
||||
/** @var Pages $pages */
|
||||
$pages = self::getGrav()['pages'];
|
||||
$page = $this->find($params[0]);
|
||||
}
|
||||
|
||||
list($what, $recurse) = array_pad($params, 2, null);
|
||||
// safety check in case page is not found
|
||||
if (!isset($page)) {
|
||||
return $results;
|
||||
}
|
||||
|
||||
if ($what == '@root') {
|
||||
$page = $pages->root();
|
||||
} else {
|
||||
$page = $this->find($what);
|
||||
// Handle a @page.descendants
|
||||
if (!empty($parts)) {
|
||||
switch ($parts[0]) {
|
||||
case 'self':
|
||||
$results = new Collection();
|
||||
$results = $results->addPage($page);
|
||||
break;
|
||||
|
||||
case 'descendants':
|
||||
$results = $pages->all($page)->remove($page->path());
|
||||
break;
|
||||
|
||||
case 'children':
|
||||
$results = $page->children();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$results = $page->children();
|
||||
}
|
||||
|
||||
if ($page) {
|
||||
if ($recurse) {
|
||||
$results = $pages->all($page)->nonModular()->published();
|
||||
} else {
|
||||
$results = $page->children()->nonModular()->published();
|
||||
}
|
||||
}
|
||||
$results = $results->nonModular()->published();
|
||||
|
||||
break;
|
||||
|
||||
case '@root':
|
||||
if (!empty($parts) && $parts[0] == 'descendants') {
|
||||
$results = $pages->all($pages->root())->nonModular()->published();
|
||||
} else {
|
||||
$results = $pages->root()->children()->nonModular()->published();
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case '@taxonomy':
|
||||
// Gets a collection of pages by using one of the following formats:
|
||||
// @taxonomy.category: blog
|
||||
@@ -2077,7 +2146,7 @@ class Page
|
||||
*/
|
||||
protected function doRelocation($reorder)
|
||||
{
|
||||
if (empty($this->_original) ) {
|
||||
if (!$this->_original) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2118,7 +2187,7 @@ class Page
|
||||
// Handle all the other pages.
|
||||
$page = $pages->get($path);
|
||||
|
||||
if ($page && $page->exists() && $page->order() != $order+1) {
|
||||
if ($page && $page->exists() && !$page->_action && $page->order() != $order+1) {
|
||||
$page = $page->move($parent);
|
||||
$page->order($order+1);
|
||||
$page->save(false);
|
||||
@@ -2126,11 +2195,12 @@ class Page
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->_action == 'move' && $this->_original->exists()) {
|
||||
Folder::move($this->_original->path(), $this->path());
|
||||
}
|
||||
if ($this->_action == 'copy' && $this->_original->exists()) {
|
||||
Folder::copy($this->_original->path(), $this->path());
|
||||
if (is_dir($this->_original->path())) {
|
||||
if ($this->_action == 'move') {
|
||||
Folder::move($this->_original->path(), $this->path());
|
||||
} elseif ($this->_action == 'copy') {
|
||||
Folder::copy($this->_original->path(), $this->path());
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->name() != $this->_original->name()) {
|
||||
@@ -2140,7 +2210,6 @@ class Page
|
||||
}
|
||||
}
|
||||
|
||||
$this->_action = null;
|
||||
$this->_original = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@ class Pages
|
||||
/**
|
||||
* Get a page instance.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $path The filesystem full path of the page
|
||||
* @return Page
|
||||
* @throws \Exception
|
||||
*/
|
||||
@@ -264,7 +264,7 @@ class Pages
|
||||
/**
|
||||
* Dispatch URI to a page.
|
||||
*
|
||||
* @param $url
|
||||
* @param string $url The relative URL of the page
|
||||
* @param bool $all
|
||||
* @return Page|null
|
||||
*/
|
||||
@@ -272,6 +272,10 @@ class Pages
|
||||
{
|
||||
// Fetch page if there's a defined route to it.
|
||||
$page = isset($this->routes[$url]) ? $this->get($this->routes[$url]) : null;
|
||||
// Try without trailing slash
|
||||
if (!$page && Utils::endsWith($url, '/')) {
|
||||
$page = isset($this->routes[rtrim($url, '/')]) ? $this->get($this->routes[rtrim($url, '/')]) : null;
|
||||
}
|
||||
|
||||
// Are we in the admin? this is important!
|
||||
$not_admin = !isset($this->grav['admin']);
|
||||
@@ -482,6 +486,36 @@ class Pages
|
||||
return static::types();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get access levels of the site pages
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function accessLevels()
|
||||
{
|
||||
$accessLevels = [];
|
||||
foreach($this->all() as $page) {
|
||||
if (isset($page->header()->access)) {
|
||||
if (is_array($page->header()->access)) {
|
||||
foreach($page->header()->access as $index => $accessLevel) {
|
||||
if (is_array($accessLevel)) {
|
||||
foreach($accessLevel as $innerIndex => $innerAccessLevel) {
|
||||
array_push($accessLevels, $innerIndex);
|
||||
}
|
||||
} else {
|
||||
array_push($accessLevels, $index);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
array_push($accessLevels, $page->header()->access);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique($accessLevels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available parents.
|
||||
*
|
||||
@@ -494,7 +528,22 @@ class Pages
|
||||
/** @var Pages $pages */
|
||||
$pages = $grav['pages'];
|
||||
|
||||
return $pages->getList();
|
||||
$parents = $pages->getList();
|
||||
|
||||
/** @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])) {
|
||||
unset($parents[$page_route]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $parents;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace Grav\Common;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Data\Blueprints;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
|
||||
@@ -105,8 +104,12 @@ class Plugins extends Iterator
|
||||
continue;
|
||||
}
|
||||
|
||||
$type = $directory->getBasename();
|
||||
$list[$type] = self::get($type);
|
||||
$plugin = $directory->getBasename();
|
||||
$result = self::get($plugin);
|
||||
|
||||
if ($result) {
|
||||
$list[$plugin] = $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
ksort($list);
|
||||
@@ -121,7 +124,13 @@ class Plugins extends Iterator
|
||||
$blueprint->name = $name;
|
||||
|
||||
// Load default configuration.
|
||||
$file = CompiledYamlFile::instance("plugins://{$name}/{$name}.yaml");
|
||||
$file = CompiledYamlFile::instance("plugins://{$name}/{$name}" . YAML_EXT);
|
||||
|
||||
// ensure this is a valid plugin
|
||||
if (!$file->exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$obj = new Data($file->content(), $blueprint);
|
||||
|
||||
// Override with user configuration.
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
<?php
|
||||
namespace Grav\Common\Service;
|
||||
|
||||
use Grav\Common\Config\CompiledBlueprints;
|
||||
use Grav\Common\Config\CompiledConfig;
|
||||
use Grav\Common\Config\CompiledLanguages;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Config\ConfigFileFinder;
|
||||
use Grav\Common\Config\Setup;
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use RocketTheme\Toolbox\Blueprints\Blueprints;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
/**
|
||||
* The Config class contains configuration information.
|
||||
@@ -14,51 +19,99 @@ use RocketTheme\Toolbox\Blueprints\Blueprints;
|
||||
*/
|
||||
class ConfigServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
private $environment;
|
||||
private $setup;
|
||||
|
||||
public function register(Container $container)
|
||||
{
|
||||
$self = $this;
|
||||
|
||||
// Pre-load setup.php as it contains our initial configuration.
|
||||
$file = GRAV_ROOT . '/setup.php';
|
||||
$this->setup = is_file($file) ? (array) include $file : [];
|
||||
$this->environment = isset($this->setup['environment']) ? $this->setup['environment'] : null;
|
||||
|
||||
$container['blueprints'] = function ($c) use ($self) {
|
||||
return $self->loadMasterBlueprints($c);
|
||||
$container['setup'] = function ($c) {
|
||||
return static::setup($c)->init();
|
||||
};
|
||||
|
||||
$container['config'] = function ($c) use ($self) {
|
||||
return $self->loadMasterConfig($c);
|
||||
$container['blueprints'] = function ($c) {
|
||||
return static::blueprints($c);
|
||||
};
|
||||
|
||||
$container['config'] = function ($c) {
|
||||
return static::load($c);
|
||||
};
|
||||
|
||||
$container['languages'] = function ($c) {
|
||||
return static::languages($c);
|
||||
};
|
||||
}
|
||||
|
||||
public function loadMasterConfig(Container $container)
|
||||
public static function setup(Container $container)
|
||||
{
|
||||
$environment = $this->getEnvironment($container);
|
||||
|
||||
$config = new Config($this->setup, $container, $environment);
|
||||
|
||||
return $config;
|
||||
return new Setup($container);
|
||||
}
|
||||
|
||||
public function loadMasterBlueprints(Container $container)
|
||||
public static function blueprints(Container $container)
|
||||
{
|
||||
$environment = $this->getEnvironment($container);
|
||||
$file = CACHE_DIR . 'compiled/blueprints/master-'.$environment.'.php';
|
||||
$data = is_file($file) ? (array) include $file : [];
|
||||
/** Setup $setup */
|
||||
$setup = $container['setup'];
|
||||
|
||||
return new Blueprints($data, $container);
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $container['locator'];
|
||||
|
||||
$cache = $locator->findResource('cache://compiled/blueprints', true, true);
|
||||
|
||||
$files = [];
|
||||
$paths = $locator->findResources('blueprints://config');
|
||||
$files += (new ConfigFileFinder)->locateFiles($paths);
|
||||
$paths = $locator->findResources('plugins://');
|
||||
$files += (new ConfigFileFinder)->setBase('plugins')->locateInFolders($paths, 'blueprints');
|
||||
|
||||
$blueprints = new CompiledBlueprints($cache, $files, GRAV_ROOT);
|
||||
|
||||
return $blueprints->name("master-{$setup->environment}")->load();
|
||||
}
|
||||
|
||||
public function getEnvironment(Container $container)
|
||||
public static function load(Container $container)
|
||||
{
|
||||
if (!isset($this->environment)) {
|
||||
$this->environment = $container['uri']->environment();
|
||||
/** Setup $setup */
|
||||
$setup = $container['setup'];
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $container['locator'];
|
||||
|
||||
$cache = $locator->findResource('cache://compiled/config', true, true);
|
||||
|
||||
$files = [];
|
||||
$paths = $locator->findResources('config://');
|
||||
$files += (new ConfigFileFinder)->locateFiles($paths);
|
||||
$paths = $locator->findResources('plugins://');
|
||||
$files += (new ConfigFileFinder)->setBase('plugins')->locateInFolders($paths);
|
||||
|
||||
$config = new CompiledConfig($cache, $files, GRAV_ROOT);
|
||||
$config->setBlueprints(function() use ($container) {
|
||||
return $container['blueprints'];
|
||||
});
|
||||
|
||||
return $config->name("master-{$setup->environment}")->load();
|
||||
}
|
||||
|
||||
public static function languages(Container $container)
|
||||
{
|
||||
/** Setup $setup */
|
||||
$setup = $container['setup'];
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $container['config'];
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $container['locator'];
|
||||
|
||||
$cache = $locator->findResource('cache://compiled/languages', true, true);
|
||||
$files = [];
|
||||
|
||||
// Process languages only if enabled in configuration.
|
||||
if ($config->get('system.languages.translations', true)) {
|
||||
$paths = $locator->findResources('languages://');
|
||||
$files += (new ConfigFileFinder)->locateFiles($paths);
|
||||
$paths = $locator->findResources('plugins://');
|
||||
$files += (new ConfigFileFinder)->setBase('plugins')->locateInFolders($paths, 'languages');
|
||||
}
|
||||
|
||||
return $this->environment;
|
||||
$languages = new CompiledLanguages($cache, $files, GRAV_ROOT);
|
||||
|
||||
return $languages->name("master-{$setup->environment}")->load();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace Grav\Common\Service;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Config\Setup;
|
||||
use Pimple\Container;
|
||||
use RocketTheme\Toolbox\DI\ServiceProviderInterface;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
@@ -14,18 +14,18 @@ class StreamsServiceProvider implements ServiceProviderInterface
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container['locator'] = function($c) {
|
||||
$locator = new UniformResourceLocator(ROOT_DIR);
|
||||
$locator = new UniformResourceLocator(GRAV_ROOT);
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $c['config'];
|
||||
$config->initializeLocator($locator);
|
||||
/** @var Setup $setup */
|
||||
$setup = $c['setup'];
|
||||
$setup->initializeLocator($locator);
|
||||
|
||||
return $locator;
|
||||
};
|
||||
|
||||
$container['streams'] = function($c) {
|
||||
/** @var Config $config */
|
||||
$config = $c['config'];
|
||||
/** @var Setup $setup */
|
||||
$setup = $c['setup'];
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $c['locator'];
|
||||
@@ -34,7 +34,7 @@ class StreamsServiceProvider implements ServiceProviderInterface
|
||||
Stream::setLocator($locator);
|
||||
ReadOnlyStream::setLocator($locator);
|
||||
|
||||
return new StreamBuilder($config->getStreams($c));
|
||||
return new StreamBuilder($setup->getStreams($c));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ class Themes extends Iterator
|
||||
|
||||
public function __construct(Grav $grav)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->grav = $grav;
|
||||
$this->config = $grav['config'];
|
||||
|
||||
@@ -34,13 +36,18 @@ class Themes extends Iterator
|
||||
|
||||
public function init()
|
||||
{
|
||||
/** @var EventDispatcher $events */
|
||||
$events = $this->grav['events'];
|
||||
|
||||
/** @var Themes $themes */
|
||||
$themes = $this->grav['themes'];
|
||||
$themes->configure();
|
||||
|
||||
$this->initTheme();
|
||||
}
|
||||
|
||||
public function initTheme()
|
||||
{
|
||||
/** @var Themes $themes */
|
||||
$themes = $this->grav['themes'];
|
||||
|
||||
try {
|
||||
$instance = $themes->load();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
@@ -48,10 +55,15 @@ class Themes extends Iterator
|
||||
}
|
||||
|
||||
if ($instance instanceof EventSubscriberInterface) {
|
||||
/** @var EventDispatcher $events */
|
||||
$events = $this->grav['events'];
|
||||
|
||||
$events->addSubscriber($instance);
|
||||
}
|
||||
|
||||
$this->grav['theme'] = $instance;
|
||||
|
||||
$this->grav->fireEvent('onThemeInitialized');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,8 +86,12 @@ class Themes extends Iterator
|
||||
continue;
|
||||
}
|
||||
|
||||
$type = $directory->getBasename();
|
||||
$list[$type] = self::get($type);
|
||||
$theme = $directory->getBasename();
|
||||
$result = self::get($theme);
|
||||
|
||||
if ($result) {
|
||||
$list[$theme] = $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
ksort($list);
|
||||
@@ -100,14 +116,20 @@ class Themes extends Iterator
|
||||
$blueprint = $blueprints->get("{$name}/blueprints");
|
||||
$blueprint->name = $name;
|
||||
|
||||
// Load default configuration.
|
||||
$file = CompiledYamlFile::instance("themes://{$name}/{$name}" . YAML_EXT);
|
||||
|
||||
// ensure this is a valid theme
|
||||
if (!$file->exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find thumbnail.
|
||||
$thumb = "themes://{$name}/thumbnail.jpg";
|
||||
if ($path = $this->grav['locator']->findResource($thumb, false)) {
|
||||
$blueprint->set('thumbnail', $this->grav['base_url'] . '/' . $path);
|
||||
}
|
||||
|
||||
// Load default configuration.
|
||||
$file = CompiledYamlFile::instance("themes://{$name}/{$name}" . YAML_EXT);
|
||||
$obj = new Data($file->content(), $blueprint);
|
||||
|
||||
// Override with user configuration.
|
||||
@@ -171,6 +193,8 @@ class Themes extends Iterator
|
||||
exit("Theme '$name' does not exist, unable to display page.");
|
||||
}
|
||||
|
||||
$this->config->set('theme', $config->get('themes.' . $name));
|
||||
|
||||
if (empty($class)) {
|
||||
$class = new Theme($grav, $config, $name);
|
||||
}
|
||||
@@ -282,11 +306,11 @@ class Themes extends Iterator
|
||||
$class = substr($class, strlen($prefix));
|
||||
|
||||
// Replace namespace tokens to directory separators
|
||||
$path = ltrim(preg_replace('#\\\|_(?!.+\\\)#', '/', $class), '/');
|
||||
$path = strtolower(ltrim(preg_replace('#\\\|_(?!.+\\\)#', '/', $class), '/'));
|
||||
$file = $locator->findResource("themes://{$path}/{$path}.php");
|
||||
|
||||
// Load class
|
||||
if (stream_resolve_include_path($file)) {
|
||||
if (file_exists($file)) {
|
||||
return include_once($file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ namespace Grav\Common\Twig;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Page\Page;
|
||||
use Grav\Common\Inflector;
|
||||
use Grav\Common\Utils;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<?php
|
||||
namespace Grav\Common\Twig;
|
||||
|
||||
use Grav\Common\GravTrait;
|
||||
|
||||
/**
|
||||
* The Twig Environment class is a wrapper that handles configurable permissions
|
||||
* for the Twig cache files
|
||||
|
||||
@@ -70,6 +70,7 @@ class TwigExtension extends \Twig_Extension
|
||||
new \Twig_SimpleFilter('randomize', [$this,'randomizeFilter']),
|
||||
new \Twig_SimpleFilter('modulus', [$this,'modulusFilter']),
|
||||
new \Twig_SimpleFilter('rtrim', [$this, 'rtrimFilter']),
|
||||
new \Twig_SimpleFilter('pad', [$this, 'padFilter']),
|
||||
new \Twig_SimpleFilter('safe_email', [$this,'safeEmailFilter']),
|
||||
new \Twig_SimpleFilter('safe_truncate', ['\Grav\Common\Utils','safeTruncate']),
|
||||
new \Twig_SimpleFilter('safe_truncate_html', ['\Grav\Common\Utils','safeTruncateHTML']),
|
||||
@@ -94,14 +95,17 @@ 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('gist', [$this, 'gistFunc']),
|
||||
new \Twig_SimpleFunction('nonce_field', [$this, 'nonceFieldFunc']),
|
||||
new \Twig_simpleFunction('random_string', [$this, 'randomStringFunc']),
|
||||
new \Twig_SimpleFunction('repeat', [$this, 'repeatFunc']),
|
||||
new \Twig_SimpleFunction('string', [$this, 'stringFunc']),
|
||||
new \Twig_simpleFunction('t', [$this, 'translate']),
|
||||
new \Twig_simpleFunction('ta', [$this, 'translateArray']),
|
||||
new \Twig_SimpleFunction('url', [$this, 'urlFunc']),
|
||||
new \Twig_SimpleFunction('evaluate', [$this, 'evaluateFunc']),
|
||||
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
@@ -351,6 +355,12 @@ class TwigExtension extends \Twig_Extension
|
||||
$periods[$j] .= '_PLURAL';
|
||||
}
|
||||
|
||||
if ($this->grav['language']->getTranslation($this->grav['language']->getLanguage(), $periods[$j] . '_MORE_THAN_TWO')) {
|
||||
if ($difference > 2) {
|
||||
$periods[$j] .= '_MORE_THAN_TWO';
|
||||
}
|
||||
}
|
||||
|
||||
$periods[$j] = $this->grav['language']->translate($periods[$j], null, true);
|
||||
|
||||
return "$difference $periods[$j] {$tense}";
|
||||
@@ -534,6 +544,22 @@ class TwigExtension extends \Twig_Extension
|
||||
return Utils::generateRandomString($count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pad a string to a certain length with another string
|
||||
*
|
||||
* @param $input
|
||||
* @param $pad_length
|
||||
* @param string $pad_string
|
||||
* @param int $pad_type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function padFilter($input, $pad_length, $pad_string = " ", $pad_type = STR_PAD_RIGHT)
|
||||
{
|
||||
return str_pad($input, (int) $pad_length, $pad_string, $pad_type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cast a value to array
|
||||
*
|
||||
@@ -595,4 +621,22 @@ class TwigExtension extends \Twig_Extension
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to add a nonce to a form. Call {{ nonce_field('action') }} specifying a string representing the action.
|
||||
*
|
||||
* For maximum protection, ensure that the string representing the action is as specific as possible.
|
||||
*
|
||||
* @todo evaluate if adding referrer or not
|
||||
*
|
||||
* @param string action the action
|
||||
* @param string nonceParamName a custom nonce param name
|
||||
*
|
||||
* @return string the nonce input field
|
||||
*/
|
||||
public function nonceFieldFunc($action, $nonceParamName = 'nonce')
|
||||
{
|
||||
$string = '<input type="hidden" id="' . $nonceParamName . '" name="' . $nonceParamName . '" value="' . Utils::getNonce($action) .'" />';
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Page\Page;
|
||||
use Grav\Common\Page\Pages;
|
||||
|
||||
/**
|
||||
* The URI object provides information about the current URL
|
||||
@@ -133,11 +132,16 @@ class Uri
|
||||
// set the original basename
|
||||
$this->basename = $parts['basename'];
|
||||
|
||||
// set the extension
|
||||
if (isset($parts['extension'])) {
|
||||
$this->extension = $parts['extension'];
|
||||
}
|
||||
|
||||
$valid_page_types = implode('|', $config->get('system.pages.types'));
|
||||
|
||||
// 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'];
|
||||
$this->extension = $parts['extension'];
|
||||
}
|
||||
|
||||
// set the new url
|
||||
@@ -481,12 +485,14 @@ class Uri
|
||||
/**
|
||||
* Converts links from absolute '/' or relative (../..) to a grav friendly format
|
||||
*
|
||||
* @param $page the current page to use as reference
|
||||
* @param Page|the $page the current page to use as reference
|
||||
* @param string $markdown_url the URL as it was written in the markdown
|
||||
* @param string $type the type of URL, image | link
|
||||
* @param null $relative if null, will use system default, if true will use relative links internally
|
||||
*
|
||||
* @return string the more friendly formatted url
|
||||
*/
|
||||
public static function convertUrl(Page $page, $markdown_url, $type = 'link')
|
||||
public static function convertUrl(Page $page, $markdown_url, $type = 'link', $relative = null)
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
|
||||
@@ -500,7 +506,13 @@ class Uri
|
||||
}
|
||||
|
||||
$pages_dir = $grav['locator']->findResource('page://');
|
||||
$base_url = rtrim($grav['base_url'] . $grav['pages']->base(), '/') . $language_append;
|
||||
if (is_null($relative)) {
|
||||
$base = $grav['base_url'];
|
||||
} else {
|
||||
$base = $relative ? $grav['base_url_relative'] : $grav['base_url_absolute'];
|
||||
}
|
||||
|
||||
$base_url = rtrim($base . $grav['pages']->base(), '/') . $language_append;
|
||||
|
||||
// if absolute and starts with a base_url move on
|
||||
if (pathinfo($markdown_url, PATHINFO_DIRNAME) == '.' && $page->url() == '/') {
|
||||
@@ -568,4 +580,20 @@ class Uri
|
||||
return $normalized_url;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the nonce to a URL for a specific action
|
||||
*
|
||||
* @param string $url the url
|
||||
* @param string $action the action
|
||||
* @param string $nonceParamName the param name to use
|
||||
*
|
||||
* @return string the url with the nonce
|
||||
*/
|
||||
public static function addNonce($url, $action, $nonceParamName = 'nonce')
|
||||
{
|
||||
$urlWithNonce = $url . '/' . $nonceParamName . Grav::instance()['config']->get('system.param_sep', ':') . Utils::getNonce($action);
|
||||
return $urlWithNonce;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
127
system/src/Grav/Common/User/Group.php
Normal file
127
system/src/Grav/Common/User/Group.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
namespace Grav\Common\User;
|
||||
|
||||
use Grav\Common\Data\Blueprints;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Utils;
|
||||
|
||||
/**
|
||||
* Group object
|
||||
*
|
||||
* @property mixed authenticated
|
||||
* @property mixed password
|
||||
* @property bool|string hashed_password
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Group extends Data
|
||||
{
|
||||
use GravTrait;
|
||||
|
||||
/**
|
||||
* Get the groups list
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function groups()
|
||||
{
|
||||
$groups = self::getGrav()['config']->get('groups');
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a group exists
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function group_exists($groupname)
|
||||
{
|
||||
return isset(self::groups()[$groupname]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a group by name
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function load($groupname)
|
||||
{
|
||||
if (self::group_exists($groupname)) {
|
||||
$content = self::groups()[$groupname];
|
||||
} else {
|
||||
$content = [];
|
||||
}
|
||||
|
||||
$blueprints = new Blueprints('blueprints://');
|
||||
$blueprint = $blueprints->get('user/group');
|
||||
if (!isset($content['groupname'])) {
|
||||
$content['groupname'] = $groupname;
|
||||
}
|
||||
$group = new Group($content, $blueprint);
|
||||
|
||||
return $group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a group
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$blueprints = new Blueprints('blueprints://');
|
||||
$blueprint = $blueprints->get('user/group');
|
||||
|
||||
$fields = $blueprint->fields();
|
||||
|
||||
self::getGrav()['config']->set("groups.$this->groupname", []);
|
||||
|
||||
foreach($fields as $field) {
|
||||
if ($field['type'] == 'text') {
|
||||
$value = $field['name'];
|
||||
if (isset($this->items[$value])) {
|
||||
self::getGrav()['config']->set("groups.$this->groupname.$value", $this->items[$value]);
|
||||
}
|
||||
}
|
||||
if ($field['type'] == 'array') {
|
||||
$value = $field['name'];
|
||||
$arrayValues = Utils::resolve($this->items, $field['name']);
|
||||
|
||||
if ($arrayValues) foreach($arrayValues as $arrayIndex => $arrayValue) {
|
||||
self::getGrav()['config']->set("groups.$this->groupname.$value.$arrayIndex", $arrayValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$type = 'groups';
|
||||
$blueprints = $this->blueprints("config/{$type}");
|
||||
$obj = new Data(self::getGrav()['config']->get($type), $blueprints);
|
||||
$file = CompiledYamlFile::instance(self::getGrav()['locator']->findResource("config://{$type}.yaml"));
|
||||
$obj->file($file);
|
||||
$obj->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a group
|
||||
*
|
||||
* @param string $username
|
||||
* @return bool True if the action was performed
|
||||
*/
|
||||
public static function remove($groupname)
|
||||
{
|
||||
$blueprints = new Blueprints('blueprints://');
|
||||
$blueprint = $blueprints->get('user/group');
|
||||
|
||||
$groups = self::getGrav()['config']->get("groups");
|
||||
unset($groups[$groupname]);
|
||||
self::getGrav()['config']->set("groups", $groups);
|
||||
|
||||
$type = 'groups';
|
||||
$obj = new Data(self::getGrav()['config']->get($type), $blueprint);
|
||||
$file = CompiledYamlFile::instance(self::getGrav()['locator']->findResource("config://{$type}.yaml"));
|
||||
$obj->file($file);
|
||||
$obj->save();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,9 @@ class User extends Data
|
||||
{
|
||||
$locator = self::getGrav()['locator'];
|
||||
|
||||
// force lowercase of username
|
||||
$username = strtolower($username);
|
||||
|
||||
$blueprints = new Blueprints('blueprints://');
|
||||
$blueprint = $blueprints->get('user/account');
|
||||
$file_path = $locator->findResource('account://' . $username . YAML_EXT);
|
||||
@@ -40,6 +43,9 @@ class User extends Data
|
||||
if (!isset($content['username'])) {
|
||||
$content['username'] = $username;
|
||||
}
|
||||
if (!isset($content['state'])) {
|
||||
$content['state'] = 'enabled';
|
||||
}
|
||||
$user = new User($content, $blueprint);
|
||||
$user->file($file);
|
||||
|
||||
@@ -50,7 +56,7 @@ class User extends Data
|
||||
* Remove user account.
|
||||
*
|
||||
* @param string $username
|
||||
* @return bool True is the action was performed
|
||||
* @return bool True if the action was performed
|
||||
*/
|
||||
public static function remove($username)
|
||||
{
|
||||
@@ -80,7 +86,10 @@ class User extends Data
|
||||
// Plain-text passwords do not match, we know we should fail but execute
|
||||
// verify to protect us from timing attacks and return false regardless of
|
||||
// the result
|
||||
Authentication::verify($password, self::getGrav()['config']->get('system.security.default_hash'));
|
||||
Authentication::verify(
|
||||
$password,
|
||||
self::getGrav()['config']->get('system.security.default_hash', '$2y$10$kwsyMVwM8/7j0K/6LHT.g.Fs49xOCTp2b8hh/S5.dPJuJcJB6T.UK')
|
||||
);
|
||||
return false;
|
||||
} else {
|
||||
// Plain-text does match, we can update the hash and proceed
|
||||
@@ -139,7 +148,32 @@ class User extends Data
|
||||
return false;
|
||||
}
|
||||
|
||||
return Utils::isPositive($this->get("access.{$action}"));
|
||||
if (isset($this->state) && $this->state !== 'enabled') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$return = false;
|
||||
|
||||
//Check group access level
|
||||
$groups = $this->get('groups');
|
||||
if ($groups) foreach($groups as $group) {
|
||||
$permission = self::getGrav()['config']->get("groups.{$group}.access.{$action}");
|
||||
if (Utils::isPositive($permission)) {
|
||||
$return = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Check user access level
|
||||
if (!$this->get('access')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Utils::resolve($this->get('access'), $action) !== null) {
|
||||
$permission = $this->get("access.{$action}");
|
||||
$return = Utils::isPositive($permission);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,8 @@ abstract class Utils
|
||||
{
|
||||
use GravTrait;
|
||||
|
||||
protected static $nonces = [];
|
||||
|
||||
/**
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
@@ -100,6 +102,9 @@ abstract class Utils
|
||||
return (object)array_merge((array)$obj1, (array)$obj2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function dateFormats()
|
||||
{
|
||||
$now = new DateTime();
|
||||
@@ -214,9 +219,11 @@ abstract class Utils
|
||||
$filesize = filesize($file);
|
||||
|
||||
// check if this function is available, if so use it to stop any timeouts
|
||||
if (function_exists('set_time_limit')) {
|
||||
set_time_limit(0);
|
||||
}
|
||||
try {
|
||||
if (!Utils::isFunctionDisabled('set_time_limit') && !ini_get('safe_mode') && function_exists('set_time_limit')) {
|
||||
set_time_limit(0);
|
||||
}
|
||||
} catch (\Exception $e) {}
|
||||
|
||||
ignore_user_abort(false);
|
||||
|
||||
@@ -300,6 +307,19 @@ abstract class Utils
|
||||
return $root . implode('/', $ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $function
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isFunctionDisabled($function)
|
||||
{
|
||||
return in_array($function, explode(',', ini_get('disable_functions')));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function timezones()
|
||||
{
|
||||
$timezones = \DateTimeZone::listIdentifiers(\DateTimeZone::ALL);
|
||||
@@ -327,6 +347,12 @@ abstract class Utils
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $source
|
||||
* @param $fn
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function arrayFilterRecursive(Array $source, $fn)
|
||||
{
|
||||
$result = array();
|
||||
@@ -346,6 +372,11 @@ abstract class Utils
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function pathPrefixedByLangCode($string)
|
||||
{
|
||||
$languages_enabled = self::getGrav()['config']->get('system.languages.supported', []);
|
||||
@@ -357,6 +388,11 @@ abstract class Utils
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $date
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function date2timestamp($date)
|
||||
{
|
||||
$config = self::getGrav()['config'];
|
||||
@@ -377,6 +413,25 @@ abstract class Utils
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value of an array using dot notation
|
||||
*/
|
||||
public static function resolve(array $array, $path, $default = null)
|
||||
{
|
||||
$current = $array;
|
||||
$p = strtok($path, '.');
|
||||
|
||||
while ($p !== false) {
|
||||
if (!isset($current[$p])) {
|
||||
return $default;
|
||||
}
|
||||
$current = $current[$p];
|
||||
$p = strtok('.');
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a value is positive
|
||||
*
|
||||
@@ -384,7 +439,98 @@ abstract class Utils
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isPositive($value) {
|
||||
public static function isPositive($value)
|
||||
{
|
||||
return in_array($value, [true, 1, '1', 'yes', 'on', 'true'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a nonce string to be hashed. Called by self::getNonce()
|
||||
*
|
||||
* @param string $action
|
||||
* @param bool $plusOneTick if true, generates the token for the next tick (the next 12 hours)
|
||||
*
|
||||
* @return string the nonce string
|
||||
*/
|
||||
private static function generateNonceString($action, $plusOneTick = false)
|
||||
{
|
||||
if (isset(self::getGrav()['user'])) {
|
||||
$user = self::getGrav()['user'];
|
||||
$username = $user->username;
|
||||
if (isset($_SERVER['REMOTE_ADDR'])) {
|
||||
$username .= $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
} else {
|
||||
$username = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
|
||||
}
|
||||
|
||||
$token = session_id();
|
||||
$i = self::nonceTick();
|
||||
|
||||
if ($plusOneTick) {
|
||||
$i++;
|
||||
}
|
||||
|
||||
return ( $i . '|' . $action . '|' . $username . '|' . $token . '|' . self::getGrav()['config']->get('security.salt'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time-dependent variable for nonce creation.
|
||||
*
|
||||
* @todo now a tick lasts a day. Once the day is passed, the nonce is not valid any more. Find a better way
|
||||
* to ensure nonces issued near the end of the day do not expire in that small amount of time
|
||||
*
|
||||
* @return int the time part of the nonce. Changes once every 24 hours
|
||||
*/
|
||||
private static function nonceTick()
|
||||
{
|
||||
$secondsInHalfADay = 60 * 60 * 12;
|
||||
return (int)ceil(time() / ( $secondsInHalfADay ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a hashed nonce tied to the passed action. Tied to the current user and time. The nonce for a given
|
||||
* action is the same for 12 hours.
|
||||
*
|
||||
* @param string $action the action the nonce is tied to (e.g. save-user-admin or move-page-homepage)
|
||||
* @param bool $plusOneTick if true, generates the token for the next tick (the next 12 hours)
|
||||
*
|
||||
* @return string the nonce
|
||||
*/
|
||||
public static function getNonce($action, $plusOneTick = false)
|
||||
{
|
||||
// Don't regenerate this again if not needed
|
||||
if (isset(static::$nonces[$action])) {
|
||||
return static::$nonces[$action];
|
||||
}
|
||||
$nonce = md5(self::generateNonceString($action, $plusOneTick));
|
||||
static::$nonces[$action] = $nonce;
|
||||
|
||||
return static::$nonces[$action];
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the passed nonce for the give action
|
||||
*
|
||||
* @param string $nonce the nonce to verify
|
||||
* @param string $action the action to verify the nonce to
|
||||
*
|
||||
* @return boolean verified or not
|
||||
*/
|
||||
public static function verifyNonce($nonce, $action)
|
||||
{
|
||||
//Nonce generated 0-12 hours ago
|
||||
if ($nonce == self::getNonce($action)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//Nonce generated 12-24 hours ago
|
||||
$plusOneTick = true;
|
||||
if ($nonce == self::getNonce($action, $plusOneTick)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//Invalid nonce
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,17 @@
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Common\Backup\ZipBackup;
|
||||
use Grav\Console\ConsoleTrait;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use RocketTheme\Toolbox\File\JsonFile;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class BackupCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class BackupCommand extends Command
|
||||
class BackupCommand extends ConsoleCommand
|
||||
{
|
||||
use ConsoleTrait;
|
||||
|
||||
protected $source;
|
||||
protected $progress;
|
||||
|
||||
@@ -42,21 +36,16 @@ class BackupCommand extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*
|
||||
* @return int|null|void
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function serve()
|
||||
{
|
||||
$this->setupConsole($input, $output);
|
||||
|
||||
$this->progress = new ProgressBar($output);
|
||||
$this->progress = new ProgressBar($this->output);
|
||||
$this->progress->setFormat('Archiving <cyan>%current%</cyan> files [<green>%bar%</green>] %elapsed:6s% %memory:6s%');
|
||||
|
||||
self::getGrav()['config']->init();
|
||||
|
||||
$destination = ($input->getArgument('destination')) ? $input->getArgument('destination') : null;
|
||||
$destination = ($this->input->getArgument('destination')) ? $this->input->getArgument('destination') : null;
|
||||
$log = JsonFile::instance(self::getGrav()['locator']->findResource("log://backup.log", true, true));
|
||||
$backup = ZipBackup::backup($destination, [$this, 'output']);
|
||||
|
||||
@@ -66,16 +55,13 @@ class BackupCommand extends Command
|
||||
]);
|
||||
$log->save();
|
||||
|
||||
$output->writeln('');
|
||||
$output->writeln('');
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $folder
|
||||
* @param $zipFile
|
||||
* @param $exclusiveLength
|
||||
* @param $progress
|
||||
* @param $args
|
||||
*/
|
||||
public function output($args)
|
||||
{
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Console\ConsoleTrait;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
|
||||
/**
|
||||
* Class CleanCommand
|
||||
@@ -14,7 +14,11 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
*/
|
||||
class CleanCommand extends Command
|
||||
{
|
||||
use ConsoleTrait;
|
||||
/* @var InputInterface $output */
|
||||
protected $input;
|
||||
|
||||
/* @var OutputInterface $output */
|
||||
protected $output;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
@@ -127,28 +131,34 @@ class CleanCommand extends Command
|
||||
'vendor/rockettheme/toolbox/.travis.yml',
|
||||
'vendor/rockettheme/toolbox/composer.json',
|
||||
'vendor/rockettheme/toolbox/phpunit.xml',
|
||||
'vendor/symfony/console/Symfony/Component/Console/composer.json',
|
||||
'vendor/symfony/console/Symfony/Component/Console/phpunit.xml.dist',
|
||||
'vendor/symfony/console/Symfony/Component/Console/.gitignore',
|
||||
'vendor/symfony/console/Symfony/Component/Console/.git',
|
||||
'vendor/symfony/console/Symfony/Component/Console/Tests',
|
||||
'vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.git',
|
||||
'vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore',
|
||||
'vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json',
|
||||
'vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist',
|
||||
'vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests',
|
||||
'vendor/symfony/yaml/Symfony/Component/Yaml/composer.json',
|
||||
'vendor/symfony/yaml/Symfony/Component/Yaml/phpunit.xml.dist',
|
||||
'vendor/symfony/yaml/Symfony/Component/Yaml/.gitignore',
|
||||
'vendor/symfony/yaml/Symfony/Component/Yaml/.git',
|
||||
'vendor/symfony/yaml/Symfony/Component/Yaml/Tests',
|
||||
'vendor/tracy/tracy/.gitattributes',
|
||||
'vendor/tracy/tracy/.travis.yml',
|
||||
'vendor/tracy/tracy/composer.json',
|
||||
'vendor/tracy/tracy/.gitignore',
|
||||
'vendor/tracy/tracy/.git',
|
||||
'vendor/tracy/tracy/examples',
|
||||
'vendor/tracy/tracy/tests',
|
||||
'vendor/symfony/console/composer.json',
|
||||
'vendor/symfony/console/phpunit.xml.dist',
|
||||
'vendor/symfony/console/.gitignore',
|
||||
'vendor/symfony/console/.git',
|
||||
'vendor/symfony/console/Tester',
|
||||
'vendor/symfony/console/Tests',
|
||||
'vendor/symfony/event-dispatcher/.git',
|
||||
'vendor/symfony/event-dispatcher/.gitignore',
|
||||
'vendor/symfony/event-dispatcher/composer.json',
|
||||
'vendor/symfony/event-dispatcher/phpunit.xml.dist',
|
||||
'vendor/symfony/event-dispatcher/Tests',
|
||||
'vendor/symfony/polyfill-iconv/.git',
|
||||
'vendor/symfony/polyfill-iconv/.gitignore',
|
||||
'vendor/symfony/polyfill-iconv/composer.json',
|
||||
'vendor/symfony/polyfill-mbstring/.git',
|
||||
'vendor/symfony/polyfill-mbstring/.gitignore',
|
||||
'vendor/symfony/polyfill-mbstring/composer.json',
|
||||
'vendor/symfony/var-dumper/.git',
|
||||
'vendor/symfony/var-dumper/.gitignore',
|
||||
'vendor/symfony/var-dumper/composer.json',
|
||||
'vendor/symfony/var-dumper/phpunit.xml.dist',
|
||||
'vendor/symfony/var-dumper/Test',
|
||||
'vendor/symfony/var-dumper/Tests',
|
||||
'vendor/symfony/yaml/composer.json',
|
||||
'vendor/symfony/yaml/phpunit.xml.dist',
|
||||
'vendor/symfony/yaml/.gitignore',
|
||||
'vendor/symfony/yaml/.git',
|
||||
'vendor/symfony/yaml/Tests',
|
||||
'vendor/twig/twig/.editorconfig',
|
||||
'vendor/twig/twig/.travis.yml',
|
||||
'vendor/twig/twig/.gitignore',
|
||||
@@ -172,14 +182,12 @@ class CleanCommand extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*
|
||||
* @return int|null|void
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->setupConsole($input, $output);
|
||||
|
||||
$this->cleanPaths();
|
||||
}
|
||||
|
||||
@@ -187,12 +195,9 @@ class CleanCommand extends Command
|
||||
{
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('<red>DELETING</red>');
|
||||
|
||||
$anything = false;
|
||||
|
||||
foreach ($this->paths_to_remove as $path) {
|
||||
$path = ROOT_DIR . $path;
|
||||
|
||||
if (is_dir($path) && @Folder::delete($path)) {
|
||||
$anything = true;
|
||||
$this->output->writeln('<red>dir: </red>' . $path);
|
||||
@@ -201,12 +206,30 @@ class CleanCommand extends Command
|
||||
$this->output->writeln('<red>file: </red>' . $path);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$anything) {
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('<green>Nothing to clean...</green>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set colors style definition for the formatter.
|
||||
*
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*/
|
||||
public function setupConsole(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
|
||||
$this->output->getFormatter()->setStyle('normal', new OutputFormatterStyle('white'));
|
||||
$this->output->getFormatter()->setStyle('yellow', new OutputFormatterStyle('yellow', null, array('bold')));
|
||||
$this->output->getFormatter()->setStyle('red', new OutputFormatterStyle('red', null, array('bold')));
|
||||
$this->output->getFormatter()->setStyle('cyan', new OutputFormatterStyle('cyan', null, array('bold')));
|
||||
$this->output->getFormatter()->setStyle('green', new OutputFormatterStyle('green', null, array('bold')));
|
||||
$this->output->getFormatter()->setStyle('magenta', new OutputFormatterStyle('magenta', null, array('bold')));
|
||||
$this->output->getFormatter()->setStyle('white', new OutputFormatterStyle('white', null, array('bold')));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,29 +2,24 @@
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Common\Cache;
|
||||
use Grav\Console\ConsoleTrait;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class ClearCacheCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class ClearCacheCommand extends Command
|
||||
class ClearCacheCommand extends ConsoleCommand
|
||||
{
|
||||
use ConsoleTrait;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName("clear-cache")
|
||||
->setDescription("Clears Grav cache")
|
||||
->setName('clear-cache')
|
||||
->setAliases(['clearcache'])
|
||||
->setDescription('Clears Grav cache')
|
||||
->addOption('all', null, InputOption::VALUE_NONE, 'If set will remove all including compiled, twig, doctrine caches')
|
||||
->addOption('assets-only', null, InputOption::VALUE_NONE, 'If set will remove only assets/*')
|
||||
->addOption('images-only', null, InputOption::VALUE_NONE, 'If set will remove only images/*')
|
||||
@@ -33,14 +28,10 @@ class ClearCacheCommand extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*
|
||||
* @return int|null|void
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function serve()
|
||||
{
|
||||
$this->setupConsole($input, $output);
|
||||
$this->cleanPaths();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
<?php
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Console\ConsoleTrait;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* Class ComposerCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class ComposerCommand extends Command
|
||||
class ComposerCommand extends ConsoleCommand
|
||||
{
|
||||
use ConsoleTrait;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
@@ -59,24 +52,19 @@ class ComposerCommand extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*
|
||||
* @return int|null|void
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function serve()
|
||||
{
|
||||
$this->setupConsole($input, $output);
|
||||
$action = $this->input->getOption('install') ? 'install' : ($this->input->getOption('update') ? 'update' : 'install');
|
||||
|
||||
$action = $input->getOption('install') ? 'install' : ($input->getOption('update') ? 'update' : 'install');
|
||||
|
||||
if ($input->getOption('install')) {
|
||||
if ($this->input->getOption('install')) {
|
||||
$action = 'install';
|
||||
}
|
||||
|
||||
// Updates composer first
|
||||
$output->writeln("\nInstalling vendor dependencies");
|
||||
$output->writeln($this->composerUpdate(GRAV_ROOT, $action));
|
||||
$this->output->writeln("\nInstalling vendor dependencies");
|
||||
$this->output->writeln($this->composerUpdate(GRAV_ROOT, $action));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
<?php
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Console\ConsoleTrait;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* Class InstallCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class InstallCommand extends Command
|
||||
class InstallCommand extends ConsoleCommand
|
||||
{
|
||||
use ConsoleTrait;
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
@@ -51,24 +46,18 @@ class InstallCommand extends Command
|
||||
'destination',
|
||||
InputArgument::OPTIONAL,
|
||||
'Where to install the required bits (default to current project)'
|
||||
|
||||
)
|
||||
->setDescription("Installs the dependencies needed by Grav. Optionally can create symbolic links")
|
||||
->setHelp('The <info>install</info> command installs the dependencies needed by Grav. Optionally can create symbolic links');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*
|
||||
* @return int|null|void
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function serve()
|
||||
{
|
||||
$this->setupConsole($input, $output);
|
||||
|
||||
$dependencies_file = '.dependencies';
|
||||
$this->destination = ($input->getArgument('destination')) ? $input->getArgument('destination') : ROOT_DIR;
|
||||
$this->destination = ($this->input->getArgument('destination')) ? $this->input->getArgument('destination') : ROOT_DIR;
|
||||
|
||||
// fix trailing slash
|
||||
$this->destination = rtrim($this->destination, DS) . DS;
|
||||
@@ -78,7 +67,7 @@ class InstallCommand extends Command
|
||||
$local_config_file = exec('eval echo ~/.grav/config');
|
||||
if (file_exists($local_config_file)) {
|
||||
$this->local_config = Yaml::parse($local_config_file);
|
||||
$output->writeln('Read local config from <cyan>' . $local_config_file . '</cyan>');
|
||||
$this->output->writeln('Read local config from <cyan>' . $local_config_file . '</cyan>');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,22 +77,22 @@ class InstallCommand extends Command
|
||||
} elseif (file_exists($this->destination . $dependencies_file)) {
|
||||
$this->config = Yaml::parse($this->destination . $dependencies_file);
|
||||
} else {
|
||||
$output->writeln('<red>ERROR</red> Missing .dependencies file in <cyan>user/</cyan> folder');
|
||||
$this->output->writeln('<red>ERROR</red> Missing .dependencies file in <cyan>user/</cyan> folder');
|
||||
}
|
||||
|
||||
// If yaml config, process
|
||||
if ($this->config) {
|
||||
if (!$input->getOption('symlink')) {
|
||||
if (!$this->input->getOption('symlink')) {
|
||||
// Updates composer first
|
||||
$output->writeln("\nInstalling vendor dependencies");
|
||||
$output->writeln($this->composerUpdate(GRAV_ROOT, 'install'));
|
||||
$this->output->writeln("\nInstalling vendor dependencies");
|
||||
$this->output->writeln($this->composerUpdate(GRAV_ROOT, 'install'));
|
||||
|
||||
$this->gitclone();
|
||||
} else {
|
||||
$this->symlink();
|
||||
}
|
||||
} else {
|
||||
$output->writeln('<red>ERROR</red> invalid YAML in ' . $dependencies_file);
|
||||
$this->output->writeln('<red>ERROR</red> invalid YAML in ' . $dependencies_file);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,29 +1,25 @@
|
||||
<?php
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Console\ConsoleTrait;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class NewProjectCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class NewProjectCommand extends Command
|
||||
class NewProjectCommand extends ConsoleCommand
|
||||
{
|
||||
use ConsoleTrait;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName("new-project")
|
||||
->setName('new-project')
|
||||
->setAliases(['newproject'])
|
||||
->addArgument(
|
||||
'destination',
|
||||
InputArgument::REQUIRED,
|
||||
@@ -35,37 +31,32 @@ class NewProjectCommand extends Command
|
||||
InputOption::VALUE_NONE,
|
||||
'Symlink the required bits'
|
||||
)
|
||||
->setDescription("Creates a new Grav project with all the dependencies installed")
|
||||
->setHelp("The <info>new-project</info> command is a combination of the `setup` and `install` commands.\nCreates a new Grav instance and performs the installation of all the required dependencies.");
|
||||
->setDescription('Creates a new Grav project with all the dependencies installed')
|
||||
->setHelp('The <info>new-project</info> command is a combination of the `setup` and `install` commands.\nCreates a new Grav instance and performs the installation of all the required dependencies.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*
|
||||
* @return int|null|void
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function serve()
|
||||
{
|
||||
$this->setupConsole($input, $output);
|
||||
|
||||
$sandboxCommand = $this->getApplication()->find('sandbox');
|
||||
$installCommand = $this->getApplication()->find('install');
|
||||
|
||||
$sandboxArguments = new ArrayInput(array(
|
||||
'command' => 'sandbox',
|
||||
'destination' => $input->getArgument('destination'),
|
||||
'-s' => $input->getOption('symlink')
|
||||
'destination' => $this->input->getArgument('destination'),
|
||||
'-s' => $this->input->getOption('symlink')
|
||||
));
|
||||
|
||||
$installArguments = new ArrayInput(array(
|
||||
'command' => 'install',
|
||||
'destination' => $input->getArgument('destination'),
|
||||
'-s' => $input->getOption('symlink')
|
||||
'destination' => $this->input->getArgument('destination'),
|
||||
'-s' => $this->input->getOption('symlink')
|
||||
));
|
||||
|
||||
$sandboxCommand->run($sandboxArguments, $output);
|
||||
$installCommand->run($installArguments, $output);
|
||||
$sandboxCommand->run($sandboxArguments, $this->output);
|
||||
$installCommand->run($installArguments, $this->output);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,160 +1,34 @@
|
||||
<?php
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Common\Data\Blueprints;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\User\User;
|
||||
use Grav\Console\ConsoleTrait;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Helper\Helper;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
|
||||
/**
|
||||
* Class CleanCommand
|
||||
*
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class NewUserCommand extends Command
|
||||
class NewUserCommand extends ConsoleCommand
|
||||
{
|
||||
use ConsoleTrait;
|
||||
|
||||
/**
|
||||
* Configure the command
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName("newuser")
|
||||
->setDescription("Creates a new user")
|
||||
->setHelp('The <info>newuser</info> creates a new user file in user/accounts/ folder');
|
||||
->setName('newuser')
|
||||
->setDescription('DEPRECATED: Creates a new user')
|
||||
->setHelp('The <info>newuser</info> from `bin/grav` has been deprecated. Please refer to `bin/plugin login new-user')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*
|
||||
* @return int|null|void
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function serve()
|
||||
{
|
||||
$this->setupConsole($input, $output);
|
||||
$helper = $this->getHelper('question');
|
||||
$data = [];
|
||||
|
||||
$this->output->writeln('<green>Create new user</green>');
|
||||
$this->output->writeln('');
|
||||
|
||||
// Get username and validate
|
||||
$question = new Question('Enter a <yellow>username</yellow>: ', 'admin');
|
||||
$question->setValidator(function ($value) {
|
||||
if (!preg_match('/^[a-z0-9_-]{3,16}$/', $value)) {
|
||||
throw new RuntimeException(
|
||||
'Username should be between 3 and 16 characters, including lowercase letters, numbers, underscores, and hyphens. Uppercase letters, spaces, and special characters are not allowed'
|
||||
);
|
||||
}
|
||||
if (file_exists(self::getGrav()['locator']->findResource('user://accounts/' . $value . YAML_EXT))) {
|
||||
throw new RuntimeException(
|
||||
'Username "'.$value.'" already exists, please pick another username'
|
||||
);
|
||||
}
|
||||
return $value;
|
||||
});
|
||||
$username = $helper->ask($this->input, $this->output, $question);
|
||||
|
||||
// Get password and validate
|
||||
$password = $this->askForPassword($helper, 'Enter a <yellow>password</yellow>: ', function ($password1) use ($helper) {
|
||||
if (!preg_match('/(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}/', $password1)) {
|
||||
throw new RuntimeException('Password must contain at least one number and one uppercase and lowercase letter, and at least 8 or more characters');
|
||||
}
|
||||
// Since input is hidden when prompting for passwords, the user is asked to repeat the password
|
||||
return $this->askForPassword($helper, 'Repeat the <yellow>password</yellow>: ', function ($password2) use ($password1) {
|
||||
if (strcmp($password2, $password1)) {
|
||||
throw new RuntimeException('Passwords did not match.');
|
||||
}
|
||||
return $password2;
|
||||
});
|
||||
});
|
||||
$data['password'] = $password;
|
||||
|
||||
// Get email and validate
|
||||
$question = new Question('Enter an <yellow>email</yellow>: ');
|
||||
$question->setValidator(function ($value) {
|
||||
if (!preg_match('/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/', $value)) {
|
||||
throw new RuntimeException(
|
||||
'Not a valid email address'
|
||||
);
|
||||
}
|
||||
return $value;
|
||||
});
|
||||
$data['email'] = $helper->ask($this->input, $this->output, $question);
|
||||
|
||||
// Choose permissions
|
||||
$question = new ChoiceQuestion(
|
||||
'Please choose a set of <yellow>permissions</yellow>:',
|
||||
array('a'=>'admin access', 's'=>'site access', 'b'=>'admin and site access'),
|
||||
'a'
|
||||
);
|
||||
$question->setErrorMessage('permissions %s is invalid.');
|
||||
$permissions_choice = $helper->ask($this->input, $this->output, $question);
|
||||
|
||||
switch ($permissions_choice) {
|
||||
case 'a':
|
||||
$data['access']['admin'] = ['login' => true, 'super' => true];
|
||||
break;
|
||||
case 's':
|
||||
$data['access']['site'] = ['login' => true];
|
||||
break;
|
||||
case 'b':
|
||||
$data['access']['admin'] = ['login' => true, 'super' => true];
|
||||
$data['access']['site'] = ['login' => true];
|
||||
}
|
||||
|
||||
// Get fullname
|
||||
$question = new Question('Enter a <yellow>fullname</yellow>: ');
|
||||
$question->setValidator(function ($value) {
|
||||
if ($value === null || trim($value) == '') {
|
||||
throw new RuntimeException(
|
||||
'Fullname is required'
|
||||
);
|
||||
}
|
||||
return $value;
|
||||
});
|
||||
$data['fullname'] = $helper->ask($this->input, $this->output, $question);
|
||||
|
||||
// Get title
|
||||
$question = new Question('Enter a <yellow>title</yellow>: ');
|
||||
$data['title'] = $helper->ask($this->input, $this->output, $question);
|
||||
|
||||
// Create user object and save it
|
||||
$user = new User($data);
|
||||
$file = CompiledYamlFile::instance(self::getGrav()['locator']->findResource('user://accounts/' . $username . YAML_EXT, true, true));
|
||||
$user->file($file);
|
||||
$user->save();
|
||||
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('<green>Success!</green> User <cyan>'. $username .'</cyan> created.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get password and validate.
|
||||
*
|
||||
* @param Helper $helper
|
||||
* @param string $question
|
||||
* @param callable $validator
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function askForPassword(Helper $helper, $question, callable $validator)
|
||||
{
|
||||
$question = new Question($question);
|
||||
$question->setValidator($validator);
|
||||
$question->setHidden(true);
|
||||
$question->setHiddenFallback(true);
|
||||
return $helper->ask($this->input, $this->output, $question);
|
||||
$this->output->writeln('<red>DEPRECATED COMMAND</red>');
|
||||
$this->output->writeln(' <white>`bin/grav new-user`</white> has been <red>deprecated</red> in favor of the new <white>`bin/plugin login new-user`</white>');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
<?php
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Console\ConsoleTrait;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class SandboxCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class SandboxCommand extends Command
|
||||
class SandboxCommand extends ConsoleCommand
|
||||
{
|
||||
use ConsoleTrait;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@@ -54,7 +48,7 @@ class SandboxCommand extends Command
|
||||
'/.editorconfig' => '/.editorconfig',
|
||||
'/.gitignore' => '/.gitignore',
|
||||
'/CHANGELOG.md' => '/CHANGELOG.md',
|
||||
'/LICENSE' => '/LICENSE',
|
||||
'/LICENSE.txt' => '/LICENSE.txt',
|
||||
'/README.md' => '/README.md',
|
||||
'/index.php' => '/index.php',
|
||||
'/composer.json' => '/composer.json',
|
||||
@@ -96,18 +90,14 @@ class SandboxCommand extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*
|
||||
* @return int|null|void
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function serve()
|
||||
{
|
||||
$this->setupConsole($input, $output);
|
||||
$this->destination = $input->getArgument('destination');
|
||||
$this->destination = $this->input->getArgument('destination');
|
||||
|
||||
// Symlink the Core Stuff
|
||||
if ($input->getOption('symlink')) {
|
||||
if ($this->input->getOption('symlink')) {
|
||||
// Create Some core stuff if it doesn't exist
|
||||
$this->createDirectories();
|
||||
|
||||
|
||||
35
system/src/Grav/Console/ConsoleCommand.php
Normal file
35
system/src/Grav/Console/ConsoleCommand.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
namespace Grav\Console;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class ConsoleCommand
|
||||
*
|
||||
* @package Grav\Console
|
||||
*/
|
||||
class ConsoleCommand extends Command
|
||||
{
|
||||
use ConsoleTrait;
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*
|
||||
* @return int|null|void
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->setupConsole($input, $output);
|
||||
$this->serve();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected function serve() { }
|
||||
|
||||
}
|
||||
@@ -2,20 +2,16 @@
|
||||
namespace Grav\Console\Gpm;
|
||||
|
||||
use Grav\Common\GPM\GPM;
|
||||
use Grav\Console\ConsoleTrait;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class IndexCommand
|
||||
*
|
||||
* @package Grav\Console\Gpm
|
||||
*/
|
||||
class IndexCommand extends Command
|
||||
class IndexCommand extends ConsoleCommand
|
||||
{
|
||||
use ConsoleTrait;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
@@ -25,6 +21,16 @@ class IndexCommand extends Command
|
||||
*/
|
||||
protected $gpm;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $sortKeys = ['name', 'slug', 'author', 'date'];
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -38,30 +44,68 @@ class IndexCommand extends Command
|
||||
InputOption::VALUE_NONE,
|
||||
'Force re-fetching the data from remote'
|
||||
)
|
||||
->addOption(
|
||||
'filter',
|
||||
'F',
|
||||
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
|
||||
'Allows to limit the results based on one or multiple filters input. This can be either portion of a name/slug or a regex'
|
||||
)
|
||||
->addOption(
|
||||
'themes-only',
|
||||
'T',
|
||||
InputOption::VALUE_NONE,
|
||||
'Filters the results to only Themes'
|
||||
)
|
||||
->addOption(
|
||||
'plugins-only',
|
||||
'P',
|
||||
InputOption::VALUE_NONE,
|
||||
'Filters the results to only Plugins'
|
||||
)
|
||||
->addOption(
|
||||
'updates-only',
|
||||
'U',
|
||||
InputOption::VALUE_NONE,
|
||||
'Filters the results to Updatable Themes and Plugins only'
|
||||
)
|
||||
->addOption(
|
||||
'sort',
|
||||
's',
|
||||
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
|
||||
'Allows to sort (ASC) the results based on one or multiple keys. SORT can be either "name", "slug", "author", "date"',
|
||||
['date']
|
||||
)
|
||||
->addOption(
|
||||
'desc',
|
||||
'D',
|
||||
InputOption::VALUE_NONE,
|
||||
'Reverses the order of the output.'
|
||||
)
|
||||
->setDescription("Lists the plugins and themes available for installation")
|
||||
->setHelp('The <info>index</info> command lists the plugins and themes available for installation');
|
||||
->setHelp('The <info>index</info> command lists the plugins and themes available for installation')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*
|
||||
* @return int|null|void
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function serve()
|
||||
{
|
||||
$this->setupConsole($input, $output);
|
||||
$this->options = $this->input->getOptions();
|
||||
|
||||
$this->gpm = new GPM($this->input->getOption('force'));
|
||||
$this->gpm = new GPM($this->options['force']);
|
||||
|
||||
$this->data = $this->gpm->getRepository();
|
||||
|
||||
$this->output->writeln('');
|
||||
|
||||
foreach ($this->data as $type => $packages) {
|
||||
$data = $this->filter($this->data);
|
||||
|
||||
foreach ($data as $type => $packages) {
|
||||
$this->output->writeln("<green>" . ucfirst($type) . "</green> [ " . count($packages) . " ]");
|
||||
|
||||
$index = 0;
|
||||
$index = 0;
|
||||
$packages = $this->sort($packages);
|
||||
foreach ($packages as $slug => $package) {
|
||||
$this->output->writeln(
|
||||
// index
|
||||
@@ -93,15 +137,15 @@ class IndexCommand extends Command
|
||||
*/
|
||||
private function versionDetails($package)
|
||||
{
|
||||
$list = $this->gpm->{'getUpdatable' . ucfirst($package->package_type)}();
|
||||
$package = isset($list[$package->slug]) ? $list[$package->slug] : $package;
|
||||
$type = ucfirst(preg_replace("/s$/", '', $package->package_type));
|
||||
$list = $this->gpm->{'getUpdatable' . ucfirst($package->package_type)}();
|
||||
$package = isset($list[$package->slug]) ? $list[$package->slug] : $package;
|
||||
$type = ucfirst(preg_replace("/s$/", '', $package->package_type));
|
||||
$updatable = $this->gpm->{'is' . $type . 'Updatable'}($package->slug);
|
||||
$installed = $this->gpm->{'is' . $type . 'Installed'}($package->slug);
|
||||
$local = $this->gpm->{'getInstalled' . $type}($package->slug);
|
||||
$local = $this->gpm->{'getInstalled' . $type}($package->slug);
|
||||
|
||||
if (!$installed || !$updatable) {
|
||||
$version = $installed ? $local->version : $package->version;
|
||||
$version = $installed ? $local->version : $package->version;
|
||||
$installed = !$installed ? ' (<magenta>not installed</magenta>)' : ' (<cyan>installed</cyan>)';
|
||||
|
||||
return str_pad(" [v<green>" . $version . "</green>]", 35) . $installed;
|
||||
@@ -116,4 +160,65 @@ class IndexCommand extends Command
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function filter($data)
|
||||
{
|
||||
// filtering and sorting
|
||||
if ($this->options['plugins-only']) {
|
||||
unset($data['themes']);
|
||||
}
|
||||
if ($this->options['themes-only']) {
|
||||
unset($data['plugins']);
|
||||
}
|
||||
|
||||
if ($this->options['filter'] || $this->options['updates-only'] || $this->options['desc']) {
|
||||
foreach ($data as $type => $packages) {
|
||||
foreach ($packages as $slug => $package) {
|
||||
$filter = true;
|
||||
|
||||
// Filtering by string
|
||||
if ($this->options['filter']) {
|
||||
$filter = preg_grep('/(' . (implode('|', $this->options['filter'])) . ')/i', [$slug, $package->name]);
|
||||
}
|
||||
|
||||
// Filtering updatables only
|
||||
if ($this->options['updates-only'] && $filter) {
|
||||
$method = ucfirst(preg_replace("/s$/", '', $package->package_type));
|
||||
$filter = $this->gpm->{'is' . $method . 'Updatable'}($package->slug);
|
||||
}
|
||||
|
||||
if (!$filter) {
|
||||
unset($data[$type][$slug]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $packages
|
||||
*/
|
||||
public function sort($packages)
|
||||
{
|
||||
foreach ($this->options['sort'] as $key) {
|
||||
$packages = $packages->sort(function ($a, $b) use ($key) {
|
||||
switch ($key) {
|
||||
case 'author':
|
||||
return strcmp($a->{$key}['name'], $b->{$key}['name']);
|
||||
break;
|
||||
default:
|
||||
return strcmp($a->$key, $b->$key);
|
||||
}
|
||||
}, $this->options['desc'] ? true : false);
|
||||
}
|
||||
|
||||
return $packages;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,16 @@
|
||||
namespace Grav\Console\Gpm;
|
||||
|
||||
use Grav\Common\GPM\GPM;
|
||||
use Grav\Console\ConsoleTrait;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class InfoCommand
|
||||
* @package Grav\Console\Gpm
|
||||
*/
|
||||
class InfoCommand extends Command
|
||||
class InfoCommand extends ConsoleCommand
|
||||
{
|
||||
use ConsoleTrait;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
@@ -49,20 +44,16 @@ class InfoCommand extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*
|
||||
* @return int|null|void
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function serve()
|
||||
{
|
||||
$this->setupConsole($input, $output);
|
||||
$this->gpm = new GPM($this->input->getOption('force'));
|
||||
|
||||
$foundPackage = $this->gpm->findPackage($input->getArgument('package'));
|
||||
$foundPackage = $this->gpm->findPackage($this->input->getArgument('package'));
|
||||
|
||||
if (!$foundPackage) {
|
||||
$this->output->writeln("The package <cyan>'" . $input->getArgument('package') . "'</cyan> was not found in the Grav repository.");
|
||||
$this->output->writeln("The package <cyan>'" . $this->input->getArgument('package') . "'</cyan> was not found in the Grav repository.");
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln("You can list all the available packages by typing:");
|
||||
$this->output->writeln(" <green>" . $this->argv . " index</green>");
|
||||
@@ -70,7 +61,7 @@ class InfoCommand extends Command
|
||||
exit;
|
||||
}
|
||||
|
||||
$this->output->writeln("Found package <cyan>'" . $input->getArgument('package') . "'</cyan> under the '<green>" . ucfirst($foundPackage->package_type) . "</green>' section");
|
||||
$this->output->writeln("Found package <cyan>'" . $this->input->getArgument('package') . "'</cyan> under the '<green>" . ucfirst($foundPackage->package_type) . "</green>' section");
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln("<cyan>" . $foundPackage->name . "</cyan> [" . $foundPackage->slug . "]");
|
||||
$this->output->writeln(str_repeat('-', strlen($foundPackage->name) + strlen($foundPackage->slug) + 3));
|
||||
|
||||
@@ -5,14 +5,10 @@ use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\GPM\GPM;
|
||||
use Grav\Common\GPM\Installer;
|
||||
use Grav\Common\GPM\Response;
|
||||
use Grav\Common\Inflector;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Console\ConsoleTrait;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
@@ -23,10 +19,8 @@ define('GIT_REGEX', '/http[s]?:\/\/(?:.*@)?(github|bitbucket)(?:.org|.com)\/.*\/
|
||||
* Class InstallCommand
|
||||
* @package Grav\Console\Gpm
|
||||
*/
|
||||
class InstallCommand extends Command
|
||||
class InstallCommand extends ConsoleCommand
|
||||
{
|
||||
use ConsoleTrait;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
@@ -87,15 +81,10 @@ class InstallCommand extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*
|
||||
* @return int|null|void
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function serve()
|
||||
{
|
||||
$this->setupConsole($input, $output);
|
||||
|
||||
$this->gpm = new GPM($this->input->getOption('force'));
|
||||
$this->destination = realpath($this->input->getOption('destination'));
|
||||
|
||||
|
||||
@@ -5,22 +5,17 @@ use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\GPM\Installer;
|
||||
use Grav\Common\GPM\Response;
|
||||
use Grav\Common\GPM\Upgrader;
|
||||
use Grav\Console\ConsoleTrait;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
|
||||
/**
|
||||
* Class SelfupgradeCommand
|
||||
* @package Grav\Console\Gpm
|
||||
*/
|
||||
class SelfupgradeCommand extends Command
|
||||
class SelfupgradeCommand extends ConsoleCommand
|
||||
{
|
||||
use ConsoleTrait;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
@@ -75,14 +70,10 @@ class SelfupgradeCommand extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*
|
||||
* @return int|null|void
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function serve()
|
||||
{
|
||||
$this->setupConsole($input, $output);
|
||||
$this->upgrader = new Upgrader($this->input->getOption('force'));
|
||||
|
||||
$update = $this->upgrader->getAssets()['grav-update'];
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user