mirror of
https://github.com/getgrav/grav.git
synced 2025-12-05 23:39:58 +01:00
Compare commits
246 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6baf7e0b35 | ||
|
|
2a56f21d13 | ||
|
|
d253c3c6c5 | ||
|
|
289a838ba1 | ||
|
|
f6f3e96106 | ||
|
|
7d22305678 | ||
|
|
7571d1d562 | ||
|
|
b4c06f537d | ||
|
|
830c723bae | ||
|
|
b83ab07374 | ||
|
|
8200cb9336 | ||
|
|
b1a38306af | ||
|
|
6ec0f4782f | ||
|
|
d25f9bcf1b | ||
|
|
21f87ade2d | ||
|
|
8f54e5739f | ||
|
|
21a6594573 | ||
|
|
01ce80fb1a | ||
|
|
174672c411 | ||
|
|
4785103081 | ||
|
|
7030422b11 | ||
|
|
2a06dc9bea | ||
|
|
66927043de | ||
|
|
7d16bafd52 | ||
|
|
ee340e2d6f | ||
|
|
66d9fd1a5e | ||
|
|
7c28de6ae5 | ||
|
|
c95f602ea2 | ||
|
|
6361280d99 | ||
|
|
b3a9d7cd41 | ||
|
|
071989c554 | ||
|
|
f956d7113f | ||
|
|
f0472fdd76 | ||
|
|
01899676a4 | ||
|
|
b13d572ca8 | ||
|
|
a588e08405 | ||
|
|
6c93483220 | ||
|
|
733c13102b | ||
|
|
688d6fe17a | ||
|
|
25ff1f230f | ||
|
|
094b58130a | ||
|
|
a18ec00962 | ||
|
|
d9188e76ed | ||
|
|
a04a79af3e | ||
|
|
0c84392d0f | ||
|
|
be12f350cb | ||
|
|
01c7eadc92 | ||
|
|
4b3abc282a | ||
|
|
81358e9984 | ||
|
|
8179d8eac1 | ||
|
|
c22a2579a9 | ||
|
|
0a26772e35 | ||
|
|
364fc7afa8 | ||
|
|
82bd54eb0b | ||
|
|
d92d9bafc6 | ||
|
|
9c541ee20b | ||
|
|
105dc34b2a | ||
|
|
0d8b46a157 | ||
|
|
6142a12f48 | ||
|
|
1bda3eb1e3 | ||
|
|
96b2b327b2 | ||
|
|
e014b12626 | ||
|
|
d473f72edd | ||
|
|
7139425812 | ||
|
|
f5722d7baa | ||
|
|
467eb00f0f | ||
|
|
0ce3646977 | ||
|
|
53dd6c0860 | ||
|
|
e4a130c919 | ||
|
|
0d9ddb92d5 | ||
|
|
68a561d4fb | ||
|
|
5e651dd0e5 | ||
|
|
1bb2965916 | ||
|
|
4ea650fc6d | ||
|
|
501b38c4ba | ||
|
|
ede958bd61 | ||
|
|
f3d099e655 | ||
|
|
db5c3ea400 | ||
|
|
504f57930a | ||
|
|
02b1b2cf9d | ||
|
|
99dbb3225b | ||
|
|
08a8d69be2 | ||
|
|
a029f89ad2 | ||
|
|
ed02fed866 | ||
|
|
06de6c8e17 | ||
|
|
9ddfcd2154 | ||
|
|
217e5e5a2c | ||
|
|
272bf357d4 | ||
|
|
9a5d14aa13 | ||
|
|
2b70b2ad4f | ||
|
|
9323385b25 | ||
|
|
fe90204772 | ||
|
|
1b66e3d2a1 | ||
|
|
a2e1b9e100 | ||
|
|
74ab81b524 | ||
|
|
01be2df935 | ||
|
|
62e5ea3bbd | ||
|
|
b8c274b7b8 | ||
|
|
e154b13b6e | ||
|
|
0f312a3c43 | ||
|
|
d8823a6b3a | ||
|
|
73d5f9da90 | ||
|
|
7707b042e5 | ||
|
|
ff95a116c2 | ||
|
|
2fe6dda365 | ||
|
|
f30a3c137e | ||
|
|
a5d31e7187 | ||
|
|
73c42313fa | ||
|
|
686ba8a3f6 | ||
|
|
3397d5d2b7 | ||
|
|
9ab3524fc5 | ||
|
|
3f10fa1b4c | ||
|
|
6811fbea3d | ||
|
|
3e245ef686 | ||
|
|
28cff4e1da | ||
|
|
2bf67e482d | ||
|
|
3976e4ce23 | ||
|
|
a1ab94ffdd | ||
|
|
d6bed5441d | ||
|
|
e505c409ac | ||
|
|
409742c078 | ||
|
|
6bd76028ce | ||
|
|
87dc53912b | ||
|
|
83d606c34a | ||
|
|
1f906326e7 | ||
|
|
ec67bf4c5b | ||
|
|
127fe7fa2a | ||
|
|
e0de6f8b5f | ||
|
|
e81e35b7c2 | ||
|
|
9bc365fe9e | ||
|
|
21772c5481 | ||
|
|
7b32cbe2e1 | ||
|
|
04a2f618bd | ||
|
|
73a104000a | ||
|
|
b8cb788324 | ||
|
|
690bb8a8be | ||
|
|
26b0b02de2 | ||
|
|
e5348ec8f1 | ||
|
|
5e22eee1a8 | ||
|
|
534bb57d3f | ||
|
|
f8c203c033 | ||
|
|
300155e82e | ||
|
|
3117a214d4 | ||
|
|
9959868022 | ||
|
|
9248c5b709 | ||
|
|
85fb5bccf3 | ||
|
|
bd595ca9f6 | ||
|
|
ace8823bd6 | ||
|
|
5051b15145 | ||
|
|
1c1f2c268a | ||
|
|
545c76088c | ||
|
|
b418a7fd1e | ||
|
|
2938848df1 | ||
|
|
17172f8920 | ||
|
|
aeb237633e | ||
|
|
d8a29dd639 | ||
|
|
5c139e4b3c | ||
|
|
d69a0a9b06 | ||
|
|
07ce9c83ac | ||
|
|
1d3559f0db | ||
|
|
be92a1da97 | ||
|
|
97b430a888 | ||
|
|
b696e0d790 | ||
|
|
5b08a8605e | ||
|
|
ab358dd820 | ||
|
|
573b3227c7 | ||
|
|
50785c2434 | ||
|
|
fb9705809d | ||
|
|
1aeac01284 | ||
|
|
0cc35b56e5 | ||
|
|
1423375312 | ||
|
|
640ba16f8b | ||
|
|
d4e17442b2 | ||
|
|
eb4eafd915 | ||
|
|
dc65475723 | ||
|
|
3f33e97f0c | ||
|
|
02508933d7 | ||
|
|
89ebf2b011 | ||
|
|
c747c4baf7 | ||
|
|
a3c848e4e2 | ||
|
|
345d61f04f | ||
|
|
f0585ddb4e | ||
|
|
a6790cace3 | ||
|
|
f244fc93c8 | ||
|
|
846a0baed8 | ||
|
|
26ebb8fa6d | ||
|
|
cb8ea7780f | ||
|
|
5400bd3951 | ||
|
|
03521cd3f6 | ||
|
|
1eb0e37214 | ||
|
|
3e769618ec | ||
|
|
9319d5c0aa | ||
|
|
eb94940df6 | ||
|
|
a4c8c53939 | ||
|
|
567dd0d2c6 | ||
|
|
6f06e0c424 | ||
|
|
8dfb9d08c4 | ||
|
|
25c44816c0 | ||
|
|
1a692b348e | ||
|
|
17ed48e677 | ||
|
|
685033bb02 | ||
|
|
b86c982ba1 | ||
|
|
a0148f36fd | ||
|
|
b88de0cd3b | ||
|
|
3207efd383 | ||
|
|
31dd235b50 | ||
|
|
bb1cdb17f5 | ||
|
|
f35287cb7a | ||
|
|
391f7d3be4 | ||
|
|
76d0583b00 | ||
|
|
7857568c92 | ||
|
|
bfe94bc5d0 | ||
|
|
cf3bcf6d7f | ||
|
|
d2aa775ee8 | ||
|
|
ce800ccd92 | ||
|
|
2c57453608 | ||
|
|
03f729602b | ||
|
|
21f064b1d0 | ||
|
|
b7f6b827e4 | ||
|
|
a33e2ed226 | ||
|
|
ef7806b509 | ||
|
|
e268cda1b5 | ||
|
|
3ae5e3c569 | ||
|
|
304c7519d1 | ||
|
|
a4a8a422a5 | ||
|
|
6700003dd2 | ||
|
|
93f99fcff1 | ||
|
|
a03f54902a | ||
|
|
4112d363dd | ||
|
|
3f1c6dd662 | ||
|
|
89b2da636f | ||
|
|
c010ae3a97 | ||
|
|
f619c8f49f | ||
|
|
a27a1a3fd1 | ||
|
|
f7470ace97 | ||
|
|
cb6b362e20 | ||
|
|
c6200f386a | ||
|
|
60423e4a28 | ||
|
|
f40410816e | ||
|
|
63a2ffc0b1 | ||
|
|
fad428b94b | ||
|
|
8b251ca350 | ||
|
|
05bd5cb964 | ||
|
|
76a6b77065 | ||
|
|
5049086062 | ||
|
|
621f38d856 |
138
CHANGELOG.md
138
CHANGELOG.md
@@ -1,3 +1,141 @@
|
||||
# v0.9.24
|
||||
## 04/15/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added support for chunked downloads of Assets
|
||||
* Added new `onBeforeDownload()` event
|
||||
* Added new `download()` and `getMimeType()` methods to Utils class
|
||||
* Added configuration option for supported page types
|
||||
* Added assets and media timestamp options (off by default)
|
||||
* Added page expires configuration option
|
||||
2. [](#bugfix)
|
||||
* Fixed issue with Nginx/Gzip and `ob_flush()` throwing error
|
||||
* Fixed assets actions on 'direct media' URLs
|
||||
* Fix for 'direct assets` with any parameters
|
||||
|
||||
# v0.9.23
|
||||
## 04/09/2015
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fix for broken GPM `selfupgrade` (Grav 0.9.21 and 0.9.22 will need to manually upgrade to this version)
|
||||
|
||||
# v0.9.22
|
||||
## 04/08/2015
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fix to normalize GRAV_ROOT path for Windows
|
||||
* Fix to normalize Media image paths for Windows
|
||||
* Fix for GPM `selfupgrade` when you are on latest version
|
||||
|
||||
# v0.9.21
|
||||
## 04/07/2015
|
||||
|
||||
1. [](#new)
|
||||
* Major Media functionality enhancements: SVG, Animated GIF, Video support!
|
||||
* Added ability to configure default image quality in system configuration
|
||||
* Added `sizes` attributes for custom retina image breakpoints
|
||||
2. [](#improved)
|
||||
* Don't scale @1x retina images
|
||||
* Add filter to Iterator class
|
||||
* Updated various composer packages
|
||||
* Various PSR fixes
|
||||
|
||||
# v0.9.20
|
||||
## 03/24/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added `addAsyncJs()` and `addDeferJs()` to Assets manager
|
||||
* Added support for extranal URL redirects
|
||||
2. [](#improved)
|
||||
* Fix unpredictable asset ordering when set from plugin/system
|
||||
* Updated `nginx.conf` to ensure system assets are accessible
|
||||
* Ensure images are served as static files in Nginx
|
||||
* Updated vendor libraries to latest versions
|
||||
* Updated included composer.phar to latest version
|
||||
3. [](#bugfix)
|
||||
* Fixed issue with markdown links to `#` breaking HTML
|
||||
|
||||
# v0.9.19
|
||||
## 02/28/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added named assets capability and bundled jQuery into Grav core
|
||||
* Added `first()` and `last()` to `Iterator` class
|
||||
2. [](#improved)
|
||||
* Improved page modification routine to skip _dot files_
|
||||
* Only use files to calculate page modification dates
|
||||
* Broke out Folder iterators into their own classes
|
||||
* Various Sensiolabs Insight fixes
|
||||
3. [](#bugfix)
|
||||
* Fixed `Iterator.nth()` method
|
||||
|
||||
# v0.9.18
|
||||
## 02/19/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added ability for GPM `install` to automatically install `_demo` content if found (w/backup)
|
||||
* Added ability for themes and plugins to have dependencies required to install via GPM
|
||||
* Added ability to override the system timezone rather than relying on server setting only
|
||||
* Added new Twig filter `random_string` for generating random id values
|
||||
* Added new Twig filter `markdown` for on-the-fly markdown processing
|
||||
* Added new Twig filter `absoluteUrl` to convert relative to absolute URLs
|
||||
* Added new `processTemplate()` method to Twig object for on-the-fly processing of twig template
|
||||
* Added `rcopy()` and `contains()` helper methods in Utils
|
||||
2. [](#improved)
|
||||
* Provided new `param_sep` variable to better support Apache on Windows
|
||||
* Moved parsedown configuration into the trait
|
||||
* Added optional **deep-copy** option to `mergeConfig()` for plugins
|
||||
* Updated bundled `composer.phar` package
|
||||
* Various Sensiolabs Insight fixes - Silver level now!
|
||||
* Various PSR Fixes
|
||||
3. [](#bugfix)
|
||||
* Fix for windows platforms not displaying installed themes/plugins via GPM
|
||||
* Fix page IDs not picking up folder-only pages
|
||||
|
||||
# v0.9.17
|
||||
## 02/05/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added **full HHVM support!** Get your speed on with Facebook's crazy fast PHP JIT compiler
|
||||
2. [](#improved)
|
||||
* More flexible page summary control
|
||||
* Support **CamelCase** plugin and theme class names. Replaces dashes and underscores
|
||||
* Moved summary delimiter into `site.yaml` so it can be configurable
|
||||
* Various PSR fixes
|
||||
3. [](#bugfix)
|
||||
* Fix for `mergeConfig()` not falling back to defaults
|
||||
* Fix for `addInlineCss()` and `addInlineJs()` Assets not working between Twig tags
|
||||
* Fix for Markdown adding HTML tags into inline CSS and JS
|
||||
|
||||
# v0.9.16
|
||||
## 01/30/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added **Retina** and **Responsive** image support via Grav media and `srcset` image attribute
|
||||
* Added image debug option that overlays responsive resolution
|
||||
* Added a new image cache stream
|
||||
2. [](#improved)
|
||||
* Improved the markdown Lightbox functionality to better mimic Twig version
|
||||
* Fullsize Lightbox can now have filters applied
|
||||
* Added a new `mergeConfig()` method to Plugin class to merge system + page header configuration
|
||||
* Added a new `disable()` method to Plugin class to programatically disable a plugin
|
||||
* Updated Parsedown and Parsedown Extra to address bugs
|
||||
* Various PSR fixes
|
||||
3. [](#bugfix)
|
||||
* Fix bug with image dispatch in traditionally _non-routable_ pages
|
||||
* Fix for markdown link not working on non-current pages
|
||||
* Fix for markdown images not being found on homepage
|
||||
|
||||
# v0.9.15
|
||||
## 01/23/2015
|
||||
|
||||
3. [](#bugfix)
|
||||
* Typo in video mime types
|
||||
* Fix for old `markdown_extra` system setting not getting picked up
|
||||
* Fix in regex for Markdown links with numeric values in path
|
||||
* Fix for broken image routing mechanism that got broken at some point
|
||||
* Fix for markdown images/links in pages with page slug override
|
||||
|
||||
# v0.9.14
|
||||
## 01/23/2015
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#  Grav
|
||||
[](https://gitter.im/getgrav/grav?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
[](https://insight.sensiolabs.com/projects/cfd20465-d0f8-4a0a-8444-467f5b5f16ad) [](https://gitter.im/getgrav/grav?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Binary file not shown.
@@ -8,15 +8,15 @@
|
||||
"require": {
|
||||
"php": ">=5.4.0",
|
||||
"twig/twig": "~1.16",
|
||||
"erusev/parsedown-extra": "~0.6",
|
||||
"erusev/parsedown-extra": "~0.7",
|
||||
"symfony/yaml": "~2.6",
|
||||
"symfony/console": "~2.6",
|
||||
"symfony/event-dispatcher": "~2.6",
|
||||
"doctrine/cache": "~1.3",
|
||||
"maximebf/debugbar": "dev-master",
|
||||
"filp/whoops": "1.2.*@dev",
|
||||
"monolog/monolog": "~1.1",
|
||||
"gregwar/image": "~2.0",
|
||||
"monolog/monolog": "~1.0",
|
||||
"gregwar/image": "~2.0",
|
||||
"ircmaxell/password-compat": "1.0.*",
|
||||
"mrclay/minify": "dev-master",
|
||||
"donatj/phpuseragentparser": "dev-master",
|
||||
|
||||
13
index.php
13
index.php
@@ -2,12 +2,13 @@
|
||||
namespace Grav;
|
||||
|
||||
if (version_compare($ver = PHP_VERSION, $req = '5.4.0', '<')) {
|
||||
exit(sprintf('You are running PHP %s, but Grav needs at least <strong>PHP %s</strong> to run.', $ver, $req));
|
||||
throw new \RuntimeException(sprintf('You are running PHP %s, but Grav needs at least <strong>PHP %s</strong> to run.', $ver, $req));
|
||||
}
|
||||
|
||||
// Ensure vendor libraries exist
|
||||
$autoload = __DIR__ . '/vendor/autoload.php';
|
||||
if (!is_file($autoload)) {
|
||||
exit('Please run: <i>bin/grav install</i>');
|
||||
throw new \RuntimeException("Please run: <i>bin/grav install</i>");
|
||||
}
|
||||
|
||||
use Grav\Common\Grav;
|
||||
@@ -15,19 +16,19 @@ use Grav\Common\Grav;
|
||||
// Register the auto-loader.
|
||||
$loader = require_once $autoload;
|
||||
|
||||
if (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('UTC');
|
||||
}
|
||||
// Set timezone to default, falls back to system if php.ini not set
|
||||
date_default_timezone_set(@date_default_timezone_get());
|
||||
|
||||
// Get the Grav instance
|
||||
$grav = Grav::instance(
|
||||
array(
|
||||
'loader' => $loader
|
||||
)
|
||||
);
|
||||
|
||||
// Process the page
|
||||
try {
|
||||
$grav->process();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$grav->fireEvent('onFatalException');
|
||||
throw $e;
|
||||
|
||||
37
nginx.conf
37
nginx.conf
@@ -25,28 +25,32 @@ http {
|
||||
index index.php;
|
||||
if (!-e $request_filename){ rewrite ^(.*)$ /index.php last; }
|
||||
}
|
||||
|
||||
location /images/ {
|
||||
# Serve images as static
|
||||
}
|
||||
|
||||
location /user {
|
||||
rewrite ^/user/accounts/(.*)$ /error redirect;
|
||||
rewrite ^/user/config/(.*)$ /error redirect;
|
||||
rewrite ^/user/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
|
||||
}
|
||||
rewrite ^/user/accounts/(.*)$ /error redirect;
|
||||
rewrite ^/user/config/(.*)$ /error redirect;
|
||||
rewrite ^/user/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
|
||||
}
|
||||
|
||||
location /cache {
|
||||
rewrite ^/cache/(.*) /error redirect;
|
||||
}
|
||||
location /cache {
|
||||
rewrite ^/cache/(.*) /error redirect;
|
||||
}
|
||||
|
||||
location /bin {
|
||||
rewrite ^/bin/(.*)$ /error redirect;
|
||||
}
|
||||
location /bin {
|
||||
rewrite ^/bin/(.*)$ /error redirect;
|
||||
}
|
||||
|
||||
location /system {
|
||||
rewrite ^/system/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
|
||||
}
|
||||
location /system {
|
||||
rewrite ^/system/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
|
||||
}
|
||||
|
||||
location /vendor {
|
||||
rewrite ^/vendor/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
|
||||
}
|
||||
location /vendor {
|
||||
rewrite ^/vendor/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
|
||||
}
|
||||
|
||||
# Remember to change 127.0.0.1:9000 to the Ip/port
|
||||
# you configured php-cgi.exe to run from
|
||||
@@ -60,7 +64,6 @@ http {
|
||||
include fastcgi_params;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
4
system/assets/jquery/jquery-2.1.3.min.js
vendored
Normal file
4
system/assets/jquery/jquery-2.1.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
system/assets/responsive-overlays/1x.png
Normal file
BIN
system/assets/responsive-overlays/1x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
BIN
system/assets/responsive-overlays/2x.png
Normal file
BIN
system/assets/responsive-overlays/2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
BIN
system/assets/responsive-overlays/3x.png
Normal file
BIN
system/assets/responsive-overlays/3x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
system/assets/responsive-overlays/4x.png
Normal file
BIN
system/assets/responsive-overlays/4x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
system/assets/responsive-overlays/unknown.png
Normal file
BIN
system/assets/responsive-overlays/unknown.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
@@ -20,10 +20,15 @@ png:
|
||||
thumb: media/thumb-png.png
|
||||
mime: image/png
|
||||
gif:
|
||||
type: image
|
||||
type: animated
|
||||
thumb: media/thumb-gif.png
|
||||
mime: image/gif
|
||||
|
||||
svg:
|
||||
type: vector
|
||||
thumb: media/thumb-gif.png
|
||||
mime: image/svg+xml
|
||||
|
||||
mp4:
|
||||
type: video
|
||||
thumb: media/thumb-mp4.png
|
||||
@@ -52,19 +57,19 @@ mp3:
|
||||
ogg:
|
||||
type: audio
|
||||
thumb: media/thumb-ogg.png
|
||||
mine: audio/ogg
|
||||
mime: audio/ogg
|
||||
wma:
|
||||
type: audio
|
||||
thumb: media/thumb-wma.png
|
||||
mine: audio/wma
|
||||
mime: audio/wma
|
||||
m4a:
|
||||
type: audio
|
||||
thumb: media/thumb-m4a.png
|
||||
mine: audio/m4a
|
||||
mime: audio/m4a
|
||||
wav:
|
||||
type: audio
|
||||
thumb: media/thumb-wav.png
|
||||
mine: audio/wav
|
||||
mime: audio/wav
|
||||
|
||||
txt:
|
||||
type: file
|
||||
|
||||
@@ -6,9 +6,12 @@ taxonomies: [category,tag] # Arbitrary list of taxonomy types
|
||||
blog:
|
||||
route: '/blog' # Route to blog
|
||||
metadata:
|
||||
description: 'My Grav Site' # Site description
|
||||
description: 'My Grav Site' # Site description
|
||||
summary:
|
||||
enabled: true # enable or disable summary of page
|
||||
format: short # long = summary delimiter will be ignored; short = use the first occurence of delimter or size
|
||||
size: 300 # Maximum length of summary (characters)
|
||||
delimiter: === # The summary delimiter
|
||||
routes:
|
||||
/something/else: '/blog/sample-3' # Alias for /blog/sample-3
|
||||
/another/one/here: '/blog/sample-3' # Another alias for /blog/sample-3
|
||||
|
||||
@@ -8,6 +8,7 @@ schemes:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user://images
|
||||
- system://images
|
||||
|
||||
page:
|
||||
type: ReadOnlyStream
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
absolute_urls: false # Absolute or relative URLs for `base_url`
|
||||
timezone: '' # Valid values: http://php.net/manual/en/timezones.php
|
||||
param_sep: ':' # Parameter separator, use ';' for Apache on windows
|
||||
|
||||
home:
|
||||
alias: '/home' # Default path for home, ie /
|
||||
@@ -28,6 +30,8 @@ pages:
|
||||
special_chars: # List of special characters to automatically convert to entities
|
||||
'>': 'gt'
|
||||
'<': 'lt'
|
||||
types: 'txt|xml|html|json|rss|atom' # Pipe separated list of valid page types
|
||||
expires: 604800 # Page expires time in seconds (default 7 days)
|
||||
|
||||
cache:
|
||||
enabled: true # Set to true to enable caching
|
||||
@@ -38,6 +42,7 @@ cache:
|
||||
lifetime: 604800 # Lifetime of cached data in seconds (0 = infinite)
|
||||
gzip: false # GZip compress the page output
|
||||
|
||||
|
||||
twig:
|
||||
cache: true # Set to true to enable twig caching
|
||||
debug: false # Enable Twig debug
|
||||
@@ -53,6 +58,9 @@ assets: # Configuration for Assets Manager (JS, C
|
||||
css_rewrite: true # Rewrite any CSS relative URLs during pipelining
|
||||
js_pipeline: false # The JS pipeline is the unification of multiple JS resources into one file
|
||||
js_minify: true # Minify the JS during pipelining
|
||||
enable_asset_timestamp: false # Enable asset timetsamps
|
||||
collections:
|
||||
jquery: system://assets/jquery/jquery-2.1.3.min.js
|
||||
|
||||
errors:
|
||||
display: true # Display full backtrace-style error page
|
||||
@@ -63,3 +71,10 @@ debugger:
|
||||
twig: true # Enable debugging of Twig templates
|
||||
shutdown:
|
||||
close_connection: true # Close the connection before calling onShutdown(). false for debugging
|
||||
|
||||
images:
|
||||
default_image_quality: 85 # Default image quality to use when resampling images (85%)
|
||||
debug: false # Show an overlay over images indicating the pixel depth of the image when working with retina for example
|
||||
|
||||
media:
|
||||
enable_media_timestamp: false # Enable media timetsamps
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
// Some standard defines
|
||||
define('GRAV', true);
|
||||
define('GRAV_VERSION', '0.9.14');
|
||||
define('GRAV_VERSION', '0.9.24');
|
||||
define('DS', '/');
|
||||
|
||||
// Directories and Paths
|
||||
if (!defined('GRAV_ROOT')) {
|
||||
define('GRAV_ROOT', getcwd());
|
||||
define('GRAV_ROOT', str_replace(DIRECTORY_SEPARATOR, DS, getcwd()));
|
||||
}
|
||||
define('ROOT_DIR', GRAV_ROOT . '/');
|
||||
define('USER_PATH', 'user/');
|
||||
@@ -40,6 +40,3 @@ define('RAW_CONTENT', 1);
|
||||
define('TWIG_CONTENT', 2);
|
||||
define('TWIG_CONTENT_LIST', 3);
|
||||
define('TWIG_TEMPLATES', 4);
|
||||
|
||||
// Misc Defines
|
||||
define('SUMMARY_DELIMITER', '===');
|
||||
|
||||
@@ -71,6 +71,7 @@ class Assets
|
||||
// Some configuration variables
|
||||
protected $config;
|
||||
protected $base_url;
|
||||
protected $timestamp = '';
|
||||
|
||||
// Default values for pipeline settings
|
||||
protected $css_minify = true;
|
||||
@@ -82,7 +83,6 @@ class Assets
|
||||
protected $css_no_pipeline = array();
|
||||
protected $js_no_pipeline = array();
|
||||
|
||||
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
// Forward config options
|
||||
@@ -120,7 +120,7 @@ class Assets
|
||||
}
|
||||
|
||||
// Set custom pipeline fetch command
|
||||
if (isset($config['fetch_command']) and ($config['fetch_command'] instanceof Closure)) {
|
||||
if (isset($config['fetch_command']) && ($config['fetch_command'] instanceof Closure)) {
|
||||
$this->fetch_command = $config['fetch_command'];
|
||||
}
|
||||
|
||||
@@ -143,17 +143,23 @@ class Assets
|
||||
}
|
||||
|
||||
// Set collections
|
||||
if (isset($config['collections']) and is_array($config['collections'])) {
|
||||
if (isset($config['collections']) && is_array($config['collections'])) {
|
||||
$this->collections = $config['collections'];
|
||||
}
|
||||
|
||||
// Autoload assets
|
||||
if (isset($config['autoload']) and is_array($config['autoload'])) {
|
||||
if (isset($config['autoload']) && is_array($config['autoload'])) {
|
||||
foreach ($config['autoload'] as $asset) {
|
||||
$this->add($asset);
|
||||
}
|
||||
}
|
||||
|
||||
// Set timestamp
|
||||
if (isset($config['enable_asset_timestamp']) && $config['enable_asset_timestamp'] === true) {
|
||||
$this->timestamp = '?' . self::getGrav()['cache']->getKey();
|
||||
}
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -163,12 +169,17 @@ class Assets
|
||||
public function init()
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
$base_url = self::$grav['base_url'];
|
||||
$config = self::getGrav()['config'];
|
||||
$base_url = self::getGrav()['base_url'];
|
||||
$asset_config = (array)$config->get('system.assets');
|
||||
|
||||
$this->config($asset_config);
|
||||
$this->base_url = $base_url . '/';
|
||||
|
||||
// Register any preconfigured collections
|
||||
foreach ($config->get('system.assets.collections') as $name => $collection) {
|
||||
$this->registerCollection($name, (array)$collection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,13 +204,15 @@ class Assets
|
||||
} elseif (isset($this->collections[$asset])) {
|
||||
$this->add($this->collections[$asset], $priority, $pipeline);
|
||||
} else {
|
||||
// Get extension
|
||||
$extension = pathinfo(parse_url($asset, PHP_URL_PATH), PATHINFO_EXTENSION);
|
||||
|
||||
// JavaScript or CSS
|
||||
$info = pathinfo($asset);
|
||||
if (isset($info['extension'])) {
|
||||
$ext = strtolower($info['extension']);
|
||||
if ($ext === 'css') {
|
||||
if (strlen($extension) > 0) {
|
||||
$extension = strtolower($extension);
|
||||
if ($extension === 'css') {
|
||||
$this->addCss($asset, $priority, $pipeline);
|
||||
} elseif ($ext === 'js') {
|
||||
} elseif ($extension === 'js') {
|
||||
$this->addJs($asset, $priority, $pipeline);
|
||||
}
|
||||
}
|
||||
@@ -226,7 +239,9 @@ class Assets
|
||||
foreach ($asset as $a) {
|
||||
$this->addCss($a, $priority, $pipeline);
|
||||
}
|
||||
|
||||
return $this;
|
||||
} elseif (isset($this->collections[$asset])) {
|
||||
$this->add($this->collections[$asset], $priority, $pipeline);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -235,7 +250,7 @@ class Assets
|
||||
}
|
||||
|
||||
$key = md5($asset);
|
||||
if ($asset && !array_key_exists($key, $this->css)) {
|
||||
if ($asset) {
|
||||
$this->css[$key] = [
|
||||
'asset' => $asset,
|
||||
'priority' => $priority,
|
||||
@@ -256,16 +271,19 @@ class Assets
|
||||
* @param mixed $asset
|
||||
* @param int $priority the priority, bigger comes first
|
||||
* @param bool $pipeline false if this should not be pipelined
|
||||
* @param string $loading how the asset is loaded (async/defer)
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addJs($asset, $priority = 10, $pipeline = true)
|
||||
public function addJs($asset, $priority = 10, $pipeline = true, $loading = '')
|
||||
{
|
||||
if (is_array($asset)) {
|
||||
foreach ($asset as $a) {
|
||||
$this->addJs($a, $priority, $pipeline);
|
||||
}
|
||||
|
||||
return $this;
|
||||
} elseif (isset($this->collections[$asset])) {
|
||||
$this->add($this->collections[$asset], $priority, $pipeline);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -274,18 +292,47 @@ class Assets
|
||||
}
|
||||
|
||||
$key = md5($asset);
|
||||
if ($asset && !array_key_exists($key, $this->js)) {
|
||||
if ($asset) {
|
||||
$this->js[$key] = [
|
||||
'asset' => $asset,
|
||||
'priority' => $priority,
|
||||
'order' => count($this->js),
|
||||
'pipeline' => $pipeline
|
||||
'pipeline' => $pipeline,
|
||||
'loading' => $loading
|
||||
];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience wrapper for async loading of JavaScript
|
||||
*
|
||||
* @param $asset
|
||||
* @param int $priority
|
||||
* @param bool $pipeline
|
||||
*
|
||||
* @return \Grav\Common\Assets
|
||||
*/
|
||||
public function addAsyncJs($asset, $priority = 10, $pipeline = true)
|
||||
{
|
||||
return $this->addJs($asset, $priority, $pipeline, 'async');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience wrapper for deferred loading of JavaScript
|
||||
*
|
||||
* @param $asset
|
||||
* @param int $priority
|
||||
* @param bool $pipeline
|
||||
*
|
||||
* @return \Grav\Common\Assets
|
||||
*/
|
||||
public function addDeferJs($asset, $priority = 10, $pipeline = true)
|
||||
{
|
||||
return $this->addJs($asset, $priority, $pipeline, 'defer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an inline CSS asset.
|
||||
*
|
||||
@@ -299,6 +346,9 @@ class Assets
|
||||
*/
|
||||
public function addInlineCss($asset, $priority = 10)
|
||||
{
|
||||
if (is_a($asset, 'Twig_Markup')) {
|
||||
$asset = strip_tags((string)$asset);
|
||||
}
|
||||
$key = md5($asset);
|
||||
if (is_string($asset) && !array_key_exists($key, $this->inline_css)) {
|
||||
$this->inline_css[$key] = [
|
||||
@@ -324,6 +374,9 @@ class Assets
|
||||
*/
|
||||
public function addInlineJs($asset, $priority = 10)
|
||||
{
|
||||
if (is_a($asset, 'Twig_Markup')) {
|
||||
$asset = strip_tags((string)$asset);
|
||||
}
|
||||
$key = md5($asset);
|
||||
if (is_string($asset) && !array_key_exists($key, $this->inline_js)) {
|
||||
$this->inline_js[$key] = [
|
||||
@@ -350,7 +403,7 @@ class Assets
|
||||
}
|
||||
|
||||
// Sort array by priorities (larger priority first)
|
||||
if (self::$grav) {
|
||||
if (self::getGrav()) {
|
||||
usort($this->css, function ($a, $b) {
|
||||
if ($a['priority'] == $b['priority']) {
|
||||
return $b['order'] - $a['order'];
|
||||
@@ -375,11 +428,11 @@ class Assets
|
||||
$output .= '<link href="' . $this->pipeline(CSS_ASSET) . '"' . $attributes . ' />' . "\n";
|
||||
|
||||
foreach ($this->css_no_pipeline as $file) {
|
||||
$output .= '<link href="' . $file['asset'] . '"' . $attributes . ' />' . "\n";
|
||||
$output .= '<link href="' . $file['asset'] . $this->timestamp . '"' . $attributes . ' />' . "\n";
|
||||
}
|
||||
} else {
|
||||
foreach ($this->css as $file) {
|
||||
$output .= '<link href="' . $file['asset'] . '"' . $attributes . ' />' . "\n";
|
||||
$output .= '<link href="' . $file['asset'] . $this->timestamp . '"' . $attributes . ' />' . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,11 +486,11 @@ class Assets
|
||||
if ($this->js_pipeline) {
|
||||
$output .= '<script src="' . $this->pipeline(JS_ASSET) . '"' . $attributes . ' ></script>' . "\n";
|
||||
foreach ($this->js_no_pipeline as $file) {
|
||||
$output .= '<script src="' . $file['asset'] . '"' . $attributes . ' ></script>' . "\n";
|
||||
$output .= '<script src="' . $file['asset'] . $this->timestamp . '"' . $attributes . ' ' . $file['loading']. '></script>' . "\n";
|
||||
}
|
||||
} else {
|
||||
foreach ($this->js as $file) {
|
||||
$output .= '<script src="' . $file['asset'] . '"' . $attributes . ' ></script>' . "\n";
|
||||
$output .= '<script src="' . $file['asset'] . $this->timestamp . '"' . $attributes . ' ' . $file['loading'].'></script>' . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,7 +516,7 @@ class Assets
|
||||
protected function pipeline($css = true)
|
||||
{
|
||||
/** @var Cache $cache */
|
||||
$cache = self::$grav['cache'];
|
||||
$cache = self::getGrav()['cache'];
|
||||
$key = '?' . $cache->getKey();
|
||||
|
||||
if ($css) {
|
||||
@@ -521,17 +574,68 @@ class Assets
|
||||
return $relative_path . $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the array of all the registered CSS assets
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCss()
|
||||
{
|
||||
return $this->css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the array of all the registered JS assets
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJs()
|
||||
{
|
||||
return $this->js;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the array of all the registered collections
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCollections()
|
||||
{
|
||||
return $this->collections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an asset exists as a collection, CSS or JS reference
|
||||
*
|
||||
* @param $asset
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists($asset)
|
||||
{
|
||||
if (isset($this->collections[$asset]) ||
|
||||
isset($this->css[$asset]) ||
|
||||
isset($this->js[$asset])) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add/replace collection.
|
||||
*
|
||||
* @param string $collectionName
|
||||
* @param array $assets
|
||||
* @param bool $overwrite
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function registerCollection($collectionName, Array $assets)
|
||||
public function registerCollection($collectionName, Array $assets, $overwrite = false)
|
||||
{
|
||||
$this->collections[$collectionName] = $assets;
|
||||
if ($overwrite || !isset($this->collections[$collectionName])) {
|
||||
$this->collections[$collectionName] = $assets;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -570,26 +674,6 @@ class Assets
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all CSS assets already added.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCss()
|
||||
{
|
||||
return $this->css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all JavaScript assets already added.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJs()
|
||||
{
|
||||
return $this->js;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all CSS assets within $directory (relative to public dir).
|
||||
*
|
||||
@@ -643,9 +727,9 @@ class Assets
|
||||
$info = pathinfo($asset);
|
||||
if (isset($info['extension'])) {
|
||||
$ext = strtolower($info['extension']);
|
||||
if ($ext === 'css' and !in_array($asset, $this->css)) {
|
||||
if ($ext === 'css' && !in_array($asset, $this->css)) {
|
||||
$this->css[] = $asset;
|
||||
} elseif ($ext === 'js' and !in_array($asset, $this->js)) {
|
||||
} elseif ($ext === 'js' && !in_array($asset, $this->js)) {
|
||||
$this->js[] = $asset;
|
||||
}
|
||||
}
|
||||
@@ -665,8 +749,8 @@ class Assets
|
||||
*/
|
||||
protected function isRemoteLink($link)
|
||||
{
|
||||
return ('http://' === substr($link, 0, 7) or 'https://' === substr($link, 0, 8)
|
||||
or '//' === substr($link, 0, 2));
|
||||
return ('http://' === substr($link, 0, 7) || 'https://' === substr($link, 0, 8)
|
||||
|| '//' === substr($link, 0, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -679,7 +763,7 @@ class Assets
|
||||
protected function buildLocalLink($asset)
|
||||
{
|
||||
try {
|
||||
$asset = self::$grav['locator']->findResource($asset, false);
|
||||
$asset = self::getGrav()['locator']->findResource($asset, false);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
|
||||
@@ -188,8 +188,7 @@ class Cache extends Getters
|
||||
public function save($id, $data, $lifetime = null)
|
||||
{
|
||||
if ($this->enabled) {
|
||||
|
||||
if ($lifetime == null) {
|
||||
if ($lifetime === null) {
|
||||
$lifetime = $this->getLifetime();
|
||||
}
|
||||
$this->driver->save($id, $data, $lifetime);
|
||||
|
||||
@@ -3,7 +3,6 @@ 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;
|
||||
|
||||
@@ -3,10 +3,7 @@ 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;
|
||||
@@ -21,6 +18,12 @@ class Config extends Data
|
||||
{
|
||||
protected $grav;
|
||||
protected $streams = [
|
||||
'system' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['system'],
|
||||
]
|
||||
],
|
||||
'user' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
@@ -60,7 +63,8 @@ class Config extends Data
|
||||
'cache' => [
|
||||
'type' => 'Stream',
|
||||
'prefixes' => [
|
||||
'' => ['cache']
|
||||
'' => ['cache'],
|
||||
'images' => ['images']
|
||||
]
|
||||
],
|
||||
'log' => [
|
||||
|
||||
@@ -11,7 +11,7 @@ use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
*/
|
||||
class Blueprint
|
||||
{
|
||||
use Export;
|
||||
use Export, DataMutatorTrait;
|
||||
|
||||
public $name;
|
||||
|
||||
@@ -46,68 +46,6 @@ class Blueprint
|
||||
$this->filter = array_flip($filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
* @example $value = $data->get('this.is.my.nested.variable');
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $default Default value (or null).
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
*
|
||||
* @return mixed Value.
|
||||
*/
|
||||
public function get($name, $default = null, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = $this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current) && isset($current->{$field})) {
|
||||
$current = $current->{$field};
|
||||
} elseif (is_array($current) && isset($current[$field])) {
|
||||
$current = $current[$field];
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sey value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
* @example $value = $data->set('this.is.my.nested.variable', true);
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $value New value.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
*/
|
||||
public function set($name, $value, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = &$this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current)) {
|
||||
// Handle objects.
|
||||
if (!isset($current->{$field})) {
|
||||
$current->{$field} = array();
|
||||
}
|
||||
$current = &$current->{$field};
|
||||
} else {
|
||||
// Handle arrays and scalars.
|
||||
if (!is_array($current)) {
|
||||
$current = array($field => array());
|
||||
} elseif (!isset($current[$field])) {
|
||||
$current[$field] = array();
|
||||
}
|
||||
$current = &$current[$field];
|
||||
}
|
||||
}
|
||||
|
||||
$current = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all form fields.
|
||||
*
|
||||
@@ -146,10 +84,9 @@ class Blueprint
|
||||
*
|
||||
* @param array $data1
|
||||
* @param array $data2
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
public function mergeData(array $data1, array $data2, $name = null)
|
||||
public function mergeData(array $data1, array $data2)
|
||||
{
|
||||
// Initialize data
|
||||
$this->fields();
|
||||
@@ -213,7 +150,7 @@ class Blueprint
|
||||
$bref = array_merge($bref, array($key => $head[$key]));
|
||||
}
|
||||
}
|
||||
} while(count($head_stack));
|
||||
} while (count($head_stack));
|
||||
|
||||
$this->items = $blueprints;
|
||||
}
|
||||
@@ -478,14 +415,15 @@ class Blueprint
|
||||
* @throws \RuntimeException
|
||||
* @internal
|
||||
*/
|
||||
protected function checkRequired(array $data, array $fields) {
|
||||
protected function checkRequired(array $data, array $fields)
|
||||
{
|
||||
foreach ($fields as $name => $field) {
|
||||
if (!is_string($field)) {
|
||||
continue;
|
||||
}
|
||||
$field = $this->rules[$field];
|
||||
if (isset($field['validate']['required'])
|
||||
&& $field['validate']['required'] == true
|
||||
&& $field['validate']['required'] === true
|
||||
&& empty($data[$name])) {
|
||||
throw new \RuntimeException("Missing required field: {$field['name']}");
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ class Blueprints
|
||||
$file = CompiledYamlFile::instance($filename);
|
||||
$blueprints = $file->content();
|
||||
} else {
|
||||
// throw new \RuntimeException("Blueprints for '{$type}' cannot be found! {$this->search}{$type}");
|
||||
$blueprints = [];
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ use RocketTheme\Toolbox\File\FileInterface;
|
||||
*/
|
||||
class Data implements DataInterface
|
||||
{
|
||||
use ArrayAccessWithGetters, Countable, Export;
|
||||
use ArrayAccessWithGetters, Countable, Export, DataMutatorTrait;
|
||||
|
||||
protected $gettersVariable = 'items';
|
||||
protected $items;
|
||||
@@ -56,67 +56,6 @@ class Data implements DataInterface
|
||||
return $this->get($name, $default, $separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
* @example $value = $data->get('this.is.my.nested.variable');
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $default Default value (or null).
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return mixed Value.
|
||||
*/
|
||||
public function get($name, $default = null, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = $this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current) && isset($current->{$field})) {
|
||||
$current = $current->{$field};
|
||||
} elseif (is_array($current) && isset($current[$field])) {
|
||||
$current = $current[$field];
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sey value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
* @example $value = $data->set('this.is.my.nested.variable', true);
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $value New value.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
*/
|
||||
public function set($name, $value, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = &$this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current)) {
|
||||
// Handle objects.
|
||||
if (!isset($current->{$field})) {
|
||||
$current->{$field} = array();
|
||||
}
|
||||
$current = &$current->{$field};
|
||||
} else {
|
||||
// Handle arrays and scalars.
|
||||
if (!is_array($current)) {
|
||||
$current = array($field => array());
|
||||
} elseif (!isset($current[$field])) {
|
||||
$current[$field] = array();
|
||||
}
|
||||
$current = &$current[$field];
|
||||
}
|
||||
}
|
||||
|
||||
$current = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
|
||||
68
system/src/Grav/Common/Data/DataMutatorTrait.php
Normal file
68
system/src/Grav/Common/Data/DataMutatorTrait.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
trait DataMutatorTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* Get value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
* @example $value = $data->get('this.is.my.nested.variable');
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $default Default value (or null).
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return mixed Value.
|
||||
*/
|
||||
public function get($name, $default = null, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = $this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current) && isset($current->{$field})) {
|
||||
$current = $current->{$field};
|
||||
} elseif (is_array($current) && isset($current[$field])) {
|
||||
$current = $current[$field];
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sey value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
* @example $value = $data->set('this.is.my.nested.variable', true);
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $value New value.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
*/
|
||||
public function set($name, $value, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = &$this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current)) {
|
||||
// Handle objects.
|
||||
if (!isset($current->{$field})) {
|
||||
$current->{$field} = array();
|
||||
}
|
||||
$current = &$current->{$field};
|
||||
} else {
|
||||
// Handle arrays and scalars.
|
||||
if (!is_array($current)) {
|
||||
$current = array($field => array());
|
||||
} elseif (!isset($current[$field])) {
|
||||
$current[$field] = array();
|
||||
}
|
||||
$current = &$current[$field];
|
||||
}
|
||||
}
|
||||
|
||||
$current = $value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -375,7 +375,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeDatetime_local($value, array $params, array $field)
|
||||
public static function typeDatetimeLocal($value, array $params, array $field)
|
||||
{
|
||||
return self::typeDatetime($value, $params, $field);
|
||||
}
|
||||
@@ -517,7 +517,7 @@ class Validation
|
||||
|
||||
public static function validateRequired($value, $params)
|
||||
{
|
||||
return (bool) $params != true || !empty($value);
|
||||
return (bool) $params !== true || !empty($value);
|
||||
}
|
||||
|
||||
public static function validatePattern($value, $params)
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use DebugBar\Bridge\Twig\TraceableTwigEnvironment;
|
||||
use DebugBar\JavascriptRenderer;
|
||||
use DebugBar\StandardDebugBar;
|
||||
//use \Tracy\Debugger as TracyDebugger;
|
||||
|
||||
/**
|
||||
* Class Debugger
|
||||
@@ -48,9 +46,11 @@ class Debugger
|
||||
public function addAssets()
|
||||
{
|
||||
if ($this->enabled()) {
|
||||
|
||||
$assets = $this->grav['assets'];
|
||||
|
||||
// Add jquery library
|
||||
$assets->add('jquery', 101);
|
||||
|
||||
$this->renderer = $this->debugbar->getJavascriptRenderer();
|
||||
$this->renderer->setIncludeVendors(false);
|
||||
|
||||
|
||||
@@ -4,9 +4,6 @@ namespace Grav\Common\Errors;
|
||||
use Grav\Common\Grav;
|
||||
use Whoops\Handler\CallbackHandler;
|
||||
use Whoops\Handler\HandlerInterface;
|
||||
use Whoops\Handler\JsonResponseHandler;
|
||||
use Whoops\Handler\PrettyPageHandler;
|
||||
use Whoops\Handler\PlainTextHandler;
|
||||
use Whoops\Run;
|
||||
|
||||
/**
|
||||
@@ -23,7 +20,7 @@ class Errors extends \Whoops\Run
|
||||
}
|
||||
|
||||
if (!$handler instanceof HandlerInterface) {
|
||||
throw new InvalidArgumentException(
|
||||
throw new \InvalidArgumentException(
|
||||
"Argument to " . __METHOD__ . " must be a callable, or instance of"
|
||||
. "Whoops\\Handler\\HandlerInterface"
|
||||
);
|
||||
|
||||
@@ -21,9 +21,7 @@ class SimplePageHandler extends Handler
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$exception = $this->getException();
|
||||
$inspector = $this->getInspector();
|
||||
$run = $this->getRun();
|
||||
|
||||
$helper = new TemplateHelper();
|
||||
$templateFile = $this->getResource("layout.html.php");
|
||||
@@ -46,6 +44,11 @@ class SimplePageHandler extends Handler
|
||||
return Handler::QUIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $resource
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getResource($resource)
|
||||
{
|
||||
// If the resource was found before, we can speed things up
|
||||
@@ -67,7 +70,7 @@ class SimplePageHandler extends Handler
|
||||
}
|
||||
|
||||
// If we got this far, nothing was found.
|
||||
throw new RuntimeException(
|
||||
throw new \RuntimeException(
|
||||
"Could not find resource '$resource' in any resource paths."
|
||||
. "(searched: " . join(", ", $this->searchPaths). ")"
|
||||
);
|
||||
@@ -76,7 +79,7 @@ class SimplePageHandler extends Handler
|
||||
public function addResourcePath($path)
|
||||
{
|
||||
if (!is_dir($path)) {
|
||||
throw new InvalidArgumentException(
|
||||
throw new \InvalidArgumentException(
|
||||
"'$path' is not a valid directory"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,12 +19,13 @@ abstract class Folder
|
||||
{
|
||||
$last_modified = 0;
|
||||
|
||||
$directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
$iterator = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
$dirItr = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
$filterItr = new RecursiveFolderFilterIterator($dirItr);
|
||||
$itr = new \RecursiveIteratorIterator($filterItr, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
|
||||
/** @var \RecursiveDirectoryIterator $file */
|
||||
foreach ($iterator as $file) {
|
||||
$dir_modified = $file->getMTime();
|
||||
foreach ($itr as $dir) {
|
||||
$dir_modified = $dir->getMTime();
|
||||
if ($dir_modified > $last_modified) {
|
||||
$last_modified = $dir_modified;
|
||||
}
|
||||
@@ -33,6 +34,34 @@ abstract class Folder
|
||||
return $last_modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively find the last modified time under given path by file.
|
||||
*
|
||||
* @param string $path
|
||||
* @return int
|
||||
*/
|
||||
public static function lastModifiedFile($path)
|
||||
{
|
||||
$last_modified = 0;
|
||||
|
||||
$dirItr = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
$filterItr = new RecursiveFileFilterIterator($dirItr);
|
||||
$itr = new \RecursiveIteratorIterator($filterItr, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
|
||||
/** @var \RecursiveDirectoryIterator $file */
|
||||
foreach ($itr as $file) {
|
||||
if ($file->isDir()) {
|
||||
continue;
|
||||
}
|
||||
$file_modified = $file->getMTime();
|
||||
if ($file_modified > $last_modified) {
|
||||
$last_modified = $file_modified;
|
||||
}
|
||||
}
|
||||
|
||||
return $last_modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get relative path between target and base path. If path isn't relative, return full path.
|
||||
*
|
||||
@@ -43,8 +72,8 @@ abstract class Folder
|
||||
public static function getRelativePath($path, $base = GRAV_ROOT)
|
||||
{
|
||||
if ($base) {
|
||||
$base = preg_replace('![\\|/]+!', '/', $base);
|
||||
$path = preg_replace('![\\|/]+!', '/', $path);
|
||||
$base = preg_replace('![\\\/]+!', '/', $base);
|
||||
$path = preg_replace('![\\\/]+!', '/', $path);
|
||||
if (strpos($path, $base) === 0) {
|
||||
$path = ltrim(substr($path, strlen($base)), '/');
|
||||
}
|
||||
@@ -68,30 +97,7 @@ abstract class Folder
|
||||
return $result ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively find the last modified time under given path by file.
|
||||
*
|
||||
* @param string $path
|
||||
* @return int
|
||||
*/
|
||||
public static function lastModifiedFile($path)
|
||||
{
|
||||
$last_modified = 0;
|
||||
|
||||
$dirItr = new \RecursiveDirectoryIterator($path);
|
||||
$filterItr = new GravRecursiveFilterIterator($dirItr);
|
||||
$itr = new \RecursiveIteratorIterator($filterItr, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
|
||||
/** @var \RecursiveDirectoryIterator $file */
|
||||
foreach ($itr as $file) {
|
||||
$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.
|
||||
@@ -295,20 +301,3 @@ abstract class Folder
|
||||
return @rmdir($folder);
|
||||
}
|
||||
}
|
||||
|
||||
class GravRecursiveFilterIterator extends \RecursiveFilterIterator
|
||||
{
|
||||
public static $FILTERS = array(
|
||||
'..', '.DS_Store'
|
||||
);
|
||||
|
||||
public function accept()
|
||||
{
|
||||
return !in_array(
|
||||
$this->current()->getFilename(),
|
||||
self::$FILTERS,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
namespace Grav\Common\Filesystem;
|
||||
|
||||
class RecursiveFileFilterIterator extends \RecursiveFilterIterator
|
||||
{
|
||||
public static $FILTERS = ['.DS_Store'];
|
||||
|
||||
public function accept()
|
||||
{
|
||||
// Ensure any filtered file names are skipped
|
||||
return !in_array($this->current()->getFilename(), self::$FILTERS, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
namespace Grav\Common\Filesystem;
|
||||
|
||||
class RecursiveFolderFilterIterator extends \RecursiveFilterIterator
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
// only accept directories
|
||||
return $this->current()->isDir();
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Local;
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Iterator;
|
||||
|
||||
class Collection extends Iterator
|
||||
{
|
||||
abstract class AbstractCollection extends Iterator {
|
||||
|
||||
use GravTrait;
|
||||
|
||||
public function toJson()
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $theme) {
|
||||
$items[$name] = $theme->toArray();
|
||||
foreach ($this->items as $name => $package) {
|
||||
$items[$name] = $package->toArray();
|
||||
}
|
||||
|
||||
return json_encode($items);
|
||||
@@ -23,8 +23,8 @@ class Collection extends Iterator
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $theme) {
|
||||
$items[$name] = $theme->toArray();
|
||||
foreach ($this->items as $name => $package) {
|
||||
$items[$name] = $package->toArray();
|
||||
}
|
||||
|
||||
return $items;
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Common;
|
||||
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Iterator;
|
||||
|
||||
abstract class AbstractPackageCollection extends Iterator {
|
||||
|
||||
use GravTrait;
|
||||
|
||||
protected $type;
|
||||
|
||||
public function toJson()
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $package) {
|
||||
$items[$name] = $package->toArray();
|
||||
}
|
||||
|
||||
return json_encode($items);
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $package) {
|
||||
$items[$name] = $package->toArray();
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
21
system/src/Grav/Common/GPM/Common/CachedCollection.php
Normal file
21
system/src/Grav/Common/GPM/Common/CachedCollection.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Common;
|
||||
|
||||
use Grav\Common\Iterator;
|
||||
|
||||
class CachedCollection extends Iterator {
|
||||
|
||||
protected static $cache;
|
||||
|
||||
public function __construct($items)
|
||||
{
|
||||
// local cache to speed things up
|
||||
if (!isset(self::$cache[get_called_class().__METHOD__])) {
|
||||
self::$cache[get_called_class().__METHOD__] = $items;
|
||||
}
|
||||
|
||||
foreach (self::$cache[get_called_class().__METHOD__] as $name => $item) {
|
||||
$this->append([$name => $item]);
|
||||
}
|
||||
}
|
||||
}
|
||||
42
system/src/Grav/Common/GPM/Common/Package.php
Normal file
42
system/src/Grav/Common/GPM/Common/Package.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Common;
|
||||
|
||||
use Grav\Common\Data\Data;
|
||||
|
||||
class Package {
|
||||
|
||||
protected $data;
|
||||
|
||||
public function __construct(Data $package, $type = null) {
|
||||
$this->data = $package;
|
||||
|
||||
if ($type) {
|
||||
$this->data->set('package_type', $type);
|
||||
}
|
||||
}
|
||||
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function __get($key) {
|
||||
return $this->data->get($key);
|
||||
}
|
||||
|
||||
public function __isset($key) {
|
||||
return isset($this->data->$key);
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
return $this->toJson();
|
||||
}
|
||||
|
||||
public function toJson() {
|
||||
return $this->data->toJson();
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
return $this->data->toArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\Inflector;
|
||||
use Grav\Common\Iterator;
|
||||
use Grav\Common\Utils;
|
||||
|
||||
class GPM extends Iterator
|
||||
{
|
||||
@@ -28,6 +30,8 @@ class GPM extends Iterator
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
protected $install_paths = ['plugins' => 'user/plugins/%name%', 'themes' => 'user/themes/%name%', 'skeletons' => 'user/'];
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -347,7 +351,20 @@ class GPM extends Iterator
|
||||
$packages = ['total' => 0, 'not_found' => []];
|
||||
|
||||
foreach ($searches as $search) {
|
||||
$repository = '';
|
||||
// if this is an object, get the search data from the key
|
||||
if (is_object($search)) {
|
||||
$search = (array) $search;
|
||||
$key = key($search);
|
||||
$repository = $search[$key];
|
||||
$search = $key;
|
||||
}
|
||||
|
||||
if ($found = $this->findPackage($search)) {
|
||||
// set override respository if provided
|
||||
if ($repository) {
|
||||
$found->override_repository = $repository;
|
||||
}
|
||||
if (!isset($packages[$found->package_type])) {
|
||||
$packages[$found->package_type] = [];
|
||||
}
|
||||
@@ -355,7 +372,20 @@ class GPM extends Iterator
|
||||
$packages[$found->package_type][$found->slug] = $found;
|
||||
$packages['total']++;
|
||||
} else {
|
||||
$packages['not_found'][] = $search;
|
||||
// make a best guess at the type based on the repo URL
|
||||
if (Utils::contains($repository, '-theme')) {
|
||||
$type = 'themes';
|
||||
} else {
|
||||
$type = 'plugins';
|
||||
}
|
||||
|
||||
$not_found = new \stdClass();
|
||||
$not_found->name = Inflector::camelize($search);
|
||||
$not_found->slug = $search;
|
||||
$not_found->package_type = $type;
|
||||
$not_found->install_path = str_replace('%name%', $search, $this->install_paths[$type]);
|
||||
$not_found->override_repository = $repository;
|
||||
$packages['not_found'][$search] = $not_found;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,15 +61,12 @@ class Installer
|
||||
$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'])
|
||||
) {
|
||||
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']
|
||||
) {
|
||||
if (self::lastErrorCode() == self::IS_LINK && $options['ignore_symlinks'] ||
|
||||
self::lastErrorCode() == self::EXISTS && !$options['overwrite']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -112,7 +109,7 @@ class Installer
|
||||
|
||||
}
|
||||
|
||||
public static function nonSophisticatedInstall($zip, $install_path, $tmp)
|
||||
public static function nonSophisticatedInstall(\ZipArchive $zip, $install_path, $tmp)
|
||||
{
|
||||
$container = $zip->getNameIndex(0); // TODO: better way of determining if zip has container folder
|
||||
if (file_exists($install_path)) {
|
||||
@@ -124,7 +121,7 @@ class Installer
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function sophisticatedInstall($zip, $install_path, $tmp)
|
||||
public static function sophisticatedInstall(\ZipArchive $zip, $install_path, $tmp)
|
||||
{
|
||||
for ($i = 0, $l = $zip->numFiles; $i < $l; $i++) {
|
||||
$filename = $zip->getNameIndex($i);
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
use Grav\Common\GPM\Common\AbstractPackageCollection as BaseCollection;
|
||||
use Grav\Common\GPM\Local\Package;
|
||||
|
||||
abstract class AbstractPackageCollection extends BaseCollection {
|
||||
|
||||
public function __construct($items)
|
||||
{
|
||||
foreach ($items as $name => $data) {
|
||||
$this->items[$name] = new Package($data, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,39 +2,24 @@
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\GPM\Common\Package as BasePackage;
|
||||
|
||||
/**
|
||||
* Class Package
|
||||
* @package Grav\Common\GPM\Local
|
||||
*/
|
||||
class Package
|
||||
class Package extends BasePackage
|
||||
{
|
||||
/**
|
||||
* @var Data
|
||||
*/
|
||||
protected $data;
|
||||
/**
|
||||
* @var \Grav\Common\Data\Blueprint
|
||||
*/
|
||||
protected $blueprints;
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @param Data $package
|
||||
* @param bool $package_type
|
||||
*/
|
||||
public function __construct(Data $package, $package_type = false)
|
||||
public function __construct(Data $package, $package_type = null)
|
||||
{
|
||||
$this->data = $package;
|
||||
$this->blueprints = $this->data->blueprints();
|
||||
$data = new Data($package->blueprints()->toArray());
|
||||
parent::__construct($data, $package_type);
|
||||
|
||||
if ($package_type) {
|
||||
$html_description = \Parsedown::instance()->line($this->blueprints->get('description'));
|
||||
$this->blueprints->set('package_type', $package_type);
|
||||
$this->blueprints->set('slug', $this->blueprints->name);
|
||||
$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));
|
||||
}
|
||||
$this->settings = $package->toArray();
|
||||
|
||||
$html_description = \Parsedown::instance()->line($this->description);
|
||||
$this->data->set('slug', $this->name);
|
||||
$this->data->set('description_html', $html_description);
|
||||
$this->data->set('description_plain', strip_tags($html_description));
|
||||
$this->data->set('symlink', is_link(USER_DIR . $package_type . DS . $this->name));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,47 +27,6 @@ class Package
|
||||
*/
|
||||
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();
|
||||
return $this->settings['enabled'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,17 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
use Grav\Common\Iterator;
|
||||
use Grav\Common\GPM\Common\CachedCollection;
|
||||
|
||||
class Packages extends Iterator
|
||||
class Packages extends CachedCollection
|
||||
{
|
||||
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()
|
||||
];
|
||||
}
|
||||
$items = [
|
||||
'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]);
|
||||
parent::__construct($items);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,22 +5,18 @@ namespace Grav\Common\GPM\Local;
|
||||
* Class Plugins
|
||||
* @package Grav\Common\GPM\Local
|
||||
*/
|
||||
class Plugins extends Collection
|
||||
class Plugins extends AbstractPackageCollection
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $type = 'plugins';
|
||||
protected $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);
|
||||
}
|
||||
parent::__construct(self::getGrav()['plugins']->all());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
class Themes extends Collection
|
||||
/**
|
||||
* Class Themes
|
||||
* @package Grav\Common\GPM\Local
|
||||
*/
|
||||
class Themes extends AbstractPackageCollection
|
||||
{
|
||||
private $type = 'themes';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'themes';
|
||||
|
||||
/**
|
||||
* Local Themes Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$grav = self::$grav;
|
||||
|
||||
foreach ($grav['themes']->all() as $name => $data) {
|
||||
$this->items[$name] = new Package($data, $this->type);
|
||||
}
|
||||
parent::__construct(self::getGrav()['themes']->all());
|
||||
}
|
||||
}
|
||||
|
||||
58
system/src/Grav/Common/GPM/PackageInterface.php
Normal file
58
system/src/Grav/Common/GPM/PackageInterface.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\Data\Data;
|
||||
|
||||
/**
|
||||
* Interface Package
|
||||
* @package Grav\Common\GPM
|
||||
*/
|
||||
class Package
|
||||
{
|
||||
/**
|
||||
* @var Data
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @var \Grav\Common\Data\Blueprint
|
||||
*/
|
||||
protected $blueprints;
|
||||
|
||||
/**
|
||||
* @param Data $package
|
||||
* @param bool $package_type
|
||||
*/
|
||||
public function __construct(Data $package, $package_type = false);
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function isEnabled();
|
||||
|
||||
/**
|
||||
* @return Data
|
||||
*/
|
||||
public function getData();
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($key);
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function toJson();
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray();
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
use Grav\Common\GPM\Common\AbstractPackageCollection as BaseCollection;
|
||||
use Grav\Common\GPM\Response;
|
||||
|
||||
use \Doctrine\Common\Cache\FilesystemCache;
|
||||
|
||||
class AbstractPackageCollection extends BaseCollection
|
||||
{
|
||||
/**
|
||||
* The cached data previously fetched
|
||||
* @var string
|
||||
*/
|
||||
protected $raw;
|
||||
|
||||
/**
|
||||
* The lifetime to store the entry in seconds
|
||||
* @var integer
|
||||
*/
|
||||
private $lifetime = 86400;
|
||||
|
||||
protected $repository;
|
||||
|
||||
protected $cache;
|
||||
|
||||
public function __construct($repository = null, $refresh = false, $callback = null)
|
||||
{
|
||||
if ($repository === null) {
|
||||
throw new \RuntimeException("A repository is required to indicate the origin of the remote collection");
|
||||
}
|
||||
|
||||
$cache_dir = self::getGrav()['locator']->findResource('cache://gpm', true, true);
|
||||
$this->cache = new FilesystemCache($cache_dir);
|
||||
|
||||
$this->repository = $repository;
|
||||
$this->raw = $this->cache->fetch(md5($this->repository));
|
||||
|
||||
$this->fetch($refresh, $callback);
|
||||
foreach (json_decode($this->raw, true) as $slug => $data) {
|
||||
$this->items[$slug] = new Package($data, $this->type);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
class Grav extends Collection
|
||||
use \Doctrine\Common\Cache\FilesystemCache;
|
||||
|
||||
class Grav extends AbstractPackageCollection
|
||||
{
|
||||
private $repository = 'http://getgrav.org/downloads/grav.json';
|
||||
protected $repository = 'http://getgrav.org/downloads/grav.json';
|
||||
private $data;
|
||||
|
||||
private $version;
|
||||
@@ -15,15 +17,17 @@ class Grav extends Collection
|
||||
*/
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
parent::__construct($this->repository);
|
||||
$cache_dir = self::getGrav()['locator']->findResource('cache://gpm', true, true);
|
||||
$this->cache = new FilesystemCache($cache_dir);
|
||||
$this->raw = $this->cache->fetch(md5($this->repository));
|
||||
|
||||
$this->fetch($refresh, $callback);
|
||||
$this->data = json_decode($this->raw);
|
||||
|
||||
$this->version = @$this->data->version ?: '-';
|
||||
$this->date = @$this->data->date ?: '-';
|
||||
$this->data = json_decode($this->raw, true);
|
||||
$this->version = isset($this->data['version']) ? $this->data['version'] : '-';
|
||||
$this->date = isset($this->data['date']) ? $this->data['date'] : '-';
|
||||
|
||||
foreach ($this->data->assets as $slug => $data) {
|
||||
foreach ($this->data['assets'] as $slug => $data) {
|
||||
$this->items[$slug] = new Package($data);
|
||||
}
|
||||
}
|
||||
@@ -34,7 +38,7 @@ class Grav extends Collection
|
||||
*/
|
||||
public function getAssets()
|
||||
{
|
||||
return $this->data->assets;
|
||||
return $this->data['assets'];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,11 +50,11 @@ class Grav extends Collection
|
||||
public function getChangelog($diff = null)
|
||||
{
|
||||
if (!$diff) {
|
||||
return $this->data->changelog;
|
||||
return $this->data['changelog'];
|
||||
}
|
||||
|
||||
$diffLog = [];
|
||||
foreach ($this->data->changelog as $version => $changelog) {
|
||||
foreach ($this->data['changelog'] as $version => $changelog) {
|
||||
preg_match("/[\d\.]+/", $version, $cleanVersion);
|
||||
|
||||
if (!$cleanVersion || version_compare($diff, $cleanVersion[0], ">=")) { continue; }
|
||||
|
||||
@@ -1,32 +1,12 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\GPM\Common\Package as BasePackage;
|
||||
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
class Package extends BasePackage {
|
||||
public function __construct($package, $package_type = null) {
|
||||
$data = new Data($package);
|
||||
parent::__construct($data, $package_type);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,28 +1,17 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
use Grav\Common\Iterator;
|
||||
use Grav\Common\GPM\Common\CachedCollection;
|
||||
|
||||
class Packages extends Iterator
|
||||
class Packages extends CachedCollection
|
||||
{
|
||||
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)
|
||||
];
|
||||
}
|
||||
$items = [
|
||||
'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]);
|
||||
parent::__construct($items);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
class Plugins extends Collection
|
||||
/**
|
||||
* Class Plugins
|
||||
* @package Grav\Common\GPM\Remote
|
||||
*/
|
||||
class Plugins extends AbstractPackageCollection
|
||||
{
|
||||
private $repository = 'http://getgrav.org/downloads/plugins.json';
|
||||
private $type = 'plugins';
|
||||
private $data;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'plugins';
|
||||
|
||||
protected $repository = 'http://getgrav.org/downloads/plugins.json';
|
||||
|
||||
/**
|
||||
* Local Plugins Constructor
|
||||
*/
|
||||
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);
|
||||
}
|
||||
parent::__construct($this->repository, $refresh, $callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
class Themes extends Collection
|
||||
/**
|
||||
* Class Themes
|
||||
* @package Grav\Common\GPM\Remote
|
||||
*/
|
||||
class Themes extends AbstractPackageCollection
|
||||
{
|
||||
private $repository = 'http://getgrav.org/downloads/themes.json';
|
||||
private $type = 'themes';
|
||||
private $data;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'themes';
|
||||
|
||||
protected $repository = 'http://getgrav.org/downloads/themes.json';
|
||||
|
||||
/**
|
||||
* Local Themes Constructor
|
||||
*/
|
||||
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);
|
||||
}
|
||||
parent::__construct($this->repository, $refresh, $callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,6 @@ class Response
|
||||
$method = 'get' . ucfirst(strtolower(self::$method));
|
||||
|
||||
self::$callback = $callback;
|
||||
|
||||
return static::$method($uri, $options, $callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\GPM\Installer;
|
||||
|
||||
class Upgrader
|
||||
{
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Page\Medium\ImageMedium;
|
||||
use Grav\Common\Page\Pages;
|
||||
use Grav\Common\Service\ConfigServiceProvider;
|
||||
use Grav\Common\Service\ErrorServiceProvider;
|
||||
@@ -10,7 +11,6 @@ use Grav\Common\Service\StreamsServiceProvider;
|
||||
use RocketTheme\Toolbox\DI\Container;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use Grav\Common\Page\Medium;
|
||||
|
||||
/**
|
||||
* Grav
|
||||
@@ -99,34 +99,34 @@ class Grav extends Container
|
||||
/** @var Pages $pages */
|
||||
$pages = $c['pages'];
|
||||
|
||||
// If base URI is set, we want to remove it from the URL.
|
||||
$path = '/' . ltrim(Folder::getRelativePath($c['uri']->route(), $pages->base()), '/');
|
||||
/** @var Uri $uri */
|
||||
$uri = $c['uri'];
|
||||
|
||||
$path = $uri->path();
|
||||
|
||||
$page = $pages->dispatch($path);
|
||||
|
||||
if (!$page || !$page->routable()) {
|
||||
$path_parts = pathinfo($path);
|
||||
$page = $c['pages']->dispatch($path_parts['dirname'], true);
|
||||
if ($page) {
|
||||
$media = $page->media()->all();
|
||||
|
||||
// special case where a media file is requested
|
||||
if (!$page) {
|
||||
$path_parts = pathinfo($path);
|
||||
$parsed_url = parse_url(urldecode($uri->basename()));
|
||||
|
||||
$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];
|
||||
$media_file = $parsed_url['path'];
|
||||
|
||||
// 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));
|
||||
}
|
||||
// if this is a media object, try actions first
|
||||
if (isset($media[$media_file])) {
|
||||
$medium = $media[$media_file];
|
||||
foreach ($uri->query(null, true) as $action => $params) {
|
||||
if (in_array($action, ImageMedium::$magic_actions)) {
|
||||
call_user_func_array(array(&$medium, $action), explode(',', $params));
|
||||
}
|
||||
header('Content-type: '. $mime = $medium->get('mime'));
|
||||
echo file_get_contents($medium->path());
|
||||
die;
|
||||
}
|
||||
Utils::download($medium->path(), false);
|
||||
} else {
|
||||
Utils::download($page->path() . DIRECTORY_SEPARATOR . $uri->basename(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,18 +174,23 @@ class Grav extends Container
|
||||
ob_start('ob_gzhandler');
|
||||
}
|
||||
|
||||
|
||||
/** @var Debugger $debugger */
|
||||
$debugger = $this['debugger'];
|
||||
|
||||
// Initialize configuration.
|
||||
$debugger->startTimer('_config', 'Configuration');
|
||||
$this['config']->init();
|
||||
$this['uri']->init();
|
||||
$this['errors']->resetHandlers();
|
||||
$debugger->init();
|
||||
$this['config']->debug();
|
||||
$debugger->stopTimer('_config');
|
||||
|
||||
// Initialize the timezone
|
||||
if ($this['config']->get('system.timezone')) {
|
||||
date_default_timezone_set($this['config']->get('system.timezone'));
|
||||
}
|
||||
|
||||
$debugger->startTimer('streams', 'Streams');
|
||||
$this['streams'];
|
||||
$debugger->stopTimer('streams');
|
||||
@@ -252,7 +257,13 @@ class Grav extends Container
|
||||
$this['session']->close();
|
||||
}
|
||||
|
||||
header("Location: " . rtrim($uri->rootUrl(), '/') .'/'. trim($route, '/'), true, $code);
|
||||
if ($this['uri']->isExternal($route)) {
|
||||
$url = $route;
|
||||
} else {
|
||||
$url = rtrim($uri->rootUrl(), '/') .'/'. trim($route, '/');
|
||||
}
|
||||
|
||||
header("Location: {$url}", true, $code);
|
||||
exit();
|
||||
}
|
||||
|
||||
@@ -287,10 +298,11 @@ class Grav extends Container
|
||||
$extension = $this['uri']->extension();
|
||||
header('Content-type: ' . $this->mime($extension));
|
||||
|
||||
header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time() + $this['config']->get('system.pages.expires')));
|
||||
|
||||
// Set debugger data in headers
|
||||
if (!($extension == null || $extension == 'html')) {
|
||||
if (!($extension === null || $extension == 'html')) {
|
||||
$this['debugger']->enabled(false);
|
||||
// $this['debugger']->sendDataInHeaders();
|
||||
}
|
||||
|
||||
// Set HTTP response code
|
||||
@@ -337,7 +349,7 @@ class Grav extends Container
|
||||
header("Connection: close\r\n");
|
||||
|
||||
ob_end_flush(); // regular buffer
|
||||
ob_flush();
|
||||
@ob_flush();
|
||||
flush();
|
||||
|
||||
if (function_exists('fastcgi_finish_request')) {
|
||||
|
||||
@@ -11,8 +11,11 @@ trait GravTrait
|
||||
/**
|
||||
* @return Grav
|
||||
*/
|
||||
public function getGrav()
|
||||
public static function getGrav()
|
||||
{
|
||||
if (!self::$grav) {
|
||||
self::$grav = Grav::instance();
|
||||
}
|
||||
return self::$grav;
|
||||
}
|
||||
|
||||
|
||||
@@ -84,10 +84,43 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
|
||||
*/
|
||||
public function nth($key)
|
||||
{
|
||||
$items = array_values($this->items);
|
||||
$items = array_keys($this->items);
|
||||
return (isset($items[$key])) ? $this->offsetGet($items[$key]) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first item
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function first()
|
||||
{
|
||||
$items = array_keys($this->items);
|
||||
return $this->offsetGet(array_shift($items));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last item
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function last()
|
||||
{
|
||||
$items = array_keys($this->items);
|
||||
return $this->offsetGet(array_pop($items));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the Iterator
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reverse()
|
||||
{
|
||||
$this->items = array_reverse($this->items);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $needle Searched value.
|
||||
* @return string|bool Key if found, otherwise false.
|
||||
@@ -164,4 +197,23 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter elements from the list
|
||||
* @param callable|null $callback A function the receives ($value, $key) and must return a boolean to indicate filter status
|
||||
* @return $this
|
||||
*/
|
||||
public function filter(callable $callback = null)
|
||||
{
|
||||
foreach ($this->items as $key => $value) {
|
||||
if (
|
||||
($callback && !call_user_func($callback, $value, $key)) ||
|
||||
(!$callback && !(bool) $value)
|
||||
) {
|
||||
unset($this->items[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ 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\Page\Medium\Medium;
|
||||
use Grav\Common\Uri;
|
||||
|
||||
/**
|
||||
@@ -14,11 +14,12 @@ trait ParsedownGravTrait
|
||||
{
|
||||
use GravTrait;
|
||||
protected $page;
|
||||
protected $pages;
|
||||
protected $base_url;
|
||||
protected $pages_dir;
|
||||
protected $special_chars;
|
||||
|
||||
protected $twig_link_regex = '/\!*\[(?:.*)\]\(([{{|{%|{#].*[#}|%}|}}])\)/';
|
||||
protected $twig_link_regex = '/\!*\[(?:.*)\]\((\{([\{%#])\s*(.*?)\s*(?:\2|\})\})\)/';
|
||||
|
||||
/**
|
||||
* Initialiazation function to setup key variables needed by the MarkdownGravLinkTrait
|
||||
@@ -28,10 +29,29 @@ trait ParsedownGravTrait
|
||||
protected function init($page)
|
||||
{
|
||||
$this->page = $page;
|
||||
$this->pages = self::getGrav()['pages'];
|
||||
$this->BlockTypes['{'] [] = "TwigTag";
|
||||
$this->base_url = rtrim(self::$grav['base_url'] . self::$grav['pages']->base(), '/');
|
||||
$this->pages_dir = self::$grav['locator']->findResource('page://');
|
||||
$this->base_url = rtrim(self::getGrav()['base_url'] . self::getGrav()['pages']->base(), '/');
|
||||
$this->pages_dir = self::getGrav()['locator']->findResource('page://');
|
||||
$this->special_chars = array('>' => 'gt', '<' => 'lt', '"' => 'quot');
|
||||
|
||||
$defaults = self::getGrav()['config']->get('system.pages.markdown');
|
||||
|
||||
$this->setBreaksEnabled($defaults['auto_line_breaks']);
|
||||
$this->setUrlsLinked($defaults['auto_url_links']);
|
||||
$this->setMarkupEscaped($defaults['escape_markup']);
|
||||
$this->setSpecialChars($defaults['special_chars']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the element function publicly accessible, Medium uses this to render from Twig
|
||||
*
|
||||
* @param array $Element
|
||||
* @return string markup
|
||||
*/
|
||||
public function elementToHtml(array $Element)
|
||||
{
|
||||
return $this->element($Element);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,7 +83,7 @@ trait ParsedownGravTrait
|
||||
|
||||
protected function inlineSpecialCharacter($Excerpt)
|
||||
{
|
||||
if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text'])) {
|
||||
if ($Excerpt['text'][0] === '&' && ! preg_match('/^&#?\w+;/', $Excerpt['text'])) {
|
||||
return array(
|
||||
'markup' => '&',
|
||||
'extent' => 1,
|
||||
@@ -90,81 +110,73 @@ trait ParsedownGravTrait
|
||||
$excerpt = parent::inlineImage($excerpt);
|
||||
}
|
||||
|
||||
// Some stuff we will need
|
||||
$actions = array();
|
||||
$media = null;
|
||||
|
||||
// if this is an image
|
||||
if (isset($excerpt['element']['attributes']['src'])) {
|
||||
|
||||
$alt = $excerpt['element']['attributes']['alt'] ?: '';
|
||||
$title = $excerpt['element']['attributes']['title'] ?: '';
|
||||
$class = isset($excerpt['element']['attributes']['class']) ? $excerpt['element']['attributes']['class'] : '';
|
||||
|
||||
//get the url and parse it
|
||||
$url = parse_url(htmlspecialchars_decode($excerpt['element']['attributes']['src']));
|
||||
|
||||
//get back to current page if possible
|
||||
$path_parts = pathinfo($url['path']);
|
||||
|
||||
// if there is no host set but there is a path, the file is local
|
||||
if (!isset($url['host']) && isset($url['path'])) {
|
||||
// get the media objects for this page
|
||||
$media = $this->page->media();
|
||||
|
||||
// get the local path to page media if possible
|
||||
if (strpos($url['path'], $this->page->url()) !== false) {
|
||||
if ($path_parts['dirname'] == $this->page->url()) {
|
||||
$url['path'] = ltrim(str_replace($this->page->url(), '', $url['path']), '/');
|
||||
// get the media objects for this page
|
||||
$media = $this->page->media();
|
||||
|
||||
} else {
|
||||
|
||||
// see if this is an external page to this one
|
||||
$page_route = str_replace($this->base_url, '', $path_parts['dirname']);
|
||||
|
||||
$ext_page = $this->pages->dispatch($page_route, true);
|
||||
if ($ext_page) {
|
||||
$media = $ext_page->media();
|
||||
$url['path'] = $path_parts['basename'];
|
||||
}
|
||||
}
|
||||
|
||||
// if there is a media file that matches the path referenced..
|
||||
if (isset($media->images()[$url['path']])) {
|
||||
if ($media && isset($media->all()[$url['path']])) {
|
||||
// get the medium object
|
||||
$medium = $media->images()[$url['path']];
|
||||
$medium = $media->all()[$url['path']];
|
||||
|
||||
// if there is a query, then parse it and build action calls
|
||||
if (isset($url['query'])) {
|
||||
parse_str($url['query'], $actions);
|
||||
$actions = array_reduce(explode('&', $url['query']), function ($carry, $item) {
|
||||
$parts = explode('=', $item, 2);
|
||||
$value = isset($parts[1]) ? $parts[1] : null;
|
||||
$carry[] = [ 'method' => $parts[0], 'params' => $value ];
|
||||
|
||||
return $carry;
|
||||
}, []);
|
||||
}
|
||||
|
||||
// loop through actions for the image and call them
|
||||
foreach ($actions as $action => $params) {
|
||||
// as long as it's a valid action
|
||||
if (in_array($action, Medium::$valid_actions)) {
|
||||
call_user_func_array(array(&$medium, $action), explode(',', $params));
|
||||
}
|
||||
foreach ($actions as $action) {
|
||||
$medium = call_user_func_array(array($medium, $action['method']), explode(',', $action['params']));
|
||||
}
|
||||
|
||||
// Get the URL for regular images, or an array of bits needed to put together
|
||||
// the lightbox HTML
|
||||
if (!isset($actions['lightbox'])) {
|
||||
$src = $medium->url();
|
||||
} else {
|
||||
$src = $medium->lightboxRaw();
|
||||
if (isset($url['fragment'])) {
|
||||
$medium->urlHash($url['fragment']);
|
||||
}
|
||||
|
||||
// set the src element with the new generated url
|
||||
if (!isset($actions['lightbox']) && !is_array($src)) {
|
||||
$excerpt['element']['attributes']['src'] = $src;
|
||||
} else {
|
||||
// Create the custom lightbox element
|
||||
$element = array(
|
||||
'name' => 'a',
|
||||
'attributes' => array('rel' => $src['a_rel'], 'href' => $src['a_url']),
|
||||
'handler' => 'element',
|
||||
'text' => array(
|
||||
'name' => 'img',
|
||||
'attributes' => array('src' => $src['img_url'], 'alt' => $alt, 'title' => $title)
|
||||
),
|
||||
);
|
||||
$excerpt['element'] = $medium->parseDownElement($title, $alt, $class);
|
||||
|
||||
// Set any custom classes on the lightbox element
|
||||
if (isset($excerpt['element']['attributes']['class'])) {
|
||||
$element['attributes']['class'] = $excerpt['element']['attributes']['class'];
|
||||
}
|
||||
|
||||
// Set the lightbox element on the Excerpt
|
||||
$excerpt['element'] = $element;
|
||||
}
|
||||
} else {
|
||||
// not a current page media file, see if it needs converting to relative
|
||||
$excerpt['element']['attributes']['src'] = $this->convertUrl(Uri::build_url($url));
|
||||
$excerpt['element']['attributes']['src'] = Uri::buildUrl($url);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,14 +199,12 @@ trait ParsedownGravTrait
|
||||
|
||||
// if this is a link
|
||||
if (isset($excerpt['element']['attributes']['href'])) {
|
||||
|
||||
$url = parse_url(htmlspecialchars_decode($excerpt['element']['attributes']['href']));
|
||||
|
||||
// if there is no scheme, the file is local
|
||||
if (!isset($url['scheme'])) {
|
||||
|
||||
if (!isset($url['scheme']) && (count($url) > 0)) {
|
||||
// convert the URl is required
|
||||
$excerpt['element']['attributes']['href'] = $this->convertUrl(Uri::build_url($url));
|
||||
$excerpt['element']['attributes']['href'] = $this->convertUrl(Uri::buildUrl($url));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,17 +220,17 @@ trait ParsedownGravTrait
|
||||
{
|
||||
// if absolute and starts with a base_url move on
|
||||
if ($this->base_url != '' && strpos($markdown_url, $this->base_url) === 0) {
|
||||
$new_url = $markdown_url;
|
||||
return $markdown_url;
|
||||
// if its absolute and starts with /
|
||||
} elseif (strpos($markdown_url, '/') === 0) {
|
||||
$new_url = $this->base_url . $markdown_url;
|
||||
return $this->base_url . $markdown_url;
|
||||
} else {
|
||||
$relative_path = $this->base_url . $this->page->route();
|
||||
$real_path = $this->page->path() . '/' . parse_url($markdown_url, PHP_URL_PATH);
|
||||
|
||||
// If this is a 'real' filepath clean it up
|
||||
if (file_exists($this->page->path() . '/' . parse_url($markdown_url, PHP_URL_PATH))) {
|
||||
$relative_path = $this->base_url . preg_replace('/\/([\d]+.)/', '/', str_replace($this->pages_dir, '', $this->page->path()));
|
||||
$markdown_url = preg_replace('/^([\d]+.)/', '', preg_replace('/\/([\d]+.)/', '/', trim(preg_replace('/[^\/]+(\.md$)/', '', $markdown_url), '/')));
|
||||
// strip numeric order from markdown path
|
||||
if (($real_path)) {
|
||||
$markdown_url = preg_replace('/^([\d]+\.)/', '', preg_replace('/\/([\d]+\.)/', '/', trim(preg_replace('/[^\/]+(\.md$)/', '', $markdown_url), '/')));
|
||||
}
|
||||
|
||||
// else its a relative path already
|
||||
|
||||
@@ -5,6 +5,8 @@ use Grav\Common\Getters;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Page\Medium\Medium;
|
||||
use Grav\Common\Page\Medium\MediumFactory;
|
||||
|
||||
/**
|
||||
* Media is a holder object that contains references to the media of page. This object is created and
|
||||
@@ -38,88 +40,93 @@ class Media extends Getters
|
||||
|
||||
$this->path = $path;
|
||||
|
||||
$iterator = new \DirectoryIterator($path);
|
||||
$iterator = new \FilesystemIterator($path, \FilesystemIterator::UNIX_PATHS | \FilesystemIterator::SKIP_DOTS);
|
||||
|
||||
$media = [];
|
||||
|
||||
/** @var \DirectoryIterator $info */
|
||||
foreach ($iterator as $info) {
|
||||
foreach ($iterator as $path => $info) {
|
||||
// Ignore folders and Markdown files.
|
||||
if ($info->isDot() || !$info->isFile() || $info->getExtension() == 'md') {
|
||||
if (!$info->isFile() || $info->getExtension() == 'md') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find out the real filename, in case of we are at the metadata.
|
||||
$filename = $info->getFilename();
|
||||
list($basename, $ext, $meta) = $this->getFileParts($filename);
|
||||
// Find out what type we're dealing with
|
||||
list($basename, $ext, $type, $extra) = $this->getFileParts($info->getFilename());
|
||||
|
||||
$media["{$basename}.{$ext}"] = isset($media["{$basename}.{$ext}"]) ? $media["{$basename}.{$ext}"] : [];
|
||||
|
||||
if ($type === 'alternative') {
|
||||
$media["{$basename}.{$ext}"][$type] = isset($media["{$basename}.{$ext}"][$type]) ? $media["{$basename}.{$ext}"][$type] : [];
|
||||
$media["{$basename}.{$ext}"][$type][$extra] = $path;
|
||||
} else {
|
||||
$media["{$basename}.{$ext}"][$type] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($media as $name => $types) {
|
||||
// First prepare the alternatives in case there is no base medium
|
||||
if (!empty($types['alternative'])) {
|
||||
foreach ($types['alternative'] as $ratio => &$file) {
|
||||
$file = MediumFactory::fromFile($file);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the base medium
|
||||
if (!empty($types['base'])) {
|
||||
$medium = MediumFactory::fromFile($types['base']);
|
||||
} else if (!empty($types['alternative'])) {
|
||||
$altMedium = reset($types['alternative']);
|
||||
$ratio = key($types['alternative']);
|
||||
|
||||
$medium = MediumFactory::scaledFromMedium($altMedium, $ratio, 1);
|
||||
}
|
||||
|
||||
// Get medium instance creating it if it didn't exist.
|
||||
$medium = $this->get("{$basename}.{$ext}", true);
|
||||
if (!$medium) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//set file size
|
||||
$medium->set('size', $info->getSize());
|
||||
|
||||
// Assign meta files to the medium.
|
||||
if ($meta) {
|
||||
$medium->addMetaFile($meta);
|
||||
if (!empty($types['meta'])) {
|
||||
$medium->addMetaFile($types['meta']);
|
||||
}
|
||||
|
||||
if (!empty($types['thumb'])) {
|
||||
// We will not turn it into medium yet because user might never request the thumbnail
|
||||
// not wasting any resources on that, maybe we should do this for medium in general?
|
||||
$medium->set('thumbnails.page', $types['thumb']);
|
||||
}
|
||||
|
||||
// Build missing alternatives
|
||||
if (!empty($types['alternative'])) {
|
||||
$alternatives = $types['alternative'];
|
||||
|
||||
$max = max(array_keys($alternatives));
|
||||
|
||||
for ($i=2; $i < $max; $i++) {
|
||||
if (isset($alternatives[$i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$types['alternative'][$i] = MediumFactory::scaledFromMedium($alternatives[$max], $max, $i);
|
||||
}
|
||||
|
||||
foreach ($types['alternative'] as $ratio => $altMedium) {
|
||||
$medium->addAlternative($ratio, $altMedium);
|
||||
}
|
||||
}
|
||||
|
||||
$this->add($name, $medium);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get medium by basename and extension.
|
||||
* Get medium by filename.
|
||||
*
|
||||
* @param string $filename
|
||||
* @param bool $create
|
||||
* @return Medium|null
|
||||
*/
|
||||
public function get($filename, $create = false)
|
||||
public function get($filename)
|
||||
{
|
||||
if ($create && !isset($this->instances[$filename])) {
|
||||
$parts = explode('.', $filename);
|
||||
$ext = array_pop($parts);
|
||||
$basename = implode('.', $parts);
|
||||
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
|
||||
// Check if medium type has been configured.
|
||||
$params = $config->get("media.".strtolower($ext));
|
||||
if (!$params) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$filePath = $this->path . '/' . $filename;
|
||||
|
||||
// Add default settings for undefined variables.
|
||||
$params += $config->get('media.defaults');
|
||||
$params += array(
|
||||
'type' => 'file',
|
||||
'thumb' => 'media/thumb.png',
|
||||
'mime' => 'application/octet-stream',
|
||||
'name' => $filename,
|
||||
'filename' => $filename,
|
||||
'basename' => $basename,
|
||||
'extension' => $ext,
|
||||
'path' => $this->path,
|
||||
'modified' => filemtime($filePath),
|
||||
);
|
||||
|
||||
$lookup = array(
|
||||
USER_DIR . 'images/',
|
||||
SYSTEM_DIR . 'images/',
|
||||
);
|
||||
foreach ($lookup as $path) {
|
||||
if (is_file($path . $params['thumb'])) {
|
||||
$params['thumb'] = $path . $params['thumb'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->add(new Medium($params));
|
||||
}
|
||||
|
||||
return isset($this->instances[$filename]) ? $this->instances[$filename] : null;
|
||||
}
|
||||
|
||||
@@ -181,21 +188,21 @@ class Media extends Getters
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function add($file)
|
||||
protected function add($name, $file)
|
||||
{
|
||||
$this->instances[$file->filename] = $file;
|
||||
$this->instances[$name] = $file;
|
||||
switch ($file->type) {
|
||||
case 'image':
|
||||
$this->images[$file->filename] = $file;
|
||||
$this->images[$name] = $file;
|
||||
break;
|
||||
case 'video':
|
||||
$this->videos[$file->filename] = $file;
|
||||
$this->videos[$name] = $file;
|
||||
break;
|
||||
case 'audio':
|
||||
$this->audios[$file->filename] = $file;
|
||||
$this->audios[$name] = $file;
|
||||
break;
|
||||
default:
|
||||
$this->files[$file->filename] = $file;
|
||||
$this->files[$name] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,19 +217,35 @@ class Media extends Getters
|
||||
$fileParts = explode('.', $filename);
|
||||
|
||||
$name = array_shift($fileParts);
|
||||
$extension = null;
|
||||
while (($part = array_shift($fileParts)) !== null) {
|
||||
if ($part != 'meta') {
|
||||
if (isset($extension)) {
|
||||
$name .= '.' . $extension;
|
||||
$type = 'base';
|
||||
$extra = null;
|
||||
|
||||
if (preg_match('/(.*)@(\d+)x\.(.*)$/', $filename, $matches)) {
|
||||
$name = $matches[1];
|
||||
$extension = $matches[3];
|
||||
$extra = (int) $matches[2];
|
||||
$type = 'alternative';
|
||||
|
||||
if ($extra === 1) {
|
||||
$type = 'base';
|
||||
$extra = null;
|
||||
}
|
||||
} else {
|
||||
$extension = null;
|
||||
while (($part = array_shift($fileParts)) !== null) {
|
||||
if ($part != 'meta' && $part != 'thumb') {
|
||||
if (isset($extension)) {
|
||||
$name .= '.' . $extension;
|
||||
}
|
||||
$extension = $part;
|
||||
} else {
|
||||
$type = $part;
|
||||
$extra = '.' . $part . '.' . implode('.', $fileParts);
|
||||
break;
|
||||
}
|
||||
$extension = $part;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$meta = implode('.', $fileParts);
|
||||
|
||||
return array($name, $extension, $meta);
|
||||
return array($name, $extension, $type, $extra);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,382 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* The Image medium holds information related to an individual image. These are then stored in the Media object.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*
|
||||
* @property string $file_name
|
||||
* @property string $type
|
||||
* @property string $name Alias of file_name
|
||||
* @property string $description
|
||||
* @property string $url
|
||||
* @property string $path
|
||||
* @property string $thumb
|
||||
* @property int $width
|
||||
* @property int $height
|
||||
* @property string $mime
|
||||
* @property int $modified
|
||||
*
|
||||
* Medium can have up to 3 files:
|
||||
* - video.mov Medium file itself.
|
||||
* - video.mov.meta.yaml Metadata for the medium.
|
||||
* - video.mov.thumb.jpg Thumbnail image for the medium.
|
||||
*
|
||||
*/
|
||||
class Medium extends Data
|
||||
{
|
||||
use GravTrait;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var ImageFile
|
||||
*/
|
||||
protected $image;
|
||||
|
||||
protected $type = 'guess';
|
||||
protected $quality = 85;
|
||||
|
||||
public static $valid_actions = ['resize', 'forceResize', 'cropResize', 'crop', 'cropZoom',
|
||||
'negate', 'brightness', 'contrast', 'grayscale', 'emboss', 'smooth', 'sharp', 'edge', 'colorize', 'sepia' ];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $meta = array();
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $linkTarget;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $linkAttributes;
|
||||
|
||||
public function __construct($items = array(), Blueprint $blueprint = null)
|
||||
{
|
||||
parent::__construct($items, $blueprint);
|
||||
|
||||
$file_path = $this->get('path') . '/' . $this->get('filename');
|
||||
$file_parts = pathinfo($file_path);
|
||||
|
||||
$this->set('thumb', $file_path);
|
||||
$this->set('extension', $file_parts['extension']);
|
||||
$this->set('filename', $this->get('filename'));
|
||||
|
||||
if ($this->get('type') == 'image') {
|
||||
$image_info = getimagesize($file_path);
|
||||
$this->def('width', $image_info[0]);
|
||||
$this->def('height', $image_info[1]);
|
||||
$this->def('mime', $image_info['mime']);
|
||||
$this->reset();
|
||||
} else {
|
||||
$this->def('mime', 'application/octet-stream');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return string representation of the object (html or url).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the quality of the image
|
||||
* @param Int $quality 0-100 quality
|
||||
* @return Medium
|
||||
*/
|
||||
public function quality($quality)
|
||||
{
|
||||
$this->quality = $quality;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return URL to file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function url()
|
||||
{
|
||||
if ($this->image) {
|
||||
$output = $this->image->cacheFile($this->type, $this->quality);
|
||||
$this->reset();
|
||||
} else {
|
||||
$relPath = preg_replace('|^' . ROOT_DIR . '|', '', $this->get('path'));
|
||||
$output = $relPath . '/' . $this->get('filename');
|
||||
}
|
||||
|
||||
return self::$grav['base_url'] . '/'. $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets image output format.
|
||||
*
|
||||
* @param string $type
|
||||
* @param int $quality
|
||||
* @return $this
|
||||
*/
|
||||
public function format($type = null, $quality = 80)
|
||||
{
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
}
|
||||
|
||||
$this->type = $type;
|
||||
$this->quality = $quality;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <img> tag from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $class
|
||||
* @param string $type
|
||||
* @param int $quality
|
||||
* @return string
|
||||
*/
|
||||
public function img($title = null, $class = null, $type = null, $quality = 80)
|
||||
{
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
}
|
||||
|
||||
$output = $this->html($title, $class, $type, $quality);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return HTML markup from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $class
|
||||
* @param string $type
|
||||
* @param int $quality
|
||||
* @return string
|
||||
*/
|
||||
public function html($title = null, $class = null, $type = null, $quality = 80)
|
||||
{
|
||||
$title = $title ? $title : $this->get('title');
|
||||
$class = $class ? $class : '';
|
||||
|
||||
if ($this->image) {
|
||||
$type = $type ? $type : $this->type;
|
||||
$quality = $quality ? $quality : $this->quality;
|
||||
|
||||
$url = $this->url($type, $quality);
|
||||
$this->reset();
|
||||
|
||||
$output = '<img src="' . $url . '" class="'. $class . '" alt="' . $title . '" />';
|
||||
} else {
|
||||
$output = $title;
|
||||
}
|
||||
|
||||
if ($this->linkTarget) {
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
|
||||
$output = '<a href="' . self::$grav['base_url'] . '/'. $this->linkTarget
|
||||
. '"' . $this->linkAttributes. ' class="'. $class . '">' . $output . '</a>';
|
||||
|
||||
$this->linkTarget = $this->linkAttributes = null;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return lightbox HTML for the medium.
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @return $this
|
||||
*/
|
||||
public function lightbox($width = null, $height = null)
|
||||
{
|
||||
$this->linkAttributes = ' rel="lightbox"';
|
||||
|
||||
return $this->link($width, $height);
|
||||
}
|
||||
|
||||
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['base_url'] . '/'. $this->linkTarget;
|
||||
|
||||
return array('a_url' => $lightbox_url, 'a_rel' => 'lightbox', 'img_url' => $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return link HTML for the medium.
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @return $this
|
||||
*/
|
||||
public function link($width = null, $height = null)
|
||||
{
|
||||
if ($this->image) {
|
||||
$image = clone $this->image;
|
||||
if ($width && $height) {
|
||||
$image->cropResize($width, $height);
|
||||
}
|
||||
$this->linkTarget = $image->cacheFile($this->type, $this->quality);
|
||||
} else {
|
||||
// TODO: we need to find out URI in a bit better way.
|
||||
$relPath = preg_replace('|^' . ROOT_DIR . '|', '', $this->get('path'));
|
||||
$this->linkTarget = $relPath. '/' . $this->get('filename');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset image.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->image = null;
|
||||
|
||||
if ($this->get('type') == 'image') {
|
||||
$this->image();
|
||||
$this->filter();
|
||||
}
|
||||
$this->type = 'guess';
|
||||
$this->quality = 80;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward the call to the image processing method.
|
||||
*
|
||||
* @param string $method
|
||||
* @param mixed $args
|
||||
* @return $this|mixed
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
if ($method == 'cropZoom') {
|
||||
$method = 'zoomCrop';
|
||||
}
|
||||
|
||||
// Always initialize image.
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
}
|
||||
|
||||
try {
|
||||
$result = call_user_func_array(array($this->image, $method), $args);
|
||||
} catch (\BadFunctionCallException $e) {
|
||||
$result = null;
|
||||
}
|
||||
|
||||
// Returns either current object or result of the action.
|
||||
return $result instanceof ImageFile ? $this : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets medium image, resets image manipulation operations.
|
||||
*
|
||||
* @param string $variable
|
||||
* @return $this
|
||||
*/
|
||||
public function image($variable = 'thumb')
|
||||
{
|
||||
// TODO: add default file
|
||||
$file = $this->get($variable);
|
||||
$this->image = ImageFile::open($file)
|
||||
->setCacheDir(basename(IMAGES_DIR))
|
||||
->setActualCacheDir(IMAGES_DIR)
|
||||
->setPrettyName(basename($this->get('basename')));
|
||||
|
||||
$this->filter();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add meta file for the medium.
|
||||
*
|
||||
* @param $type
|
||||
* @return $this
|
||||
*/
|
||||
public function addMetaFile($type)
|
||||
{
|
||||
$this->meta[$type] = $type;
|
||||
|
||||
$path = $this->get('path') . '/' . $this->get('filename') . '.meta.' . $type;
|
||||
if ($type == 'yaml') {
|
||||
$this->merge(CompiledYamlFile::instance($path)->content());
|
||||
} elseif (in_array($type, array('jpg', 'jpeg', 'png', 'gif'))) {
|
||||
$this->set('thumb', $path);
|
||||
}
|
||||
$this->reset();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter image by using user defined filter parameters.
|
||||
*
|
||||
* @param string $filter Filter to be used.
|
||||
*/
|
||||
public function filter($filter = 'image.filters.default')
|
||||
{
|
||||
$filters = (array) $this->get($filter, array());
|
||||
foreach ($filters as $params) {
|
||||
$params = (array) $params;
|
||||
$method = array_shift($params);
|
||||
$this->__call($method, $params);
|
||||
}
|
||||
}
|
||||
}
|
||||
397
system/src/Grav/Common/Page/Medium/ImageMedium.php
Normal file
397
system/src/Grav/Common/Page/Medium/ImageMedium.php
Normal file
@@ -0,0 +1,397 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Gregwar\Image\Image as ImageFile;
|
||||
|
||||
class ImageMedium extends Medium
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $thumbnailTypes = [ 'page', 'media', 'default' ];
|
||||
|
||||
/**
|
||||
* @var ImageFile
|
||||
*/
|
||||
protected $image;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $format = 'guess';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $quality;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $default_quality;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected $debug_watermarked = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $magic_actions = [
|
||||
'resize', 'forceResize', 'cropResize', 'crop', 'zoomCrop',
|
||||
'negate', 'brightness', 'contrast', 'grayscale', 'emboss',
|
||||
'smooth', 'sharp', 'edge', 'colorize', 'sepia', 'enableProgressive'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $magic_resize_actions = [
|
||||
'resize' => [ 0, 1 ],
|
||||
'forceResize' => [ 0, 1 ],
|
||||
'cropResize' => [ 0, 1 ],
|
||||
'crop' => [ 0, 1, 2, 3 ],
|
||||
'cropResize' => [ 0, 1 ],
|
||||
'zoomCrop' => [ 0, 1 ]
|
||||
];
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param array $items
|
||||
* @param Blueprint $blueprint
|
||||
*/
|
||||
public function __construct($items = [], Blueprint $blueprint = null)
|
||||
{
|
||||
parent::__construct($items, $blueprint);
|
||||
|
||||
$image_info = getimagesize($this->get('filepath'));
|
||||
$this->def('width', $image_info[0]);
|
||||
$this->def('height', $image_info[1]);
|
||||
$this->def('mime', $image_info['mime']);
|
||||
$this->def('debug', self::$grav['config']->get('system.images.debug'));
|
||||
|
||||
$this->set('thumbnails.media', $this->get('filepath'));
|
||||
|
||||
$this->default_quality = self::$grav['config']->get('system.images.default_image_quality', 85);
|
||||
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add meta file for the medium.
|
||||
*
|
||||
* @param $filepath
|
||||
* @return $this
|
||||
*/
|
||||
public function addMetaFile($filepath)
|
||||
{
|
||||
parent::addMetaFile($filepath);
|
||||
|
||||
// Apply filters in meta file
|
||||
$this->reset();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return PATH to image.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string path to image
|
||||
*/
|
||||
public function path($reset = true)
|
||||
{
|
||||
$output = $this->saveImage();
|
||||
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return URL to image.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function url($reset = true)
|
||||
{
|
||||
$output = preg_replace('|^' . GRAV_ROOT . '|', '', $this->saveImage());
|
||||
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return self::$grav['base_url'] . $output . $this->querystring() . $this->urlHash();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return srcset string for this Medium and its alternatives.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function srcset($reset = true)
|
||||
{
|
||||
if (empty($this->alternatives)) {
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
$srcset = [ $this->url($reset) . ' ' . $this->get('width') . 'w' ];
|
||||
|
||||
foreach ($this->alternatives as $ratio => $medium) {
|
||||
$srcset[] = $medium->url($reset) . ' ' . $medium->get('width') . 'w';
|
||||
}
|
||||
|
||||
return implode(', ', $srcset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsedown element for source display mode
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
public function sourceParsedownElement(array $attributes, $reset = true)
|
||||
{
|
||||
empty($attributes['src']) && $attributes['src'] = $this->url(false);
|
||||
|
||||
$srcset = $this->srcset($reset);
|
||||
if ($srcset) {
|
||||
empty($attributes['srcset']) && $attributes['srcset'] = $srcset;
|
||||
empty($attributes['sizes']) && $attributes['sizes'] = $this->sizes();
|
||||
}
|
||||
|
||||
return [ 'name' => 'img', 'attributes' => $attributes ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset image.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
parent::reset();
|
||||
|
||||
if ($this->image) {
|
||||
$this->image();
|
||||
$this->filter();
|
||||
}
|
||||
|
||||
$this->format = 'guess';
|
||||
$this->quality = $this->default_quality;
|
||||
|
||||
$this->debug_watermarked = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the current Medium into a Link
|
||||
*
|
||||
* @param boolean $reset
|
||||
* @param array $attributes
|
||||
* @return Link
|
||||
*/
|
||||
public function link($reset = true, array $attributes = [])
|
||||
{
|
||||
$attributes['href'] = $this->url(false);
|
||||
$srcset = $this->srcset(false);
|
||||
if ($srcset) {
|
||||
$attributes['data-srcset'] = $srcset;
|
||||
}
|
||||
|
||||
return parent::link($reset, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the current Medium inta a Link with lightbox enabled
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @param boolean $reset
|
||||
* @return Link
|
||||
*/
|
||||
public function lightbox($width = null, $height = null, $reset = true)
|
||||
{
|
||||
if ($this->mode !== 'source') {
|
||||
$this->display('source');
|
||||
}
|
||||
|
||||
if ($width && $height) {
|
||||
$this->cropResize($width, $height);
|
||||
}
|
||||
|
||||
return parent::lightbox($width, $height, $reset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the quality of the image
|
||||
*
|
||||
* @param int $quality 0-100 quality
|
||||
* @return Medium
|
||||
*/
|
||||
public function quality($quality)
|
||||
{
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
}
|
||||
|
||||
$this->quality = $quality;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets image output format.
|
||||
*
|
||||
* @param string $format
|
||||
* @return $this
|
||||
*/
|
||||
public function format($format)
|
||||
{
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
}
|
||||
|
||||
$this->format = $format;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or get sizes parameter for srcset media action
|
||||
*
|
||||
* @param string $sizes
|
||||
* @return $this
|
||||
*/
|
||||
public function sizes($sizes = null)
|
||||
{
|
||||
|
||||
if ($sizes) {
|
||||
$this->attributes['sizes'] = $sizes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
return empty($this->attributes['sizes']) ? '100vw' : $this->attributes['sizes'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward the call to the image processing method.
|
||||
*
|
||||
* @param string $method
|
||||
* @param mixed $args
|
||||
* @return $this|mixed
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
if ($method == 'cropZoom') {
|
||||
$method = 'zoomCrop';
|
||||
}
|
||||
|
||||
if (!in_array($method, self::$magic_actions)) {
|
||||
return parent::__call($method, $args);
|
||||
}
|
||||
|
||||
// Always initialize image.
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
}
|
||||
|
||||
try {
|
||||
$result = call_user_func_array([$this->image, $method], $args);
|
||||
|
||||
foreach ($this->alternatives as $ratio => $medium) {
|
||||
$args_copy = $args;
|
||||
|
||||
// regular image: resize 400x400 -> 200x200
|
||||
// --> @2x: resize 800x800->400x400
|
||||
if (isset(self::$magic_resize_actions[$method])) {
|
||||
foreach (self::$magic_resize_actions[$method] as $param) {
|
||||
if (isset($args_copy[$param])) {
|
||||
$args_copy[$param] = (int) $args_copy[$param] * $ratio;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
call_user_func_array([$medium, $method], $args_copy);
|
||||
}
|
||||
} catch (\BadFunctionCallException $e) {
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets medium image, resets image manipulation operations.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function image()
|
||||
{
|
||||
$locator = self::$grav['locator'];
|
||||
|
||||
$file = $this->get('filepath');
|
||||
$cacheDir = $locator->findResource('cache://images', true);
|
||||
|
||||
$this->image = ImageFile::open($file)
|
||||
->setCacheDir($cacheDir)
|
||||
->setActualCacheDir($cacheDir)
|
||||
->setPrettyName(basename($this->get('basename')));
|
||||
|
||||
$this->filter();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the image with cache.
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
protected function saveImage()
|
||||
{
|
||||
if (!$this->image) {
|
||||
return parent::path(false);
|
||||
}
|
||||
|
||||
if ($this->get('debug') && !$this->debug_watermarked) {
|
||||
$ratio = $this->get('ratio');
|
||||
if (!$ratio) {
|
||||
$ratio = 1;
|
||||
}
|
||||
|
||||
$locator = self::$grav['locator'];
|
||||
$overlay = $locator->findResource("system://assets/responsive-overlays/{$ratio}x.png") ?: $locator->findResource('system://assets/responsive-overlays/unknown.png');
|
||||
$this->image->merge(ImageFile::open($overlay));
|
||||
}
|
||||
|
||||
$result = $this->image->cacheFile($this->format, $this->quality);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter image by using user defined filter parameters.
|
||||
*
|
||||
* @param string $filter Filter to be used.
|
||||
*/
|
||||
public function filter($filter = 'image.filters.default')
|
||||
{
|
||||
$filters = (array) $this->get($filter, []);
|
||||
foreach ($filters as $params) {
|
||||
$params = (array) $params;
|
||||
$method = array_shift($params);
|
||||
$this->__call($method, $params);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
system/src/Grav/Common/Page/Medium/Link.php
Normal file
65
system/src/Grav/Common/Page/Medium/Link.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\GravTrait;
|
||||
|
||||
class Link implements RenderableInterface
|
||||
{
|
||||
use GravTrait;
|
||||
use ParsedownHtmlTrait;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [];
|
||||
protected $source;
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
* @param array $attributes
|
||||
* @param Medium $medium
|
||||
*/
|
||||
public function __construct(array $attributes, Medium $medium)
|
||||
{
|
||||
$this->attributes = $attributes;
|
||||
$this->source = $medium->reset()->thumbnail('auto')->display('thumbnail');
|
||||
$this->source->linked = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an element (is array) that can be rendered by the Parsedown engine
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
public function parsedownElement($title = null, $alt = null, $class = null, $reset = true)
|
||||
{
|
||||
$innerElement = $this->source->parsedownElement($title, $alt, $class, $reset);
|
||||
|
||||
return [
|
||||
'name' => 'a',
|
||||
'attributes' => $this->attributes,
|
||||
'handler' => is_string($innerElement) ? 'line' : 'element',
|
||||
'text' => $innerElement
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward the call to the source element
|
||||
*
|
||||
* @param string $method
|
||||
* @param mixed $args
|
||||
* @return $this|mixed
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
$this->source = call_user_func_array(array($this->source, $method), $args);
|
||||
|
||||
// Don't start nesting links, if user has multiple link calls in his
|
||||
// actions, we will drop the previous links.
|
||||
return $this->source instanceof LinkMedium ? $this->source : $this;
|
||||
}
|
||||
}
|
||||
415
system/src/Grav/Common/Page/Medium/Medium.php
Normal file
415
system/src/Grav/Common/Page/Medium/Medium.php
Normal file
@@ -0,0 +1,415 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
|
||||
/**
|
||||
* The Medium is a general class for multimedia objects in Grav pages, specific implementations will derive from
|
||||
*
|
||||
* @author Grav
|
||||
* @license MIT
|
||||
*
|
||||
*/
|
||||
class Medium extends Data implements RenderableInterface
|
||||
{
|
||||
use GravTrait;
|
||||
use ParsedownHtmlTrait;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $mode = 'source';
|
||||
|
||||
/**
|
||||
* @var Medium
|
||||
*/
|
||||
protected $_thumbnail = null;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $thumbnailTypes = [ 'page', 'default' ];
|
||||
|
||||
protected $thumbnailType = null;
|
||||
|
||||
/**
|
||||
* @var Medium[]
|
||||
*/
|
||||
protected $alternatives = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $styleAttributes = [];
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param array $items
|
||||
* @param Blueprint $blueprint
|
||||
*/
|
||||
public function __construct($items = [], Blueprint $blueprint = null)
|
||||
{
|
||||
parent::__construct($items, $blueprint);
|
||||
|
||||
if (self::getGrav()['config']->get('media.enable_media_timestamp', true)) {
|
||||
$this->querystring('&' . self::getGrav()['cache']->getKey());
|
||||
}
|
||||
|
||||
$this->def('mime', 'application/octet-stream');
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add meta file for the medium.
|
||||
*
|
||||
* @param $filepath
|
||||
*/
|
||||
public function addMetaFile($filepath)
|
||||
{
|
||||
$this->merge(CompiledYamlFile::instance($filepath)->content());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add alternative Medium to this Medium.
|
||||
*
|
||||
* @param $ratio
|
||||
* @param Medium $alternative
|
||||
*/
|
||||
public function addAlternative($ratio, Medium $alternative)
|
||||
{
|
||||
if (!is_numeric($ratio) || $ratio === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$alternative->set('ratio', $ratio);
|
||||
$this->alternatives[(float) $ratio] = $alternative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return string representation of the object (html).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->html();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return PATH to file.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string path to file
|
||||
*/
|
||||
public function path($reset = true)
|
||||
{
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return $this->get('filepath');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return URL to file.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function url($reset = true)
|
||||
{
|
||||
$output = preg_replace('|^' . GRAV_ROOT . '|', '', $this->get('filepath'));
|
||||
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return self::$grav['base_url'] . $output . $this->querystring() . $this->urlHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set querystring for the file's url
|
||||
*
|
||||
* @param string $hash
|
||||
* @param boolean $withHash
|
||||
* @return string
|
||||
*/
|
||||
public function querystring($querystring = null, $withQuestionmark = true)
|
||||
{
|
||||
if ($querystring) {
|
||||
$this->set('querystring', ltrim($querystring, '?&'));
|
||||
|
||||
foreach ($this->alternatives as $alt) {
|
||||
$alt->querystring($querystring, $withQuestionmark);
|
||||
}
|
||||
}
|
||||
|
||||
$querystring = $this->get('querystring', '');
|
||||
|
||||
if ($withQuestionmark && !empty($querystring)) {
|
||||
return '?' . $querystring;
|
||||
} else {
|
||||
return $querystring;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set hash for the file's url
|
||||
*
|
||||
* @param string $hash
|
||||
* @param boolean $withHash
|
||||
* @return string
|
||||
*/
|
||||
public function urlHash($hash = null, $withHash = true)
|
||||
{
|
||||
if ($hash) {
|
||||
$this->set('urlHash', ltrim($hash, '#'));
|
||||
}
|
||||
|
||||
$hash = $this->get('urlHash', '');
|
||||
|
||||
if ($withHash && !empty($hash)) {
|
||||
return '#' . $hash;
|
||||
} else {
|
||||
return $hash;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an element (is array) that can be rendered by the Parsedown engine
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
public function parsedownElement($title = null, $alt = null, $class = null, $reset = true)
|
||||
{
|
||||
$attributes = $this->attributes;
|
||||
|
||||
$style = '';
|
||||
foreach ($this->styleAttributes as $key => $value) {
|
||||
$style .= $key . ': ' . $value . ';';
|
||||
}
|
||||
$attributes['style'] = $style;
|
||||
|
||||
!empty($title) && empty($attributes['title']) && $attributes['title'] = $title;
|
||||
!empty($alt) && empty($attributes['alt']) && $attributes['alt'] = $alt;
|
||||
!empty($class) && empty($attributes['class']) && $attributes['class'] = $class;
|
||||
|
||||
switch ($this->mode) {
|
||||
case 'text':
|
||||
$element = $this->textParsedownElement($attributes, false);
|
||||
break;
|
||||
case 'thumbnail':
|
||||
$element = $this->getThumbnail()->sourceParsedownElement($attributes, false);
|
||||
break;
|
||||
case 'source':
|
||||
$element = $this->sourceParsedownElement($attributes, false);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
$this->display('source');
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsedown element for source display mode
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
protected function sourceParsedownElement(array $attributes, $reset = true)
|
||||
{
|
||||
return $this->textParsedownElement($attributes, $reset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsedown element for text display mode
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
protected function textParsedownElement(array $attributes, $reset = true)
|
||||
{
|
||||
$text = empty($attributes['title']) ? empty($attributes['alt']) ? $this->get('filename') : $attributes['alt'] : $attributes['title'];
|
||||
|
||||
$element = [
|
||||
'name' => 'p',
|
||||
'attributes' => $attributes,
|
||||
'text' => $text
|
||||
];
|
||||
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset medium.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->attributes = [];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch display mode.
|
||||
*
|
||||
* @param string $mode
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function display($mode = 'source')
|
||||
{
|
||||
if ($this->mode === $mode) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
$this->mode = $mode;
|
||||
|
||||
return $mode === 'thumbnail' ? $this->getThumbnail()->reset() : $this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch thumbnail.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function thumbnail($type = 'auto')
|
||||
{
|
||||
if ($type !== 'auto' && !in_array($type, $this->thumbnailTypes)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($this->thumbnailType !== $type) {
|
||||
$this->_thumbnail = null;
|
||||
}
|
||||
|
||||
$this->thumbnailType = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the current Medium into a Link
|
||||
*
|
||||
* @param boolean $reset
|
||||
* @param array $attributes
|
||||
* @return Link
|
||||
*/
|
||||
public function link($reset = true, array $attributes = [])
|
||||
{
|
||||
if ($this->mode !== 'source') {
|
||||
$this->display('source');
|
||||
}
|
||||
|
||||
foreach ($this->attributes as $key => $value) {
|
||||
empty($attributes['data-' . $key]) && $attributes['data-' . $key] = $value;
|
||||
}
|
||||
|
||||
empty($attributes['href']) && $attributes['href'] = $this->url();
|
||||
|
||||
return new Link($attributes, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the current Medium inta a Link with lightbox enabled
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @param boolean $reset
|
||||
* @return Link
|
||||
*/
|
||||
public function lightbox($width = null, $height = null, $reset = true)
|
||||
{
|
||||
$attributes = ['rel' => 'lightbox'];
|
||||
|
||||
if ($width && $height) {
|
||||
$attributes['data-width'] = $width;
|
||||
$attributes['data-height'] = $height;
|
||||
}
|
||||
|
||||
return $this->link($reset, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow any action to be called on this medium from twig or markdown
|
||||
*
|
||||
* @param string $method
|
||||
* @param mixed $args
|
||||
* @return $this
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
$qs = $method;
|
||||
if (count($args) > 1 || (count($args) == 1 && !empty($args[0]))) {
|
||||
$qs .= '=' . implode(',', array_map(function ($a) { return urlencode($a); }, $args));
|
||||
}
|
||||
|
||||
if (!empty($qs)) {
|
||||
$this->querystring($this->querystring(null, false) . '&' . $qs);
|
||||
}
|
||||
|
||||
self::$grav['debugger']->addMessage($this->querystring());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the thumbnail Medium object
|
||||
*
|
||||
* @return ThumbnailImageMedium
|
||||
*/
|
||||
protected function getThumbnail()
|
||||
{
|
||||
if (!$this->_thumbnail) {
|
||||
$types = $this->thumbnailTypes;
|
||||
|
||||
if ($this->thumbnailType !== 'auto') {
|
||||
array_unshift($types, $this->thumbnailType);
|
||||
}
|
||||
|
||||
foreach ($types as $type) {
|
||||
$thumb = $this->get('thumbnails.' . $type, false);
|
||||
|
||||
if ($thumb) {
|
||||
$thumb = $thumb instanceof ThumbnailMedium ? $thumb : MediumFactory::fromFile($thumb, ['type' => 'thumbnail']);
|
||||
$thumb->parent = $this;
|
||||
}
|
||||
|
||||
if ($thumb) {
|
||||
$this->_thumbnail = $thumb;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_thumbnail;
|
||||
}
|
||||
}
|
||||
145
system/src/Grav/Common/Page/Medium/MediumFactory.php
Normal file
145
system/src/Grav/Common/Page/Medium/MediumFactory.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
|
||||
/**
|
||||
* MediumFactory can be used to more easily create various Medium objects from files or arrays, it should
|
||||
* contain most logic for instantiating a Medium object.
|
||||
*
|
||||
* @author Grav
|
||||
* @license MIT
|
||||
*
|
||||
*/
|
||||
class MediumFactory
|
||||
{
|
||||
use GravTrait;
|
||||
|
||||
/**
|
||||
* Create Medium from a file
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $params
|
||||
* @return Medium
|
||||
*/
|
||||
public static function fromFile($file, array $params = [])
|
||||
{
|
||||
if (!file_exists($file)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$path = dirname($file);
|
||||
$filename = basename($file);
|
||||
$parts = explode('.', $filename);
|
||||
$ext = array_pop($parts);
|
||||
$basename = implode('.', $parts);
|
||||
|
||||
$config = self::getGrav()['config'];
|
||||
|
||||
$media_params = $config->get("media.".strtolower($ext));
|
||||
if (!$media_params) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$params += $media_params;
|
||||
|
||||
// Add default settings for undefined variables.
|
||||
$params += $config->get('media.defaults');
|
||||
$params += [
|
||||
'type' => 'file',
|
||||
'thumb' => 'media/thumb.png',
|
||||
'mime' => 'application/octet-stream',
|
||||
'filepath' => $file,
|
||||
'filename' => $filename,
|
||||
'basename' => $basename,
|
||||
'extension' => $ext,
|
||||
'path' => $path,
|
||||
'modified' => filemtime($file),
|
||||
'thumbnails' => []
|
||||
];
|
||||
|
||||
$locator = self::getGrav()['locator'];
|
||||
|
||||
$lookup = $locator->findResources('image://');
|
||||
foreach ($lookup as $lookupPath) {
|
||||
if (is_file($lookupPath . '/' . $params['thumb'])) {
|
||||
$params['thumbnails']['default'] = $lookupPath . '/' . $params['thumb'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return static::fromArray($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Medium from array of parameters
|
||||
*
|
||||
* @param array $items
|
||||
* @param Blueprint|null $blueprint
|
||||
* @return Medium
|
||||
*/
|
||||
public static function fromArray(array $items = [], Blueprint $blueprint = null)
|
||||
{
|
||||
$type = isset($items['type']) ? $items['type'] : null;
|
||||
|
||||
switch ($type) {
|
||||
case 'image':
|
||||
return new ImageMedium($items, $blueprint);
|
||||
break;
|
||||
case 'thumbnail':
|
||||
return new ThumbnailImageMedium($items, $blueprint);
|
||||
break;
|
||||
case 'animated':
|
||||
case 'vector':
|
||||
return new StaticImageMedium($items, $blueprint);
|
||||
break;
|
||||
case 'video':
|
||||
return new VideoMedium($items, $blueprint);
|
||||
break;
|
||||
default:
|
||||
return new Medium($items, $blueprint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ImageMedium by scaling another ImageMedium object.
|
||||
*
|
||||
* @param ImageMedium $medium
|
||||
* @param int $from
|
||||
* @param int $to
|
||||
* @return Medium
|
||||
*/
|
||||
public static function scaledFromMedium($medium, $from, $to)
|
||||
{
|
||||
if (! $medium instanceof ImageMedium) {
|
||||
return $medium;
|
||||
}
|
||||
|
||||
if ($to > $from) {
|
||||
return $medium;
|
||||
}
|
||||
|
||||
$ratio = $to / $from;
|
||||
$width = (int) ($medium->get('width') * $ratio);
|
||||
$height = (int) ($medium->get('height') * $ratio);
|
||||
|
||||
$basename = $medium->get('basename');
|
||||
$basename = str_replace('@'.$from.'x', '@'.$to.'x', $basename);
|
||||
|
||||
$debug = $medium->get('debug');
|
||||
$medium->set('debug', false);
|
||||
|
||||
$file = $medium->resize($width, $height)->path();
|
||||
|
||||
$medium->set('debug', $debug);
|
||||
|
||||
$size = filesize($file);
|
||||
|
||||
$medium = self::fromFile($file);
|
||||
$medium->set('size', $size);
|
||||
|
||||
return $medium;
|
||||
}
|
||||
}
|
||||
31
system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php
Normal file
31
system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\Markdown\Parsedown;
|
||||
|
||||
trait ParsedownHtmlTrait
|
||||
{
|
||||
/**
|
||||
* @var \Grav\Common\Markdown\Parsedown
|
||||
*/
|
||||
protected $parsedown = null;
|
||||
|
||||
/**
|
||||
* Return HTML markup from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $class
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function html($title = null, $alt = null, $class = null, $reset = true)
|
||||
{
|
||||
$element = $this->parsedownElement($title, $alt, $class, $reset);
|
||||
|
||||
if (!$this->parsedown) {
|
||||
$this->parsedown = new Parsedown(null);
|
||||
}
|
||||
|
||||
return $this->parsedown->elementToHtml($element);
|
||||
}
|
||||
}
|
||||
34
system/src/Grav/Common/Page/Medium/RenderableInterface.php
Normal file
34
system/src/Grav/Common/Page/Medium/RenderableInterface.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
/**
|
||||
* Renderable Medium objects can be rendered to HTML markup and Parsedown objects
|
||||
*
|
||||
* @author Grav
|
||||
* @license MIT
|
||||
*
|
||||
*/
|
||||
interface RenderableInterface
|
||||
{
|
||||
/**
|
||||
* Return HTML markup from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function html($title = null, $alt = null, $class = null, $reset = true);
|
||||
|
||||
/**
|
||||
* Return Parsedown Element from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function parsedownElement($title = null, $alt = null, $class = null, $reset = true);
|
||||
}
|
||||
28
system/src/Grav/Common/Page/Medium/StaticImageMedium.php
Normal file
28
system/src/Grav/Common/Page/Medium/StaticImageMedium.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
/**
|
||||
* The Image medium holds information related to an individual image. These are then stored in the Media object.
|
||||
*
|
||||
* @author Grav
|
||||
* @license MIT
|
||||
*
|
||||
*/
|
||||
class StaticImageMedium extends Medium
|
||||
{
|
||||
use StaticResizeTrait;
|
||||
|
||||
/**
|
||||
* Parsedown element for source display mode
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
protected function sourceParsedownElement(array $attributes, $reset = true)
|
||||
{
|
||||
empty($attributes['src']) && $attributes['src'] = $this->url($reset);
|
||||
|
||||
return [ 'name' => 'image', 'attributes' => $attributes ];
|
||||
}
|
||||
}
|
||||
20
system/src/Grav/Common/Page/Medium/StaticResizeTrait.php
Normal file
20
system/src/Grav/Common/Page/Medium/StaticResizeTrait.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
trait StaticResizeTrait
|
||||
{
|
||||
/**
|
||||
* Resize media by setting attributes
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @return Medium
|
||||
*/
|
||||
public function resize($width = null, $height = null)
|
||||
{
|
||||
$this->styleAttributes['width'] = $width . 'px';
|
||||
$this->styleAttributes['height'] = $height . 'px';
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
121
system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php
Normal file
121
system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
class ThumbnailImageMedium extends ImageMedium
|
||||
{
|
||||
/**
|
||||
* @var Medium
|
||||
*/
|
||||
public $parent = null;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
public $linked = false;
|
||||
|
||||
/**
|
||||
* Return srcset string for this Medium and its alternatives.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function srcset($reset = true)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an element (is array) that can be rendered by the Parsedown engine
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
public function parsedownElement($title = null, $alt = null, $class = null, $reset = true)
|
||||
{
|
||||
return $this->bubble('parsedownElement', [$title, $alt, $class, $reset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return HTML markup from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function html($title = null, $alt = null, $class = null, $reset = true)
|
||||
{
|
||||
return $this->bubble('html', [$title, $alt, $class, $reset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch display mode.
|
||||
*
|
||||
* @param string $mode
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function display($mode = 'source')
|
||||
{
|
||||
return $this->bubble('display', [$mode], false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch thumbnail.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function thumbnail($type = 'auto')
|
||||
{
|
||||
$this->bubble('thumbnail', [$type], false);
|
||||
return $this->bubble('getThumbnail', [], false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the current Medium into a Link
|
||||
*
|
||||
* @param boolean $reset
|
||||
* @param array $attributes
|
||||
* @return Link
|
||||
*/
|
||||
public function link($reset = true, array $attributes = [])
|
||||
{
|
||||
return $this->bubble('link', [$reset, $attributes], false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the current Medium inta a Link with lightbox enabled
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @param boolean $reset
|
||||
* @return Link
|
||||
*/
|
||||
public function lightbox($width = null, $height = null, $reset = true)
|
||||
{
|
||||
return $this->bubble('lightbox', [$width, $height, $reset], false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bubble a function call up to either the superclass function or the parent Medium instance
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @param boolean $testLinked
|
||||
* @return Medium
|
||||
*/
|
||||
protected function bubble($method, array $arguments = [], $testLinked = true)
|
||||
{
|
||||
if (!$testLinked || $this->linked) {
|
||||
return $this->parent ? call_user_func_array(array($this->parent, $method), $arguments) : $this;
|
||||
} else {
|
||||
return call_user_func_array(array($this, 'parent::' . $method), $arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
46
system/src/Grav/Common/Page/Medium/VideoMedium.php
Normal file
46
system/src/Grav/Common/Page/Medium/VideoMedium.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Data\Data;
|
||||
use Gregwar\Image\Image as ImageFile;
|
||||
|
||||
class VideoMedium extends Medium
|
||||
{
|
||||
use StaticResizeTrait;
|
||||
|
||||
/**
|
||||
* Parsedown element for source display mode
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
protected function sourceParsedownElement(array $attributes, $reset = true)
|
||||
{
|
||||
$location = $this->url($reset);
|
||||
|
||||
return [
|
||||
'name' => 'video',
|
||||
'text' => '<source src="' . $location . '">Your browser does not support the video tag.',
|
||||
'attributes' => $attributes
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset medium.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
parent::reset();
|
||||
|
||||
$this->attributes['controls'] = true;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -90,13 +90,11 @@ class Page
|
||||
|
||||
/**
|
||||
* Page Object Constructor
|
||||
*
|
||||
* @param array $array An array of existing page objects
|
||||
*/
|
||||
public function __construct($array = array())
|
||||
public function __construct()
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
$config = self::getGrav()['config'];
|
||||
|
||||
$this->routable = true;
|
||||
$this->taxonomy = array();
|
||||
@@ -110,7 +108,7 @@ class Page
|
||||
* @param \SplFileInfo $file The file information for the .md file that the page represents
|
||||
* @return void
|
||||
*/
|
||||
public function init($file)
|
||||
public function init(\SplFileInfo $file)
|
||||
{
|
||||
$this->filePath($file->getPathName());
|
||||
$this->modified($file->getMTime());
|
||||
@@ -123,20 +121,20 @@ class Page
|
||||
$this->modularTwig($this->slug[0] == '_');
|
||||
|
||||
// Handle publishing dates if no explict published option set
|
||||
if (self::$grav['config']->get('system.pages.publish_dates') && !isset($this->header->published)) {
|
||||
if (self::getGrav()['config']->get('system.pages.publish_dates') && !isset($this->header->published)) {
|
||||
// unpublish if required, if not clear cache right before page should be unpublished
|
||||
if ($this->unpublishDate()) {
|
||||
if ($this->unpublishDate() < time()) {
|
||||
$this->published(false);
|
||||
} else {
|
||||
$this->published();
|
||||
self::$grav['cache']->setLifeTime($this->unpublishDate());
|
||||
self::getGrav()['cache']->setLifeTime($this->unpublishDate());
|
||||
}
|
||||
}
|
||||
// publish if required, if not clear cache right before page is published
|
||||
if ($this->publishDate() != $this->modified() && $this->publishDate() > time()) {
|
||||
$this->published(false);
|
||||
self::$grav['cache']->setLifeTime($this->publishDate());
|
||||
self::getGrav()['cache']->setLifeTime($this->publishDate());
|
||||
}
|
||||
}
|
||||
$this->published();
|
||||
@@ -280,6 +278,17 @@ class Page
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify a header value directly
|
||||
*
|
||||
* @param $key
|
||||
* @param $value
|
||||
*/
|
||||
public function modifyHeader($key, $value)
|
||||
{
|
||||
$this->header->$key = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the summary.
|
||||
*
|
||||
@@ -288,30 +297,40 @@ class Page
|
||||
*/
|
||||
public function summary($size = null)
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::getGrav()['config'];
|
||||
$content = $this->content();
|
||||
|
||||
// Return summary based on settings in site config file
|
||||
if (!$config->get('site.summary.enabled', true)) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
// Get summary size from site config's file
|
||||
if (is_null($size)) {
|
||||
$size = $config->get('site.summary.size', null);
|
||||
}
|
||||
|
||||
// Return calculated summary based on summary divider's position
|
||||
if (!$size && isset($this->summary_size)) {
|
||||
$format = $config->get('site.summary.format', 'short');
|
||||
// Return entire page content on wrong/ unknown format
|
||||
if (!in_array($format, array('short', 'long'))) {
|
||||
return $content;
|
||||
} elseif (($format === 'short') && isset($this->summary_size)) {
|
||||
return substr($content, 0, $this->summary_size);
|
||||
}
|
||||
|
||||
// Return calculated summary based on setting in site config file
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
if (!$size && $config->get('site.summary.size')) {
|
||||
$size = $config->get('site.summary.size');
|
||||
}
|
||||
|
||||
// If the size is zero, return the entire page content
|
||||
if ($size === 0) {
|
||||
return $content;
|
||||
// Return calculated summary based on defaults
|
||||
if (!$size) {
|
||||
} elseif (!is_numeric($size) || ($size < 0)) {
|
||||
$size = 300;
|
||||
}
|
||||
|
||||
return Utils::truncateHTML($content, $size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets and Sets the content based on content portion of the .md file
|
||||
*
|
||||
@@ -336,13 +355,12 @@ class Page
|
||||
|
||||
// If no content, process it
|
||||
if ($this->content === null) {
|
||||
|
||||
// Get media
|
||||
$this->media();
|
||||
|
||||
// Load cached content
|
||||
/** @var Cache $cache */
|
||||
$cache = self::$grav['cache'];
|
||||
$cache = self::getGrav()['cache'];
|
||||
$cache_id = md5('page'.$this->id());
|
||||
$this->content = $cache->fetch($cache_id);
|
||||
|
||||
@@ -353,10 +371,9 @@ class Page
|
||||
$twig_already_processed = false;
|
||||
|
||||
// if no cached-content run everything
|
||||
if ($this->content == false) {
|
||||
|
||||
if ($this->content === false) {
|
||||
$this->content = $this->raw_content;
|
||||
self::$grav->fireEvent('onPageContentRaw', new Event(['page' => $this]));
|
||||
self::getGrav()->fireEvent('onPageContentRaw', new Event(['page' => $this]));
|
||||
|
||||
if ($twig_first) {
|
||||
if ($process_twig) {
|
||||
@@ -393,10 +410,11 @@ class Page
|
||||
}
|
||||
|
||||
// Handle summary divider
|
||||
$divider_pos = strpos($this->content, '<p>'.SUMMARY_DELIMITER.'</p>');
|
||||
$delimiter = self::getGrav()['config']->get('site.summary.delimiter', '===');
|
||||
$divider_pos = strpos($this->content, "<p>{$delimiter}</p>");
|
||||
if ($divider_pos !== false) {
|
||||
$this->summary_size = $divider_pos;
|
||||
$this->content = str_replace('<p>'.SUMMARY_DELIMITER.'</p>', '', $this->content);
|
||||
$this->content = str_replace("<p>{$delimiter}</p>", '', $this->content);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -410,7 +428,7 @@ class Page
|
||||
protected function processMarkdown()
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
$config = self::getGrav()['config'];
|
||||
|
||||
$defaults = (array) $config->get('system.pages.markdown');
|
||||
if (isset($this->header()->markdown)) {
|
||||
@@ -419,7 +437,7 @@ class Page
|
||||
|
||||
// pages.markdown_extra is deprecated, but still check it...
|
||||
if (isset($this->markdown_extra) || $config->get('system.pages.markdown_extra') !== null) {
|
||||
$defaults['extra'] = $this->markdown_extra;
|
||||
$defaults['extra'] = $this->markdown_extra ?: $config->get('system.pages.markdown_extra');
|
||||
}
|
||||
|
||||
// Initialize the preferred variant of Parsedown
|
||||
@@ -429,11 +447,6 @@ class Page
|
||||
$parsedown = new Parsedown($this);
|
||||
}
|
||||
|
||||
$parsedown->setBreaksEnabled($defaults['auto_line_breaks']);
|
||||
$parsedown->setUrlsLinked($defaults['auto_url_links']);
|
||||
$parsedown->setMarkupEscaped($defaults['escape_markup']);
|
||||
$parsedown->setSpecialChars($defaults['special_chars']);
|
||||
|
||||
$this->content = $parsedown->text($this->content);
|
||||
}
|
||||
|
||||
@@ -443,7 +456,7 @@ class Page
|
||||
*/
|
||||
private function processTwig()
|
||||
{
|
||||
$twig = self::$grav['twig'];
|
||||
$twig = self::getGrav()['twig'];
|
||||
$this->content = $twig->processPage($this, $this->content);
|
||||
}
|
||||
|
||||
@@ -452,10 +465,10 @@ class Page
|
||||
*/
|
||||
private function cachePageContent()
|
||||
{
|
||||
$cache = self::$grav['cache'];
|
||||
$cache = self::getGrav()['cache'];
|
||||
$cache_id = md5('page'.$this->id());
|
||||
|
||||
self::$grav->fireEvent('onPageContentProcessed', new Event(['page' => $this]));
|
||||
self::getGrav()->fireEvent('onPageContentProcessed', new Event(['page' => $this]));
|
||||
$cache->save($cache_id, $this->content);
|
||||
}
|
||||
|
||||
@@ -630,7 +643,7 @@ class Page
|
||||
public function blueprints()
|
||||
{
|
||||
/** @var Pages $pages */
|
||||
$pages = self::$grav['pages'];
|
||||
$pages = self::getGrav()['pages'];
|
||||
|
||||
return $pages->blueprints($this->template());
|
||||
}
|
||||
@@ -709,7 +722,7 @@ class Page
|
||||
public function media($var = null)
|
||||
{
|
||||
/** @var Cache $cache */
|
||||
$cache = self::$grav['cache'];
|
||||
$cache = self::getGrav()['cache'];
|
||||
|
||||
if ($var) {
|
||||
$this->media = $var;
|
||||
@@ -927,7 +940,6 @@ class Page
|
||||
|
||||
// if not metadata yet, process it.
|
||||
if (null === $this->metadata) {
|
||||
|
||||
$header_tag_http_equivs = ['content-type', 'default-style', 'refresh'];
|
||||
$this->metadata = array();
|
||||
$page_header = $this->header;
|
||||
@@ -938,7 +950,7 @@ class Page
|
||||
// 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');
|
||||
$defaults = (array) self::getGrav()['config']->get('site.metadata');
|
||||
|
||||
if (isset($page_header->metadata)) {
|
||||
$page_header->metadata = array_merge($defaults, $page_header->metadata);
|
||||
@@ -948,7 +960,6 @@ class Page
|
||||
|
||||
// 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) {
|
||||
@@ -1041,10 +1052,10 @@ class Page
|
||||
public function url($include_host = false)
|
||||
{
|
||||
/** @var Pages $pages */
|
||||
$pages = self::$grav['pages'];
|
||||
$pages = self::getGrav()['pages'];
|
||||
|
||||
/** @var Uri $uri */
|
||||
$uri = self::$grav['uri'];
|
||||
$uri = self::getGrav()['uri'];
|
||||
|
||||
$rootUrl = $uri->rootUrl($include_host) . $pages->base();
|
||||
$url = $rootUrl.'/'.trim($this->route(), '/');
|
||||
@@ -1116,7 +1127,7 @@ class Page
|
||||
// Path to the page.
|
||||
$this->path = dirname(dirname($var));
|
||||
}
|
||||
return $this->name ? $this->path . '/' . $this->folder . '/' . $this->name : null;
|
||||
return $this->path . '/' . $this->folder . '/' . ($this->name ?: '');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1243,7 +1254,7 @@ class Page
|
||||
}
|
||||
if (empty($this->max_count)) {
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
$config = self::getGrav()['config'];
|
||||
$this->max_count = (int) $config->get('system.pages.list.count');
|
||||
}
|
||||
return $this->max_count;
|
||||
@@ -1318,7 +1329,7 @@ class Page
|
||||
}
|
||||
|
||||
/** @var Pages $pages */
|
||||
$pages = self::$grav['pages'];
|
||||
$pages = self::getGrav()['pages'];
|
||||
|
||||
return $pages->get($this->parent);
|
||||
}
|
||||
@@ -1331,7 +1342,7 @@ class Page
|
||||
public function children()
|
||||
{
|
||||
/** @var Pages $pages */
|
||||
$pages = self::$grav['pages'];
|
||||
$pages = self::getGrav()['pages'];
|
||||
return $pages->children($this->path());
|
||||
}
|
||||
|
||||
@@ -1398,7 +1409,7 @@ class Page
|
||||
public function active()
|
||||
{
|
||||
/** @var Uri $uri */
|
||||
$uri = self::$grav['uri'];
|
||||
$uri = self::getGrav()['uri'];
|
||||
if ($this->url() == $uri->url()) {
|
||||
return true;
|
||||
}
|
||||
@@ -1414,8 +1425,8 @@ class Page
|
||||
public function activeChild()
|
||||
{
|
||||
/** @var Uri $uri */
|
||||
$uri = self::$grav['uri'];
|
||||
$config = self::$grav['config'];
|
||||
$uri = self::getGrav()['uri'];
|
||||
$config = self::getGrav()['config'];
|
||||
|
||||
// Special check when item is home
|
||||
if ($this->home()) {
|
||||
@@ -1450,7 +1461,7 @@ class Page
|
||||
*/
|
||||
public function root()
|
||||
{
|
||||
if (!$this->parent && !$this->name and !$this->visible) {
|
||||
if (!$this->parent && !$this->name && !$this->visible) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -1469,7 +1480,7 @@ class Page
|
||||
public function find($url, $all = false)
|
||||
{
|
||||
/** @var Pages $pages */
|
||||
$pages = self::$grav['pages'];
|
||||
$pages = self::getGrav()['pages'];
|
||||
return $pages->dispatch($url, $all);
|
||||
}
|
||||
|
||||
@@ -1501,9 +1512,9 @@ class Page
|
||||
|
||||
// TODO: MOVE THIS INTO SOMEWHERE ELSE?
|
||||
/** @var Uri $uri */
|
||||
$uri = self::$grav['uri'];
|
||||
$uri = self::getGrav()['uri'];
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
$config = self::getGrav()['config'];
|
||||
|
||||
foreach ((array) $config->get('site.taxonomies') as $taxonomy) {
|
||||
if ($uri->param($taxonomy)) {
|
||||
@@ -1539,7 +1550,7 @@ class Page
|
||||
}
|
||||
|
||||
/** @var Grav $grav */
|
||||
$grav = self::$grav['grav'];
|
||||
$grav = self::getGrav()['grav'];
|
||||
|
||||
// New Custom event to handle things like pagination.
|
||||
$grav->fireEvent('onCollectionProcessed', new Event(['collection' => $collection]));
|
||||
@@ -1619,7 +1630,7 @@ class Page
|
||||
// @taxonomy: { category: [ blog, featured ], level: 1 }
|
||||
|
||||
/** @var Taxonomy $taxonomy_map */
|
||||
$taxonomy_map = self::$grav['taxonomy'];
|
||||
$taxonomy_map = self::getGrav()['taxonomy'];
|
||||
|
||||
if (!empty($parts)) {
|
||||
$params = [implode('.', $parts) => $params];
|
||||
@@ -1695,7 +1706,7 @@ class Page
|
||||
// Do reordering.
|
||||
if ($reorder && $this->order() != $this->_original->order()) {
|
||||
/** @var Pages $pages */
|
||||
$pages = self::$grav['pages'];
|
||||
$pages = self::getGrav()['pages'];
|
||||
|
||||
$parent = $this->parent();
|
||||
|
||||
|
||||
@@ -523,20 +523,27 @@ class Pages
|
||||
|
||||
/** @var \DirectoryIterator $file */
|
||||
foreach ($iterator as $file) {
|
||||
if ($file->isDot()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$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]));
|
||||
if ($file->isFile()) {
|
||||
// Update the last modified if it's newer than already found
|
||||
if ($file->getBasename() !== '.DS_Store' && ($modified = $file->getMTime()) > $last_modified) {
|
||||
$last_modified = $modified;
|
||||
}
|
||||
|
||||
} elseif ($file->isDir() && !$file->isDot()) {
|
||||
if (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]));
|
||||
}
|
||||
}
|
||||
} elseif ($file->isDir()) {
|
||||
if (!$page->path()) {
|
||||
$page->path($file->getPath());
|
||||
}
|
||||
@@ -554,11 +561,6 @@ class Pages
|
||||
$this->grav->fireEvent('onFolderProcessed', new Event(['page' => $page]));
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -587,7 +589,6 @@ class Pages
|
||||
// Build routes and taxonomy map.
|
||||
/** @var $page Page */
|
||||
foreach ($this->instances as $page) {
|
||||
|
||||
$parent = $page->parent();
|
||||
|
||||
if ($parent) {
|
||||
@@ -637,7 +638,6 @@ class Pages
|
||||
}
|
||||
|
||||
foreach ($pages as $key => $info) {
|
||||
|
||||
$child = isset($this->instances[$key]) ? $this->instances[$key] : null;
|
||||
if (!$child) {
|
||||
throw new \RuntimeException("Page does not exist: {$key}");
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Page\Page;
|
||||
use Grav\Common\Config\Config;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
|
||||
@@ -24,6 +26,10 @@ class Plugin implements EventSubscriberInterface
|
||||
protected $config;
|
||||
|
||||
protected $active = true;
|
||||
/**
|
||||
* @var \Grav\Common\string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* By default assign all methods as listeners using the default priority.
|
||||
@@ -47,13 +53,15 @@ class Plugin implements EventSubscriberInterface
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Grav $grav
|
||||
* @param Config $config
|
||||
* @param string $name
|
||||
* @param Grav $grav
|
||||
* @param Config $config
|
||||
*/
|
||||
public function __construct(Grav $grav, Config $config)
|
||||
public function __construct($name, Grav $grav, Config $config)
|
||||
{
|
||||
$this->grav = $grav;
|
||||
$this->config = $config;
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function isAdmin()
|
||||
@@ -84,4 +92,70 @@ class Plugin implements EventSubscriberInterface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $events
|
||||
*/
|
||||
protected function disable(array $events)
|
||||
{
|
||||
/** @var EventDispatcher $dispatcher */
|
||||
$dispatcher = $this->grav['events'];
|
||||
|
||||
foreach ($events as $eventName => $params) {
|
||||
if (is_string($params)) {
|
||||
$dispatcher->removeListener($eventName, array($this, $params));
|
||||
} elseif (is_string($params[0])) {
|
||||
$dispatcher->removeListener($eventName, array($this, $params[0]));
|
||||
} else {
|
||||
foreach ($params as $listener) {
|
||||
$dispatcher->removeListener($eventName, array($this, $listener[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge global and page configurations.
|
||||
*
|
||||
* @param Page $page The page to merge the configurations with the
|
||||
* plugin settings.
|
||||
*
|
||||
* @param bool $deep Should you use deep or shallow merging
|
||||
*
|
||||
* @return \Grav\Common\Data\Data
|
||||
*/
|
||||
protected function mergeConfig(Page $page, $deep = false)
|
||||
{
|
||||
$class_name = $this->name;
|
||||
$class_name_merged = $class_name . '.merged';
|
||||
$defaults = $this->config->get('plugins.' . $class_name, array());
|
||||
$header = array();
|
||||
|
||||
if (isset($page->header()->$class_name_merged)) {
|
||||
$merged = $page->header()->$class_name_merged;
|
||||
if (count($merged) > 0) {
|
||||
return $merged;
|
||||
} else {
|
||||
return new Data($defaults);
|
||||
}
|
||||
}
|
||||
|
||||
// Get default plugin configurations and retrieve page header configuration
|
||||
if (isset($page->header()->$class_name)) {
|
||||
if ($deep) {
|
||||
$header = array_replace_recursive($defaults, $page->header()->$class_name);
|
||||
} else {
|
||||
$header = array_merge($defaults, $page->header()->$class_name);
|
||||
}
|
||||
} else {
|
||||
$header = $defaults;
|
||||
}
|
||||
|
||||
// Create new config object and set it on the page object so it's cached for next time
|
||||
$config = new Data($header);
|
||||
$page->modifyHeader($class_name_merged, $config);
|
||||
|
||||
// Return configurations as a new data config class
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,14 +47,15 @@ class Plugins extends Iterator
|
||||
|
||||
$filePath = $this->grav['locator']('plugins://' . $plugin . DS . $plugin . PLUGIN_EXT);
|
||||
if (!is_file($filePath)) {
|
||||
throw new \RuntimeException(sprintf("Plugin '%s' enabled but not found! Try clearing cache with `bin/grav clear-cache`", $plugin));
|
||||
$this->grav['log']->addWarning(sprintf("Plugin '%s' enabled but not found! Try clearing cache with `bin/grav clear-cache`", $plugin));
|
||||
continue;
|
||||
}
|
||||
|
||||
require_once $filePath;
|
||||
|
||||
$pluginClassFormat = [
|
||||
'Grav\\Plugin\\'.ucfirst($plugin).'Plugin',
|
||||
'Grav\\Plugin\\'.str_replace(['_','-'], '', $plugin).'Plugin'
|
||||
'Grav\\Plugin\\'.Inflector::camelize($plugin).'Plugin'
|
||||
];
|
||||
$pluginClassName = false;
|
||||
|
||||
@@ -69,7 +70,7 @@ class Plugins extends Iterator
|
||||
throw new \RuntimeException(sprintf("Plugin '%s' class not found! Try reinstalling this plugin.", $plugin));
|
||||
}
|
||||
|
||||
$instance = new $pluginClassName($this->grav, $config);
|
||||
$instance = new $pluginClassName($plugin, $this->grav, $config);
|
||||
if ($instance instanceof EventSubscriberInterface) {
|
||||
$events->addSubscriber($instance);
|
||||
}
|
||||
@@ -93,7 +94,8 @@ class Plugins extends Iterator
|
||||
public static function all()
|
||||
{
|
||||
$list = array();
|
||||
$iterator = new \DirectoryIterator('plugins://');
|
||||
$locator = Grav::instance()['locator'];
|
||||
$iterator = new \DirectoryIterator($locator->findResource('plugins://', false));
|
||||
|
||||
/** @var \DirectoryIterator $directory */
|
||||
foreach ($iterator as $directory) {
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
namespace Grav\Common\Service;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use RocketTheme\Toolbox\Blueprints\Blueprints;
|
||||
|
||||
@@ -7,7 +7,6 @@ use Pimple\ServiceProviderInterface;
|
||||
use Whoops\Handler\JsonResponseHandler;
|
||||
use Whoops\Handler\PrettyPageHandler;
|
||||
use Whoops\Handler\PlainTextHandler;
|
||||
use Whoops\Run;
|
||||
|
||||
class ErrorServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
@@ -30,7 +29,7 @@ class ErrorServiceProvider implements ServiceProviderInterface
|
||||
$errors->pushHandler($json_page, 'json');
|
||||
|
||||
$logger = $container['log'];
|
||||
$errors->pushHandler(function ($exception, $inspector, $run) use($logger) {
|
||||
$errors->pushHandler(function (\Exception $exception, $inspector, $run) use ($logger) {
|
||||
$logger->addCritical($exception->getMessage(). ' - Trace: '. $exception->getTraceAsString());
|
||||
}, 'log');
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use \Monolog\Logger;
|
||||
use \Monolog\Handler\StreamHandler;
|
||||
use \Monolog\Handler\RotatingFileHandler;
|
||||
|
||||
class LoggerServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
@@ -13,9 +12,7 @@ class LoggerServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
$log = new Logger('grav');
|
||||
$log_file = LOG_DIR.'grav.log';
|
||||
$log_days = 14;
|
||||
|
||||
// $log->pushHandler(new RotatingFileHandler($log_file, $log_days, Logger::WARNING));
|
||||
$log->pushHandler(new StreamHandler($log_file, Logger::WARNING));
|
||||
|
||||
$container['log'] = $log;
|
||||
|
||||
@@ -18,6 +18,6 @@ class Theme extends Plugin
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
parent::__construct($grav, $config);
|
||||
parent::__construct($name, $grav, $config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,8 @@ class Themes extends Iterator
|
||||
public function all()
|
||||
{
|
||||
$list = array();
|
||||
$iterator = new \DirectoryIterator('themes://');
|
||||
$locator = Grav::instance()['locator'];
|
||||
$iterator = new \DirectoryIterator($locator->findResource('themes://', false));
|
||||
|
||||
/** @var \DirectoryIterator $directory */
|
||||
foreach ($iterator as $directory) {
|
||||
@@ -145,10 +146,19 @@ class Themes extends Iterator
|
||||
$class = include $file;
|
||||
|
||||
if (!is_object($class)) {
|
||||
$className = '\\Grav\\Theme\\' . ucfirst($name);
|
||||
|
||||
if (class_exists($className)) {
|
||||
$class = new $className($grav, $config, $name);
|
||||
$themeClassFormat = [
|
||||
'Grav\\Theme\\'.ucfirst($name),
|
||||
'Grav\\Theme\\'.Inflector::camelize($name)
|
||||
];
|
||||
$themeClassName = false;
|
||||
|
||||
foreach ($themeClassFormat as $themeClass) {
|
||||
if (class_exists($themeClass)) {
|
||||
$themeClassName = $themeClass;
|
||||
$class = new $themeClassName($grav, $config, $name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif (!$locator('theme://') && !defined('GRAV_CLI')) {
|
||||
|
||||
@@ -219,6 +219,34 @@ class Twig
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a Twig template directly by using a template name
|
||||
* and optional array of variables
|
||||
*
|
||||
* @param string $template template to render with
|
||||
* @param array $vars Optional variables
|
||||
* @return string
|
||||
*/
|
||||
public function processTemplate($template, $vars = array())
|
||||
{
|
||||
// override the twig header vars for local resolution
|
||||
$this->grav->fireEvent('onTwigTemplateVariables');
|
||||
$vars += $this->twig_vars;
|
||||
|
||||
try {
|
||||
$output = $this->twig->render($template, $vars);
|
||||
} catch (\Twig_Error_Loader $e) {
|
||||
throw new \RuntimeException($e->getRawMessage(), 404, $e);
|
||||
}
|
||||
|
||||
return $output;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process a Twig template directly by using a Twig string
|
||||
* and optional array of variables
|
||||
*
|
||||
* @param string $string string to render.
|
||||
* @param array $vars Optional variables
|
||||
* @return string
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Markdown\Parsedown;
|
||||
use Grav\Common\Markdown\ParsedownExtra;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
|
||||
@@ -49,6 +51,8 @@ class TwigExtension extends \Twig_Extension
|
||||
new \Twig_SimpleFilter('ksort', [$this,'ksortFilter']),
|
||||
new \Twig_SimpleFilter('contains', [$this, 'containsFilter']),
|
||||
new \Twig_SimpleFilter('nicetime', [$this, 'nicetimeFilter']),
|
||||
new \Twig_SimpleFilter('absolute_url', [$this, 'absoluteUrlFilter']),
|
||||
new \Twig_SimpleFilter('markdown', [$this, 'markdownFilter'])
|
||||
];
|
||||
}
|
||||
|
||||
@@ -64,7 +68,8 @@ class TwigExtension extends \Twig_Extension
|
||||
new \Twig_SimpleFunction('url', [$this, 'urlFunc']),
|
||||
new \Twig_SimpleFunction('dump', [$this, 'dump'], ['needs_context' => true, 'needs_environment' => true]),
|
||||
new \Twig_SimpleFunction('debug', [$this, 'dump'], ['needs_context' => true, 'needs_environment' => true]),
|
||||
new \Twig_SimpleFunction('gist', [$this, 'gistFunc'])
|
||||
new \Twig_SimpleFunction('gist', [$this, 'gistFunc']),
|
||||
new \Twig_simpleFunction('random_string', [$this, 'randomStringFunc']),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -90,7 +95,8 @@ class TwigExtension extends \Twig_Extension
|
||||
public function safeEmailFilter($str)
|
||||
{
|
||||
$email = '';
|
||||
for ($i = 0; $i < strlen($str); $i++) {
|
||||
$str_len = strlen($str);
|
||||
for ($i = 0; $i < $str_len; $i++) {
|
||||
$email .= "&#" . ord($str[$i]);
|
||||
}
|
||||
return $email;
|
||||
@@ -164,8 +170,8 @@ class TwigExtension extends \Twig_Extension
|
||||
* {{ 'send_email'|camelize }} => SendEmail
|
||||
* {{ 'CamelCased'|underscorize }} => camel_cased
|
||||
* {{ 'Something Text'|hyphenize }} => something-text
|
||||
* {{ 'something text to read'|humanize }} => "Something text to read"
|
||||
* {{ '181'|monthize}} => 6
|
||||
* {{ 'something_text_to_read'|humanize }} => "Something text to read"
|
||||
* {{ '181'|monthize }} => 6
|
||||
* {{ '10'|ordinalize }} => 10th
|
||||
*
|
||||
* @param string $action
|
||||
@@ -316,6 +322,31 @@ class TwigExtension extends \Twig_Extension
|
||||
return "$difference $periods[$j] {$tense}";
|
||||
}
|
||||
|
||||
public function absoluteUrlFilter($string)
|
||||
{
|
||||
$url = $this->grav['uri']->base();
|
||||
$string = preg_replace('/((?:href|src) *= *[\'"](?!(http|ftp)))/i', "$1$url", $string);
|
||||
return $string;
|
||||
|
||||
}
|
||||
|
||||
public function markdownFilter($string)
|
||||
{
|
||||
$page = $this->grav['page'];
|
||||
$defaults = $this->grav['config']->get('system.pages.markdown');
|
||||
|
||||
// Initialize the preferred variant of Parsedown
|
||||
if ($defaults['extra']) {
|
||||
$parsedown = new ParsedownExtra($page);
|
||||
} else {
|
||||
$parsedown = new Parsedown($page);
|
||||
}
|
||||
|
||||
$string = $parsedown->text($string);
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeat given string x times.
|
||||
*
|
||||
@@ -404,4 +435,16 @@ class TwigExtension extends \Twig_Extension
|
||||
{
|
||||
return '<script src="https://gist.github.com/'.$id.'.js"></script>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random string
|
||||
*
|
||||
* @param int $count
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function randomStringFunc($count = 5)
|
||||
{
|
||||
return Utils::generateRandomString($count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ class Uri
|
||||
{
|
||||
public $url;
|
||||
|
||||
protected $basename;
|
||||
protected $base;
|
||||
protected $root;
|
||||
protected $bits;
|
||||
@@ -65,8 +66,6 @@ class Uri
|
||||
$this->root = $base . $root_path;
|
||||
$this->url = $base . $uri;
|
||||
|
||||
$this->init();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,29 +73,24 @@ class Uri
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$config = Grav::instance()['config'];
|
||||
|
||||
// get any params and remove them
|
||||
$uri = str_replace($this->root, '', $this->url);
|
||||
|
||||
$this->params = array();
|
||||
if (strpos($uri, ':')) {
|
||||
$bits = explode('/', $uri);
|
||||
$path = array();
|
||||
foreach ($bits as $bit) {
|
||||
if (strpos($bit, ':') !== false) {
|
||||
$param = explode(':', $bit);
|
||||
if (count($param) == 2) {
|
||||
$this->params[$param[0]] = str_replace('%7C', '/', filter_var($param[1], FILTER_SANITIZE_STRING));
|
||||
}
|
||||
} else {
|
||||
$path[] = $bit;
|
||||
}
|
||||
}
|
||||
$uri = implode('/', $path);
|
||||
}
|
||||
// reset params
|
||||
$this->params = [];
|
||||
|
||||
// process params
|
||||
$uri = $this->processParams($uri, $config->get('system.param_sep'));
|
||||
|
||||
// remove the extension if there is one set
|
||||
$parts = pathinfo($uri);
|
||||
if (preg_match("/\.(txt|xml|html|json|rss|atom)$/", $parts['basename'])) {
|
||||
|
||||
// set the original basename
|
||||
$this->basename = $parts['basename'];
|
||||
|
||||
if (preg_match("/\.(".$config->get('system.pages.types').")$/", $parts['basename'])) {
|
||||
$uri = rtrim($parts['dirname'], '/').'/'.$parts['filename'];
|
||||
$this->extension = $parts['extension'];
|
||||
}
|
||||
@@ -120,6 +114,34 @@ class Uri
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process any params based in this URL, supports any valid delimiter
|
||||
*
|
||||
* @param $uri
|
||||
* @param string $delimiter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function processParams($uri, $delimiter = ':')
|
||||
{
|
||||
if (strpos($uri, $delimiter) !== false) {
|
||||
$bits = explode('/', $uri);
|
||||
$path = array();
|
||||
foreach ($bits as $bit) {
|
||||
if (strpos($bit, $delimiter) !== false) {
|
||||
$param = explode($delimiter, $bit);
|
||||
if (count($param) == 2) {
|
||||
$this->params[$param[0]] = str_replace(urlencode($delimiter), '/', filter_var($param[1], FILTER_SANITIZE_STRING));
|
||||
}
|
||||
} else {
|
||||
$path[] = $bit;
|
||||
}
|
||||
}
|
||||
$uri = implode('/', $path);
|
||||
}
|
||||
return $uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return URI path.
|
||||
*
|
||||
@@ -174,15 +196,17 @@ class Uri
|
||||
*/
|
||||
public function params($id = null)
|
||||
{
|
||||
$config = Grav::instance()['config'];
|
||||
|
||||
$params = null;
|
||||
if ($id === null) {
|
||||
$output = array();
|
||||
foreach ($this->params as $key => $value) {
|
||||
$output[] = $key . ':' . $value;
|
||||
$output[] = $key . $config->get('system.param_sep') . $value;
|
||||
$params = '/'.implode('/', $output);
|
||||
}
|
||||
} elseif (isset($this->params[$id])) {
|
||||
$params = "/{$id}:".$this->params[$id];
|
||||
$params = "/{$id}". $config->get('system.param_sep') . $this->params[$id];
|
||||
}
|
||||
|
||||
return $params;
|
||||
@@ -232,6 +256,8 @@ class Uri
|
||||
/**
|
||||
* Return the Extension of the URI
|
||||
*
|
||||
* @param null $default
|
||||
*
|
||||
* @return String The extension of the URI
|
||||
*/
|
||||
public function extension($default = null)
|
||||
@@ -262,6 +288,17 @@ class Uri
|
||||
return $this->host();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the basename of the URI
|
||||
*
|
||||
* @return String The basename of the URI
|
||||
*/
|
||||
public function basename()
|
||||
{
|
||||
return $this->basename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base of the URI
|
||||
*
|
||||
@@ -358,6 +395,20 @@ class Uri
|
||||
return $ipaddress;
|
||||
|
||||
}
|
||||
/**
|
||||
* Is this an external URL? if it starts with `http` then yes, else false
|
||||
*
|
||||
* @param string $url the URL in question
|
||||
* @return boolean is eternal state
|
||||
*/
|
||||
public function isExternal($url)
|
||||
{
|
||||
if (Utils::startsWith($url, 'http')) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The opposite of built-in PHP method parse_url()
|
||||
@@ -365,7 +416,7 @@ class Uri
|
||||
* @param $parsed_url
|
||||
* @return string
|
||||
*/
|
||||
public static function build_url($parsed_url)
|
||||
public static function buildUrl($parsed_url)
|
||||
{
|
||||
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
|
||||
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
|
||||
|
||||
@@ -30,7 +30,6 @@ abstract class Authentication
|
||||
public static function verify($password, $hash)
|
||||
{
|
||||
// Always accept plaintext passwords (needs an update).
|
||||
// FIXME: not safe to do this...
|
||||
if ($password && $password == $hash) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ class User extends Data
|
||||
*/
|
||||
public static function load($username)
|
||||
{
|
||||
$locator = self::$grav['locator'];
|
||||
$locator = self::getGrav()['locator'];
|
||||
|
||||
// FIXME: validate directory name
|
||||
// TODO: validate directory name
|
||||
$blueprints = new Blueprints('blueprints://user');
|
||||
$blueprint = $blueprints->get('account');
|
||||
$file_path = $locator->findResource('account://' . $username . YAML_EXT);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
|
||||
/**
|
||||
* Misc utilities.
|
||||
*
|
||||
@@ -8,6 +10,8 @@ namespace Grav\Common;
|
||||
*/
|
||||
abstract class Utils
|
||||
{
|
||||
use GravTrait;
|
||||
|
||||
/**
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
@@ -28,6 +32,16 @@ abstract class Utils
|
||||
return $needle === '' || substr($haystack, -strlen($needle)) === $needle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
* @return bool
|
||||
*/
|
||||
public static function contains($haystack, $needle)
|
||||
{
|
||||
return $needle === '' || strpos($haystack, $needle) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two objects into one.
|
||||
*
|
||||
@@ -41,12 +55,13 @@ abstract class Utils
|
||||
}
|
||||
|
||||
/**
|
||||
* Recurseive remove a directory - DANGEROUS! USE WITH CARE!!!!
|
||||
* Recursive remove a directory - DANGEROUS! USE WITH CARE!!!!
|
||||
*
|
||||
* @param $dir
|
||||
* @return bool
|
||||
*/
|
||||
public static function rrmdir($dir) {
|
||||
public static function rrmdir($dir)
|
||||
{
|
||||
$files = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::CHILD_FIRST
|
||||
@@ -55,15 +70,59 @@ abstract class Utils
|
||||
/** @var \DirectoryIterator $fileinfo */
|
||||
foreach ($files as $fileinfo) {
|
||||
if ($fileinfo->isDir()) {
|
||||
if (false === rmdir($fileinfo->getRealPath())) return false;
|
||||
if (false === rmdir($fileinfo->getRealPath())) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (false === unlink($fileinfo->getRealPath())) return false;
|
||||
if (false === unlink($fileinfo->getRealPath())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rmdir($dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive copy of one directory to another
|
||||
*
|
||||
* @param $src
|
||||
* @param $dest
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function rcopy($src, $dest)
|
||||
{
|
||||
|
||||
// If the src is not a directory do a simple file copy
|
||||
if (!is_dir($src)) {
|
||||
copy($src, $dest);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the destination directory does not exist create it
|
||||
if (!is_dir($dest)) {
|
||||
if (!mkdir($dest)) {
|
||||
// If the destination directory could not be created stop processing
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Open the source directory to read in files
|
||||
$i = new \DirectoryIterator($src);
|
||||
/** @var \DirectoryIterator $f */
|
||||
foreach ($i as $f) {
|
||||
if ($f->isFile()) {
|
||||
copy($f->getRealPath(), "$dest/" . $f->getFilename());
|
||||
} else {
|
||||
if (!$f->isDot() && $f->isDir()) {
|
||||
static::rcopy($f->getRealPath(), "$dest/$f");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate HTML by text length.
|
||||
*
|
||||
@@ -74,7 +133,8 @@ abstract class Utils
|
||||
* @param bool $considerHtml
|
||||
* @return string
|
||||
*/
|
||||
public static function truncateHtml($text, $length = 100, $ending = '...', $exact = false, $considerHtml = true) {
|
||||
public static function truncateHtml($text, $length = 100, $ending = '...', $exact = false, $considerHtml = true)
|
||||
{
|
||||
$open_tags = array();
|
||||
if ($considerHtml) {
|
||||
// if the plain text is shorter than the maximum length, return the whole text
|
||||
@@ -163,4 +223,177 @@ abstract class Utils
|
||||
}
|
||||
return $truncate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random string of a given length
|
||||
*
|
||||
* @param int $length
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function generateRandomString($length = 5)
|
||||
{
|
||||
return substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the ability to download a file to the browser
|
||||
*
|
||||
* @param $file the full path to the file to be downloaded
|
||||
* @param bool $force_download as opposed to letting browser choose if to download or render
|
||||
*/
|
||||
public static function download($file, $force_download = true)
|
||||
{
|
||||
if (file_exists($file)) {
|
||||
// fire download event
|
||||
self::getGrav()->fireEvent('onBeforeDownload', new Event(['file' => $file]));
|
||||
|
||||
$file_parts = pathinfo($file);
|
||||
$filesize = filesize($file);
|
||||
$range = false;
|
||||
|
||||
set_time_limit(0);
|
||||
ignore_user_abort(false);
|
||||
ini_set('output_buffering', 0);
|
||||
ini_set('zlib.output_compression', 0);
|
||||
|
||||
if ($force_download) {
|
||||
header('Content-Description: File Transfer');
|
||||
header('Content-Type: application/octet-stream');
|
||||
header('Content-Disposition: attachment; filename='.$file_parts['basename']);
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header('Pragma: public');
|
||||
} else {
|
||||
header("Content-Type: " . Utils::getMimeType($file_parts['extension']));
|
||||
}
|
||||
header('Content-Length: ' . $filesize);
|
||||
|
||||
// 8kb chunks for now
|
||||
$chunk = 8 * 1024;
|
||||
|
||||
$fh = fopen($file, "rb");
|
||||
|
||||
if ($fh === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Repeat reading until EOF
|
||||
while (!feof($fh)) {
|
||||
echo fread($fh, $chunk);
|
||||
|
||||
ob_flush(); // flush output
|
||||
flush();
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mimetype based on filename
|
||||
*
|
||||
* @param $extension Extension of file (eg .txt)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getMimeType($extension)
|
||||
{
|
||||
$extension = strtolower($extension);
|
||||
|
||||
switch($extension)
|
||||
{
|
||||
case "js":
|
||||
return "application/x-javascript";
|
||||
|
||||
case "json":
|
||||
return "application/json";
|
||||
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
case "jpe":
|
||||
return "image/jpg";
|
||||
|
||||
case "png":
|
||||
case "gif":
|
||||
case "bmp":
|
||||
case "tiff":
|
||||
return "image/" . $extension;
|
||||
|
||||
case "css":
|
||||
return "text/css";
|
||||
|
||||
case "xml":
|
||||
return "application/xml";
|
||||
|
||||
case "doc":
|
||||
case "docx":
|
||||
return "application/msword";
|
||||
|
||||
case "xls":
|
||||
case "xlt":
|
||||
case "xlm":
|
||||
case "xld":
|
||||
case "xla":
|
||||
case "xlc":
|
||||
case "xlw":
|
||||
case "xll":
|
||||
return "application/vnd.ms-excel";
|
||||
|
||||
case "ppt":
|
||||
case "pps":
|
||||
return "application/vnd.ms-powerpoint";
|
||||
|
||||
case "rtf":
|
||||
return "application/rtf";
|
||||
|
||||
case "pdf":
|
||||
return "application/pdf";
|
||||
|
||||
case "html":
|
||||
case "htm":
|
||||
case "php":
|
||||
return "text/html";
|
||||
|
||||
case "txt":
|
||||
return "text/plain";
|
||||
|
||||
case "mpeg":
|
||||
case "mpg":
|
||||
case "mpe":
|
||||
return "video/mpeg";
|
||||
|
||||
case "mp3":
|
||||
return "audio/mpeg3";
|
||||
|
||||
case "wav":
|
||||
return "audio/wav";
|
||||
|
||||
case "aiff":
|
||||
case "aif":
|
||||
return "audio/aiff";
|
||||
|
||||
case "avi":
|
||||
return "video/msvideo";
|
||||
|
||||
case "wmv":
|
||||
return "video/x-ms-wmv";
|
||||
|
||||
case "mov":
|
||||
return "video/quicktime";
|
||||
|
||||
case "zip":
|
||||
return "application/zip";
|
||||
|
||||
case "tar":
|
||||
return "application/x-tar";
|
||||
|
||||
case "swf":
|
||||
return "application/x-shockwave-flash";
|
||||
|
||||
default:
|
||||
return "application/octet-stream";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,7 @@ use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* Class BackupCommand
|
||||
|
||||
@@ -4,9 +4,7 @@ namespace Grav\Console\Cli;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
<?php
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Console\ConsoleTrait;
|
||||
use Grav\Common\Cache;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* Class ClearCacheCommand
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* Class NewProjectCommand
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Utils;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
@@ -188,7 +189,7 @@ class SandboxCommand extends Command
|
||||
$to = $this->destination . $target;
|
||||
|
||||
$this->output->writeln(' <cyan>' . $source . '</cyan> <comment>-></comment> ' . $to);
|
||||
$this->rcopy($from, $to);
|
||||
Utils::rcopy($from, $to);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +269,7 @@ class SandboxCommand extends Command
|
||||
|
||||
if (count($pages_files) == 0) {
|
||||
$destination = $this->source . '/user/pages';
|
||||
$this->rcopy($destination, $pages_dir);
|
||||
Utils::rcopy($destination, $pages_dir);
|
||||
$this->output->writeln(' <cyan>' . $destination . '</cyan> <comment>-></comment> Created');
|
||||
|
||||
}
|
||||
@@ -326,42 +327,4 @@ class SandboxCommand extends Command
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $src
|
||||
* @param $dest
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function rcopy($src, $dest)
|
||||
{
|
||||
|
||||
// If the src is not a directory do a simple file copy
|
||||
if (!is_dir($src)) {
|
||||
copy($src, $dest);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the destination directory does not exist create it
|
||||
if (!is_dir($dest)) {
|
||||
if (!mkdir($dest)) {
|
||||
// If the destination directory could not be created stop processing
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Open the source directory to read in files
|
||||
$i = new \DirectoryIterator($src);
|
||||
/** @var \DirectoryIterator $f */
|
||||
foreach ($i as $f) {
|
||||
if ($f->isFile()) {
|
||||
copy($f->getRealPath(), "$dest/" . $f->getFilename());
|
||||
} else {
|
||||
if (!$f->isDot() && $f->isDir()) {
|
||||
$this->rcopy($f->getRealPath(), "$dest/$f");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@ trait ConsoleTrait
|
||||
*/
|
||||
public function setupConsole(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if (self::$grav) {
|
||||
self::$grav['config']->set('system.cache.driver', 'default');
|
||||
if (self::getGrav()) {
|
||||
self::getGrav()['config']->set('system.cache.driver', 'default');
|
||||
}
|
||||
|
||||
$this->argv = $_SERVER['argv'][0];
|
||||
|
||||
@@ -78,12 +78,12 @@ class InfoCommand extends Command
|
||||
$this->output->writeln('');
|
||||
|
||||
$packageURL = '';
|
||||
if (isset($foundPackage->author->url)) {
|
||||
$packageURL = '<' . $foundPackage->author->url . '>';
|
||||
if (isset($foundPackage->author['url'])) {
|
||||
$packageURL = '<' . $foundPackage->author['url'] . '>';
|
||||
}
|
||||
|
||||
$this->output->writeln("<green>" . str_pad("Author",
|
||||
12) . ":</green> " . $foundPackage->author->name . ' <' . $foundPackage->author->email . '> ' . $packageURL);
|
||||
12) . ":</green> " . $foundPackage->author['name'] . ' <' . $foundPackage->author['email'] . '> ' . $packageURL);
|
||||
|
||||
foreach (array(
|
||||
'version',
|
||||
|
||||
@@ -5,6 +5,8 @@ use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\GPM\GPM;
|
||||
use Grav\Common\GPM\Installer;
|
||||
use Grav\Common\GPM\Response;
|
||||
use Grav\Common\Inflector;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Console\ConsoleTrait;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
@@ -12,6 +14,10 @@ use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
define('GIT_REGEX', '/http[s]?:\/\/(?:.*@)?(github|bitbucket)(?:.org|.com)\/.*\/(.*)/');
|
||||
|
||||
/**
|
||||
* Class InstallCommand
|
||||
@@ -42,6 +48,9 @@ class InstallCommand extends Command
|
||||
*/
|
||||
protected $tmp;
|
||||
|
||||
protected $local_config;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -93,6 +102,11 @@ class InstallCommand extends Command
|
||||
$packages = array_map('strtolower', $this->input->getArgument('package'));
|
||||
$this->data = $this->gpm->findPackages($packages);
|
||||
|
||||
$local_config_file = exec('eval echo ~/.grav/config');
|
||||
if (file_exists($local_config_file)) {
|
||||
$this->local_config = Yaml::parse($local_config_file);
|
||||
}
|
||||
|
||||
if (
|
||||
!Installer::isGravInstance($this->destination) ||
|
||||
!Installer::isValidDestination($this->destination, [Installer::EXISTS, Installer::IS_LINK])
|
||||
@@ -111,7 +125,7 @@ class InstallCommand extends Command
|
||||
|
||||
if (count($this->data['not_found'])) {
|
||||
$this->output->writeln("These packages were not found on Grav: <red>" . implode('</red>, <red>',
|
||||
$this->data['not_found']) . "</red>");
|
||||
array_keys($this->data['not_found'])) . "</red>");
|
||||
}
|
||||
|
||||
unset($this->data['not_found']);
|
||||
@@ -119,29 +133,29 @@ class InstallCommand extends Command
|
||||
|
||||
foreach ($this->data as $data) {
|
||||
foreach ($data as $package) {
|
||||
$version = isset($package->available) ? $package->available : $package->version;
|
||||
$this->output->writeln("Preparing to install <cyan>" . $package->name . "</cyan> [v" . $version . "]");
|
||||
|
||||
$this->output->write(" |- Downloading package... 0%");
|
||||
$this->file = $this->downloadPackage($package);
|
||||
|
||||
$this->output->write(" |- Checking destination... ");
|
||||
$checks = $this->checkDestination($package);
|
||||
|
||||
if (!$checks) {
|
||||
$this->output->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
//Check for dependencies
|
||||
if (isset($package->dependencies)) {
|
||||
$this->output->writeln("Package <cyan>" . $package->name . "</cyan> has ". count($package->dependencies) . " required dependencies that must be installed first...");
|
||||
$this->output->writeln('');
|
||||
} else {
|
||||
$this->output->write(" |- Installing package... ");
|
||||
$installation = $this->installPackage($package);
|
||||
if (!$installation) {
|
||||
$this->output->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
|
||||
$dependency_data = $this->gpm->findPackages($package->dependencies);
|
||||
|
||||
if (!$dependency_data['total']) {
|
||||
$this->output->writeln("No dependencies found...");
|
||||
$this->output->writeln('');
|
||||
} else {
|
||||
$this->output->writeln(" '- <green>Success!</green> ");
|
||||
$this->output->writeln('');
|
||||
unset($dependency_data['total']);
|
||||
|
||||
foreach($dependency_data as $type => $dep_data) {
|
||||
foreach($dep_data as $name => $dep_package) {
|
||||
|
||||
$this->processPackage($dep_package);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->processPackage($package);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +163,255 @@ class InstallCommand extends Command
|
||||
$this->clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $package
|
||||
*/
|
||||
private function processPackage($package)
|
||||
{
|
||||
$install_options = ['GPM'];
|
||||
|
||||
// if no name, not found in GPM
|
||||
if (!isset($package->version)) {
|
||||
unset($install_options[0]);
|
||||
}
|
||||
// if local config found symlink is a valid option
|
||||
if (isset($this->local_config) && $this->getSymlinkSource($package)) {
|
||||
$install_options[] = 'Symlink';
|
||||
}
|
||||
// if override set, can install via git
|
||||
if (isset($package->override_repository)) {
|
||||
$install_options[] = 'Git';
|
||||
}
|
||||
|
||||
// reindex list
|
||||
$install_options = array_values($install_options);
|
||||
|
||||
if (count($install_options) == 0) {
|
||||
// no valid install options - error and return
|
||||
$this->output->writeln("<red>not valid installation methods found!</red>");
|
||||
return;
|
||||
} elseif (count($install_options) == 1) {
|
||||
// only one option, use it...
|
||||
$method = $install_options[0];
|
||||
} else {
|
||||
$helper = $this->getHelper('question');
|
||||
$question = new ChoiceQuestion(
|
||||
'Please select installation method for <cyan>' . $package->name . '</cyan> (<magenta>'.$install_options[0].' is default</magenta>)', array_values($install_options), 0
|
||||
);
|
||||
$question->setErrorMessage('Method %s is invalid');
|
||||
$method = $helper->ask($this->input, $this->output, $question);
|
||||
}
|
||||
|
||||
$this->output->writeln('');
|
||||
|
||||
$method_name = 'process'.$method;
|
||||
$this->$method_name($package);
|
||||
|
||||
$this->installDemoContent($package);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $package
|
||||
*/
|
||||
private function installDemoContent($package)
|
||||
{
|
||||
$demo_dir = $this->destination . DS . $package->install_path . DS . '_demo';
|
||||
$dest_dir = $this->destination . DS . 'user';
|
||||
$pages_dir = $dest_dir . DS . 'pages';
|
||||
|
||||
if (file_exists($demo_dir)) {
|
||||
// Demo content exists, prompt to install it.
|
||||
$this->output->writeln("<white>Attention: </white><cyan>".$package->name . "</cyan> contains demo content");
|
||||
$helper = $this->getHelper('question');
|
||||
$question = new ConfirmationQuestion('Do you wish to install this demo content? [y|N] ', false);
|
||||
|
||||
if (!$helper->ask($this->input, $this->output, $question)) {
|
||||
$this->output->writeln(" '- <red>Skipped!</red> ");
|
||||
$this->output->writeln('');
|
||||
return;
|
||||
}
|
||||
|
||||
// if pages folder exists in demo
|
||||
if (file_exists($demo_dir . DS . 'pages')) {
|
||||
$pages_backup = 'pages.' . date('m-d-Y-H-i-s');
|
||||
$question = new ConfirmationQuestion('This will backup your current `user/pages` folder to `user/'. $pages_backup. '`, continue? [y|N]', false);
|
||||
|
||||
if (!$helper->ask($this->input, $this->output, $question)) {
|
||||
$this->output->writeln(" '- <red>Skipped!</red> ");
|
||||
$this->output->writeln('');
|
||||
return;
|
||||
}
|
||||
|
||||
// backup current pages folder
|
||||
if (file_exists($dest_dir)) {
|
||||
if (rename($pages_dir, $dest_dir . DS . $pages_backup)) {
|
||||
$this->output->writeln(" |- Backing up pages... <green>ok</green>");
|
||||
} else {
|
||||
$this->output->writeln(" |- Backing up pages... <red>failed</red>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Confirmation received, copy over the data
|
||||
$this->output->writeln(" |- Installing demo content... <green>ok</green> ");
|
||||
Utils::rcopy($demo_dir, $dest_dir);
|
||||
$this->output->writeln(" '- <green>Success!</green> ");
|
||||
$this->output->writeln('');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $package
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getGitRegexMatches($package)
|
||||
{
|
||||
if (isset($package->override_repository)) {
|
||||
$repository = $package->override_repository;
|
||||
} elseif (isset($package->repository)) {
|
||||
$repository = $package->repository;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
preg_match(GIT_REGEX, $repository, $matches);
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $package
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
private function getSymlinkSource($package)
|
||||
{
|
||||
$matches = $this->getGitRegexMatches($package);
|
||||
|
||||
foreach ($this->local_config as $path) {
|
||||
if (Utils::endsWith($matches[2], '.git')) {
|
||||
$repo_dir = preg_replace('/\.git$/', '', $matches[2]);
|
||||
} else {
|
||||
$repo_dir = $matches[2];
|
||||
}
|
||||
|
||||
$from = rtrim($path, '/') . '/' . $repo_dir;
|
||||
|
||||
if (file_exists($from)) {
|
||||
return $from;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $package
|
||||
*/
|
||||
private function processSymlink($package)
|
||||
{
|
||||
|
||||
exec('cd ' . $this->destination);
|
||||
|
||||
$to = $this->destination . DS . $package->install_path;
|
||||
$from = $this->getSymlinkSource($package);
|
||||
|
||||
$this->output->writeln("Preparing to Symlink <cyan>" . $package->name . "</cyan>");
|
||||
$this->output->write(" |- Checking source... ");
|
||||
|
||||
if (file_exists($from)) {
|
||||
$this->output->writeln("<green>ok</green>");
|
||||
|
||||
$this->output->write(" |- Checking destination... ");
|
||||
$checks = $this->checkDestination($package);
|
||||
|
||||
if (!$checks) {
|
||||
$this->output->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
$this->output->writeln('');
|
||||
} else {
|
||||
if (file_exists($to)) {
|
||||
$this->output->writeln(" '- <red>Symlink cannot overwrite an existing package, please remove first</red>");
|
||||
$this->output->writeln('');
|
||||
} else {
|
||||
symlink($from, $to);
|
||||
|
||||
// extra white spaces to clear out the buffer properly
|
||||
$this->output->writeln(" |- Symlinking package... <green>ok</green> ");
|
||||
|
||||
$this->output->writeln(" '- <green>Success!</green> ");
|
||||
$this->output->writeln('');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$this->output->writeln("<red>not found!</red>");
|
||||
$this->output->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $package
|
||||
*/
|
||||
private function processGit($package)
|
||||
{
|
||||
$matches = $this->getGitRegexMatches($package);
|
||||
|
||||
$to = $this->destination . DS . $package->install_path;
|
||||
|
||||
$this->output->writeln("Preparing to Git clone <cyan>" . $package->name . "</cyan> from " . $matches[0]);
|
||||
|
||||
$this->output->write(" |- Checking destination... ");
|
||||
$checks = $this->checkDestination($package);
|
||||
|
||||
if (!$checks) {
|
||||
$this->output->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
$this->output->writeln('');
|
||||
} else {
|
||||
$cmd = 'cd ' . $this->destination . ' && git clone ' . $matches[0] . ' ' . $package->install_path;
|
||||
exec($cmd);
|
||||
|
||||
// extra white spaces to clear out the buffer properly
|
||||
$this->output->writeln(" |- Cloning package... <green>ok</green> ");
|
||||
|
||||
$this->output->writeln(" '- <green>Success!</green> ");
|
||||
$this->output->writeln('');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $package
|
||||
*/
|
||||
private function processGPM($package)
|
||||
{
|
||||
$version = isset($package->available) ? $package->available : $package->version;
|
||||
|
||||
$this->output->writeln("Preparing to install <cyan>" . $package->name . "</cyan> [v" . $version . "]");
|
||||
|
||||
$this->output->write(" |- Downloading package... 0%");
|
||||
$this->file = $this->downloadPackage($package);
|
||||
|
||||
$this->output->write(" |- Checking destination... ");
|
||||
$checks = $this->checkDestination($package);
|
||||
|
||||
if (!$checks) {
|
||||
$this->output->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
$this->output->writeln('');
|
||||
} else {
|
||||
$this->output->write(" |- Installing package... ");
|
||||
$installation = $this->installPackage($package);
|
||||
if (!$installation) {
|
||||
$this->output->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
$this->output->writeln('');
|
||||
} else {
|
||||
$this->output->writeln(" '- <green>Success!</green> ");
|
||||
$this->output->writeln('');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $package
|
||||
*
|
||||
@@ -218,6 +481,8 @@ class InstallCommand extends Command
|
||||
$this->output->writeln(" | '- <red>You decided to not delete the symlink automatically.</red>");
|
||||
|
||||
return false;
|
||||
} else {
|
||||
unlink($this->destination . DS . $package->install_path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,9 +84,10 @@ class SelfupgradeCommand extends Command
|
||||
$this->setupConsole($input, $output);
|
||||
$this->upgrader = new Upgrader($this->input->getOption('force'));
|
||||
|
||||
$update = $this->upgrader->getAssets()['grav-update'];
|
||||
|
||||
$local = $this->upgrader->getLocalVersion();
|
||||
$remote = $this->upgrader->getRemoteVersion();
|
||||
$update = $this->upgrader->getAssets()->{'grav-update'};
|
||||
$release = strftime('%c', strtotime($this->upgrader->getReleaseDate()));
|
||||
|
||||
if (!$this->upgrader->isUpgradable()) {
|
||||
@@ -110,10 +111,10 @@ class SelfupgradeCommand extends Command
|
||||
|
||||
$this->output->writeln("");
|
||||
foreach ($changelog as $version => $log) {
|
||||
$title = $version . ' [' . $log->date . ']';
|
||||
$title = $version . ' [' . $log['date'] . ']';
|
||||
$content = preg_replace_callback("/\d\.\s\[\]\(#(.*)\)/", function ($match) {
|
||||
return "\n" . ucfirst($match[1]) . ":";
|
||||
}, $log->content);
|
||||
}, $log['content']);
|
||||
|
||||
$this->output->writeln($title);
|
||||
$this->output->writeln(str_repeat('-', strlen($title)));
|
||||
@@ -138,7 +139,7 @@ class SelfupgradeCommand extends Command
|
||||
$this->output->writeln("");
|
||||
$this->output->writeln("Preparing to upgrade to v<cyan>$remote</cyan>..");
|
||||
|
||||
$this->output->write(" |- Downloading upgrade [" . $this->formatBytes($update->size) . "]... 0%");
|
||||
$this->output->write(" |- Downloading upgrade [" . $this->formatBytes($update['size']) . "]... 0%");
|
||||
$this->file = $this->download($update);
|
||||
|
||||
$this->output->write(" |- Installing upgrade... ");
|
||||
@@ -164,17 +165,17 @@ class SelfupgradeCommand extends Command
|
||||
private function download($package)
|
||||
{
|
||||
$this->tmp = CACHE_DIR . DS . 'tmp/Grav-' . uniqid();
|
||||
$output = Response::get($package->download, [], [$this, 'progress']);
|
||||
$output = Response::get($package['download'], [], [$this, 'progress']);
|
||||
|
||||
Folder::mkdir($this->tmp);
|
||||
|
||||
$this->output->write("\x0D");
|
||||
$this->output->write(" |- Downloading upgrade [" . $this->formatBytes($package->size) . "]... 100%");
|
||||
$this->output->write(" |- Downloading upgrade [" . $this->formatBytes($package['size']) . "]... 100%");
|
||||
$this->output->writeln('');
|
||||
|
||||
file_put_contents($this->tmp . DS . $package->name, $output);
|
||||
file_put_contents($this->tmp . DS . $package['name'], $output);
|
||||
|
||||
return $this->tmp . DS . $package->name;
|
||||
return $this->tmp . DS . $package['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -140,7 +140,7 @@ class UninstallCommand extends Command
|
||||
*/
|
||||
private function uninstallPackage($package)
|
||||
{
|
||||
$path = self::$grav['locator']->findResource($package->package_type . '://' . $package->slug);
|
||||
$path = self::getGrav()['locator']->findResource($package->package_type . '://' . $package->slug);
|
||||
Installer::uninstall($path);
|
||||
$errorCode = Installer::lastErrorCode();
|
||||
|
||||
@@ -167,7 +167,7 @@ class UninstallCommand extends Command
|
||||
|
||||
private function checkDestination($package)
|
||||
{
|
||||
$path = self::$grav['locator']->findResource($package->package_type . '://' . $package->slug);
|
||||
$path = self::getGrav()['locator']->findResource($package->package_type . '://' . $package->slug);
|
||||
$questionHelper = $this->getHelper('question');
|
||||
$skipPrompt = $this->input->getOption('all-yes');
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<action type="Redirect" url="error" redirectType="Permanent" />
|
||||
</rule>
|
||||
<rule name="system" stopProcessing="true">
|
||||
<match url="^system/(.*)$" ignoreCase="false" />
|
||||
<match url="^system/(.*)\.(txt|md|html|yaml|php|twig|sh|bat)$" ignoreCase="false" />
|
||||
<action type="Redirect" url="error" redirectType="Permanent" />
|
||||
</rule>
|
||||
<rule name="vendor" stopProcessing="true">
|
||||
|
||||
Reference in New Issue
Block a user