mirror of
https://github.com/getgrav/grav.git
synced 2025-12-05 15:29:57 +01:00
Compare commits
231 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c276457390 | ||
|
|
538fa23b9d | ||
|
|
d2bfacad4e | ||
|
|
cc68065f94 | ||
|
|
00f6738b7e | ||
|
|
9310c3f12f | ||
|
|
0fc1f5492f | ||
|
|
4562de083d | ||
|
|
7250a3ecc1 | ||
|
|
ac1874795b | ||
|
|
2fb8fb62c6 | ||
|
|
fd9816c177 | ||
|
|
5a6e32fce7 | ||
|
|
774850f30d | ||
|
|
41f4d269b0 | ||
|
|
18e79ce4fe | ||
|
|
92ce0aa816 | ||
|
|
5ca9180033 | ||
|
|
1856d1f8c7 | ||
|
|
af9810d1b3 | ||
|
|
a454312bd2 | ||
|
|
88063c2a60 | ||
|
|
8c010fbd3c | ||
|
|
7f615f19f7 | ||
|
|
5f7b70748b | ||
|
|
79268130f4 | ||
|
|
8b78b4997e | ||
|
|
e224841650 | ||
|
|
a69e5fa115 | ||
|
|
539b7931e6 | ||
|
|
f303c1243f | ||
|
|
347d8ecebe | ||
|
|
f977092115 | ||
|
|
5430792687 | ||
|
|
e591212166 | ||
|
|
77f19974f5 | ||
|
|
8b34e7dc19 | ||
|
|
3d4fb6a7d4 | ||
|
|
af7d3ccf83 | ||
|
|
a8b6841923 | ||
|
|
e09ab139c8 | ||
|
|
fe6a0a27b3 | ||
|
|
2f4c32e682 | ||
|
|
fdf2884f97 | ||
|
|
8d8129b49e | ||
|
|
a811ee67cc | ||
|
|
f9bfed2b22 | ||
|
|
3991f51cdf | ||
|
|
0902840aa4 | ||
|
|
df475af2fd | ||
|
|
9fb56df9a0 | ||
|
|
b11b39752f | ||
|
|
8cd361ca41 | ||
|
|
4c04dfc747 | ||
|
|
6cc3d15ee7 | ||
|
|
c0bf787dc7 | ||
|
|
4452cf3d1b | ||
|
|
9fa97ad46b | ||
|
|
6c3e1d83a7 | ||
|
|
9f2d1b48e6 | ||
|
|
fd7fd2fb0c | ||
|
|
a58ea17867 | ||
|
|
093e101f1b | ||
|
|
93dd673639 | ||
|
|
71b25190b8 | ||
|
|
1d90cecc11 | ||
|
|
e771393489 | ||
|
|
122107d1f8 | ||
|
|
ebd81a0dc8 | ||
|
|
69282e4423 | ||
|
|
ce71ac352e | ||
|
|
7738e052e1 | ||
|
|
f43e047497 | ||
|
|
9eb06da9a9 | ||
|
|
ce4a9a02d4 | ||
|
|
a8a82e1a3c | ||
|
|
5abc9b320b | ||
|
|
0aba432688 | ||
|
|
d80429a0ea | ||
|
|
d9145b0ebc | ||
|
|
c05252a570 | ||
|
|
acd81eb7c3 | ||
|
|
a3c58fcc5a | ||
|
|
16b541a8ee | ||
|
|
d705530e64 | ||
|
|
84873484d5 | ||
|
|
eb1883854e | ||
|
|
9cb83ba368 | ||
|
|
82bc6fb308 | ||
|
|
5aa95c0b7e | ||
|
|
419b46afb0 | ||
|
|
0b607c5197 | ||
|
|
9da1ec836e | ||
|
|
5639941ff3 | ||
|
|
9d59db5adc | ||
|
|
033f43b82f | ||
|
|
91f4dc1a79 | ||
|
|
9fe24272e3 | ||
|
|
b156c8752a | ||
|
|
93c51584db | ||
|
|
f36f31dfcf | ||
|
|
5cef486981 | ||
|
|
5db100ae49 | ||
|
|
46bcd1b095 | ||
|
|
1fc668f0fc | ||
|
|
5589b48e01 | ||
|
|
cc28f85fde | ||
|
|
a6d46ebcd7 | ||
|
|
ac3b0ba3ec | ||
|
|
e4ff2ea39d | ||
|
|
4653df1350 | ||
|
|
e3c5234038 | ||
|
|
4f0e1ea8b0 | ||
|
|
eaa05ee252 | ||
|
|
0c4d0b5646 | ||
|
|
00ac1da29d | ||
|
|
6ac126867e | ||
|
|
ea7ddeaf55 | ||
|
|
f536dbfc20 | ||
|
|
7261cea92f | ||
|
|
41d9b52005 | ||
|
|
50529ae711 | ||
|
|
1bfa2f31e7 | ||
|
|
2b8e3d16a3 | ||
|
|
c1e9cc2f02 | ||
|
|
a39656d536 | ||
|
|
103369cc64 | ||
|
|
0ab39a9b85 | ||
|
|
af582f88fc | ||
|
|
cccd084a55 | ||
|
|
bde5e65d10 | ||
|
|
874bfe47b8 | ||
|
|
31c12c9c0d | ||
|
|
ed8903ece3 | ||
|
|
5c92069e52 | ||
|
|
79229f6b35 | ||
|
|
f9272887f5 | ||
|
|
aa9af76f02 | ||
|
|
967202cbc3 | ||
|
|
a7007eabfa | ||
|
|
0e75f999cc | ||
|
|
4e3b5039da | ||
|
|
7a2af8e63e | ||
|
|
3fe18a9213 | ||
|
|
bfa9cc294e | ||
|
|
1af0e7068e | ||
|
|
c8d6bfa455 | ||
|
|
1ad80acd72 | ||
|
|
e19b15cacd | ||
|
|
2178971e5e | ||
|
|
ffecd8bb87 | ||
|
|
4326e059a5 | ||
|
|
adaadf3c60 | ||
|
|
c4d590674d | ||
|
|
a86159a02c | ||
|
|
88c9436df9 | ||
|
|
b26d938147 | ||
|
|
97808a8b6a | ||
|
|
6d27da4f9b | ||
|
|
9df91e482e | ||
|
|
eba77a8028 | ||
|
|
82533c17e6 | ||
|
|
fd64fd0822 | ||
|
|
04fb7326e5 | ||
|
|
1d8f41df35 | ||
|
|
1b01dff466 | ||
|
|
75d0b687c1 | ||
|
|
d0e6b23637 | ||
|
|
e9f4c7d9a5 | ||
|
|
a3b76252d1 | ||
|
|
231e278c76 | ||
|
|
5f41beccde | ||
|
|
a44193db16 | ||
|
|
c6dcb22d56 | ||
|
|
8e489f6f09 | ||
|
|
618a461835 | ||
|
|
281eb14178 | ||
|
|
1cbb39a38b | ||
|
|
da0fafa800 | ||
|
|
bfb5a74197 | ||
|
|
cceaeb8d42 | ||
|
|
162409053f | ||
|
|
9d1b50ffbb | ||
|
|
e7568bf8d0 | ||
|
|
353f69deb3 | ||
|
|
24d1d4774e | ||
|
|
b0f37079b0 | ||
|
|
026646cc46 | ||
|
|
258f55fe96 | ||
|
|
d2cd2a3772 | ||
|
|
c8db29ce46 | ||
|
|
8a4a810771 | ||
|
|
412216188d | ||
|
|
9103ea0c52 | ||
|
|
ea1c26caa3 | ||
|
|
0977d330e6 | ||
|
|
f7c2df0050 | ||
|
|
e690e50d86 | ||
|
|
0f87946276 | ||
|
|
9e032a7120 | ||
|
|
17e218bdb3 | ||
|
|
cb705f2d96 | ||
|
|
ac8483f894 | ||
|
|
3098ac9998 | ||
|
|
30246a8666 | ||
|
|
f606999c40 | ||
|
|
affa768efb | ||
|
|
615c91b1c5 | ||
|
|
a0bc5bf765 | ||
|
|
2ab433c339 | ||
|
|
267fa23d51 | ||
|
|
e6565d9394 | ||
|
|
3b848e4a91 | ||
|
|
2c4a724fc0 | ||
|
|
3568c25ef1 | ||
|
|
3948aa77a8 | ||
|
|
0d0c22c940 | ||
|
|
1ac4014d64 | ||
|
|
627646bf35 | ||
|
|
67a325e8e9 | ||
|
|
69e345f64f | ||
|
|
37980d15e4 | ||
|
|
3fc6c47b5e | ||
|
|
dee24787ba | ||
|
|
7d9142fa03 | ||
|
|
51cca81bc8 | ||
|
|
8de6231116 | ||
|
|
b507815eef | ||
|
|
c56efb9a10 | ||
|
|
eba9002400 | ||
|
|
7d5426144d |
@@ -11,3 +11,8 @@ trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# 2 space indentation
|
||||
[*.yaml, *.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,7 +1,7 @@
|
||||
# Composer
|
||||
composer.lock
|
||||
.composer
|
||||
vendor
|
||||
vendor/
|
||||
|
||||
# Sass
|
||||
.sass-cache
|
||||
|
||||
115
CHANGELOG.md
Normal file
115
CHANGELOG.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# v0.9.3 beta
|
||||
## 10/09/2014
|
||||
|
||||
1. [](#new)
|
||||
* GPM (Grav Package Manager) Added
|
||||
* Support for multiple Grav configurations
|
||||
* Dynamic media support via URL
|
||||
* Added inlineCss and inlineJs support for Assets
|
||||
2. [](#improved)
|
||||
* YAML caching for increased performance
|
||||
* Use stream wrapper in pages, plugins and themes
|
||||
* Switched to RocketTheme toolbox for some core functionality
|
||||
* Renamed `setup` CLI command to `sandbox`
|
||||
* Broke cache types out into multiple directories in the cache folder
|
||||
* Removed vendor libs from github repository
|
||||
* Various PSR cleanup of code
|
||||
* Various Blueprint updates to support upcoming Admin plugin
|
||||
* Added ability to filter page children for normal/modular/all
|
||||
* Added `sort_by_key` twig filter
|
||||
* Added `visible()` and `routable()` filters to page collections
|
||||
* Use session class in shutdown process
|
||||
* Improvements to modular page loading
|
||||
* Various code cleanup and optimizations
|
||||
3. [](#bugfix)
|
||||
* Fixed file checking not updating the last modified time. For real this time!
|
||||
* Switched debugger to PRODUCTION mode by default
|
||||
* Various fixes in URI class for increased reliability
|
||||
|
||||
# v0.9.2 beta
|
||||
## 09/15/2014
|
||||
|
||||
1. [](#new)
|
||||
* New flexible site and page metadata support including ObjectGraph and Facebook
|
||||
* New method to get user IP address in URI object
|
||||
* Added new onShutdown() event that fires after connection is closed for Async features
|
||||
2. [](#improved)
|
||||
* Skip assets pipeline minify on Windows platforms by default due to PHP issue 47689
|
||||
* Fixed multiple level menus not highlighting correctly
|
||||
* Updated some blueprints in preparation for admin plugin
|
||||
* Fail gracefully when theme does not exist
|
||||
* Add stream support into ResourceLocator::addPath()
|
||||
* Separate themes from plugins, add themes:// stream and onTask events
|
||||
* Added barDump() to Debugger
|
||||
* Removed stray test page
|
||||
* Override modified only if a non-markdown file was modified
|
||||
* Added assets attributes support
|
||||
* Auto-run composer install when running the Grav CLI
|
||||
* Vendor folder removed from repository
|
||||
* Minor configuration performance optimizations
|
||||
* Minor debugger performance optimizations
|
||||
3. [](#bugfix)
|
||||
* Fix url() twig function when Grav isn't installed at root
|
||||
* Workaround for PHP bug 52065
|
||||
* Fixed getList() method on Pages object that was not working
|
||||
* Fix for open_basedir error
|
||||
* index.php now warns if not running on PHP 5.4
|
||||
* Removed memcached option (redundant)
|
||||
* Removed memcache from auto setup, added memcache server configuration option
|
||||
* Fix broken password validation
|
||||
* Back to proper PSR-4 Autoloader
|
||||
|
||||
# v0.9.1 beta
|
||||
## 09/02/2014
|
||||
|
||||
1. [](#new)
|
||||
* Added new `theme://` PHP stream for current theme
|
||||
2. [](#improved)
|
||||
* Default to new `file` modification checking rather than `folder`
|
||||
* Added support for various markdown link formats to convert to Grav-friendly URLs
|
||||
* Moved configure() from Theme to Themes class
|
||||
* Fix autoloading without composer update -o
|
||||
* Added support for Twig url method
|
||||
* Minor code cleanup
|
||||
3. [](#bugfix)
|
||||
* Fixed issue with page changes not being picked up
|
||||
* Fixed Minify to provide `@supports` tag compatibility
|
||||
* Fixed ResourceLocator not working with multiple paths
|
||||
* Fixed issue with Markdown process not stripping LFs
|
||||
* Restrict file type extensions for added security
|
||||
* Fixed template inheritance
|
||||
* Moved Browser class to proper location
|
||||
|
||||
# v0.9.0 beta
|
||||
## 08/25/2014
|
||||
|
||||
1. [](#new)
|
||||
* Addition of Dependency Injection Container
|
||||
* Refactored plugins to use Symfony Event Dispatcher
|
||||
* New Asset Manager to provide unified management of JavaScript and CSS
|
||||
* Asset Pipelining to provide unification, minify, and optimazation of JavaScript and CSS
|
||||
* Grav Media support directly in Markdown syntax
|
||||
* Additional Grav Generator meta tag in default themes
|
||||
* Added support for PHP Stream Wrapper for resource location
|
||||
* Markdown Extra support
|
||||
* Browser object for fast browser detection
|
||||
2. [](#improved)
|
||||
* PSR-4 Autoloader mechanism
|
||||
* Tracy Debugger new `detect` option to detect running environment
|
||||
* Added new `random` collection sort option
|
||||
* Make media images progressive by default
|
||||
* Additional URI filtering for improved security
|
||||
* Safety checks to ensure PHP 5.4.0+
|
||||
* Move to Slidebars side navigation in default Antimatter theme
|
||||
* Updates to `.htaccess` including section on `RewriteBase` which is needed for some hosting providers
|
||||
3. [](#bugfix)
|
||||
* Fixed issue when installing in an apache userdir (~username) folder
|
||||
* Various mobile CSS issues in default themes
|
||||
* Various minor bug fixes
|
||||
|
||||
|
||||
# v0.8.0 beta
|
||||
## 08/13/2014
|
||||
|
||||
1. [](#new)
|
||||
* Initial Release
|
||||
@@ -1,6 +1,6 @@
|
||||
#  Grav
|
||||
|
||||
Grav is a **Fast**, **Simple**, and **Flexible**, file-based Web-platform. There is **Zero** installation required. Just extract the ZIP archive, and you are already up and running. It follows similar principals to other flat-file CMS platforms, but has a different design philosophy than most.
|
||||
Grav is a **Fast**, **Simple**, and **Flexible**, file-based Web-platform. There is **Zero** installation required. Just extract the ZIP archive, and you are already up and running. It follows similar principals to other flat-file CMS platforms, but has a different design philosophy than most. Grav comes with a powerful **Package Management System** to allow for simple installation and upgrading of plugins and themes, as well as simple updating of Grav itself.
|
||||
|
||||
The underlying architecture of Grav has been designed to use well-established and _best-in-class_ technologies, where applicable, to ensure that Grav is simple to use and easy to extend. Some of these key technologies include:
|
||||
|
||||
|
||||
Binary file not shown.
42
bin/gpm
Executable file
42
bin/gpm
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/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')){
|
||||
// Before we can even start, we need to run composer first
|
||||
echo "Preparing to install vendor dependencies...\n\n";
|
||||
echo system('php bin/composer.phar --working-dir="'.__DIR__.'/../" --no-interaction install');
|
||||
echo "\n\n";
|
||||
}
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
use Grav\Common\Grav;
|
||||
|
||||
$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['plugins']->init();
|
||||
$grav['themes']->init();
|
||||
|
||||
$app = new Application('Grav Package Manager', GRAV_VERSION);
|
||||
$app->addCommands(array(
|
||||
new \Grav\Console\Gpm\IndexCommand(),
|
||||
new \Grav\Console\Gpm\InfoCommand(),
|
||||
new \Grav\Console\Gpm\InstallCommand(),
|
||||
new \Grav\Console\Gpm\UpdateCommand(),
|
||||
new \Grav\Console\Gpm\SelfupgradeCommand(),
|
||||
));
|
||||
$app->run();
|
||||
17
bin/grav
17
bin/grav
@@ -1,5 +1,6 @@
|
||||
#!/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));
|
||||
@@ -8,13 +9,13 @@ if (version_compare($ver = PHP_VERSION, $req = '5.4.0', '<')) {
|
||||
if (!file_exists(__DIR__ . '/../vendor')){
|
||||
// Before we can even start, we need to run composer first
|
||||
echo "Preparing to install vendor dependencies...\n\n";
|
||||
echo system('php bin/composer.phar --working-dir="'.__DIR__.'/../" --no-interaction install -o');
|
||||
echo system('php bin/composer.phar --working-dir="'.__DIR__.'/../" --no-interaction install');
|
||||
echo "\n\n";
|
||||
}
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
require_once(__DIR__ . '/../vendor/autoload.php');
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
if (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('UTC');
|
||||
@@ -26,11 +27,11 @@ if (!file_exists(ROOT_DIR . 'index.php')) {
|
||||
|
||||
$app = new Application('Grav CLI Application', '0.1.0');
|
||||
$app->addCommands(array(
|
||||
new Grav\Console\InstallCommand(),
|
||||
new Grav\Console\SetupCommand(),
|
||||
new Grav\Console\CleanCommand(),
|
||||
new Grav\Console\ClearCacheCommand(),
|
||||
new Grav\Console\BackupCommand(),
|
||||
new Grav\Console\NewProjectCommand(),
|
||||
new Grav\Console\Cli\InstallCommand(),
|
||||
new Grav\Console\Cli\SandboxCommand(),
|
||||
new Grav\Console\Cli\CleanCommand(),
|
||||
new Grav\Console\Cli\ClearCacheCommand(),
|
||||
new Grav\Console\Cli\BackupCommand(),
|
||||
new Grav\Console\Cli\NewProjectCommand(),
|
||||
));
|
||||
$app->run();
|
||||
|
||||
@@ -18,8 +18,19 @@
|
||||
"ircmaxell/password-compat": "1.0.*",
|
||||
"mrclay/minify": "dev-master",
|
||||
"donatj/phpuseragentparser": "dev-master",
|
||||
"pimple/pimple": "~3.0"
|
||||
"pimple/pimple": "~3.0",
|
||||
"rockettheme/toolbox": "dev-develop"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/rockettheme/toolbox"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/rhukster/minify"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Grav\\": "system/src/Grav"
|
||||
@@ -28,11 +39,5 @@
|
||||
},
|
||||
"archive": {
|
||||
"exclude": ["VERSION"]
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/rhukster/minify"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
title: Media
|
||||
validation: loose
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
@@ -1,5 +1,6 @@
|
||||
title: Site
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
content:
|
||||
@@ -26,6 +27,7 @@ form:
|
||||
|
||||
taxonomies:
|
||||
type: text
|
||||
size: large
|
||||
label: Taxonomy Types
|
||||
classes: fancy
|
||||
validate:
|
||||
7
system/blueprints/config/streams.yaml
Normal file
7
system/blueprints/config/streams.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
title: File Streams
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
schemes.xxx:
|
||||
type: array
|
||||
@@ -1,6 +1,7 @@
|
||||
title: System
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
content:
|
||||
@@ -233,6 +234,8 @@ form:
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
assets.css_minify:
|
||||
type: toggle
|
||||
@@ -241,6 +244,8 @@ form:
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
assets.css_minify_windows:
|
||||
type: toggle
|
||||
@@ -249,6 +254,8 @@ form:
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
assets.css_rewrite:
|
||||
type: toggle
|
||||
@@ -257,6 +264,8 @@ form:
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
assets.js_pipeline:
|
||||
type: toggle
|
||||
@@ -265,6 +274,8 @@ form:
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
assets.js_minify:
|
||||
type: toggle
|
||||
@@ -273,6 +284,8 @@ form:
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
debugger:
|
||||
type: section
|
||||
@@ -340,7 +353,7 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
debugger.shutdown.close_conection:
|
||||
debugger.shutdown.close_connection:
|
||||
type: toggle
|
||||
label: Shutdown Close Connection
|
||||
highlight: 1
|
||||
@@ -349,5 +362,3 @@ form:
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
|
||||
54
system/blueprints/pages/modular_new.yaml
Normal file
54
system/blueprints/pages/modular_new.yaml
Normal file
@@ -0,0 +1,54 @@
|
||||
rules:
|
||||
slug:
|
||||
pattern: "[a-z][a-z0-9_\-]+"
|
||||
min: 2
|
||||
max: 80
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
section:
|
||||
type: section
|
||||
title: Add Modular Content
|
||||
|
||||
title:
|
||||
type: text
|
||||
label: Page Title
|
||||
validate:
|
||||
required: true
|
||||
|
||||
folder:
|
||||
type: text
|
||||
label: Folder Name
|
||||
validate:
|
||||
type: slug
|
||||
required: true
|
||||
|
||||
|
||||
|
||||
route:
|
||||
type: select
|
||||
label: Page
|
||||
classes: fancy
|
||||
@data-options: '\Grav\Common\Page\Pages::parents'
|
||||
@data-default: '\Grav\Plugin\admin::route'
|
||||
options:
|
||||
'': '- Select -'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
type:
|
||||
type: select
|
||||
classes: fancy
|
||||
label: Modular Template
|
||||
default: default
|
||||
@data-options: '\Grav\Common\Page\Pages::modularTypes'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
modular:
|
||||
type: hidden
|
||||
default: 1
|
||||
validate:
|
||||
type: bool
|
||||
84
system/blueprints/pages/modular_raw.yaml
Normal file
84
system/blueprints/pages/modular_raw.yaml
Normal file
@@ -0,0 +1,84 @@
|
||||
rules:
|
||||
slug:
|
||||
pattern: "[a-z][a-z0-9_\-]+"
|
||||
min: 2
|
||||
max: 80
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
tabs:
|
||||
type: tabs
|
||||
active: 1
|
||||
|
||||
fields:
|
||||
content:
|
||||
type: tab
|
||||
title: Content
|
||||
|
||||
fields:
|
||||
frontmatter:
|
||||
type: frontmatter
|
||||
label: Frontmatter
|
||||
|
||||
|
||||
content:
|
||||
type: markdown
|
||||
label: Content
|
||||
|
||||
uploads:
|
||||
type: uploads
|
||||
label: Page Media
|
||||
|
||||
|
||||
options:
|
||||
type: tab
|
||||
title: Options
|
||||
|
||||
fields:
|
||||
|
||||
columns:
|
||||
type: columns
|
||||
|
||||
fields:
|
||||
column1:
|
||||
type: column
|
||||
|
||||
fields:
|
||||
|
||||
folder:
|
||||
type: text
|
||||
label: Filename
|
||||
validate:
|
||||
type: slug
|
||||
required: true
|
||||
|
||||
route:
|
||||
type: select
|
||||
label: Parent
|
||||
classes: fancy
|
||||
@data-options: '\Grav\Common\Page\Pages::parents'
|
||||
@data-default: '\Grav\Plugin\admin::route'
|
||||
options:
|
||||
'/': '- Root -'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
type:
|
||||
type: select
|
||||
classes: fancy
|
||||
label: Modular Template
|
||||
default: default
|
||||
@data-options: '\Grav\Common\Page\Pages::modularTypes'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
column2:
|
||||
type: column
|
||||
|
||||
fields:
|
||||
order:
|
||||
type: order
|
||||
label: Ordering
|
||||
|
||||
48
system/blueprints/pages/new.yaml
Normal file
48
system/blueprints/pages/new.yaml
Normal file
@@ -0,0 +1,48 @@
|
||||
rules:
|
||||
slug:
|
||||
pattern: "[a-z][a-z0-9_\-]+"
|
||||
min: 2
|
||||
max: 80
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
section:
|
||||
type: section
|
||||
title: Add Page
|
||||
|
||||
title:
|
||||
type: text
|
||||
label: Page Title
|
||||
validate:
|
||||
required: true
|
||||
|
||||
folder:
|
||||
type: text
|
||||
label: Folder Name
|
||||
validate:
|
||||
type: slug
|
||||
required: true
|
||||
|
||||
|
||||
|
||||
route:
|
||||
type: select
|
||||
label: Parent Page
|
||||
classes: fancy
|
||||
@data-options: '\Grav\Common\Page\Pages::parents'
|
||||
@data-default: '\Grav\Plugin\admin::route'
|
||||
options:
|
||||
'/': '- Root -'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
type:
|
||||
type: select
|
||||
classes: fancy
|
||||
label: Display Template
|
||||
default: default
|
||||
@data-options: '\Grav\Common\Page\Pages::types'
|
||||
validate:
|
||||
required: true
|
||||
@@ -5,14 +5,14 @@ rules:
|
||||
max: 80
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
route:
|
||||
type: select
|
||||
label: Parent
|
||||
@data-options: '\Grav\Common\Page\Pages::parents'
|
||||
@data-default: '\Grav\Plugin\admin::route'
|
||||
options:
|
||||
'': '- Root -'
|
||||
|
||||
title:
|
||||
type: text
|
||||
label: Title
|
||||
validate:
|
||||
required: true
|
||||
|
||||
folder:
|
||||
type: text
|
||||
@@ -21,8 +21,22 @@ form:
|
||||
type: slug
|
||||
required: true
|
||||
|
||||
route:
|
||||
type: select
|
||||
label: Parent
|
||||
classes: fancy
|
||||
@data-options: '\Grav\Common\Page\Pages::parents'
|
||||
@data-default: '\Grav\Plugin\admin::route'
|
||||
options:
|
||||
'/': '- Root -'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
type:
|
||||
type: select
|
||||
label: Page Type
|
||||
classes: fancy
|
||||
label: Display Template
|
||||
default: default
|
||||
@data-options: '\Grav\Common\Page\Pages::types'
|
||||
validate:
|
||||
required: true
|
||||
84
system/blueprints/pages/raw.yaml
Normal file
84
system/blueprints/pages/raw.yaml
Normal file
@@ -0,0 +1,84 @@
|
||||
rules:
|
||||
slug:
|
||||
pattern: "[a-z][a-z0-9_\-]+"
|
||||
min: 2
|
||||
max: 80
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
tabs:
|
||||
type: tabs
|
||||
active: 1
|
||||
|
||||
fields:
|
||||
content:
|
||||
type: tab
|
||||
title: Content
|
||||
|
||||
fields:
|
||||
frontmatter:
|
||||
type: frontmatter
|
||||
label: Frontmatter
|
||||
|
||||
|
||||
content:
|
||||
type: markdown
|
||||
label: Content
|
||||
|
||||
uploads:
|
||||
type: uploads
|
||||
label: Page Media
|
||||
|
||||
|
||||
options:
|
||||
type: tab
|
||||
title: Options
|
||||
|
||||
fields:
|
||||
|
||||
columns:
|
||||
type: columns
|
||||
|
||||
fields:
|
||||
column1:
|
||||
type: column
|
||||
|
||||
fields:
|
||||
|
||||
folder:
|
||||
type: text
|
||||
label: Folder Name
|
||||
validate:
|
||||
type: slug
|
||||
required: true
|
||||
|
||||
route:
|
||||
type: select
|
||||
label: Parent
|
||||
classes: fancy
|
||||
@data-options: '\Grav\Common\Page\Pages::parents'
|
||||
@data-default: '\Grav\Plugin\admin::route'
|
||||
options:
|
||||
'/': '- Root -'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
type:
|
||||
type: select
|
||||
classes: fancy
|
||||
label: Display Template
|
||||
default: default
|
||||
@data-options: '\Grav\Common\Page\Pages::types'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
column2:
|
||||
type: column
|
||||
|
||||
fields:
|
||||
order:
|
||||
type: order
|
||||
label: Ordering
|
||||
|
||||
81
system/blueprints/user/account.yaml
Normal file
81
system/blueprints/user/account.yaml
Normal file
@@ -0,0 +1,81 @@
|
||||
title: Site
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
content:
|
||||
type: section
|
||||
title: Account
|
||||
|
||||
fields:
|
||||
username:
|
||||
type: unset
|
||||
|
||||
password:
|
||||
type: password
|
||||
size: large
|
||||
label: Password
|
||||
validate:
|
||||
required: true
|
||||
|
||||
email:
|
||||
type: text
|
||||
size: large
|
||||
label: Email
|
||||
validate:
|
||||
required: true
|
||||
|
||||
fullname:
|
||||
type: text
|
||||
size: large
|
||||
label: Full name
|
||||
validate:
|
||||
required: true
|
||||
|
||||
title:
|
||||
type: text
|
||||
size: large
|
||||
label: Title
|
||||
|
||||
admin:
|
||||
type: section
|
||||
title: Admin Access
|
||||
|
||||
fields:
|
||||
access.admin.super:
|
||||
type: toggle
|
||||
label: Super user
|
||||
default: 0
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
access.admin.login:
|
||||
type: toggle
|
||||
label: Admin login
|
||||
default: 0
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
site:
|
||||
type: section
|
||||
title: Site Access
|
||||
|
||||
fields:
|
||||
access.site.login:
|
||||
type: toggle
|
||||
label: Site login
|
||||
default: 1
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
15
system/blueprints/user/account_new.yaml
Normal file
15
system/blueprints/user/account_new.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
title: Add Account
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
content:
|
||||
type: section
|
||||
title: Add Account
|
||||
|
||||
username:
|
||||
type: text
|
||||
label: Username
|
||||
validate:
|
||||
required: true
|
||||
@@ -1,46 +1,25 @@
|
||||
schemes:
|
||||
plugin:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user/plugins
|
||||
- system/plugins
|
||||
|
||||
asset:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- assets
|
||||
|
||||
cache:
|
||||
image:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- cache
|
||||
|
||||
log:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- logs
|
||||
|
||||
user:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user
|
||||
- user://images
|
||||
|
||||
page:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user/pages
|
||||
- user://pages
|
||||
|
||||
account:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user/accounts
|
||||
- user://accounts
|
||||
|
||||
data:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user/data
|
||||
|
||||
themes:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user/themes
|
||||
- user://data
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
// Some standard defines
|
||||
define('GRAV', true);
|
||||
define('GRAV_VERSION', '0.9.2');
|
||||
define('GRAV_VERSION', '0.9.3');
|
||||
define('DS', '/');
|
||||
|
||||
// Directories and Paths
|
||||
@@ -14,7 +14,7 @@ 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('CACHE_DIR', ROOT_DIR . 'cache/');
|
||||
define('IMAGES_DIR', ROOT_DIR . 'images/');
|
||||
define('LOG_DIR', ROOT_DIR .'logs/');
|
||||
define('VENDOR_DIR', ROOT_DIR .'vendor/');
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Grav\Common;
|
||||
use Closure;
|
||||
use Exception;
|
||||
use FilesystemIterator;
|
||||
use Grav\Common\Config\Config;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use RegexIterator;
|
||||
@@ -64,6 +65,8 @@ class Assets
|
||||
protected $collections = array();
|
||||
protected $css = array();
|
||||
protected $js = array();
|
||||
protected $inline_css = array();
|
||||
protected $inline_js = array();
|
||||
|
||||
// Some configuration variables
|
||||
protected $config;
|
||||
@@ -201,6 +204,25 @@ class Assets
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an inline CSS asset.
|
||||
*
|
||||
* It checks for duplicates.
|
||||
* For adding chunks of string-based inline CSS
|
||||
*
|
||||
* @param mixed $asset
|
||||
* @param int $priority the priority, bigger comes first
|
||||
* @return $this
|
||||
*/
|
||||
public function addInlineCss($asset, $priority = 10) {
|
||||
|
||||
if (is_string($asset) && !in_array($asset, $this->inline_css)) {
|
||||
$this->inline_css[] = $asset;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a CSS asset.
|
||||
*
|
||||
@@ -222,11 +244,32 @@ class Assets
|
||||
return $this;
|
||||
}
|
||||
|
||||
if( ! $this->isRemoteLink($asset))
|
||||
if( !$this->isRemoteLink($asset)) {
|
||||
$asset = $this->buildLocalLink($asset);
|
||||
}
|
||||
|
||||
if( ! in_array($asset, $this->css))
|
||||
$this->css[] = ['asset'=>$asset, 'priority'=>$priority, 'pipeline'=>$pipeline];
|
||||
if( !array_key_exists($asset, $this->css)) {
|
||||
$this->css[$asset] = ['asset'=>$asset, 'priority'=>$priority, 'pipeline'=>$pipeline];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an inline JS asset.
|
||||
*
|
||||
* It checks for duplicates.
|
||||
* For adding chunks of string-based inline JS
|
||||
*
|
||||
* @param mixed $asset
|
||||
* @param int $priority the priority, bigger comes first
|
||||
* @return $this
|
||||
*/
|
||||
public function addInlineJs($asset) {
|
||||
|
||||
if (is_string($asset) && !in_array($asset, $this->inline_js)) {
|
||||
$this->inline_js[] = $asset;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -252,11 +295,13 @@ class Assets
|
||||
return $this;
|
||||
}
|
||||
|
||||
if( ! $this->isRemoteLink($asset))
|
||||
if( !$this->isRemoteLink($asset)) {
|
||||
$asset = $this->buildLocalLink($asset);
|
||||
}
|
||||
|
||||
if( ! in_array($asset, $this->js))
|
||||
$this->js[] = ['asset'=>$asset, 'priority'=>$priority, 'pipeline'=>$pipeline];
|
||||
if( !array_key_exists($asset, $this->js)) {
|
||||
$this->js[$asset] = ['asset' => $asset, 'priority' => $priority, 'pipeline' => $pipeline];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -289,8 +334,19 @@ class Assets
|
||||
}
|
||||
|
||||
|
||||
foreach($this->css as $file)
|
||||
$output .= '<link href="'.$file['asset'].'"'.$attributes.' />'."\n";
|
||||
foreach($this->css as $file) {
|
||||
$output .= '<link href="' . $file['asset'] . '"' . $attributes . ' />' . "\n";
|
||||
}
|
||||
|
||||
// Render Inline CSS
|
||||
if (count($this->inline_css) > 0) {
|
||||
$output .= "<style>\n";
|
||||
foreach($this->inline_css as $inline) {
|
||||
$output .= $inline . "\n";
|
||||
}
|
||||
$output .= "</style>\n";
|
||||
}
|
||||
|
||||
|
||||
return $output;
|
||||
}
|
||||
@@ -322,8 +378,18 @@ class Assets
|
||||
}
|
||||
|
||||
|
||||
foreach($this->js as $file)
|
||||
$output .= '<script src="'.$file['asset'].'"'.$attributes.' ></script>'."\n";
|
||||
foreach($this->js as $file) {
|
||||
$output .= '<script src="' . $file['asset'] . '"' . $attributes . ' ></script>' . "\n";
|
||||
}
|
||||
|
||||
// Render Inline JS
|
||||
if (count($this->inline_js) > 0) {
|
||||
$output .= "<script>\n";
|
||||
foreach($this->inline_js as $inline) {
|
||||
$output .= $inline . "\n";
|
||||
}
|
||||
$output .= "</script>\n";
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace Grav\Common;
|
||||
|
||||
use \Doctrine\Common\Cache\Cache as DoctrineCache;
|
||||
use Grav\Common\Config\Config;
|
||||
|
||||
/**
|
||||
* The GravCache object is used throughout Grav to store and retrieve cached data.
|
||||
@@ -34,6 +35,8 @@ class Cache extends Getters
|
||||
*/
|
||||
protected $enabled;
|
||||
|
||||
protected $cache_dir;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@@ -55,6 +58,8 @@ class Cache extends Getters
|
||||
/** @var Config $config */
|
||||
$this->config = $grav['config'];
|
||||
|
||||
$this->cache_dir = $grav['locator']->findResource('cache://doctrine', true, true);
|
||||
|
||||
/** @var Uri $uri */
|
||||
$uri = $grav['uri'];
|
||||
|
||||
@@ -63,7 +68,7 @@ class Cache extends Getters
|
||||
$this->enabled = (bool) $this->config->get('system.cache.enabled');
|
||||
|
||||
// Cache key allows us to invalidate all cache on configuration changes.
|
||||
$this->key = substr(md5(($prefix ? $prefix : 'g') . $uri->rootUrl(true) . $this->config->key . GRAV_VERSION), 2, 8);
|
||||
$this->key = substr(md5(($prefix ? $prefix : 'g') . $uri->rootUrl(true) . $this->config->key() . GRAV_VERSION), 2, 8);
|
||||
|
||||
$this->driver = $this->getCacheDriver();
|
||||
|
||||
@@ -117,7 +122,7 @@ class Cache extends Getters
|
||||
break;
|
||||
|
||||
default:
|
||||
$driver = new \Doctrine\Common\Cache\FilesystemCache(CACHE_DIR);
|
||||
$driver = new \Doctrine\Common\Cache\FilesystemCache($this->cache_dir);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,259 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Data\Blueprints;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Filesystem\File;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
|
||||
/**
|
||||
* The Config class contains configuration information.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Config extends Data
|
||||
{
|
||||
/**
|
||||
* @var string Configuration location in the disk.
|
||||
*/
|
||||
public $filename;
|
||||
|
||||
/**
|
||||
* @var string MD5 from the files.
|
||||
*/
|
||||
public $key;
|
||||
|
||||
/**
|
||||
* @var array Configuration file list.
|
||||
*/
|
||||
public $files = array();
|
||||
|
||||
/**
|
||||
* @var bool Flag to tell if configuration needs to be saved.
|
||||
*/
|
||||
public $updated = false;
|
||||
public $issues = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct($filename)
|
||||
{
|
||||
$this->filename = realpath(dirname($filename)) . '/' . basename($filename);
|
||||
|
||||
$this->reload(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force reload of the configuration from the disk.
|
||||
*
|
||||
* @param bool $force
|
||||
* @return $this
|
||||
*/
|
||||
public function reload($force = true)
|
||||
{
|
||||
// Build file map.
|
||||
$files = $this->build();
|
||||
$key = md5(json_encode($files) . GRAV_VERSION);
|
||||
|
||||
if ($force || $key != $this->key) {
|
||||
// First take non-blocking lock to the file.
|
||||
File\Config::instance($this->filename)->lock(false);
|
||||
|
||||
// Reset configuration.
|
||||
$this->items = array();
|
||||
$this->files = array();
|
||||
$this->init($files);
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save configuration into file.
|
||||
*
|
||||
* Note: Only saves the file if updated flag is set!
|
||||
*
|
||||
* @return $this
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
// If configuration was updated, store it as cached version.
|
||||
try {
|
||||
$file = File\Config::instance($this->filename);
|
||||
|
||||
// Only save configuration file if it wasn't locked. Also invalidate opcache after saving.
|
||||
// This prevents us from saving the file multiple times in a row and gives faster recovery.
|
||||
if ($file->locked() !== false) {
|
||||
$file->save($this);
|
||||
$file->unlock();
|
||||
}
|
||||
$this->updated = false;
|
||||
} catch (\Exception $e) {
|
||||
$this->issues[] = 'Writing configuration into cache failed.';
|
||||
//throw new \RuntimeException('Writing configuration into cache failed.', 500, $e);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load configuration.
|
||||
*
|
||||
* @param Grav $grav
|
||||
* @return \Grav\Common\Config
|
||||
*/
|
||||
public static function instance(Grav $grav)
|
||||
{
|
||||
$filename = $grav['config_path'];
|
||||
|
||||
// Load cached version if available..
|
||||
if (file_exists($filename)) {
|
||||
require_once $filename;
|
||||
|
||||
if (class_exists('\Grav\Config')) {
|
||||
$instance = new \Grav\Config($filename);
|
||||
}
|
||||
}
|
||||
|
||||
// Or initialize new configuration object..
|
||||
if (!isset($instance)) {
|
||||
$instance = new static($filename);
|
||||
}
|
||||
|
||||
// If configuration was updated, store it as cached version.
|
||||
if ($instance->updated) {
|
||||
$instance->save();
|
||||
}
|
||||
|
||||
|
||||
// If not set, add manually current base url.
|
||||
if (empty($instance->items['system']['base_url_absolute'])) {
|
||||
$instance->items['system']['base_url_absolute'] = $grav['uri']->rootUrl(true);
|
||||
}
|
||||
|
||||
if (empty($instance->items['system']['base_url_relative'])) {
|
||||
$instance->items['system']['base_url_relative'] = $grav['uri']->rootUrl(false);
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert configuration into an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return array('key' => $this->key, 'files' => $this->files, 'items' => $this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize object by loading all the configuration files.
|
||||
*
|
||||
* @param array $files
|
||||
*/
|
||||
protected function init(array $files)
|
||||
{
|
||||
$this->updated = true;
|
||||
|
||||
// Combine all configuration files into one larger lookup table (only keys matter).
|
||||
$allFiles = $files['user'] + $files['plugins'] + $files['system'];
|
||||
|
||||
// Then sort the files to have all parent nodes first.
|
||||
// This is to make sure that child nodes override parents content.
|
||||
uksort(
|
||||
$allFiles,
|
||||
function($a, $b) {
|
||||
$diff = substr_count($a, '/') - substr_count($b, '/');
|
||||
return $diff ? $diff : strcmp($a, $b);
|
||||
}
|
||||
);
|
||||
|
||||
$systemBlueprints = new Blueprints(SYSTEM_DIR . 'blueprints');
|
||||
$pluginBlueprints = new Blueprints(USER_DIR);
|
||||
|
||||
$items = array();
|
||||
foreach ($allFiles as $name => $dummy) {
|
||||
$lookup = array(
|
||||
'system' => SYSTEM_DIR . 'config/' . $name . YAML_EXT,
|
||||
'plugins' => USER_DIR . $name . '/' . basename($name) . YAML_EXT,
|
||||
'user' => USER_DIR . 'config/' . $name . YAML_EXT,
|
||||
);
|
||||
if (strpos($name, 'plugins/') === 0) {
|
||||
$blueprint = $pluginBlueprints->get("{$name}/blueprints");
|
||||
} else {
|
||||
$blueprint = $systemBlueprints->get($name);
|
||||
}
|
||||
|
||||
$data = new Data(array(), $blueprint);
|
||||
foreach ($lookup as $key => $path) {
|
||||
if (is_file($path)) {
|
||||
$data->merge(File\Yaml::instance($path)->content());
|
||||
}
|
||||
}
|
||||
// $data->validate();
|
||||
// $data->filter();
|
||||
|
||||
// Find the current sub-tree location.
|
||||
$current = &$items;
|
||||
$parts = explode('/', $name);
|
||||
foreach ($parts as $part) {
|
||||
if (!isset($current[$part])) {
|
||||
$current[$part] = array();
|
||||
}
|
||||
$current = &$current[$part];
|
||||
}
|
||||
|
||||
// Handle both updated and deleted configuration files.
|
||||
$current = $data->toArray();
|
||||
}
|
||||
|
||||
$this->items = $items;
|
||||
$this->files = $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a list of configuration files with their timestamps. Used for loading settings and caching them.
|
||||
*
|
||||
* @return array
|
||||
* @internal
|
||||
*/
|
||||
protected function build()
|
||||
{
|
||||
// Find all plugins with default configuration options.
|
||||
$plugins = array();
|
||||
$iterator = new \DirectoryIterator(PLUGINS_DIR);
|
||||
|
||||
/** @var \DirectoryIterator $plugin */
|
||||
foreach ($iterator as $plugin) {
|
||||
if ($iterator->isFile() || $iterator->isDot()) continue;
|
||||
|
||||
$name = $plugin->getBasename();
|
||||
$file = $plugin->getPathname() . DS . $name . YAML_EXT;
|
||||
|
||||
if (!file_exists($file)) continue;
|
||||
|
||||
$modified = filemtime($file);
|
||||
$plugins["plugins/{$name}"] = $modified;
|
||||
}
|
||||
|
||||
// Find all system and user configuration files.
|
||||
$options = array(
|
||||
'compare' => 'Filename',
|
||||
'pattern' => '|\.yaml$|',
|
||||
'filters' => array('key' => '|\.yaml$|'),
|
||||
'key' => 'SubPathname',
|
||||
'value' => 'MTime'
|
||||
);
|
||||
|
||||
$system = Folder::all(SYSTEM_DIR . 'config', $options);
|
||||
$user = Folder::all(USER_DIR . 'config', $options);
|
||||
|
||||
return array('system' => $system, 'plugins' => $plugins, 'user' => $user);
|
||||
}
|
||||
}
|
||||
208
system/src/Grav/Common/Config/Blueprints.php
Normal file
208
system/src/Grav/Common/Config/Blueprints.php
Normal file
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\GravTrait;
|
||||
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];
|
||||
}
|
||||
}
|
||||
463
system/src/Grav/Common/Config/Config.php
Normal file
463
system/src/Grav/Common/Config/Config.php
Normal file
@@ -0,0 +1,463 @@
|
||||
<?php
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use RocketTheme\Toolbox\Blueprints\Blueprints;
|
||||
use RocketTheme\Toolbox\File\PhpFile;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
/**
|
||||
* The Config class contains configuration information.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Config extends Data
|
||||
{
|
||||
protected $grav;
|
||||
protected $streams = [
|
||||
'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'],
|
||||
]
|
||||
],
|
||||
'cache' => [
|
||||
'type' => 'Stream',
|
||||
'prefixes' => [
|
||||
'' => ['cache']
|
||||
]
|
||||
],
|
||||
'log' => [
|
||||
'type' => 'Stream',
|
||||
'prefixes' => [
|
||||
'' => ['logs']
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
protected $blueprintFiles = [];
|
||||
protected $configFiles = [];
|
||||
protected $checksum;
|
||||
protected $timestamp;
|
||||
|
||||
protected $configLookup;
|
||||
protected $blueprintLookup;
|
||||
protected $pluginLookup;
|
||||
|
||||
|
||||
public function __construct(array $items = array(), Grav $grav = null)
|
||||
{
|
||||
$this->grav = $grav ?: Grav::instance();
|
||||
|
||||
if (isset($items['@class'])) {
|
||||
if ($items['@class'] != get_class($this)) {
|
||||
throw new \InvalidArgumentException('Unrecognized config cache file!');
|
||||
}
|
||||
// Loading pre-compiled configuration.
|
||||
$this->timestamp = (int) $items['timestamp'];
|
||||
$this->checksum = (string) $items['checksum'];
|
||||
$this->items = (array) $items['data'];
|
||||
} else {
|
||||
// Make sure that
|
||||
if (!isset($items['streams']['schemes'])) {
|
||||
$items['streams']['schemes'] = [];
|
||||
}
|
||||
$items['streams']['schemes'] += $this->streams;
|
||||
|
||||
parent::__construct($items);
|
||||
}
|
||||
$this->check();
|
||||
}
|
||||
|
||||
public function key()
|
||||
{
|
||||
return $this->checksum;
|
||||
}
|
||||
|
||||
public function reload()
|
||||
{
|
||||
$this->check();
|
||||
$this->init();
|
||||
|
||||
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 init()
|
||||
{
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $this->grav['locator'];
|
||||
|
||||
$this->configLookup = $locator->findResources('config://');
|
||||
$this->blueprintLookup = $locator->findResources('blueprints://config');
|
||||
$this->pluginLookup = $locator->findResources('plugins://');
|
||||
|
||||
$checksum = $this->checksum();
|
||||
if ($checksum == $this->checksum) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->checksum = $checksum;
|
||||
|
||||
/** @var Uri $uri */
|
||||
$uri = $this->grav['uri'];
|
||||
|
||||
// If not set, add manually current base url.
|
||||
$this->def('system.base_url_absolute', $uri->rootUrl(true));
|
||||
$this->def('system.base_url_relative', $uri->rootUrl(false));
|
||||
|
||||
$this->loadCompiledBlueprints($this->blueprintLookup, $this->pluginLookup, 'master');
|
||||
$this->loadCompiledConfig($this->configLookup, $this->pluginLookup, 'master');
|
||||
}
|
||||
|
||||
public function checksum()
|
||||
{
|
||||
$checkBlueprints = $this->get('system.cache.check.blueprints', false);
|
||||
$checkConfig = $this->get('system.cache.check.config', true);
|
||||
$checkSystem = $this->get('system.cache.check.system', true);
|
||||
|
||||
if (!$checkBlueprints && !$checkConfig && !$checkSystem) {
|
||||
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->detectFile($this->configLookup, 'system') : [];
|
||||
} else {
|
||||
// Check changes in all configuration files.
|
||||
$cc = $this->getConfigFiles($this->configLookup, $this->pluginLookup);
|
||||
}
|
||||
$cb = $checkBlueprints ? $this->getBlueprintFiles($this->blueprintLookup, $this->pluginLookup) : [];
|
||||
|
||||
return md5(json_encode([$cc, $cb]));
|
||||
}
|
||||
|
||||
protected function loadCompiledBlueprints($blueprints, $plugins, $filename = null)
|
||||
{
|
||||
$checksum = md5(json_encode($blueprints));
|
||||
$filename = $filename
|
||||
? CACHE_DIR . 'compiled/blueprints/' . $filename .'.php'
|
||||
: CACHE_DIR . 'compiled/blueprints/' . $checksum .'.php';
|
||||
$file = PhpFile::instance($filename);
|
||||
|
||||
if ($file->exists()) {
|
||||
$cache = $file->exists() ? $file->content() : null;
|
||||
} else {
|
||||
$cache = null;
|
||||
}
|
||||
|
||||
$blueprintFiles = $this->getBlueprintFiles($blueprints, $plugins);
|
||||
$checksum .= ':'.md5(json_encode($blueprintFiles));
|
||||
$class = get_class($this);
|
||||
|
||||
// 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']);
|
||||
}
|
||||
}
|
||||
|
||||
protected function loadCompiledConfig($configs, $plugins, $filename = null)
|
||||
{
|
||||
$checksum = md5(json_encode($configs));
|
||||
$filename = $filename
|
||||
? CACHE_DIR . 'compiled/config/' . $filename .'.php'
|
||||
: CACHE_DIR . 'compiled/config/' . $checksum .'.php';
|
||||
$file = PhpFile::instance($filename);
|
||||
|
||||
if ($file->exists()) {
|
||||
$cache = $file->exists() ? $file->content() : null;
|
||||
} else {
|
||||
$cache = null;
|
||||
}
|
||||
|
||||
$configFiles = $this->getConfigFiles($configs, $plugins);
|
||||
$checksum .= ':'.md5(json_encode($configFiles));
|
||||
$class = get_class($this);
|
||||
|
||||
// Load real file if cache isn't up to date (or is invalid).
|
||||
if (
|
||||
!is_array($cache)
|
||||
|| $cache['checksum'] != $checksum
|
||||
|| $cache['@class'] != $class
|
||||
) {
|
||||
// Attempt to lock the file for writing.
|
||||
$file->lock(false);
|
||||
|
||||
// Load configuration.
|
||||
foreach ($configFiles as $key => $files) {
|
||||
$this->loadConfig($key);
|
||||
}
|
||||
$cache = [
|
||||
'@class' => $class,
|
||||
'timestamp' => time(),
|
||||
'checksum' => $this->checksum,
|
||||
'data' => $this->toArray()
|
||||
];
|
||||
|
||||
// If compiled file wasn't already locked by another process, save it.
|
||||
if ($file->locked() !== false) {
|
||||
$file->save($cache);
|
||||
$file->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
$this->items = $cache['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Load global blueprints.
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $files
|
||||
*/
|
||||
public function loadBlueprints($key, array $files = null)
|
||||
{
|
||||
if (is_null($files)) {
|
||||
$files = $this->blueprintFiles[$key];
|
||||
}
|
||||
foreach ($files as $name => $item) {
|
||||
$file = CompiledYamlFile::instance($item['file']);
|
||||
$this->blueprints->embed($name, $file->content(), '/');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load global configuration.
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $files
|
||||
*/
|
||||
public function loadConfig($key, array $files = null)
|
||||
{
|
||||
if (is_null($files)) {
|
||||
$files = $this->configFiles[$key];
|
||||
}
|
||||
foreach ($files as $name => $item) {
|
||||
$file = CompiledYamlFile::instance($item['file']);
|
||||
$this->join($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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all configuration files.
|
||||
*
|
||||
* @param array $configs
|
||||
* @param array $plugins
|
||||
* @return array
|
||||
*/
|
||||
protected function getConfigFiles(array $configs, array $plugins)
|
||||
{
|
||||
$list = [];
|
||||
foreach (array_reverse($plugins) as $folder) {
|
||||
$list += $this->detectPlugins($folder);
|
||||
}
|
||||
foreach (array_reverse($configs) as $folder) {
|
||||
$list += $this->detectConfig($folder);
|
||||
}
|
||||
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 (file_exists($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];
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects all instances of the file and returns last modification time.
|
||||
*
|
||||
* @param array $lookups Locations to look up from.
|
||||
* @param string $name
|
||||
* @return array
|
||||
* @internal
|
||||
*/
|
||||
protected function detectFile(array $lookups, $name)
|
||||
{
|
||||
$list = [];
|
||||
$filename = "{$name}.yaml";
|
||||
foreach ($lookups as $lookup) {
|
||||
$path = trim(Folder::getRelativePath($lookup), '/');
|
||||
|
||||
if (is_file("{$lookup}/{$filename}")) {
|
||||
$modified = filemtime("{$lookup}/{$filename}");
|
||||
} else {
|
||||
$modified = 0;
|
||||
}
|
||||
$list[$path] = [$name => ['file' => "{$path}/{$filename}", 'modified' => $modified]];
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use \Symfony\Component\Yaml\Yaml;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
|
||||
/**
|
||||
* Blueprint handles the inside logic of blueprints.
|
||||
@@ -11,26 +11,41 @@ use \Symfony\Component\Yaml\Yaml;
|
||||
*/
|
||||
class Blueprint
|
||||
{
|
||||
use Export;
|
||||
|
||||
public $name;
|
||||
|
||||
public $initialized = false;
|
||||
protected $blueprints;
|
||||
|
||||
protected $items;
|
||||
protected $context;
|
||||
protected $fields;
|
||||
protected $rules = array();
|
||||
protected $nested = array();
|
||||
protected $filter = ['validation' => 1];
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $data
|
||||
* @param Blueprints $context
|
||||
*/
|
||||
public function __construct($name, array $data, Blueprints $context)
|
||||
public function __construct($name, array $data = array(), Blueprints $context = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->blueprints = $data;
|
||||
$this->items = $data;
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set filter for inherited properties.
|
||||
*
|
||||
* @param array $filter List of field names to be inherited.
|
||||
*/
|
||||
public function setFilter(array $filter)
|
||||
{
|
||||
$this->filter = array_flip($filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
@@ -45,7 +60,7 @@ class Blueprint
|
||||
public function get($name, $default = null, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = $this->blueprints;
|
||||
$current = $this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current) && isset($current->{$field})) {
|
||||
$current = $current->{$field};
|
||||
@@ -71,7 +86,7 @@ class Blueprint
|
||||
public function set($name, $value, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = &$this->blueprints;
|
||||
$current = &$this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current)) {
|
||||
// Handle objects.
|
||||
@@ -101,8 +116,8 @@ class Blueprint
|
||||
public function fields()
|
||||
{
|
||||
if (!isset($this->fields)) {
|
||||
$this->fields = isset($this->blueprints['form']['fields']) ? $this->blueprints['form']['fields'] : array();
|
||||
$this->getFields($this->fields);
|
||||
$this->fields = [];
|
||||
$this->embed('', $this->items);
|
||||
}
|
||||
|
||||
return $this->fields;
|
||||
@@ -131,9 +146,10 @@ class Blueprint
|
||||
*
|
||||
* @param array $data1
|
||||
* @param array $data2
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
public function mergeData(array $data1, array $data2)
|
||||
public function mergeData(array $data1, array $data2, $name = null)
|
||||
{
|
||||
// Initialize data
|
||||
$this->fields();
|
||||
@@ -167,6 +183,71 @@ class Blueprint
|
||||
return $this->extraArray($data, $this->nested, $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend blueprint with another blueprint.
|
||||
*
|
||||
* @param Blueprint $extends
|
||||
* @param bool $append
|
||||
*/
|
||||
public function extend(Blueprint $extends, $append = false)
|
||||
{
|
||||
$blueprints = $append ? $this->items : $extends->toArray();
|
||||
$appended = $append ? $extends->toArray() : $this->items;
|
||||
|
||||
$bref_stack = array(&$blueprints);
|
||||
$head_stack = array($appended);
|
||||
|
||||
do {
|
||||
end($bref_stack);
|
||||
|
||||
$bref = &$bref_stack[key($bref_stack)];
|
||||
$head = array_pop($head_stack);
|
||||
|
||||
unset($bref_stack[key($bref_stack)]);
|
||||
|
||||
foreach (array_keys($head) as $key) {
|
||||
if (isset($key, $bref[$key]) && is_array($bref[$key]) && is_array($head[$key])) {
|
||||
$bref_stack[] = &$bref[$key];
|
||||
$head_stack[] = $head[$key];
|
||||
} else {
|
||||
$bref = array_merge($bref, array($key => $head[$key]));
|
||||
}
|
||||
}
|
||||
} while(count($head_stack));
|
||||
|
||||
$this->items = $blueprints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert object into an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getState()
|
||||
{
|
||||
return ['name' => $this->name, 'items' => $this->items, 'rules' => $this->rules, 'nested' => $this->nested];
|
||||
}
|
||||
|
||||
/**
|
||||
* Embed an array to the blueprint.
|
||||
*
|
||||
* @param $name
|
||||
* @param array $value
|
||||
* @param string $separator
|
||||
*/
|
||||
public function embed($name, array $value, $separator = '.')
|
||||
{
|
||||
|
||||
if (!isset($value['form']['fields']) || !is_array($value['form']['fields'])) {
|
||||
return;
|
||||
}
|
||||
// Initialize data
|
||||
$this->fields();
|
||||
$prefix = $name ? strtr($name, $separator, '.') . '.' : '';
|
||||
$params = array_intersect_key($this->filter, $value);
|
||||
$this->parseFormFields($value['form']['fields'], $params, $prefix, $this->fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param array $rules
|
||||
@@ -187,7 +268,7 @@ class Blueprint
|
||||
} elseif (is_array($field) && is_array($val)) {
|
||||
// Array has been defined in blueprints.
|
||||
$this->validateArray($field, $val);
|
||||
} elseif (isset($this->blueprints['validation']) && $this->blueprints['validation'] == 'strict') {
|
||||
} elseif (isset($this->items['form']['validation']) && $this->items['form']['validation'] == 'strict') {
|
||||
// Undefined/extra item.
|
||||
throw new \RuntimeException(sprintf('%s is not defined in blueprints', $key));
|
||||
}
|
||||
@@ -213,7 +294,7 @@ class Blueprint
|
||||
} elseif (is_array($field) && is_array($val)) {
|
||||
// Array has been defined in blueprints.
|
||||
$field = $this->filterArray($field, $val);
|
||||
} elseif (isset($this->blueprints['validation']) && $this->blueprints['validation'] == 'strict') {
|
||||
} elseif (isset($this->items['form']['validation']) && $this->items['form']['validation'] == 'strict') {
|
||||
$field = null;
|
||||
}
|
||||
|
||||
@@ -281,26 +362,32 @@ class Blueprint
|
||||
* Gets all field definitions from the blueprints.
|
||||
*
|
||||
* @param array $fields
|
||||
* @param array $params
|
||||
* @param string $prefix
|
||||
* @param array $current
|
||||
* @internal
|
||||
*/
|
||||
protected function getFields(array &$fields)
|
||||
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'] = $key;
|
||||
$field['name'] = $prefix . $key;
|
||||
$field += $params;
|
||||
|
||||
if (isset($field['fields'])) {
|
||||
// Recursively get all the nested fields.
|
||||
$this->getFields($field['fields']);
|
||||
$newParams = array_intersect_key($this->filter, $field);
|
||||
$this->parseFormFields($field['fields'], $newParams, $prefix, $current[$key]['fields']);
|
||||
} else {
|
||||
// Add rule.
|
||||
$this->rules[$key] = &$field;
|
||||
$this->addProperty($key);
|
||||
$this->rules[$prefix . $key] = &$field;
|
||||
$this->addProperty($prefix . $key);
|
||||
|
||||
foreach ($field as $name => $value) {
|
||||
// Support nested blueprints.
|
||||
if ($name == '@import') {
|
||||
if ($this->context && $name == '@import') {
|
||||
$values = (array) $value;
|
||||
if (!isset($field['fields'])) {
|
||||
$field['fields'] = array();
|
||||
@@ -379,8 +466,8 @@ class Blueprint
|
||||
*/
|
||||
protected function getRule($rule)
|
||||
{
|
||||
if (isset($this->blueprints['rules'][$rule]) && is_array($this->blueprints['rules'][$rule])) {
|
||||
return $this->blueprints['rules'][$rule];
|
||||
if (isset($this->items['rules'][$rule]) && is_array($this->items['rules'][$rule])) {
|
||||
return $this->items['rules'][$rule];
|
||||
}
|
||||
return array();
|
||||
}
|
||||
@@ -404,69 +491,4 @@ class Blueprint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert blueprints into an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->blueprints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert blueprints into YAML string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toYaml()
|
||||
{
|
||||
return Yaml::dump($this->blueprints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert blueprints into JSON string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toJson()
|
||||
{
|
||||
return json_encode($this->blueprints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend blueprint with another blueprint.
|
||||
*
|
||||
* @param Blueprint $extends
|
||||
* @param bool $append
|
||||
*/
|
||||
public function extend(Blueprint $extends, $append = false)
|
||||
{
|
||||
$blueprints = $append ? $this->blueprints : $extends->toArray();
|
||||
$appended = $append ? $extends->toArray() : $this->blueprints;
|
||||
|
||||
$bref_stack = array(&$blueprints);
|
||||
$head_stack = array($appended);
|
||||
|
||||
do {
|
||||
end($bref_stack);
|
||||
|
||||
$bref = &$bref_stack[key($bref_stack)];
|
||||
$head = array_pop($head_stack);
|
||||
|
||||
unset($bref_stack[key($bref_stack)]);
|
||||
|
||||
foreach (array_keys($head) as $key) {
|
||||
if (isset($key, $bref[$key]) && is_array($bref[$key]) && is_array($head[$key])) {
|
||||
$bref_stack[] = &$bref[$key];
|
||||
$head_stack[] = $head[$key];
|
||||
} else {
|
||||
$bref = array_merge($bref, array($key => $head[$key]));
|
||||
}
|
||||
}
|
||||
} while(count($head_stack));
|
||||
|
||||
$this->blueprints = $blueprints;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use \Symfony\Component\Yaml\Yaml;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
|
||||
/**
|
||||
* Blueprints class keeps track on blueprint instances.
|
||||
@@ -16,11 +16,15 @@ class Blueprints
|
||||
protected $instances = array();
|
||||
|
||||
/**
|
||||
* @param string $search Search path.
|
||||
* @param string|array $search Search path.
|
||||
*/
|
||||
public function __construct($search)
|
||||
{
|
||||
$this->search = rtrim($search, '\\/') . '/';
|
||||
if (!is_string($search)) {
|
||||
$this->search = $search;
|
||||
} else {
|
||||
$this->search = rtrim($search, '\\/') . '/';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,11 +37,18 @@ class Blueprints
|
||||
public function get($type)
|
||||
{
|
||||
if (!isset($this->instances[$type])) {
|
||||
if (is_file($this->search . $type . YAML_EXT)) {
|
||||
$blueprints = (array) Yaml::parse($this->search . $type . YAML_EXT);
|
||||
if (is_string($this->search)) {
|
||||
$filename = $this->search . $type . YAML_EXT;
|
||||
} else {
|
||||
$filename = isset($this->search[$type]) ? $this->search[$type] : '';
|
||||
}
|
||||
|
||||
if ($filename && is_file($filename)) {
|
||||
$file = CompiledYamlFile::instance($filename);
|
||||
$blueprints = $file->content();
|
||||
} else {
|
||||
// throw new \RuntimeException("Blueprints for '{$type}' cannot be found! {$this->search}{$type}");
|
||||
$blueprints = array();
|
||||
$blueprints = [];
|
||||
}
|
||||
|
||||
$blueprint = new Blueprint($type, $blueprints, $this);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<?php
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use Grav\Common\Filesystem\FileInterface;
|
||||
use \Grav\Common\Getters;
|
||||
use \Grav\Common\Filesystem\File;
|
||||
use RocketTheme\Toolbox\ArrayTraits\ArrayAccessWithGetters;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Countable;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
use RocketTheme\Toolbox\File\File;
|
||||
use RocketTheme\Toolbox\File\FileInterface;
|
||||
|
||||
/**
|
||||
* Recursive data object
|
||||
@@ -11,8 +13,10 @@ use \Grav\Common\Filesystem\File;
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Data extends Getters implements DataInterface
|
||||
class Data implements DataInterface
|
||||
{
|
||||
use ArrayAccessWithGetters, Countable, Export;
|
||||
|
||||
protected $gettersVariable = 'items';
|
||||
protected $items;
|
||||
|
||||
@@ -22,7 +26,7 @@ class Data extends Getters implements DataInterface
|
||||
protected $blueprints;
|
||||
|
||||
/**
|
||||
* @var File\General
|
||||
* @var File
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
@@ -121,17 +125,64 @@ class Data extends Getters implements DataInterface
|
||||
* @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 def($name, $default = null, $separator = '.')
|
||||
{
|
||||
$this->set($name, $this->get($name, $default, $separator), $separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join two values together by using blueprints if available.
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $value Value to be joined.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
$this->set($name, $value, $separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join two values together by using blueprints if available.
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $value Value to be joined.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
*/
|
||||
public function joinDefaults($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($value, $old, $name, $separator);
|
||||
} else {
|
||||
// No blueprints: replace existing top level variables with the new ones.
|
||||
$value = array_merge($value, $old);
|
||||
}
|
||||
|
||||
$this->set($name, $value, $separator);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Merge two sets of data together.
|
||||
*
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function merge(array $data)
|
||||
{
|
||||
@@ -142,6 +193,21 @@ class Data extends Getters implements DataInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add default data to the set.
|
||||
*
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function setDefaults(array $data)
|
||||
{
|
||||
if ($this->blueprints) {
|
||||
$this->items = $this->blueprints->mergeData($data, $this->items);
|
||||
} else {
|
||||
$this->items = array_merge($data, $this->items);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return blueprints.
|
||||
*
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<?php
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use Grav\Common\Filesystem\FileInterface;
|
||||
use \Grav\Common\Filesystem\File;
|
||||
use RocketTheme\Toolbox\File\FileInterface;
|
||||
|
||||
/**
|
||||
* Data interface
|
||||
|
||||
@@ -114,12 +114,6 @@ class Validation
|
||||
|
||||
protected static function filterText($value, array $params, array $field)
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
var_dump($value);
|
||||
var_dump($params);
|
||||
var_dump($field);
|
||||
die();
|
||||
}
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
|
||||
65
system/src/Grav/Common/File/CompiledFile.php
Normal file
65
system/src/Grav/Common/File/CompiledFile.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
namespace Grav\Common\File;
|
||||
|
||||
use RocketTheme\Toolbox\File\PhpFile;
|
||||
|
||||
/**
|
||||
* Class CompiledFile
|
||||
* @package Grav\Common\File
|
||||
*
|
||||
* @property string $filename
|
||||
* @property string $extension
|
||||
* @property string $raw
|
||||
* @property array|string $content
|
||||
*/
|
||||
trait CompiledFile
|
||||
{
|
||||
/**
|
||||
* Get/set parsed file contents.
|
||||
*
|
||||
* @param mixed $var
|
||||
* @return string
|
||||
*/
|
||||
public function content($var = null)
|
||||
{
|
||||
// If nothing has been loaded, attempt to get pre-compiled version of the file first.
|
||||
if ($var === null && $this->raw === null && $this->content === null) {
|
||||
$key = md5($this->filename);
|
||||
$file = PhpFile::instance(CACHE_DIR . "/compiled/files/{$key}{$this->extension}.php");
|
||||
$modified = $this->modified();
|
||||
$class = get_class($this);
|
||||
|
||||
$cache = $file->exists() ? $file->content() : null;
|
||||
|
||||
// Load real file if cache isn't up to date (or is invalid).
|
||||
if (
|
||||
!isset($cache['@class'])
|
||||
|| $cache['@class'] != $class
|
||||
|| $cache['modified'] != $modified
|
||||
|| $cache['filename'] != $this->filename
|
||||
) {
|
||||
// Attempt to lock the file for writing.
|
||||
$file->lock(false);
|
||||
|
||||
// Decode RAW file into compiled array.
|
||||
$data = $this->decode($this->raw());
|
||||
$cache = [
|
||||
'@class' => $class,
|
||||
'filename' => $this->filename,
|
||||
'modified' => $modified,
|
||||
'data' => $data
|
||||
];
|
||||
|
||||
// If compiled file wasn't already locked by another process, save it.
|
||||
if ($file->locked() !== false) {
|
||||
$file->save($cache);
|
||||
$file->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
$this->content = $cache['data'];
|
||||
}
|
||||
|
||||
return parent::content($var);
|
||||
}
|
||||
}
|
||||
9
system/src/Grav/Common/File/CompiledMarkdownFile.php
Normal file
9
system/src/Grav/Common/File/CompiledMarkdownFile.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
namespace Grav\Common\File;
|
||||
|
||||
use RocketTheme\Toolbox\File\MarkdownFile;
|
||||
|
||||
class CompiledMarkdownFile extends MarkdownFile
|
||||
{
|
||||
use CompiledFile;
|
||||
}
|
||||
9
system/src/Grav/Common/File/CompiledYamlFile.php
Normal file
9
system/src/Grav/Common/File/CompiledYamlFile.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
namespace Grav\Common\File;
|
||||
|
||||
use RocketTheme\Toolbox\File\YamlFile;
|
||||
|
||||
class CompiledYamlFile extends YamlFile
|
||||
{
|
||||
use CompiledFile;
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Filesystem\File;
|
||||
|
||||
/**
|
||||
* File handling class.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Config extends General
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $extension = '.php';
|
||||
|
||||
/**
|
||||
* @var array|General[]
|
||||
*/
|
||||
static protected $instances = array();
|
||||
|
||||
/**
|
||||
* Saves configuration file and invalidates opcache.
|
||||
*
|
||||
* @param mixed $data Optional data to be saved, usually array.
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function save($data = null)
|
||||
{
|
||||
parent::save($data);
|
||||
|
||||
// Invalidate configuration file from the opcache.
|
||||
if (function_exists('opcache_invalidate')) {
|
||||
// PHP 5.5.5+
|
||||
@opcache_invalidate($this->filename);
|
||||
} elseif (function_exists('apc_invalidate')) {
|
||||
// APC
|
||||
@apc_invalidate($this->filename);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check contents and make sure it is in correct format.
|
||||
*
|
||||
* @param \Grav\Common\Config $var
|
||||
* @return \Grav\Common\Config
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function check($var)
|
||||
{
|
||||
if (!($var instanceof \Grav\Common\Config)) {
|
||||
throw new \RuntimeException('Provided data is not configuration');
|
||||
}
|
||||
|
||||
return $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode configuration object into RAW string (PHP class).
|
||||
*
|
||||
* @param \Grav\Common\Config $var
|
||||
* @return string
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function encode($var)
|
||||
{
|
||||
if (!($var instanceof \Grav\Common\Config)) {
|
||||
throw new \RuntimeException('Provided data is not configuration');
|
||||
}
|
||||
|
||||
// Build the object variables string
|
||||
$vars = array();
|
||||
$options = $var->toArray();
|
||||
|
||||
foreach ($options as $k => $v) {
|
||||
if (is_int($v)) {
|
||||
$vars[] = "\tpublic $" . $k . " = " . $v . ";";
|
||||
} elseif (is_bool($v)) {
|
||||
$vars[] = "\tpublic $" . $k . " = " . ($v ? 'true' : 'false') . ";";
|
||||
} elseif (is_scalar($v)) {
|
||||
$vars[] = "\tpublic $" . $k . " = '" . addcslashes($v, '\\\'') . "';";
|
||||
} elseif (is_array($v) || is_object($v)) {
|
||||
$vars[] = "\tpublic $" . $k . " = " . $this->encodeArray((array) $v) . ";";
|
||||
}
|
||||
}
|
||||
$vars = implode("\n", $vars);
|
||||
|
||||
return "<?php\nnamespace Grav;\n\nclass Config extends \\Grav\\Common\\Config {\n {$vars}\n}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get an array as an exported string.
|
||||
*
|
||||
* @param array $a The array to get as a string.
|
||||
* @param int $level Used internally to indent rows.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function encodeArray($a, $level = 1)
|
||||
{
|
||||
$r = array();
|
||||
foreach ($a as $k => $v) {
|
||||
if (is_array($v) || is_object($v)) {
|
||||
$r[] = '"' . $k . '" => ' . $this->encodeArray((array) $v, $level+1);
|
||||
} elseif (is_int($v)) {
|
||||
$r[] = "'" . $k . "' => " . $v;
|
||||
} elseif (is_bool($v)) {
|
||||
$r[] = "'" . $k . "' => " . ($v ? 'true' : 'false');
|
||||
} else {
|
||||
$r[] .= "'" . $k . "' => " . "'" . addslashes($v) . "'";
|
||||
}
|
||||
}
|
||||
|
||||
$tabs = str_repeat("\t", $level);
|
||||
return "array(\n\t{$tabs}" . implode(",\n\t{$tabs}", $r) . "\n{$tabs})";
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode RAW string into contents.
|
||||
*
|
||||
* @param string $var
|
||||
* @return \Grav\Common\Config
|
||||
*/
|
||||
protected function decode($var)
|
||||
{
|
||||
// TODO: improve this one later, works only for single file...
|
||||
return class_exists('\Grav\Config') ? new \Grav\Config($this->filename) : new Config($this->filename);
|
||||
}
|
||||
}
|
||||
@@ -1,352 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Filesystem\File;
|
||||
|
||||
use Grav\Common\Filesystem\FileInterface;
|
||||
|
||||
/**
|
||||
* General file handling class.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class General implements FileInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $filename;
|
||||
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
protected $handle;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
protected $locked;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $extension;
|
||||
|
||||
/**
|
||||
* @var string Raw file contents.
|
||||
*/
|
||||
protected $raw;
|
||||
|
||||
/**
|
||||
* @var array Parsed file contents.
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* @var array|General[]
|
||||
*/
|
||||
static protected $instances = array();
|
||||
|
||||
/**
|
||||
* Get file instance.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return FileInterface
|
||||
*/
|
||||
public static function instance($filename)
|
||||
{
|
||||
if (!isset(static::$instances[$filename])) {
|
||||
static::$instances[$filename] = new static;
|
||||
static::$instances[$filename]->init($filename);
|
||||
}
|
||||
return static::$instances[$filename];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent constructor from being used.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent cloning.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected function __clone()
|
||||
{
|
||||
//Me not like clones! Me smash clones!
|
||||
}
|
||||
|
||||
/**
|
||||
* Set filename.
|
||||
*
|
||||
* @param $filename
|
||||
*/
|
||||
protected function init($filename)
|
||||
{
|
||||
$this->filename = $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set the file location.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
public function filename($var = null)
|
||||
{
|
||||
if ($var !== null) {
|
||||
$this->filename = $var;
|
||||
}
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return basename of the file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function basename()
|
||||
{
|
||||
return basename($this->filename, $this->extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if file exits.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists()
|
||||
{
|
||||
return is_file($this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return file modification time.
|
||||
*
|
||||
* @return int|bool Timestamp or false if file doesn't exist.
|
||||
*/
|
||||
public function modified()
|
||||
{
|
||||
return is_file($this->filename) ? filemtime($this->filename) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock file for writing. You need to manually unlock().
|
||||
*
|
||||
* @param bool $block For non-blocking lock, set the parameter to false.
|
||||
* @return bool
|
||||
*/
|
||||
public function lock($block = true)
|
||||
{
|
||||
if (!$this->handle) {
|
||||
if (!$this->mkdir(dirname($this->filename))) {
|
||||
throw new \RuntimeException('Creating directory failed for ' . $this->filename);
|
||||
}
|
||||
$this->handle = fopen($this->filename, 'wb+');
|
||||
}
|
||||
$lock = $block ? LOCK_EX : LOCK_EX | LOCK_NB;
|
||||
return $this->locked = $this->handle ? flock($this->handle, $lock) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if file has been locked for writing.
|
||||
*
|
||||
* @return bool|null True = locked, false = failed, null = not locked.
|
||||
*/
|
||||
public function locked()
|
||||
{
|
||||
return $this->locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock file.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unlock()
|
||||
{
|
||||
if (!$this->handle) {
|
||||
return;
|
||||
}
|
||||
if ($this->locked) {
|
||||
flock($this->handle, LOCK_UN);
|
||||
$this->locked = null;
|
||||
}
|
||||
fclose($this->handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if file can be written.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function writable()
|
||||
{
|
||||
return is_writable($this->filename) || $this->writableDir(dirname($this->filename));
|
||||
}
|
||||
|
||||
/**
|
||||
* (Re)Load a file and return RAW file contents.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
$this->raw = $this->exists() ? (string) file_get_contents($this->filename) : '';
|
||||
$this->content = null;
|
||||
|
||||
return $this->raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set raw file contents.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
public function raw($var = null)
|
||||
{
|
||||
if ($var !== null) {
|
||||
$this->raw = (string) $var;
|
||||
$this->content = null;
|
||||
}
|
||||
|
||||
if (!is_string($this->raw)) {
|
||||
$this->raw = $this->load();
|
||||
}
|
||||
|
||||
return $this->raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set parsed file contents.
|
||||
*
|
||||
* @param mixed $var
|
||||
* @return string
|
||||
*/
|
||||
public function content($var = null)
|
||||
{
|
||||
if ($var !== null) {
|
||||
$this->content = $this->check($var);
|
||||
|
||||
// Update RAW, too.
|
||||
$this->raw = $this->encode($this->content);
|
||||
|
||||
} elseif ($this->content === null) {
|
||||
// Decode RAW file.
|
||||
$this->content = $this->decode($this->raw());
|
||||
}
|
||||
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save file.
|
||||
*
|
||||
* @param mixed $data Optional data to be saved, usually array.
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function save($data = null)
|
||||
{
|
||||
if ($data !== null) {
|
||||
$this->content($data);
|
||||
}
|
||||
|
||||
if (!$this->locked) {
|
||||
// Obtain blocking lock or fail.
|
||||
if (!$this->lock()) {
|
||||
throw new \RuntimeException('Obtaining write lock failed on file: ' . $this->filename);
|
||||
}
|
||||
$lock = true;
|
||||
}
|
||||
|
||||
if (@fwrite($this->handle, $this->raw()) === false) {
|
||||
$this->unlock();
|
||||
throw new \RuntimeException('Saving file failed: ' . $this->filename);
|
||||
}
|
||||
|
||||
if (isset($lock)) {
|
||||
$this->unlock();
|
||||
}
|
||||
|
||||
// Touch the directory as well, thus marking it modified.
|
||||
@touch(dirname($this->filename));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete file from filesystem.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
return unlink($this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check contents and make sure it is in correct format.
|
||||
*
|
||||
* Override in derived class.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
protected function check($var)
|
||||
{
|
||||
return (string) $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode contents into RAW string.
|
||||
*
|
||||
* Override in derived class.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
protected function encode($var)
|
||||
{
|
||||
return (string) $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode RAW string into contents.
|
||||
*
|
||||
* Override in derived class.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string mixed
|
||||
*/
|
||||
protected function decode($var)
|
||||
{
|
||||
return (string) $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $dir
|
||||
* @return bool
|
||||
* @internal
|
||||
*/
|
||||
protected function mkdir($dir)
|
||||
{
|
||||
return is_dir($dir) || mkdir($dir, 0777, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $dir
|
||||
* @return bool
|
||||
* @internal
|
||||
*/
|
||||
protected function writableDir($dir)
|
||||
{
|
||||
if ($dir && !file_exists($dir)) {
|
||||
return $this->writableDir(dirname($dir));
|
||||
}
|
||||
|
||||
return $dir && is_dir($dir) && is_writable($dir);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Filesystem\File;
|
||||
|
||||
/**
|
||||
* File handling class for JSON.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Json extends General
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $extension = '.json';
|
||||
|
||||
/**
|
||||
* @var array|General[]
|
||||
*/
|
||||
static protected $instances = array();
|
||||
|
||||
/**
|
||||
* Check contents and make sure it is in correct format.
|
||||
*
|
||||
* @param array $var
|
||||
* @return array
|
||||
*/
|
||||
protected function check($var)
|
||||
{
|
||||
return (array) $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode contents into RAW string.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
protected function encode($var)
|
||||
{
|
||||
return (string) json_encode($var);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode RAW string into contents.
|
||||
*
|
||||
* @param string $var
|
||||
* @return array mixed
|
||||
*/
|
||||
protected function decode($var)
|
||||
{
|
||||
return (array) json_decode($var);
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Filesystem\File;
|
||||
|
||||
/**
|
||||
* File handling class for Log files.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Log extends General
|
||||
{
|
||||
/**
|
||||
* @var array|General[]
|
||||
*/
|
||||
static protected $instances = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
protected function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->extension = '.log';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check contents and make sure it is in correct format.
|
||||
*
|
||||
* @param array $var
|
||||
* @return array
|
||||
*/
|
||||
protected function check($var)
|
||||
{
|
||||
return (array) $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode contents into RAW string (unsupported).
|
||||
*
|
||||
* @param string $var
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function encode($var)
|
||||
{
|
||||
throw new \Exception('Saving log file is forbidden.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode RAW string into contents.
|
||||
*
|
||||
* @param string $var
|
||||
* @return array mixed
|
||||
*/
|
||||
protected function decode($var)
|
||||
{
|
||||
$lines = (array) preg_split('#(\r\n|\n|\r)#', $var);
|
||||
|
||||
$results = array();
|
||||
foreach ($lines as $line) {
|
||||
preg_match('#^\[(.*)\] (.*) @ (.*) @@ (.*)$#', $line, $matches);
|
||||
if ($matches) {
|
||||
$results[] = ['date' => $matches[1], 'message' => $matches[2], 'url' => $matches[3], 'file' => $matches[4]];
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Filesystem\File;
|
||||
|
||||
use \Symfony\Component\Yaml\Yaml as YamlParser;
|
||||
|
||||
/**
|
||||
* File handling class.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Markdown extends General
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $extension = '.md';
|
||||
|
||||
/**
|
||||
* @var array|General[]
|
||||
*/
|
||||
static protected $instances = array();
|
||||
|
||||
/**
|
||||
* Get/set file header.
|
||||
*
|
||||
* @param array $var
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function header(array $var = null)
|
||||
{
|
||||
$content = $this->content();
|
||||
|
||||
if ($var !== null) {
|
||||
$content['header'] = $var;
|
||||
$this->content($content);
|
||||
}
|
||||
|
||||
return $content['header'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set markdown content.
|
||||
*
|
||||
* @param string $var
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function markdown($var = null)
|
||||
{
|
||||
$content = $this->content();
|
||||
|
||||
if ($var !== null) {
|
||||
$content['markdown'] = (string) $var;
|
||||
$this->content($content);
|
||||
}
|
||||
|
||||
return $content['markdown'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check contents and make sure it is in correct format.
|
||||
*
|
||||
* @param array $var
|
||||
* @return array
|
||||
*/
|
||||
protected function check($var)
|
||||
{
|
||||
$var = (array) $var;
|
||||
if (!isset($var['header']) || !is_array($var['header'])) {
|
||||
$var['header'] = array();
|
||||
}
|
||||
if (!isset($var['markdown']) || !is_string($var['markdown'])) {
|
||||
$var['markdown'] = '';
|
||||
}
|
||||
|
||||
return $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode contents into RAW string.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
protected function encode($var)
|
||||
{
|
||||
// Create Markdown file with YAML header.
|
||||
$o = (!empty($var['header']) ? "---\n" . trim(YamlParser::dump($var['header'])) . "\n---\n\n" : '') . $var['markdown'];
|
||||
|
||||
// Normalize line endings to Unix style.
|
||||
$o = preg_replace("/(\r\n|\r)/", "\n", $o);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode RAW string into contents.
|
||||
*
|
||||
* @param string $var
|
||||
* @return array mixed
|
||||
*/
|
||||
protected function decode($var)
|
||||
{
|
||||
$content = array();
|
||||
|
||||
// Normalize line endings to Unix style.
|
||||
$var = preg_replace("/(\r\n|\r)/", "\n", $var);
|
||||
|
||||
// Parse header.
|
||||
preg_match("/---\n(.+?)\n---(\n\n|$)/uism", $var, $m);
|
||||
$content['header'] = isset($m[1]) ? YamlParser::parse(preg_replace("/\n\t/", "\n ", $m[1])) : array();
|
||||
|
||||
// Strip header to get content.
|
||||
$content['markdown'] = trim(preg_replace("/---\n(.+?)\n---(\n\n|$)/uism", '', $var));
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Filesystem\File;
|
||||
|
||||
use \Symfony\Component\Yaml\Yaml as YamlParser;
|
||||
|
||||
/**
|
||||
* File handling class for YAML.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Yaml extends General
|
||||
{
|
||||
/**
|
||||
* @var array|General[]
|
||||
*/
|
||||
static protected $instances = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
protected function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->extension = YAML_EXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check contents and make sure it is in correct format.
|
||||
*
|
||||
* @param array $var
|
||||
* @return array
|
||||
*/
|
||||
protected function check($var)
|
||||
{
|
||||
return (array) $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode contents into RAW string.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
protected function encode($var)
|
||||
{
|
||||
return (string) YamlParser::dump($var);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode RAW string into contents.
|
||||
*
|
||||
* @param string $var
|
||||
* @return array mixed
|
||||
*/
|
||||
protected function decode($var)
|
||||
{
|
||||
return (array) YamlParser::parse($var);
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Filesystem;
|
||||
|
||||
/**
|
||||
* File interface.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
interface FileInterface
|
||||
{
|
||||
/**
|
||||
* Get file instance.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return mixed
|
||||
*/
|
||||
public static function instance($filename);
|
||||
|
||||
/**
|
||||
* Check if file exits.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists();
|
||||
|
||||
/**
|
||||
* Return file modification time.
|
||||
*
|
||||
* @return int Timestamp
|
||||
*/
|
||||
public function modified();
|
||||
|
||||
/**
|
||||
* Lock file for writing. Lock gets automatically released during the save().
|
||||
*
|
||||
* @param bool $block For non-blocking lock, set the parameter to false.
|
||||
* @return bool
|
||||
*/
|
||||
public function lock($block = true);
|
||||
|
||||
/**
|
||||
* Returns true if file has been locked for writing.
|
||||
*
|
||||
* @return bool|null True = locked, false = failed, null = not locked.
|
||||
*/
|
||||
public function locked();
|
||||
|
||||
/**
|
||||
* Unlock file.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unlock();
|
||||
|
||||
/**
|
||||
* Check if file can be written.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function writable();
|
||||
|
||||
/**
|
||||
* (Re)Load a file and return its contents.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function load();
|
||||
|
||||
/**
|
||||
* Get/set raw file contents.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
public function raw($var = null);
|
||||
|
||||
/**
|
||||
* Get/set parsed file contents.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
public function content($var = null);
|
||||
|
||||
/**
|
||||
* Save file.
|
||||
*
|
||||
* @param string $data Optional data to be saved.
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function save($data = null);
|
||||
|
||||
/**
|
||||
* Delete file from filesystem.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete();
|
||||
}
|
||||
@@ -12,7 +12,7 @@ abstract class Folder
|
||||
/**
|
||||
* Recursively find the last modified time under given path.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $path
|
||||
* @return int
|
||||
*/
|
||||
public static function lastModifiedFolder($path)
|
||||
@@ -29,13 +29,25 @@ abstract class Folder
|
||||
$last_modified = $dir_modified;
|
||||
}
|
||||
}
|
||||
|
||||
return $last_modified;
|
||||
}
|
||||
|
||||
|
||||
public static function getRelativePath($to, $from = ROOT_DIR)
|
||||
{
|
||||
$from = preg_replace('![\\|/]+!', '/', $from);
|
||||
$to = preg_replace('![\\|/]+!', '/', $to);
|
||||
if (strpos($to, $from) === 0) {
|
||||
$to = substr($to, strlen($from));
|
||||
}
|
||||
|
||||
return $to;
|
||||
}
|
||||
/**
|
||||
* Recursively find the last modified time under given path by file.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $path
|
||||
* @return int
|
||||
*/
|
||||
public static function lastModifiedFile($path)
|
||||
@@ -48,43 +60,43 @@ abstract class Folder
|
||||
|
||||
/** @var \RecursiveDirectoryIterator $file */
|
||||
foreach ($itr as $file) {
|
||||
if (!$file->isDir()) {
|
||||
$file_modified = $file->getMTime();
|
||||
if ($file_modified > $last_modified) {
|
||||
$last_modified = $file_modified;
|
||||
}
|
||||
$file_modified = $file->getMTime();
|
||||
if ($file_modified > $last_modified) {
|
||||
$last_modified = $file_modified;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $last_modified;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return recursive list of all files and directories under given path.
|
||||
*
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function all($path, array $params = array())
|
||||
{
|
||||
$path = realpath($path);
|
||||
|
||||
if ($path === false) {
|
||||
throw new \RuntimeException("Path to {$path} doesn't exist.");
|
||||
}
|
||||
|
||||
$compare = $params['compare'] ? 'get' . $params['compare'] : null;
|
||||
$pattern = $params['pattern'] ? $params['pattern'] : null;
|
||||
$filters = $params['filters'] ? $params['filters'] : null;
|
||||
$key = $params['key'] ? 'get' . $params['key'] : null;
|
||||
$value = $params['value'] ? 'get' . $params['value'] : 'SubPathname';
|
||||
$compare = isset($params['compare']) ? 'get' . $params['compare'] : null;
|
||||
$pattern = isset($params['pattern']) ? $params['pattern'] : null;
|
||||
$filters = isset($params['filters']) ? $params['filters'] : null;
|
||||
$recursive = isset($params['recursive']) ? $params['recursive'] : true;
|
||||
$key = isset($params['key']) ? 'get' . $params['key'] : null;
|
||||
$value = isset($params['value']) ? 'get' . $params['value'] : ($recursive ? 'getSubPathname' : 'getFilename');
|
||||
|
||||
$directory = new \RecursiveDirectoryIterator($path,
|
||||
\RecursiveDirectoryIterator::SKIP_DOTS + \FilesystemIterator::UNIX_PATHS + \FilesystemIterator::CURRENT_AS_SELF);
|
||||
$iterator = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
if ($recursive) {
|
||||
$directory = new \RecursiveDirectoryIterator($path,
|
||||
\RecursiveDirectoryIterator::SKIP_DOTS + \FilesystemIterator::UNIX_PATHS + \FilesystemIterator::CURRENT_AS_SELF);
|
||||
$iterator = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
} else {
|
||||
$iterator = new \FilesystemIterator($path);
|
||||
}
|
||||
|
||||
$results = array();
|
||||
|
||||
@@ -100,20 +112,30 @@ abstract class Folder
|
||||
$fileKey = preg_replace($filters['key'], '', $fileKey);
|
||||
}
|
||||
if (isset($filters['value'])) {
|
||||
$filePath = preg_replace($filters['value'], '', $filePath);
|
||||
$filter = $filters['value'];
|
||||
if (is_callable($filter)) {
|
||||
$filePath = call_user_func($filter, $file);
|
||||
} else {
|
||||
$filePath = preg_replace($filter, '', $filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($fileKey !== null) {
|
||||
$results[$fileKey] = $filePath;
|
||||
} else {
|
||||
$results[] = $filePath;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively copy directory in filesystem.
|
||||
*
|
||||
* @param string $source
|
||||
* @param string $target
|
||||
* @param string $source
|
||||
* @param string $target
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function copy($source, $target)
|
||||
@@ -157,8 +179,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)
|
||||
@@ -186,7 +208,7 @@ abstract class Folder
|
||||
/**
|
||||
* Recursively delete directory from filesystem.
|
||||
*
|
||||
* @param string $target
|
||||
* @param string $target
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function delete($target)
|
||||
@@ -204,6 +226,26 @@ abstract class Folder
|
||||
|
||||
// Make sure that the change will be detected when caching.
|
||||
@touch(dirname($target));
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $folder
|
||||
* @throws \RuntimeException
|
||||
* @internal
|
||||
*/
|
||||
public static function mkdir($folder)
|
||||
{
|
||||
if (is_dir($folder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$success = @mkdir($folder, 0777, true);
|
||||
|
||||
if (!$success) {
|
||||
$error = error_get_last();
|
||||
throw new \RuntimeException($error['message']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,34 +269,16 @@ abstract class Folder
|
||||
|
||||
return @rmdir($folder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $folder
|
||||
* @throws \RuntimeException
|
||||
* @internal
|
||||
*/
|
||||
protected static function mkdir($folder)
|
||||
{
|
||||
if (is_dir($folder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$success = @mkdir($folder, 0777, true);
|
||||
|
||||
if (!$success) {
|
||||
$error = error_get_last();
|
||||
throw new \RuntimeException($error['message']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GravRecursiveFilterIterator extends \RecursiveFilterIterator {
|
||||
|
||||
class GravRecursiveFilterIterator extends \RecursiveFilterIterator
|
||||
{
|
||||
public static $FILTERS = array(
|
||||
'.', '..', '.DS_Store'
|
||||
'..', '.DS_Store'
|
||||
);
|
||||
|
||||
public function accept() {
|
||||
public function accept()
|
||||
{
|
||||
return !in_array(
|
||||
$this->current()->getFilename(),
|
||||
self::$FILTERS,
|
||||
|
||||
364
system/src/Grav/Common/GPM/GPM.php
Normal file
364
system/src/Grav/Common/GPM/GPM.php
Normal file
@@ -0,0 +1,364 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\Iterator;
|
||||
|
||||
class GPM extends Iterator
|
||||
{
|
||||
/**
|
||||
* Local installed Packages
|
||||
* @var Packages
|
||||
*/
|
||||
private $installed;
|
||||
|
||||
/**
|
||||
* Remote available Packages
|
||||
* @var Packages
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var Remote\Grav
|
||||
*/
|
||||
public $grav;
|
||||
|
||||
/**
|
||||
* Internal cache
|
||||
* @var Iterator
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* Creates a new GPM instance with Local and Remote packages available
|
||||
* @param boolean $refresh Applies to Remote Packages only and forces a refetch of data
|
||||
* @param callable $callback Either a function or callback in array notation
|
||||
*/
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
$this->installed = new Local\Packages();
|
||||
$this->repository = new Remote\Packages($refresh, $callback);
|
||||
$this->grav = new Remote\Grav($refresh, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Locally installed packages
|
||||
* @return Iterator The installed packages
|
||||
*/
|
||||
public function getInstalled()
|
||||
{
|
||||
return $this->installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of locally installed packages
|
||||
* @return integer Amount of installed packages
|
||||
*/
|
||||
public function countInstalled()
|
||||
{
|
||||
$installed = $this->getInstalled();
|
||||
|
||||
return count($installed['plugins']) + count($installed['themes']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the instance of a specific Plugin
|
||||
* @param string $slug The slug of the Plugin
|
||||
* @return Package The instance of the Plugin
|
||||
*/
|
||||
public function getInstalledPlugin($slug)
|
||||
{
|
||||
return $this->installed['plugins'][$slug];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Locally installed plugins
|
||||
* @return Iterator The installed plugins
|
||||
*/
|
||||
public function getInstalledPlugins()
|
||||
{
|
||||
return $this->installed['plugins'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a Plugin is installed
|
||||
* @param string $slug The slug of the Plugin
|
||||
* @return boolean True if the Plugin has been installed. False otherwise
|
||||
*/
|
||||
public function isPluginInstalled($slug)
|
||||
{
|
||||
return isset($this->installed['plugins'][$slug]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the instance of a specific Theme
|
||||
* @param string $slug The slug of the Theme
|
||||
* @return Package The instance of the Theme
|
||||
*/
|
||||
public function getInstalledTheme($slug)
|
||||
{
|
||||
return $this->installed['themes'][$slug];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Locally installed themes
|
||||
* @return Iterator The installed themes
|
||||
*/
|
||||
public function getInstalledThemes()
|
||||
{
|
||||
return $this->installed['themes'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a Theme is installed
|
||||
* @param string $slug The slug of the Theme
|
||||
* @return boolean True if the Theme has been installed. False otherwise
|
||||
*/
|
||||
public function isThemeInstalled($slug)
|
||||
{
|
||||
return isset($this->installed['themes'][$slug]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of updates available
|
||||
* @return integer Amount of available updates
|
||||
*/
|
||||
public function countUpdates()
|
||||
{
|
||||
$count = 0;
|
||||
|
||||
$count += count($this->getUpdatablePlugins());
|
||||
$count += count($this->getUpdatableThemes());
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of Plugins and Themes that can be updated.
|
||||
* Plugins and Themes are extended with the `available` property that relies to the remote version
|
||||
* @return array Array of updatable Plugins and Themes.
|
||||
* Format: ['total' => int, 'plugins' => array, 'themes' => array]
|
||||
*/
|
||||
public function getUpdatable()
|
||||
{
|
||||
$plugins = $this->getUpdatablePlugins();
|
||||
$themes = $this->getUpdatableThemes();
|
||||
|
||||
$items = [
|
||||
'total' => count($plugins)+count($themes),
|
||||
'plugins' => $plugins,
|
||||
'themes' => $themes
|
||||
];
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of Plugins that can be updated.
|
||||
* The Plugins are extended with the `available` property that relies to the remote version
|
||||
* @return Iterator Array of updatable Plugins
|
||||
*/
|
||||
public function getUpdatablePlugins()
|
||||
{
|
||||
$items = [];
|
||||
$repository = $this->repository['plugins'];
|
||||
|
||||
// local cache to speed things up
|
||||
if (isset($this->cache[__METHOD__])) {
|
||||
return $this->cache[__METHOD__];
|
||||
}
|
||||
|
||||
foreach ($this->installed['plugins'] as $slug => $plugin) {
|
||||
if (!isset($repository[$slug]) || $plugin->symlink) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$local_version = $plugin->version ? $plugin->version : 'Unknown';
|
||||
$remote_version = $repository[$slug]->version;
|
||||
|
||||
if (version_compare($local_version, $remote_version) < 0) {
|
||||
$repository[$slug]->available = $remote_version;
|
||||
$repository[$slug]->version = $local_version;
|
||||
$items[$slug] = $repository[$slug];
|
||||
}
|
||||
}
|
||||
|
||||
$this->cache[__METHOD__] = $items;
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a Plugin or Theme is updatable
|
||||
* @param string $slug The slug of the package
|
||||
* @return boolean True if updatable. False otherwise or if not found
|
||||
*/
|
||||
public function isUpdatable($slug)
|
||||
{
|
||||
return $this->isPluginUpdatable($slug) || $this->isThemeUpdatable($slug);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a Plugin is updatable
|
||||
* @param string $plugin The slug of the Plugin
|
||||
* @return boolean True if the Plugin is updatable. False otherwise
|
||||
*/
|
||||
public function isPluginUpdatable($plugin)
|
||||
{
|
||||
return array_key_exists($plugin, $this->getUpdatablePlugins());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of Themes that can be updated.
|
||||
* The Themes are extended with the `available` property that relies to the remote version
|
||||
* @return Iterator Array of updatable Themes
|
||||
*/
|
||||
public function getUpdatableThemes()
|
||||
{
|
||||
$items = [];
|
||||
$repository = $this->repository['themes'];
|
||||
|
||||
// local cache to speed things up
|
||||
if (isset($this->cache[__METHOD__])) {
|
||||
return $this->cache[__METHOD__];
|
||||
}
|
||||
|
||||
foreach ($this->installed['themes'] as $slug => $plugin) {
|
||||
if (!isset($repository[$slug]) || $plugin->symlink) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$local_version = $plugin->version ? $plugin->version : 'Unknown';
|
||||
$remote_version = $repository[$slug]->version;
|
||||
|
||||
if (version_compare($local_version, $remote_version) < 0) {
|
||||
$repository[$slug]->available = $remote_version;
|
||||
$repository[$slug]->version = $local_version;
|
||||
$items[$slug] = $repository[$slug];
|
||||
}
|
||||
}
|
||||
|
||||
$this->cache[__METHOD__] = $items;
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a Theme is Updatable
|
||||
* @param string $theme The slug of the Theme
|
||||
* @return boolean True if the Theme is updatable. False otherwise
|
||||
*/
|
||||
public function isThemeUpdatable($theme)
|
||||
{
|
||||
return array_key_exists($theme, $this->getUpdatableThemes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Plugin from the repository
|
||||
* @param string $slug The slug of the Plugin
|
||||
* @return mixed Package if found, NULL if not
|
||||
*/
|
||||
public function getRepositoryPlugin($slug)
|
||||
{
|
||||
return @$this->repository['plugins'][$slug];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of Plugins available in the repository
|
||||
* @return Iterator The Plugins remotely available
|
||||
*/
|
||||
public function getRepositoryPlugins()
|
||||
{
|
||||
return $this->repository['plugins'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Theme from the repository
|
||||
* @param string $slug The slug of the Theme
|
||||
* @return mixed Package if found, NULL if not
|
||||
*/
|
||||
public function getRepositoryTheme($slug)
|
||||
{
|
||||
return @$this->repository['themes'][$slug];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of Themes available in the repository
|
||||
* @return Iterator The Themes remotely available
|
||||
*/
|
||||
public function getRepositoryThemes()
|
||||
{
|
||||
return $this->repository['themes'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of Plugins and Themes available in the repository
|
||||
* @return array Array of available Plugins and Themes
|
||||
* Format: ['plugins' => array, 'themes' => array]
|
||||
*/
|
||||
public function getRepository()
|
||||
{
|
||||
return $this->repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a Package in the repository
|
||||
* @param string $search Can be either the slug or the name
|
||||
* @return Package Package if found, FALSE if not
|
||||
*/
|
||||
public function findPackage($search)
|
||||
{
|
||||
$search = strtolower($search);
|
||||
if ($found = $this->getRepositoryTheme($search)) {
|
||||
return $found;
|
||||
}
|
||||
|
||||
if ($found = $this->getRepositoryPlugin($search)) {
|
||||
return $found;
|
||||
}
|
||||
|
||||
foreach ($this->getRepositoryThemes() as $slug => $theme) {
|
||||
if ($search == $slug || $search == $theme->name) {
|
||||
return $theme;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->getRepositoryPlugins() as $slug => $plugin) {
|
||||
if ($search == $slug || $search == $plugin->name) {
|
||||
return $plugin;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of Plugins and Themes available in the repository
|
||||
* @return array Array of available Plugins and Themes
|
||||
* Format: ['plugins' => array, 'themes' => array]
|
||||
*/
|
||||
/**
|
||||
* Searches for a list of Packages in the repository
|
||||
* @param array $searches An array of either slugs or names
|
||||
* @return array Array of found Packages
|
||||
* Format: ['total' => int, 'not_found' => array, <found-slugs>]
|
||||
*/
|
||||
public function findPackages($searches = [])
|
||||
{
|
||||
$packages = ['total' => 0, 'not_found' => []];
|
||||
|
||||
foreach ($searches as $search) {
|
||||
if ($found = $this->findPackage($search)) {
|
||||
if (!isset($packages[$found->package_type])) {
|
||||
$packages[$found->package_type] = [];
|
||||
}
|
||||
|
||||
$packages[$found->package_type][$found->slug] = $found;
|
||||
$packages['total']++;
|
||||
} else {
|
||||
$packages['not_found'][] = $search;
|
||||
}
|
||||
}
|
||||
|
||||
return $packages;
|
||||
}
|
||||
}
|
||||
275
system/src/Grav/Common/GPM/Installer.php
Normal file
275
system/src/Grav/Common/GPM/Installer.php
Normal file
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
|
||||
class Installer
|
||||
{
|
||||
/** @const No error */
|
||||
const OK = 0;
|
||||
/** @const Target already exists */
|
||||
const EXISTS = 1;
|
||||
/** @const Target is a symbolic link */
|
||||
const IS_LINK = 2;
|
||||
/** @const Target doesn't exist */
|
||||
const NOT_FOUND = 4;
|
||||
/** @const Target is not a directory */
|
||||
const NOT_DIRECTORY = 8;
|
||||
/** @const Target is not a Grav instance */
|
||||
const NOT_GRAV_ROOT = 16;
|
||||
/** @const Error while trying to open the ZIP package */
|
||||
const ZIP_OPEN_ERROR = 32;
|
||||
/** @const Error while trying to extract the ZIP package */
|
||||
const ZIP_EXTRACT_ERROR = 64;
|
||||
|
||||
/**
|
||||
* Destination folder on which validation checks are applied
|
||||
* @var string
|
||||
*/
|
||||
protected static $target;
|
||||
|
||||
/**
|
||||
* Error Code
|
||||
* @var integer
|
||||
*/
|
||||
protected static $error = 0;
|
||||
|
||||
/**
|
||||
* Default options for the install
|
||||
* @var array
|
||||
*/
|
||||
protected static $options = [
|
||||
'overwrite' => true,
|
||||
'ignore_symlinks' => true,
|
||||
'sophisticated' => false,
|
||||
'install_path' => '',
|
||||
'exclude_checks' => [self::EXISTS, self::NOT_FOUND, self::IS_LINK]
|
||||
];
|
||||
|
||||
/**
|
||||
* Installs a given package to a given destination.
|
||||
*
|
||||
* @param string $package The local path to the ZIP package
|
||||
* @param string $destination The local path to the Grav Instance
|
||||
* @param array $options Options to use for installing. ie, ['install_path' => 'user/themes/antimatter']
|
||||
*
|
||||
* @return boolean True if everything went fine, False otherwise.
|
||||
*/
|
||||
public static function install($package, $destination, $options = [])
|
||||
{
|
||||
$destination = rtrim($destination, DS);
|
||||
$options = array_merge(self::$options, $options);
|
||||
$install_path = rtrim($destination . DS . ltrim($options['install_path'], DS), DS);
|
||||
|
||||
if (!self::isGravInstance($destination) || !self::isValidDestination($install_path, $options['exclude_checks'])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
self::lastErrorCode() == self::IS_LINK && $options['ignore_symlinks'] ||
|
||||
self::lastErrorCode() == self::EXISTS && !$options['overwrite']
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$zip = new \ZipArchive();
|
||||
$archive = $zip->open($package);
|
||||
$tmp = CACHE_DIR . DS . 'tmp/Grav-' . uniqid();
|
||||
|
||||
if ($archive !== true) {
|
||||
self::$error = self::ZIP_OPEN_ERROR;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Folder::mkdir($tmp);
|
||||
|
||||
$unzip = $zip->extractTo($tmp);
|
||||
|
||||
if (!$unzip) {
|
||||
self::$error = self::ZIP_EXTRACT_ERROR;
|
||||
|
||||
$zip->close();
|
||||
Folder::delete($tmp);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!$options['sophisticated']) {
|
||||
self::nonSophisticatedInstall($zip, $install_path, $tmp);
|
||||
} else {
|
||||
self::sophisticatedInstall($zip, $install_path, $tmp);
|
||||
}
|
||||
|
||||
Folder::delete($tmp);
|
||||
$zip->close();
|
||||
|
||||
self::$error = self::OK;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public static function nonSophisticatedInstall($zip, $install_path, $tmp)
|
||||
{
|
||||
$container = $zip->getNameIndex(0); // TODO: better way of determining if zip has container folder
|
||||
if (file_exists($install_path)) {
|
||||
Folder::delete($install_path);
|
||||
}
|
||||
|
||||
Folder::move($tmp . DS . $container, $install_path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function sophisticatedInstall($zip, $install_path, $tmp)
|
||||
{
|
||||
for ($i = 0, $l = $zip->numFiles; $i < $l; $i++) {
|
||||
$filename = $zip->getNameIndex($i);
|
||||
$fileinfo = pathinfo($filename);
|
||||
$depth = count(explode(DS, rtrim($filename, '/')));
|
||||
|
||||
if ($depth > 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$path = $install_path . DS . $fileinfo['basename'];
|
||||
|
||||
if (is_link($path)) {
|
||||
continue;
|
||||
} else {
|
||||
if (is_dir($path)) {
|
||||
Folder::delete($path);
|
||||
Folder::move($tmp . DS . $filename, $path);
|
||||
|
||||
if ($fileinfo['basename'] == 'bin') {
|
||||
foreach (glob($path . DS . '*') as $file) {
|
||||
@chmod($file, 0755);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (is_file($path)) {
|
||||
@unlink($path);
|
||||
@copy($tmp . DS . $filename, $path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a set of checks on the destination and sets the Error if any
|
||||
*
|
||||
* @param string $destination The directory to run validations at
|
||||
* @param array $exclude An array of constants to exclude from the validation
|
||||
*
|
||||
* @return boolean True if validation passed. False otherwise
|
||||
*/
|
||||
public static function isValidDestination($destination, $exclude = [])
|
||||
{
|
||||
self::$error = 0;
|
||||
self::$target = $destination;
|
||||
|
||||
if (is_link($destination)) {
|
||||
self::$error = self::IS_LINK;
|
||||
} elseif (file_exists($destination)) {
|
||||
self::$error = self::EXISTS;
|
||||
} elseif (!file_exists($destination)) {
|
||||
self::$error = self::NOT_FOUND;
|
||||
} elseif (!is_dir($destination)) {
|
||||
self::$error = self::NOT_DIRECTORY;
|
||||
}
|
||||
|
||||
if (count($exclude) && in_array(self::$error, $exclude)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !(self::$error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the given path is a Grav Instance
|
||||
*
|
||||
* @param string $target The local path to the Grav Instance
|
||||
*
|
||||
* @return boolean True if is a Grav Instance. False otherwise
|
||||
*/
|
||||
public static function isGravInstance($target)
|
||||
{
|
||||
self::$error = 0;
|
||||
self::$target = $target;
|
||||
|
||||
if (
|
||||
!file_exists($target . DS . 'index.php') ||
|
||||
!file_exists($target . DS . 'bin') ||
|
||||
!file_exists($target . DS . 'user') ||
|
||||
!file_exists($target . DS . 'system' . DS . 'config' . DS . 'system.yaml')
|
||||
) {
|
||||
self::$error = self::NOT_GRAV_ROOT;
|
||||
}
|
||||
|
||||
return !self::$error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last error occurred in a string message format
|
||||
* @return string The message of the last error
|
||||
*/
|
||||
public static function lastErrorMsg()
|
||||
{
|
||||
$msg = 'Unknown Error';
|
||||
|
||||
switch (self::$error) {
|
||||
case 0:
|
||||
$msg = 'No Error';
|
||||
break;
|
||||
|
||||
case self::EXISTS:
|
||||
$msg = 'The target path "' . self::$target . '" already exists';
|
||||
break;
|
||||
|
||||
case self::IS_LINK:
|
||||
$msg = 'The target path "' . self::$target . '" is a symbolic link';
|
||||
break;
|
||||
|
||||
case self::NOT_FOUND:
|
||||
$msg = 'The target path "' . self::$target . '" does not appear to exist';
|
||||
break;
|
||||
|
||||
case self::NOT_DIRECTORY:
|
||||
$msg = 'The target path "' . self::$target . '" does not appear to be a folder';
|
||||
break;
|
||||
|
||||
case self::NOT_GRAV_ROOT:
|
||||
$msg = 'The target path "' . self::$target . '" does not appear to be a Grav instance';
|
||||
break;
|
||||
|
||||
case self::ZIP_OPEN_ERROR:
|
||||
$msg = 'Unable to open the package file';
|
||||
break;
|
||||
|
||||
case self::ZIP_EXTRACT_ERROR:
|
||||
$msg = 'An error occurred while extracting the package';
|
||||
break;
|
||||
|
||||
default:
|
||||
return 'Unknown error';
|
||||
break;
|
||||
}
|
||||
|
||||
return $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last error code of the occurred error
|
||||
* @return integer The code of the last error
|
||||
*/
|
||||
public static function lastErrorCode()
|
||||
{
|
||||
return self::$error;
|
||||
}
|
||||
}
|
||||
32
system/src/Grav/Common/GPM/Local/Collection.php
Normal file
32
system/src/Grav/Common/GPM/Local/Collection.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Iterator;
|
||||
|
||||
class Collection extends Iterator
|
||||
{
|
||||
use GravTrait;
|
||||
|
||||
public function toJson()
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $theme) {
|
||||
$items[$name] = $theme->toArray();
|
||||
}
|
||||
|
||||
return json_encode($items);
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $theme) {
|
||||
$items[$name] = $theme->toArray();
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
87
system/src/Grav/Common/GPM/Local/Package.php
Normal file
87
system/src/Grav/Common/GPM/Local/Package.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
use Grav\Common\Data\Data;
|
||||
|
||||
/**
|
||||
* Class Package
|
||||
* @package Grav\Common\GPM\Local
|
||||
*/
|
||||
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)
|
||||
{
|
||||
$this->data = $package;
|
||||
$this->blueprints = $this->data->blueprints();
|
||||
|
||||
if ($package_type) {
|
||||
$html_description = \Parsedown::instance()->line($this->blueprints->get('description'));
|
||||
$this->blueprints->set('package_type', $package_type);
|
||||
$this->blueprints->set('description_html', $html_description);
|
||||
$this->blueprints->set('description_plain', strip_tags($html_description));
|
||||
$this->blueprints->set('symlink', is_link(USER_DIR . $package_type . DS . $this->blueprints->name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function isEnabled()
|
||||
{
|
||||
return $this->data['enabled'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Data
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
return $this->blueprints->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->toJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function toJson()
|
||||
{
|
||||
return $this->blueprints->toJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->blueprints->toArray();
|
||||
}
|
||||
}
|
||||
28
system/src/Grav/Common/GPM/Local/Packages.php
Normal file
28
system/src/Grav/Common/GPM/Local/Packages.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
use Grav\Common\Iterator;
|
||||
|
||||
class Packages extends Iterator
|
||||
{
|
||||
private $plugins;
|
||||
private $themes;
|
||||
protected static $cache;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// local cache to speed things up
|
||||
if (!isset(self::$cache[__METHOD__])) {
|
||||
self::$cache[__METHOD__] = [
|
||||
'plugins' => new Plugins(),
|
||||
'themes' => new Themes()
|
||||
];
|
||||
}
|
||||
|
||||
$this->plugins = self::$cache[__METHOD__]['plugins'];
|
||||
$this->themes = self::$cache[__METHOD__]['themes'];
|
||||
|
||||
$this->append(['plugins' => $this->plugins]);
|
||||
$this->append(['themes' => $this->themes]);
|
||||
}
|
||||
}
|
||||
26
system/src/Grav/Common/GPM/Local/Plugins.php
Normal file
26
system/src/Grav/Common/GPM/Local/Plugins.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
/**
|
||||
* Class Plugins
|
||||
* @package Grav\Common\GPM\Local
|
||||
*/
|
||||
class Plugins extends Collection
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $type = 'plugins';
|
||||
|
||||
/**
|
||||
* Local Plugins Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$grav = self::$grav;
|
||||
|
||||
foreach ($grav['plugins']->all() as $name => $data) {
|
||||
$this->items[$name] = new Package($data, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
system/src/Grav/Common/GPM/Local/Themes.php
Normal file
15
system/src/Grav/Common/GPM/Local/Themes.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
class Themes extends Collection
|
||||
{
|
||||
private $type = 'themes';
|
||||
public function __construct()
|
||||
{
|
||||
$grav = self::$grav;
|
||||
|
||||
foreach ($grav['themes']->all() as $name => $data) {
|
||||
$this->items[$name] = new Package($data, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
70
system/src/Grav/Common/GPM/Remote/Collection.php
Normal file
70
system/src/Grav/Common/GPM/Remote/Collection.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
use Grav\Common\GPM\Response;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Iterator;
|
||||
use \Doctrine\Common\Cache\Cache as DoctrineCache;
|
||||
use \Doctrine\Common\Cache\FilesystemCache;
|
||||
|
||||
class Collection extends Iterator {
|
||||
use GravTrait;
|
||||
|
||||
/**
|
||||
* The cached data previously fetched
|
||||
* @var string
|
||||
*/
|
||||
protected $raw;
|
||||
|
||||
/**
|
||||
* The lifetime to store the entry in seconds
|
||||
* @var integer
|
||||
*/
|
||||
private $lifetime = 86400;
|
||||
private $repository;
|
||||
private $cache;
|
||||
|
||||
private $plugins, $themes;
|
||||
|
||||
public function __construct($repository = null) {
|
||||
if ($repository == null) {
|
||||
throw new \RuntimeException("A repository is required for storing the cache");
|
||||
}
|
||||
|
||||
$cache_dir = self::$grav['locator']->findResource('cache://gpm', true, true);
|
||||
$this->cache = new FilesystemCache($cache_dir);
|
||||
|
||||
$this->repository = $repository;
|
||||
$this->raw = $this->cache->fetch(md5($this->repository));
|
||||
}
|
||||
|
||||
public function toJson() {
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $theme) {
|
||||
$items[$name] = $theme->toArray();
|
||||
}
|
||||
|
||||
return json_encode($items);
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $theme) {
|
||||
$items[$name] = $theme->toArray();
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function fetch($refresh = false, $callback = null) {
|
||||
if (!$this->raw || $refresh) {
|
||||
$response = Response::get($this->repository, [], $callback);
|
||||
$this->raw = $response;
|
||||
$this->cache->save(md5($this->repository), $this->raw, $this->lifetime);
|
||||
}
|
||||
|
||||
return $this->raw;
|
||||
}
|
||||
}
|
||||
86
system/src/Grav/Common/GPM/Remote/Grav.php
Normal file
86
system/src/Grav/Common/GPM/Remote/Grav.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
class Grav extends Collection
|
||||
{
|
||||
private $repository = 'http://getgrav.org/downloads/grav.json';
|
||||
private $data;
|
||||
|
||||
private $version;
|
||||
private $date;
|
||||
|
||||
/**
|
||||
* @param bool $refresh
|
||||
* @param null $callback
|
||||
*/
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
parent::__construct($this->repository);
|
||||
|
||||
$this->fetch($refresh, $callback);
|
||||
$this->data = json_decode($this->raw);
|
||||
|
||||
$this->version = @$this->data->version ?: '-';
|
||||
$this->date = @$this->data->date ?: '-';
|
||||
|
||||
foreach ($this->data->assets as $slug => $data) {
|
||||
$this->items[$slug] = new Package($data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of assets associated to the latest version of Grav
|
||||
* @return array list of assets
|
||||
*/
|
||||
public function getAssets()
|
||||
{
|
||||
return $this->data->assets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the changelog list for each version of Grav
|
||||
* @param string $diff the version number to start the diff from
|
||||
*
|
||||
* @return array changelog list for each version
|
||||
*/
|
||||
public function getChangelog($diff = null)
|
||||
{
|
||||
if (!$diff) {
|
||||
return $this->data->changelog;
|
||||
}
|
||||
|
||||
$diffLog = [];
|
||||
foreach ($this->data->changelog as $version => $changelog) {
|
||||
preg_match("/[\d\.]+/", $version, $cleanVersion);
|
||||
|
||||
if (!$cleanVersion || version_compare($diff, $cleanVersion[0], ">=")) { continue; }
|
||||
|
||||
$diffLog[$version] = $changelog;
|
||||
}
|
||||
|
||||
return $diffLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest version of Grav available remotely
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the release date of the latest Grav
|
||||
* @return string
|
||||
*/
|
||||
public function getDate()
|
||||
{
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
public function isUpdatable()
|
||||
{
|
||||
return version_compare(GRAV_VERSION, $this->getVersion(), '<');
|
||||
}
|
||||
}
|
||||
32
system/src/Grav/Common/GPM/Remote/Package.php
Normal file
32
system/src/Grav/Common/GPM/Remote/Package.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
class Package {
|
||||
public function __construct($package, $package_type = false) {
|
||||
$this->data = $package;
|
||||
if ($package_type) {
|
||||
$this->data->package_type = $package_type;
|
||||
}
|
||||
}
|
||||
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function __get($key) {
|
||||
return $this->data->$key;
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
return $this->toJson();
|
||||
}
|
||||
|
||||
public function toJson() {
|
||||
return json_encode($this->data);
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
}
|
||||
28
system/src/Grav/Common/GPM/Remote/Packages.php
Normal file
28
system/src/Grav/Common/GPM/Remote/Packages.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
use Grav\Common\Iterator;
|
||||
|
||||
class Packages extends Iterator
|
||||
{
|
||||
private $plugins;
|
||||
private $themes;
|
||||
protected static $cache;
|
||||
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
// local cache to speed things up
|
||||
if (!isset(self::$cache[__METHOD__])) {
|
||||
self::$cache[__METHOD__] = [
|
||||
'plugins' => new Plugins($refresh, $callback),
|
||||
'themes' => new Themes($refresh, $callback)
|
||||
];
|
||||
}
|
||||
|
||||
$this->plugins = self::$cache[__METHOD__]['plugins']->toArray();
|
||||
$this->themes = self::$cache[__METHOD__]['themes']->toArray();
|
||||
|
||||
$this->append(['plugins' => $this->plugins]);
|
||||
$this->append(['themes' => $this->themes]);
|
||||
}
|
||||
}
|
||||
21
system/src/Grav/Common/GPM/Remote/Plugins.php
Normal file
21
system/src/Grav/Common/GPM/Remote/Plugins.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
class Plugins extends Collection
|
||||
{
|
||||
private $repository = 'http://getgrav.org/downloads/plugins.json';
|
||||
private $type = 'plugins';
|
||||
private $data;
|
||||
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
parent::__construct($this->repository);
|
||||
|
||||
$this->fetch($refresh, $callback);
|
||||
$this->data = json_decode($this->raw);
|
||||
|
||||
foreach ($this->data as $slug => $data) {
|
||||
$this->items[$slug] = new Package($data, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
system/src/Grav/Common/GPM/Remote/Themes.php
Normal file
21
system/src/Grav/Common/GPM/Remote/Themes.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
class Themes extends Collection
|
||||
{
|
||||
private $repository = 'http://getgrav.org/downloads/themes.json';
|
||||
private $type = 'themes';
|
||||
private $data;
|
||||
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
parent::__construct($this->repository);
|
||||
|
||||
$this->fetch($refresh, $callback);
|
||||
$this->data = json_decode($this->raw);
|
||||
|
||||
foreach ($this->data as $slug => $data) {
|
||||
$this->items[$slug] = new Package($data, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
220
system/src/Grav/Common/GPM/Response.php
Normal file
220
system/src/Grav/Common/GPM/Response.php
Normal file
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
class Response
|
||||
{
|
||||
/**
|
||||
* The callback for the progress
|
||||
* @var callable Either a function or callback in array notation
|
||||
*/
|
||||
public static $callback = null;
|
||||
|
||||
/**
|
||||
* Which method to use for HTTP calls, can be 'curl', 'fopen' or 'auto'. Auto is default and fopen is the preferred method
|
||||
* @var string
|
||||
*/
|
||||
private static $method = 'auto';
|
||||
|
||||
/**
|
||||
* Default parameters for `curl` and `fopen`
|
||||
* @var array
|
||||
*/
|
||||
private static $defaults = [
|
||||
|
||||
'curl' => [
|
||||
CURLOPT_REFERER => 'Grav GPM',
|
||||
CURLOPT_USERAGENT => 'Grav GPM',
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_HEADER => false,
|
||||
/**
|
||||
* Example of callback parameters from within your own class
|
||||
*/
|
||||
//CURLOPT_NOPROGRESS => false,
|
||||
//CURLOPT_PROGRESSFUNCTION => [$this, 'progress']
|
||||
],
|
||||
'fopen' => [
|
||||
'method' => 'GET',
|
||||
'user_agent' => 'Grav GPM',
|
||||
'max_redirects' => 5,
|
||||
'follow_location' => 1,
|
||||
'timeout' => 15,
|
||||
/**
|
||||
* Example of callback parameters from within your own class
|
||||
*/
|
||||
//'notification' => [$this, 'progress']
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* Sets the preferred method to use for making HTTP calls.
|
||||
* @param string $method Default is `auto`
|
||||
*/
|
||||
public static function setMethod($method = 'auto')
|
||||
{
|
||||
if (!in_array($method, ['auto', 'curl', 'fopen'])) {
|
||||
$method = 'auto';
|
||||
}
|
||||
|
||||
self::$method = $method;
|
||||
|
||||
return new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`
|
||||
* @return string The response of the request
|
||||
*/
|
||||
public static function get($uri = '', $options = [], $callback = null)
|
||||
{
|
||||
if (!self::isCurlAvailable() && !self::isFopenAvailable()) {
|
||||
throw new \RuntimeException('Could not start an HTTP request. `allow_url_open` is disabled and `cURL` is not available');
|
||||
}
|
||||
|
||||
$options = array_replace_recursive(self::$defaults, $options);
|
||||
$method = 'get' . ucfirst(strtolower(self::$method));
|
||||
|
||||
self::$callback = $callback;
|
||||
|
||||
return static::$method($uri, $options, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Progress normalized for cURL and Fopen
|
||||
* @param args Variable length of arguments passed in by stream method
|
||||
* @return array Normalized array with useful data.
|
||||
* Format: ['code' => int|false, 'filesize' => bytes, 'transferred' => bytes, 'percent' => int]
|
||||
*/
|
||||
public static function progress()
|
||||
{
|
||||
static $filesize = null;
|
||||
|
||||
$args = func_get_args();
|
||||
$isCurlResource = is_resource($args[0]) && get_resource_type($args[0]) == 'curl';
|
||||
|
||||
$notification_code = !$isCurlResource ? $args[0] : false;
|
||||
$bytes_transferred = $isCurlResource ? $args[2] : $args[4];
|
||||
|
||||
if ($isCurlResource) {
|
||||
$filesize = $args[1];
|
||||
} elseif ($notification_code == STREAM_NOTIFY_FILE_SIZE_IS) {
|
||||
$filesize = $args[5];
|
||||
}
|
||||
|
||||
if ($bytes_transferred > 0) {
|
||||
if ($notification_code == STREAM_NOTIFY_PROGRESS|STREAM_NOTIFY_COMPLETED || $isCurlResource) {
|
||||
|
||||
$progress = [
|
||||
'code' => $notification_code,
|
||||
'filesize' => $filesize,
|
||||
'transferred' => $bytes_transferred,
|
||||
'percent' => $filesize <= 0 ? '-' : round(($bytes_transferred * 100) / $filesize, 1)
|
||||
];
|
||||
|
||||
if (self::$callback !== null) {
|
||||
call_user_func_array(self::$callback, [$progress]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if cURL is available
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isCurlAvailable()
|
||||
{
|
||||
return function_exists('curl_version');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the remote fopen request is enabled in PHP
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isFopenAvailable()
|
||||
{
|
||||
return preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically picks the preferred method
|
||||
* @return string The response of the request
|
||||
*/
|
||||
private static function getAuto()
|
||||
{
|
||||
if (self::isFopenAvailable()) {
|
||||
return self::getFopen(func_get_args());
|
||||
}
|
||||
|
||||
if (self::isCurlAvailable()) {
|
||||
return self::getCurl(func_get_args());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a HTTP request via cURL
|
||||
* @return string The response of the request
|
||||
*/
|
||||
private static function getCurl()
|
||||
{
|
||||
$args = func_get_args();
|
||||
$uri = $args[0];
|
||||
$options = $args[1];
|
||||
$callback = $args[2];
|
||||
|
||||
$ch = curl_init($uri);
|
||||
curl_setopt_array($ch, $options['curl']);
|
||||
|
||||
if ($callback) {
|
||||
curl_setopt_array(
|
||||
$ch,
|
||||
[
|
||||
CURLOPT_NOPROGRESS => false,
|
||||
CURLOPT_PROGRESSFUNCTION => ['self', 'progress']
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if ($errno = curl_errno($ch)) {
|
||||
$error_message = curl_strerror($errno);
|
||||
throw new \RuntimeException("cURL error ({$errno}):\n {$error_message}");
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a HTTP request via fopen
|
||||
* @return string The response of the request
|
||||
*/
|
||||
private static function getFopen()
|
||||
{
|
||||
if (count($args = func_get_args()) == 1) {
|
||||
$args = $args[0];
|
||||
}
|
||||
|
||||
$uri = $args[0];
|
||||
$options = $args[1];
|
||||
$callback = $args[2];
|
||||
|
||||
if ($callback) {
|
||||
$options['fopen']['notification'] = ['self', 'progress'];
|
||||
}
|
||||
|
||||
$stream = stream_context_create(['http' => $options['fopen']], $options['fopen']);
|
||||
$content = @file_get_contents($uri, false, $stream);
|
||||
|
||||
if ($content === false) {
|
||||
throw new \RuntimeException("Error while trying to download '$uri'");
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
86
system/src/Grav/Common/GPM/Upgrader.php
Normal file
86
system/src/Grav/Common/GPM/Upgrader.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\GPM\Installer;
|
||||
|
||||
class Upgrader
|
||||
{
|
||||
/**
|
||||
* Remote details about latest Grav version
|
||||
* @var Packages
|
||||
*/
|
||||
private $remote;
|
||||
|
||||
/**
|
||||
* Internal cache
|
||||
* @var Iterator
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* Creates a new GPM instance with Local and Remote packages available
|
||||
* @param boolean $refresh Applies to Remote Packages only and forces a refetch of data
|
||||
* @param callable $callback Either a function or callback in array notation
|
||||
*/
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
$this->remote = new Remote\Grav($refresh, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the release date of the latest version of Grav
|
||||
* @return string
|
||||
*/
|
||||
public function getReleaseDate()
|
||||
{
|
||||
return $this->remote->getDate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the installed Grav
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalVersion()
|
||||
{
|
||||
return GRAV_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the remotely available Grav
|
||||
* @return string
|
||||
*/
|
||||
public function getRemoteVersion()
|
||||
{
|
||||
return $this->remote->getVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of assets available to download remotely
|
||||
* @return array
|
||||
*/
|
||||
public function getAssets()
|
||||
{
|
||||
return $this->remote->getAssets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the changelog list for each version of Grav
|
||||
* @param string $diff the version number to start the diff from
|
||||
*
|
||||
* @return array return the chagenlog list for each version
|
||||
*/
|
||||
public function getChangelog($diff = null)
|
||||
{
|
||||
return $this->remote->getChangelog($diff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the currently installed Grav is upgradable to a newer version
|
||||
* @return boolean True if it's upgradable, False otherwise.
|
||||
*/
|
||||
public function isUpgradable()
|
||||
{
|
||||
return version_compare($this->getLocalVersion(), $this->getRemoteVersion(), "<");
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Page\Pages;
|
||||
use Grav\Common\Service\ConfigServiceProvider;
|
||||
use Grav\Common\Service\StreamsServiceProvider;
|
||||
use Grav\Component\DI\Container;
|
||||
use Grav\Component\EventDispatcher\Event;
|
||||
use Grav\Component\EventDispatcher\EventDispatcher;
|
||||
use RocketTheme\Toolbox\DI\Container;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use Grav\Common\Page\Medium;
|
||||
|
||||
/**
|
||||
* Grav
|
||||
@@ -50,8 +53,6 @@ class Grav extends Container
|
||||
{
|
||||
$container = new static($values);
|
||||
|
||||
$container['config_path'] = CACHE_DIR . 'config.php';
|
||||
|
||||
$container['grav'] = $container;
|
||||
|
||||
$container['uri'] = function ($c) {
|
||||
@@ -65,9 +66,6 @@ class Grav extends Container
|
||||
$container['events'] = function ($c) {
|
||||
return new EventDispatcher;
|
||||
};
|
||||
$container['config'] = function ($c) {
|
||||
return Config::instance($c);
|
||||
};
|
||||
$container['cache'] = function ($c) {
|
||||
return new Cache($c);
|
||||
};
|
||||
@@ -90,9 +88,36 @@ class Grav extends Container
|
||||
return new Assets();
|
||||
};
|
||||
$container['page'] = function ($c) {
|
||||
$page = $c['pages']->dispatch($c['uri']->route());
|
||||
/** @var Pages $pages */
|
||||
$pages = $c['pages'];
|
||||
$page = $pages->dispatch($c['uri']->route());
|
||||
|
||||
if (!$page || !$page->routable()) {
|
||||
|
||||
// special case where a media file is requested
|
||||
if (!$page) {
|
||||
$path_parts = pathinfo($c['uri']->route());
|
||||
$page = $c['pages']->dispatch($path_parts['dirname']);
|
||||
if ($page) {
|
||||
$media = $page->media()->all();
|
||||
$media_file = urldecode($path_parts['basename']);
|
||||
if (isset($media[$media_file])) {
|
||||
$medium = $media[$media_file];
|
||||
|
||||
// loop through actions for the image and call them
|
||||
foreach ($c['uri']->query(null,true) as $action => $params) {
|
||||
if (in_array($action, Medium::$valid_actions)) {
|
||||
call_user_func_array(array(&$medium, $action), explode(',', $params));
|
||||
}
|
||||
}
|
||||
header('Content-type: '. $mime = $medium->get('mime'));
|
||||
echo file_get_contents($medium->path());
|
||||
die;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no page found, fire event
|
||||
$event = $c->fireEvent('onPageNotFound');
|
||||
|
||||
if (isset($event->page)) {
|
||||
@@ -111,6 +136,7 @@ class Grav extends Container
|
||||
};
|
||||
|
||||
$container->register(new StreamsServiceProvider);
|
||||
$container->register(new ConfigServiceProvider);
|
||||
|
||||
return $container;
|
||||
}
|
||||
@@ -120,8 +146,8 @@ class Grav extends Container
|
||||
// Use output buffering to prevent headers from being sent too early.
|
||||
ob_start();
|
||||
|
||||
// Initialize stream wrappers.
|
||||
$this['locator'];
|
||||
// Initialize configuration.
|
||||
$this['config']->init();
|
||||
|
||||
$this['plugins']->init();
|
||||
|
||||
@@ -172,6 +198,11 @@ class Grav extends Container
|
||||
{
|
||||
/** @var Uri $uri */
|
||||
$uri = $this['uri'];
|
||||
|
||||
if (isset($this['session'])) {
|
||||
$this['session']->close();
|
||||
}
|
||||
|
||||
header("Location: " . rtrim($uri->rootUrl(), '/') .'/'. trim($route, '/'), true, $code);
|
||||
exit();
|
||||
}
|
||||
@@ -232,7 +263,10 @@ class Grav extends Container
|
||||
if($this['config']->get('system.debugger.shutdown.close_connection')) {
|
||||
set_time_limit(0);
|
||||
ignore_user_abort(true);
|
||||
session_write_close();
|
||||
|
||||
if (isset($this['session'])) {
|
||||
$this['session']->close();
|
||||
}
|
||||
|
||||
header('Content-length: ' . ob_get_length());
|
||||
header("Connection: close\r\n");
|
||||
|
||||
@@ -90,9 +90,8 @@ class Inflector
|
||||
/**
|
||||
* Singularizes English nouns.
|
||||
*
|
||||
* @access static public
|
||||
* @static
|
||||
* @param string $word English noun to singularize
|
||||
* @param int $count
|
||||
* @return string Singular noun.
|
||||
*/
|
||||
public static function singularize($word, $count = 1)
|
||||
@@ -353,10 +352,10 @@ class Inflector
|
||||
|
||||
public static function monthize($days)
|
||||
{
|
||||
$now = new JDate();
|
||||
$end = new JDate();
|
||||
$now = new \DateTime();
|
||||
$end = new \DateTime();
|
||||
|
||||
$duration = new DateInterval("P{$days}D");
|
||||
$duration = new \DateInterval("P{$days}D");
|
||||
|
||||
$diff = $end->add($duration)->diff($now);
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use RocketTheme\Toolbox\ArrayTraits\ArrayAccessWithGetters;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Iterator as ArrayIterator;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Constructor;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Countable;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Serializable;
|
||||
|
||||
/**
|
||||
* Class Iterator
|
||||
@@ -9,25 +14,12 @@ use Symfony\Component\Yaml\Yaml;
|
||||
*/
|
||||
class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
|
||||
{
|
||||
use Constructor, ArrayAccessWithGetters, ArrayIterator, Countable, Serializable, Export;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $items = array();
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $unset = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $items Initial items inside the iterator.
|
||||
*/
|
||||
public function __construct(array $items = array())
|
||||
{
|
||||
$this->items = $items;
|
||||
}
|
||||
protected $items = [];
|
||||
|
||||
/**
|
||||
* Convert function calls for the existing keys into their values.
|
||||
@@ -41,49 +33,6 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
|
||||
return (isset($this->items[$key])) ? $this->items[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Array getter shorthand to get items.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
return (isset($this->items[$key])) ? $this->items[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Array setter shorthand to set the value.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __set($key, $value)
|
||||
{
|
||||
$this->items[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Array isset shorthand to set the value.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($key)
|
||||
{
|
||||
return isset($this->items[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Array unset shorthand to remove the key.
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
public function __unset($key)
|
||||
{
|
||||
$this->offsetUnset($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone the iterator.
|
||||
*/
|
||||
@@ -164,7 +113,7 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
|
||||
shuffle($keys);
|
||||
|
||||
$new = array();
|
||||
foreach($keys as $key) {
|
||||
foreach ($keys as $key) {
|
||||
$new[$key] = $this->items[$key];
|
||||
}
|
||||
|
||||
@@ -215,180 +164,4 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Implements export functions to array, YAML and JSON.
|
||||
|
||||
/**
|
||||
* Return items as an array.
|
||||
*
|
||||
* @return array Array presentation of the iterator.
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return YAML encoded string of items.
|
||||
*
|
||||
* @return string YAML presentation of the iterator.
|
||||
*/
|
||||
public function toYaml()
|
||||
{
|
||||
return Yaml::dump($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return JSON encoded string of items.
|
||||
*
|
||||
* @return string JSON presentation of the iterator.
|
||||
*/
|
||||
public function toJson()
|
||||
{
|
||||
return json_encode($this->items);
|
||||
}
|
||||
|
||||
// Implements Iterator.
|
||||
|
||||
/**
|
||||
* Returns the current element.
|
||||
*
|
||||
* @return mixed Can return any type.
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return current($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key of the current element.
|
||||
*
|
||||
* @return mixed Returns scalar on success, or NULL on failure.
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return key($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the current position to the next element.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
if ($this->unset) {
|
||||
// If current item was unset, position is already in the next element (do nothing).
|
||||
$this->unset = false;
|
||||
} else {
|
||||
next($this->items);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewinds back to the first element of the Iterator.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->unset = false;
|
||||
reset($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called after Iterator::rewind() and Iterator::next() to check if the current position is valid.
|
||||
*
|
||||
* @return bool Returns TRUE on success or FALSE on failure.
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return key($this->items) !== null;
|
||||
}
|
||||
|
||||
// Implements ArrayAccess
|
||||
|
||||
/**
|
||||
* Whether or not an offset exists.
|
||||
*
|
||||
* @param mixed $offset An offset to check for.
|
||||
* @return bool Returns TRUE on success or FALSE on failure.
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->items[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at specified offset.
|
||||
*
|
||||
* @param mixed $offset The offset to retrieve.
|
||||
* @return mixed Can return all value types.
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return isset($this->items[$offset]) ? $this->items[$offset] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a value to the specified offset.
|
||||
*
|
||||
* @param mixed $offset The offset to assign the value to.
|
||||
* @param mixed $value The value to set.
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if (is_null($offset)) {
|
||||
$this->items[] = $value;
|
||||
} else {
|
||||
$this->items[$offset] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets an offset.
|
||||
*
|
||||
* @param mixed $offset The offset to unset.
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
if ($offset == key($this->items)) {
|
||||
$this->unset = true;
|
||||
}
|
||||
unset($this->items[$offset]);
|
||||
}
|
||||
|
||||
// Implements Countable
|
||||
|
||||
/**
|
||||
* This method is executed when using the count() function.
|
||||
*
|
||||
* @return int The count of items.
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->items);
|
||||
}
|
||||
|
||||
// Implements Serializable
|
||||
|
||||
/**
|
||||
* Returns string representation of the object.
|
||||
*
|
||||
* @return string Returns the string representation of the object.
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during unserialization of the object.
|
||||
*
|
||||
* @param string $serialized The string representation of the object.
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
$this->items = unserialize($serialized);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<?php
|
||||
namespace Grav\Common\Markdown;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Debugger;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Page\Medium;
|
||||
use Grav\Common\Uri;
|
||||
|
||||
/**
|
||||
* A trait to add some custom processing to the identifyLink() method in Parsedown and ParsedownExtra
|
||||
@@ -13,10 +16,13 @@ trait MarkdownGravLinkTrait
|
||||
|
||||
protected function identifyLink($Excerpt)
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
|
||||
// Run the parent method to get the actual results
|
||||
$Excerpt = parent::identifyLink($Excerpt);
|
||||
$actions = array();
|
||||
$this->base_url = trim(self::$grav['config']->get('system.base_url_relative'));
|
||||
$this->base_url = trim($config->get('system.base_url_relative'));
|
||||
|
||||
// if this is a link
|
||||
if (isset($Excerpt['element']['attributes']['href'])) {
|
||||
@@ -57,8 +63,8 @@ trait MarkdownGravLinkTrait
|
||||
|
||||
// loop through actions for the image and call them
|
||||
foreach ($actions as $action => $params) {
|
||||
// as long as it's not an html, url or ligtbox action
|
||||
if (!in_array($action, ['html','url','lightbox'])) {
|
||||
// as long as it's a valid action
|
||||
if (in_array($action, Medium::$valid_actions)) {
|
||||
call_user_func_array(array(&$medium, $action), explode(',', $params));
|
||||
}
|
||||
}
|
||||
@@ -92,7 +98,7 @@ trait MarkdownGravLinkTrait
|
||||
}
|
||||
} else {
|
||||
// not a current page media file, see if it needs converting to relative
|
||||
$Excerpt['element']['attributes']['src'] = $this->convertUrl($url['path']);
|
||||
$Excerpt['element']['attributes']['src'] = $this->convertUrl(Uri::build_url($url));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,4 +192,40 @@ class Collection extends Iterator
|
||||
public function currentPosition($path) {
|
||||
return array_search($path, array_keys($this->items));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only visible pages
|
||||
*
|
||||
* @return Collection The collection with only visible pages
|
||||
*/
|
||||
public function visible()
|
||||
{
|
||||
$visible = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page->visible()) {
|
||||
$visible[$path] = $slug;
|
||||
}
|
||||
}
|
||||
return new static($visible, $this->params, $this->pages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only routable pages
|
||||
*
|
||||
* @return Collection The collection with only routable pages
|
||||
*/
|
||||
public function routable()
|
||||
{
|
||||
$routable = [];
|
||||
|
||||
foreach (array_keys($this->items) as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page->routable()) {
|
||||
$routable[$path] = $slug;
|
||||
}
|
||||
}
|
||||
return new static($routable, $this->params, $this->pages);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace Grav\Common\Page;
|
||||
|
||||
use Grav\Common\Getters;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Config;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\GravTrait;
|
||||
|
||||
/**
|
||||
@@ -56,6 +56,9 @@ class Media extends Getters
|
||||
continue;
|
||||
}
|
||||
|
||||
//set file size
|
||||
$medium->set('size',$info->getSize());
|
||||
|
||||
// Assign meta files to the medium.
|
||||
if ($meta) {
|
||||
$medium->addMetaFile($meta);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
use Grav\Common\Config;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Filesystem\File\Yaml;
|
||||
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;
|
||||
use Gregwar\Image\Image as ImageFile;
|
||||
|
||||
/**
|
||||
@@ -50,6 +50,8 @@ class Medium extends Data
|
||||
protected $type = 'guess';
|
||||
protected $quality = 80;
|
||||
|
||||
public static $valid_actions = ['resize', 'forceResize', 'cropResize', 'crop', 'cropZoom', 'negate', 'brightness', 'contrast', 'grayscale', 'emboss', 'smooth', 'sharp', 'edge', 'colorize', 'sepia' ];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@@ -92,6 +94,26 @@ class Medium extends Data
|
||||
return $this->linkImage ? $this->html() : $this->url();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return PATH to file.
|
||||
*
|
||||
* @return string path to file
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
|
||||
if ($this->image) {
|
||||
$output = $this->image->cacheFile($this->type, $this->quality);
|
||||
$this->reset();
|
||||
$output = ROOT_DIR . $output;
|
||||
} else {
|
||||
$output = $this->get('path') . '/' . $this->get('filename');
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return URL to file.
|
||||
*
|
||||
@@ -204,9 +226,11 @@ class Medium extends Data
|
||||
|
||||
public function lightboxRaw($width = null, $height = null)
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
$url = $this->url();
|
||||
$this->link($width, $height);
|
||||
$lightbox_url = self::$grav['config']->get('system.base_url_relative') . '/'. $this->linkTarget;
|
||||
$lightbox_url = $config->get('system.base_url_relative') . '/'. $this->linkTarget;
|
||||
|
||||
return array('a_url' => $lightbox_url, 'a_rel' => 'lightbox', 'img_url' => $url);
|
||||
}
|
||||
@@ -309,7 +333,7 @@ class Medium extends Data
|
||||
|
||||
$path = $this->get('path') . '/' . $this->get('filename') . '.meta.' . $type;
|
||||
if ($type == 'yaml') {
|
||||
$this->merge(Yaml::instance($path)->content());
|
||||
$this->merge(CompiledYamlFile::instance($path)->content());
|
||||
} elseif (in_array($type, array('jpg', 'jpeg', 'png', 'gif'))) {
|
||||
$this->set('thumb', $path);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
use Grav\Common\Config;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Common\Cache;
|
||||
use Grav\Common\Twig;
|
||||
use Grav\Common\Filesystem\File;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Data;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Taxonomy;
|
||||
use Grav\Common\Markdown\Markdown;
|
||||
use Grav\Common\Markdown\MarkdownExtra;
|
||||
use Grav\Component\EventDispatcher\Event;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\File\MarkdownFile;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
@@ -30,6 +30,11 @@ class Page
|
||||
{
|
||||
use GravTrait;
|
||||
|
||||
const ALL_PAGES = 0; // both standard and modular pages
|
||||
const STANDARD_PAGES = 1; // visible and invisible pages (e.g. 01.regular/, invisible/)
|
||||
const MODULAR_PAGES = 2; // modular pages (e.g. _modular/)
|
||||
|
||||
|
||||
/**
|
||||
* @var string Filename. Leave as null if page is folder.
|
||||
*/
|
||||
@@ -54,6 +59,7 @@ class Page
|
||||
protected $modified;
|
||||
protected $id;
|
||||
protected $header;
|
||||
protected $frontmatter;
|
||||
protected $content;
|
||||
protected $raw_content;
|
||||
protected $pagination;
|
||||
@@ -137,7 +143,27 @@ class Page
|
||||
$this->header = null;
|
||||
$this->content = null;
|
||||
}
|
||||
return $file->raw();
|
||||
return $file ? $file->raw() : '';
|
||||
}
|
||||
|
||||
public function frontmatter($var = null) {
|
||||
|
||||
if ($var) {
|
||||
$this->frontmatter = (string) $var;
|
||||
|
||||
// Update also file object.
|
||||
$file = $this->file();
|
||||
if ($file) {
|
||||
$file->frontmatter((string) $var);
|
||||
}
|
||||
|
||||
// Force content re-processing.
|
||||
$this->id(time().md5($this->filePath()));
|
||||
}
|
||||
if (!$this->frontmatter) {
|
||||
$this->header();
|
||||
}
|
||||
return $this->frontmatter;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,6 +190,7 @@ class Page
|
||||
$file = $this->file();
|
||||
if ($file) {
|
||||
$this->raw_content = $file->markdown();
|
||||
$this->frontmatter = $file->frontmatter();
|
||||
$this->header = (object) $file->header();
|
||||
|
||||
$var = true;
|
||||
@@ -254,6 +281,8 @@ class Page
|
||||
return Utils::truncateHTML($content, $size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets and Sets the content based on content portion of the .md file
|
||||
*
|
||||
@@ -376,6 +405,10 @@ class Page
|
||||
$path = explode('.', $name);
|
||||
$scope = array_shift($path);
|
||||
|
||||
if ($name == 'frontmatter') {
|
||||
return $this->frontmatter;
|
||||
}
|
||||
|
||||
if ($scope == 'header') {
|
||||
$current = $this->header();
|
||||
foreach ($path as $field) {
|
||||
@@ -397,12 +430,13 @@ class Page
|
||||
/**
|
||||
* Get file object to the page.
|
||||
*
|
||||
* @return File\Markdown|null
|
||||
* @return MarkdownFile|null
|
||||
*/
|
||||
public function file()
|
||||
{
|
||||
if ($this->name) {
|
||||
return File\Markdown::instance($this->filePath());
|
||||
// TODO: use CompiledMarkdownFile after fixing issues in it.
|
||||
return MarkdownFile::instance($this->filePath());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -420,7 +454,7 @@ class Page
|
||||
if ($file) {
|
||||
$file->filename($this->filePath());
|
||||
$file->header((array) $this->header());
|
||||
$file->markdown($this->content());
|
||||
$file->markdown($this->raw_content);
|
||||
$file->save();
|
||||
}
|
||||
}
|
||||
@@ -472,7 +506,7 @@ class Page
|
||||
/**
|
||||
* Get blueprints for the page.
|
||||
*
|
||||
* @return Data\Blueprint
|
||||
* @return Blueprint
|
||||
*/
|
||||
public function blueprints()
|
||||
{
|
||||
@@ -610,7 +644,7 @@ class Page
|
||||
$this->template = $var;
|
||||
}
|
||||
if (empty($this->template)) {
|
||||
$this->template = str_replace(CONTENT_EXT, '', $this->name());
|
||||
$this->template = ($this->modular() ? 'modular/' : '') . str_replace(CONTENT_EXT, '', $this->name());
|
||||
}
|
||||
return $this->template;
|
||||
}
|
||||
@@ -716,33 +750,36 @@ class Page
|
||||
$this->metadata = array();
|
||||
$page_header = $this->header;
|
||||
|
||||
|
||||
// Set the Generator tag
|
||||
$this->metadata['generator'] = array('name'=>'generator', 'content'=>'Grav ' . GRAV_VERSION);
|
||||
|
||||
// Merge any site.metadata settings in with page metadata
|
||||
$defaults = (array) self::$grav['config']->get('site.metadata');
|
||||
if (isset($page_header->metadata)) {
|
||||
$page_header->metadata = array_merge($defaults, $page_header->metadata);
|
||||
} else {
|
||||
$page_header->metadata = $defaults;
|
||||
}
|
||||
// Safety check to ensure we have a header
|
||||
if ($page_header) {
|
||||
// Merge any site.metadata settings in with page metadata
|
||||
$defaults = (array) self::$grav['config']->get('site.metadata');
|
||||
|
||||
// Build an array of meta objects..
|
||||
foreach((array)$page_header->metadata as $key => $value) {
|
||||
|
||||
// If this is a property type metadata: "og", "twitter", "facebook" etc
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $property => $prop_value) {
|
||||
$prop_key = $key.":".$property;
|
||||
$this->metadata[$prop_key] = array('property'=>$prop_key, 'content'=>$prop_value);
|
||||
}
|
||||
// If it this is a standard meta data type
|
||||
if (isset($page_header->metadata)) {
|
||||
$page_header->metadata = array_merge($defaults, $page_header->metadata);
|
||||
} else {
|
||||
if (in_array($key, $header_tag_http_equivs)) {
|
||||
$this->metadata[$key] = array('http_equiv'=>$key, 'content'=>$value);
|
||||
$page_header->metadata = $defaults;
|
||||
}
|
||||
|
||||
// Build an array of meta objects..
|
||||
foreach((array)$page_header->metadata as $key => $value) {
|
||||
|
||||
// If this is a property type metadata: "og", "twitter", "facebook" etc
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $property => $prop_value) {
|
||||
$prop_key = $key.":".$property;
|
||||
$this->metadata[$prop_key] = array('property'=>$prop_key, 'content'=>$prop_value);
|
||||
}
|
||||
// If it this is a standard meta data type
|
||||
} else {
|
||||
$this->metadata[$key] = array('name'=>$key, 'content'=>$value);
|
||||
if (in_array($key, $header_tag_http_equivs)) {
|
||||
$this->metadata[$key] = array('http_equiv'=>$key, 'content'=>$value);
|
||||
} else {
|
||||
$this->metadata[$key] = array('name'=>$key, 'content'=>$value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1100,14 +1137,25 @@ class Page
|
||||
/**
|
||||
* Returns children of this page.
|
||||
*
|
||||
* @param bool $modular|null whether or not to return modular children
|
||||
* @return Collection
|
||||
*/
|
||||
public function children()
|
||||
public function children($type = Page::STANDARD_PAGES)
|
||||
{
|
||||
/** @var Pages $pages */
|
||||
$pages = self::$grav['pages'];
|
||||
$children = $pages->children($this->path());
|
||||
|
||||
return $pages->children($this->path());
|
||||
// Filter out modular pages on regular call
|
||||
// Filter out non-modular pages when all you want is modular
|
||||
foreach ($children as $child) {
|
||||
$is_modular_page = $child->modular();
|
||||
if (($is_modular_page && $type == Page::STANDARD_PAGES) || (!$is_modular_page && $type == Page::MODULAR_PAGES)) {
|
||||
$children->remove($child->path());
|
||||
}
|
||||
}
|
||||
|
||||
return $children;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1276,6 +1324,7 @@ class Page
|
||||
*/
|
||||
public function activeChild()
|
||||
{
|
||||
/** @var Uri $uri */
|
||||
$uri = self::$grav['uri'];
|
||||
|
||||
if (!$this->home() && (strpos($uri->url(), $this->url()) === 0)) {
|
||||
@@ -1434,12 +1483,10 @@ class Page
|
||||
if (!empty($parts)) {
|
||||
switch ($parts[0]) {
|
||||
case 'modular':
|
||||
// FIXME: filter by modular
|
||||
$results = $this->children();
|
||||
$results = $this->children(Page::MODULAR_PAGES);
|
||||
break;
|
||||
case 'children':
|
||||
// FIXME: filter by non-modular
|
||||
$results = $this->children();
|
||||
$results = $this->children(Page::STANDARD_PAGES);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1575,7 +1622,7 @@ class Page
|
||||
} else {
|
||||
$parsedown = new Markdown($this);
|
||||
}
|
||||
$content = $parsedown->parse($content);
|
||||
$content = $parsedown->text($content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
@@ -1613,17 +1660,14 @@ class Page
|
||||
$parent = $this->parent();
|
||||
|
||||
// Extract visible children from the parent page.
|
||||
$visible = array();
|
||||
$list = array();
|
||||
/** @var Page $page */
|
||||
foreach ($parent as $page) {
|
||||
foreach ($parent->children()->visible() as $page) {
|
||||
if ($page->order()) {
|
||||
$visible[$page->slug] = $page->path();
|
||||
$list[$page->slug] = $page->path();
|
||||
}
|
||||
}
|
||||
|
||||
// List only visible pages.
|
||||
$list = array_intersect($visible, $pages->sort($parent));
|
||||
|
||||
// If page was moved, take it out of the list.
|
||||
if ($this->_action == 'move') {
|
||||
unset($list[$this->slug()]);
|
||||
@@ -1660,6 +1704,13 @@ class Page
|
||||
Folder::copy($this->_original->path(), $this->path());
|
||||
}
|
||||
|
||||
if ($this->name() != $this->_original->name()) {
|
||||
$path = $this->path();
|
||||
if (is_file($path . '/' . $this->_original->name())) {
|
||||
rename($path . '/' . $this->_original->name(), $path . '/' . $this->name());
|
||||
}
|
||||
}
|
||||
|
||||
$this->_action = null;
|
||||
$this->_original = null;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Config;
|
||||
use Grav\Common\Data;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Common\Cache;
|
||||
use Grav\Common\Taxonomy;
|
||||
use Grav\Component\EventDispatcher\Event;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Data\Blueprints;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
|
||||
/**
|
||||
* GravPages is the class that is the entry point into the hierarchy of pages
|
||||
@@ -36,7 +37,7 @@ class Pages
|
||||
/**
|
||||
* @var array|string[]
|
||||
*/
|
||||
protected $routes;
|
||||
protected $routes = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
@@ -44,7 +45,7 @@ class Pages
|
||||
protected $sort;
|
||||
|
||||
/**
|
||||
* @var Data\Blueprints
|
||||
* @var Blueprints
|
||||
*/
|
||||
protected $blueprints;
|
||||
|
||||
@@ -53,6 +54,11 @@ class Pages
|
||||
*/
|
||||
protected $last_modified;
|
||||
|
||||
/**
|
||||
* @var Types
|
||||
*/
|
||||
static protected $types;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@@ -172,6 +178,9 @@ class Pages
|
||||
public function sortCollection(Collection $collection, $orderBy, $orderDir = 'asc', $orderManual = null)
|
||||
{
|
||||
$items = $collection->toArray();
|
||||
if (!$items) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$lookup = md5(json_encode($items));
|
||||
if (!isset($this->sort[$lookup][$orderBy])) {
|
||||
@@ -193,6 +202,7 @@ class Pages
|
||||
*
|
||||
* @param string $path
|
||||
* @return Page
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function get($path)
|
||||
{
|
||||
@@ -252,15 +262,12 @@ class Pages
|
||||
* Get a blueprint for a page type.
|
||||
*
|
||||
* @param string $type
|
||||
* @return Data\Blueprint
|
||||
* @return Blueprint
|
||||
*/
|
||||
public function blueprints($type)
|
||||
{
|
||||
if (!isset($this->blueprints)) {
|
||||
/** @var Config $config */
|
||||
$config = $this->grav['config'];
|
||||
|
||||
$this->blueprints = new Data\Blueprints(THEMES_DIR . $config->get('system.pages.theme') . '/blueprints/');
|
||||
$this->blueprints = new Blueprints(self::getTypes());
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -307,6 +314,26 @@ class Pages
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available page types.
|
||||
*
|
||||
* @return Types
|
||||
*/
|
||||
static public function getTypes()
|
||||
{
|
||||
if (!self::$types) {
|
||||
self::$types = new Types();
|
||||
self::$types->scanBlueprints('theme://blueprints/');
|
||||
self::$types->scanTemplates('theme://templates/');
|
||||
|
||||
$event = new Event();
|
||||
$event->types = self::$types;
|
||||
Grav::instance()->fireEvent('onGetPageTemplates', $event);
|
||||
}
|
||||
|
||||
return self::$types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available page types.
|
||||
*
|
||||
@@ -314,14 +341,21 @@ class Pages
|
||||
*/
|
||||
static public function types()
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
$types = self::getTypes();
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $grav['config'];
|
||||
return $types->pageSelect();
|
||||
}
|
||||
|
||||
$blueprints = new Data\Blueprints(THEMES_DIR . $config->get('system.pages.theme') . '/blueprints/');
|
||||
/**
|
||||
* Get available page types.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
static public function modularTypes()
|
||||
{
|
||||
$types = self::getTypes();
|
||||
|
||||
return $blueprints->types();
|
||||
return $types->modularSelect();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -408,6 +442,8 @@ class Pages
|
||||
$directory = rtrim($directory, DS);
|
||||
$iterator = new \DirectoryIterator($directory);
|
||||
$page = new Page;
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $this->grav['config'];
|
||||
|
||||
$page->path($directory);
|
||||
@@ -429,13 +465,18 @@ class Pages
|
||||
// set current modified of page
|
||||
$last_modified = $page->modified();
|
||||
|
||||
// flat for content availability
|
||||
$content_exists = false;
|
||||
|
||||
/** @var \DirectoryIterator $file */
|
||||
foreach ($iterator as $file) {
|
||||
$name = $file->getFilename();
|
||||
$modified = $file->getMTime();
|
||||
|
||||
if ($file->isFile() && Utils::endsWith($name, CONTENT_EXT)) {
|
||||
|
||||
$page->init($file);
|
||||
$content_exists = true;
|
||||
|
||||
if ($config->get('system.pages.events.page')) {
|
||||
$this->grav->fireEvent('onPageProcessed', new Event(['page' => $page]));
|
||||
@@ -458,22 +499,23 @@ class Pages
|
||||
|
||||
// set the modified time if not already set
|
||||
if (!$page->date()) {
|
||||
$page->date($file->getMTime());
|
||||
$page->date($modified);
|
||||
}
|
||||
|
||||
// set the last modified time on pages
|
||||
$this->lastModified($file->getMTime());
|
||||
|
||||
if ($config->get('system.pages.events.page')) {
|
||||
$this->grav->fireEvent('onFolderProcessed', new Event(['page' => $page]));
|
||||
}
|
||||
} else {
|
||||
$date = $file->getMTime();
|
||||
if ($date > $last_modified) {
|
||||
$last_modified = $date;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the last modified if it's newer than already found
|
||||
if ($modified > $last_modified) {
|
||||
$last_modified = $modified;
|
||||
}
|
||||
}
|
||||
|
||||
// Set routability to false if no page found
|
||||
if (!$content_exists) {
|
||||
$page->routable(false);
|
||||
}
|
||||
|
||||
// Override the modified and ID so that it takes the latest change into account
|
||||
|
||||
86
system/src/Grav/Common/Page/Types.php
Normal file
86
system/src/Grav/Common/Page/Types.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Constructor;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Countable;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Iterator;
|
||||
|
||||
class Types implements \ArrayAccess, \Iterator, \Countable
|
||||
{
|
||||
use ArrayAccess, Constructor, Iterator, Countable, Export;
|
||||
|
||||
protected $items;
|
||||
|
||||
public function register($type, $blueprint = null)
|
||||
{
|
||||
if ($blueprint || empty($this->items[$type])) {
|
||||
$this->items[$type] = $blueprint;
|
||||
}
|
||||
}
|
||||
|
||||
public function scanBlueprints($path)
|
||||
{
|
||||
$options = [
|
||||
'compare' => 'Filename',
|
||||
'pattern' => '|\.yaml$|',
|
||||
'filters' => [
|
||||
'key' => '|\.yaml$|'
|
||||
],
|
||||
'key' => 'SubPathName',
|
||||
'value' => 'PathName',
|
||||
];
|
||||
|
||||
$this->items = Folder::all($path, $options) + $this->items;
|
||||
}
|
||||
|
||||
public function scanTemplates($path)
|
||||
{
|
||||
$options = [
|
||||
'compare' => 'Filename',
|
||||
'pattern' => '|\.html\.twig$|',
|
||||
'filters' => [
|
||||
'value' => '|\.html\.twig$|'
|
||||
],
|
||||
'value' => 'Filename',
|
||||
'recursive' => false
|
||||
];
|
||||
|
||||
foreach (Folder::all($path, $options) as $type) {
|
||||
$this->register($type);
|
||||
}
|
||||
if (file_exists($path . 'modular/')) {
|
||||
foreach (Folder::all($path . 'modular/', $options) as $type) {
|
||||
$this->register('modular/' . $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function pageSelect()
|
||||
{
|
||||
$list = [];
|
||||
foreach ($this->items as $name => $file) {
|
||||
if (strpos($name, '/')) {
|
||||
continue;
|
||||
}
|
||||
$list[$name] = ucfirst(strtr($name, '_', ' '));
|
||||
}
|
||||
ksort($list);
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function modularSelect()
|
||||
{
|
||||
$list = [];
|
||||
foreach ($this->items as $name => $file) {
|
||||
if (strpos($name, 'modular/') !== 0) {
|
||||
continue;
|
||||
}
|
||||
$list[$name] = trim(ucfirst(strtr(basename($name), '_', ' ')));
|
||||
}
|
||||
ksort($list);
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Component\EventDispatcher\EventDispatcher;
|
||||
use Grav\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Grav\Common\Config\Config;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* The Plugin object just holds the id and path to a plugin.
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Filesystem\File;
|
||||
use Grav\Component\EventDispatcher\EventDispatcher;
|
||||
use Grav\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Data\Blueprints;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* The Plugins object holds an array of all the plugin objects that
|
||||
@@ -41,7 +44,7 @@ class Plugins extends Iterator
|
||||
continue;
|
||||
}
|
||||
|
||||
$filePath = $this->grav['locator']('plugin://' . $plugin . DS . $plugin . PLUGIN_EXT);
|
||||
$filePath = $this->grav['locator']('plugins://' . $plugin . DS . $plugin . PLUGIN_EXT);
|
||||
if (!is_file($filePath)) {
|
||||
throw new \RuntimeException(sprintf("Plugin '%s' enabled but not found!", $filePath, $plugin));
|
||||
}
|
||||
@@ -73,12 +76,12 @@ class Plugins extends Iterator
|
||||
/**
|
||||
* Return list of all plugin data with their blueprints.
|
||||
*
|
||||
* @return array|Data\Data[]
|
||||
* @return array
|
||||
*/
|
||||
static public function all()
|
||||
{
|
||||
$list = array();
|
||||
$iterator = new \DirectoryIterator('plugin:///');
|
||||
$iterator = new \DirectoryIterator('plugins://');
|
||||
|
||||
/** @var \DirectoryIterator $directory */
|
||||
foreach ($iterator as $directory) {
|
||||
@@ -95,18 +98,18 @@ class Plugins extends Iterator
|
||||
return $list;
|
||||
}
|
||||
|
||||
static public function get($type)
|
||||
static public function get($name)
|
||||
{
|
||||
$blueprints = new Data\Blueprints('plugin://' . $type);
|
||||
$blueprints = new Blueprints("plugins://{$name}");
|
||||
$blueprint = $blueprints->get('blueprints');
|
||||
$blueprint->name = $type;
|
||||
$blueprint->name = $name;
|
||||
|
||||
// Load default configuration.
|
||||
$file = File\Yaml::instance('plugin://' . "{$type}/{$type}" . YAML_EXT);
|
||||
$obj = new Data\Data($file->content(), $blueprint);
|
||||
$file = CompiledYamlFile::instance("plugins://{$name}/{$name}.yaml");
|
||||
$obj = new Data($file->content(), $blueprint);
|
||||
|
||||
// Override with user configuration.
|
||||
$file = File\Yaml::instance('plugin://' . "config/plugins/{$type}" . YAML_EXT);
|
||||
$file = CompiledYamlFile::instance("user://config/plugins/{$name}.yaml");
|
||||
$obj->merge($file->content());
|
||||
|
||||
// Save configuration always to user/config.
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
/**
|
||||
* The Registry class is an implementation of the Registry Pattern to store and retrieve
|
||||
* instances of objects used by Grav
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
* @deprecated
|
||||
*/
|
||||
class Registry
|
||||
{
|
||||
|
||||
/**
|
||||
* Return global instance.
|
||||
*
|
||||
* @return Registry
|
||||
*/
|
||||
public static function instance()
|
||||
{
|
||||
user_error(__METHOD__ . '()', E_USER_DEPRECATED);
|
||||
return new Registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entry from the registry.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function get($key)
|
||||
{
|
||||
user_error(__METHOD__ . '()', E_USER_DEPRECATED);
|
||||
$instance = Grav::instance();
|
||||
return $instance[strtolower($key)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
private function __clone()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Store entry to the registry.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function store($key, $value)
|
||||
{
|
||||
user_error(__CLASS__ . '::' . __METHOD__ . '()', E_USER_DEPRECATED);
|
||||
$instance = Grav::instance();
|
||||
$instance[strtolower($key)] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entry from the registry.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function retrieve($key)
|
||||
{
|
||||
user_error(__CLASS__ . '::' . __METHOD__ . '()', E_USER_DEPRECATED);
|
||||
$instance = Grav::instance();
|
||||
return $instance[strtolower($key)];
|
||||
}
|
||||
}
|
||||
59
system/src/Grav/Common/Service/ConfigServiceProvider.php
Normal file
59
system/src/Grav/Common/Service/ConfigServiceProvider.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
namespace Grav\Common\Service;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use RocketTheme\Toolbox\Blueprints\Blueprints;
|
||||
|
||||
/**
|
||||
* The Config class contains configuration information.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class ConfigServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
public function register(Container $container)
|
||||
{
|
||||
$self = $this;
|
||||
|
||||
$container['blueprints'] = function ($c) use ($self) {
|
||||
return $self->loadMasterBlueprints($c);
|
||||
};
|
||||
|
||||
$container['config'] = function ($c) use ($self) {
|
||||
return $self->loadMasterConfig($c);
|
||||
};
|
||||
}
|
||||
|
||||
public function loadMasterConfig(Container $container)
|
||||
{
|
||||
$file = CACHE_DIR . 'compiled/config/master.php';
|
||||
$data = is_file($file) ? (array) include $file : [];
|
||||
if ($data) {
|
||||
try {
|
||||
$config = new Config($data, $container);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($config)) {
|
||||
$file = GRAV_ROOT . '/setup.php';
|
||||
$data = is_file($file) ? (array) include $file : [];
|
||||
$config = new Config($data, $container);
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
public function loadMasterBlueprints(Container $container)
|
||||
{
|
||||
$file = CACHE_DIR . 'compiled/blueprints/master.php';
|
||||
$data = is_file($file) ? (array) include $file : [];
|
||||
|
||||
return new Blueprints($data, $container);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
<?php
|
||||
namespace Grav\Common\Service;
|
||||
|
||||
use Grav\Component\DI\ServiceProviderInterface;
|
||||
use Grav\Component\Filesystem\ResourceLocator;
|
||||
use Grav\Component\Filesystem\StreamWrapper\ReadOnlyStream;
|
||||
use Grav\Component\Filesystem\StreamWrapper\Stream;
|
||||
use Grav\Common\Config\Config;
|
||||
use Pimple\Container;
|
||||
use RocketTheme\Toolbox\DI\ServiceProviderInterface;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use RocketTheme\Toolbox\StreamWrapper\ReadOnlyStream;
|
||||
use RocketTheme\Toolbox\StreamWrapper\Stream;
|
||||
|
||||
class StreamsServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
@@ -14,16 +15,18 @@ class StreamsServiceProvider implements ServiceProviderInterface
|
||||
$self = $this;
|
||||
|
||||
$container['locator'] = function($c) use ($self) {
|
||||
$locator = new ResourceLocator;
|
||||
$locator = new UniformResourceLocator(ROOT_DIR);
|
||||
$self->init($c, $locator);
|
||||
|
||||
return $locator;
|
||||
};
|
||||
}
|
||||
|
||||
protected function init(Container $container, ResourceLocator $locator)
|
||||
protected function init(Container $container, UniformResourceLocator $locator)
|
||||
{
|
||||
$schemes = $container['config']->get('streams.schemes');
|
||||
/** @var Config $config */
|
||||
$config = $container['config'];
|
||||
$schemes = $config->get('streams.schemes');
|
||||
|
||||
if (!$schemes) {
|
||||
return;
|
||||
@@ -50,7 +53,7 @@ class StreamsServiceProvider implements ServiceProviderInterface
|
||||
}
|
||||
$type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream';
|
||||
if ($type[0] != '\\') {
|
||||
$type = '\\Grav\\Component\\Filesystem\\StreamWrapper\\' . $type;
|
||||
$type = '\\RocketTheme\\Toolbox\\StreamWrapper\\' . $type;
|
||||
}
|
||||
|
||||
if (!stream_wrapper_register($scheme, $type)) {
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Session;
|
||||
|
||||
use Grav\Common\Getters;
|
||||
|
||||
/**
|
||||
* Session wide messages.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Message
|
||||
{
|
||||
/**
|
||||
* @var array|string[]
|
||||
*/
|
||||
protected $messages = array();
|
||||
|
||||
/**
|
||||
* Add message to the queue.
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $scope
|
||||
* @return $this
|
||||
*/
|
||||
public function add($message, $scope = 'default')
|
||||
{
|
||||
$message = array('message' => $message, 'scope' => $scope);
|
||||
|
||||
$this->messages[] = $message;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear message queue.
|
||||
*
|
||||
* @param string $scope
|
||||
* @return $this
|
||||
*/
|
||||
public function clear($scope = null)
|
||||
{
|
||||
if ($scope === null) {
|
||||
$this->messages = array();
|
||||
} else {
|
||||
foreach ($this->messages as $key => $message) {
|
||||
if ($message['scope'] == $scope) {
|
||||
unset($this->messages[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all messages.
|
||||
*
|
||||
* @param string $scope
|
||||
* @return array
|
||||
*/
|
||||
public function all($scope = null)
|
||||
{
|
||||
if ($scope === null) {
|
||||
return array_values($this->messages);
|
||||
}
|
||||
|
||||
$messages = array();
|
||||
foreach ($this->messages as $message) {
|
||||
if ($message['scope'] == $scope) {
|
||||
$messages[] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and clear message queue.
|
||||
*
|
||||
* @param string $scope
|
||||
* @return array
|
||||
*/
|
||||
public function fetch($scope = null)
|
||||
{
|
||||
$messages = $this->all($scope);
|
||||
$this->clear($scope);
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Session;
|
||||
|
||||
/**
|
||||
* Session handling.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Session implements \IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $started = false;
|
||||
|
||||
/**
|
||||
* @var Session
|
||||
*/
|
||||
static $instance;
|
||||
|
||||
/**
|
||||
* @param int $lifetime Defaults to 1800 seconds.
|
||||
* @param string $path Cookie path.
|
||||
*/
|
||||
public function __construct($lifetime, $path)
|
||||
{
|
||||
if (isset(self::$instance)) {
|
||||
throw new \RuntimeException("Session has already been initialized.", 500);
|
||||
}
|
||||
|
||||
// Destroy any existing sessions started with session.auto_start
|
||||
if (session_id())
|
||||
{
|
||||
session_unset();
|
||||
session_destroy();
|
||||
}
|
||||
|
||||
// Disable transparent sid support
|
||||
ini_set('session.use_trans_sid', '0');
|
||||
|
||||
// Only allow cookies
|
||||
ini_set('session.use_cookies', 1);
|
||||
|
||||
session_set_cookie_params($lifetime, $path);
|
||||
register_shutdown_function('session_write_close');
|
||||
session_cache_limiter('none');
|
||||
|
||||
self::$instance = $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current session instance.
|
||||
*
|
||||
* @return Session
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function instance()
|
||||
{
|
||||
if (!isset(self::$instance)) {
|
||||
throw new \RuntimeException("Session hasn't been initialized.", 500);
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the session storage
|
||||
*
|
||||
* @return $this
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function start()
|
||||
{
|
||||
if (!session_start()) {
|
||||
throw new \RuntimeException('Failed to start session');
|
||||
}
|
||||
|
||||
$this->started = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get session ID
|
||||
*
|
||||
* @return string Session ID
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return session_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set session Id
|
||||
*
|
||||
* @param string $id Session ID
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
session_id($id);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get session name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return session_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set session name
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
session_name($name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates the current session.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function invalidate()
|
||||
{
|
||||
$params = session_get_cookie_params();
|
||||
setcookie(session_name(), '', time() - 42000,
|
||||
$params['path'], $params['domain'],
|
||||
$params['secure'], $params['httponly']
|
||||
);
|
||||
|
||||
session_unset();
|
||||
session_destroy();
|
||||
|
||||
$this->started = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the session to be saved and closed
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
session_write_close();
|
||||
|
||||
$this->started = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an attribute is defined.
|
||||
*
|
||||
* @param string $name The attribute name
|
||||
*
|
||||
* @return bool True if the attribute is defined, false otherwise
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
return isset($_SESSION[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an attribute.
|
||||
*
|
||||
* @param string $name The attribute name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return isset($_SESSION[$name]) ? $_SESSION[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an attribute.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$_SESSION[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an attribute.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return mixed The removed value or null when it does not exist
|
||||
*/
|
||||
public function __unset($name)
|
||||
{
|
||||
unset($_SESSION[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns attributes.
|
||||
*
|
||||
* @return array Attributes
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
return $_SESSION;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve an external iterator
|
||||
*
|
||||
* @return \ArrayIterator Return an ArrayIterator of $_SESSION
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($_SESSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the session was started.
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function started()
|
||||
{
|
||||
return $this->started;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use \Grav\Common\Page;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Page\Collection;
|
||||
use Grav\Common\Page\Page;
|
||||
|
||||
/**
|
||||
* The Taxonomy object is a singleton that holds a reference to a 'taxonomy map'. This map is
|
||||
@@ -41,10 +43,10 @@ class Taxonomy
|
||||
* Takes an individual page and processes the taxonomies configured in its header. It
|
||||
* then adds those taxonomies to the map
|
||||
*
|
||||
* @param Page\Page $page the page to process
|
||||
* @param Page $page the page to process
|
||||
* @param array $page_taxonomy
|
||||
*/
|
||||
public function addTaxonomy(Page\Page $page, $page_taxonomy = null)
|
||||
public function addTaxonomy(Page $page, $page_taxonomy = null)
|
||||
{
|
||||
if (!$page_taxonomy) {
|
||||
$page_taxonomy = $page->taxonomy();
|
||||
@@ -69,7 +71,7 @@ class Taxonomy
|
||||
* particular taxonomy.
|
||||
*
|
||||
* @param array $taxonomies taxonomies to search, eg ['tag'=>['animal','cat']]
|
||||
* @return Page\Page page object with sub-pages set to contain matches found in the taxonomy map
|
||||
* @return Page page object with sub-pages set to contain matches found in the taxonomy map
|
||||
*/
|
||||
public function findTaxonomy($taxonomies)
|
||||
{
|
||||
@@ -83,7 +85,7 @@ class Taxonomy
|
||||
}
|
||||
}
|
||||
|
||||
return new Page\Collection($results, ['taxonomies' => $taxonomies]);
|
||||
return new Collection($results, ['taxonomies' => $taxonomies]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Filesystem\File\Yaml;
|
||||
use Grav\Component\Filesystem\ResourceLocator;
|
||||
use Grav\Common\Config\Config;
|
||||
|
||||
class Theme extends Plugin
|
||||
{
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Data\Blueprints;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Filesystem\File;
|
||||
use Grav\Component\EventDispatcher\EventDispatcher;
|
||||
use Grav\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Grav\Component\Filesystem\ResourceLocator;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
/**
|
||||
* The Themes object holds an array of all the theme objects that Grav knows about.
|
||||
@@ -73,7 +74,7 @@ class Themes extends Iterator
|
||||
/**
|
||||
* Get theme configuration or throw exception if it cannot be found.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $name
|
||||
* @return Data
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
@@ -95,11 +96,11 @@ class Themes extends Iterator
|
||||
}
|
||||
|
||||
// Load default configuration.
|
||||
$file = File\Yaml::instance("themes://{$name}/{$name}" . YAML_EXT);
|
||||
$file = CompiledYamlFile::instance("themes://{$name}/{$name}" . YAML_EXT);
|
||||
$obj = new Data($file->content(), $blueprint);
|
||||
|
||||
// Override with user configuration.
|
||||
$file = File\Yaml::instance("user://config/themes/{$name}" . YAML_EXT);
|
||||
$file = CompiledYamlFile::instance("user://config/themes/{$name}" . YAML_EXT);
|
||||
$obj->merge($file->content());
|
||||
|
||||
// Save configuration always to user/config.
|
||||
@@ -130,7 +131,7 @@ class Themes extends Iterator
|
||||
$config = $this->config;
|
||||
$name = $this->current();
|
||||
|
||||
/** @var ResourceLocator $locator */
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $grav['locator'];
|
||||
$file = $locator('theme://theme.php') ?: $locator("theme://{$name}.php");
|
||||
|
||||
@@ -145,7 +146,7 @@ class Themes extends Iterator
|
||||
$class = new $className($grav, $config, $name);
|
||||
}
|
||||
}
|
||||
} elseif (!$locator('theme://')) {
|
||||
} elseif (!$locator('theme://') && !defined('GRAV_CLI')) {
|
||||
exit("Theme '$name' does not exist, unable to display page.");
|
||||
}
|
||||
|
||||
@@ -161,24 +162,21 @@ class Themes extends Iterator
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function configure() {
|
||||
public function configure()
|
||||
{
|
||||
$name = $this->current();
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $this->config;
|
||||
|
||||
$themeConfig = File\Yaml::instance("themes://{$name}/{$name}" . YAML_EXT)->content();
|
||||
$this->loadConfiguration($name, $config);
|
||||
|
||||
$config->merge(['themes' => [$name => $themeConfig]]);
|
||||
|
||||
/** @var ResourceLocator $locator */
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $this->grav['locator'];
|
||||
|
||||
// TODO: move
|
||||
$registered = stream_get_wrappers();
|
||||
$schemes = $config->get(
|
||||
"themes.{$name}.streams.schemes",
|
||||
['theme' => ['paths' => ["user/themes/{$name}"]]]
|
||||
['theme' => ['paths' => $locator->findResources("themes://{$name}", false)]]
|
||||
);
|
||||
|
||||
foreach ($schemes as $scheme => $config) {
|
||||
@@ -196,7 +194,7 @@ class Themes extends Iterator
|
||||
}
|
||||
$type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream';
|
||||
if ($type[0] != '\\') {
|
||||
$type = '\\Grav\\Component\\Filesystem\\StreamWrapper\\' . $type;
|
||||
$type = '\\RocketTheme\\Toolbox\\StreamWrapper\\' . $type;
|
||||
}
|
||||
|
||||
if (!stream_wrapper_register($scheme, $type)) {
|
||||
@@ -204,4 +202,11 @@ class Themes extends Iterator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function loadConfiguration($name, Config $config)
|
||||
{
|
||||
$themeConfig = CompiledYamlFile::instance("themes://{$name}/{$name}" . YAML_EXT)->content();
|
||||
|
||||
$config->joinDefaults("themes.{$name}", $themeConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use \Grav\Common\Page\Page;
|
||||
use Grav\Component\Filesystem\ResourceLocator;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Page\Page;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
/**
|
||||
* The Twig object handles all the Twig template rendering for Grav. It's a singleton object
|
||||
@@ -67,7 +68,7 @@ class Twig
|
||||
if (!isset($this->twig)) {
|
||||
/** @var Config $config */
|
||||
$config = $this->grav['config'];
|
||||
/** @var ResourceLocator $locator */
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $this->grav['locator'];
|
||||
|
||||
$this->twig_paths = $locator->findResources('theme://templates');
|
||||
@@ -79,7 +80,7 @@ class Twig
|
||||
|
||||
$params = $config->get('system.twig');
|
||||
if (!empty($params['cache'])) {
|
||||
$params['cache'] = $locator->findResource('cache://');
|
||||
$params['cache'] = $locator->findResource('cache://twig', true, true);
|
||||
}
|
||||
|
||||
$this->twig = new \Twig_Environment($loader_chain, $params);
|
||||
@@ -172,8 +173,7 @@ class Twig
|
||||
// Get Twig template layout
|
||||
if ($item->modularTwig()) {
|
||||
$twig_vars['content'] = $content;
|
||||
// FIXME: this is inconsistent with main page.
|
||||
$template = $this->template('modular/' . $item->template()) . TEMPLATE_EXT;
|
||||
$template = $item->template() . TEMPLATE_EXT;
|
||||
$output = $this->twig->render($template, $twig_vars);
|
||||
} else {
|
||||
$name = '@Page:' . $item->path();
|
||||
@@ -184,6 +184,24 @@ class Twig
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string string to render.
|
||||
* @param array $vars Optional variables
|
||||
* @return string
|
||||
*/
|
||||
public function processString($string, array $vars = array())
|
||||
{
|
||||
// override the twig header vars for local resolution
|
||||
$this->grav->fireEvent('onTwigStringVariables');
|
||||
$vars += $this->twig_vars;
|
||||
|
||||
$name = '@Var:' . $string;
|
||||
$this->setTemplate($name, $string);
|
||||
$output = $this->twig->render($name, $vars);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Twig process that renders the site layout. This is the main twig process that renders the overall
|
||||
* page and handles all the layout for the site display.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
use Grav\Component\Filesystem\ResourceLocator;
|
||||
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
|
||||
/**
|
||||
* The Twig extension adds some filters and functions that are useful for Grav
|
||||
@@ -34,6 +36,7 @@ class TwigExtension extends \Twig_Extension
|
||||
new \Twig_SimpleFilter('truncate', array($this,'truncateFilter')),
|
||||
new \Twig_SimpleFilter('*ize', array($this,'inflectorFilter')),
|
||||
new \Twig_SimpleFilter('md5', array($this,'md5Filter')),
|
||||
new \Twig_SimpleFilter('sort_by_key', array($this,'sortByKeyFilter')),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -209,11 +212,38 @@ class TwigExtension extends \Twig_Extension
|
||||
public function urlFunc($input, $domain = false)
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
/** @var ResourceLocator $locator */
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $grav['locator'];
|
||||
|
||||
/** @var Uri $uri */
|
||||
$uri = $grav['uri'];
|
||||
|
||||
return $uri->rootUrl($domain) .'/'. $locator->findResource($input, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts a collection by key
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $filter
|
||||
* @param string $direction
|
||||
* @return string
|
||||
*/
|
||||
public function sortByKeyFilter($input, $filter, $direction = SORT_ASC)
|
||||
{
|
||||
$output = [];
|
||||
|
||||
if (!$input) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
foreach ($input as $key => $row) {
|
||||
$output[$key] = $row[$filter];
|
||||
}
|
||||
|
||||
array_multisort($output, $direction, $input);
|
||||
|
||||
return $input;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,10 @@ class Uri
|
||||
{
|
||||
|
||||
$base = 'http://';
|
||||
$uri = $_SERVER['REQUEST_URI'];
|
||||
$name = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost';
|
||||
$port = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 80;
|
||||
$uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
|
||||
|
||||
$root_path = rtrim(substr($_SERVER['PHP_SELF'], 0, strpos($_SERVER['PHP_SELF'], 'index.php')), '/');
|
||||
|
||||
|
||||
@@ -36,15 +39,15 @@ class Uri
|
||||
$base = (@$_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';
|
||||
}
|
||||
|
||||
$base .= $_SERVER['SERVER_NAME'];
|
||||
$base .= $name;
|
||||
|
||||
if ($_SERVER['SERVER_PORT'] != '80' && $_SERVER['SERVER_PORT'] != '443') {
|
||||
$base .= ":".$_SERVER['SERVER_PORT'];
|
||||
if ($port != '80' && $port != '443') {
|
||||
$base .= ":".$port;
|
||||
}
|
||||
|
||||
// check if userdir in the path and workaround PHP bug with PHP_SELF
|
||||
if (strpos($_SERVER['REQUEST_URI'], '/~') !== false && strpos($_SERVER['PHP_SELF'], '/~') === false) {
|
||||
$root_path = substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '/', 1)) . $root_path;
|
||||
if (strpos($uri, '/~') !== false && strpos($_SERVER['PHP_SELF'], '/~') === false) {
|
||||
$root_path = substr($uri, 0, strpos($uri, '/', 1)) . $root_path;
|
||||
}
|
||||
|
||||
$this->base = $base;
|
||||
@@ -130,7 +133,7 @@ class Uri
|
||||
*/
|
||||
public function route($absolute = false, $domain = false)
|
||||
{
|
||||
return ($absolute ? $this->rootUrl($domain) : '') . '/' . implode('/', $this->paths);
|
||||
return urldecode(($absolute ? $this->rootUrl($domain) : '') . '/' . implode('/', $this->paths));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,12 +142,16 @@ class Uri
|
||||
* @param string $id Optional attribute.
|
||||
* @return string
|
||||
*/
|
||||
public function query($id = null)
|
||||
public function query($id = null, $raw = false)
|
||||
{
|
||||
if (isset($id)) {
|
||||
return filter_var($this->query[$id], FILTER_SANITIZE_STRING) ;
|
||||
return isset($this->query[$id]) ? filter_var($this->query[$id], FILTER_SANITIZE_STRING) : null;
|
||||
} else {
|
||||
return http_build_query($this->query);
|
||||
if ($raw) {
|
||||
return $this->query;
|
||||
} else {
|
||||
return http_build_query($this->query);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +222,10 @@ class Uri
|
||||
*
|
||||
* @return String The extension of the URI
|
||||
*/
|
||||
public function extension() {
|
||||
public function extension($default = null) {
|
||||
if (!$this->extension) {
|
||||
$this->extension = $default;
|
||||
}
|
||||
return $this->extension;
|
||||
}
|
||||
|
||||
@@ -306,7 +316,6 @@ class Uri
|
||||
*/
|
||||
public function ip()
|
||||
{
|
||||
$ipaddress = '';
|
||||
if (getenv('HTTP_CLIENT_IP'))
|
||||
$ipaddress = getenv('HTTP_CLIENT_IP');
|
||||
else if(getenv('HTTP_X_FORWARDED_FOR'))
|
||||
@@ -324,4 +333,23 @@ class Uri
|
||||
return $ipaddress;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The opposite of built-in PHP method parse_url()
|
||||
*
|
||||
* @param $parsed_url
|
||||
* @return string
|
||||
*/
|
||||
public static function build_url($parsed_url) {
|
||||
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
|
||||
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
|
||||
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
|
||||
$user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
|
||||
$pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
|
||||
$pass = ($user || $pass) ? "$pass@" : '';
|
||||
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
|
||||
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
|
||||
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
|
||||
return "$scheme$user$pass$host$port$path$query$fragment";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
namespace Grav\Common\User;
|
||||
|
||||
use Grav\Common\Data\Blueprints;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
|
||||
/**
|
||||
* User object
|
||||
@@ -11,6 +13,30 @@ use Grav\Common\Data\Data;
|
||||
*/
|
||||
class User extends Data
|
||||
{
|
||||
/**
|
||||
* Load user account.
|
||||
*
|
||||
* Always creates user object. To check if user exists, use $this->exists().
|
||||
*
|
||||
* @param string $username
|
||||
* @return User
|
||||
*/
|
||||
public static function load($username)
|
||||
{
|
||||
// FIXME: validate directory name
|
||||
$blueprints = new Blueprints('blueprints://user');
|
||||
$blueprint = $blueprints->get('account');
|
||||
$file = CompiledYamlFile::instance(ACCOUNTS_DIR . $username . YAML_EXT);
|
||||
$content = $file->content();
|
||||
if (!isset($content['username'])) {
|
||||
$content['username'] = $username;
|
||||
}
|
||||
$user = new User($content, $blueprint);
|
||||
$user->file($file);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate user.
|
||||
*
|
||||
|
||||
@@ -40,6 +40,30 @@ abstract class Utils
|
||||
return (object) array_merge((array) $obj1, (array) $obj2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recurseive remove a directory - DANGEROUS! USE WITH CARE!!!!
|
||||
*
|
||||
* @param $dir
|
||||
* @return bool
|
||||
*/
|
||||
public static function rrmdir($dir) {
|
||||
$files = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
|
||||
/** @var \DirectoryIterator $fileinfo */
|
||||
foreach ($files as $fileinfo) {
|
||||
if ($fileinfo->isDir()) {
|
||||
if (false === rmdir($fileinfo->getRealPath())) return false;
|
||||
} else {
|
||||
if (false === unlink($fileinfo->getRealPath())) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return rmdir($dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate HTML by text length.
|
||||
*
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Component\ArrayTraits;
|
||||
|
||||
/**
|
||||
* Class ArrayAccess
|
||||
* @package Grav\Component\ArrayTraits
|
||||
*
|
||||
* @property array $items
|
||||
*/
|
||||
trait ArrayAccess
|
||||
{
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->items[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return isset($this->items[$offset]) ? $this->items[$offset] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->items[$offset] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->items[$offset]);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Component\ArrayTraits;
|
||||
|
||||
/**
|
||||
* Class Constructor
|
||||
* @package Grav\Component\ArrayTraits
|
||||
*
|
||||
* @property array $items
|
||||
*/
|
||||
trait Constructor
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $items Initial items inside the iterator.
|
||||
*/
|
||||
public function __construct(array $items = array())
|
||||
{
|
||||
$this->items = $items;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Component\ArrayTraits;
|
||||
|
||||
/**
|
||||
* Class Countable
|
||||
* @package Grav\Component\ArrayTraits
|
||||
*
|
||||
* @property array $items;
|
||||
*/
|
||||
trait Countable
|
||||
{
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
count($this->items);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Component\ArrayTraits;
|
||||
|
||||
trait Getters
|
||||
{
|
||||
use ArrayAccess;
|
||||
|
||||
/**
|
||||
* Magic setter method
|
||||
*
|
||||
* @param mixed $offset Asset name value
|
||||
* @param mixed $value Asset value
|
||||
*/
|
||||
public function __set($offset, $value)
|
||||
{
|
||||
$this->offsetSet($offset, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic getter method
|
||||
*
|
||||
* @param mixed $offset Asset name value
|
||||
* @return mixed Asset value
|
||||
*/
|
||||
public function __get($offset)
|
||||
{
|
||||
return $this->offsetGet($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method to determine if the attribute is set
|
||||
*
|
||||
* @param mixed $offset Asset name value
|
||||
* @return boolean True if the value is set
|
||||
*/
|
||||
public function __isset($offset)
|
||||
{
|
||||
return $this->offsetExists($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method to unset the attribute
|
||||
*
|
||||
* @param mixed $offset The name value to unset
|
||||
*/
|
||||
public function __unset($offset)
|
||||
{
|
||||
$this->offsetUnset($offset);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Component\DI;
|
||||
|
||||
class Container extends \Pimple\Container
|
||||
{
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Component\DI;
|
||||
|
||||
interface ServiceProviderInterface extends \Pimple\ServiceProviderInterface
|
||||
{
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Component\EventDispatcher;
|
||||
|
||||
use Grav\Component\ArrayTraits\ArrayAccess;
|
||||
use Grav\Component\ArrayTraits\Constructor;
|
||||
|
||||
class Event extends \Symfony\Component\EventDispatcher\Event implements \ArrayAccess
|
||||
{
|
||||
use ArrayAccess, Constructor;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $items = array();
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Component\EventDispatcher;
|
||||
|
||||
use \Symfony\Component\EventDispatcher\Event as BaseEvent;
|
||||
|
||||
class EventDispatcher extends \Symfony\Component\EventDispatcher\EventDispatcher
|
||||
{
|
||||
public function dispatch($eventName, BaseEvent $event = null)
|
||||
{
|
||||
if (null === $event) {
|
||||
$event = new Event();
|
||||
}
|
||||
|
||||
return parent::dispatch($eventName, $event);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Component\EventDispatcher;
|
||||
|
||||
interface EventSubscriberInterface extends \Symfony\Component\EventDispatcher\EventSubscriberInterface
|
||||
{
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Grav\Component\Filesystem;
|
||||
|
||||
/**
|
||||
* Implements Uniform Resource Location
|
||||
*
|
||||
* @link http://webmozarts.com/2013/06/19/the-power-of-uniform-resource-location-in-php/
|
||||
*/
|
||||
class ResourceLocator
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $schemes = [];
|
||||
|
||||
/**
|
||||
* @param string $scheme
|
||||
* @param string $prefix
|
||||
* @param string|array $paths
|
||||
*/
|
||||
public function addPath($scheme, $prefix, $paths)
|
||||
{
|
||||
$list = [];
|
||||
foreach((array) $paths as $path) {
|
||||
$path = trim($path, '/');
|
||||
if (strstr($path, '://')) {
|
||||
$list = array_merge($list, $this->find($path, true, false));
|
||||
} else {
|
||||
$list[] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->schemes[$scheme][$prefix])) {
|
||||
$list = array_merge($list, $this->schemes[$scheme][$prefix]);
|
||||
}
|
||||
|
||||
$this->schemes[$scheme][$prefix] = $list;
|
||||
|
||||
// Sort in reverse order to get longer prefixes to be matched first.
|
||||
krsort($this->schemes[$scheme]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $uri
|
||||
* @return string|bool
|
||||
*/
|
||||
public function __invoke($uri)
|
||||
{
|
||||
return $this->find($uri, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uri
|
||||
* @param bool $absolute
|
||||
* @return string|bool
|
||||
*/
|
||||
public function findResource($uri, $absolute = true)
|
||||
{
|
||||
return $this->find($uri, false, $absolute);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uri
|
||||
* @param bool $absolute
|
||||
* @return array
|
||||
*/
|
||||
public function findResources($uri, $absolute = true)
|
||||
{
|
||||
return $this->find($uri, true, $absolute);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uri
|
||||
* @param bool $absolute
|
||||
* @param bool $array
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @return array|string|bool
|
||||
*/
|
||||
protected function find($uri, $array, $absolute)
|
||||
{
|
||||
$segments = explode('://', $uri, 2);
|
||||
$file = array_pop($segments);
|
||||
$scheme = array_pop($segments);
|
||||
|
||||
if (!$scheme) {
|
||||
$scheme = 'file';
|
||||
}
|
||||
|
||||
if (!isset($this->schemes[$scheme])) {
|
||||
throw new \InvalidArgumentException("Invalid resource {$scheme}://");
|
||||
}
|
||||
if (!$file && $scheme == 'file') {
|
||||
$file = getcwd();
|
||||
}
|
||||
|
||||
$results = $array ? [] : false;
|
||||
foreach ($this->schemes[$scheme] as $prefix => $paths) {
|
||||
if ($prefix && strpos($file, $prefix) !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($paths as $path) {
|
||||
$filename = $path . '/' . ltrim(substr($file, strlen($prefix)), '\/');
|
||||
$lookup = ROOT_DIR . '/' . $filename;
|
||||
|
||||
if (file_exists($lookup)) {
|
||||
if (!$array) {
|
||||
return $absolute ? $lookup : $filename;
|
||||
}
|
||||
$results[] = $absolute ? $lookup : $filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Component\Filesystem\StreamWrapper;
|
||||
|
||||
use Grav\Component\Filesystem\ResourceLocator;
|
||||
|
||||
class ReadOnlyStream extends Stream implements StreamInterface
|
||||
{
|
||||
/**
|
||||
* @var ResourceLocator
|
||||
*/
|
||||
protected static $locator;
|
||||
|
||||
public function stream_open($uri, $mode, $options, &$opened_url)
|
||||
{
|
||||
if (!in_array($mode, ['r', 'rb', 'rt'])) {
|
||||
if ($options & STREAM_REPORT_ERRORS) {
|
||||
trigger_error('stream_open() write modes not supported for read-only stream wrappers', E_USER_WARNING);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = $this->getPath($uri);
|
||||
|
||||
if (!$path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->handle = ($options & STREAM_REPORT_ERRORS) ? fopen($path, $mode) : @fopen($path, $mode);
|
||||
|
||||
return (bool) $this->handle;
|
||||
}
|
||||
|
||||
public function stream_lock($operation)
|
||||
{
|
||||
// Disallow exclusive lock or non-blocking lock requests
|
||||
if (!in_array($operation, [LOCK_SH, LOCK_UN, LOCK_SH | LOCK_NB])) {
|
||||
trigger_error(
|
||||
'stream_lock() exclusive lock operations not supported for read-only stream wrappers',
|
||||
E_USER_WARNING
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return flock($this->handle, $operation);
|
||||
}
|
||||
|
||||
public function stream_write($data)
|
||||
{
|
||||
throw new \BadMethodCallException('stream_write() not supported for read-only stream wrappers');
|
||||
}
|
||||
|
||||
public function unlink($uri)
|
||||
{
|
||||
throw new \BadMethodCallException('unlink() not supported for read-only stream wrappers');
|
||||
}
|
||||
|
||||
public function rename($from_uri, $to_uri)
|
||||
{
|
||||
throw new \BadMethodCallException('rename() not supported for read-only stream wrappers');
|
||||
}
|
||||
|
||||
public function mkdir($uri, $mode, $options)
|
||||
{
|
||||
throw new \BadMethodCallException('mkdir() not supported for read-only stream wrappers');
|
||||
}
|
||||
|
||||
public function rmdir($uri, $options)
|
||||
{
|
||||
throw new \BadMethodCallException('rmdir() not supported for read-only stream wrappers');
|
||||
}
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Component\Filesystem\StreamWrapper;
|
||||
|
||||
use Grav\Component\Filesystem\ResourceLocator;
|
||||
|
||||
class Stream implements StreamInterface
|
||||
{
|
||||
/**
|
||||
* A generic resource handle.
|
||||
*
|
||||
* @var Resource
|
||||
*/
|
||||
protected $handle = null;
|
||||
|
||||
/**
|
||||
* @var ResourceLocator
|
||||
*/
|
||||
protected static $locator;
|
||||
|
||||
/**
|
||||
* @param ResourceLocator $locator
|
||||
*/
|
||||
public static function setLocator(ResourceLocator $locator)
|
||||
{
|
||||
static::$locator = $locator;
|
||||
}
|
||||
|
||||
public function stream_open($uri, $mode, $options, &$opened_url)
|
||||
{
|
||||
$path = $this->getPath($uri, $mode);
|
||||
|
||||
if (!$path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->handle = ($options & STREAM_REPORT_ERRORS) ? fopen($path, $mode) : @fopen($path, $mode);
|
||||
|
||||
return (bool) $this->handle;
|
||||
}
|
||||
|
||||
public function stream_close()
|
||||
{
|
||||
return fclose($this->handle);
|
||||
}
|
||||
|
||||
public function stream_lock($operation)
|
||||
{
|
||||
if (in_array($operation, [LOCK_SH, LOCK_EX, LOCK_UN, LOCK_NB])) {
|
||||
return flock($this->handle, $operation);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function stream_metadata($uri, $option, $value)
|
||||
{
|
||||
switch ($option) {
|
||||
case STREAM_META_TOUCH:
|
||||
list ($time, $atime) = $value;
|
||||
return touch($uri, $time, $atime);
|
||||
|
||||
case STREAM_META_OWNER_NAME:
|
||||
case STREAM_META_OWNER:
|
||||
return chown($uri, $value);
|
||||
|
||||
case STREAM_META_GROUP_NAME:
|
||||
case STREAM_META_GROUP:
|
||||
return chgrp($uri, $value);
|
||||
|
||||
case STREAM_META_ACCESS:
|
||||
return chmod($uri, $value);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function stream_read($count)
|
||||
{
|
||||
return fread($this->handle, $count);
|
||||
}
|
||||
|
||||
public function stream_write($data)
|
||||
{
|
||||
return fwrite($this->handle, $data);
|
||||
}
|
||||
|
||||
public function stream_eof()
|
||||
{
|
||||
return feof($this->handle);
|
||||
}
|
||||
|
||||
public function stream_seek($offset, $whence)
|
||||
{
|
||||
// fseek returns 0 on success and -1 on a failure.
|
||||
return !fseek($this->handle, $offset, $whence);
|
||||
}
|
||||
|
||||
public function stream_flush()
|
||||
{
|
||||
return fflush($this->handle);
|
||||
}
|
||||
|
||||
public function stream_tell()
|
||||
{
|
||||
return ftell($this->handle);
|
||||
}
|
||||
|
||||
public function stream_stat()
|
||||
{
|
||||
return fstat($this->handle);
|
||||
}
|
||||
|
||||
public function unlink($uri)
|
||||
{
|
||||
$path = $this->getPath($uri);
|
||||
|
||||
if (!$path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return unlink($path);
|
||||
}
|
||||
|
||||
public function rename($fromUri, $toUri)
|
||||
{
|
||||
$fromPath = $this->getPath($fromUri);
|
||||
$toPath = $this->getPath($toUri);
|
||||
|
||||
if (!($fromPath && $toPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return rename($fromPath, $toPath);
|
||||
}
|
||||
|
||||
public function mkdir($uri, $mode, $options)
|
||||
{
|
||||
$recursive = (bool) ($options & STREAM_MKDIR_RECURSIVE);
|
||||
$path = $this->getPath($uri, $recursive ? $mode : null);
|
||||
|
||||
if (!$path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ($options & STREAM_REPORT_ERRORS) ? mkdir($path, $mode, $recursive) : @mkdir($path, $mode, $recursive);
|
||||
}
|
||||
|
||||
public function rmdir($uri, $options)
|
||||
{
|
||||
$path = $this->getPath($uri);
|
||||
|
||||
if (!$path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ($options & STREAM_REPORT_ERRORS) ? rmdir($path) : @rmdir($path);
|
||||
}
|
||||
|
||||
public function url_stat($uri, $flags)
|
||||
{
|
||||
$path = $this->getPath($uri);
|
||||
|
||||
if (!$path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Suppress warnings if requested or if the file or directory does not
|
||||
// exist. This is consistent with PHP's plain filesystem stream wrapper.
|
||||
return ($flags & STREAM_URL_STAT_QUIET || !file_exists($path)) ? @stat($path) : stat($path);
|
||||
}
|
||||
|
||||
public function dir_opendir($uri, $options)
|
||||
{
|
||||
$path = $this->getPath($uri);
|
||||
|
||||
if (!$path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->handle = opendir($path);
|
||||
|
||||
return (bool) $this->handle;
|
||||
}
|
||||
|
||||
public function dir_readdir()
|
||||
{
|
||||
return readdir($this->handle);
|
||||
}
|
||||
|
||||
public function dir_rewinddir()
|
||||
{
|
||||
rewinddir($this->handle);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function dir_closedir()
|
||||
{
|
||||
closedir($this->handle);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getPath($uri, $mode = null)
|
||||
{
|
||||
$path = $this->findPath($uri);
|
||||
|
||||
if ($mode == null || !$path || file_exists($path)) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
if ($mode[0] == 'r') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We are either opening a file or creating directory.
|
||||
list($scheme, $target) = explode('://', $uri, 2);
|
||||
|
||||
$path = $this->findPath($scheme . '://' . dirname($target));
|
||||
|
||||
if (!$path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $path . '/' . basename($uri);
|
||||
}
|
||||
|
||||
protected function findPath($uri)
|
||||
{
|
||||
return static::$locator ? static::$locator->findResource($uri) : false;
|
||||
}
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Component\Filesystem\StreamWrapper;
|
||||
|
||||
/**
|
||||
* Generic PHP stream wrapper interface.
|
||||
*
|
||||
* @see http://www.php.net/manual/class.streamwrapper.php
|
||||
*/
|
||||
interface StreamInterface
|
||||
{
|
||||
/**
|
||||
* Support for fopen(), file_get_contents(), file_put_contents() etc.
|
||||
*
|
||||
* @param string $uri A string containing the URI to the file to open.
|
||||
* @param string $mode The file mode ("r", "wb" etc.).
|
||||
* @param int $options A bit mask of STREAM_USE_PATH and STREAM_REPORT_ERRORS.
|
||||
* @param string $opened_url A string containing the path actually opened.
|
||||
*
|
||||
* @return bool Returns TRUE if file was opened successfully.
|
||||
* @see http://php.net/manual/streamwrapper.stream-open.php
|
||||
*/
|
||||
public function stream_open($uri, $mode, $options, &$opened_url);
|
||||
|
||||
/**
|
||||
* Support for fclose().
|
||||
*
|
||||
* @return bool TRUE if stream was successfully closed.
|
||||
* @see http://php.net/manual/streamwrapper.stream-close.php
|
||||
*/
|
||||
public function stream_close();
|
||||
|
||||
/**
|
||||
* Support for flock().
|
||||
*
|
||||
* @param $operation
|
||||
* One of the following:
|
||||
* - LOCK_SH to acquire a shared lock (reader).
|
||||
* - LOCK_EX to acquire an exclusive lock (writer).
|
||||
* - LOCK_UN to release a lock (shared or exclusive).
|
||||
* - LOCK_NB if you don't want flock() to block while locking (not
|
||||
* supported on Windows).
|
||||
*
|
||||
* @return bool Always returns TRUE at the present time.
|
||||
* @see http://php.net/manual/streamwrapper.stream-lock.php
|
||||
*/
|
||||
public function stream_lock($operation);
|
||||
|
||||
/**
|
||||
* Support for touch(), chmod(), chown(), chgrp().
|
||||
*
|
||||
* @param $path
|
||||
* The file path or URL to set metadata. Note that in the case of a URL, it must be a :// delimited URL.
|
||||
* Other URL forms are not supported.
|
||||
*
|
||||
* @param $option
|
||||
* One of:
|
||||
* - STREAM_META_TOUCH The method was called in response to touch()
|
||||
* - STREAM_META_OWNER_NAME The method was called in response to chown() with string parameter
|
||||
* - STREAM_META_OWNER The method was called in response to chown()
|
||||
* - STREAM_META_GROUP_NAME The method was called in response to chgrp()
|
||||
* - STREAM_META_GROUP The method was called in response to chgrp()
|
||||
* - STREAM_META_ACCESS The method was called in response to chmod()
|
||||
*
|
||||
* @param $value
|
||||
* If option is
|
||||
* - STREAM_META_TOUCH: Array consisting of two arguments of the touch() function.
|
||||
* - STREAM_META_OWNER_NAME or
|
||||
* STREAM_META_GROUP_NAME: The name of the owner user/group as string.
|
||||
* - STREAM_META_OWNER or
|
||||
* STREAM_META_GROUP: The value owner user/group argument as integer.
|
||||
* - STREAM_META_ACCESS: The argument of the chmod() as integer.
|
||||
|
||||
|
||||
*
|
||||
* @return bool
|
||||
* @see http://php.net/manual/en/streamwrapper.stream-metadata.php
|
||||
*/
|
||||
public function stream_metadata($path, $option, $value);
|
||||
|
||||
/**
|
||||
* Support for fread(), file_get_contents() etc.
|
||||
*
|
||||
* @param $count
|
||||
* Maximum number of bytes to be read.
|
||||
*
|
||||
* @return string|bool The string that was read, or FALSE in case of an error.
|
||||
* @see http://php.net/manual/streamwrapper.stream-read.php
|
||||
*/
|
||||
public function stream_read($count);
|
||||
|
||||
/**
|
||||
* Support for fwrite(), file_put_contents() etc.
|
||||
*
|
||||
* @param $data
|
||||
* The string to be written.
|
||||
*
|
||||
* @return int The number of bytes written (integer).
|
||||
* @see http://php.net/manual/streamwrapper.stream-write.php
|
||||
*/
|
||||
public function stream_write($data);
|
||||
|
||||
/**
|
||||
* Support for feof().
|
||||
*
|
||||
* @return bool TRUE if end-of-file has been reached.
|
||||
* @see http://php.net/manual/streamwrapper.stream-eof.php
|
||||
*/
|
||||
public function stream_eof();
|
||||
|
||||
/**
|
||||
* Support for fseek().
|
||||
*
|
||||
* @param $offset
|
||||
* The byte offset to got to.
|
||||
* @param $whence
|
||||
* SEEK_SET, SEEK_CUR, or SEEK_END.
|
||||
*
|
||||
* @return bool TRUE on success.
|
||||
* @see http://php.net/manual/streamwrapper.stream-seek.php
|
||||
*/
|
||||
public function stream_seek($offset, $whence);
|
||||
|
||||
/**
|
||||
* Support for fflush().
|
||||
*
|
||||
* @return bool TRUE if data was successfully stored (or there was no data to store).
|
||||
* @see http://php.net/manual/streamwrapper.stream-flush.php
|
||||
*/
|
||||
public function stream_flush();
|
||||
|
||||
/**
|
||||
* Support for ftell().
|
||||
*
|
||||
* @return int The current offset in bytes from the beginning of file.
|
||||
* @see http://php.net/manual/streamwrapper.stream-tell.php
|
||||
*/
|
||||
public function stream_tell();
|
||||
|
||||
/**
|
||||
* Support for fstat().
|
||||
*
|
||||
* @return array An array with file status, or FALSE in case of an error - see fstat()
|
||||
* @see http://php.net/manual/streamwrapper.stream-stat.php
|
||||
*/
|
||||
public function stream_stat();
|
||||
|
||||
/**
|
||||
* Support for unlink().
|
||||
*
|
||||
* @param $uri
|
||||
* A string containing the URI to the resource to delete.
|
||||
*
|
||||
* @return
|
||||
* TRUE if resource was successfully deleted.
|
||||
* @see http://php.net/manual/streamwrapper.unlink.php
|
||||
*/
|
||||
public function unlink($uri);
|
||||
|
||||
/**
|
||||
* Support for rename().
|
||||
*
|
||||
* @param $from_uri ,
|
||||
* The URI to the file to rename.
|
||||
* @param $to_uri
|
||||
* The new URI for file.
|
||||
*
|
||||
* @return bool TRUE if file was successfully renamed.
|
||||
* @see http://php.net/manual/streamwrapper.rename.php
|
||||
*/
|
||||
public function rename($from_uri, $to_uri);
|
||||
|
||||
/**
|
||||
* Support for mkdir().
|
||||
*
|
||||
* @param $uri
|
||||
* A string containing the URI to the directory to create.
|
||||
* @param $mode
|
||||
* Permission flags - see mkdir().
|
||||
* @param $options
|
||||
* A bit mask of STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE.
|
||||
*
|
||||
* @return bool TRUE if directory was successfully created.
|
||||
* @see http://php.net/manual/streamwrapper.mkdir.php
|
||||
*/
|
||||
public function mkdir($uri, $mode, $options);
|
||||
|
||||
/**
|
||||
* Support for rmdir().
|
||||
*
|
||||
* @param $uri
|
||||
* A string containing the URI to the directory to delete.
|
||||
* @param $options
|
||||
* A bit mask of STREAM_REPORT_ERRORS.
|
||||
*
|
||||
* @return
|
||||
* TRUE if directory was successfully removed.
|
||||
*
|
||||
* @see http://php.net/manual/streamwrapper.rmdir.php
|
||||
*/
|
||||
public function rmdir($uri, $options);
|
||||
|
||||
/**
|
||||
* Support for stat().
|
||||
*
|
||||
* @param $uri
|
||||
* A string containing the URI to get information about.
|
||||
* @param $flags
|
||||
* A bit mask of STREAM_URL_STAT_LINK and STREAM_URL_STAT_QUIET.
|
||||
*
|
||||
* @return array An array with file status, or FALSE in case of an error - see fstat()
|
||||
* @see http://php.net/manual/streamwrapper.url-stat.php
|
||||
*/
|
||||
public function url_stat($uri, $flags);
|
||||
|
||||
/**
|
||||
* Support for opendir().
|
||||
*
|
||||
* @param $uri
|
||||
* A string containing the URI to the directory to open.
|
||||
* @param $options
|
||||
* Unknown (parameter is not documented in PHP Manual).
|
||||
*
|
||||
* @return bool TRUE on success.
|
||||
* @see http://php.net/manual/streamwrapper.dir-opendir.php
|
||||
*/
|
||||
public function dir_opendir($uri, $options);
|
||||
|
||||
/**
|
||||
* Support for readdir().
|
||||
*
|
||||
* @return string The next filename, or FALSE if there are no more files in the directory.
|
||||
* @see http://php.net/manual/streamwrapper.dir-readdir.php
|
||||
*/
|
||||
public function dir_readdir();
|
||||
|
||||
/**
|
||||
* Support for rewinddir().
|
||||
*
|
||||
* @return bool TRUE on success.
|
||||
* @see http://php.net/manual/streamwrapper.dir-rewinddir.php
|
||||
*/
|
||||
public function dir_rewinddir();
|
||||
|
||||
/**
|
||||
* Support for closedir().
|
||||
*
|
||||
* @return bool TRUE on success.
|
||||
* @see http://php.net/manual/streamwrapper.dir-closedir.php
|
||||
*/
|
||||
public function dir_closedir();
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace Grav\Console;
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
@@ -8,7 +8,7 @@ use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use \Symfony\Component\Yaml\Yaml;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class BackupCommand extends Command {
|
||||
|
||||
@@ -25,7 +25,7 @@ class BackupCommand extends Command {
|
||||
|
||||
)
|
||||
->setDescription("Creates a backup of the Grav instance")
|
||||
->setHelp('The <info>backup</info> creates a zipped backup');
|
||||
->setHelp('The <info>backup</info> creates a zipped backup. Optionally can be saved in a different destination.');
|
||||
|
||||
|
||||
$this->source = getcwd();
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Grav\Console;
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
@@ -10,8 +11,6 @@ use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
|
||||
class CleanCommand extends Command {
|
||||
|
||||
protected $destination_dir = 'distribution';
|
||||
|
||||
protected $paths_to_remove = [
|
||||
'user/plugins/email/vendor/swiftmailer/swiftmailer/.travis.yml',
|
||||
'user/plugins/email/vendor/swiftmailer/swiftmailer/build.xml',
|
||||
@@ -48,7 +47,10 @@ class CleanCommand extends Command {
|
||||
'vendor/erusev/parsedown/.git',
|
||||
'vendor/erusev/parsedown/test',
|
||||
'vendor/erusev/parsedown-extra/composer.json',
|
||||
'vendor/erusev/parsedown-extra/phpunit.xml.dist',
|
||||
'vendor/erusev/parsedown-extra/.travis.yml',
|
||||
'vendor/erusev/parsedown-extra/.git',
|
||||
'vendor/erusev/parsedown-extra/test',
|
||||
'vendor/gregwar/image/Gregwar/Image/composer.json',
|
||||
'vendor/gregwar/image/Gregwar/Image/phpunit.xml',
|
||||
'vendor/gregwar/image/Gregwar/Image/.gitignore',
|
||||
@@ -87,6 +89,12 @@ class CleanCommand extends Command {
|
||||
'vendor/pimple/pimple/ext',
|
||||
'vendor/pimple/pimple/phpunit.xml.dist',
|
||||
'vendor/pimple/pimple/src/Pimple/Tests',
|
||||
'vendor/rockettheme/toolbox/.git',
|
||||
'vendor/rockettheme/toolbox/.gitignore',
|
||||
'vendor/rockettheme/toolbox/.scrutinizer.yml',
|
||||
'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',
|
||||
@@ -153,7 +161,7 @@ class CleanCommand extends Command {
|
||||
foreach($this->paths_to_remove as $path) {
|
||||
$path = ROOT_DIR . $path;
|
||||
|
||||
if (is_dir($path) && @$this->rrmdir($path)) {
|
||||
if (is_dir($path) && @Folder::delete($path)) {
|
||||
$anything = true;
|
||||
$output->writeln('<red>dir: </red>' . $path);
|
||||
} elseif (is_file($path) && @unlink($path)) {
|
||||
@@ -169,18 +177,4 @@ class CleanCommand extends Command {
|
||||
|
||||
}
|
||||
|
||||
// Recursively Delete folder - DANGEROUS! USE WITH CARE!!!!
|
||||
private function rrmdir($dir) {
|
||||
if (is_dir($dir)) {
|
||||
$objects = scandir($dir);
|
||||
foreach ($objects as $object) {
|
||||
if ($object != "." && $object != "..") {
|
||||
if (filetype($dir."/".$object) == "dir") $this->rrmdir($dir."/".$object); else unlink($dir."/".$object);
|
||||
}
|
||||
}
|
||||
reset($objects);
|
||||
rmdir($dir);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Grav\Console;
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
@@ -11,36 +12,42 @@ use \Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class ClearCacheCommand extends Command {
|
||||
|
||||
protected $paths_to_remove = [
|
||||
'cache',
|
||||
'images',
|
||||
'assets'
|
||||
protected $standard_remove = [
|
||||
'cache/twig/',
|
||||
'cache/doctrine/',
|
||||
'cache/compiled/',
|
||||
'cache/validated-',
|
||||
'images/',
|
||||
'assets/',
|
||||
];
|
||||
|
||||
protected $all_remove = [
|
||||
'cache/',
|
||||
'images/',
|
||||
'assets/'
|
||||
];
|
||||
|
||||
protected function configure() {
|
||||
$this
|
||||
->setName("clear-cache")
|
||||
->setDescription("Clears Grav cache")
|
||||
->addOption('all', null, InputOption::VALUE_NONE, 'If set will remove all')
|
||||
->setHelp('The <info>clear-cache</info> deletes all cache files');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
|
||||
|
||||
// Create a red output option
|
||||
$output->getFormatter()->setStyle('red', new OutputFormatterStyle('red'));
|
||||
$output->getFormatter()->setStyle('cyan', new OutputFormatterStyle('cyan'));
|
||||
$output->getFormatter()->setStyle('green', new OutputFormatterStyle('green'));
|
||||
$output->getFormatter()->setStyle('magenta', new OutputFormatterStyle('magenta'));
|
||||
|
||||
$this->cleanPaths($output);
|
||||
|
||||
|
||||
$this->cleanPaths($input, $output);
|
||||
}
|
||||
|
||||
// loops over the array of paths and deletes the files/folders
|
||||
private function cleanPaths($output)
|
||||
private function cleanPaths($input, $output)
|
||||
{
|
||||
$output->writeln('');
|
||||
$output->writeln('<magenta>Clearing cache</magenta>');
|
||||
@@ -50,12 +57,23 @@ class ClearCacheCommand extends Command {
|
||||
|
||||
$anything = false;
|
||||
|
||||
foreach($this->paths_to_remove as $path) {
|
||||
$files = glob(ROOT_DIR . rtrim($path, DS) . DS .'*');
|
||||
if ($input->getOption('all')) {
|
||||
$remove_paths = $this->all_remove;
|
||||
} else {
|
||||
$remove_paths = $this->standard_remove;
|
||||
}
|
||||
|
||||
foreach($remove_paths as $path) {
|
||||
|
||||
$files = glob(ROOT_DIR . $path . '*');
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (is_file($file) && @unlink($file)) $anything = true;
|
||||
elseif (is_dir($file) && @$this->rrmdir($file)) $anything = true;
|
||||
if (is_file($file)) {
|
||||
if (@unlink($file)) $anything = true;
|
||||
}
|
||||
elseif (is_dir($file)) {
|
||||
if (@Folder::delete($file)) $anything = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($anything) $output->writeln('<red>Cleared: </red>' . $path . '*');
|
||||
@@ -74,20 +92,5 @@ class ClearCacheCommand extends Command {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Recursively Delete folder - DANGEROUS! USE WITH CARE!!!!
|
||||
private function rrmdir($dir) {
|
||||
if (is_dir($dir)) {
|
||||
$objects = scandir($dir);
|
||||
foreach ($objects as $object) {
|
||||
if ($object != "." && $object != "..") {
|
||||
if (filetype($dir."/".$object) == "dir") $this->rrmdir($dir."/".$object); else unlink($dir."/".$object);
|
||||
}
|
||||
}
|
||||
reset($objects);
|
||||
rmdir($dir);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace Grav\Console;
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
@@ -31,8 +31,8 @@ class InstallCommand extends Command {
|
||||
'Where to install the required bits (default to current project)'
|
||||
|
||||
)
|
||||
->setDescription("Handles cloning and symlinking for Grav")
|
||||
->setHelp('The <info>install</info> provides clone and symlink installation chores');
|
||||
->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');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
@@ -54,8 +54,7 @@ class InstallCommand extends Command {
|
||||
|
||||
if (file_exists($local_config_file)) {
|
||||
$this->local_config = Yaml::parse($local_config_file);
|
||||
$output->writeln('');
|
||||
$output->writeln('read local config from <cyan>' . $local_config_file . '</cyan>');
|
||||
$output->writeln('Read local config from <cyan>' . $local_config_file . '</cyan>');
|
||||
}
|
||||
|
||||
// Look for dependencies file in ROOT and USER dir
|
||||
@@ -67,6 +66,10 @@ class InstallCommand extends Command {
|
||||
$output->writeln('<red>ERROR</red> Missing .dependencies file in <cyan>user/</cyan> folder');
|
||||
}
|
||||
|
||||
// Updates composer first
|
||||
$output->writeln("\nInstalling vendor dependencies");
|
||||
$output->writeln(system('php bin/composer.phar --working-dir="'.$this->destination.'" --no-interaction update'));
|
||||
|
||||
// If yaml config, process
|
||||
if ($this->config) {
|
||||
if (!$input->getOption('symlink')) {
|
||||
@@ -114,7 +117,7 @@ class InstallCommand extends Command {
|
||||
if (!$this->local_config) {
|
||||
$output->writeln('<red>No local configuration available, aborting...</red>');
|
||||
$output->writeln('');
|
||||
exit;
|
||||
return;
|
||||
}
|
||||
|
||||
exec('cd ' . $this->destination);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user