mirror of
https://github.com/getgrav/grav.git
synced 2025-12-05 15:29:57 +01:00
Compare commits
464 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b568ad8f56 | ||
|
|
c8287d12fa | ||
|
|
8fdf517613 | ||
|
|
0725a4729a | ||
|
|
a67b1f7350 | ||
|
|
a7abd91868 | ||
|
|
7d9ea51ea5 | ||
|
|
f284147b31 | ||
|
|
0f71e1e795 | ||
|
|
14780c3bb1 | ||
|
|
1409b7284b | ||
|
|
c7e3b4d026 | ||
|
|
139d9f8531 | ||
|
|
b9a569b71f | ||
|
|
8991af149e | ||
|
|
fccebb83c2 | ||
|
|
dbfd2373fe | ||
|
|
2fda197eff | ||
|
|
fbf09a7741 | ||
|
|
45e26c0936 | ||
|
|
92159d1df8 | ||
|
|
3ba491d02c | ||
|
|
b3047f7156 | ||
|
|
ed818b55de | ||
|
|
2a308d2a08 | ||
|
|
8fa8b55c7e | ||
|
|
08fc3918a7 | ||
|
|
32162532d5 | ||
|
|
d012c8ed2d | ||
|
|
e66b89f08c | ||
|
|
94d019982b | ||
|
|
29d2eed5fb | ||
|
|
50940da168 | ||
|
|
d1060940f0 | ||
|
|
a05df55b15 | ||
|
|
d5e71072c0 | ||
|
|
099589da90 | ||
|
|
8784372d41 | ||
|
|
f2f00bb09b | ||
|
|
7262fbac55 | ||
|
|
bb0635c36f | ||
|
|
a66ce64171 | ||
|
|
ea4690db3f | ||
|
|
47859d496e | ||
|
|
9d6cc2cbcc | ||
|
|
0b7d2e6d7e | ||
|
|
99783a6ab9 | ||
|
|
1a5102e47b | ||
|
|
dd6986e5ff | ||
|
|
05bea0bf50 | ||
|
|
96e8ab4610 | ||
|
|
5626f56595 | ||
|
|
614b126939 | ||
|
|
d38a0973c6 | ||
|
|
a910568144 | ||
|
|
4a558ca89d | ||
|
|
a45c31b952 | ||
|
|
2abc5d82da | ||
|
|
1aec50ca65 | ||
|
|
3c24dcc5aa | ||
|
|
d3265baa4e | ||
|
|
50cdf01fae | ||
|
|
36688c9451 | ||
|
|
cbfd6936e6 | ||
|
|
2d3e452baf | ||
|
|
6879a2d54c | ||
|
|
a2aaa2ce79 | ||
|
|
088075888e | ||
|
|
16ce10a5c7 | ||
|
|
87d16e36cb | ||
|
|
19fc443518 | ||
|
|
376c436318 | ||
|
|
edb102b1dd | ||
|
|
dc28ba5d1a | ||
|
|
91f43c1613 | ||
|
|
318319731d | ||
|
|
4b46ea241c | ||
|
|
2a66f6b441 | ||
|
|
60bbb32833 | ||
|
|
31063c5749 | ||
|
|
210c66de21 | ||
|
|
45525af846 | ||
|
|
2c247a4529 | ||
|
|
2fac370741 | ||
|
|
454b3a61e7 | ||
|
|
376cc0c095 | ||
|
|
21b1323d29 | ||
|
|
fb549701ff | ||
|
|
d4cb85174c | ||
|
|
d596dd98dd | ||
|
|
16ba82b795 | ||
|
|
78279e4d7a | ||
|
|
699fade3d1 | ||
|
|
fdd94633a8 | ||
|
|
2f1e0c6be2 | ||
|
|
d0cded11fb | ||
|
|
8cd962e0f3 | ||
|
|
9bb7d0c37b | ||
|
|
f41ba3172b | ||
|
|
aecdbaaa27 | ||
|
|
e572015913 | ||
|
|
e11c426c38 | ||
|
|
e2aff43963 | ||
|
|
7551d0c69a | ||
|
|
7ac6e436fa | ||
|
|
10ca2ef3fd | ||
|
|
04e197f9cc | ||
|
|
f2638f17c3 | ||
|
|
051a7e66f6 | ||
|
|
ff1db8ece8 | ||
|
|
3bfe32eda1 | ||
|
|
588bd0168c | ||
|
|
e81980b6c0 | ||
|
|
e33693f224 | ||
|
|
74747844e4 | ||
|
|
9b2221f702 | ||
|
|
cac8503c72 | ||
|
|
7c37216d6a | ||
|
|
359c33dae5 | ||
|
|
a9b517386c | ||
|
|
7202766cb5 | ||
|
|
9bd62558c1 | ||
|
|
ac8887129c | ||
|
|
2d4eb6d364 | ||
|
|
046a50ffcd | ||
|
|
fc863c560f | ||
|
|
9928b75d0d | ||
|
|
a54d116be3 | ||
|
|
7046c104d6 | ||
|
|
bb0ee34082 | ||
|
|
8950e6661f | ||
|
|
112dd6f4ae | ||
|
|
9426b42a33 | ||
|
|
e6bf5d9ea5 | ||
|
|
ce9e955f21 | ||
|
|
5025a430b6 | ||
|
|
458f6cb55d | ||
|
|
b379b38fff | ||
|
|
0711875200 | ||
|
|
767ac573af | ||
|
|
c96bacc320 | ||
|
|
8aec9f7c15 | ||
|
|
a66fede72d | ||
|
|
5586c1923c | ||
|
|
01467e1b32 | ||
|
|
6be04d6406 | ||
|
|
a7176c4a6e | ||
|
|
67484a1a90 | ||
|
|
783d148551 | ||
|
|
5134b28bad | ||
|
|
c10882f290 | ||
|
|
19bbfd0a50 | ||
|
|
3f6b5e35de | ||
|
|
002902c9d9 | ||
|
|
d93e17cff4 | ||
|
|
9fa92b504f | ||
|
|
74d25d240a | ||
|
|
a136241952 | ||
|
|
c89f683b8c | ||
|
|
45e79f88a3 | ||
|
|
bdd0c1f4c7 | ||
|
|
2bfcd88bfd | ||
|
|
d18f0bf751 | ||
|
|
3d983d8377 | ||
|
|
6c912d126a | ||
|
|
48b02b0f05 | ||
|
|
523b6376bb | ||
|
|
c276457390 | ||
|
|
d983a908e4 | ||
|
|
538fa23b9d | ||
|
|
d2bfacad4e | ||
|
|
cc68065f94 | ||
|
|
00f6738b7e | ||
|
|
9310c3f12f | ||
|
|
0fc1f5492f | ||
|
|
4562de083d | ||
|
|
7250a3ecc1 | ||
|
|
ac1874795b | ||
|
|
2fb8fb62c6 | ||
|
|
fd9816c177 | ||
|
|
5a6e32fce7 | ||
|
|
774850f30d | ||
|
|
41f4d269b0 | ||
|
|
18e79ce4fe | ||
|
|
92ce0aa816 | ||
|
|
5ca9180033 | ||
|
|
1856d1f8c7 | ||
|
|
af9810d1b3 | ||
|
|
a454312bd2 | ||
|
|
88063c2a60 | ||
|
|
8c010fbd3c | ||
|
|
7f615f19f7 | ||
|
|
5f7b70748b | ||
|
|
79268130f4 | ||
|
|
8b78b4997e | ||
|
|
e224841650 | ||
|
|
a69e5fa115 | ||
|
|
539b7931e6 | ||
|
|
f303c1243f | ||
|
|
347d8ecebe | ||
|
|
f977092115 | ||
|
|
5430792687 | ||
|
|
e591212166 | ||
|
|
77f19974f5 | ||
|
|
8b34e7dc19 | ||
|
|
3d4fb6a7d4 | ||
|
|
af7d3ccf83 | ||
|
|
a8b6841923 | ||
|
|
e09ab139c8 | ||
|
|
fe6a0a27b3 | ||
|
|
2f4c32e682 | ||
|
|
fdf2884f97 | ||
|
|
8d8129b49e | ||
|
|
a811ee67cc | ||
|
|
f9bfed2b22 | ||
|
|
3991f51cdf | ||
|
|
0902840aa4 | ||
|
|
df475af2fd | ||
|
|
9fb56df9a0 | ||
|
|
b11b39752f | ||
|
|
8cd361ca41 | ||
|
|
4c04dfc747 | ||
|
|
6cc3d15ee7 | ||
|
|
c0bf787dc7 | ||
|
|
4452cf3d1b | ||
|
|
9fa97ad46b | ||
|
|
6c3e1d83a7 | ||
|
|
9f2d1b48e6 | ||
|
|
fd7fd2fb0c | ||
|
|
a58ea17867 | ||
|
|
093e101f1b | ||
|
|
93dd673639 | ||
|
|
71b25190b8 | ||
|
|
1d90cecc11 | ||
|
|
e771393489 | ||
|
|
122107d1f8 | ||
|
|
ebd81a0dc8 | ||
|
|
69282e4423 | ||
|
|
ce71ac352e | ||
|
|
7738e052e1 | ||
|
|
f43e047497 | ||
|
|
9eb06da9a9 | ||
|
|
ce4a9a02d4 | ||
|
|
a8a82e1a3c | ||
|
|
5abc9b320b | ||
|
|
0aba432688 | ||
|
|
d80429a0ea | ||
|
|
d9145b0ebc | ||
|
|
c05252a570 | ||
|
|
acd81eb7c3 | ||
|
|
a3c58fcc5a | ||
|
|
16b541a8ee | ||
|
|
d705530e64 | ||
|
|
84873484d5 | ||
|
|
eb1883854e | ||
|
|
9cb83ba368 | ||
|
|
82bc6fb308 | ||
|
|
5aa95c0b7e | ||
|
|
419b46afb0 | ||
|
|
0b607c5197 | ||
|
|
9da1ec836e | ||
|
|
5639941ff3 | ||
|
|
9d59db5adc | ||
|
|
033f43b82f | ||
|
|
91f4dc1a79 | ||
|
|
9fe24272e3 | ||
|
|
b156c8752a | ||
|
|
93c51584db | ||
|
|
f36f31dfcf | ||
|
|
5cef486981 | ||
|
|
5db100ae49 | ||
|
|
46bcd1b095 | ||
|
|
1fc668f0fc | ||
|
|
5589b48e01 | ||
|
|
cc28f85fde | ||
|
|
a6d46ebcd7 | ||
|
|
ac3b0ba3ec | ||
|
|
e4ff2ea39d | ||
|
|
4653df1350 | ||
|
|
e3c5234038 | ||
|
|
4f0e1ea8b0 | ||
|
|
eaa05ee252 | ||
|
|
0c4d0b5646 | ||
|
|
00ac1da29d | ||
|
|
6ac126867e | ||
|
|
ea7ddeaf55 | ||
|
|
f536dbfc20 | ||
|
|
7261cea92f | ||
|
|
41d9b52005 | ||
|
|
50529ae711 | ||
|
|
1bfa2f31e7 | ||
|
|
2b8e3d16a3 | ||
|
|
c1e9cc2f02 | ||
|
|
a39656d536 | ||
|
|
103369cc64 | ||
|
|
0ab39a9b85 | ||
|
|
af582f88fc | ||
|
|
cccd084a55 | ||
|
|
bde5e65d10 | ||
|
|
874bfe47b8 | ||
|
|
31c12c9c0d | ||
|
|
ed8903ece3 | ||
|
|
5c92069e52 | ||
|
|
79229f6b35 | ||
|
|
f9272887f5 | ||
|
|
aa9af76f02 | ||
|
|
967202cbc3 | ||
|
|
a7007eabfa | ||
|
|
0e75f999cc | ||
|
|
4e3b5039da | ||
|
|
7a2af8e63e | ||
|
|
3fe18a9213 | ||
|
|
bfa9cc294e | ||
|
|
81c4ecaabd | ||
|
|
2f3f4a1137 | ||
|
|
c50cba64bb | ||
|
|
5b2aa6ead9 | ||
|
|
6a3f6b9be2 | ||
|
|
61169d868c | ||
|
|
72023fcd4a | ||
|
|
7c160b9b08 | ||
|
|
e8e90892aa | ||
|
|
16ad95c205 | ||
|
|
d509ed0a34 | ||
|
|
6d0539c793 | ||
|
|
0276ce94df | ||
|
|
978dc571bb | ||
|
|
059189ec77 | ||
|
|
1af0e7068e | ||
|
|
c8d6bfa455 | ||
|
|
1ad80acd72 | ||
|
|
e19b15cacd | ||
|
|
2178971e5e | ||
|
|
ffecd8bb87 | ||
|
|
4326e059a5 | ||
|
|
adaadf3c60 | ||
|
|
c4d590674d | ||
|
|
a86159a02c | ||
|
|
88c9436df9 | ||
|
|
b26d938147 | ||
|
|
97808a8b6a | ||
|
|
bf39adbda7 | ||
|
|
ec82760cbf | ||
|
|
3f6901c036 | ||
|
|
6d27da4f9b | ||
|
|
ee623596a8 | ||
|
|
9df91e482e | ||
|
|
5739e9729f | ||
|
|
eba77a8028 | ||
|
|
82533c17e6 | ||
|
|
fd64fd0822 | ||
|
|
04fb7326e5 | ||
|
|
1d8f41df35 | ||
|
|
1b01dff466 | ||
|
|
e2021d1075 | ||
|
|
75d0b687c1 | ||
|
|
d0e6b23637 | ||
|
|
e9f4c7d9a5 | ||
|
|
a3b76252d1 | ||
|
|
231e278c76 | ||
|
|
5f41beccde | ||
|
|
a44193db16 | ||
|
|
c6dcb22d56 | ||
|
|
ff3ebb1f17 | ||
|
|
8e489f6f09 | ||
|
|
618a461835 | ||
|
|
281eb14178 | ||
|
|
1cbb39a38b | ||
|
|
b50ec3fedd | ||
|
|
5f03dfa8ed | ||
|
|
fa92ec0141 | ||
|
|
193eda633c | ||
|
|
da0fafa800 | ||
|
|
bfb5a74197 | ||
|
|
cceaeb8d42 | ||
|
|
2a43983ede | ||
|
|
cd5707f2d2 | ||
|
|
5667f8b023 | ||
|
|
09248f2917 | ||
|
|
162409053f | ||
|
|
528f85642f | ||
|
|
9d1b50ffbb | ||
|
|
95a806c8f8 | ||
|
|
7a38ac0810 | ||
|
|
0fec4c003b | ||
|
|
80d08eace7 | ||
|
|
e7568bf8d0 | ||
|
|
353f69deb3 | ||
|
|
e2917e36f8 | ||
|
|
8ddfa571d3 | ||
|
|
393c8cab2a | ||
|
|
cb03c7ab0d | ||
|
|
bce0797c55 | ||
|
|
d234b8e212 | ||
|
|
481e61b08b | ||
|
|
f8fe108db5 | ||
|
|
103abc5f6d | ||
|
|
f515438c62 | ||
|
|
470bac23bc | ||
|
|
123c75ae2d | ||
|
|
d05c049d4c | ||
|
|
c7ece89fea | ||
|
|
4c9e02d648 | ||
|
|
57ab99fb13 | ||
|
|
217db86b10 | ||
|
|
e5037eb69b | ||
|
|
8da3eb32f0 | ||
|
|
24d1d4774e | ||
|
|
b0f37079b0 | ||
|
|
026646cc46 | ||
|
|
258f55fe96 | ||
|
|
d2cd2a3772 | ||
|
|
c8db29ce46 | ||
|
|
8a4a810771 | ||
|
|
412216188d | ||
|
|
9103ea0c52 | ||
|
|
ea1c26caa3 | ||
|
|
0977d330e6 | ||
|
|
f7c2df0050 | ||
|
|
e690e50d86 | ||
|
|
0b0ce2eb27 | ||
|
|
cc81ea7e3e | ||
|
|
32dfc001af | ||
|
|
a0f5e35ad5 | ||
|
|
971508f613 | ||
|
|
144d64e0c8 | ||
|
|
a999046882 | ||
|
|
943921eadb | ||
|
|
0f87946276 | ||
|
|
9e032a7120 | ||
|
|
17e218bdb3 | ||
|
|
cb705f2d96 | ||
|
|
ac8483f894 | ||
|
|
3098ac9998 | ||
|
|
30246a8666 | ||
|
|
f606999c40 | ||
|
|
affa768efb | ||
|
|
c73e0d140d | ||
|
|
615c91b1c5 | ||
|
|
a0bc5bf765 | ||
|
|
2ab433c339 | ||
|
|
267fa23d51 | ||
|
|
e6565d9394 | ||
|
|
3b848e4a91 | ||
|
|
15db488abc | ||
|
|
2c4a724fc0 | ||
|
|
3568c25ef1 | ||
|
|
3948aa77a8 | ||
|
|
0d0c22c940 | ||
|
|
1ac4014d64 | ||
|
|
627646bf35 | ||
|
|
67a325e8e9 | ||
|
|
69e345f64f | ||
|
|
37980d15e4 | ||
|
|
3fc6c47b5e | ||
|
|
dee24787ba | ||
|
|
7d9142fa03 | ||
|
|
51cca81bc8 | ||
|
|
8de6231116 | ||
|
|
fbc7ca4ef2 | ||
|
|
b507815eef | ||
|
|
c56efb9a10 | ||
|
|
eba9002400 | ||
|
|
7d5426144d |
@@ -11,3 +11,8 @@ trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# 2 space indentation
|
||||
[*.yaml, *.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
2
.gitignore
vendored
Executable file → Normal file
2
.gitignore
vendored
Executable file → Normal file
@@ -1,5 +1,7 @@
|
||||
# Composer
|
||||
composer.lock
|
||||
.composer
|
||||
vendor/
|
||||
|
||||
# Sass
|
||||
.sass-cache
|
||||
|
||||
55
.htaccess
Executable file → Normal file
55
.htaccess
Executable file → Normal file
@@ -2,7 +2,24 @@
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
##
|
||||
## Begin - Exploits
|
||||
# If you experience problems on your site block out the operations listed below
|
||||
# This attempts to block the most common type of exploit `attempts` to Grav
|
||||
#
|
||||
# Block out any script trying to base64_encode data within the URL.
|
||||
RewriteCond %{QUERY_STRING} base64_encode[^(]*\([^)]*\) [OR]
|
||||
# Block out any script that includes a <script> tag in URL.
|
||||
RewriteCond %{QUERY_STRING} (<|%3C)([^s]*s)+cript.*(>|%3E) [NC,OR]
|
||||
# Block out any script trying to set a PHP GLOBALS variable via URL.
|
||||
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
|
||||
# Block out any script trying to modify a _REQUEST variable via URL.
|
||||
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2})
|
||||
# Return 403 Forbidden header and show the content of the root homepage
|
||||
RewriteRule .* index.php [F]
|
||||
#
|
||||
## End - Exploits
|
||||
|
||||
## Begin RewriteBase
|
||||
# If you are getting 404 errors on subpages, you may have to uncomment the RewriteBase entry
|
||||
# You should change the '/' to your appropriate subfolder. For example if you have
|
||||
# your Grav install at the root of your site '/' should work, else it might be something
|
||||
@@ -11,29 +28,29 @@ RewriteEngine On
|
||||
|
||||
# RewriteBase /
|
||||
|
||||
# Access site
|
||||
## End - RewriteBase
|
||||
|
||||
## Begin - Index
|
||||
# If the requested path and file is not /index.php and the request
|
||||
# has not already been internally rewritten to the index.php script
|
||||
RewriteCond %{REQUEST_URI} !^/index\.php
|
||||
# and the requested path and file doesn't directly match a physical file
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
# and the requested path and file doesn't directly match a physical folder
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
# internally rewrite the request to the index.php script
|
||||
RewriteRule .* index.php [L]
|
||||
## End - Index
|
||||
|
||||
# Block various user files from being accessed directly
|
||||
RewriteRule ^user/accounts/(.*)$ error [R=301,L]
|
||||
RewriteRule ^user/config/(.*)$ error [R=301,L]
|
||||
RewriteRule ^user/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ error [R=301,L]
|
||||
|
||||
# Block cache/
|
||||
RewriteRule ^cache/(.*) error [R=301,L]
|
||||
|
||||
# Block bin/
|
||||
RewriteRule ^bin/(.*)$ error [R=301,L]
|
||||
|
||||
# Block system/
|
||||
RewriteRule ^system/(.*)$ error [R=301,L]
|
||||
|
||||
# Block vendor/
|
||||
RewriteRule ^vendor/(.*)$ error [R=301,L]
|
||||
## Begin - Security
|
||||
# Block all direct access for these folders
|
||||
RewriteRule ^(cache|bin|logs)/(.*) error [L]
|
||||
# Block access to specific file types for these folders
|
||||
RewriteRule ^(system|user|vendor)/(.*)\.(txt|md|html|yaml|php|twig|sh|bat)$ error [L]
|
||||
## End - Security
|
||||
|
||||
</IfModule>
|
||||
|
||||
# Prevent file browsing
|
||||
# Begin - Prevent Browsing
|
||||
Options -Indexes
|
||||
# End - Prevent Browsing
|
||||
|
||||
243
CHANGELOG.md
Normal file
243
CHANGELOG.md
Normal file
@@ -0,0 +1,243 @@
|
||||
# v0.9.11
|
||||
## 12/21/2014
|
||||
|
||||
1. [](#new)
|
||||
* Added support for simple redirects as well as routes
|
||||
2. [](#improved)
|
||||
* Handle Twig errors more cleanly
|
||||
3. [](#bugfix)
|
||||
* Fix for error caused by invalid or missing user agent string
|
||||
* Fix for directory relative links and URL fragments (#pagelink)
|
||||
* Fix for relative links with no subfolder in `base_url`
|
||||
|
||||
# v0.9.10
|
||||
## 12/12/2014
|
||||
|
||||
1. [](#new)
|
||||
* Added Facebook-style `nicetime` date Twig filter
|
||||
2. [](#improved)
|
||||
* Moved `clear-cache` functionality into Cache object required for Admin plugin
|
||||
3. [](#bugfix)
|
||||
* Fix for undefined index with previous/next buttons
|
||||
|
||||
# v0.9.9
|
||||
## 12/05/2014
|
||||
|
||||
1. [](#new)
|
||||
* Added new `@page` collection type
|
||||
* Added `ksort` and `contains` Twig filters
|
||||
* Added `gist` Twig function
|
||||
2. [](#improved)
|
||||
* Refactored Page previous/next/adjacent functionality
|
||||
* Updated to Symfony 2.6 for yaml/console/event-dispatcher libraries
|
||||
* More PSR code fixes
|
||||
3. [](#bugfix)
|
||||
* Fix for over-escaped apostrophes in YAML
|
||||
|
||||
# v0.9.8
|
||||
## 12/01/2014
|
||||
|
||||
1. [](#new)
|
||||
* Added configuration option to set default lifetime on cache saves
|
||||
* Added ability to set HTTP status code from page header
|
||||
* Implemented simple wild-card custom routing
|
||||
2. [](#improved)
|
||||
* Fixed elusive double load to fully cache issue (crossing fingers...)
|
||||
* Ensure Twig tags are treated as block items in markdown
|
||||
* Removed some older deprecated methods
|
||||
* Ensure onPageContentProcessed() event only fires when not cached
|
||||
* More PSR code fixes
|
||||
3. [](#bugfix)
|
||||
* Fix issue with miscalculation of blog separator location `===`
|
||||
|
||||
# v0.9.7
|
||||
## 11/24/2014
|
||||
|
||||
1. [](#improved)
|
||||
* Nginx configuration updated
|
||||
* Added gitter.im badge to README
|
||||
* Removed `set_time_limit()` and put checks around `ignore_user_abort`
|
||||
* More PSR code fixes
|
||||
2. [](#bugfix)
|
||||
* Fix issue with non-valid asset path showing up when they shouldn't
|
||||
* Fix for JS asset pipeline and scripts that don't end in `;`
|
||||
* Fix for schema-based markdown URLs broken routes (eg `mailto:`)
|
||||
|
||||
# v0.9.6
|
||||
## 11/17/2014
|
||||
|
||||
1. [](#improved)
|
||||
* Moved base_url variables into Grav container
|
||||
* Forced media sorting to use natural sort order by default
|
||||
* Various PSR code tidying
|
||||
* Added filename, extension, thumb to all medium objects
|
||||
2. [](#bugfix)
|
||||
* Fix for infinite loop in page.content()
|
||||
* Fix hostname for configuration overrides
|
||||
* Fix for cached configuration
|
||||
* Fix for relative URLs in markdown on installs with no base_url
|
||||
* Fix for page media images with uppercase extension
|
||||
|
||||
# v0.9.5
|
||||
## 11/09/2014
|
||||
|
||||
1. [](#new)
|
||||
* Added quality setting to medium for compression configuration of images
|
||||
* Added new onPageContentProcessed() event that is post-content processing but pre-caching
|
||||
2. [](#improved)
|
||||
* Added support for AND and OR taxonomy filtering. AND by default (was OR)
|
||||
* Added specific clearing options for CLI clear-cache command
|
||||
* Moved environment method to URI so it can be accessible in plugins and themes
|
||||
* Set Grav's output variable to public so it can be manipulated in onOutputGenerated event
|
||||
* Updated vendor libraries to latest versions
|
||||
* Better handing of 'home' in active menu state detection
|
||||
* Various PSR code tidying
|
||||
* Improved some error messages and notices
|
||||
3. [](#bugfix)
|
||||
* Force route rebuild when configuration changes
|
||||
* Fix for 'installed undefined' error in CLI versions command
|
||||
* Do not remove the JSON/Text error handlers
|
||||
* Fix for supporting inline JS and CSS when Asset pipeline enabled
|
||||
* Fix for Data URLs in CSS being badly formed
|
||||
* Fix Markdown links with fragment and query elements
|
||||
|
||||
# v0.9.4
|
||||
## 10/29/2014
|
||||
|
||||
1. [](#new)
|
||||
* New improved Debugbar with messages, timing, config, twig information
|
||||
* New exception handling system utilizing Whoops
|
||||
* New logging system utilizing Monolog
|
||||
* Support for auto-detecting environment configuration
|
||||
* New version command for CLI
|
||||
* Integrate Twig dump() calls into Debugbar
|
||||
2. [](#improved)
|
||||
* Selfupgrade now clears cache on successful upgrade
|
||||
* Selfupgrade now supports files without extensions
|
||||
* Improved error messages when plugin is missing
|
||||
* Improved security in .htaccess
|
||||
* Support CSS/JS/Image assets in vendor/system folders via .htaccess
|
||||
* Add support for system timers
|
||||
* Improved and optimized configuration loading
|
||||
* Automatically disable Debugbar on non-HTML pages
|
||||
* Disable Debugbar by default
|
||||
3. [](#bugfix)
|
||||
* More YAML blueprint fixes
|
||||
* Fix potential double // in assets
|
||||
* Load debugger as early as possible
|
||||
|
||||
# v0.9.3
|
||||
## 10/09/2014
|
||||
|
||||
1. [](#new)
|
||||
* GPM (Grav Package Manager) Added
|
||||
* Support for multiple Grav configurations
|
||||
* Dynamic media support via URL
|
||||
* Added inlineCss and inlineJs support for Assets
|
||||
2. [](#improved)
|
||||
* YAML caching for increased performance
|
||||
* Use stream wrapper in pages, plugins and themes
|
||||
* Switched to RocketTheme toolbox for some core functionality
|
||||
* Renamed `setup` CLI command to `sandbox`
|
||||
* Broke cache types out into multiple directories in the cache folder
|
||||
* Removed vendor libs from github repository
|
||||
* Various PSR cleanup of code
|
||||
* Various Blueprint updates to support upcoming Admin plugin
|
||||
* Added ability to filter page children for normal/modular/all
|
||||
* Added `sort_by_key` twig filter
|
||||
* Added `visible()` and `routable()` filters to page collections
|
||||
* Use session class in shutdown process
|
||||
* Improvements to modular page loading
|
||||
* Various code cleanup and optimizations
|
||||
3. [](#bugfix)
|
||||
* Fixed file checking not updating the last modified time. For real this time!
|
||||
* Switched debugger to PRODUCTION mode by default
|
||||
* Various fixes in URI class for increased reliability
|
||||
|
||||
# v0.9.2
|
||||
## 09/15/2014
|
||||
|
||||
1. [](#new)
|
||||
* New flexible site and page metadata support including ObjectGraph and Facebook
|
||||
* New method to get user IP address in URI object
|
||||
* Added new onShutdown() event that fires after connection is closed for Async features
|
||||
2. [](#improved)
|
||||
* Skip assets pipeline minify on Windows platforms by default due to PHP issue 47689
|
||||
* Fixed multiple level menus not highlighting correctly
|
||||
* Updated some blueprints in preparation for admin plugin
|
||||
* Fail gracefully when theme does not exist
|
||||
* Add stream support into ResourceLocator::addPath()
|
||||
* Separate themes from plugins, add themes:// stream and onTask events
|
||||
* Added barDump() to Debugger
|
||||
* Removed stray test page
|
||||
* Override modified only if a non-markdown file was modified
|
||||
* Added assets attributes support
|
||||
* Auto-run composer install when running the Grav CLI
|
||||
* Vendor folder removed from repository
|
||||
* Minor configuration performance optimizations
|
||||
* Minor debugger performance optimizations
|
||||
3. [](#bugfix)
|
||||
* Fix url() twig function when Grav isn't installed at root
|
||||
* Workaround for PHP bug 52065
|
||||
* Fixed getList() method on Pages object that was not working
|
||||
* Fix for open_basedir error
|
||||
* index.php now warns if not running on PHP 5.4
|
||||
* Removed memcached option (redundant)
|
||||
* Removed memcache from auto setup, added memcache server configuration option
|
||||
* Fix broken password validation
|
||||
* Back to proper PSR-4 Autoloader
|
||||
|
||||
# v0.9.1
|
||||
## 09/02/2014
|
||||
|
||||
1. [](#new)
|
||||
* Added new `theme://` PHP stream for current theme
|
||||
2. [](#improved)
|
||||
* Default to new `file` modification checking rather than `folder`
|
||||
* Added support for various markdown link formats to convert to Grav-friendly URLs
|
||||
* Moved configure() from Theme to Themes class
|
||||
* Fix autoloading without composer update -o
|
||||
* Added support for Twig url method
|
||||
* Minor code cleanup
|
||||
3. [](#bugfix)
|
||||
* Fixed issue with page changes not being picked up
|
||||
* Fixed Minify to provide `@supports` tag compatibility
|
||||
* Fixed ResourceLocator not working with multiple paths
|
||||
* Fixed issue with Markdown process not stripping LFs
|
||||
* Restrict file type extensions for added security
|
||||
* Fixed template inheritance
|
||||
* Moved Browser class to proper location
|
||||
|
||||
# v0.9.0
|
||||
## 08/25/2014
|
||||
|
||||
1. [](#new)
|
||||
* Addition of Dependency Injection Container
|
||||
* Refactored plugins to use Symfony Event Dispatcher
|
||||
* New Asset Manager to provide unified management of JavaScript and CSS
|
||||
* Asset Pipelining to provide unification, minify, and optimazation of JavaScript and CSS
|
||||
* Grav Media support directly in Markdown syntax
|
||||
* Additional Grav Generator meta tag in default themes
|
||||
* Added support for PHP Stream Wrapper for resource location
|
||||
* Markdown Extra support
|
||||
* Browser object for fast browser detection
|
||||
2. [](#improved)
|
||||
* PSR-4 Autoloader mechanism
|
||||
* Tracy Debugger new `detect` option to detect running environment
|
||||
* Added new `random` collection sort option
|
||||
* Make media images progressive by default
|
||||
* Additional URI filtering for improved security
|
||||
* Safety checks to ensure PHP 5.4.0+
|
||||
* Move to Slidebars side navigation in default Antimatter theme
|
||||
* Updates to `.htaccess` including section on `RewriteBase` which is needed for some hosting providers
|
||||
3. [](#bugfix)
|
||||
* Fixed issue when installing in an apache userdir (~username) folder
|
||||
* Various mobile CSS issues in default themes
|
||||
* Various minor bug fixes
|
||||
|
||||
|
||||
# v0.8.0
|
||||
## 08/13/2014
|
||||
|
||||
1. [](#new)
|
||||
* Initial Release
|
||||
31
README.md
31
README.md
@@ -1,6 +1,7 @@
|
||||
#  Grav
|
||||
[](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 is a **Fast**, **Simple**, and **Flexible**, file-based Web-platform. There is **Zero** installation required. Just extract the ZIP archive, and you are already up and running. It follows similar principals to other flat-file CMS platforms, but has a different design philosophy than most. Grav comes with a powerful **Package Management System** to allow for simple installation and upgrading of plugins and themes, as well as simple updating of Grav itself.
|
||||
|
||||
The underlying architecture of Grav has been designed to use well-established and _best-in-class_ technologies, where applicable, to ensure that Grav is simple to use and easy to extend. Some of these key technologies include:
|
||||
|
||||
@@ -38,6 +39,34 @@ You can download a **ready-built** package from the [Downloads page on http://ge
|
||||
|
||||
Check out the [install procedures](http://learn.getgrav.org/basics/installation) for more information.
|
||||
|
||||
# Adding Functionality
|
||||
|
||||
You can download manually from the [Downloads page on http://getgrav.org](http://getgrav.org/downloads), but the preferred solution is to use the [Grav Package Manager](http://learn.getgrav.org/advanced/grav-gpm) or `GPM`:
|
||||
|
||||
```
|
||||
$ bin/gpm index
|
||||
```
|
||||
|
||||
This will display all the available plugins and then you can install one ore more with:
|
||||
|
||||
```
|
||||
$ bin/gpm install <plugin/theme>
|
||||
```
|
||||
|
||||
# Updating
|
||||
|
||||
To update Grav you should use the [Grav Package Manager](http://learn.getgrav.org/advanced/grav-gpm) or `GPM`:
|
||||
|
||||
```
|
||||
$ bin/gpm selfupgrade
|
||||
```
|
||||
|
||||
To update plugins and themes:
|
||||
|
||||
```
|
||||
$ bin/gpm update
|
||||
```
|
||||
|
||||
|
||||
# Contributing
|
||||
We appreciate any contribution to Grav, whether it is related to bugs, grammar, or simply a suggestion or improvement.
|
||||
|
||||
BIN
bin/composer.phar
Executable file
BIN
bin/composer.phar
Executable file
Binary file not shown.
44
bin/gpm
Executable file
44
bin/gpm
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
define('GRAV_CLI', true);
|
||||
|
||||
if (version_compare($ver = PHP_VERSION, $req = '5.4.0', '<')) {
|
||||
exit(sprintf("You are running PHP %s, but Grav needs at least PHP %s to run.\n", $ver, $req));
|
||||
}
|
||||
|
||||
if (!file_exists(__DIR__ . '/../vendor')){
|
||||
// Before we can even start, we need to run composer first
|
||||
echo "Preparing to install vendor dependencies...\n\n";
|
||||
echo system('php bin/composer.phar --working-dir="'.__DIR__.'/../" --no-interaction install');
|
||||
echo "\n\n";
|
||||
}
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
use Grav\Common\Grav;
|
||||
|
||||
$autoload = require_once(__DIR__ . '/../vendor/autoload.php');
|
||||
|
||||
if (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('UTC');
|
||||
}
|
||||
|
||||
if (!file_exists(ROOT_DIR . 'index.php')) {
|
||||
exit('FATAL: Must be run from ROOT directory of Grav!');
|
||||
}
|
||||
|
||||
$grav = Grav::instance(array('loader' => $autoload));
|
||||
$grav['config']->init();
|
||||
$grav['streams'];
|
||||
$grav['plugins']->init();
|
||||
$grav['themes']->init();
|
||||
|
||||
$app = new Application('Grav Package Manager', GRAV_VERSION);
|
||||
$app->addCommands(array(
|
||||
new \Grav\Console\Gpm\IndexCommand(),
|
||||
new \Grav\Console\Gpm\VersionCommand(),
|
||||
new \Grav\Console\Gpm\InfoCommand(),
|
||||
new \Grav\Console\Gpm\InstallCommand(),
|
||||
new \Grav\Console\Gpm\UpdateCommand(),
|
||||
new \Grav\Console\Gpm\SelfupgradeCommand(),
|
||||
));
|
||||
$app->run();
|
||||
22
bin/grav
22
bin/grav
@@ -1,13 +1,21 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
define('GRAV_CLI', true);
|
||||
|
||||
if (version_compare($ver = PHP_VERSION, $req = '5.4.0', '<')) {
|
||||
exit(sprintf("You are running PHP %s, but Grav needs at least PHP %s to run.\n", $ver, $req));
|
||||
}
|
||||
|
||||
if (!file_exists(__DIR__ . '/../vendor')){
|
||||
// Before we can even start, we need to run composer first
|
||||
echo "Preparing to install vendor dependencies...\n\n";
|
||||
echo system('php bin/composer.phar --working-dir="'.__DIR__.'/../" --no-interaction install');
|
||||
echo "\n\n";
|
||||
}
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
require_once(__DIR__ . '/../vendor/autoload.php');
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
if (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('UTC');
|
||||
@@ -19,11 +27,11 @@ if (!file_exists(ROOT_DIR . 'index.php')) {
|
||||
|
||||
$app = new Application('Grav CLI Application', '0.1.0');
|
||||
$app->addCommands(array(
|
||||
new Grav\Console\InstallCommand(),
|
||||
new Grav\Console\SetupCommand(),
|
||||
new Grav\Console\CleanCommand(),
|
||||
new Grav\Console\ClearCacheCommand(),
|
||||
new Grav\Console\BackupCommand(),
|
||||
new Grav\Console\NewProjectCommand(),
|
||||
new Grav\Console\Cli\InstallCommand(),
|
||||
new Grav\Console\Cli\SandboxCommand(),
|
||||
new Grav\Console\Cli\CleanCommand(),
|
||||
new Grav\Console\Cli\ClearCacheCommand(),
|
||||
new Grav\Console\Cli\BackupCommand(),
|
||||
new Grav\Console\Cli\NewProjectCommand(),
|
||||
));
|
||||
$app->run();
|
||||
|
||||
@@ -9,30 +9,33 @@
|
||||
"php": ">=5.4.0",
|
||||
"twig/twig": "~1.16",
|
||||
"erusev/parsedown-extra": "dev-master",
|
||||
"symfony/yaml": "~2.5",
|
||||
"symfony/console": "~2.5",
|
||||
"symfony/event-dispatcher": "~2.5",
|
||||
"symfony/yaml": "~2.6",
|
||||
"symfony/console": "~2.6",
|
||||
"symfony/event-dispatcher": "~2.6",
|
||||
"doctrine/cache": "~1.3",
|
||||
"tracy/tracy": "~2.2",
|
||||
"gregwar/image": "~2.0",
|
||||
"maximebf/debugbar": "dev-master",
|
||||
"filp/whoops": "1.2.*@dev",
|
||||
"monolog/monolog": "~1.1",
|
||||
"gregwar/image": "~2.0",
|
||||
"ircmaxell/password-compat": "1.0.*",
|
||||
"mrclay/minify": "dev-master",
|
||||
"donatj/phpuseragentparser": "dev-master",
|
||||
"pimple/pimple": "~3.0"
|
||||
"pimple/pimple": "~3.0",
|
||||
"rockettheme/toolbox": "dev-develop"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/rockettheme/toolbox"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Grav\\": "system/src/"
|
||||
"psr-4": {
|
||||
"Grav\\": "system/src/Grav"
|
||||
},
|
||||
"files": ["system/defines.php"]
|
||||
},
|
||||
"archive": {
|
||||
"exclude": ["VERSION"]
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/rhukster/minify"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
56
htaccess.txt
Normal file
56
htaccess.txt
Normal file
@@ -0,0 +1,56 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
## Begin - Exploits
|
||||
# If you experience problems on your site block out the operations listed below
|
||||
# This attempts to block the most common type of exploit `attempts` to Grav
|
||||
#
|
||||
# Block out any script trying to base64_encode data within the URL.
|
||||
RewriteCond %{QUERY_STRING} base64_encode[^(]*\([^)]*\) [OR]
|
||||
# Block out any script that includes a <script> tag in URL.
|
||||
RewriteCond %{QUERY_STRING} (<|%3C)([^s]*s)+cript.*(>|%3E) [NC,OR]
|
||||
# Block out any script trying to set a PHP GLOBALS variable via URL.
|
||||
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
|
||||
# Block out any script trying to modify a _REQUEST variable via URL.
|
||||
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2})
|
||||
# Return 403 Forbidden header and show the content of the root homepage
|
||||
RewriteRule .* index.php [F]
|
||||
#
|
||||
## End - Exploits
|
||||
|
||||
## Begin RewriteBase
|
||||
# If you are getting 404 errors on subpages, you may have to uncomment the RewriteBase entry
|
||||
# You should change the '/' to your appropriate subfolder. For example if you have
|
||||
# your Grav install at the root of your site '/' should work, else it might be something
|
||||
# along the lines of: RewriteBase /<your_sub_folder>
|
||||
##
|
||||
|
||||
# RewriteBase /
|
||||
|
||||
## End - RewriteBase
|
||||
|
||||
## Begin - Index
|
||||
# If the requested path and file is not /index.php and the request
|
||||
# has not already been internally rewritten to the index.php script
|
||||
RewriteCond %{REQUEST_URI} !^/index\.php
|
||||
# and the requested path and file doesn't directly match a physical file
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
# and the requested path and file doesn't directly match a physical folder
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
# internally rewrite the request to the index.php script
|
||||
RewriteRule .* index.php [L]
|
||||
## End - Index
|
||||
|
||||
## Begin - Security
|
||||
# Block all direct access for these folders
|
||||
RewriteRule ^(cache|bin|logs)/(.*) error [L]
|
||||
# Block access to specific file types for these folders
|
||||
RewriteRule ^(system|user|vendor)/(.*)\.(txt|md|html|yaml|php|twig|sh|bat)$ error [L]
|
||||
## End - Security
|
||||
|
||||
</IfModule>
|
||||
|
||||
# Begin - Prevent Browsing
|
||||
Options -Indexes
|
||||
# End - Prevent Browsing
|
||||
12
index.php
12
index.php
@@ -4,13 +4,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));
|
||||
}
|
||||
|
||||
$autoload = __DIR__ . '/vendor/autoload.php';
|
||||
if (!is_file($autoload)) {
|
||||
exit('Please run: <i>composer install -o</i>');
|
||||
exit('Please run: <i>bin/grav install</i>');
|
||||
}
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Debugger;
|
||||
|
||||
// Register the auto-loader.
|
||||
$loader = require_once $autoload;
|
||||
@@ -20,14 +20,12 @@ if (!ini_get('date.timezone')) {
|
||||
}
|
||||
|
||||
$grav = Grav::instance(
|
||||
[
|
||||
'loader' => $loader,
|
||||
'debugger' => new Debugger(Debugger::PRODUCTION)
|
||||
]
|
||||
array(
|
||||
'loader' => $loader
|
||||
)
|
||||
);
|
||||
|
||||
try {
|
||||
$grav['debugger']->init();
|
||||
$grav->process();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
|
||||
32
nginx.conf
Executable file → Normal file
32
nginx.conf
Executable file → Normal file
@@ -27,26 +27,26 @@ http {
|
||||
}
|
||||
|
||||
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/(.*)$ /error redirect;
|
||||
}
|
||||
location /system {
|
||||
rewrite ^/system/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
|
||||
}
|
||||
|
||||
location /vendor {
|
||||
rewrite ^/vendor/(.*)$ /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
|
||||
|
||||
54
system/assets/debugger.css
Normal file
54
system/assets/debugger.css
Normal file
@@ -0,0 +1,54 @@
|
||||
div.phpdebugbar {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.phpdebugbar pre {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header > div > * {
|
||||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header > div.phpdebugbar-header-right > * {
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header, .phpdebugbar a.phpdebugbar-restore-btn {
|
||||
background-image: url(grav.png);
|
||||
}
|
||||
|
||||
.phpdebugbar a.phpdebugbar-restore-btn {
|
||||
width: 13px;
|
||||
}
|
||||
|
||||
.phpdebugbar a.phpdebugbar-tab.phpdebugbar-active {
|
||||
background: #3DB9EC;
|
||||
color: #fff;
|
||||
margin-top: -1px;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.phpdebugbar .phpdebugbar-widgets-toolbar {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.phpdebugbar input[type=text] {
|
||||
padding: 0;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.phpdebugbar dl.phpdebugbar-widgets-varlist, ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label {
|
||||
font-family: "DejaVu Sans Mono", Menlo, Monaco, Consolas, Courier, monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label {
|
||||
text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.phpdebugbar pre, .phpdebugbar code {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
BIN
system/assets/grav.png
Normal file
BIN
system/assets/grav.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 548 B |
110
system/assets/whoops.css
Normal file
110
system/assets/whoops.css
Normal file
@@ -0,0 +1,110 @@
|
||||
body {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
body header {
|
||||
background: #349886;
|
||||
border-left: 8px solid #29796B;
|
||||
}
|
||||
|
||||
body .clipboard {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcAQMAAABIw03XAAAAA3NCSVQICAjb4U/gAAAABlBMVEX///////9VfPVsAAAAAnRSTlP/AOW3MEoAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAFnRFWHRDcmVhdGlvbiBUaW1lADEwLzE1LzE0xr/LJAAAADhJREFUCJlj+P///wcGBPGDQR5E8OMi2IEEczOIaAQRHSCioQBGHAAR/7AT/z+DiA8MMALVXhABAJf9Sr5aY+UFAAAAAElFTkSuQmCC);
|
||||
}
|
||||
|
||||
body .exc-title-primary {
|
||||
color: #1C3631;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
body .exc-title {
|
||||
color: #2F5B52;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
body .data-table-container label {
|
||||
color: #0082BA;
|
||||
}
|
||||
|
||||
body .frame {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
body .frames-container {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
body .active .frame-class {
|
||||
color: #E3D8E9;
|
||||
}
|
||||
|
||||
body .frame-class {
|
||||
color: #9055AF;
|
||||
}
|
||||
|
||||
body .frame.active {
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
background-color: #9055AF;
|
||||
}
|
||||
|
||||
body .frame:not(.active):hover {
|
||||
background: #e9e9e9;
|
||||
}
|
||||
|
||||
body .frame-file, body .data-table tbody {
|
||||
font-family: "DejaVu Sans Mono", Menlo, Monaco, Consolas, Courier, monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
body .frame-code {
|
||||
background: #305669;
|
||||
border-left: 8px solid #253A47;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
body .frame-code .frame-file {
|
||||
background: #253A47;
|
||||
color: #eee;
|
||||
text-shadow: none;
|
||||
box-shadow: none;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
body .frame-code .frame-file strong {
|
||||
color: #fff;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
body .frame-comments {
|
||||
background: #283E4D;
|
||||
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
body .frame-comments.empty:before {
|
||||
color: #789AAB;
|
||||
}
|
||||
|
||||
body .details-container {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
body .details {
|
||||
background-color: #eee;
|
||||
border-left: 8px solid #ddd;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
body .code-block {
|
||||
background: #2C4454;
|
||||
box-shadow: none;
|
||||
font-family: "DejaVu Sans Mono", Menlo, Monaco, Consolas, Courier, monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
body .handler.active {
|
||||
background: #666;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
title: Media
|
||||
validation: loose
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
70
system/blueprints/config/site.yaml
Normal file
70
system/blueprints/config/site.yaml
Normal file
@@ -0,0 +1,70 @@
|
||||
title: Site
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
content:
|
||||
type: section
|
||||
title: Defaults
|
||||
|
||||
fields:
|
||||
title:
|
||||
type: text
|
||||
label: Site Title
|
||||
size: large
|
||||
placeholder: "Site wide title"
|
||||
help: Default title for your site
|
||||
|
||||
author.name:
|
||||
type: text
|
||||
size: large
|
||||
label: Default Author
|
||||
|
||||
author.email:
|
||||
type: text
|
||||
size: large
|
||||
label: Default Email
|
||||
|
||||
taxonomies:
|
||||
type: text
|
||||
size: large
|
||||
label: Taxonomy Types
|
||||
classes: fancy
|
||||
validate:
|
||||
type: commalist
|
||||
|
||||
metadata:
|
||||
type: array
|
||||
label: Metadata
|
||||
placeholder_key: Name
|
||||
placeholder_value: Content
|
||||
|
||||
blog:
|
||||
type: section
|
||||
title: Blog
|
||||
|
||||
fields:
|
||||
blog.route:
|
||||
type: text
|
||||
size: large
|
||||
label: Blog URL
|
||||
|
||||
summary.size:
|
||||
type: text
|
||||
size: x-small
|
||||
label: Summary Size
|
||||
validate:
|
||||
type: int
|
||||
min: 0
|
||||
max: 65536
|
||||
|
||||
routes:
|
||||
type: section
|
||||
title: Routes
|
||||
|
||||
fields:
|
||||
routes:
|
||||
type: array
|
||||
label: Custom
|
||||
placeholder_key: /your/alias
|
||||
placeholder_value: /your/route
|
||||
7
system/blueprints/config/streams.yaml
Normal file
7
system/blueprints/config/streams.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
title: File Streams
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
schemes.xxx:
|
||||
type: array
|
||||
364
system/blueprints/config/system.yaml
Normal file
364
system/blueprints/config/system.yaml
Normal file
@@ -0,0 +1,364 @@
|
||||
title: System
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
content:
|
||||
type: section
|
||||
title: Content
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
home.alias:
|
||||
type: pages
|
||||
size: medium
|
||||
classes: fancy
|
||||
label: Home Page
|
||||
show_all: false
|
||||
show_modular: false
|
||||
show_root: false
|
||||
help: "The page that Grav will use as the default landing page"
|
||||
|
||||
pages.theme:
|
||||
type: themeselect
|
||||
classes: fancy
|
||||
size: medium
|
||||
label: Default Theme
|
||||
help: "Set the theme (defaults to 'default')"
|
||||
|
||||
pages.markdown_extra:
|
||||
type: toggle
|
||||
label: Markdown Extra
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
pages.process:
|
||||
type: checkboxes
|
||||
label: Process
|
||||
default: [markdown: true, twig: true]
|
||||
options:
|
||||
markdown: Markdown
|
||||
twig: Twig
|
||||
use: keys
|
||||
|
||||
pages.dateformat.short:
|
||||
type: select
|
||||
size: medium
|
||||
classes: fancy
|
||||
label: Short Date Format
|
||||
help: "Set the short date format"
|
||||
default: 'jS M Y'
|
||||
options:
|
||||
'F jS \\a\\t g:ia': "January 1st at 11:59pm"
|
||||
'l jS of F g:i A': "Monday 1st of January at 11:59 PM"
|
||||
'D, m M Y G:i:s': "Mon, 01 Jan 2014 23:59:00"
|
||||
'd-m-y G:i': "01-01-14 23:59"
|
||||
'jS M Y': "10th Feb 2014"
|
||||
|
||||
pages.dateformat.long:
|
||||
type: select
|
||||
size: medium
|
||||
classes: fancy
|
||||
label: Long Date Format
|
||||
help: "Set the long date format"
|
||||
options:
|
||||
'F jS \a\t g:ia': "January 1st at 11:59pm"
|
||||
'l jS of F g:i A': "Monday 1st of January at 11:59 PM"
|
||||
'D, m M Y G:i:s': "Mon, 01 Jan 2014 23:59:00"
|
||||
'd-m-y G:i': "01-01-14 23:59"
|
||||
'jS M Y': "10th Feb 2014"
|
||||
|
||||
pages.order.by:
|
||||
type: select
|
||||
size: medium
|
||||
classes: fancy
|
||||
label: Default Ordering
|
||||
options:
|
||||
default: Default - based on folder name
|
||||
folder: Folder - based on prefix-less folder name
|
||||
title: Title - based on title field in header
|
||||
date: Date - based on date field in header
|
||||
|
||||
pages.order.dir:
|
||||
type: toggle
|
||||
label: Default Order Direction
|
||||
highlight: asc
|
||||
default: desc
|
||||
options:
|
||||
asc: Ascending
|
||||
desc: Descending
|
||||
|
||||
pages.list.count:
|
||||
type: text
|
||||
size: x-small
|
||||
label: Default Item Count
|
||||
help: "Default max pages count"
|
||||
validate:
|
||||
type: number
|
||||
min: 1
|
||||
|
||||
|
||||
|
||||
events:
|
||||
type: section
|
||||
title: Events
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
pages.events.page:
|
||||
type: toggle
|
||||
label: Page events
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
pages.events.twig:
|
||||
type: toggle
|
||||
label: Twig events
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
caching:
|
||||
type: section
|
||||
title: Caching
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
cache.enabled:
|
||||
type: toggle
|
||||
label: Caching
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
cache.check.method:
|
||||
type: select
|
||||
size: small
|
||||
classes: fancy
|
||||
label: Cache Check Method
|
||||
options:
|
||||
file: File
|
||||
folder: Folder
|
||||
none: None
|
||||
|
||||
cache.driver:
|
||||
type: select
|
||||
size: small
|
||||
classes: fancy
|
||||
label: Cache driver
|
||||
options:
|
||||
auto: Auto detect
|
||||
file: File
|
||||
apc: APC
|
||||
xcache: XCache
|
||||
memcache: MemCache
|
||||
wincache: WinCache
|
||||
|
||||
cache.prefix:
|
||||
type: text
|
||||
size: x-small
|
||||
label: Cache Prefix
|
||||
placeholder: "Derived from base URL (override by entering random string)"
|
||||
|
||||
twig:
|
||||
type: section
|
||||
title: Twig Templating
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
twig.cache:
|
||||
type: toggle
|
||||
label: Twig caching
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
twig.debug:
|
||||
type: toggle
|
||||
label: Twig debug
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
twig.auto_reload:
|
||||
type: toggle
|
||||
label: Detect changes
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
twig.autoescape:
|
||||
type: toggle
|
||||
label: Autoescape variables
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
assets:
|
||||
type: section
|
||||
title: Assets
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
assets.css_pipeline:
|
||||
type: toggle
|
||||
label: CSS Pipeline
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
assets.css_minify:
|
||||
type: toggle
|
||||
label: CSS Minify
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
assets.css_minify_windows:
|
||||
type: toggle
|
||||
label: CSS Minify Windows Override
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
assets.css_rewrite:
|
||||
type: toggle
|
||||
label: CSS Rewrite
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
assets.js_pipeline:
|
||||
type: toggle
|
||||
label: JavaScript Pipeline
|
||||
highlight: 01
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
assets.js_minify:
|
||||
type: toggle
|
||||
label: JavaScript Minify
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
debugger:
|
||||
type: section
|
||||
title: Debugger
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
debugger.enabled:
|
||||
type: toggle
|
||||
label: Debugger
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
debugger.mode:
|
||||
type: select
|
||||
size: small
|
||||
classes: fancy
|
||||
label: Mode
|
||||
options:
|
||||
detect: Auto-Detect
|
||||
development: Development
|
||||
production: Production
|
||||
|
||||
debugger.strict:
|
||||
type: toggle
|
||||
label: Strict
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
debugger.max_depth:
|
||||
type: select
|
||||
size: small
|
||||
classes: fancy
|
||||
label: Detail Level
|
||||
placeholder: "How many nested levels to display for objects or arrays"
|
||||
options:
|
||||
1: 1 level
|
||||
2: 2 levels
|
||||
3: 3 levels
|
||||
4: 4 levels
|
||||
5: 5 levels
|
||||
6: 6 levels
|
||||
7: 7 levels
|
||||
8: 8 levels
|
||||
9: 9 levels
|
||||
10: 10 levels
|
||||
validate:
|
||||
type: number
|
||||
|
||||
debugger.log.enabled:
|
||||
type: toggle
|
||||
label: Logging
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
debugger.shutdown.close_connection:
|
||||
type: toggle
|
||||
label: Shutdown Close Connection
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
54
system/blueprints/pages/modular_new.yaml
Normal file
54
system/blueprints/pages/modular_new.yaml
Normal file
@@ -0,0 +1,54 @@
|
||||
rules:
|
||||
slug:
|
||||
pattern: "[a-z][a-z0-9_\-]+"
|
||||
min: 2
|
||||
max: 80
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
section:
|
||||
type: section
|
||||
title: Add Modular Content
|
||||
|
||||
title:
|
||||
type: text
|
||||
label: Page Title
|
||||
validate:
|
||||
required: true
|
||||
|
||||
folder:
|
||||
type: text
|
||||
label: Folder Name
|
||||
validate:
|
||||
type: slug
|
||||
required: true
|
||||
|
||||
|
||||
|
||||
route:
|
||||
type: select
|
||||
label: Page
|
||||
classes: fancy
|
||||
@data-options: '\Grav\Common\Page\Pages::parents'
|
||||
@data-default: '\Grav\Plugin\admin::route'
|
||||
options:
|
||||
'': '- Select -'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
type:
|
||||
type: select
|
||||
classes: fancy
|
||||
label: Modular Template
|
||||
default: default
|
||||
@data-options: '\Grav\Common\Page\Pages::modularTypes'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
modular:
|
||||
type: hidden
|
||||
default: 1
|
||||
validate:
|
||||
type: bool
|
||||
84
system/blueprints/pages/modular_raw.yaml
Normal file
84
system/blueprints/pages/modular_raw.yaml
Normal file
@@ -0,0 +1,84 @@
|
||||
rules:
|
||||
slug:
|
||||
pattern: "[a-z][a-z0-9_\-]+"
|
||||
min: 2
|
||||
max: 80
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
tabs:
|
||||
type: tabs
|
||||
active: 1
|
||||
|
||||
fields:
|
||||
content:
|
||||
type: tab
|
||||
title: Content
|
||||
|
||||
fields:
|
||||
frontmatter:
|
||||
type: frontmatter
|
||||
label: Frontmatter
|
||||
|
||||
|
||||
content:
|
||||
type: markdown
|
||||
label: Content
|
||||
|
||||
uploads:
|
||||
type: uploads
|
||||
label: Page Media
|
||||
|
||||
|
||||
options:
|
||||
type: tab
|
||||
title: Options
|
||||
|
||||
fields:
|
||||
|
||||
columns:
|
||||
type: columns
|
||||
|
||||
fields:
|
||||
column1:
|
||||
type: column
|
||||
|
||||
fields:
|
||||
|
||||
folder:
|
||||
type: text
|
||||
label: Filename
|
||||
validate:
|
||||
type: slug
|
||||
required: true
|
||||
|
||||
route:
|
||||
type: select
|
||||
label: Parent
|
||||
classes: fancy
|
||||
@data-options: '\Grav\Common\Page\Pages::parents'
|
||||
@data-default: '\Grav\Plugin\admin::route'
|
||||
options:
|
||||
'/': '- Root -'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
type:
|
||||
type: select
|
||||
classes: fancy
|
||||
label: Modular Template
|
||||
default: default
|
||||
@data-options: '\Grav\Common\Page\Pages::modularTypes'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
column2:
|
||||
type: column
|
||||
|
||||
fields:
|
||||
order:
|
||||
type: order
|
||||
label: Ordering
|
||||
|
||||
48
system/blueprints/pages/new.yaml
Normal file
48
system/blueprints/pages/new.yaml
Normal file
@@ -0,0 +1,48 @@
|
||||
rules:
|
||||
slug:
|
||||
pattern: "[a-z][a-z0-9_\-]+"
|
||||
min: 2
|
||||
max: 80
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
section:
|
||||
type: section
|
||||
title: Add Page
|
||||
|
||||
title:
|
||||
type: text
|
||||
label: Page Title
|
||||
validate:
|
||||
required: true
|
||||
|
||||
folder:
|
||||
type: text
|
||||
label: Folder Name
|
||||
validate:
|
||||
type: slug
|
||||
required: true
|
||||
|
||||
|
||||
|
||||
route:
|
||||
type: select
|
||||
label: Parent Page
|
||||
classes: fancy
|
||||
@data-options: '\Grav\Common\Page\Pages::parents'
|
||||
@data-default: '\Grav\Plugin\admin::route'
|
||||
options:
|
||||
'/': '- Root -'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
type:
|
||||
type: select
|
||||
classes: fancy
|
||||
label: Display Template
|
||||
default: default
|
||||
@data-options: '\Grav\Common\Page\Pages::types'
|
||||
validate:
|
||||
required: true
|
||||
@@ -5,14 +5,14 @@ rules:
|
||||
max: 80
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
route:
|
||||
type: select
|
||||
label: Parent
|
||||
@data-options: '\Grav\Common\Page\Pages::parents'
|
||||
@data-default: '\Grav\Plugin\admin::route'
|
||||
options:
|
||||
'': '- Root -'
|
||||
|
||||
title:
|
||||
type: text
|
||||
label: Title
|
||||
validate:
|
||||
required: true
|
||||
|
||||
folder:
|
||||
type: text
|
||||
@@ -21,8 +21,22 @@ form:
|
||||
type: slug
|
||||
required: true
|
||||
|
||||
route:
|
||||
type: select
|
||||
label: Parent
|
||||
classes: fancy
|
||||
@data-options: '\Grav\Common\Page\Pages::parents'
|
||||
@data-default: '\Grav\Plugin\admin::route'
|
||||
options:
|
||||
'/': '- Root -'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
type:
|
||||
type: select
|
||||
label: Page Type
|
||||
classes: fancy
|
||||
label: Display Template
|
||||
default: default
|
||||
@data-options: '\Grav\Common\Page\Pages::types'
|
||||
validate:
|
||||
required: true
|
||||
84
system/blueprints/pages/raw.yaml
Normal file
84
system/blueprints/pages/raw.yaml
Normal file
@@ -0,0 +1,84 @@
|
||||
rules:
|
||||
slug:
|
||||
pattern: "[a-z][a-z0-9_\-]+"
|
||||
min: 2
|
||||
max: 80
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
tabs:
|
||||
type: tabs
|
||||
active: 1
|
||||
|
||||
fields:
|
||||
content:
|
||||
type: tab
|
||||
title: Content
|
||||
|
||||
fields:
|
||||
frontmatter:
|
||||
type: frontmatter
|
||||
label: Frontmatter
|
||||
|
||||
|
||||
content:
|
||||
type: markdown
|
||||
label: Content
|
||||
|
||||
uploads:
|
||||
type: uploads
|
||||
label: Page Media
|
||||
|
||||
|
||||
options:
|
||||
type: tab
|
||||
title: Options
|
||||
|
||||
fields:
|
||||
|
||||
columns:
|
||||
type: columns
|
||||
|
||||
fields:
|
||||
column1:
|
||||
type: column
|
||||
|
||||
fields:
|
||||
|
||||
folder:
|
||||
type: text
|
||||
label: Folder Name
|
||||
validate:
|
||||
type: slug
|
||||
required: true
|
||||
|
||||
route:
|
||||
type: select
|
||||
label: Parent
|
||||
classes: fancy
|
||||
@data-options: '\Grav\Common\Page\Pages::parents'
|
||||
@data-default: '\Grav\Plugin\admin::route'
|
||||
options:
|
||||
'/': '- Root -'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
type:
|
||||
type: select
|
||||
classes: fancy
|
||||
label: Display Template
|
||||
default: default
|
||||
@data-options: '\Grav\Common\Page\Pages::types'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
column2:
|
||||
type: column
|
||||
|
||||
fields:
|
||||
order:
|
||||
type: order
|
||||
label: Ordering
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
title: Site settings
|
||||
validation: strict
|
||||
|
||||
form:
|
||||
fields:
|
||||
title:
|
||||
type: text
|
||||
label: Site title
|
||||
|
||||
description:
|
||||
type: textarea
|
||||
label: Description
|
||||
|
||||
summary.size:
|
||||
type: text
|
||||
label: Summary size
|
||||
validate:
|
||||
type: int
|
||||
min: 0
|
||||
max: 65536
|
||||
|
||||
author.name:
|
||||
type: text
|
||||
label: Default author
|
||||
|
||||
author.email:
|
||||
type: text
|
||||
label: Default email
|
||||
|
||||
taxonomies:
|
||||
type: text
|
||||
label: Taxonomy types
|
||||
validate:
|
||||
type: commalist
|
||||
|
||||
blog.route:
|
||||
type: text
|
||||
label: Blog URL
|
||||
|
||||
routes:
|
||||
type: array
|
||||
label: Custom routes
|
||||
@@ -1,275 +0,0 @@
|
||||
title: Configuration
|
||||
validation: strict
|
||||
|
||||
form:
|
||||
fields:
|
||||
basics:
|
||||
type: section
|
||||
title: Basics
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
title:
|
||||
type: text
|
||||
label: Site Title
|
||||
placeholder: "Site wide title"
|
||||
help: Default title for your site
|
||||
|
||||
base_url_absolute:
|
||||
type: text
|
||||
label: Absolute Base URL
|
||||
placeholder: "Override Absolute base URL (e.g. http://example.com)"
|
||||
help: You can provide a base URL to use rather than letting Grav guess what it is
|
||||
|
||||
base_url_relative:
|
||||
type: text
|
||||
label: Relative Base URL
|
||||
placeholder: "Override Relative base URL (e.g. /subdirectory/site)"
|
||||
help: You can provide a base URL to use rather than letting Grav guess what it is
|
||||
|
||||
pages.dateformat.short:
|
||||
type: select
|
||||
label: PHP date format
|
||||
help: "Set the PHP date format"
|
||||
default: 'jS M Y'
|
||||
options:
|
||||
'F jS \\a\\t g:ia': "January 1st at 11:59pm"
|
||||
'l jS of F g:i A': "Monday 1st of January at 11:59 PM"
|
||||
'D, m M Y G:i:s': "Mon, 01 Jan 2014 23:59:00"
|
||||
'd-m-y G:i': "01-01-14 23:59"
|
||||
'jS M Y': "10th Feb 2014"
|
||||
|
||||
pages.dateformat.long:
|
||||
type: select
|
||||
label: Default date format
|
||||
help: "Set default date format rather than default for |date() filter"
|
||||
options:
|
||||
'F jS \a\t g:ia': "January 1st at 11:59pm"
|
||||
'l jS of F g:i A': "Monday 1st of January at 11:59 PM"
|
||||
'D, m M Y G:i:s': "Mon, 01 Jan 2014 23:59:00"
|
||||
'd-m-y G:i': "01-01-14 23:59"
|
||||
'jS M Y': "10th Feb 2014"
|
||||
|
||||
pages.theme:
|
||||
type: themeselect
|
||||
label: Default Theme
|
||||
help: "Set the theme (defaults to 'default')"
|
||||
|
||||
content:
|
||||
type: section
|
||||
title: Content
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
home.alias:
|
||||
type: pages
|
||||
label: Home page
|
||||
show_all: false
|
||||
show_modular: false
|
||||
show_root: false
|
||||
help: "The page that Grav will use as the default landing page"
|
||||
|
||||
pages.order.by:
|
||||
type: select
|
||||
label: Default ordering
|
||||
options:
|
||||
default: Default - based on folder name
|
||||
folder: Folder - based on prefix-less folder name
|
||||
title: Title - based on title field in header
|
||||
date: Date - based on date field in header
|
||||
|
||||
pages.order.dir:
|
||||
type: toggle
|
||||
label: Ordering direction
|
||||
default: asc
|
||||
options:
|
||||
asc: Ascending
|
||||
desc: Descending
|
||||
|
||||
pages.list.count:
|
||||
type: text
|
||||
label: Item count
|
||||
help: "Default max pages count"
|
||||
validate:
|
||||
type: number
|
||||
min: 1
|
||||
|
||||
pages.process:
|
||||
type: checkboxes
|
||||
label: Process
|
||||
default: [markdown: true, twig: true]
|
||||
options:
|
||||
markdown: Markdown
|
||||
twig: Twig
|
||||
use: keys
|
||||
|
||||
events:
|
||||
type: section
|
||||
title: Events
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
pages.events.page:
|
||||
type: toggle
|
||||
label: Page events
|
||||
highlight: 1
|
||||
options:
|
||||
1: Enabled
|
||||
0: Disabled
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
pages.events.twig:
|
||||
type: toggle
|
||||
label: Twig events
|
||||
highlight: 1
|
||||
options:
|
||||
1: Enabled
|
||||
0: Disabled
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
caching:
|
||||
type: section
|
||||
title: Caching
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
cache.enabled:
|
||||
type: toggle
|
||||
label: Caching
|
||||
highlight: 1
|
||||
options:
|
||||
1: Enabled
|
||||
0: Disabled
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
cache.check.pages:
|
||||
type: toggle
|
||||
label: Detect changes in pages
|
||||
help: Be careful changing this setting. If you disable this setting, Grav no longer automatically updates the pages when the underlaying files are changed.
|
||||
highlight: 1
|
||||
default: 1
|
||||
options:
|
||||
1: Enabled
|
||||
0: Disabled
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
cache.prefix:
|
||||
type: text
|
||||
label: Cache prefix
|
||||
placeholder: "Derived from base URL (override by entering random string)"
|
||||
|
||||
cache.driver:
|
||||
type: select
|
||||
label: Cache driver
|
||||
options:
|
||||
auto: Auto detect
|
||||
file: File
|
||||
apc: APC
|
||||
xcache: XCache
|
||||
memcache: MemCache
|
||||
memcached: MemCached
|
||||
wincache: WinCache
|
||||
|
||||
twig:
|
||||
type: section
|
||||
title: Twig Templating
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
twig.cache:
|
||||
type: toggle
|
||||
label: Twig caching
|
||||
highlight: 1
|
||||
options:
|
||||
1: Enabled
|
||||
0: Disabled
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
twig.debug:
|
||||
type: toggle
|
||||
label: Twig debug
|
||||
highlight: 1
|
||||
options:
|
||||
1: Enabled
|
||||
0: Disabled
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
twig.auto_reload:
|
||||
type: toggle
|
||||
label: Detect changes
|
||||
highlight: 1
|
||||
options:
|
||||
1: Enabled
|
||||
0: Disabled
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
twig.autoescape:
|
||||
type: toggle
|
||||
label: Autoescape variables
|
||||
highlight: 1
|
||||
options:
|
||||
1: Enabled
|
||||
0: Disabled
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
debugger:
|
||||
type: section
|
||||
title: Debugger
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
debugger.enabled:
|
||||
type: toggle
|
||||
label: Debugger
|
||||
highlight: 1
|
||||
options:
|
||||
1: Enabled
|
||||
0: Disabled
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
debugger.max_depth:
|
||||
type: select
|
||||
label: Detail levels
|
||||
placeholder: "How many nested levels to display for objects or arrays"
|
||||
options:
|
||||
1: 1 level
|
||||
2: 2 levels
|
||||
3: 3 levels
|
||||
4: 4 levels
|
||||
5: 5 levels
|
||||
6: 6 levels
|
||||
7: 7 levels
|
||||
8: 8 levels
|
||||
9: 9 levels
|
||||
10: 10 levels
|
||||
validate:
|
||||
type: number
|
||||
|
||||
debugger.log.enabled:
|
||||
type: toggle
|
||||
label: Logging
|
||||
highlight: 1
|
||||
options:
|
||||
1: Enabled
|
||||
0: Disabled
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
debugger.log.timing:
|
||||
type: toggle
|
||||
label: Log timings
|
||||
highlight: 1
|
||||
options:
|
||||
1: Enabled
|
||||
0: Disabled
|
||||
validate:
|
||||
type: bool
|
||||
86
system/blueprints/user/account.yaml
Normal file
86
system/blueprints/user/account.yaml
Normal file
@@ -0,0 +1,86 @@
|
||||
title: Site
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
content:
|
||||
type: section
|
||||
title: Account
|
||||
|
||||
fields:
|
||||
username:
|
||||
type: text
|
||||
size: large
|
||||
label: Username
|
||||
readonly: true
|
||||
|
||||
email:
|
||||
type: text
|
||||
size: large
|
||||
label: Email
|
||||
validate:
|
||||
required: true
|
||||
|
||||
password:
|
||||
type: password
|
||||
size: large
|
||||
label: Password
|
||||
validate:
|
||||
required: true
|
||||
|
||||
|
||||
|
||||
fullname:
|
||||
type: text
|
||||
size: large
|
||||
label: Full name
|
||||
validate:
|
||||
required: true
|
||||
|
||||
title:
|
||||
type: text
|
||||
size: large
|
||||
label: Title
|
||||
|
||||
admin:
|
||||
type: section
|
||||
title: Admin Access
|
||||
|
||||
fields:
|
||||
access.admin.super:
|
||||
type: toggle
|
||||
label: Super user
|
||||
default: 0
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
access.admin.login:
|
||||
type: toggle
|
||||
label: Admin login
|
||||
default: 0
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
site:
|
||||
type: section
|
||||
title: Site Access
|
||||
|
||||
fields:
|
||||
access.site.login:
|
||||
type: toggle
|
||||
label: Site login
|
||||
default: 1
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
15
system/blueprints/user/account_new.yaml
Normal file
15
system/blueprints/user/account_new.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
title: Add Account
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
|
||||
content:
|
||||
type: section
|
||||
title: Add Account
|
||||
|
||||
username:
|
||||
type: text
|
||||
label: Username
|
||||
validate:
|
||||
required: true
|
||||
@@ -1,13 +1,15 @@
|
||||
title: Grav # Name of the site
|
||||
title: Grav # Name of the site
|
||||
author:
|
||||
name: John Appleseed # Default author name
|
||||
email: 'john@email.com' # Default author email
|
||||
taxonomies: [category,tag] # Arbitrary list of taxonomy types
|
||||
name: John Appleseed # Default author name
|
||||
email: 'john@email.com' # Default author email
|
||||
taxonomies: [category,tag] # Arbitrary list of taxonomy types
|
||||
blog:
|
||||
route: '/blog' # Route to blog
|
||||
description: 'Grav Site Description' # Site description
|
||||
route: '/blog' # Route to blog
|
||||
metadata:
|
||||
description: 'My Grav Site' # Site description
|
||||
summary:
|
||||
size: 300 # Maximum length of summary (characters)
|
||||
size: 300 # Maximum length of summary (characters)
|
||||
routes:
|
||||
/something/else: '/blog/sample-3' # Alias for /blog/sample-3
|
||||
/another/one/here: '/blog/sample-3' # Another alias for /blog/sample-3
|
||||
/something/else: '/blog/sample-3' # Alias for /blog/sample-3
|
||||
/another/one/here: '/blog/sample-3' # Another alias for /blog/sample-3
|
||||
/new/*: '/blog/*' # Wildcard any /new/my-page URL to /blog/my-page Route
|
||||
|
||||
@@ -1,47 +1,20 @@
|
||||
schemes:
|
||||
plugin:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user/plugins
|
||||
- system/plugins
|
||||
|
||||
asset:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- assets
|
||||
|
||||
cache:
|
||||
image:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- cache
|
||||
|
||||
log:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- logs
|
||||
|
||||
user:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user
|
||||
- user://images
|
||||
|
||||
page:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user/pages
|
||||
- user://pages
|
||||
|
||||
account:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user/accounts
|
||||
|
||||
data:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user/data
|
||||
|
||||
theme:
|
||||
type: ReadOnlyStream
|
||||
prefixes:
|
||||
'/':
|
||||
- user/themes
|
||||
- user://accounts
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
absolute_urls: false # Absolute or relative URLs for `base_url`
|
||||
|
||||
home:
|
||||
alias: '/home' # Default path for home, ie /
|
||||
|
||||
@@ -23,8 +25,9 @@ cache:
|
||||
enabled: true # Set to true to enable caching
|
||||
check:
|
||||
method: file # Method to check for updates in pages: file|folder|none
|
||||
driver: auto # One of: auto|file|apc|xcache|memcache|memcached|wincache
|
||||
driver: auto # One of: auto|file|apc|xcache|memcache|wincache
|
||||
prefix: 'g' # Cache prefix string (prevents cache conflicts)
|
||||
lifetime: 0 # Lifetime of cached data in seconds (0 = infinite)
|
||||
|
||||
twig:
|
||||
cache: true # Set to true to enable twig caching
|
||||
@@ -35,15 +38,17 @@ twig:
|
||||
assets: # Configuration for Assets Manager (JS, CSS)
|
||||
css_pipeline: false # The CSS pipeline is the unification of multiple CSS resources into one file
|
||||
css_minify: true # Minify the CSS during pipelining
|
||||
css_minify_windows: false # Minify Override for Windows platforms. False by default due to ThreadStackSize
|
||||
css_rewrite: true # Rewrite any CSS relative URLs during pipelining
|
||||
js_pipeline: false # The JS pipeline is the unification of multiple JS resources into one file
|
||||
js_minify: true # Minify the JS during pipelining
|
||||
|
||||
errors:
|
||||
display: true # Display full backtrace-style error page
|
||||
log: true # Log errors to /logs folder
|
||||
|
||||
debugger:
|
||||
enabled: false # Enable Grav debugger and following settings
|
||||
mode: detect # Mode tracy Debugger should be set to when enabled: detect|development|production
|
||||
strict: false # Throw fatal error also on PHP warnings and notices
|
||||
max_depth: 10 # How many nested levels to display for objects or arrays
|
||||
log:
|
||||
enabled: true # Enable logging
|
||||
timing: false # Enable timing logging
|
||||
twig: true # Enable debugging of Twig templates
|
||||
shutdown:
|
||||
close_connection: true # Close the connection before calling onShutdown(). false for debugging
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
// Some standard defines
|
||||
define('GRAV', true);
|
||||
define('GRAV_VERSION', '0.9.1');
|
||||
define('GRAV_VERSION', '0.9.11');
|
||||
define('DS', '/');
|
||||
|
||||
// Directories and Paths
|
||||
@@ -14,7 +14,7 @@ define('USER_PATH', 'user/');
|
||||
define('USER_DIR', ROOT_DIR . USER_PATH);
|
||||
define('SYSTEM_DIR', ROOT_DIR .'system/');
|
||||
define('ASSETS_DIR', ROOT_DIR . 'assets/');
|
||||
define('CACHE_DIR', ROOT_DIR .'cache/');
|
||||
define('CACHE_DIR', ROOT_DIR . 'cache/');
|
||||
define('IMAGES_DIR', ROOT_DIR . 'images/');
|
||||
define('LOG_DIR', ROOT_DIR .'logs/');
|
||||
define('VENDOR_DIR', ROOT_DIR .'vendor/');
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,13 +4,18 @@ namespace Grav\Common;
|
||||
/**
|
||||
* Simple wrapper for the very simple parse_user_agent() function
|
||||
*/
|
||||
class Browser {
|
||||
class Browser
|
||||
{
|
||||
|
||||
protected $useragent;
|
||||
protected $useragent = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->useragent = parse_user_agent();
|
||||
try {
|
||||
$this->useragent = parse_user_agent();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$this->useragent = parse_user_agent("Mozilla/5.0 (compatible; Unknown;)");
|
||||
}
|
||||
}
|
||||
|
||||
public function getBrowser()
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
namespace Grav\Common;
|
||||
|
||||
use \Doctrine\Common\Cache\Cache as DoctrineCache;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
|
||||
/**
|
||||
* The GravCache object is used throughout Grav to store and retrieve cached data.
|
||||
@@ -34,6 +36,35 @@ class Cache extends Getters
|
||||
*/
|
||||
protected $enabled;
|
||||
|
||||
protected $cache_dir;
|
||||
|
||||
protected static $standard_remove = [
|
||||
'cache/twig/',
|
||||
'cache/doctrine/',
|
||||
'cache/compiled/',
|
||||
'cache/validated-',
|
||||
'images/',
|
||||
'assets/',
|
||||
];
|
||||
|
||||
protected static $all_remove = [
|
||||
'cache/',
|
||||
'images/',
|
||||
'assets/'
|
||||
];
|
||||
|
||||
protected static $assets_remove = [
|
||||
'assets/'
|
||||
];
|
||||
|
||||
protected static $images_remove = [
|
||||
'images/'
|
||||
];
|
||||
|
||||
protected static $cache_remove = [
|
||||
'cache/'
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@@ -55,6 +86,8 @@ class Cache extends Getters
|
||||
/** @var Config $config */
|
||||
$this->config = $grav['config'];
|
||||
|
||||
$this->cache_dir = $grav['locator']->findResource('cache://doctrine', true, true);
|
||||
|
||||
/** @var Uri $uri */
|
||||
$uri = $grav['uri'];
|
||||
|
||||
@@ -63,7 +96,7 @@ class Cache extends Getters
|
||||
$this->enabled = (bool) $this->config->get('system.cache.enabled');
|
||||
|
||||
// Cache key allows us to invalidate all cache on configuration changes.
|
||||
$this->key = substr(md5(($prefix ? $prefix : 'g') . $uri->rootUrl(true) . $this->config->key . GRAV_VERSION), 2, 8);
|
||||
$this->key = substr(md5(($prefix ? $prefix : 'g') . $uri->rootUrl(true) . $this->config->key() . GRAV_VERSION), 2, 8);
|
||||
|
||||
$this->driver = $this->getCacheDriver();
|
||||
|
||||
@@ -90,8 +123,6 @@ class Cache extends Getters
|
||||
$driver_name = 'wincache';
|
||||
} elseif (extension_loaded('xcache')) {
|
||||
$driver_name = 'xcache';
|
||||
} elseif (extension_loaded('memcache')) {
|
||||
$driver_name = 'memcache';
|
||||
}
|
||||
} else {
|
||||
$driver_name = $setting;
|
||||
@@ -111,15 +142,15 @@ class Cache extends Getters
|
||||
break;
|
||||
|
||||
case 'memcache':
|
||||
$memcache = new \Memcache();
|
||||
$memcache->connect($this->config->get('system.cache.memcache.server','localhost'),
|
||||
$this->config->get('system.cache.memcache.port', 11211));
|
||||
$driver = new \Doctrine\Common\Cache\MemcacheCache();
|
||||
break;
|
||||
|
||||
case 'memcached':
|
||||
$driver = new \Doctrine\Common\Cache\MemcachedCache();
|
||||
$driver->setMemcache($memcache);
|
||||
break;
|
||||
|
||||
default:
|
||||
$driver = new \Doctrine\Common\Cache\FilesystemCache(CACHE_DIR);
|
||||
$driver = new \Doctrine\Common\Cache\FilesystemCache($this->cache_dir);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -151,6 +182,10 @@ class Cache extends Getters
|
||||
public function save($id, $data, $lifetime = null)
|
||||
{
|
||||
if ($this->enabled) {
|
||||
|
||||
if ($lifetime == null) {
|
||||
$lifetime = $this->config->get('system.cache.lifetime') ?: null;
|
||||
}
|
||||
$this->driver->save($id, $data, $lifetime);
|
||||
}
|
||||
}
|
||||
@@ -162,4 +197,70 @@ class Cache extends Getters
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to clear all Grav caches
|
||||
*
|
||||
* @param string $remove standard|all|assets-only|images-only|cache-only
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function clearCache($remove = 'standard')
|
||||
{
|
||||
|
||||
$output = [];
|
||||
$user_config = USER_DIR . 'config/system.yaml';
|
||||
|
||||
switch($remove) {
|
||||
case 'all':
|
||||
$remove_paths = self::$all_remove;
|
||||
break;
|
||||
case 'assets-only':
|
||||
$remove_paths = self::$assets_remove;
|
||||
break;
|
||||
case 'images-only':
|
||||
$remove_paths = self::$images_remove;
|
||||
break;
|
||||
case 'cache-only':
|
||||
$remove_paths = self::$cache_remove;
|
||||
break;
|
||||
default:
|
||||
$remove_paths = self::$standard_remove;
|
||||
}
|
||||
|
||||
|
||||
foreach ($remove_paths as $path) {
|
||||
|
||||
$anything = false;
|
||||
$files = glob(ROOT_DIR . $path . '*');
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (is_file($file)) {
|
||||
if (@unlink($file)) {
|
||||
$anything = true;
|
||||
}
|
||||
} elseif (is_dir($file)) {
|
||||
if (@Folder::delete($file)) {
|
||||
$anything = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($anything) {
|
||||
$output[] = '<red>Cleared: </red>' . $path . '*';
|
||||
}
|
||||
}
|
||||
|
||||
$output[] = '';
|
||||
|
||||
if (($remove == 'all' || $remove == 'standard') && file_exists($user_config)) {
|
||||
touch($user_config);
|
||||
|
||||
$output[] = '<red>Touched: </red>' . $user_config;
|
||||
$output[] = '';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,259 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Data\Blueprints;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Filesystem\File;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
|
||||
/**
|
||||
* The Config class contains configuration information.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Config extends Data
|
||||
{
|
||||
/**
|
||||
* @var string Configuration location in the disk.
|
||||
*/
|
||||
public $filename;
|
||||
|
||||
/**
|
||||
* @var string MD5 from the files.
|
||||
*/
|
||||
public $key;
|
||||
|
||||
/**
|
||||
* @var array Configuration file list.
|
||||
*/
|
||||
public $files = array();
|
||||
|
||||
/**
|
||||
* @var bool Flag to tell if configuration needs to be saved.
|
||||
*/
|
||||
public $updated = false;
|
||||
public $issues = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct($filename)
|
||||
{
|
||||
$this->filename = realpath(dirname($filename)) . '/' . basename($filename);
|
||||
|
||||
$this->reload(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force reload of the configuration from the disk.
|
||||
*
|
||||
* @param bool $force
|
||||
* @return $this
|
||||
*/
|
||||
public function reload($force = true)
|
||||
{
|
||||
// Build file map.
|
||||
$files = $this->build();
|
||||
$key = md5(serialize($files) . GRAV_VERSION);
|
||||
|
||||
if ($force || $key != $this->key) {
|
||||
// First take non-blocking lock to the file.
|
||||
File\Config::instance($this->filename)->lock(false);
|
||||
|
||||
// Reset configuration.
|
||||
$this->items = array();
|
||||
$this->files = array();
|
||||
$this->init($files);
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save configuration into file.
|
||||
*
|
||||
* Note: Only saves the file if updated flag is set!
|
||||
*
|
||||
* @return $this
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
// If configuration was updated, store it as cached version.
|
||||
try {
|
||||
$file = File\Config::instance($this->filename);
|
||||
|
||||
// Only save configuration file if it wasn't locked. Also invalidate opcache after saving.
|
||||
// This prevents us from saving the file multiple times in a row and gives faster recovery.
|
||||
if ($file->locked() !== false) {
|
||||
$file->save($this);
|
||||
$file->unlock();
|
||||
}
|
||||
$this->updated = false;
|
||||
} catch (\Exception $e) {
|
||||
$this->issues[] = 'Writing configuration into cache failed.';
|
||||
//throw new \RuntimeException('Writing configuration into cache failed.', 500, $e);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load configuration.
|
||||
*
|
||||
* @param Grav $grav
|
||||
* @return \Grav\Common\Config
|
||||
*/
|
||||
public static function instance(Grav $grav)
|
||||
{
|
||||
$filename = $grav['config_path'];
|
||||
|
||||
// Load cached version if available..
|
||||
if (file_exists($filename)) {
|
||||
require_once $filename;
|
||||
|
||||
if (class_exists('\Grav\Config')) {
|
||||
$instance = new \Grav\Config($filename);
|
||||
}
|
||||
}
|
||||
|
||||
// Or initialize new configuration object..
|
||||
if (!isset($instance)) {
|
||||
$instance = new static($filename);
|
||||
}
|
||||
|
||||
// If configuration was updated, store it as cached version.
|
||||
if ($instance->updated) {
|
||||
$instance->save();
|
||||
}
|
||||
|
||||
|
||||
// If not set, add manually current base url.
|
||||
if (empty($instance->items['system']['base_url_absolute'])) {
|
||||
$instance->items['system']['base_url_absolute'] = $grav['uri']->rootUrl(true);
|
||||
}
|
||||
|
||||
if (empty($instance->items['system']['base_url_relative'])) {
|
||||
$instance->items['system']['base_url_relative'] = $grav['uri']->rootUrl(false);
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert configuration into an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return array('key' => $this->key, 'files' => $this->files, 'items' => $this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize object by loading all the configuration files.
|
||||
*
|
||||
* @param array $files
|
||||
*/
|
||||
protected function init(array $files)
|
||||
{
|
||||
$this->updated = true;
|
||||
|
||||
// Combine all configuration files into one larger lookup table (only keys matter).
|
||||
$allFiles = $files['user'] + $files['plugins'] + $files['system'];
|
||||
|
||||
// Then sort the files to have all parent nodes first.
|
||||
// This is to make sure that child nodes override parents content.
|
||||
uksort(
|
||||
$allFiles,
|
||||
function($a, $b) {
|
||||
$diff = substr_count($a, '/') - substr_count($b, '/');
|
||||
return $diff ? $diff : strcmp($a, $b);
|
||||
}
|
||||
);
|
||||
|
||||
$systemBlueprints = new Blueprints(SYSTEM_DIR . 'blueprints');
|
||||
$pluginBlueprints = new Blueprints(USER_DIR);
|
||||
|
||||
$items = array();
|
||||
foreach ($allFiles as $name => $dummy) {
|
||||
$lookup = array(
|
||||
'system' => SYSTEM_DIR . 'config/' . $name . YAML_EXT,
|
||||
'plugins' => USER_DIR . $name . '/' . basename($name) . YAML_EXT,
|
||||
'user' => USER_DIR . 'config/' . $name . YAML_EXT,
|
||||
);
|
||||
if (strpos($name, 'plugins/') === 0) {
|
||||
$blueprint = $pluginBlueprints->get("{$name}/blueprints");
|
||||
} else {
|
||||
$blueprint = $systemBlueprints->get($name);
|
||||
}
|
||||
|
||||
$data = new Data(array(), $blueprint);
|
||||
foreach ($lookup as $key => $path) {
|
||||
if (is_file($path)) {
|
||||
$data->merge(File\Yaml::instance($path)->content());
|
||||
}
|
||||
}
|
||||
// $data->validate();
|
||||
// $data->filter();
|
||||
|
||||
// Find the current sub-tree location.
|
||||
$current = &$items;
|
||||
$parts = explode('/', $name);
|
||||
foreach ($parts as $part) {
|
||||
if (!isset($current[$part])) {
|
||||
$current[$part] = array();
|
||||
}
|
||||
$current = &$current[$part];
|
||||
}
|
||||
|
||||
// Handle both updated and deleted configuration files.
|
||||
$current = $data->toArray();
|
||||
}
|
||||
|
||||
$this->items = $items;
|
||||
$this->files = $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a list of configuration files with their timestamps. Used for loading settings and caching them.
|
||||
*
|
||||
* @return array
|
||||
* @internal
|
||||
*/
|
||||
protected function build()
|
||||
{
|
||||
// Find all plugins with default configuration options.
|
||||
$plugins = array();
|
||||
$iterator = new \DirectoryIterator(PLUGINS_DIR);
|
||||
|
||||
/** @var \DirectoryIterator $plugin */
|
||||
foreach ($iterator as $plugin) {
|
||||
$name = $plugin->getBasename();
|
||||
$file = $plugin->getPathname() . DS . $name . YAML_EXT;
|
||||
|
||||
if (!is_file($file)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$modified = filemtime($file);
|
||||
$plugins["plugins/{$name}"] = $modified;
|
||||
}
|
||||
|
||||
// Find all system and user configuration files.
|
||||
$options = array(
|
||||
'compare' => 'Filename',
|
||||
'pattern' => '|\.yaml$|',
|
||||
'filters' => array('key' => '|\.yaml$|'),
|
||||
'key' => 'SubPathname',
|
||||
'value' => 'MTime'
|
||||
);
|
||||
|
||||
$system = Folder::all(SYSTEM_DIR . 'config', $options);
|
||||
$user = Folder::all(USER_DIR . 'config', $options);
|
||||
|
||||
return array('system' => $system, 'plugins' => $plugins, 'user' => $user);
|
||||
}
|
||||
}
|
||||
208
system/src/Grav/Common/Config/Blueprints.php
Normal file
208
system/src/Grav/Common/Config/Blueprints.php
Normal file
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use RocketTheme\Toolbox\Blueprints\Blueprints as BaseBlueprints;
|
||||
use RocketTheme\Toolbox\File\PhpFile;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
/**
|
||||
* The Blueprints class contains configuration rules.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Blueprints extends BaseBlueprints
|
||||
{
|
||||
protected $grav;
|
||||
protected $files = [];
|
||||
protected $blueprints;
|
||||
|
||||
public function __construct(array $serialized = null, Grav $grav = null)
|
||||
{
|
||||
parent::__construct($serialized);
|
||||
$this->grav = $grav ?: Grav::instance();
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $this->grav['locator'];
|
||||
|
||||
$blueprints = $locator->findResources('blueprints://config');
|
||||
$plugins = $locator->findResources('plugins://');
|
||||
|
||||
$blueprintFiles = $this->getBlueprintFiles($blueprints, $plugins);
|
||||
|
||||
$this->loadCompiledBlueprints($plugins + $blueprints, $blueprintFiles);
|
||||
}
|
||||
|
||||
protected function loadCompiledBlueprints($blueprints, $blueprintFiles)
|
||||
{
|
||||
$checksum = md5(serialize($blueprints));
|
||||
$filename = CACHE_DIR . 'compiled/blueprints/' . $checksum .'.php';
|
||||
$checksum .= ':'.md5(serialize($blueprintFiles));
|
||||
$class = get_class($this);
|
||||
$file = PhpFile::instance($filename);
|
||||
|
||||
if ($file->exists()) {
|
||||
$cache = $file->exists() ? $file->content() : null;
|
||||
} else {
|
||||
$cache = null;
|
||||
}
|
||||
|
||||
|
||||
// Load real file if cache isn't up to date (or is invalid).
|
||||
if (
|
||||
!is_array($cache)
|
||||
|| empty($cache['checksum'])
|
||||
|| empty($cache['$class'])
|
||||
|| $cache['checksum'] != $checksum
|
||||
|| $cache['@class'] != $class
|
||||
) {
|
||||
// Attempt to lock the file for writing.
|
||||
$file->lock(false);
|
||||
|
||||
// Load blueprints.
|
||||
$this->blueprints = new Blueprints();
|
||||
foreach ($blueprintFiles as $key => $files) {
|
||||
$this->loadBlueprints($key);
|
||||
}
|
||||
|
||||
$cache = [
|
||||
'@class' => $class,
|
||||
'checksum' => $checksum,
|
||||
'files' => $blueprintFiles,
|
||||
'data' => $this->blueprints->toArray()
|
||||
];
|
||||
|
||||
// If compiled file wasn't already locked by another process, save it.
|
||||
if ($file->locked() !== false) {
|
||||
$file->save($cache);
|
||||
$file->unlock();
|
||||
}
|
||||
} else {
|
||||
$this->blueprints = new Blueprints($cache['data']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load global blueprints.
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $files
|
||||
*/
|
||||
public function loadBlueprints($key, array $files = null)
|
||||
{
|
||||
if (is_null($files)) {
|
||||
$files = $this->files[$key];
|
||||
}
|
||||
foreach ($files as $name => $item) {
|
||||
$file = CompiledYamlFile::instance($item['file']);
|
||||
$this->blueprints->embed($name, $file->content(), '/');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all blueprint files (including plugins).
|
||||
*
|
||||
* @param array $blueprints
|
||||
* @param array $plugins
|
||||
* @return array
|
||||
*/
|
||||
protected function getBlueprintFiles(array $blueprints, array $plugins)
|
||||
{
|
||||
$list = [];
|
||||
foreach (array_reverse($plugins) as $folder) {
|
||||
$list += $this->detectPlugins($folder, true);
|
||||
}
|
||||
foreach (array_reverse($blueprints) as $folder) {
|
||||
$list += $this->detectConfig($folder, true);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects all plugins with a configuration file and returns last modification time.
|
||||
*
|
||||
* @param string $lookup Location to look up from.
|
||||
* @param bool $blueprints
|
||||
* @return array
|
||||
* @internal
|
||||
*/
|
||||
protected function detectPlugins($lookup = SYSTEM_DIR, $blueprints = false)
|
||||
{
|
||||
$find = $blueprints ? 'blueprints.yaml' : '.yaml';
|
||||
$location = $blueprints ? 'blueprintFiles' : 'configFiles';
|
||||
$path = trim(Folder::getRelativePath($lookup), '/');
|
||||
if (isset($this->{$location}[$path])) {
|
||||
return [$path => $this->{$location}[$path]];
|
||||
}
|
||||
|
||||
$list = [];
|
||||
|
||||
if (is_dir($lookup)) {
|
||||
$iterator = new \DirectoryIterator($lookup);
|
||||
|
||||
/** @var \DirectoryIterator $directory */
|
||||
foreach ($iterator as $directory) {
|
||||
if (!$directory->isDir() || $directory->isDot()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $directory->getBasename();
|
||||
$filename = "{$path}/{$name}/" . ($find && $find[0] != '.' ? $find : $name . $find);
|
||||
|
||||
if (is_file($filename)) {
|
||||
$list["plugins/{$name}"] = ['file' => $filename, 'modified' => filemtime($filename)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->{$location}[$path] = $list;
|
||||
|
||||
return [$path => $list];
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects all plugins with a configuration file and returns last modification time.
|
||||
*
|
||||
* @param string $lookup Location to look up from.
|
||||
* @param bool $blueprints
|
||||
* @return array
|
||||
* @internal
|
||||
*/
|
||||
protected function detectConfig($lookup = SYSTEM_DIR, $blueprints = false)
|
||||
{
|
||||
$location = $blueprints ? 'blueprintFiles' : 'configFiles';
|
||||
$path = trim(Folder::getRelativePath($lookup), '/');
|
||||
if (isset($this->{$location}[$path])) {
|
||||
return [$path => $this->{$location}[$path]];
|
||||
}
|
||||
|
||||
if (is_dir($lookup)) {
|
||||
// Find all system and user configuration files.
|
||||
$options = [
|
||||
'compare' => 'Filename',
|
||||
'pattern' => '|\.yaml$|',
|
||||
'filters' => [
|
||||
'key' => '|\.yaml$|',
|
||||
'value' => function (\RecursiveDirectoryIterator $file) use ($path) {
|
||||
return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()];
|
||||
}],
|
||||
'key' => 'SubPathname'
|
||||
];
|
||||
|
||||
$list = Folder::all($lookup, $options);
|
||||
} else {
|
||||
$list = [];
|
||||
}
|
||||
|
||||
$this->{$location}[$path] = $list;
|
||||
|
||||
return [$path => $list];
|
||||
}
|
||||
}
|
||||
334
system/src/Grav/Common/Config/Config.php
Normal file
334
system/src/Grav/Common/Config/Config.php
Normal file
@@ -0,0 +1,334 @@
|
||||
<?php
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use RocketTheme\Toolbox\Blueprints\Blueprints;
|
||||
use RocketTheme\Toolbox\File\PhpFile;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
/**
|
||||
* The Config class contains configuration information.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Config extends Data
|
||||
{
|
||||
protected $grav;
|
||||
protected $streams = [
|
||||
'user' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user'],
|
||||
]
|
||||
],
|
||||
'blueprints' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user://blueprints', 'system/blueprints'],
|
||||
]
|
||||
],
|
||||
'config' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user://config', 'system/config'],
|
||||
]
|
||||
],
|
||||
'plugins' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user://plugins'],
|
||||
]
|
||||
],
|
||||
'plugin' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user://plugins'],
|
||||
]
|
||||
],
|
||||
'themes' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['user://themes'],
|
||||
]
|
||||
],
|
||||
'cache' => [
|
||||
'type' => 'Stream',
|
||||
'prefixes' => [
|
||||
'' => ['cache']
|
||||
]
|
||||
],
|
||||
'log' => [
|
||||
'type' => 'Stream',
|
||||
'prefixes' => [
|
||||
'' => ['logs']
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
protected $blueprintFiles = [];
|
||||
protected $configFiles = [];
|
||||
protected $checksum;
|
||||
protected $timestamp;
|
||||
|
||||
protected $configLookup;
|
||||
protected $blueprintLookup;
|
||||
protected $pluginLookup;
|
||||
|
||||
protected $finder;
|
||||
protected $environment;
|
||||
protected $messages = [];
|
||||
|
||||
public function __construct(array $items = array(), Grav $grav = null, $environment = null)
|
||||
{
|
||||
$this->grav = $grav ?: Grav::instance();
|
||||
$this->finder = new ConfigFinder;
|
||||
$this->environment = $environment ?: 'localhost';
|
||||
$this->messages[] = 'Environment Name: ' . $this->environment;
|
||||
|
||||
if (isset($items['@class'])) {
|
||||
if ($items['@class'] != get_class($this)) {
|
||||
throw new \InvalidArgumentException('Unrecognized config cache file!');
|
||||
}
|
||||
// Loading pre-compiled configuration.
|
||||
$this->timestamp = (int) $items['timestamp'];
|
||||
$this->checksum = $items['checksum'];
|
||||
$this->items = (array) $items['data'];
|
||||
} else {
|
||||
// Make sure that
|
||||
if (!isset($items['streams']['schemes'])) {
|
||||
$items['streams']['schemes'] = [];
|
||||
}
|
||||
$items['streams']['schemes'] += $this->streams;
|
||||
|
||||
$items = $this->autoDetectEnvironmentConfig($items);
|
||||
$this->messages[] = $items['streams']['schemes']['config']['prefixes'][''];
|
||||
|
||||
parent::__construct($items);
|
||||
}
|
||||
$this->check();
|
||||
}
|
||||
|
||||
public function key()
|
||||
{
|
||||
return $this->checksum();
|
||||
}
|
||||
|
||||
public function reload()
|
||||
{
|
||||
$this->check();
|
||||
$this->init();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function check()
|
||||
{
|
||||
$streams = isset($this->items['streams']['schemes']) ? $this->items['streams']['schemes'] : null;
|
||||
if (!is_array($streams)) {
|
||||
throw new \InvalidArgumentException('Configuration is missing streams.schemes!');
|
||||
}
|
||||
$diff = array_keys(array_diff_key($this->streams, $streams));
|
||||
if ($diff) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('Configuration is missing keys %s from streams.schemes!', implode(', ', $diff))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function debug()
|
||||
{
|
||||
foreach ($this->messages as $message) {
|
||||
$this->grav['debugger']->addMessage($message);
|
||||
}
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $this->grav['locator'];
|
||||
|
||||
$this->configLookup = $locator->findResources('config://');
|
||||
$this->blueprintLookup = $locator->findResources('blueprints://config');
|
||||
$this->pluginLookup = $locator->findResources('plugins://');
|
||||
|
||||
if (!isset($this->checksum)) {
|
||||
$this->messages[] = 'No cached configuration, compiling new configuration..';
|
||||
} elseif ($this->checksum() != $this->checksum) {
|
||||
$this->messages[] = 'Configuration checksum mismatch, reloading configuration..';
|
||||
} else {
|
||||
$this->messages[] = 'Configuration checksum matches, using cached version.';
|
||||
return;
|
||||
}
|
||||
|
||||
$this->loadCompiledBlueprints($this->blueprintLookup, $this->pluginLookup, 'master');
|
||||
$this->loadCompiledConfig($this->configLookup, $this->pluginLookup, 'master');
|
||||
}
|
||||
|
||||
public function checksum()
|
||||
{
|
||||
$checkBlueprints = $this->get('system.cache.check.blueprints', false);
|
||||
$checkConfig = $this->get('system.cache.check.config', true);
|
||||
$checkSystem = $this->get('system.cache.check.system', true);
|
||||
|
||||
if (!$checkBlueprints && !$checkConfig && !$checkSystem) {
|
||||
$this->messages[] = 'Skip configuration timestamp check.';
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate checksum according to the configuration settings.
|
||||
if (!$checkConfig) {
|
||||
$this->messages[] = 'Check configuration timestamps from system.yaml files.';
|
||||
// Just check changes in system.yaml files and ignore all the other files.
|
||||
$cc = $checkSystem ? $this->finder->locateConfigFile($this->configLookup, 'system') : [];
|
||||
} else {
|
||||
$this->messages[] = 'Check configuration timestamps from all configuration files.';
|
||||
// Check changes in all configuration files.
|
||||
$cc = $this->finder->locateConfigFiles($this->configLookup, $this->pluginLookup);
|
||||
}
|
||||
|
||||
if ($checkBlueprints) {
|
||||
$this->messages[] = 'Check blueprint timestamps from all blueprint files.';
|
||||
$cb = $this->finder->locateBlueprintFiles($this->blueprintLookup, $this->pluginLookup);
|
||||
} else {
|
||||
$cb = [];
|
||||
}
|
||||
|
||||
return md5(json_encode([$cc, $cb]));
|
||||
}
|
||||
|
||||
protected function autoDetectEnvironmentConfig($items)
|
||||
{
|
||||
$environment = $this->environment;
|
||||
$env_stream = 'user://'.$environment.'/config';
|
||||
|
||||
if (file_exists(USER_DIR.$environment.'/config')) {
|
||||
array_unshift($items['streams']['schemes']['config']['prefixes'][''], $env_stream);
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
protected function loadCompiledBlueprints($blueprints, $plugins, $filename = null)
|
||||
{
|
||||
$checksum = md5(json_encode($blueprints));
|
||||
$filename = $filename
|
||||
? CACHE_DIR . 'compiled/blueprints/' . $filename . '-' . $this->environment . '.php'
|
||||
: CACHE_DIR . 'compiled/blueprints/' . $checksum . '-' . $this->environment . '.php';
|
||||
$file = PhpFile::instance($filename);
|
||||
$cache = $file->exists() ? $file->content() : null;
|
||||
$blueprintFiles = $this->finder->locateBlueprintFiles($blueprints, $plugins);
|
||||
$checksum .= ':'.md5(json_encode($blueprintFiles));
|
||||
$class = get_class($this);
|
||||
|
||||
// Load real file if cache isn't up to date (or is invalid).
|
||||
if (
|
||||
!is_array($cache)
|
||||
|| !isset($cache['checksum'])
|
||||
|| !isset($cache['@class'])
|
||||
|| $cache['checksum'] != $checksum
|
||||
|| $cache['@class'] != $class
|
||||
) {
|
||||
// Attempt to lock the file for writing.
|
||||
$file->lock(false);
|
||||
|
||||
// Load blueprints.
|
||||
$this->blueprints = new Blueprints;
|
||||
foreach ($blueprintFiles as $files) {
|
||||
$this->loadBlueprintFiles($files);
|
||||
}
|
||||
|
||||
$cache = [
|
||||
'@class' => $class,
|
||||
'checksum' => $checksum,
|
||||
'files' => $blueprintFiles,
|
||||
'data' => $this->blueprints->toArray()
|
||||
];
|
||||
|
||||
// If compiled file wasn't already locked by another process, save it.
|
||||
if ($file->locked() !== false) {
|
||||
$this->messages[] = 'Saving compiled blueprints.';
|
||||
$file->save($cache);
|
||||
$file->unlock();
|
||||
}
|
||||
} else {
|
||||
$this->blueprints = new Blueprints($cache['data']);
|
||||
}
|
||||
}
|
||||
|
||||
protected function loadCompiledConfig($configs, $plugins, $filename = null)
|
||||
{
|
||||
$checksum = md5(json_encode($configs));
|
||||
$filename = $filename
|
||||
? CACHE_DIR . 'compiled/config/' . $filename . '-' . $this->environment . '.php'
|
||||
: CACHE_DIR . 'compiled/config/' . $checksum . '-' . $this->environment . '.php';
|
||||
$file = PhpFile::instance($filename);
|
||||
$cache = $file->exists() ? $file->content() : null;
|
||||
$configFiles = $this->finder->locateConfigFiles($configs, $plugins);
|
||||
$checksum .= ':'.md5(json_encode($configFiles));
|
||||
$class = get_class($this);
|
||||
|
||||
// Load real file if cache isn't up to date (or is invalid).
|
||||
if (
|
||||
!is_array($cache)
|
||||
|| !isset($cache['checksum'])
|
||||
|| !isset($cache['@class'])
|
||||
|| $cache['checksum'] != $checksum
|
||||
|| $cache['@class'] != $class
|
||||
) {
|
||||
// Attempt to lock the file for writing.
|
||||
$file->lock(false);
|
||||
|
||||
// Load configuration.
|
||||
foreach ($configFiles as $files) {
|
||||
$this->loadConfigFiles($files);
|
||||
}
|
||||
$cache = [
|
||||
'@class' => $class,
|
||||
'timestamp' => time(),
|
||||
'checksum' => $this->checksum(),
|
||||
'data' => $this->toArray()
|
||||
];
|
||||
|
||||
// If compiled file wasn't already locked by another process, save it.
|
||||
if ($file->locked() !== false) {
|
||||
$this->messages[] = 'Saving compiled configuration.';
|
||||
$file->save($cache);
|
||||
$file->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
$this->items = $cache['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Load blueprints.
|
||||
*
|
||||
* @param array $files
|
||||
*/
|
||||
public function loadBlueprintFiles(array $files)
|
||||
{
|
||||
foreach ($files as $name => $item) {
|
||||
$file = CompiledYamlFile::instance($item['file']);
|
||||
$this->blueprints->embed($name, $file->content(), '/');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load configuration.
|
||||
*
|
||||
* @param array $files
|
||||
*/
|
||||
public function loadConfigFiles(array $files)
|
||||
{
|
||||
foreach ($files as $name => $item) {
|
||||
$file = CompiledYamlFile::instance($item['file']);
|
||||
$this->join($name, $file->content(), '/');
|
||||
}
|
||||
}
|
||||
}
|
||||
146
system/src/Grav/Common/Config/ConfigFinder.php
Normal file
146
system/src/Grav/Common/Config/ConfigFinder.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
|
||||
/**
|
||||
* The Configuration Finder class.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class ConfigFinder
|
||||
{
|
||||
/**
|
||||
* Get all locations for blueprint files (including plugins).
|
||||
*
|
||||
* @param array $blueprints
|
||||
* @param array $plugins
|
||||
* @return array
|
||||
*/
|
||||
public function locateBlueprintFiles(array $blueprints, array $plugins)
|
||||
{
|
||||
$list = [];
|
||||
foreach (array_reverse($plugins) as $folder) {
|
||||
$list += $this->detectInFolder($folder, 'blueprints');
|
||||
}
|
||||
foreach (array_reverse($blueprints) as $folder) {
|
||||
$list += $this->detectRecursive($folder);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all locations for configuration files (including plugins).
|
||||
*
|
||||
* @param array $configs
|
||||
* @param array $plugins
|
||||
* @return array
|
||||
*/
|
||||
public function locateConfigFiles(array $configs, array $plugins)
|
||||
{
|
||||
$list = [];
|
||||
foreach (array_reverse($plugins) as $folder) {
|
||||
$list += $this->detectInFolder($folder);
|
||||
}
|
||||
foreach (array_reverse($configs) as $folder) {
|
||||
$list += $this->detectRecursive($folder);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all locations for a single configuration file.
|
||||
*
|
||||
* @param array $folders Locations to look up from.
|
||||
* @param string $name Filename to be located.
|
||||
* @return array
|
||||
*/
|
||||
public function locateConfigFile(array $folders, $name)
|
||||
{
|
||||
$filename = "{$name}.yaml";
|
||||
|
||||
$list = [];
|
||||
foreach ($folders as $folder) {
|
||||
$path = trim(Folder::getRelativePath($folder), '/');
|
||||
|
||||
if (is_file("{$folder}/{$filename}")) {
|
||||
$modified = filemtime("{$folder}/{$filename}");
|
||||
} else {
|
||||
$modified = 0;
|
||||
}
|
||||
$list[$path] = [$name => ['file' => "{$path}/{$filename}", 'modified' => $modified]];
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects all plugins with a configuration file and returns them with last modification time.
|
||||
*
|
||||
* @param string $folder Location to look up from.
|
||||
* @param string $lookup Filename to be located.
|
||||
* @return array
|
||||
* @internal
|
||||
*/
|
||||
protected function detectInFolder($folder, $lookup = null)
|
||||
{
|
||||
$path = trim(Folder::getRelativePath($folder), '/');
|
||||
|
||||
$list = [];
|
||||
|
||||
if (is_dir($folder)) {
|
||||
$iterator = new \DirectoryIterator($folder);
|
||||
|
||||
/** @var \DirectoryIterator $directory */
|
||||
foreach ($iterator as $directory) {
|
||||
if (!$directory->isDir() || $directory->isDot()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $directory->getBasename();
|
||||
$find = ($lookup ?: $name) . '.yaml';
|
||||
$filename = "{$path}/{$name}/$find";
|
||||
|
||||
if (file_exists($filename)) {
|
||||
$list["plugins/{$name}"] = ['file' => $filename, 'modified' => filemtime($filename)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [$path => $list];
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects all plugins with a configuration file and returns them with last modification time.
|
||||
*
|
||||
* @param string $folder Location to look up from.
|
||||
* @return array
|
||||
* @internal
|
||||
*/
|
||||
protected function detectRecursive($folder)
|
||||
{
|
||||
$path = trim(Folder::getRelativePath($folder), '/');
|
||||
|
||||
if (is_dir($folder)) {
|
||||
// Find all system and user configuration files.
|
||||
$options = [
|
||||
'compare' => 'Filename',
|
||||
'pattern' => '|\.yaml$|',
|
||||
'filters' => [
|
||||
'key' => '|\.yaml$|',
|
||||
'value' => function (\RecursiveDirectoryIterator $file) use ($path) {
|
||||
return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()];
|
||||
}
|
||||
],
|
||||
'key' => 'SubPathname'
|
||||
];
|
||||
|
||||
$list = Folder::all($folder, $options);
|
||||
} else {
|
||||
$list = [];
|
||||
}
|
||||
|
||||
return [$path => $list];
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use \Symfony\Component\Yaml\Yaml;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
|
||||
/**
|
||||
* Blueprint handles the inside logic of blueprints.
|
||||
@@ -11,26 +11,41 @@ use \Symfony\Component\Yaml\Yaml;
|
||||
*/
|
||||
class Blueprint
|
||||
{
|
||||
use Export;
|
||||
|
||||
public $name;
|
||||
|
||||
public $initialized = false;
|
||||
protected $blueprints;
|
||||
|
||||
protected $items;
|
||||
protected $context;
|
||||
protected $fields;
|
||||
protected $rules = array();
|
||||
protected $nested = array();
|
||||
protected $filter = ['validation' => 1];
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $data
|
||||
* @param Blueprints $context
|
||||
*/
|
||||
public function __construct($name, array $data, Blueprints $context)
|
||||
public function __construct($name, array $data = array(), Blueprints $context = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->blueprints = $data;
|
||||
$this->items = $data;
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set filter for inherited properties.
|
||||
*
|
||||
* @param array $filter List of field names to be inherited.
|
||||
*/
|
||||
public function setFilter(array $filter)
|
||||
{
|
||||
$this->filter = array_flip($filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
@@ -45,7 +60,7 @@ class Blueprint
|
||||
public function get($name, $default = null, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = $this->blueprints;
|
||||
$current = $this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current) && isset($current->{$field})) {
|
||||
$current = $current->{$field};
|
||||
@@ -71,7 +86,7 @@ class Blueprint
|
||||
public function set($name, $value, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = &$this->blueprints;
|
||||
$current = &$this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current)) {
|
||||
// Handle objects.
|
||||
@@ -101,8 +116,8 @@ class Blueprint
|
||||
public function fields()
|
||||
{
|
||||
if (!isset($this->fields)) {
|
||||
$this->fields = isset($this->blueprints['form']['fields']) ? $this->blueprints['form']['fields'] : array();
|
||||
$this->getFields($this->fields);
|
||||
$this->fields = [];
|
||||
$this->embed('', $this->items);
|
||||
}
|
||||
|
||||
return $this->fields;
|
||||
@@ -131,9 +146,10 @@ class Blueprint
|
||||
*
|
||||
* @param array $data1
|
||||
* @param array $data2
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
public function mergeData(array $data1, array $data2)
|
||||
public function mergeData(array $data1, array $data2, $name = null)
|
||||
{
|
||||
// Initialize data
|
||||
$this->fields();
|
||||
@@ -167,6 +183,71 @@ class Blueprint
|
||||
return $this->extraArray($data, $this->nested, $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend blueprint with another blueprint.
|
||||
*
|
||||
* @param Blueprint $extends
|
||||
* @param bool $append
|
||||
*/
|
||||
public function extend(Blueprint $extends, $append = false)
|
||||
{
|
||||
$blueprints = $append ? $this->items : $extends->toArray();
|
||||
$appended = $append ? $extends->toArray() : $this->items;
|
||||
|
||||
$bref_stack = array(&$blueprints);
|
||||
$head_stack = array($appended);
|
||||
|
||||
do {
|
||||
end($bref_stack);
|
||||
|
||||
$bref = &$bref_stack[key($bref_stack)];
|
||||
$head = array_pop($head_stack);
|
||||
|
||||
unset($bref_stack[key($bref_stack)]);
|
||||
|
||||
foreach (array_keys($head) as $key) {
|
||||
if (isset($key, $bref[$key]) && is_array($bref[$key]) && is_array($head[$key])) {
|
||||
$bref_stack[] = &$bref[$key];
|
||||
$head_stack[] = $head[$key];
|
||||
} else {
|
||||
$bref = array_merge($bref, array($key => $head[$key]));
|
||||
}
|
||||
}
|
||||
} while(count($head_stack));
|
||||
|
||||
$this->items = $blueprints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert object into an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getState()
|
||||
{
|
||||
return ['name' => $this->name, 'items' => $this->items, 'rules' => $this->rules, 'nested' => $this->nested];
|
||||
}
|
||||
|
||||
/**
|
||||
* Embed an array to the blueprint.
|
||||
*
|
||||
* @param $name
|
||||
* @param array $value
|
||||
* @param string $separator
|
||||
*/
|
||||
public function embed($name, array $value, $separator = '.')
|
||||
{
|
||||
|
||||
if (!isset($value['form']['fields']) || !is_array($value['form']['fields'])) {
|
||||
return;
|
||||
}
|
||||
// Initialize data
|
||||
$this->fields();
|
||||
$prefix = $name ? strtr($name, $separator, '.') . '.' : '';
|
||||
$params = array_intersect_key($this->filter, $value);
|
||||
$this->parseFormFields($value['form']['fields'], $params, $prefix, $this->fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param array $rules
|
||||
@@ -187,7 +268,7 @@ class Blueprint
|
||||
} elseif (is_array($field) && is_array($val)) {
|
||||
// Array has been defined in blueprints.
|
||||
$this->validateArray($field, $val);
|
||||
} elseif (isset($this->blueprints['validation']) && $this->blueprints['validation'] == 'strict') {
|
||||
} elseif (isset($this->items['form']['validation']) && $this->items['form']['validation'] == 'strict') {
|
||||
// Undefined/extra item.
|
||||
throw new \RuntimeException(sprintf('%s is not defined in blueprints', $key));
|
||||
}
|
||||
@@ -213,7 +294,7 @@ class Blueprint
|
||||
} elseif (is_array($field) && is_array($val)) {
|
||||
// Array has been defined in blueprints.
|
||||
$field = $this->filterArray($field, $val);
|
||||
} elseif (isset($this->blueprints['validation']) && $this->blueprints['validation'] == 'strict') {
|
||||
} elseif (isset($this->items['form']['validation']) && $this->items['form']['validation'] == 'strict') {
|
||||
$field = null;
|
||||
}
|
||||
|
||||
@@ -281,26 +362,32 @@ class Blueprint
|
||||
* Gets all field definitions from the blueprints.
|
||||
*
|
||||
* @param array $fields
|
||||
* @param array $params
|
||||
* @param string $prefix
|
||||
* @param array $current
|
||||
* @internal
|
||||
*/
|
||||
protected function getFields(array &$fields)
|
||||
protected function parseFormFields(array &$fields, $params, $prefix, array &$current)
|
||||
{
|
||||
// Go though all the fields in current level.
|
||||
foreach ($fields as $key => &$field) {
|
||||
$current[$key] = &$field;
|
||||
// Set name from the array key.
|
||||
$field['name'] = $key;
|
||||
$field['name'] = $prefix . $key;
|
||||
$field += $params;
|
||||
|
||||
if (isset($field['fields'])) {
|
||||
// Recursively get all the nested fields.
|
||||
$this->getFields($field['fields']);
|
||||
$newParams = array_intersect_key($this->filter, $field);
|
||||
$this->parseFormFields($field['fields'], $newParams, $prefix, $current[$key]['fields']);
|
||||
} else {
|
||||
// Add rule.
|
||||
$this->rules[$key] = &$field;
|
||||
$this->addProperty($key);
|
||||
$this->rules[$prefix . $key] = &$field;
|
||||
$this->addProperty($prefix . $key);
|
||||
|
||||
foreach ($field as $name => $value) {
|
||||
// Support nested blueprints.
|
||||
if ($name == '@import') {
|
||||
if ($this->context && $name == '@import') {
|
||||
$values = (array) $value;
|
||||
if (!isset($field['fields'])) {
|
||||
$field['fields'] = array();
|
||||
@@ -379,8 +466,8 @@ class Blueprint
|
||||
*/
|
||||
protected function getRule($rule)
|
||||
{
|
||||
if (isset($this->blueprints['rules'][$rule]) && is_array($this->blueprints['rules'][$rule])) {
|
||||
return $this->blueprints['rules'][$rule];
|
||||
if (isset($this->items['rules'][$rule]) && is_array($this->items['rules'][$rule])) {
|
||||
return $this->items['rules'][$rule];
|
||||
}
|
||||
return array();
|
||||
}
|
||||
@@ -404,69 +491,4 @@ class Blueprint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert blueprints into an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->blueprints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert blueprints into YAML string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toYaml()
|
||||
{
|
||||
return Yaml::dump($this->blueprints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert blueprints into JSON string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toJson()
|
||||
{
|
||||
return json_encode($this->blueprints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend blueprint with another blueprint.
|
||||
*
|
||||
* @param Blueprint $extends
|
||||
* @param bool $append
|
||||
*/
|
||||
public function extend(Blueprint $extends, $append = false)
|
||||
{
|
||||
$blueprints = $append ? $this->blueprints : $extends->toArray();
|
||||
$appended = $append ? $extends->toArray() : $this->blueprints;
|
||||
|
||||
$bref_stack = array(&$blueprints);
|
||||
$head_stack = array($appended);
|
||||
|
||||
do {
|
||||
end($bref_stack);
|
||||
|
||||
$bref = &$bref_stack[key($bref_stack)];
|
||||
$head = array_pop($head_stack);
|
||||
|
||||
unset($bref_stack[key($bref_stack)]);
|
||||
|
||||
foreach (array_keys($head) as $key) {
|
||||
if (isset($key, $bref[$key]) && is_array($bref[$key]) && is_array($head[$key])) {
|
||||
$bref_stack[] = &$bref[$key];
|
||||
$head_stack[] = $head[$key];
|
||||
} else {
|
||||
$bref = array_merge($bref, array($key => $head[$key]));
|
||||
}
|
||||
}
|
||||
} while(count($head_stack));
|
||||
|
||||
$this->blueprints = $blueprints;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use \Symfony\Component\Yaml\Yaml;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
|
||||
/**
|
||||
* Blueprints class keeps track on blueprint instances.
|
||||
@@ -16,11 +16,15 @@ class Blueprints
|
||||
protected $instances = array();
|
||||
|
||||
/**
|
||||
* @param string $search Search path.
|
||||
* @param string|array $search Search path.
|
||||
*/
|
||||
public function __construct($search)
|
||||
{
|
||||
$this->search = rtrim($search, '\\/') . '/';
|
||||
if (!is_string($search)) {
|
||||
$this->search = $search;
|
||||
} else {
|
||||
$this->search = rtrim($search, '\\/') . '/';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,11 +37,18 @@ class Blueprints
|
||||
public function get($type)
|
||||
{
|
||||
if (!isset($this->instances[$type])) {
|
||||
if (is_file($this->search . $type . YAML_EXT)) {
|
||||
$blueprints = (array) Yaml::parse($this->search . $type . YAML_EXT);
|
||||
if (is_string($this->search)) {
|
||||
$filename = $this->search . $type . YAML_EXT;
|
||||
} else {
|
||||
$filename = isset($this->search[$type]) ? $this->search[$type] : '';
|
||||
}
|
||||
|
||||
if ($filename && is_file($filename)) {
|
||||
$file = CompiledYamlFile::instance($filename);
|
||||
$blueprints = $file->content();
|
||||
} else {
|
||||
// throw new \RuntimeException("Blueprints for '{$type}' cannot be found! {$this->search}{$type}");
|
||||
$blueprints = array();
|
||||
$blueprints = [];
|
||||
}
|
||||
|
||||
$blueprint = new Blueprint($type, $blueprints, $this);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<?php
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use Grav\Common\Filesystem\FileInterface;
|
||||
use \Grav\Common\Getters;
|
||||
use \Grav\Common\Filesystem\File;
|
||||
use RocketTheme\Toolbox\ArrayTraits\ArrayAccessWithGetters;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Countable;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
use RocketTheme\Toolbox\File\File;
|
||||
use RocketTheme\Toolbox\File\FileInterface;
|
||||
|
||||
/**
|
||||
* Recursive data object
|
||||
@@ -11,8 +13,10 @@ use \Grav\Common\Filesystem\File;
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Data extends Getters implements DataInterface
|
||||
class Data implements DataInterface
|
||||
{
|
||||
use ArrayAccessWithGetters, Countable, Export;
|
||||
|
||||
protected $gettersVariable = 'items';
|
||||
protected $items;
|
||||
|
||||
@@ -22,7 +26,7 @@ class Data extends Getters implements DataInterface
|
||||
protected $blueprints;
|
||||
|
||||
/**
|
||||
* @var File\General
|
||||
* @var File
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
@@ -121,17 +125,64 @@ class Data extends Getters implements DataInterface
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $default Default value (or null).
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return mixed Value.
|
||||
*/
|
||||
public function def($name, $default = null, $separator = '.')
|
||||
{
|
||||
$this->set($name, $this->get($name, $default, $separator), $separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join two values together by using blueprints if available.
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $value Value to be joined.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
*/
|
||||
public function join($name, $value, $separator = '.')
|
||||
{
|
||||
$old = $this->get($name, null, $separator);
|
||||
if ($old === null) {
|
||||
// Variable does not exist yet: just use the incoming value.
|
||||
} elseif ($this->blueprints) {
|
||||
// Blueprints: join values by using blueprints.
|
||||
$value = $this->blueprints->mergeData($old, $value, $name, $separator);
|
||||
} else {
|
||||
// No blueprints: replace existing top level variables with the new ones.
|
||||
$value = array_merge($old, $value);
|
||||
}
|
||||
|
||||
$this->set($name, $value, $separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join two values together by using blueprints if available.
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $value Value to be joined.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
*/
|
||||
public function joinDefaults($name, $value, $separator = '.')
|
||||
{
|
||||
$old = $this->get($name, null, $separator);
|
||||
if ($old === null) {
|
||||
// Variable does not exist yet: just use the incoming value.
|
||||
} elseif ($this->blueprints) {
|
||||
// Blueprints: join values by using blueprints.
|
||||
$value = $this->blueprints->mergeData($value, $old, $name, $separator);
|
||||
} else {
|
||||
// No blueprints: replace existing top level variables with the new ones.
|
||||
$value = array_merge($value, $old);
|
||||
}
|
||||
|
||||
$this->set($name, $value, $separator);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Merge two sets of data together.
|
||||
*
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function merge(array $data)
|
||||
{
|
||||
@@ -142,6 +193,21 @@ class Data extends Getters implements DataInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add default data to the set.
|
||||
*
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function setDefaults(array $data)
|
||||
{
|
||||
if ($this->blueprints) {
|
||||
$this->items = $this->blueprints->mergeData($data, $this->items);
|
||||
} else {
|
||||
$this->items = array_merge($data, $this->items);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return blueprints.
|
||||
*
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<?php
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use Grav\Common\Filesystem\FileInterface;
|
||||
use \Grav\Common\Filesystem\File;
|
||||
use RocketTheme\Toolbox\File\FileInterface;
|
||||
|
||||
/**
|
||||
* Data interface
|
||||
|
||||
@@ -114,12 +114,6 @@ class Validation
|
||||
|
||||
protected static function filterText($value, array $params, array $field)
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
var_dump($value);
|
||||
var_dump($params);
|
||||
var_dump($field);
|
||||
die();
|
||||
}
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use \Tracy\Debugger as TracyDebugger;
|
||||
use DebugBar\Bridge\Twig\TraceableTwigEnvironment;
|
||||
use DebugBar\JavascriptRenderer;
|
||||
use DebugBar\StandardDebugBar;
|
||||
//use \Tracy\Debugger as TracyDebugger;
|
||||
|
||||
/**
|
||||
* Class Debugger
|
||||
@@ -9,65 +12,110 @@ use \Tracy\Debugger as TracyDebugger;
|
||||
*/
|
||||
class Debugger
|
||||
{
|
||||
const PRODUCTION = TracyDebugger::PRODUCTION;
|
||||
const DEVELOPMENT = TracyDebugger::DEVELOPMENT;
|
||||
const DETECT = TracyDebugger::DETECT;
|
||||
protected $grav;
|
||||
protected $debugbar;
|
||||
protected $renderer;
|
||||
protected $enabled;
|
||||
|
||||
public function __construct($mode = self::PRODUCTION)
|
||||
public function __construct()
|
||||
{
|
||||
// Start the timer and enable debugger in production mode as we do not have system configuration yet.
|
||||
// Debugger catches all errors and logs them, for example if the script doesn't have write permissions.
|
||||
TracyDebugger::timer();
|
||||
TracyDebugger::enable($mode, is_dir(LOG_DIR) ? LOG_DIR : null);
|
||||
$this->debugbar = new StandardDebugBar();
|
||||
$this->debugbar['time']->addMeasure('Loading', $this->debugbar['time']->getRequestStartTime(), microtime(true));
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
$this->grav = Grav::instance();
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $grav['config'];
|
||||
|
||||
$mode = $config->get('system.debugger.mode');
|
||||
TracyDebugger::$logDirectory = $config->get('system.debugger.log.enabled') ? LOG_DIR : null;
|
||||
TracyDebugger::$maxDepth = $config->get('system.debugger.max_depth');
|
||||
|
||||
// Switch debugger into development mode if configured
|
||||
if ($config->get('system.debugger.enabled')) {
|
||||
if ($config->get('system.debugger.strict')) {
|
||||
TracyDebugger::$strictMode = true;
|
||||
}
|
||||
|
||||
if (function_exists('ini_set')) {
|
||||
ini_set('display_errors', true);
|
||||
}
|
||||
|
||||
if ($mode == strtolower('detect')) {
|
||||
TracyDebugger::$productionMode = self::DETECT;
|
||||
} elseif ($mode == strtolower('production')) {
|
||||
TracyDebugger::$productionMode = self::PRODUCTION;
|
||||
} else {
|
||||
TracyDebugger::$productionMode = self::DEVELOPMENT;
|
||||
}
|
||||
|
||||
|
||||
if ($this->enabled()) {
|
||||
$this->debugbar->addCollector(new \DebugBar\DataCollector\ConfigCollector((array)$this->grav['config']->get('system')));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message.
|
||||
*
|
||||
* @param string $message
|
||||
*/
|
||||
public function log($message)
|
||||
public function enabled($state = null)
|
||||
{
|
||||
if (TracyDebugger::$logDirectory) {
|
||||
TracyDebugger::log(sprintf($message, TracyDebugger::timer() * 1000));
|
||||
if (isset($state)) {
|
||||
$this->enabled = $state;
|
||||
} else {
|
||||
if (!isset($this->enabled)) {
|
||||
$this->enabled = $this->grav['config']->get('system.debugger.enabled');
|
||||
}
|
||||
}
|
||||
return $this->enabled;
|
||||
}
|
||||
|
||||
public static function dump($var)
|
||||
public function addAssets()
|
||||
{
|
||||
TracyDebugger::dump($var);
|
||||
if ($this->enabled()) {
|
||||
|
||||
$assets = $this->grav['assets'];
|
||||
|
||||
$this->renderer = $this->debugbar->getJavascriptRenderer();
|
||||
$this->renderer->setIncludeVendors(false);
|
||||
|
||||
// Get the required CSS files
|
||||
list($css_files, $js_files) = $this->renderer->getAssets(null, JavascriptRenderer::RELATIVE_URL);
|
||||
foreach ($css_files as $css) {
|
||||
$assets->addCss($css);
|
||||
}
|
||||
|
||||
$assets->addCss('/system/assets/debugger.css');
|
||||
|
||||
foreach ($js_files as $js) {
|
||||
$assets->addJs($js);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addCollector($collector)
|
||||
{
|
||||
$this->debugbar->addCollector($collector);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCollector($collector)
|
||||
{
|
||||
return $this->debugbar->getCollector($collector);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
if ($this->enabled()) {
|
||||
echo $this->renderer->render();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function sendDataInHeaders()
|
||||
{
|
||||
$this->debugbar->sendDataInHeaders();
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function startTimer($name, $desription = null)
|
||||
{
|
||||
if ($name[0] == '_' || $this->grav['config']->get('system.debugger.enabled')) {
|
||||
$this->debugbar['time']->startMeasure($name, $desription);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function stopTimer($name)
|
||||
{
|
||||
if ($name[0] == '_' || $this->grav['config']->get('system.debugger.enabled')) {
|
||||
$this->debugbar['time']->stopMeasure($name);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function addMessage($message, $label = 'info', $isString = true)
|
||||
{
|
||||
if ($this->enabled()) {
|
||||
$this->debugbar['messages']->addMessage($message, $label, $isString);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
55
system/src/Grav/Common/Errors/Errors.php
Normal file
55
system/src/Grav/Common/Errors/Errors.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
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;
|
||||
|
||||
/**
|
||||
* Class Debugger
|
||||
* @package Grav\Common
|
||||
*/
|
||||
class Errors extends \Whoops\Run
|
||||
{
|
||||
|
||||
public function pushHandler($handler, $key = null)
|
||||
{
|
||||
if (is_callable($handler)) {
|
||||
$handler = new CallbackHandler($handler);
|
||||
}
|
||||
|
||||
if (!$handler instanceof HandlerInterface) {
|
||||
throw new InvalidArgumentException(
|
||||
"Argument to " . __METHOD__ . " must be a callable, or instance of"
|
||||
. "Whoops\\Handler\\HandlerInterface"
|
||||
);
|
||||
}
|
||||
|
||||
// Store with key if provided
|
||||
if ($key) {
|
||||
$this->handlerStack[$key] = $handler;
|
||||
} else {
|
||||
$this->handlerStack[] = $handler;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function resetHandlers()
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
$config = $grav['config']->get('system.errors');
|
||||
if (isset($config['display']) && !$config['display']) {
|
||||
unset($this->handlerStack['pretty']);
|
||||
$this->handlerStack = array('simple' => new SimplePageHandler()) + $this->handlerStack;
|
||||
}
|
||||
if (isset($config['log']) && !$config['log']) {
|
||||
unset($this->handlerStack['log']);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
51
system/src/Grav/Common/Errors/Resources/error.css
Normal file
51
system/src/Grav/Common/Errors/Resources/error.css
Normal file
@@ -0,0 +1,51 @@
|
||||
html, body {
|
||||
height: 100%
|
||||
}
|
||||
body {
|
||||
margin:0 3rem;
|
||||
padding:0;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 1.5rem;
|
||||
line-height: 1.4;
|
||||
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
|
||||
display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */
|
||||
display: -ms-flexbox; /* TWEENER - IE 10 */
|
||||
display: -webkit-flex; /* NEW - Chrome */
|
||||
display: flex;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
-webkit-justify-content: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.container {
|
||||
margin: 0rem;
|
||||
max-width: 600px;
|
||||
padding-bottom:5rem;
|
||||
}
|
||||
|
||||
header {
|
||||
color: #000;
|
||||
font-size: 4rem;
|
||||
letter-spacing: 2px;
|
||||
line-height: 1.1;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
p {
|
||||
font-family: Optima, Segoe, "Segoe UI", Candara, Calibri, Arial, sans-serif;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-weight: normal;
|
||||
color: #999;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-weight: normal;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
code {
|
||||
font-weight: bold;
|
||||
}
|
||||
26
system/src/Grav/Common/Errors/Resources/layout.html.php
Normal file
26
system/src/Grav/Common/Errors/Resources/layout.html.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* Layout template file for Whoops's pretty error output.
|
||||
*/
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Whoops there was an error!</title>
|
||||
<style><?php echo $stylesheet ?></style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="details">
|
||||
<header>
|
||||
Server Error
|
||||
</header>
|
||||
<p>We're sorry! The server has encountered an internal error and was unable to complete your request.
|
||||
Please contact the system administrator for more information.</p>
|
||||
<h6>For further details please review your <code>logs/</code> folder, or enable displaying of errors in your system configuration.</h6>
|
||||
<h6>Error Code: <b><?php echo $code ?></b></h6>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
91
system/src/Grav/Common/Errors/SimplePageHandler.php
Normal file
91
system/src/Grav/Common/Errors/SimplePageHandler.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
namespace Grav\Common\Errors;
|
||||
|
||||
use Whoops\Handler\Handler;
|
||||
use Whoops\Util\Misc;
|
||||
use Whoops\Util\TemplateHelper;
|
||||
|
||||
class SimplePageHandler extends Handler
|
||||
{
|
||||
private $searchPaths = array();
|
||||
private $resourceCache = array();
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// Add the default, local resource search path:
|
||||
$this->searchPaths[] = __DIR__ . "/Resources";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$exception = $this->getException();
|
||||
$inspector = $this->getInspector();
|
||||
$run = $this->getRun();
|
||||
|
||||
$helper = new TemplateHelper();
|
||||
$templateFile = $this->getResource("layout.html.php");
|
||||
$cssFile = $this->getResource("error.css");
|
||||
|
||||
$code = $inspector->getException()->getCode();
|
||||
|
||||
if ($inspector->getException() instanceof \ErrorException) {
|
||||
$code = Misc::translateErrorCode($code);
|
||||
}
|
||||
|
||||
$vars = array(
|
||||
"stylesheet" => file_get_contents($cssFile),
|
||||
"code" => $code,
|
||||
);
|
||||
|
||||
$helper->setVariables($vars);
|
||||
$helper->render($templateFile);
|
||||
|
||||
return Handler::QUIT;
|
||||
}
|
||||
|
||||
protected function getResource($resource)
|
||||
{
|
||||
// If the resource was found before, we can speed things up
|
||||
// by caching its absolute, resolved path:
|
||||
if (isset($this->resourceCache[$resource])) {
|
||||
return $this->resourceCache[$resource];
|
||||
}
|
||||
|
||||
// Search through available search paths, until we find the
|
||||
// resource we're after:
|
||||
foreach ($this->searchPaths as $path) {
|
||||
$fullPath = $path . "/$resource";
|
||||
|
||||
if (is_file($fullPath)) {
|
||||
// Cache the result:
|
||||
$this->resourceCache[$resource] = $fullPath;
|
||||
return $fullPath;
|
||||
}
|
||||
}
|
||||
|
||||
// If we got this far, nothing was found.
|
||||
throw new RuntimeException(
|
||||
"Could not find resource '$resource' in any resource paths."
|
||||
. "(searched: " . join(", ", $this->searchPaths). ")"
|
||||
);
|
||||
}
|
||||
|
||||
public function addResourcePath($path)
|
||||
{
|
||||
if (!is_dir($path)) {
|
||||
throw new InvalidArgumentException(
|
||||
"'$path' is not a valid directory"
|
||||
);
|
||||
}
|
||||
|
||||
array_unshift($this->searchPaths, $path);
|
||||
}
|
||||
|
||||
public function getResourcePaths()
|
||||
{
|
||||
return $this->searchPaths;
|
||||
}
|
||||
}
|
||||
70
system/src/Grav/Common/File/CompiledFile.php
Normal file
70
system/src/Grav/Common/File/CompiledFile.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
namespace Grav\Common\File;
|
||||
|
||||
use RocketTheme\Toolbox\File\PhpFile;
|
||||
|
||||
/**
|
||||
* Class CompiledFile
|
||||
* @package Grav\Common\File
|
||||
*
|
||||
* @property string $filename
|
||||
* @property string $extension
|
||||
* @property string $raw
|
||||
* @property array|string $content
|
||||
*/
|
||||
trait CompiledFile
|
||||
{
|
||||
/**
|
||||
* Get/set parsed file contents.
|
||||
*
|
||||
* @param mixed $var
|
||||
* @return string
|
||||
*/
|
||||
public function content($var = null)
|
||||
{
|
||||
// If nothing has been loaded, attempt to get pre-compiled version of the file first.
|
||||
if ($var === null && $this->raw === null && $this->content === null) {
|
||||
$key = md5($this->filename);
|
||||
$file = PhpFile::instance(CACHE_DIR . "/compiled/files/{$key}{$this->extension}.php");
|
||||
$modified = $this->modified();
|
||||
|
||||
if (!$modified) {
|
||||
return $this->decode($this->raw());
|
||||
}
|
||||
|
||||
$class = get_class($this);
|
||||
|
||||
$cache = $file->exists() ? $file->content() : null;
|
||||
|
||||
// Load real file if cache isn't up to date (or is invalid).
|
||||
if (
|
||||
!isset($cache['@class'])
|
||||
|| $cache['@class'] != $class
|
||||
|| $cache['modified'] != $modified
|
||||
|| $cache['filename'] != $this->filename
|
||||
) {
|
||||
// Attempt to lock the file for writing.
|
||||
$file->lock(false);
|
||||
|
||||
// Decode RAW file into compiled array.
|
||||
$data = $this->decode($this->raw());
|
||||
$cache = [
|
||||
'@class' => $class,
|
||||
'filename' => $this->filename,
|
||||
'modified' => $modified,
|
||||
'data' => $data
|
||||
];
|
||||
|
||||
// If compiled file wasn't already locked by another process, save it.
|
||||
if ($file->locked() !== false) {
|
||||
$file->save($cache);
|
||||
$file->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
$this->content = $cache['data'];
|
||||
}
|
||||
|
||||
return parent::content($var);
|
||||
}
|
||||
}
|
||||
9
system/src/Grav/Common/File/CompiledMarkdownFile.php
Normal file
9
system/src/Grav/Common/File/CompiledMarkdownFile.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
namespace Grav\Common\File;
|
||||
|
||||
use RocketTheme\Toolbox\File\MarkdownFile;
|
||||
|
||||
class CompiledMarkdownFile extends MarkdownFile
|
||||
{
|
||||
use CompiledFile;
|
||||
}
|
||||
9
system/src/Grav/Common/File/CompiledYamlFile.php
Normal file
9
system/src/Grav/Common/File/CompiledYamlFile.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
namespace Grav\Common\File;
|
||||
|
||||
use RocketTheme\Toolbox\File\YamlFile;
|
||||
|
||||
class CompiledYamlFile extends YamlFile
|
||||
{
|
||||
use CompiledFile;
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Filesystem\File;
|
||||
|
||||
/**
|
||||
* File handling class.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Config extends General
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $extension = '.php';
|
||||
|
||||
/**
|
||||
* @var array|General[]
|
||||
*/
|
||||
static protected $instances = array();
|
||||
|
||||
/**
|
||||
* Saves configuration file and invalidates opcache.
|
||||
*
|
||||
* @param mixed $data Optional data to be saved, usually array.
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function save($data = null)
|
||||
{
|
||||
parent::save($data);
|
||||
|
||||
// Invalidate configuration file from the opcache.
|
||||
if (function_exists('opcache_invalidate')) {
|
||||
// PHP 5.5.5+
|
||||
@opcache_invalidate($this->filename);
|
||||
} elseif (function_exists('apc_invalidate')) {
|
||||
// APC
|
||||
@apc_invalidate($this->filename);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check contents and make sure it is in correct format.
|
||||
*
|
||||
* @param \Grav\Common\Config $var
|
||||
* @return \Grav\Common\Config
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function check($var)
|
||||
{
|
||||
if (!($var instanceof \Grav\Common\Config)) {
|
||||
throw new \RuntimeException('Provided data is not configuration');
|
||||
}
|
||||
|
||||
return $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode configuration object into RAW string (PHP class).
|
||||
*
|
||||
* @param \Grav\Common\Config $var
|
||||
* @return string
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function encode($var)
|
||||
{
|
||||
if (!($var instanceof \Grav\Common\Config)) {
|
||||
throw new \RuntimeException('Provided data is not configuration');
|
||||
}
|
||||
|
||||
// Build the object variables string
|
||||
$vars = array();
|
||||
$options = $var->toArray();
|
||||
|
||||
foreach ($options as $k => $v) {
|
||||
if (is_int($v)) {
|
||||
$vars[] = "\tpublic $" . $k . " = " . $v . ";";
|
||||
} elseif (is_bool($v)) {
|
||||
$vars[] = "\tpublic $" . $k . " = " . ($v ? 'true' : 'false') . ";";
|
||||
} elseif (is_scalar($v)) {
|
||||
$vars[] = "\tpublic $" . $k . " = '" . addcslashes($v, '\\\'') . "';";
|
||||
} elseif (is_array($v) || is_object($v)) {
|
||||
$vars[] = "\tpublic $" . $k . " = " . $this->encodeArray((array) $v) . ";";
|
||||
}
|
||||
}
|
||||
$vars = implode("\n", $vars);
|
||||
|
||||
return "<?php\nnamespace Grav;\n\nclass Config extends \\Grav\\Common\\Config {\n {$vars}\n}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get an array as an exported string.
|
||||
*
|
||||
* @param array $a The array to get as a string.
|
||||
* @param int $level Used internally to indent rows.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function encodeArray($a, $level = 1)
|
||||
{
|
||||
$r = array();
|
||||
foreach ($a as $k => $v) {
|
||||
if (is_array($v) || is_object($v)) {
|
||||
$r[] = '"' . $k . '" => ' . $this->encodeArray((array) $v, $level+1);
|
||||
} elseif (is_int($v)) {
|
||||
$r[] = "'" . $k . "' => " . $v;
|
||||
} elseif (is_bool($v)) {
|
||||
$r[] = "'" . $k . "' => " . ($v ? 'true' : 'false');
|
||||
} else {
|
||||
$r[] .= "'" . $k . "' => " . "'" . addslashes($v) . "'";
|
||||
}
|
||||
}
|
||||
|
||||
$tabs = str_repeat("\t", $level);
|
||||
return "array(\n\t{$tabs}" . implode(",\n\t{$tabs}", $r) . "\n{$tabs})";
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode RAW string into contents.
|
||||
*
|
||||
* @param string $var
|
||||
* @return \Grav\Common\Config
|
||||
*/
|
||||
protected function decode($var)
|
||||
{
|
||||
// TODO: improve this one later, works only for single file...
|
||||
return class_exists('\Grav\Config') ? new \Grav\Config($this->filename) : new Config($this->filename);
|
||||
}
|
||||
}
|
||||
@@ -1,352 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Filesystem\File;
|
||||
|
||||
use Grav\Common\Filesystem\FileInterface;
|
||||
|
||||
/**
|
||||
* General file handling class.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class General implements FileInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $filename;
|
||||
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
protected $handle;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
protected $locked;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $extension;
|
||||
|
||||
/**
|
||||
* @var string Raw file contents.
|
||||
*/
|
||||
protected $raw;
|
||||
|
||||
/**
|
||||
* @var array Parsed file contents.
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* @var array|General[]
|
||||
*/
|
||||
static protected $instances = array();
|
||||
|
||||
/**
|
||||
* Get file instance.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return FileInterface
|
||||
*/
|
||||
public static function instance($filename)
|
||||
{
|
||||
if (!isset(static::$instances[$filename])) {
|
||||
static::$instances[$filename] = new static;
|
||||
static::$instances[$filename]->init($filename);
|
||||
}
|
||||
return static::$instances[$filename];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent constructor from being used.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent cloning.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected function __clone()
|
||||
{
|
||||
//Me not like clones! Me smash clones!
|
||||
}
|
||||
|
||||
/**
|
||||
* Set filename.
|
||||
*
|
||||
* @param $filename
|
||||
*/
|
||||
protected function init($filename)
|
||||
{
|
||||
$this->filename = $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set the file location.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
public function filename($var = null)
|
||||
{
|
||||
if ($var !== null) {
|
||||
$this->filename = $var;
|
||||
}
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return basename of the file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function basename()
|
||||
{
|
||||
return basename($this->filename, $this->extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if file exits.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists()
|
||||
{
|
||||
return is_file($this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return file modification time.
|
||||
*
|
||||
* @return int|bool Timestamp or false if file doesn't exist.
|
||||
*/
|
||||
public function modified()
|
||||
{
|
||||
return is_file($this->filename) ? filemtime($this->filename) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock file for writing. You need to manually unlock().
|
||||
*
|
||||
* @param bool $block For non-blocking lock, set the parameter to false.
|
||||
* @return bool
|
||||
*/
|
||||
public function lock($block = true)
|
||||
{
|
||||
if (!$this->handle) {
|
||||
if (!$this->mkdir(dirname($this->filename))) {
|
||||
throw new \RuntimeException('Creating directory failed for ' . $this->filename);
|
||||
}
|
||||
$this->handle = fopen($this->filename, 'wb+');
|
||||
}
|
||||
$lock = $block ? LOCK_EX : LOCK_EX | LOCK_NB;
|
||||
return $this->locked = $this->handle ? flock($this->handle, $lock) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if file has been locked for writing.
|
||||
*
|
||||
* @return bool|null True = locked, false = failed, null = not locked.
|
||||
*/
|
||||
public function locked()
|
||||
{
|
||||
return $this->locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock file.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unlock()
|
||||
{
|
||||
if (!$this->handle) {
|
||||
return;
|
||||
}
|
||||
if ($this->locked) {
|
||||
flock($this->handle, LOCK_UN);
|
||||
$this->locked = null;
|
||||
}
|
||||
fclose($this->handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if file can be written.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function writable()
|
||||
{
|
||||
return is_writable($this->filename) || $this->writableDir(dirname($this->filename));
|
||||
}
|
||||
|
||||
/**
|
||||
* (Re)Load a file and return RAW file contents.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
$this->raw = $this->exists() ? (string) file_get_contents($this->filename) : '';
|
||||
$this->content = null;
|
||||
|
||||
return $this->raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set raw file contents.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
public function raw($var = null)
|
||||
{
|
||||
if ($var !== null) {
|
||||
$this->raw = (string) $var;
|
||||
$this->content = null;
|
||||
}
|
||||
|
||||
if (!is_string($this->raw)) {
|
||||
$this->raw = $this->load();
|
||||
}
|
||||
|
||||
return $this->raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set parsed file contents.
|
||||
*
|
||||
* @param mixed $var
|
||||
* @return string
|
||||
*/
|
||||
public function content($var = null)
|
||||
{
|
||||
if ($var !== null) {
|
||||
$this->content = $this->check($var);
|
||||
|
||||
// Update RAW, too.
|
||||
$this->raw = $this->encode($this->content);
|
||||
|
||||
} elseif ($this->content === null) {
|
||||
// Decode RAW file.
|
||||
$this->content = $this->decode($this->raw());
|
||||
}
|
||||
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save file.
|
||||
*
|
||||
* @param mixed $data Optional data to be saved, usually array.
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function save($data = null)
|
||||
{
|
||||
if ($data !== null) {
|
||||
$this->content($data);
|
||||
}
|
||||
|
||||
if (!$this->locked) {
|
||||
// Obtain blocking lock or fail.
|
||||
if (!$this->lock()) {
|
||||
throw new \RuntimeException('Obtaining write lock failed on file: ' . $this->filename);
|
||||
}
|
||||
$lock = true;
|
||||
}
|
||||
|
||||
if (@fwrite($this->handle, $this->raw()) === false) {
|
||||
$this->unlock();
|
||||
throw new \RuntimeException('Saving file failed: ' . $this->filename);
|
||||
}
|
||||
|
||||
if (isset($lock)) {
|
||||
$this->unlock();
|
||||
}
|
||||
|
||||
// Touch the directory as well, thus marking it modified.
|
||||
@touch(dirname($this->filename));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete file from filesystem.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
return unlink($this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check contents and make sure it is in correct format.
|
||||
*
|
||||
* Override in derived class.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
protected function check($var)
|
||||
{
|
||||
return (string) $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode contents into RAW string.
|
||||
*
|
||||
* Override in derived class.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
protected function encode($var)
|
||||
{
|
||||
return (string) $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode RAW string into contents.
|
||||
*
|
||||
* Override in derived class.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string mixed
|
||||
*/
|
||||
protected function decode($var)
|
||||
{
|
||||
return (string) $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $dir
|
||||
* @return bool
|
||||
* @internal
|
||||
*/
|
||||
protected function mkdir($dir)
|
||||
{
|
||||
return is_dir($dir) || mkdir($dir, 0777, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $dir
|
||||
* @return bool
|
||||
* @internal
|
||||
*/
|
||||
protected function writableDir($dir)
|
||||
{
|
||||
if ($dir && !file_exists($dir)) {
|
||||
return $this->writableDir(dirname($dir));
|
||||
}
|
||||
|
||||
return $dir && is_dir($dir) && is_writable($dir);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Filesystem\File;
|
||||
|
||||
/**
|
||||
* File handling class for JSON.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Json extends General
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $extension = '.json';
|
||||
|
||||
/**
|
||||
* @var array|General[]
|
||||
*/
|
||||
static protected $instances = array();
|
||||
|
||||
/**
|
||||
* Check contents and make sure it is in correct format.
|
||||
*
|
||||
* @param array $var
|
||||
* @return array
|
||||
*/
|
||||
protected function check($var)
|
||||
{
|
||||
return (array) $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode contents into RAW string.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
protected function encode($var)
|
||||
{
|
||||
return (string) json_encode($var);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode RAW string into contents.
|
||||
*
|
||||
* @param string $var
|
||||
* @return array mixed
|
||||
*/
|
||||
protected function decode($var)
|
||||
{
|
||||
return (array) json_decode($var);
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Filesystem\File;
|
||||
|
||||
/**
|
||||
* File handling class for Log files.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Log extends General
|
||||
{
|
||||
/**
|
||||
* @var array|General[]
|
||||
*/
|
||||
static protected $instances = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
protected function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->extension = '.log';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check contents and make sure it is in correct format.
|
||||
*
|
||||
* @param array $var
|
||||
* @return array
|
||||
*/
|
||||
protected function check($var)
|
||||
{
|
||||
return (array) $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode contents into RAW string (unsupported).
|
||||
*
|
||||
* @param string $var
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function encode($var)
|
||||
{
|
||||
throw new \Exception('Saving log file is forbidden.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode RAW string into contents.
|
||||
*
|
||||
* @param string $var
|
||||
* @return array mixed
|
||||
*/
|
||||
protected function decode($var)
|
||||
{
|
||||
$lines = (array) preg_split('#(\r\n|\n|\r)#', $var);
|
||||
|
||||
$results = array();
|
||||
foreach ($lines as $line) {
|
||||
preg_match('#^\[(.*)\] (.*) @ (.*) @@ (.*)$#', $line, $matches);
|
||||
if ($matches) {
|
||||
$results[] = ['date' => $matches[1], 'message' => $matches[2], 'url' => $matches[3], 'file' => $matches[4]];
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Filesystem\File;
|
||||
|
||||
use \Symfony\Component\Yaml\Yaml as YamlParser;
|
||||
|
||||
/**
|
||||
* File handling class.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Markdown extends General
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $extension = '.md';
|
||||
|
||||
/**
|
||||
* @var array|General[]
|
||||
*/
|
||||
static protected $instances = array();
|
||||
|
||||
/**
|
||||
* Get/set file header.
|
||||
*
|
||||
* @param array $var
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function header(array $var = null)
|
||||
{
|
||||
$content = $this->content();
|
||||
|
||||
if ($var !== null) {
|
||||
$content['header'] = $var;
|
||||
$this->content($content);
|
||||
}
|
||||
|
||||
return $content['header'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set markdown content.
|
||||
*
|
||||
* @param string $var
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function markdown($var = null)
|
||||
{
|
||||
$content = $this->content();
|
||||
|
||||
if ($var !== null) {
|
||||
$content['markdown'] = (string) $var;
|
||||
$this->content($content);
|
||||
}
|
||||
|
||||
return $content['markdown'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check contents and make sure it is in correct format.
|
||||
*
|
||||
* @param array $var
|
||||
* @return array
|
||||
*/
|
||||
protected function check($var)
|
||||
{
|
||||
$var = (array) $var;
|
||||
if (!isset($var['header']) || !is_array($var['header'])) {
|
||||
$var['header'] = array();
|
||||
}
|
||||
if (!isset($var['markdown']) || !is_string($var['markdown'])) {
|
||||
$var['markdown'] = '';
|
||||
}
|
||||
|
||||
return $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode contents into RAW string.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
protected function encode($var)
|
||||
{
|
||||
// Create Markdown file with YAML header.
|
||||
$o = (!empty($var['header']) ? "---\n" . trim(YamlParser::dump($var['header'])) . "\n---\n\n" : '') . $var['markdown'];
|
||||
|
||||
// Normalize line endings to Unix style.
|
||||
$o = preg_replace("/(\r\n|\r)/", "\n", $o);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode RAW string into contents.
|
||||
*
|
||||
* @param string $var
|
||||
* @return array mixed
|
||||
*/
|
||||
protected function decode($var)
|
||||
{
|
||||
$content = array();
|
||||
|
||||
// Normalize line endings to Unix style.
|
||||
$var = preg_replace("/(\r\n|\r)/", "\n", $var);
|
||||
|
||||
// Parse header.
|
||||
preg_match("/---\n(.+?)\n---(\n\n|$)/uism", $var, $m);
|
||||
$content['header'] = isset($m[1]) ? YamlParser::parse(preg_replace("/\n\t/", "\n ", $m[1])) : array();
|
||||
|
||||
// Strip header to get content.
|
||||
$content['markdown'] = trim(preg_replace("/---\n(.+?)\n---(\n\n|$)/uism", '', $var));
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Filesystem\File;
|
||||
|
||||
use \Symfony\Component\Yaml\Yaml as YamlParser;
|
||||
|
||||
/**
|
||||
* File handling class for YAML.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Yaml extends General
|
||||
{
|
||||
/**
|
||||
* @var array|General[]
|
||||
*/
|
||||
static protected $instances = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
protected function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->extension = YAML_EXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check contents and make sure it is in correct format.
|
||||
*
|
||||
* @param array $var
|
||||
* @return array
|
||||
*/
|
||||
protected function check($var)
|
||||
{
|
||||
return (array) $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode contents into RAW string.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
protected function encode($var)
|
||||
{
|
||||
return (string) YamlParser::dump($var);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode RAW string into contents.
|
||||
*
|
||||
* @param string $var
|
||||
* @return array mixed
|
||||
*/
|
||||
protected function decode($var)
|
||||
{
|
||||
return (array) YamlParser::parse($var);
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Filesystem;
|
||||
|
||||
/**
|
||||
* File interface.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
interface FileInterface
|
||||
{
|
||||
/**
|
||||
* Get file instance.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return mixed
|
||||
*/
|
||||
public static function instance($filename);
|
||||
|
||||
/**
|
||||
* Check if file exits.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists();
|
||||
|
||||
/**
|
||||
* Return file modification time.
|
||||
*
|
||||
* @return int Timestamp
|
||||
*/
|
||||
public function modified();
|
||||
|
||||
/**
|
||||
* Lock file for writing. Lock gets automatically released during the save().
|
||||
*
|
||||
* @param bool $block For non-blocking lock, set the parameter to false.
|
||||
* @return bool
|
||||
*/
|
||||
public function lock($block = true);
|
||||
|
||||
/**
|
||||
* Returns true if file has been locked for writing.
|
||||
*
|
||||
* @return bool|null True = locked, false = failed, null = not locked.
|
||||
*/
|
||||
public function locked();
|
||||
|
||||
/**
|
||||
* Unlock file.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unlock();
|
||||
|
||||
/**
|
||||
* Check if file can be written.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function writable();
|
||||
|
||||
/**
|
||||
* (Re)Load a file and return its contents.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function load();
|
||||
|
||||
/**
|
||||
* Get/set raw file contents.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
public function raw($var = null);
|
||||
|
||||
/**
|
||||
* Get/set parsed file contents.
|
||||
*
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
public function content($var = null);
|
||||
|
||||
/**
|
||||
* Save file.
|
||||
*
|
||||
* @param string $data Optional data to be saved.
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function save($data = null);
|
||||
|
||||
/**
|
||||
* Delete file from filesystem.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete();
|
||||
}
|
||||
@@ -12,7 +12,7 @@ abstract class Folder
|
||||
/**
|
||||
* Recursively find the last modified time under given path.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $path
|
||||
* @return int
|
||||
*/
|
||||
public static function lastModifiedFolder($path)
|
||||
@@ -29,13 +29,25 @@ abstract class Folder
|
||||
$last_modified = $dir_modified;
|
||||
}
|
||||
}
|
||||
|
||||
return $last_modified;
|
||||
}
|
||||
|
||||
|
||||
public static function getRelativePath($to, $from = ROOT_DIR)
|
||||
{
|
||||
$from = preg_replace('![\\|/]+!', '/', $from);
|
||||
$to = preg_replace('![\\|/]+!', '/', $to);
|
||||
if (strpos($to, $from) === 0) {
|
||||
$to = substr($to, strlen($from));
|
||||
}
|
||||
|
||||
return $to;
|
||||
}
|
||||
/**
|
||||
* Recursively find the last modified time under given path by file.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $path
|
||||
* @return int
|
||||
*/
|
||||
public static function lastModifiedFile($path)
|
||||
@@ -48,43 +60,43 @@ abstract class Folder
|
||||
|
||||
/** @var \RecursiveDirectoryIterator $file */
|
||||
foreach ($itr as $file) {
|
||||
if (!$file->isDir()) {
|
||||
$file_modified = $file->getMTime();
|
||||
if ($file_modified > $last_modified) {
|
||||
$last_modified = $file_modified;
|
||||
}
|
||||
$file_modified = $file->getMTime();
|
||||
if ($file_modified > $last_modified) {
|
||||
$last_modified = $file_modified;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $last_modified;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return recursive list of all files and directories under given path.
|
||||
*
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function all($path, array $params = array())
|
||||
{
|
||||
$path = realpath($path);
|
||||
|
||||
if ($path === false) {
|
||||
throw new \RuntimeException("Path to {$path} doesn't exist.");
|
||||
}
|
||||
|
||||
$compare = $params['compare'] ? 'get' . $params['compare'] : null;
|
||||
$pattern = $params['pattern'] ? $params['pattern'] : null;
|
||||
$filters = $params['filters'] ? $params['filters'] : null;
|
||||
$key = $params['key'] ? 'get' . $params['key'] : null;
|
||||
$value = $params['value'] ? 'get' . $params['value'] : 'SubPathname';
|
||||
$compare = isset($params['compare']) ? 'get' . $params['compare'] : null;
|
||||
$pattern = isset($params['pattern']) ? $params['pattern'] : null;
|
||||
$filters = isset($params['filters']) ? $params['filters'] : null;
|
||||
$recursive = isset($params['recursive']) ? $params['recursive'] : true;
|
||||
$key = isset($params['key']) ? 'get' . $params['key'] : null;
|
||||
$value = isset($params['value']) ? 'get' . $params['value'] : ($recursive ? 'getSubPathname' : 'getFilename');
|
||||
|
||||
$directory = new \RecursiveDirectoryIterator($path,
|
||||
\RecursiveDirectoryIterator::SKIP_DOTS + \FilesystemIterator::UNIX_PATHS + \FilesystemIterator::CURRENT_AS_SELF);
|
||||
$iterator = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
if ($recursive) {
|
||||
$directory = new \RecursiveDirectoryIterator($path,
|
||||
\RecursiveDirectoryIterator::SKIP_DOTS + \FilesystemIterator::UNIX_PATHS + \FilesystemIterator::CURRENT_AS_SELF);
|
||||
$iterator = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
} else {
|
||||
$iterator = new \FilesystemIterator($path);
|
||||
}
|
||||
|
||||
$results = array();
|
||||
|
||||
@@ -100,20 +112,30 @@ abstract class Folder
|
||||
$fileKey = preg_replace($filters['key'], '', $fileKey);
|
||||
}
|
||||
if (isset($filters['value'])) {
|
||||
$filePath = preg_replace($filters['value'], '', $filePath);
|
||||
$filter = $filters['value'];
|
||||
if (is_callable($filter)) {
|
||||
$filePath = call_user_func($filter, $file);
|
||||
} else {
|
||||
$filePath = preg_replace($filter, '', $filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($fileKey !== null) {
|
||||
$results[$fileKey] = $filePath;
|
||||
} else {
|
||||
$results[] = $filePath;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively copy directory in filesystem.
|
||||
*
|
||||
* @param string $source
|
||||
* @param string $target
|
||||
* @param string $source
|
||||
* @param string $target
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function copy($source, $target)
|
||||
@@ -157,8 +179,8 @@ abstract class Folder
|
||||
/**
|
||||
* Move directory in filesystem.
|
||||
*
|
||||
* @param string $source
|
||||
* @param string $target
|
||||
* @param string $source
|
||||
* @param string $target
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function move($source, $target)
|
||||
@@ -186,7 +208,7 @@ abstract class Folder
|
||||
/**
|
||||
* Recursively delete directory from filesystem.
|
||||
*
|
||||
* @param string $target
|
||||
* @param string $target
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function delete($target)
|
||||
@@ -204,6 +226,26 @@ abstract class Folder
|
||||
|
||||
// Make sure that the change will be detected when caching.
|
||||
@touch(dirname($target));
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $folder
|
||||
* @throws \RuntimeException
|
||||
* @internal
|
||||
*/
|
||||
public static function mkdir($folder)
|
||||
{
|
||||
if (is_dir($folder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$success = @mkdir($folder, 0777, true);
|
||||
|
||||
if (!$success) {
|
||||
$error = error_get_last();
|
||||
throw new \RuntimeException($error['message']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,34 +269,16 @@ abstract class Folder
|
||||
|
||||
return @rmdir($folder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $folder
|
||||
* @throws \RuntimeException
|
||||
* @internal
|
||||
*/
|
||||
protected static function mkdir($folder)
|
||||
{
|
||||
if (is_dir($folder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$success = @mkdir($folder, 0777, true);
|
||||
|
||||
if (!$success) {
|
||||
$error = error_get_last();
|
||||
throw new \RuntimeException($error['message']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GravRecursiveFilterIterator extends \RecursiveFilterIterator {
|
||||
|
||||
class GravRecursiveFilterIterator extends \RecursiveFilterIterator
|
||||
{
|
||||
public static $FILTERS = array(
|
||||
'.', '..', '.DS_Store'
|
||||
'..', '.DS_Store'
|
||||
);
|
||||
|
||||
public function accept() {
|
||||
public function accept()
|
||||
{
|
||||
return !in_array(
|
||||
$this->current()->getFilename(),
|
||||
self::$FILTERS,
|
||||
|
||||
364
system/src/Grav/Common/GPM/GPM.php
Normal file
364
system/src/Grav/Common/GPM/GPM.php
Normal file
@@ -0,0 +1,364 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\Iterator;
|
||||
|
||||
class GPM extends Iterator
|
||||
{
|
||||
/**
|
||||
* Local installed Packages
|
||||
* @var Local\Packages
|
||||
*/
|
||||
private $installed;
|
||||
|
||||
/**
|
||||
* Remote available Packages
|
||||
* @var Remote\Packages
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var Remote\Grav
|
||||
*/
|
||||
public $grav;
|
||||
|
||||
/**
|
||||
* Internal cache
|
||||
* @var
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* Creates a new GPM instance with Local and Remote packages available
|
||||
* @param boolean $refresh Applies to Remote Packages only and forces a refetch of data
|
||||
* @param callable $callback Either a function or callback in array notation
|
||||
*/
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
$this->installed = new Local\Packages();
|
||||
$this->repository = new Remote\Packages($refresh, $callback);
|
||||
$this->grav = new Remote\Grav($refresh, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Locally installed packages
|
||||
* @return Iterator The installed packages
|
||||
*/
|
||||
public function getInstalled()
|
||||
{
|
||||
return $this->installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of locally installed packages
|
||||
* @return integer Amount of installed packages
|
||||
*/
|
||||
public function countInstalled()
|
||||
{
|
||||
$installed = $this->getInstalled();
|
||||
|
||||
return count($installed['plugins']) + count($installed['themes']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the instance of a specific Plugin
|
||||
* @param string $slug The slug of the Plugin
|
||||
* @return Local\Package The instance of the Plugin
|
||||
*/
|
||||
public function getInstalledPlugin($slug)
|
||||
{
|
||||
return $this->installed['plugins'][$slug];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Locally installed plugins
|
||||
* @return Iterator The installed plugins
|
||||
*/
|
||||
public function getInstalledPlugins()
|
||||
{
|
||||
return $this->installed['plugins'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a Plugin is installed
|
||||
* @param string $slug The slug of the Plugin
|
||||
* @return boolean True if the Plugin has been installed. False otherwise
|
||||
*/
|
||||
public function isPluginInstalled($slug)
|
||||
{
|
||||
return isset($this->installed['plugins'][$slug]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the instance of a specific Theme
|
||||
* @param string $slug The slug of the Theme
|
||||
* @return Local\Package The instance of the Theme
|
||||
*/
|
||||
public function getInstalledTheme($slug)
|
||||
{
|
||||
return $this->installed['themes'][$slug];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Locally installed themes
|
||||
* @return Iterator The installed themes
|
||||
*/
|
||||
public function getInstalledThemes()
|
||||
{
|
||||
return $this->installed['themes'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a Theme is installed
|
||||
* @param string $slug The slug of the Theme
|
||||
* @return boolean True if the Theme has been installed. False otherwise
|
||||
*/
|
||||
public function isThemeInstalled($slug)
|
||||
{
|
||||
return isset($this->installed['themes'][$slug]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of updates available
|
||||
* @return integer Amount of available updates
|
||||
*/
|
||||
public function countUpdates()
|
||||
{
|
||||
$count = 0;
|
||||
|
||||
$count += count($this->getUpdatablePlugins());
|
||||
$count += count($this->getUpdatableThemes());
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of Plugins and Themes that can be updated.
|
||||
* Plugins and Themes are extended with the `available` property that relies to the remote version
|
||||
* @return array Array of updatable Plugins and Themes.
|
||||
* Format: ['total' => int, 'plugins' => array, 'themes' => array]
|
||||
*/
|
||||
public function getUpdatable()
|
||||
{
|
||||
$plugins = $this->getUpdatablePlugins();
|
||||
$themes = $this->getUpdatableThemes();
|
||||
|
||||
$items = [
|
||||
'total' => count($plugins)+count($themes),
|
||||
'plugins' => $plugins,
|
||||
'themes' => $themes
|
||||
];
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of Plugins that can be updated.
|
||||
* The Plugins are extended with the `available` property that relies to the remote version
|
||||
* @return Iterator Array of updatable Plugins
|
||||
*/
|
||||
public function getUpdatablePlugins()
|
||||
{
|
||||
$items = [];
|
||||
$repository = $this->repository['plugins'];
|
||||
|
||||
// local cache to speed things up
|
||||
if (isset($this->cache[__METHOD__])) {
|
||||
return $this->cache[__METHOD__];
|
||||
}
|
||||
|
||||
foreach ($this->installed['plugins'] as $slug => $plugin) {
|
||||
if (!isset($repository[$slug]) || $plugin->symlink) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$local_version = $plugin->version ? $plugin->version : 'Unknown';
|
||||
$remote_version = $repository[$slug]->version;
|
||||
|
||||
if (version_compare($local_version, $remote_version) < 0) {
|
||||
$repository[$slug]->available = $remote_version;
|
||||
$repository[$slug]->version = $local_version;
|
||||
$items[$slug] = $repository[$slug];
|
||||
}
|
||||
}
|
||||
|
||||
$this->cache[__METHOD__] = $items;
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a Plugin or Theme is updatable
|
||||
* @param string $slug The slug of the package
|
||||
* @return boolean True if updatable. False otherwise or if not found
|
||||
*/
|
||||
public function isUpdatable($slug)
|
||||
{
|
||||
return $this->isPluginUpdatable($slug) || $this->isThemeUpdatable($slug);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a Plugin is updatable
|
||||
* @param string $plugin The slug of the Plugin
|
||||
* @return boolean True if the Plugin is updatable. False otherwise
|
||||
*/
|
||||
public function isPluginUpdatable($plugin)
|
||||
{
|
||||
return array_key_exists($plugin, (array) $this->getUpdatablePlugins());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of Themes that can be updated.
|
||||
* The Themes are extended with the `available` property that relies to the remote version
|
||||
* @return Iterator Array of updatable Themes
|
||||
*/
|
||||
public function getUpdatableThemes()
|
||||
{
|
||||
$items = [];
|
||||
$repository = $this->repository['themes'];
|
||||
|
||||
// local cache to speed things up
|
||||
if (isset($this->cache[__METHOD__])) {
|
||||
return $this->cache[__METHOD__];
|
||||
}
|
||||
|
||||
foreach ($this->installed['themes'] as $slug => $plugin) {
|
||||
if (!isset($repository[$slug]) || $plugin->symlink) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$local_version = $plugin->version ? $plugin->version : 'Unknown';
|
||||
$remote_version = $repository[$slug]->version;
|
||||
|
||||
if (version_compare($local_version, $remote_version) < 0) {
|
||||
$repository[$slug]->available = $remote_version;
|
||||
$repository[$slug]->version = $local_version;
|
||||
$items[$slug] = $repository[$slug];
|
||||
}
|
||||
}
|
||||
|
||||
$this->cache[__METHOD__] = $items;
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a Theme is Updatable
|
||||
* @param string $theme The slug of the Theme
|
||||
* @return boolean True if the Theme is updatable. False otherwise
|
||||
*/
|
||||
public function isThemeUpdatable($theme)
|
||||
{
|
||||
return array_key_exists($theme, (array) $this->getUpdatableThemes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Plugin from the repository
|
||||
* @param string $slug The slug of the Plugin
|
||||
* @return mixed Package if found, NULL if not
|
||||
*/
|
||||
public function getRepositoryPlugin($slug)
|
||||
{
|
||||
return @$this->repository['plugins'][$slug];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of Plugins available in the repository
|
||||
* @return Iterator The Plugins remotely available
|
||||
*/
|
||||
public function getRepositoryPlugins()
|
||||
{
|
||||
return $this->repository['plugins'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Theme from the repository
|
||||
* @param string $slug The slug of the Theme
|
||||
* @return mixed Package if found, NULL if not
|
||||
*/
|
||||
public function getRepositoryTheme($slug)
|
||||
{
|
||||
return @$this->repository['themes'][$slug];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of Themes available in the repository
|
||||
* @return Iterator The Themes remotely available
|
||||
*/
|
||||
public function getRepositoryThemes()
|
||||
{
|
||||
return $this->repository['themes'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of Plugins and Themes available in the repository
|
||||
* @return array Array of available Plugins and Themes
|
||||
* Format: ['plugins' => array, 'themes' => array]
|
||||
*/
|
||||
public function getRepository()
|
||||
{
|
||||
return $this->repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a Package in the repository
|
||||
* @param string $search Can be either the slug or the name
|
||||
* @return Remote\Package Package if found, FALSE if not
|
||||
*/
|
||||
public function findPackage($search)
|
||||
{
|
||||
$search = strtolower($search);
|
||||
if ($found = $this->getRepositoryTheme($search)) {
|
||||
return $found;
|
||||
}
|
||||
|
||||
if ($found = $this->getRepositoryPlugin($search)) {
|
||||
return $found;
|
||||
}
|
||||
|
||||
foreach ($this->getRepositoryThemes() as $slug => $theme) {
|
||||
if ($search == $slug || $search == $theme->name) {
|
||||
return $theme;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->getRepositoryPlugins() as $slug => $plugin) {
|
||||
if ($search == $slug || $search == $plugin->name) {
|
||||
return $plugin;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of Plugins and Themes available in the repository
|
||||
* @return array Array of available Plugins and Themes
|
||||
* Format: ['plugins' => array, 'themes' => array]
|
||||
*/
|
||||
/**
|
||||
* Searches for a list of Packages in the repository
|
||||
* @param array $searches An array of either slugs or names
|
||||
* @return array Array of found Packages
|
||||
* Format: ['total' => int, 'not_found' => array, <found-slugs>]
|
||||
*/
|
||||
public function findPackages($searches = [])
|
||||
{
|
||||
$packages = ['total' => 0, 'not_found' => []];
|
||||
|
||||
foreach ($searches as $search) {
|
||||
if ($found = $this->findPackage($search)) {
|
||||
if (!isset($packages[$found->package_type])) {
|
||||
$packages[$found->package_type] = [];
|
||||
}
|
||||
|
||||
$packages[$found->package_type][$found->slug] = $found;
|
||||
$packages['total']++;
|
||||
} else {
|
||||
$packages['not_found'][] = $search;
|
||||
}
|
||||
}
|
||||
|
||||
return $packages;
|
||||
}
|
||||
}
|
||||
273
system/src/Grav/Common/GPM/Installer.php
Normal file
273
system/src/Grav/Common/GPM/Installer.php
Normal file
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
|
||||
class Installer
|
||||
{
|
||||
/** @const No error */
|
||||
const OK = 0;
|
||||
/** @const Target already exists */
|
||||
const EXISTS = 1;
|
||||
/** @const Target is a symbolic link */
|
||||
const IS_LINK = 2;
|
||||
/** @const Target doesn't exist */
|
||||
const NOT_FOUND = 4;
|
||||
/** @const Target is not a directory */
|
||||
const NOT_DIRECTORY = 8;
|
||||
/** @const Target is not a Grav instance */
|
||||
const NOT_GRAV_ROOT = 16;
|
||||
/** @const Error while trying to open the ZIP package */
|
||||
const ZIP_OPEN_ERROR = 32;
|
||||
/** @const Error while trying to extract the ZIP package */
|
||||
const ZIP_EXTRACT_ERROR = 64;
|
||||
|
||||
/**
|
||||
* Destination folder on which validation checks are applied
|
||||
* @var string
|
||||
*/
|
||||
protected static $target;
|
||||
|
||||
/**
|
||||
* Error Code
|
||||
* @var integer
|
||||
*/
|
||||
protected static $error = 0;
|
||||
|
||||
/**
|
||||
* Default options for the install
|
||||
* @var array
|
||||
*/
|
||||
protected static $options = [
|
||||
'overwrite' => true,
|
||||
'ignore_symlinks' => true,
|
||||
'sophisticated' => false,
|
||||
'install_path' => '',
|
||||
'exclude_checks' => [self::EXISTS, self::NOT_FOUND, self::IS_LINK]
|
||||
];
|
||||
|
||||
/**
|
||||
* Installs a given package to a given destination.
|
||||
*
|
||||
* @param string $package The local path to the ZIP package
|
||||
* @param string $destination The local path to the Grav Instance
|
||||
* @param array $options Options to use for installing. ie, ['install_path' => 'user/themes/antimatter']
|
||||
*
|
||||
* @return boolean True if everything went fine, False otherwise.
|
||||
*/
|
||||
public static function install($package, $destination, $options = [])
|
||||
{
|
||||
$destination = rtrim($destination, DS);
|
||||
$options = array_merge(self::$options, $options);
|
||||
$install_path = rtrim($destination . DS . ltrim($options['install_path'], DS), DS);
|
||||
|
||||
if (!self::isGravInstance($destination) || !self::isValidDestination($install_path, $options['exclude_checks'])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
self::lastErrorCode() == self::IS_LINK && $options['ignore_symlinks'] ||
|
||||
self::lastErrorCode() == self::EXISTS && !$options['overwrite']
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$zip = new \ZipArchive();
|
||||
$archive = $zip->open($package);
|
||||
$tmp = CACHE_DIR . DS . 'tmp/Grav-' . uniqid();
|
||||
|
||||
if ($archive !== true) {
|
||||
self::$error = self::ZIP_OPEN_ERROR;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Folder::mkdir($tmp);
|
||||
|
||||
$unzip = $zip->extractTo($tmp);
|
||||
|
||||
if (!$unzip) {
|
||||
self::$error = self::ZIP_EXTRACT_ERROR;
|
||||
|
||||
$zip->close();
|
||||
Folder::delete($tmp);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!$options['sophisticated']) {
|
||||
self::nonSophisticatedInstall($zip, $install_path, $tmp);
|
||||
} else {
|
||||
self::sophisticatedInstall($zip, $install_path, $tmp);
|
||||
}
|
||||
|
||||
Folder::delete($tmp);
|
||||
$zip->close();
|
||||
|
||||
self::$error = self::OK;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public static function nonSophisticatedInstall($zip, $install_path, $tmp)
|
||||
{
|
||||
$container = $zip->getNameIndex(0); // TODO: better way of determining if zip has container folder
|
||||
if (file_exists($install_path)) {
|
||||
Folder::delete($install_path);
|
||||
}
|
||||
|
||||
Folder::move($tmp . DS . $container, $install_path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function sophisticatedInstall($zip, $install_path, $tmp)
|
||||
{
|
||||
for ($i = 0, $l = $zip->numFiles; $i < $l; $i++) {
|
||||
$filename = $zip->getNameIndex($i);
|
||||
$fileinfo = pathinfo($filename);
|
||||
$depth = count(explode(DS, rtrim($filename, '/')));
|
||||
|
||||
if ($depth > 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$path = $install_path . DS . $fileinfo['basename'];
|
||||
|
||||
if (is_link($path)) {
|
||||
continue;
|
||||
} else {
|
||||
if (is_dir($path)) {
|
||||
Folder::delete($path);
|
||||
Folder::move($tmp . DS . $filename, $path);
|
||||
|
||||
if ($fileinfo['basename'] == 'bin') {
|
||||
foreach (glob($path . DS . '*') as $file) {
|
||||
@chmod($file, 0755);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@unlink($path);
|
||||
@copy($tmp . DS . $filename, $path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a set of checks on the destination and sets the Error if any
|
||||
*
|
||||
* @param string $destination The directory to run validations at
|
||||
* @param array $exclude An array of constants to exclude from the validation
|
||||
*
|
||||
* @return boolean True if validation passed. False otherwise
|
||||
*/
|
||||
public static function isValidDestination($destination, $exclude = [])
|
||||
{
|
||||
self::$error = 0;
|
||||
self::$target = $destination;
|
||||
|
||||
if (is_link($destination)) {
|
||||
self::$error = self::IS_LINK;
|
||||
} elseif (file_exists($destination)) {
|
||||
self::$error = self::EXISTS;
|
||||
} elseif (!file_exists($destination)) {
|
||||
self::$error = self::NOT_FOUND;
|
||||
} elseif (!is_dir($destination)) {
|
||||
self::$error = self::NOT_DIRECTORY;
|
||||
}
|
||||
|
||||
if (count($exclude) && in_array(self::$error, $exclude)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !(self::$error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the given path is a Grav Instance
|
||||
*
|
||||
* @param string $target The local path to the Grav Instance
|
||||
*
|
||||
* @return boolean True if is a Grav Instance. False otherwise
|
||||
*/
|
||||
public static function isGravInstance($target)
|
||||
{
|
||||
self::$error = 0;
|
||||
self::$target = $target;
|
||||
|
||||
if (
|
||||
!file_exists($target . DS . 'index.php') ||
|
||||
!file_exists($target . DS . 'bin') ||
|
||||
!file_exists($target . DS . 'user') ||
|
||||
!file_exists($target . DS . 'system' . DS . 'config' . DS . 'system.yaml')
|
||||
) {
|
||||
self::$error = self::NOT_GRAV_ROOT;
|
||||
}
|
||||
|
||||
return !self::$error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last error occurred in a string message format
|
||||
* @return string The message of the last error
|
||||
*/
|
||||
public static function lastErrorMsg()
|
||||
{
|
||||
$msg = 'Unknown Error';
|
||||
|
||||
switch (self::$error) {
|
||||
case 0:
|
||||
$msg = 'No Error';
|
||||
break;
|
||||
|
||||
case self::EXISTS:
|
||||
$msg = 'The target path "' . self::$target . '" already exists';
|
||||
break;
|
||||
|
||||
case self::IS_LINK:
|
||||
$msg = 'The target path "' . self::$target . '" is a symbolic link';
|
||||
break;
|
||||
|
||||
case self::NOT_FOUND:
|
||||
$msg = 'The target path "' . self::$target . '" does not appear to exist';
|
||||
break;
|
||||
|
||||
case self::NOT_DIRECTORY:
|
||||
$msg = 'The target path "' . self::$target . '" does not appear to be a folder';
|
||||
break;
|
||||
|
||||
case self::NOT_GRAV_ROOT:
|
||||
$msg = 'The target path "' . self::$target . '" does not appear to be a Grav instance';
|
||||
break;
|
||||
|
||||
case self::ZIP_OPEN_ERROR:
|
||||
$msg = 'Unable to open the package file';
|
||||
break;
|
||||
|
||||
case self::ZIP_EXTRACT_ERROR:
|
||||
$msg = 'An error occurred while extracting the package';
|
||||
break;
|
||||
|
||||
default:
|
||||
return 'Unknown error';
|
||||
break;
|
||||
}
|
||||
|
||||
return $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last error code of the occurred error
|
||||
* @return integer The code of the last error
|
||||
*/
|
||||
public static function lastErrorCode()
|
||||
{
|
||||
return self::$error;
|
||||
}
|
||||
}
|
||||
32
system/src/Grav/Common/GPM/Local/Collection.php
Normal file
32
system/src/Grav/Common/GPM/Local/Collection.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Iterator;
|
||||
|
||||
class Collection extends Iterator
|
||||
{
|
||||
use GravTrait;
|
||||
|
||||
public function toJson()
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $theme) {
|
||||
$items[$name] = $theme->toArray();
|
||||
}
|
||||
|
||||
return json_encode($items);
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $theme) {
|
||||
$items[$name] = $theme->toArray();
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
87
system/src/Grav/Common/GPM/Local/Package.php
Normal file
87
system/src/Grav/Common/GPM/Local/Package.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
use Grav\Common\Data\Data;
|
||||
|
||||
/**
|
||||
* Class Package
|
||||
* @package Grav\Common\GPM\Local
|
||||
*/
|
||||
class Package
|
||||
{
|
||||
/**
|
||||
* @var Data
|
||||
*/
|
||||
protected $data;
|
||||
/**
|
||||
* @var \Grav\Common\Data\Blueprint
|
||||
*/
|
||||
protected $blueprints;
|
||||
|
||||
/**
|
||||
* @param Data $package
|
||||
* @param bool $package_type
|
||||
*/
|
||||
public function __construct(Data $package, $package_type = false)
|
||||
{
|
||||
$this->data = $package;
|
||||
$this->blueprints = $this->data->blueprints();
|
||||
|
||||
if ($package_type) {
|
||||
$html_description = \Parsedown::instance()->line($this->blueprints->get('description'));
|
||||
$this->blueprints->set('package_type', $package_type);
|
||||
$this->blueprints->set('description_html', $html_description);
|
||||
$this->blueprints->set('description_plain', strip_tags($html_description));
|
||||
$this->blueprints->set('symlink', is_link(USER_DIR . $package_type . DS . $this->blueprints->name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function isEnabled()
|
||||
{
|
||||
return $this->data['enabled'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Data
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
return $this->blueprints->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->toJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function toJson()
|
||||
{
|
||||
return $this->blueprints->toJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->blueprints->toArray();
|
||||
}
|
||||
}
|
||||
28
system/src/Grav/Common/GPM/Local/Packages.php
Normal file
28
system/src/Grav/Common/GPM/Local/Packages.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
use Grav\Common\Iterator;
|
||||
|
||||
class Packages extends Iterator
|
||||
{
|
||||
private $plugins;
|
||||
private $themes;
|
||||
protected static $cache;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// local cache to speed things up
|
||||
if (!isset(self::$cache[__METHOD__])) {
|
||||
self::$cache[__METHOD__] = [
|
||||
'plugins' => new Plugins(),
|
||||
'themes' => new Themes()
|
||||
];
|
||||
}
|
||||
|
||||
$this->plugins = self::$cache[__METHOD__]['plugins'];
|
||||
$this->themes = self::$cache[__METHOD__]['themes'];
|
||||
|
||||
$this->append(['plugins' => $this->plugins]);
|
||||
$this->append(['themes' => $this->themes]);
|
||||
}
|
||||
}
|
||||
26
system/src/Grav/Common/GPM/Local/Plugins.php
Normal file
26
system/src/Grav/Common/GPM/Local/Plugins.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
/**
|
||||
* Class Plugins
|
||||
* @package Grav\Common\GPM\Local
|
||||
*/
|
||||
class Plugins extends Collection
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $type = 'plugins';
|
||||
|
||||
/**
|
||||
* Local Plugins Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$grav = self::$grav;
|
||||
|
||||
foreach ($grav['plugins']->all() as $name => $data) {
|
||||
$this->items[$name] = new Package($data, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
system/src/Grav/Common/GPM/Local/Themes.php
Normal file
15
system/src/Grav/Common/GPM/Local/Themes.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
class Themes extends Collection
|
||||
{
|
||||
private $type = 'themes';
|
||||
public function __construct()
|
||||
{
|
||||
$grav = self::$grav;
|
||||
|
||||
foreach ($grav['themes']->all() as $name => $data) {
|
||||
$this->items[$name] = new Package($data, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
70
system/src/Grav/Common/GPM/Remote/Collection.php
Normal file
70
system/src/Grav/Common/GPM/Remote/Collection.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
use Grav\Common\GPM\Response;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Iterator;
|
||||
use \Doctrine\Common\Cache\Cache as DoctrineCache;
|
||||
use \Doctrine\Common\Cache\FilesystemCache;
|
||||
|
||||
class Collection extends Iterator {
|
||||
use GravTrait;
|
||||
|
||||
/**
|
||||
* The cached data previously fetched
|
||||
* @var string
|
||||
*/
|
||||
protected $raw;
|
||||
|
||||
/**
|
||||
* The lifetime to store the entry in seconds
|
||||
* @var integer
|
||||
*/
|
||||
private $lifetime = 86400;
|
||||
private $repository;
|
||||
private $cache;
|
||||
|
||||
private $plugins, $themes;
|
||||
|
||||
public function __construct($repository = null) {
|
||||
if ($repository == null) {
|
||||
throw new \RuntimeException("A repository is required for storing the cache");
|
||||
}
|
||||
|
||||
$cache_dir = self::$grav['locator']->findResource('cache://gpm', true, true);
|
||||
$this->cache = new FilesystemCache($cache_dir);
|
||||
|
||||
$this->repository = $repository;
|
||||
$this->raw = $this->cache->fetch(md5($this->repository));
|
||||
}
|
||||
|
||||
public function toJson() {
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $theme) {
|
||||
$items[$name] = $theme->toArray();
|
||||
}
|
||||
|
||||
return json_encode($items);
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $theme) {
|
||||
$items[$name] = $theme->toArray();
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function fetch($refresh = false, $callback = null) {
|
||||
if (!$this->raw || $refresh) {
|
||||
$response = Response::get($this->repository, [], $callback);
|
||||
$this->raw = $response;
|
||||
$this->cache->save(md5($this->repository), $this->raw, $this->lifetime);
|
||||
}
|
||||
|
||||
return $this->raw;
|
||||
}
|
||||
}
|
||||
86
system/src/Grav/Common/GPM/Remote/Grav.php
Normal file
86
system/src/Grav/Common/GPM/Remote/Grav.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
class Grav extends Collection
|
||||
{
|
||||
private $repository = 'http://getgrav.org/downloads/grav.json';
|
||||
private $data;
|
||||
|
||||
private $version;
|
||||
private $date;
|
||||
|
||||
/**
|
||||
* @param bool $refresh
|
||||
* @param null $callback
|
||||
*/
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
parent::__construct($this->repository);
|
||||
|
||||
$this->fetch($refresh, $callback);
|
||||
$this->data = json_decode($this->raw);
|
||||
|
||||
$this->version = @$this->data->version ?: '-';
|
||||
$this->date = @$this->data->date ?: '-';
|
||||
|
||||
foreach ($this->data->assets as $slug => $data) {
|
||||
$this->items[$slug] = new Package($data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of assets associated to the latest version of Grav
|
||||
* @return array list of assets
|
||||
*/
|
||||
public function getAssets()
|
||||
{
|
||||
return $this->data->assets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the changelog list for each version of Grav
|
||||
* @param string $diff the version number to start the diff from
|
||||
*
|
||||
* @return array changelog list for each version
|
||||
*/
|
||||
public function getChangelog($diff = null)
|
||||
{
|
||||
if (!$diff) {
|
||||
return $this->data->changelog;
|
||||
}
|
||||
|
||||
$diffLog = [];
|
||||
foreach ($this->data->changelog as $version => $changelog) {
|
||||
preg_match("/[\d\.]+/", $version, $cleanVersion);
|
||||
|
||||
if (!$cleanVersion || version_compare($diff, $cleanVersion[0], ">=")) { continue; }
|
||||
|
||||
$diffLog[$version] = $changelog;
|
||||
}
|
||||
|
||||
return $diffLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest version of Grav available remotely
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the release date of the latest Grav
|
||||
* @return string
|
||||
*/
|
||||
public function getDate()
|
||||
{
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
public function isUpdatable()
|
||||
{
|
||||
return version_compare(GRAV_VERSION, $this->getVersion(), '<');
|
||||
}
|
||||
}
|
||||
32
system/src/Grav/Common/GPM/Remote/Package.php
Normal file
32
system/src/Grav/Common/GPM/Remote/Package.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
class Package {
|
||||
public function __construct($package, $package_type = false) {
|
||||
$this->data = $package;
|
||||
if ($package_type) {
|
||||
$this->data->package_type = $package_type;
|
||||
}
|
||||
}
|
||||
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function __get($key) {
|
||||
return $this->data->$key;
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
return $this->toJson();
|
||||
}
|
||||
|
||||
public function toJson() {
|
||||
return json_encode($this->data);
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
}
|
||||
28
system/src/Grav/Common/GPM/Remote/Packages.php
Normal file
28
system/src/Grav/Common/GPM/Remote/Packages.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
use Grav\Common\Iterator;
|
||||
|
||||
class Packages extends Iterator
|
||||
{
|
||||
private $plugins;
|
||||
private $themes;
|
||||
protected static $cache;
|
||||
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
// local cache to speed things up
|
||||
if (!isset(self::$cache[__METHOD__])) {
|
||||
self::$cache[__METHOD__] = [
|
||||
'plugins' => new Plugins($refresh, $callback),
|
||||
'themes' => new Themes($refresh, $callback)
|
||||
];
|
||||
}
|
||||
|
||||
$this->plugins = self::$cache[__METHOD__]['plugins']->toArray();
|
||||
$this->themes = self::$cache[__METHOD__]['themes']->toArray();
|
||||
|
||||
$this->append(['plugins' => $this->plugins]);
|
||||
$this->append(['themes' => $this->themes]);
|
||||
}
|
||||
}
|
||||
21
system/src/Grav/Common/GPM/Remote/Plugins.php
Normal file
21
system/src/Grav/Common/GPM/Remote/Plugins.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
class Plugins extends Collection
|
||||
{
|
||||
private $repository = 'http://getgrav.org/downloads/plugins.json';
|
||||
private $type = 'plugins';
|
||||
private $data;
|
||||
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
parent::__construct($this->repository);
|
||||
|
||||
$this->fetch($refresh, $callback);
|
||||
$this->data = json_decode($this->raw);
|
||||
|
||||
foreach ($this->data as $slug => $data) {
|
||||
$this->items[$slug] = new Package($data, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
system/src/Grav/Common/GPM/Remote/Themes.php
Normal file
21
system/src/Grav/Common/GPM/Remote/Themes.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
class Themes extends Collection
|
||||
{
|
||||
private $repository = 'http://getgrav.org/downloads/themes.json';
|
||||
private $type = 'themes';
|
||||
private $data;
|
||||
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
parent::__construct($this->repository);
|
||||
|
||||
$this->fetch($refresh, $callback);
|
||||
$this->data = json_decode($this->raw);
|
||||
|
||||
foreach ($this->data as $slug => $data) {
|
||||
$this->items[$slug] = new Package($data, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
220
system/src/Grav/Common/GPM/Response.php
Normal file
220
system/src/Grav/Common/GPM/Response.php
Normal file
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
class Response
|
||||
{
|
||||
/**
|
||||
* The callback for the progress
|
||||
* @var callable Either a function or callback in array notation
|
||||
*/
|
||||
public static $callback = null;
|
||||
|
||||
/**
|
||||
* Which method to use for HTTP calls, can be 'curl', 'fopen' or 'auto'. Auto is default and fopen is the preferred method
|
||||
* @var string
|
||||
*/
|
||||
private static $method = 'auto';
|
||||
|
||||
/**
|
||||
* Default parameters for `curl` and `fopen`
|
||||
* @var array
|
||||
*/
|
||||
private static $defaults = [
|
||||
|
||||
'curl' => [
|
||||
CURLOPT_REFERER => 'Grav GPM',
|
||||
CURLOPT_USERAGENT => 'Grav GPM',
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_HEADER => false,
|
||||
/**
|
||||
* Example of callback parameters from within your own class
|
||||
*/
|
||||
//CURLOPT_NOPROGRESS => false,
|
||||
//CURLOPT_PROGRESSFUNCTION => [$this, 'progress']
|
||||
],
|
||||
'fopen' => [
|
||||
'method' => 'GET',
|
||||
'user_agent' => 'Grav GPM',
|
||||
'max_redirects' => 5,
|
||||
'follow_location' => 1,
|
||||
'timeout' => 15,
|
||||
/**
|
||||
* Example of callback parameters from within your own class
|
||||
*/
|
||||
//'notification' => [$this, 'progress']
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* Sets the preferred method to use for making HTTP calls.
|
||||
* @param string $method Default is `auto`
|
||||
*/
|
||||
public static function setMethod($method = 'auto')
|
||||
{
|
||||
if (!in_array($method, ['auto', 'curl', 'fopen'])) {
|
||||
$method = 'auto';
|
||||
}
|
||||
|
||||
self::$method = $method;
|
||||
|
||||
return new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a request to the URL by using the preferred method
|
||||
* @param string $uri URL to call
|
||||
* @param array $options An array of parameters for both `curl` and `fopen`
|
||||
* @return string The response of the request
|
||||
*/
|
||||
public static function get($uri = '', $options = [], $callback = null)
|
||||
{
|
||||
if (!self::isCurlAvailable() && !self::isFopenAvailable()) {
|
||||
throw new \RuntimeException('Could not start an HTTP request. `allow_url_open` is disabled and `cURL` is not available');
|
||||
}
|
||||
|
||||
$options = array_replace_recursive(self::$defaults, $options);
|
||||
$method = 'get' . ucfirst(strtolower(self::$method));
|
||||
|
||||
self::$callback = $callback;
|
||||
|
||||
return static::$method($uri, $options, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Progress normalized for cURL and Fopen
|
||||
* @param args Variable length of arguments passed in by stream method
|
||||
* @return array Normalized array with useful data.
|
||||
* Format: ['code' => int|false, 'filesize' => bytes, 'transferred' => bytes, 'percent' => int]
|
||||
*/
|
||||
public static function progress()
|
||||
{
|
||||
static $filesize = null;
|
||||
|
||||
$args = func_get_args();
|
||||
$isCurlResource = is_resource($args[0]) && get_resource_type($args[0]) == 'curl';
|
||||
|
||||
$notification_code = !$isCurlResource ? $args[0] : false;
|
||||
$bytes_transferred = $isCurlResource ? $args[2] : $args[4];
|
||||
|
||||
if ($isCurlResource) {
|
||||
$filesize = $args[1];
|
||||
} elseif ($notification_code == STREAM_NOTIFY_FILE_SIZE_IS) {
|
||||
$filesize = $args[5];
|
||||
}
|
||||
|
||||
if ($bytes_transferred > 0) {
|
||||
if ($notification_code == STREAM_NOTIFY_PROGRESS|STREAM_NOTIFY_COMPLETED || $isCurlResource) {
|
||||
|
||||
$progress = [
|
||||
'code' => $notification_code,
|
||||
'filesize' => $filesize,
|
||||
'transferred' => $bytes_transferred,
|
||||
'percent' => $filesize <= 0 ? '-' : round(($bytes_transferred * 100) / $filesize, 1)
|
||||
];
|
||||
|
||||
if (self::$callback !== null) {
|
||||
call_user_func_array(self::$callback, [$progress]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if cURL is available
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isCurlAvailable()
|
||||
{
|
||||
return function_exists('curl_version');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the remote fopen request is enabled in PHP
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isFopenAvailable()
|
||||
{
|
||||
return preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically picks the preferred method
|
||||
* @return string The response of the request
|
||||
*/
|
||||
private static function getAuto()
|
||||
{
|
||||
if (self::isFopenAvailable()) {
|
||||
return self::getFopen(func_get_args());
|
||||
}
|
||||
|
||||
if (self::isCurlAvailable()) {
|
||||
return self::getCurl(func_get_args());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a HTTP request via cURL
|
||||
* @return string The response of the request
|
||||
*/
|
||||
private static function getCurl()
|
||||
{
|
||||
$args = func_get_args();
|
||||
$uri = $args[0];
|
||||
$options = $args[1];
|
||||
$callback = $args[2];
|
||||
|
||||
$ch = curl_init($uri);
|
||||
curl_setopt_array($ch, $options['curl']);
|
||||
|
||||
if ($callback) {
|
||||
curl_setopt_array(
|
||||
$ch,
|
||||
[
|
||||
CURLOPT_NOPROGRESS => false,
|
||||
CURLOPT_PROGRESSFUNCTION => ['self', 'progress']
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if ($errno = curl_errno($ch)) {
|
||||
$error_message = curl_strerror($errno);
|
||||
throw new \RuntimeException("cURL error ({$errno}):\n {$error_message}");
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a HTTP request via fopen
|
||||
* @return string The response of the request
|
||||
*/
|
||||
private static function getFopen()
|
||||
{
|
||||
if (count($args = func_get_args()) == 1) {
|
||||
$args = $args[0];
|
||||
}
|
||||
|
||||
$uri = $args[0];
|
||||
$options = $args[1];
|
||||
$callback = $args[2];
|
||||
|
||||
if ($callback) {
|
||||
$options['fopen']['notification'] = ['self', 'progress'];
|
||||
}
|
||||
|
||||
$stream = stream_context_create(['http' => $options['fopen']], $options['fopen']);
|
||||
$content = @file_get_contents($uri, false, $stream);
|
||||
|
||||
if ($content === false) {
|
||||
throw new \RuntimeException("Error while trying to download '$uri'");
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
86
system/src/Grav/Common/GPM/Upgrader.php
Normal file
86
system/src/Grav/Common/GPM/Upgrader.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\GPM\Installer;
|
||||
|
||||
class Upgrader
|
||||
{
|
||||
/**
|
||||
* Remote details about latest Grav version
|
||||
* @var Packages
|
||||
*/
|
||||
private $remote;
|
||||
|
||||
/**
|
||||
* Internal cache
|
||||
* @var Iterator
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* Creates a new GPM instance with Local and Remote packages available
|
||||
* @param boolean $refresh Applies to Remote Packages only and forces a refetch of data
|
||||
* @param callable $callback Either a function or callback in array notation
|
||||
*/
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
$this->remote = new Remote\Grav($refresh, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the release date of the latest version of Grav
|
||||
* @return string
|
||||
*/
|
||||
public function getReleaseDate()
|
||||
{
|
||||
return $this->remote->getDate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the installed Grav
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalVersion()
|
||||
{
|
||||
return GRAV_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the remotely available Grav
|
||||
* @return string
|
||||
*/
|
||||
public function getRemoteVersion()
|
||||
{
|
||||
return $this->remote->getVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of assets available to download remotely
|
||||
* @return array
|
||||
*/
|
||||
public function getAssets()
|
||||
{
|
||||
return $this->remote->getAssets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the changelog list for each version of Grav
|
||||
* @param string $diff the version number to start the diff from
|
||||
*
|
||||
* @return array return the chagenlog list for each version
|
||||
*/
|
||||
public function getChangelog($diff = null)
|
||||
{
|
||||
return $this->remote->getChangelog($diff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the currently installed Grav is upgradable to a newer version
|
||||
* @return boolean True if it's upgradable, False otherwise.
|
||||
*/
|
||||
public function isUpgradable()
|
||||
{
|
||||
return version_compare($this->getLocalVersion(), $this->getRemoteVersion(), "<");
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,15 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Page\Pages;
|
||||
use Grav\Common\Service\ConfigServiceProvider;
|
||||
use Grav\Common\Service\ErrorServiceProvider;
|
||||
use Grav\Common\Service\LoggerServiceProvider;
|
||||
use Grav\Common\Service\StreamsServiceProvider;
|
||||
use Grav\Component\DI\Container;
|
||||
use Grav\Component\EventDispatcher\Event;
|
||||
use Grav\Component\EventDispatcher\EventDispatcher;
|
||||
use RocketTheme\Toolbox\DI\Container;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use Grav\Common\Page\Medium;
|
||||
|
||||
/**
|
||||
* Grav
|
||||
@@ -12,9 +17,7 @@ use Grav\Component\EventDispatcher\EventDispatcher;
|
||||
* @author Andy Miller
|
||||
* @link http://www.rockettheme.com
|
||||
* @license http://opensource.org/licenses/MIT
|
||||
* @version 0.8.0
|
||||
*
|
||||
* Originally based on Pico by Gilbert Pellegrom - http://pico.dev7studios.com
|
||||
* Influenced by Pico, Stacey, Kirby, PieCrust and other great platforms...
|
||||
*/
|
||||
class Grav extends Container
|
||||
@@ -22,7 +25,7 @@ class Grav extends Container
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $output;
|
||||
public $output;
|
||||
|
||||
/**
|
||||
* @var static
|
||||
@@ -50,18 +53,25 @@ class Grav extends Container
|
||||
{
|
||||
$container = new static($values);
|
||||
|
||||
$container['config_path'] = CACHE_DIR . 'config.php';
|
||||
|
||||
$container['grav'] = $container;
|
||||
|
||||
$container['events'] = function ($c) {
|
||||
return new EventDispatcher;
|
||||
};
|
||||
$container['debugger'] = new Debugger();
|
||||
$container['debugger']->startTimer('_init', 'Initialize');
|
||||
|
||||
$container->register(new LoggerServiceProvider);
|
||||
|
||||
$container->register(new ErrorServiceProvider);
|
||||
|
||||
$container['uri'] = function ($c) {
|
||||
return new Uri($c);
|
||||
};
|
||||
$container['config'] = function ($c) {
|
||||
return Config::instance($c);
|
||||
|
||||
$container['task'] = function ($c) {
|
||||
return !empty($_POST['task']) ? $_POST['task'] : $c['uri']->param('task');
|
||||
};
|
||||
|
||||
$container['events'] = function ($c) {
|
||||
return new EventDispatcher;
|
||||
};
|
||||
$container['cache'] = function ($c) {
|
||||
return new Cache($c);
|
||||
@@ -85,9 +95,36 @@ class Grav extends Container
|
||||
return new Assets();
|
||||
};
|
||||
$container['page'] = function ($c) {
|
||||
$page = $c['pages']->dispatch($c['uri']->route());
|
||||
/** @var Pages $pages */
|
||||
$pages = $c['pages'];
|
||||
$page = $pages->dispatch($c['uri']->route());
|
||||
|
||||
if (!$page || !$page->routable()) {
|
||||
|
||||
// special case where a media file is requested
|
||||
if (!$page) {
|
||||
$path_parts = pathinfo($c['uri']->route());
|
||||
$page = $c['pages']->dispatch($path_parts['dirname']);
|
||||
if ($page) {
|
||||
$media = $page->media()->all();
|
||||
$media_file = urldecode($path_parts['basename']);
|
||||
if (isset($media[$media_file])) {
|
||||
$medium = $media[$media_file];
|
||||
|
||||
// loop through actions for the image and call them
|
||||
foreach ($c['uri']->query(null, true) as $action => $params) {
|
||||
if (in_array($action, Medium::$valid_actions)) {
|
||||
call_user_func_array(array(&$medium, $action), explode(',', $params));
|
||||
}
|
||||
}
|
||||
header('Content-type: '. $mime = $medium->get('mime'));
|
||||
echo file_get_contents($medium->path());
|
||||
die;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no page found, fire event
|
||||
$event = $c->fireEvent('onPageNotFound');
|
||||
|
||||
if (isset($event->page)) {
|
||||
@@ -105,7 +142,20 @@ class Grav extends Container
|
||||
return new Browser();
|
||||
};
|
||||
|
||||
$container['base_url_absolute'] = function ($c) {
|
||||
return $c['config']->get('system.base_url_absolute') ?: $c['uri']->rootUrl(true);
|
||||
};
|
||||
$container['base_url_relative'] = function ($c) {
|
||||
return $c['config']->get('system.base_url_relative') ?: $c['uri']->rootUrl(false);
|
||||
};
|
||||
$container['base_url'] = function ($c) {
|
||||
return $c['config']->get('system.absolute_urls') ? $c['base_url_absolute'] : $c['base_url_relative'];
|
||||
};
|
||||
|
||||
$container->register(new StreamsServiceProvider);
|
||||
$container->register(new ConfigServiceProvider);
|
||||
|
||||
$container['debugger']->stopTimer('_init');
|
||||
|
||||
return $container;
|
||||
}
|
||||
@@ -115,38 +165,66 @@ class Grav extends Container
|
||||
// Use output buffering to prevent headers from being sent too early.
|
||||
ob_start();
|
||||
|
||||
// Initialize stream wrappers.
|
||||
$this['locator'];
|
||||
/** @var Debugger $debugger */
|
||||
$debugger = $this['debugger'];
|
||||
|
||||
// Initialize configuration.
|
||||
$debugger->startTimer('_config', 'Configuration');
|
||||
$this['config']->init();
|
||||
$this['errors']->resetHandlers();
|
||||
$debugger->init();
|
||||
$this['config']->debug();
|
||||
$debugger->stopTimer('_config');
|
||||
|
||||
$debugger->startTimer('streams', 'Streams');
|
||||
$this['streams'];
|
||||
$debugger->stopTimer('streams');
|
||||
|
||||
$debugger->startTimer('plugins', 'Plugins');
|
||||
$this['plugins']->init();
|
||||
|
||||
$this->fireEvent('onPluginsInitialized');
|
||||
$debugger->stopTimer('plugins');
|
||||
|
||||
$debugger->startTimer('themes', 'Themes');
|
||||
$this['themes']->init();
|
||||
$this->fireEvent('onThemeInitialized');
|
||||
$debugger->stopTimer('themes');
|
||||
|
||||
$task = $this['task'];
|
||||
if ($task) {
|
||||
$this->fireEvent('onTask.' . $task);
|
||||
}
|
||||
|
||||
$this['assets']->init();
|
||||
|
||||
$this->fireEvent('onAssetsInitialized');
|
||||
|
||||
$debugger->startTimer('twig', 'Twig');
|
||||
$this['twig']->init();
|
||||
$this['pages']->init();
|
||||
$debugger->stopTimer('twig');
|
||||
|
||||
$debugger->startTimer('pages', 'Pages');
|
||||
$this['pages']->init();
|
||||
$this->fireEvent('onPagesInitialized');
|
||||
$debugger->stopTimer('pages');
|
||||
|
||||
$this->fireEvent('onPageInitialized');
|
||||
|
||||
// Process whole page as required
|
||||
$this->output = $this['output'];
|
||||
$debugger->addAssets();
|
||||
|
||||
// Process whole page as required
|
||||
$debugger->startTimer('render', 'Render');
|
||||
$this->output = $this['output'];
|
||||
$this->fireEvent('onOutputGenerated');
|
||||
$debugger->stopTimer('render');
|
||||
|
||||
// Set the header type
|
||||
$this->header();
|
||||
|
||||
echo $this->output;
|
||||
|
||||
ob_end_flush();
|
||||
flush();
|
||||
$debugger->render();
|
||||
|
||||
$this->fireEvent('onOutputRendered');
|
||||
|
||||
register_shutdown_function([$this, 'shutdown']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,6 +237,11 @@ class Grav extends Container
|
||||
{
|
||||
/** @var Uri $uri */
|
||||
$uri = $this['uri'];
|
||||
|
||||
if (isset($this['session'])) {
|
||||
$this['session']->close();
|
||||
}
|
||||
|
||||
header("Location: " . rtrim($uri->rootUrl(), '/') .'/'. trim($route, '/'), true, $code);
|
||||
exit();
|
||||
}
|
||||
@@ -191,9 +274,19 @@ class Grav extends Container
|
||||
*/
|
||||
public function header()
|
||||
{
|
||||
/** @var Uri $uri */
|
||||
$uri = $this['uri'];
|
||||
header('Content-type: ' . $this->mime($uri->extension()));
|
||||
$extension = $this['uri']->extension();
|
||||
header('Content-type: ' . $this->mime($extension));
|
||||
|
||||
// Set debugger data in headers
|
||||
if (!($extension == null || $extension == 'html')) {
|
||||
$this['debugger']->enabled(false);
|
||||
// $this['debugger']->sendDataInHeaders();
|
||||
}
|
||||
|
||||
// Set HTTP response code
|
||||
if (isset($this['page']->header()->http_response_code)) {
|
||||
http_response_code($this['page']->header()->http_response_code);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,4 +302,31 @@ class Grav extends Container
|
||||
$events = $this['events'];
|
||||
return $events->dispatch($eventName, $event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the final content length for the page and flush the buffer
|
||||
*
|
||||
*/
|
||||
public function shutdown()
|
||||
{
|
||||
if ($this['config']->get('system.debugger.shutdown.close_connection')) {
|
||||
|
||||
if (function_exists('ignore_user_abort')) {
|
||||
@ignore_user_abort(true);
|
||||
}
|
||||
|
||||
if (isset($this['session'])) {
|
||||
$this['session']->close();
|
||||
}
|
||||
|
||||
header('Content-length: ' . ob_get_length());
|
||||
header("Connection: close\r\n");
|
||||
|
||||
ob_end_flush();
|
||||
ob_flush();
|
||||
flush();
|
||||
}
|
||||
|
||||
$this->fireEvent('onShutdown');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,9 +90,8 @@ class Inflector
|
||||
/**
|
||||
* Singularizes English nouns.
|
||||
*
|
||||
* @access static public
|
||||
* @static
|
||||
* @param string $word English noun to singularize
|
||||
* @param int $count
|
||||
* @return string Singular noun.
|
||||
*/
|
||||
public static function singularize($word, $count = 1)
|
||||
@@ -353,10 +352,10 @@ class Inflector
|
||||
|
||||
public static function monthize($days)
|
||||
{
|
||||
$now = new JDate();
|
||||
$end = new JDate();
|
||||
$now = new \DateTime();
|
||||
$end = new \DateTime();
|
||||
|
||||
$duration = new DateInterval("P{$days}D");
|
||||
$duration = new \DateInterval("P{$days}D");
|
||||
|
||||
$diff = $end->add($duration)->diff($now);
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use RocketTheme\Toolbox\ArrayTraits\ArrayAccessWithGetters;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Iterator as ArrayIterator;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Constructor;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Countable;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Serializable;
|
||||
|
||||
/**
|
||||
* Class Iterator
|
||||
@@ -9,25 +14,12 @@ use Symfony\Component\Yaml\Yaml;
|
||||
*/
|
||||
class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
|
||||
{
|
||||
use Constructor, ArrayAccessWithGetters, ArrayIterator, Countable, Serializable, Export;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $items = array();
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $unset = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $items Initial items inside the iterator.
|
||||
*/
|
||||
public function __construct(array $items = array())
|
||||
{
|
||||
$this->items = $items;
|
||||
}
|
||||
protected $items = [];
|
||||
|
||||
/**
|
||||
* Convert function calls for the existing keys into their values.
|
||||
@@ -41,49 +33,6 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
|
||||
return (isset($this->items[$key])) ? $this->items[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Array getter shorthand to get items.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
return (isset($this->items[$key])) ? $this->items[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Array setter shorthand to set the value.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __set($key, $value)
|
||||
{
|
||||
$this->items[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Array isset shorthand to set the value.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($key)
|
||||
{
|
||||
return isset($this->items[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Array unset shorthand to remove the key.
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
public function __unset($key)
|
||||
{
|
||||
$this->offsetUnset($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone the iterator.
|
||||
*/
|
||||
@@ -164,7 +113,7 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
|
||||
shuffle($keys);
|
||||
|
||||
$new = array();
|
||||
foreach($keys as $key) {
|
||||
foreach ($keys as $key) {
|
||||
$new[$key] = $this->items[$key];
|
||||
}
|
||||
|
||||
@@ -215,180 +164,4 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Implements export functions to array, YAML and JSON.
|
||||
|
||||
/**
|
||||
* Return items as an array.
|
||||
*
|
||||
* @return array Array presentation of the iterator.
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return YAML encoded string of items.
|
||||
*
|
||||
* @return string YAML presentation of the iterator.
|
||||
*/
|
||||
public function toYaml()
|
||||
{
|
||||
return Yaml::dump($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return JSON encoded string of items.
|
||||
*
|
||||
* @return string JSON presentation of the iterator.
|
||||
*/
|
||||
public function toJson()
|
||||
{
|
||||
return json_encode($this->items);
|
||||
}
|
||||
|
||||
// Implements Iterator.
|
||||
|
||||
/**
|
||||
* Returns the current element.
|
||||
*
|
||||
* @return mixed Can return any type.
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return current($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key of the current element.
|
||||
*
|
||||
* @return mixed Returns scalar on success, or NULL on failure.
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return key($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the current position to the next element.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
if ($this->unset) {
|
||||
// If current item was unset, position is already in the next element (do nothing).
|
||||
$this->unset = false;
|
||||
} else {
|
||||
next($this->items);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewinds back to the first element of the Iterator.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->unset = false;
|
||||
reset($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called after Iterator::rewind() and Iterator::next() to check if the current position is valid.
|
||||
*
|
||||
* @return bool Returns TRUE on success or FALSE on failure.
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return key($this->items) !== null;
|
||||
}
|
||||
|
||||
// Implements ArrayAccess
|
||||
|
||||
/**
|
||||
* Whether or not an offset exists.
|
||||
*
|
||||
* @param mixed $offset An offset to check for.
|
||||
* @return bool Returns TRUE on success or FALSE on failure.
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->items[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at specified offset.
|
||||
*
|
||||
* @param mixed $offset The offset to retrieve.
|
||||
* @return mixed Can return all value types.
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return isset($this->items[$offset]) ? $this->items[$offset] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a value to the specified offset.
|
||||
*
|
||||
* @param mixed $offset The offset to assign the value to.
|
||||
* @param mixed $value The value to set.
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if (is_null($offset)) {
|
||||
$this->items[] = $value;
|
||||
} else {
|
||||
$this->items[$offset] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets an offset.
|
||||
*
|
||||
* @param mixed $offset The offset to unset.
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
if ($offset == key($this->items)) {
|
||||
$this->unset = true;
|
||||
}
|
||||
unset($this->items[$offset]);
|
||||
}
|
||||
|
||||
// Implements Countable
|
||||
|
||||
/**
|
||||
* This method is executed when using the count() function.
|
||||
*
|
||||
* @return int The count of items.
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->items);
|
||||
}
|
||||
|
||||
// Implements Serializable
|
||||
|
||||
/**
|
||||
* Returns string representation of the object.
|
||||
*
|
||||
* @return string Returns the string representation of the object.
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during unserialization of the object.
|
||||
*
|
||||
* @param string $serialized The string representation of the object.
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
$this->items = unserialize($serialized);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@ class Markdown extends \Parsedown
|
||||
{
|
||||
use MarkdownGravLinkTrait;
|
||||
|
||||
function __construct($page)
|
||||
public function __construct($page)
|
||||
{
|
||||
$this->page = $page;
|
||||
$this->BlockTypes['{'] [] = "TwigTag";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@ class MarkdownExtra extends \ParsedownExtra
|
||||
{
|
||||
use MarkdownGravLinkTrait;
|
||||
|
||||
function __construct($page)
|
||||
public function __construct($page)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->page = $page;
|
||||
$this->BlockTypes['{'] [] = "TwigTag";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<?php
|
||||
namespace Grav\Common\Markdown;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Debugger;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Page\Medium;
|
||||
use Grav\Common\Uri;
|
||||
|
||||
/**
|
||||
* A trait to add some custom processing to the identifyLink() method in Parsedown and ParsedownExtra
|
||||
@@ -11,23 +14,39 @@ trait MarkdownGravLinkTrait
|
||||
{
|
||||
use GravTrait;
|
||||
|
||||
/**
|
||||
* Ensure Twig tags are treated as block level items with no <p></p> tags
|
||||
*/
|
||||
protected function identifyTwigTag($Line)
|
||||
{
|
||||
if (preg_match('/[{%|{{|{#].*[#}|}}|%}]/', $Line['body'], $matches)) {
|
||||
$Block = array(
|
||||
'element' => $Line['body'],
|
||||
);
|
||||
return $Block;
|
||||
}
|
||||
}
|
||||
|
||||
protected function identifyLink($Excerpt)
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
|
||||
// Run the parent method to get the actual results
|
||||
$Excerpt = parent::identifyLink($Excerpt);
|
||||
$actions = array();
|
||||
$this->base_url = trim(self::$grav['config']->get('system.base_url_relative'));
|
||||
$this->base_url = self::$grav['base_url'];
|
||||
|
||||
// if this is a link
|
||||
if (isset($Excerpt['element']['attributes']['href'])) {
|
||||
|
||||
$url = parse_url(htmlspecialchars_decode($Excerpt['element']['attributes']['href']));
|
||||
|
||||
// if there is no host set but there is a path, the file is local
|
||||
if (!isset($url['host']) && isset($url['path'])) {
|
||||
// if there is no scheme, the file is local
|
||||
if (!isset($url['scheme'])) {
|
||||
|
||||
// convert the URl is required
|
||||
$Excerpt['element']['attributes']['href'] = $this->convertUrl($url['path']);
|
||||
$Excerpt['element']['attributes']['href'] = $this->convertUrl(Uri::build_url($url));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,8 +76,8 @@ trait MarkdownGravLinkTrait
|
||||
|
||||
// loop through actions for the image and call them
|
||||
foreach ($actions as $action => $params) {
|
||||
// as long as it's not an html, url or ligtbox action
|
||||
if (!in_array($action, ['html','url','lightbox'])) {
|
||||
// as long as it's a valid action
|
||||
if (in_array($action, Medium::$valid_actions)) {
|
||||
call_user_func_array(array(&$medium, $action), explode(',', $params));
|
||||
}
|
||||
}
|
||||
@@ -92,7 +111,7 @@ trait MarkdownGravLinkTrait
|
||||
}
|
||||
} else {
|
||||
// not a current page media file, see if it needs converting to relative
|
||||
$Excerpt['element']['attributes']['src'] = $this->convertUrl($url['path']);
|
||||
$Excerpt['element']['attributes']['src'] = $this->convertUrl(Uri::build_url($url));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,22 +126,18 @@ trait MarkdownGravLinkTrait
|
||||
protected function convertUrl($markdown_url)
|
||||
{
|
||||
// if absolue and starts with a base_url move on
|
||||
if ($this->base_url == '' || strpos($markdown_url, $this->base_url) === 0) {
|
||||
if ($this->base_url != '' && strpos($markdown_url, $this->base_url) === 0) {
|
||||
$new_url = $markdown_url;
|
||||
// if its absolute with /
|
||||
} elseif (strpos($markdown_url, '/') === 0) {
|
||||
$new_url = rtrim($this->base_url, '/') . $markdown_url;
|
||||
} else {
|
||||
$relative_path = rtrim($this->base_url, '/') . $this->page->route();
|
||||
$relative_path = rtrim($this->base_url, '/') . $this->page->route();
|
||||
|
||||
// If this is a 'real' filepath clean it up
|
||||
if (file_exists($this->page->path().'/'.$markdown_url)) {
|
||||
$relative_path = rtrim($this->base_url, '/') .
|
||||
preg_replace('/\/([\d]+.)/', '/',
|
||||
str_replace(PAGES_DIR, '/', $this->page->path()));
|
||||
$markdown_url = preg_replace('/^([\d]+.)/', '',
|
||||
preg_replace('/\/([\d]+.)/', '/',
|
||||
trim(preg_replace('/[^\/]+(\.md$)/', '', $markdown_url), '/')));
|
||||
if (file_exists($this->page->path().'/'.parse_url($markdown_url, PHP_URL_PATH))) {
|
||||
$relative_path = rtrim($this->base_url, '/') . preg_replace('/\/([\d]+.)/', '/', str_replace(PAGES_DIR, '/', $this->page->path()));
|
||||
$markdown_url = preg_replace('/^([\d]+.)/', '', preg_replace('/\/([\d]+.)/', '/', trim(preg_replace('/[^\/]+(\.md$)/', '', $markdown_url), '/')));
|
||||
}
|
||||
|
||||
// else its a relative path already
|
||||
@@ -139,7 +154,7 @@ trait MarkdownGravLinkTrait
|
||||
}
|
||||
|
||||
// build the new url
|
||||
$new_url = $relative_path . '/' . implode('/', $newpath);
|
||||
$new_url = rtrim($relative_path, '/') . '/' . implode('/', $newpath);
|
||||
}
|
||||
|
||||
return $new_url;
|
||||
|
||||
@@ -22,7 +22,8 @@ class Collection extends Iterator
|
||||
*/
|
||||
protected $params;
|
||||
|
||||
public function __construct($items = array(), array $params = array(), Pages $pages = null) {
|
||||
public function __construct($items = array(), array $params = array(), Pages $pages = null)
|
||||
{
|
||||
parent::__construct($items);
|
||||
|
||||
$this->params = $params;
|
||||
@@ -189,7 +190,44 @@ class Collection extends Iterator
|
||||
* @param string $path the path the item
|
||||
* @return Page Item in the array the the current position.
|
||||
*/
|
||||
public function currentPosition($path) {
|
||||
public function currentPosition($path)
|
||||
{
|
||||
return array_search($path, array_keys($this->items));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only visible pages
|
||||
*
|
||||
* @return Collection The collection with only visible pages
|
||||
*/
|
||||
public function visible()
|
||||
{
|
||||
$visible = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page->visible()) {
|
||||
$visible[$path] = $slug;
|
||||
}
|
||||
}
|
||||
return new static($visible, $this->params, $this->pages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only routable pages
|
||||
*
|
||||
* @return Collection The collection with only routable pages
|
||||
*/
|
||||
public function routable()
|
||||
{
|
||||
$routable = [];
|
||||
|
||||
foreach (array_keys($this->items) as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page->routable()) {
|
||||
$routable[$path] = $slug;
|
||||
}
|
||||
}
|
||||
return new static($routable, $this->params, $this->pages);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace Grav\Common\Page;
|
||||
|
||||
use Grav\Common\Getters;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Config;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\GravTrait;
|
||||
|
||||
/**
|
||||
@@ -56,6 +56,9 @@ class Media extends Getters
|
||||
continue;
|
||||
}
|
||||
|
||||
//set file size
|
||||
$medium->set('size', $info->getSize());
|
||||
|
||||
// Assign meta files to the medium.
|
||||
if ($meta) {
|
||||
$medium->addMetaFile($meta);
|
||||
@@ -81,7 +84,7 @@ class Media extends Getters
|
||||
$config = self::$grav['config'];
|
||||
|
||||
// Check if medium type has been configured.
|
||||
$params = $config->get("media.{$ext}");
|
||||
$params = $config->get("media.".strtolower($ext));
|
||||
if (!$params) {
|
||||
return null;
|
||||
}
|
||||
@@ -126,6 +129,7 @@ class Media extends Getters
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
ksort($this->instances, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
return $this->instances;
|
||||
}
|
||||
|
||||
@@ -136,6 +140,7 @@ class Media extends Getters
|
||||
*/
|
||||
public function images()
|
||||
{
|
||||
ksort($this->images, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
return $this->images;
|
||||
}
|
||||
|
||||
@@ -146,6 +151,7 @@ class Media extends Getters
|
||||
*/
|
||||
public function videos()
|
||||
{
|
||||
ksort($this->videos, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
return $this->videos;
|
||||
}
|
||||
|
||||
@@ -156,6 +162,7 @@ class Media extends Getters
|
||||
*/
|
||||
public function files()
|
||||
{
|
||||
ksort($this->files, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
use Grav\Common\Config;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Filesystem\File\Yaml;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Data\Data;
|
||||
use Gregwar\Image\Image as ImageFile;
|
||||
|
||||
/**
|
||||
@@ -48,7 +48,10 @@ class Medium extends Data
|
||||
protected $image;
|
||||
|
||||
protected $type = 'guess';
|
||||
protected $quality = 80;
|
||||
protected $quality = 85;
|
||||
|
||||
public static $valid_actions = ['resize', 'forceResize', 'cropResize', 'crop', 'cropZoom',
|
||||
'negate', 'brightness', 'contrast', 'grayscale', 'emboss', 'smooth', 'sharp', 'edge', 'colorize', 'sepia' ];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
@@ -69,10 +72,15 @@ class Medium extends Data
|
||||
{
|
||||
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') {
|
||||
$filePath = $this->get('path') . '/' . $this->get('filename');
|
||||
$image_info = getimagesize($filePath);
|
||||
$this->set('thumb', $filePath);
|
||||
$image_info = getimagesize($file_path);
|
||||
$this->def('width', $image_info[0]);
|
||||
$this->def('height', $image_info[1]);
|
||||
$this->def('mime', $image_info['mime']);
|
||||
@@ -80,6 +88,8 @@ class Medium extends Data
|
||||
} else {
|
||||
$this->def('mime', 'application/octet-stream');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,6 +102,37 @@ class Medium extends Data
|
||||
return $this->linkImage ? $this->html() : $this->url();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return PATH to file.
|
||||
*
|
||||
* @return string path to file
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
|
||||
if ($this->image) {
|
||||
$output = $this->image->cacheFile($this->type, $this->quality);
|
||||
$this->reset();
|
||||
$output = ROOT_DIR . $output;
|
||||
} else {
|
||||
$output = $this->get('path') . '/' . $this->get('filename');
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@@ -99,9 +140,6 @@ class Medium extends Data
|
||||
*/
|
||||
public function url()
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
|
||||
if ($this->image) {
|
||||
$output = $this->image->cacheFile($this->type, $this->quality);
|
||||
$this->reset();
|
||||
@@ -110,7 +148,7 @@ class Medium extends Data
|
||||
$output = $relPath . '/' . $this->get('filename');
|
||||
}
|
||||
|
||||
return $config->get('system.base_url_relative') . '/'. $output;
|
||||
return self::$grav['base_url'] . '/'. $output;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,6 +156,7 @@ class Medium extends Data
|
||||
*
|
||||
* @param string $type
|
||||
* @param int $quality
|
||||
* @return $this
|
||||
*/
|
||||
public function format($type = null, $quality = 80)
|
||||
{
|
||||
@@ -127,6 +166,7 @@ class Medium extends Data
|
||||
|
||||
$this->type = $type;
|
||||
$this->quality = $quality;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,7 +219,7 @@ class Medium extends Data
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
|
||||
$output = '<a href="' . $config->get('system.base_url_relative') . '/'. $this->linkTarget
|
||||
$output = '<a href="' . self::$grav['base_url'] . '/'. $this->linkTarget
|
||||
. '"' . $this->linkAttributes. ' class="'. $class . '">' . $output . '</a>';
|
||||
|
||||
$this->linkTarget = $this->linkAttributes = null;
|
||||
@@ -204,9 +244,11 @@ class Medium extends Data
|
||||
|
||||
public function lightboxRaw($width = null, $height = null)
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
$url = $this->url();
|
||||
$this->link($width, $height);
|
||||
$lightbox_url = self::$grav['config']->get('system.base_url_relative') . '/'. $this->linkTarget;
|
||||
$lightbox_url = self::$grav['base_url'] . '/'. $this->linkTarget;
|
||||
|
||||
return array('a_url' => $lightbox_url, 'a_rel' => 'lightbox', 'img_url' => $url);
|
||||
}
|
||||
@@ -309,7 +351,7 @@ class Medium extends Data
|
||||
|
||||
$path = $this->get('path') . '/' . $this->get('filename') . '.meta.' . $type;
|
||||
if ($type == 'yaml') {
|
||||
$this->merge(Yaml::instance($path)->content());
|
||||
$this->merge(CompiledYamlFile::instance($path)->content());
|
||||
} elseif (in_array($type, array('jpg', 'jpeg', 'png', 'gif'))) {
|
||||
$this->set('thumb', $path);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
use Grav\Common\Config;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Common\Cache;
|
||||
use Grav\Common\Twig;
|
||||
use Grav\Common\Filesystem\File;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Data;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Taxonomy;
|
||||
use Grav\Common\Markdown\Markdown;
|
||||
use Grav\Common\Markdown\MarkdownExtra;
|
||||
use Grav\Component\EventDispatcher\Event;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\File\MarkdownFile;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
@@ -30,6 +30,11 @@ class Page
|
||||
{
|
||||
use GravTrait;
|
||||
|
||||
const ALL_PAGES = 0; // both standard and modular pages
|
||||
const STANDARD_PAGES = 1; // visible and invisible pages (e.g. 01.regular/, invisible/)
|
||||
const MODULAR_PAGES = 2; // modular pages (e.g. _modular/)
|
||||
|
||||
|
||||
/**
|
||||
* @var string Filename. Leave as null if page is folder.
|
||||
*/
|
||||
@@ -53,11 +58,14 @@ class Page
|
||||
protected $routable;
|
||||
protected $modified;
|
||||
protected $id;
|
||||
protected $items;
|
||||
protected $header;
|
||||
protected $frontmatter;
|
||||
protected $content;
|
||||
protected $raw_content;
|
||||
protected $pagination;
|
||||
protected $media;
|
||||
protected $metadata;
|
||||
protected $title;
|
||||
protected $max_count;
|
||||
protected $menu;
|
||||
@@ -109,6 +117,7 @@ class Page
|
||||
$this->modified($file->getMTime());
|
||||
$this->id($this->modified().md5($this->filePath()));
|
||||
$this->header();
|
||||
$this->metadata();
|
||||
$this->slug();
|
||||
$this->visible();
|
||||
$this->modularTwig($this->slug[0] == '_');
|
||||
@@ -120,7 +129,8 @@ class Page
|
||||
* @param string $var Raw content string
|
||||
* @return Object Raw content string
|
||||
*/
|
||||
public function raw($var = null) {
|
||||
public function raw($var = null)
|
||||
{
|
||||
$file = $this->file();
|
||||
|
||||
if ($var) {
|
||||
@@ -135,7 +145,28 @@ class Page
|
||||
$this->header = null;
|
||||
$this->content = null;
|
||||
}
|
||||
return $file->raw();
|
||||
return $file ? $file->raw() : '';
|
||||
}
|
||||
|
||||
public function frontmatter($var = null)
|
||||
{
|
||||
|
||||
if ($var) {
|
||||
$this->frontmatter = (string) $var;
|
||||
|
||||
// Update also file object.
|
||||
$file = $this->file();
|
||||
if ($file) {
|
||||
$file->frontmatter((string) $var);
|
||||
}
|
||||
|
||||
// Force content re-processing.
|
||||
$this->id(time().md5($this->filePath()));
|
||||
}
|
||||
if (!$this->frontmatter) {
|
||||
$this->header();
|
||||
}
|
||||
return $this->frontmatter;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,6 +193,7 @@ class Page
|
||||
$file = $this->file();
|
||||
if ($file) {
|
||||
$this->raw_content = $file->markdown();
|
||||
$this->frontmatter = $file->frontmatter();
|
||||
$this->header = (object) $file->header();
|
||||
|
||||
$var = true;
|
||||
@@ -187,9 +219,6 @@ class Page
|
||||
if (isset($this->header->visible)) {
|
||||
$this->visible = $this->header->visible;
|
||||
}
|
||||
if (isset($this->header->modular)) {
|
||||
$this->modular = $this->header->modular;
|
||||
}
|
||||
if (isset($this->header->order_dir)) {
|
||||
$this->order_dir = trim($this->header->order_dir);
|
||||
}
|
||||
@@ -204,7 +233,7 @@ class Page
|
||||
}
|
||||
if (isset($this->header->markdown_extra)) {
|
||||
$this->markdown_extra = (bool)$this->header->markdown_extra;
|
||||
}
|
||||
}
|
||||
if (isset($this->header->taxonomy)) {
|
||||
foreach ($this->header->taxonomy as $taxonomy => $taxitems) {
|
||||
$this->taxonomy[$taxonomy] = (array)$taxitems;
|
||||
@@ -218,7 +247,6 @@ class Page
|
||||
$this->process[$process] = $status;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $this->header;
|
||||
@@ -232,7 +260,6 @@ class Page
|
||||
*/
|
||||
public function summary($size = null)
|
||||
{
|
||||
|
||||
$content = $this->content();
|
||||
|
||||
// Return calculated summary based on summary divider's position
|
||||
@@ -255,6 +282,8 @@ class Page
|
||||
return Utils::truncateHTML($content, $size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets and Sets the content based on content portion of the .md file
|
||||
*
|
||||
@@ -287,12 +316,12 @@ class Page
|
||||
/** @var Cache $cache */
|
||||
$cache = self::$grav['cache'];
|
||||
$cache_id = md5('page'.$this->id());
|
||||
$content = $cache->fetch($cache_id);
|
||||
$this->content = $cache->fetch($cache_id);
|
||||
|
||||
$update_cache = false;
|
||||
if ($content === false) {
|
||||
if ($this->content === false) {
|
||||
// Process Markdown
|
||||
$content = $this->processMarkdown();
|
||||
$this->content = $this->processMarkdown();
|
||||
$update_cache = true;
|
||||
}
|
||||
|
||||
@@ -304,7 +333,7 @@ class Page
|
||||
|
||||
// Do we want to cache markdown, but process twig in each page?
|
||||
if ($update_cache && $process_twig) {
|
||||
$cache->save($cache_id, $content);
|
||||
$cache->save($cache_id, $this->content);
|
||||
$update_cache = false;
|
||||
}
|
||||
|
||||
@@ -312,29 +341,49 @@ class Page
|
||||
if ($update_cache || $process_twig) {
|
||||
/** @var Twig $twig */
|
||||
$twig = self::$grav['twig'];
|
||||
$content = $twig->processPage($this, $content);
|
||||
$this->content = $twig->processPage($this, $this->content);
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the whole page, including processed content
|
||||
if ($update_cache) {
|
||||
$cache->save($cache_id, $content);
|
||||
// Process any post-processing but pre-caching functionality
|
||||
self::$grav->fireEvent('onPageContentProcessed', new Event(['page' => $this]));
|
||||
$cache->save($cache_id, $this->content);
|
||||
}
|
||||
|
||||
// Handle summary divider
|
||||
$divider_pos = strpos($content, '<p>'.SUMMARY_DELIMITER.'</p>');
|
||||
$divider_pos = strpos($this->content, '<p>'.SUMMARY_DELIMITER.'</p>');
|
||||
if ($divider_pos !== false) {
|
||||
$this->summary_size = $divider_pos;
|
||||
$content = str_replace('<p>'.SUMMARY_DELIMITER.'</p>', '', $content);
|
||||
$this->content = str_replace('<p>'.SUMMARY_DELIMITER.'</p>', '', $this->content);
|
||||
}
|
||||
|
||||
$this->content = $content;
|
||||
|
||||
}
|
||||
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed by the onPageContentProcessed event to get the raw page content
|
||||
*
|
||||
* @return string the current page content
|
||||
*/
|
||||
public function getRawContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed by the onPageContentProcessed event to set the raw page content
|
||||
*
|
||||
* @param $content
|
||||
*/
|
||||
public function setRawContent($content)
|
||||
{
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value from a page variable (used mostly for creating edit forms).
|
||||
*
|
||||
@@ -377,6 +426,10 @@ class Page
|
||||
$path = explode('.', $name);
|
||||
$scope = array_shift($path);
|
||||
|
||||
if ($name == 'frontmatter') {
|
||||
return $this->frontmatter;
|
||||
}
|
||||
|
||||
if ($scope == 'header') {
|
||||
$current = $this->header();
|
||||
foreach ($path as $field) {
|
||||
@@ -398,12 +451,13 @@ class Page
|
||||
/**
|
||||
* Get file object to the page.
|
||||
*
|
||||
* @return File\Markdown|null
|
||||
* @return MarkdownFile|null
|
||||
*/
|
||||
public function file()
|
||||
{
|
||||
if ($this->name) {
|
||||
return File\Markdown::instance($this->filePath());
|
||||
// TODO: use CompiledMarkdownFile after fixing issues in it.
|
||||
return MarkdownFile::instance($this->filePath());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -421,7 +475,7 @@ class Page
|
||||
if ($file) {
|
||||
$file->filename($this->filePath());
|
||||
$file->header((array) $this->header());
|
||||
$file->markdown($this->content());
|
||||
$file->markdown($this->raw_content);
|
||||
$file->save();
|
||||
}
|
||||
}
|
||||
@@ -473,7 +527,7 @@ class Page
|
||||
/**
|
||||
* Get blueprints for the page.
|
||||
*
|
||||
* @return Data\Blueprint
|
||||
* @return Blueprint
|
||||
*/
|
||||
public function blueprints()
|
||||
{
|
||||
@@ -593,7 +647,7 @@ class Page
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function child_type()
|
||||
public function childType()
|
||||
{
|
||||
return isset($this->header->child_type) ? (string) $this->header->child_type : 'default';
|
||||
}
|
||||
@@ -611,7 +665,7 @@ class Page
|
||||
$this->template = $var;
|
||||
}
|
||||
if (empty($this->template)) {
|
||||
$this->template = str_replace(CONTENT_EXT, '', $this->name());
|
||||
$this->template = ($this->modular() ? 'modular/' : '') . str_replace(CONTENT_EXT, '', $this->name());
|
||||
}
|
||||
return $this->template;
|
||||
}
|
||||
@@ -704,6 +758,57 @@ class Page
|
||||
return $this->process;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to merge page metadata tags and build an array of Metadata objects
|
||||
* that can then be rendered in the page.
|
||||
*/
|
||||
public function metadata()
|
||||
{
|
||||
// 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;
|
||||
|
||||
// Set the Generator tag
|
||||
$this->metadata['generator'] = array('name'=>'generator', 'content'=>'Grav ' . GRAV_VERSION);
|
||||
|
||||
// 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');
|
||||
|
||||
if (isset($page_header->metadata)) {
|
||||
$page_header->metadata = array_merge($defaults, $page_header->metadata);
|
||||
} else {
|
||||
$page_header->metadata = $defaults;
|
||||
}
|
||||
|
||||
// Build an array of meta objects..
|
||||
foreach ((array)$page_header->metadata as $key => $value) {
|
||||
|
||||
// If this is a property type metadata: "og", "twitter", "facebook" etc
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $property => $prop_value) {
|
||||
$prop_key = $key.":".$property;
|
||||
$this->metadata[$prop_key] = array('property'=>$prop_key, 'content'=>$prop_value);
|
||||
}
|
||||
// If it this is a standard meta data type
|
||||
} else {
|
||||
if (in_array($key, $header_tag_http_equivs)) {
|
||||
$this->metadata[$key] = array('http_equiv'=>$key, 'content'=>$value);
|
||||
} else {
|
||||
$this->metadata[$key] = array('name'=>$key, 'content'=>$value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and Sets the slug for the Page. The slug is used in the URL routing. If not set it uses
|
||||
* the parent folder from the path
|
||||
@@ -999,10 +1104,7 @@ class Page
|
||||
*/
|
||||
public function modular($var = null)
|
||||
{
|
||||
if ($var !== null) {
|
||||
$this->modular = (bool) $var;
|
||||
}
|
||||
return $this->modular;
|
||||
return $this->modularTwig($var);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1056,81 +1158,25 @@ class Page
|
||||
/**
|
||||
* Returns children of this page.
|
||||
*
|
||||
* @param bool $modular|null whether or not to return modular children
|
||||
* @return Collection
|
||||
*/
|
||||
public function children()
|
||||
public function children($type = Page::STANDARD_PAGES)
|
||||
{
|
||||
/** @var Pages $pages */
|
||||
$pages = self::$grav['pages'];
|
||||
$children = $pages->children($this->path());
|
||||
|
||||
return $pages->children($this->path());
|
||||
}
|
||||
// Filter out modular pages on regular call
|
||||
// Filter out non-modular pages when all you want is modular
|
||||
foreach ($children as $child) {
|
||||
$is_modular_page = $child->modular();
|
||||
if (($is_modular_page && $type == Page::STANDARD_PAGES) || (!$is_modular_page && $type == Page::MODULAR_PAGES)) {
|
||||
$children->remove($child->path());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
* @deprecated
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
throw new \Exception('Use $page->children()->count() instead.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @throws \Exception
|
||||
* @deprecated
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
throw new \Exception('Use $page->children()->__get() instead.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param $value
|
||||
* @throws \Exception
|
||||
* @deprecated
|
||||
*/
|
||||
public function __set($key, $value)
|
||||
{
|
||||
throw new \Exception('Use $page->children()->__set() instead.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
* @deprecated
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
throw new \Exception('Use $page->children()->current() instead.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
* @deprecated
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
throw new \Exception('Use $page->children()->next() instead.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
* @deprecated
|
||||
*/
|
||||
public function prev()
|
||||
{
|
||||
throw new \Exception('Use $page->children()->prev() instead.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @throws \Exception
|
||||
* @deprecated
|
||||
*/
|
||||
public function nth($key)
|
||||
{
|
||||
throw new \Exception('Use $page->children()->nth($position) instead.');
|
||||
return $children;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1140,15 +1186,8 @@ class Page
|
||||
*/
|
||||
public function isFirst()
|
||||
{
|
||||
/** @var Pages $pages */
|
||||
$pages = self::$grav['pages'];
|
||||
$parent = $pages->get($this->parent);
|
||||
|
||||
if ($this->path() == array_values($parent->items)[0]) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
$collection = $this->parent()->collection('content', false);
|
||||
return $collection->isFirst($this->path());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1158,15 +1197,8 @@ class Page
|
||||
*/
|
||||
public function isLast()
|
||||
{
|
||||
/** @var Pages $pages */
|
||||
$pages = self::$grav['pages'];
|
||||
$parent = $pages->get($this->parent);
|
||||
|
||||
if ($this->path() == array_values($parent->items)[count($parent->items)-1]) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
$collection = $this->parent()->collection('content', false);
|
||||
return $collection->isLast($this->path());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1197,16 +1229,8 @@ class Page
|
||||
*/
|
||||
public function adjacentSibling($direction = 1)
|
||||
{
|
||||
/** @var Pages $pages */
|
||||
$pages = self::$grav['pages'];
|
||||
$parent = $pages->get($this->parent);
|
||||
$current = $this->slug();
|
||||
|
||||
$keys = array_flip(array_keys($parent->items));
|
||||
$values = array_values($parent->items);
|
||||
$index = $keys[$current] - $direction;
|
||||
|
||||
return array_key_exists($index, $values) ? $pages->get($values[$index]) : $this;
|
||||
$collection = $this->parent()->collection('content', false);
|
||||
return $collection->adjacentSibling($this->path(), $direction);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1232,10 +1256,23 @@ class Page
|
||||
*/
|
||||
public function activeChild()
|
||||
{
|
||||
/** @var Uri $uri */
|
||||
$uri = self::$grav['uri'];
|
||||
if (!$this->home() && (strpos($uri->url(), $this->url()) !== false)) {
|
||||
return true;
|
||||
$config = self::$grav['config'];
|
||||
|
||||
// Special check when item is home
|
||||
if ($this->home()) {
|
||||
$paths = $uri->paths();
|
||||
$home = ltrim($config->get('system.home.alias'), '/');
|
||||
if ($paths[0] == $home) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (strpos($uri->url(), $this->url()) === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1281,10 +1318,11 @@ class Page
|
||||
* Get a collection of pages in the current context.
|
||||
*
|
||||
* @param string|array $params
|
||||
* @param boolean $pagination
|
||||
* @return Collection
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function collection($params = 'content')
|
||||
public function collection($params = 'content', $pagination = true)
|
||||
{
|
||||
if (is_string($params)) {
|
||||
$params = (array) $this->value('header.'.$params);
|
||||
@@ -1324,8 +1362,6 @@ class Page
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$config->set('system.cache.enabled', false); // TODO: Do we still need this?
|
||||
}
|
||||
}
|
||||
// TODO: END OF MOVE
|
||||
@@ -1343,13 +1379,16 @@ class Page
|
||||
// New Custom event to handle things like pagination.
|
||||
$grav->fireEvent('onCollectionProcessed', new Event(['collection' => $collection]));
|
||||
|
||||
$params = $collection->params();
|
||||
// Slice and dice the collection if pagination is required
|
||||
if ($pagination) {
|
||||
$params = $collection->params();
|
||||
|
||||
$limit = isset($params['limit']) ? $params['limit'] : 0;
|
||||
$start = !empty($params['pagination']) ? ($uri->currentPage() - 1) * $limit : 0;
|
||||
$limit = isset($params['limit']) ? $params['limit'] : 0;
|
||||
$start = !empty($params['pagination']) ? ($uri->currentPage() - 1) * $limit : 0;
|
||||
|
||||
if ($limit && $collection->count() > $limit) {
|
||||
$collection->slice($start, $limit);
|
||||
if ($limit && $collection->count() > $limit) {
|
||||
$collection->slice($start, $limit);
|
||||
}
|
||||
}
|
||||
|
||||
return $collection;
|
||||
@@ -1389,16 +1428,24 @@ class Page
|
||||
if (!empty($parts)) {
|
||||
switch ($parts[0]) {
|
||||
case 'modular':
|
||||
// FIXME: filter by modular
|
||||
$results = $this->children();
|
||||
$results = $this->children(Page::MODULAR_PAGES);
|
||||
break;
|
||||
case 'children':
|
||||
// FIXME: filter by non-modular
|
||||
$results = $this->children();
|
||||
$results = $this->children(Page::STANDARD_PAGES);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '@page':
|
||||
if (!empty($params)) {
|
||||
$page = $this->find($params[0]);
|
||||
if ($page) {
|
||||
$results = $page->children(Page::STANDARD_PAGES);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '@taxonomy':
|
||||
// Gets a collection of pages by using one of the following formats:
|
||||
// @taxonomy.category: blog
|
||||
@@ -1530,7 +1577,7 @@ class Page
|
||||
} else {
|
||||
$parsedown = new Markdown($this);
|
||||
}
|
||||
$content = $parsedown->parse($content);
|
||||
$content = $parsedown->text($content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
@@ -1568,17 +1615,14 @@ class Page
|
||||
$parent = $this->parent();
|
||||
|
||||
// Extract visible children from the parent page.
|
||||
$visible = array();
|
||||
$list = array();
|
||||
/** @var Page $page */
|
||||
foreach ($parent as $page) {
|
||||
foreach ($parent->children()->visible() as $page) {
|
||||
if ($page->order()) {
|
||||
$visible[$page->slug] = $page->path();
|
||||
$list[$page->slug] = $page->path();
|
||||
}
|
||||
}
|
||||
|
||||
// List only visible pages.
|
||||
$list = array_intersect($visible, $pages->sort($parent));
|
||||
|
||||
// If page was moved, take it out of the list.
|
||||
if ($this->_action == 'move') {
|
||||
unset($list[$this->slug()]);
|
||||
@@ -1615,7 +1659,16 @@ class Page
|
||||
Folder::copy($this->_original->path(), $this->path());
|
||||
}
|
||||
|
||||
if ($this->name() != $this->_original->name()) {
|
||||
$path = $this->path();
|
||||
if (is_file($path . '/' . $this->_original->name())) {
|
||||
rename($path . '/' . $this->_original->name(), $path . '/' . $this->name());
|
||||
}
|
||||
}
|
||||
|
||||
$this->_action = null;
|
||||
$this->_original = null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Config;
|
||||
use Grav\Common\Data;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Common\Cache;
|
||||
use Grav\Common\Taxonomy;
|
||||
use Grav\Component\EventDispatcher\Event;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Data\Blueprints;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
|
||||
/**
|
||||
* GravPages is the class that is the entry point into the hierarchy of pages
|
||||
@@ -36,7 +37,7 @@ class Pages
|
||||
/**
|
||||
* @var array|string[]
|
||||
*/
|
||||
protected $routes;
|
||||
protected $routes = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
@@ -44,7 +45,7 @@ class Pages
|
||||
protected $sort;
|
||||
|
||||
/**
|
||||
* @var Data\Blueprints
|
||||
* @var Blueprints
|
||||
*/
|
||||
protected $blueprints;
|
||||
|
||||
@@ -53,10 +54,15 @@ class Pages
|
||||
*/
|
||||
protected $last_modified;
|
||||
|
||||
/**
|
||||
* @var Types
|
||||
*/
|
||||
static protected $types;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @params Grav $c
|
||||
* @param Grav $c
|
||||
*/
|
||||
public function __construct(Grav $c)
|
||||
{
|
||||
@@ -172,8 +178,11 @@ class Pages
|
||||
public function sortCollection(Collection $collection, $orderBy, $orderDir = 'asc', $orderManual = null)
|
||||
{
|
||||
$items = $collection->toArray();
|
||||
if (!$items) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$lookup = md5(serialize($items));
|
||||
$lookup = md5(json_encode($items));
|
||||
if (!isset($this->sort[$lookup][$orderBy])) {
|
||||
$this->buildSort($lookup, $items, $orderBy, $orderManual);
|
||||
}
|
||||
@@ -193,10 +202,13 @@ class Pages
|
||||
*
|
||||
* @param string $path
|
||||
* @return Page
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function get($path)
|
||||
{
|
||||
if (!is_null($path) && !is_string($path)) throw new \Exception();
|
||||
if (!is_null($path) && !is_string($path)) {
|
||||
throw new \Exception();
|
||||
}
|
||||
return isset($this->instances[(string) $path]) ? $this->instances[(string) $path] : null;
|
||||
}
|
||||
|
||||
@@ -224,14 +236,35 @@ class Pages
|
||||
// Fetch page if there's a defined route to it.
|
||||
$page = isset($this->routes[$url]) ? $this->get($this->routes[$url]) : null;
|
||||
|
||||
// If the page cannot be reached, look into site wide routes.
|
||||
|
||||
|
||||
// If the page cannot be reached, look into site wide redirects, routes + wildcards
|
||||
if (!$all && (!$page || !$page->routable())) {
|
||||
/** @var Config $config */
|
||||
$config = $this->grav['config'];
|
||||
|
||||
// Try redirects
|
||||
$redirect = $config->get("site.redirects.{$url}");
|
||||
if ($redirect) {
|
||||
$this->grav->redirect($redirect);
|
||||
}
|
||||
|
||||
// See if route matches one in the site configuration
|
||||
$route = $config->get("site.routes.{$url}");
|
||||
if ($route) {
|
||||
$page = $this->dispatch($route, $all);
|
||||
} else {
|
||||
// Try looking for wildcards
|
||||
foreach ($config->get("site.routes") as $alias => $route) {
|
||||
$match = rtrim($alias, '*');
|
||||
if (strpos($alias, '*') !== false && strpos($url, $match) !== false) {
|
||||
$wildcard_url = str_replace('*', str_replace($match, '', $url), $route);
|
||||
$page = isset($this->routes[$wildcard_url]) ? $this->get($this->routes[$wildcard_url]) : null;
|
||||
if ($page) {
|
||||
return $page;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,15 +285,12 @@ class Pages
|
||||
* Get a blueprint for a page type.
|
||||
*
|
||||
* @param string $type
|
||||
* @return Data\Blueprint
|
||||
* @return Blueprint
|
||||
*/
|
||||
public function blueprints($type)
|
||||
{
|
||||
if (!isset($this->blueprints)) {
|
||||
/** @var Config $config */
|
||||
$config = $this->grav['config'];
|
||||
|
||||
$this->blueprints = new Data\Blueprints(THEMES_DIR . $config->get('system.pages.theme') . '/blueprints/');
|
||||
$this->blueprints = new Blueprints(self::getTypes());
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -300,28 +330,55 @@ class Pages
|
||||
$list[$current->route()] = str_repeat(' ', ($level-1)*2) . $current->title();
|
||||
}
|
||||
|
||||
foreach ($current as $next) {
|
||||
foreach ($current->children() as $next) {
|
||||
$list = array_merge($list, $this->getList($next, $level + 1));
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available page types.
|
||||
*
|
||||
* @return Types
|
||||
*/
|
||||
public static function getTypes()
|
||||
{
|
||||
if (!self::$types) {
|
||||
self::$types = new Types();
|
||||
self::$types->scanBlueprints('theme://blueprints/');
|
||||
self::$types->scanTemplates('theme://templates/');
|
||||
|
||||
$event = new Event();
|
||||
$event->types = self::$types;
|
||||
Grav::instance()->fireEvent('onGetPageTemplates', $event);
|
||||
}
|
||||
|
||||
return self::$types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available page types.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
static public function types()
|
||||
public static function types()
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
$types = self::getTypes();
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $grav['config'];
|
||||
return $types->pageSelect();
|
||||
}
|
||||
|
||||
$blueprints = new Data\Blueprints(THEMES_DIR . $config->get('system.pages.theme') . '/blueprints/');
|
||||
/**
|
||||
* Get available page types.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function modularTypes()
|
||||
{
|
||||
$types = self::getTypes();
|
||||
|
||||
return $blueprints->types();
|
||||
return $types->modularSelect();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -329,7 +386,7 @@ class Pages
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
static public function parents()
|
||||
public static function parents()
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
|
||||
@@ -357,8 +414,6 @@ class Pages
|
||||
/** @var Taxonomy $taxonomy */
|
||||
$taxonomy = $this->grav['taxonomy'];
|
||||
|
||||
$last_modified = 0;
|
||||
|
||||
// how should we check for last modified? Default is by file
|
||||
switch (strtolower($config->get('system.cache.check.method', 'file'))) {
|
||||
case 'none':
|
||||
@@ -372,10 +427,11 @@ class Pages
|
||||
$last_modified = Folder::lastModifiedFile(PAGES_DIR);
|
||||
}
|
||||
|
||||
$page_cache_id = md5(USER_DIR.$last_modified);
|
||||
$page_cache_id = md5(USER_DIR.$last_modified.$config->checksum());
|
||||
|
||||
list($this->instances, $this->routes, $this->children, $taxonomy_map, $this->sort) = $cache->fetch($page_cache_id);
|
||||
if (!$this->instances) {
|
||||
$this->grav['debugger']->addMessage('Page cache missed, rebuilding pages..');
|
||||
$this->recurse();
|
||||
$this->buildRoutes();
|
||||
|
||||
@@ -386,6 +442,7 @@ class Pages
|
||||
);
|
||||
} else {
|
||||
// If pages was found in cache, set the taxonomy
|
||||
$this->grav['debugger']->addMessage('Page cache hit.');
|
||||
$taxonomy->taxonomy($taxonomy_map);
|
||||
}
|
||||
} else {
|
||||
@@ -398,7 +455,7 @@ class Pages
|
||||
* Recursive function to load & build page relationships.
|
||||
*
|
||||
* @param string $directory
|
||||
* @param null $parent
|
||||
* @param Page|null $parent
|
||||
* @return Page
|
||||
* @throws \RuntimeException
|
||||
* @internal
|
||||
@@ -408,10 +465,14 @@ class Pages
|
||||
$directory = rtrim($directory, DS);
|
||||
$iterator = new \DirectoryIterator($directory);
|
||||
$page = new Page;
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $this->grav['config'];
|
||||
|
||||
$page->path($directory);
|
||||
if ($parent) $page->parent($parent);
|
||||
if ($parent) {
|
||||
$page->parent($parent);
|
||||
}
|
||||
|
||||
$page->orderDir($config->get('system.pages.order.dir'));
|
||||
$page->orderBy($config->get('system.pages.order.by'));
|
||||
@@ -426,20 +487,21 @@ class Pages
|
||||
throw new \RuntimeException('Fatal error when creating page instances.');
|
||||
}
|
||||
|
||||
$last_modified = 0;
|
||||
// set current modified of page
|
||||
$last_modified = $page->modified();
|
||||
|
||||
// flat for content availability
|
||||
$content_exists = false;
|
||||
|
||||
/** @var \DirectoryIterator $file */
|
||||
foreach ($iterator as $file) {
|
||||
$name = $file->getFilename();
|
||||
|
||||
$date = $file->getMTime();
|
||||
if ($date > $last_modified) {
|
||||
$last_modified = $date;
|
||||
}
|
||||
$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]));
|
||||
@@ -462,21 +524,26 @@ class Pages
|
||||
|
||||
// set the modified time if not already set
|
||||
if (!$page->date()) {
|
||||
$page->date($file->getMTime());
|
||||
$page->date($modified);
|
||||
}
|
||||
|
||||
// set the last modified time on pages
|
||||
$this->lastModified($file->getMTime());
|
||||
|
||||
if ($config->get('system.pages.events.page')) {
|
||||
$this->grav->fireEvent('onFolderProcessed', new Event(['page' => $page]));
|
||||
}
|
||||
}
|
||||
|
||||
// Update the last modified if it's newer than already found
|
||||
if ($modified > $last_modified) {
|
||||
$last_modified = $modified;
|
||||
}
|
||||
}
|
||||
|
||||
// Override the modified and ID so that it takes the latest change
|
||||
// into account
|
||||
// Set routability to false if no page found
|
||||
if (!$content_exists) {
|
||||
$page->routable(false);
|
||||
}
|
||||
|
||||
// Override the modified and ID so that it takes the latest change into account
|
||||
$page->modified($last_modified);
|
||||
$page->id($last_modified.md5($page->filePath()));
|
||||
|
||||
@@ -568,7 +635,7 @@ class Pages
|
||||
|
||||
// handle special case when order_by is random
|
||||
if ($order_by == 'random') {
|
||||
$list = $this->array_shuffle($list);
|
||||
$list = $this->arrayShuffle($list);
|
||||
} else {
|
||||
// else just sort the list according to specified key
|
||||
asort($list);
|
||||
@@ -603,12 +670,13 @@ class Pages
|
||||
}
|
||||
|
||||
// Shuffles and associative array
|
||||
protected function array_shuffle($list) {
|
||||
protected function arrayShuffle($list)
|
||||
{
|
||||
$keys = array_keys($list);
|
||||
shuffle($keys);
|
||||
|
||||
$new = array();
|
||||
foreach($keys as $key) {
|
||||
foreach ($keys as $key) {
|
||||
$new[$key] = $list[$key];
|
||||
}
|
||||
|
||||
|
||||
86
system/src/Grav/Common/Page/Types.php
Normal file
86
system/src/Grav/Common/Page/Types.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Constructor;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Countable;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Iterator;
|
||||
|
||||
class Types implements \ArrayAccess, \Iterator, \Countable
|
||||
{
|
||||
use ArrayAccess, Constructor, Iterator, Countable, Export;
|
||||
|
||||
protected $items;
|
||||
|
||||
public function register($type, $blueprint = null)
|
||||
{
|
||||
if ($blueprint || empty($this->items[$type])) {
|
||||
$this->items[$type] = $blueprint;
|
||||
}
|
||||
}
|
||||
|
||||
public function scanBlueprints($path)
|
||||
{
|
||||
$options = [
|
||||
'compare' => 'Filename',
|
||||
'pattern' => '|\.yaml$|',
|
||||
'filters' => [
|
||||
'key' => '|\.yaml$|'
|
||||
],
|
||||
'key' => 'SubPathName',
|
||||
'value' => 'PathName',
|
||||
];
|
||||
|
||||
$this->items = Folder::all($path, $options) + $this->items;
|
||||
}
|
||||
|
||||
public function scanTemplates($path)
|
||||
{
|
||||
$options = [
|
||||
'compare' => 'Filename',
|
||||
'pattern' => '|\.html\.twig$|',
|
||||
'filters' => [
|
||||
'value' => '|\.html\.twig$|'
|
||||
],
|
||||
'value' => 'Filename',
|
||||
'recursive' => false
|
||||
];
|
||||
|
||||
foreach (Folder::all($path, $options) as $type) {
|
||||
$this->register($type);
|
||||
}
|
||||
if (file_exists($path . 'modular/')) {
|
||||
foreach (Folder::all($path . 'modular/', $options) as $type) {
|
||||
$this->register('modular/' . $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function pageSelect()
|
||||
{
|
||||
$list = [];
|
||||
foreach ($this->items as $name => $file) {
|
||||
if (strpos($name, '/')) {
|
||||
continue;
|
||||
}
|
||||
$list[$name] = ucfirst(strtr($name, '_', ' '));
|
||||
}
|
||||
ksort($list);
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function modularSelect()
|
||||
{
|
||||
$list = [];
|
||||
foreach ($this->items as $name => $file) {
|
||||
if (strpos($name, 'modular/') !== 0) {
|
||||
continue;
|
||||
}
|
||||
$list[$name] = trim(ucfirst(strtr(basename($name), '_', ' ')));
|
||||
}
|
||||
ksort($list);
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Component\EventDispatcher\EventDispatcher;
|
||||
use Grav\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Grav\Common\Config\Config;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* The Plugin object just holds the id and path to a plugin.
|
||||
@@ -22,12 +23,15 @@ class Plugin implements EventSubscriberInterface
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
protected $active = true;
|
||||
|
||||
/**
|
||||
* By default assign all methods as listeners using the default priority.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
$methods = get_class_methods(get_called_class());
|
||||
|
||||
$list = array();
|
||||
@@ -52,6 +56,14 @@ class Plugin implements EventSubscriberInterface
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function isAdmin()
|
||||
{
|
||||
if (isset($this->grav['admin'])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $events
|
||||
*/
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Filesystem\File;
|
||||
use Grav\Component\EventDispatcher\EventDispatcher;
|
||||
use Grav\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Data\Blueprints;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* The Plugins object holds an array of all the plugin objects that
|
||||
@@ -16,7 +19,8 @@ class Plugins extends Iterator
|
||||
{
|
||||
protected $grav;
|
||||
|
||||
public function __construct(Grav $grav) {
|
||||
public function __construct(Grav $grav)
|
||||
{
|
||||
$this->grav = $grav;
|
||||
}
|
||||
|
||||
@@ -41,9 +45,9 @@ class Plugins extends Iterator
|
||||
continue;
|
||||
}
|
||||
|
||||
$filePath = $this->grav['locator']('plugin://' . $plugin . DS . $plugin . PLUGIN_EXT);
|
||||
$filePath = $this->grav['locator']('plugins://' . $plugin . DS . $plugin . PLUGIN_EXT);
|
||||
if (!is_file($filePath)) {
|
||||
throw new \RuntimeException(sprintf("Plugin '%s' enabled but not found!", $filePath, $plugin));
|
||||
throw new \RuntimeException(sprintf("Plugin '%s' enabled but not found! Try clearing cache with `bin/grav clear-cache`", $plugin));
|
||||
}
|
||||
|
||||
require_once $filePath;
|
||||
@@ -51,7 +55,7 @@ class Plugins extends Iterator
|
||||
$pluginClass = 'Grav\\Plugin\\'.ucfirst($plugin).'Plugin';
|
||||
|
||||
if (!class_exists($pluginClass)) {
|
||||
throw new \RuntimeException(sprintf("Plugin '%s' class not found!", $plugin));
|
||||
throw new \RuntimeException(sprintf("Plugin '%s' class not found! Try reinstalling this plugin.", $plugin));
|
||||
}
|
||||
|
||||
$instance = new $pluginClass($this->grav, $config);
|
||||
@@ -60,14 +64,6 @@ class Plugins extends Iterator
|
||||
}
|
||||
}
|
||||
|
||||
/** @var Themes $themes */
|
||||
$themes = $this->grav['themes'];
|
||||
$themes->configure();
|
||||
$instance = $themes->load();
|
||||
if ($instance instanceof EventSubscriberInterface) {
|
||||
$events->addSubscriber($instance);
|
||||
}
|
||||
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
@@ -81,12 +77,12 @@ class Plugins extends Iterator
|
||||
/**
|
||||
* Return list of all plugin data with their blueprints.
|
||||
*
|
||||
* @return array|Data\Data[]
|
||||
* @return array
|
||||
*/
|
||||
static public function all()
|
||||
public static function all()
|
||||
{
|
||||
$list = array();
|
||||
$iterator = new \DirectoryIterator('plugin:///');
|
||||
$iterator = new \DirectoryIterator('plugins://');
|
||||
|
||||
/** @var \DirectoryIterator $directory */
|
||||
foreach ($iterator as $directory) {
|
||||
@@ -103,18 +99,18 @@ class Plugins extends Iterator
|
||||
return $list;
|
||||
}
|
||||
|
||||
static public function get($type)
|
||||
public static function get($name)
|
||||
{
|
||||
$blueprints = new Data\Blueprints('plugin://' . $type);
|
||||
$blueprints = new Blueprints("plugins://{$name}");
|
||||
$blueprint = $blueprints->get('blueprints');
|
||||
$blueprint->name = $type;
|
||||
$blueprint->name = $name;
|
||||
|
||||
// Load default configuration.
|
||||
$file = File\Yaml::instance('plugin://' . "{$type}/{$type}" . YAML_EXT);
|
||||
$obj = new Data\Data($file->content(), $blueprint);
|
||||
$file = CompiledYamlFile::instance("plugins://{$name}/{$name}.yaml");
|
||||
$obj = new Data($file->content(), $blueprint);
|
||||
|
||||
// Override with user configuration.
|
||||
$file = File\Yaml::instance('plugin://' . "config/plugins/{$type}" . YAML_EXT);
|
||||
$file = CompiledYamlFile::instance("user://config/plugins/{$name}.yaml");
|
||||
$obj->merge($file->content());
|
||||
|
||||
// Save configuration always to user/config.
|
||||
@@ -122,4 +118,5 @@ class Plugins extends Iterator
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
/**
|
||||
* The Registry class is an implementation of the Registry Pattern to store and retrieve
|
||||
* instances of objects used by Grav
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
* @deprecated
|
||||
*/
|
||||
class Registry
|
||||
{
|
||||
|
||||
/**
|
||||
* Return global instance.
|
||||
*
|
||||
* @return Registry
|
||||
*/
|
||||
public static function instance()
|
||||
{
|
||||
user_error(__METHOD__ . '()', E_USER_DEPRECATED);
|
||||
return new Registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entry from the registry.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function get($key)
|
||||
{
|
||||
user_error(__METHOD__ . '()', E_USER_DEPRECATED);
|
||||
$instance = Grav::instance();
|
||||
return $instance[strtolower($key)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
private function __clone()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Store entry to the registry.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function store($key, $value)
|
||||
{
|
||||
user_error(__CLASS__ . '::' . __METHOD__ . '()', E_USER_DEPRECATED);
|
||||
$instance = Grav::instance();
|
||||
$instance[strtolower($key)] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entry from the registry.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function retrieve($key)
|
||||
{
|
||||
user_error(__CLASS__ . '::' . __METHOD__ . '()', E_USER_DEPRECATED);
|
||||
$instance = Grav::instance();
|
||||
return $instance[strtolower($key)];
|
||||
}
|
||||
}
|
||||
73
system/src/Grav/Common/Service/ConfigServiceProvider.php
Normal file
73
system/src/Grav/Common/Service/ConfigServiceProvider.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
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;
|
||||
|
||||
/**
|
||||
* The Config class contains configuration information.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class ConfigServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
private $environment;
|
||||
|
||||
public function register(Container $container)
|
||||
{
|
||||
$self = $this;
|
||||
|
||||
$container['blueprints'] = function ($c) use ($self) {
|
||||
return $self->loadMasterBlueprints($c);
|
||||
};
|
||||
|
||||
$container['config'] = function ($c) use ($self) {
|
||||
return $self->loadMasterConfig($c);
|
||||
};
|
||||
}
|
||||
|
||||
public function loadMasterConfig(Container $container)
|
||||
{
|
||||
$environment = $this->getEnvironment($container);
|
||||
$file = CACHE_DIR . 'compiled/config/master-'.$environment.'.php';
|
||||
$data = is_file($file) ? (array) include $file : [];
|
||||
if ($data) {
|
||||
try {
|
||||
$config = new Config($data, $container, $environment);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($config)) {
|
||||
$file = GRAV_ROOT . '/setup.php';
|
||||
$data = is_file($file) ? (array) include $file : [];
|
||||
$config = new Config($data, $container, $environment);
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
public function loadMasterBlueprints(Container $container)
|
||||
{
|
||||
$environment = $this->getEnvironment($container);
|
||||
$file = CACHE_DIR . 'compiled/blueprints/master-'.$environment.'.php';
|
||||
$data = is_file($file) ? (array) include $file : [];
|
||||
|
||||
return new Blueprints($data, $container);
|
||||
}
|
||||
|
||||
public function getEnvironment(Container $container)
|
||||
{
|
||||
if (!isset($this->environment)) {
|
||||
$this->environment = $container['uri']->environment();
|
||||
}
|
||||
|
||||
return $this->environment;
|
||||
}
|
||||
}
|
||||
41
system/src/Grav/Common/Service/ErrorServiceProvider.php
Normal file
41
system/src/Grav/Common/Service/ErrorServiceProvider.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
namespace Grav\Common\Service;
|
||||
|
||||
use Grav\Common\Errors\Errors;
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use Whoops\Handler\JsonResponseHandler;
|
||||
use Whoops\Handler\PrettyPageHandler;
|
||||
use Whoops\Handler\PlainTextHandler;
|
||||
use Whoops\Run;
|
||||
|
||||
class ErrorServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
public function register(Container $container)
|
||||
{
|
||||
// Setup Whoops-based error handler
|
||||
$errors = new Errors;
|
||||
|
||||
$error_page = new PrettyPageHandler;
|
||||
$error_page->setPageTitle('Crikey! There was an error...');
|
||||
$error_page->setEditor('sublime');
|
||||
$error_page->addResourcePath(GRAV_ROOT . '/system/assets');
|
||||
$error_page->addCustomCss('whoops.css');
|
||||
|
||||
$json_page = new JsonResponseHandler;
|
||||
$json_page->onlyForAjaxRequests(true);
|
||||
|
||||
$errors->pushHandler($error_page, 'pretty');
|
||||
$errors->pushHandler(new PlainTextHandler, 'text');
|
||||
$errors->pushHandler($json_page, 'json');
|
||||
|
||||
$logger = $container['log'];
|
||||
$errors->pushHandler(function ($exception, $inspector, $run) use($logger) {
|
||||
$logger->addCritical($exception->getMessage(). ' - Trace: '. $exception->getTraceAsString());
|
||||
}, 'log');
|
||||
|
||||
$errors->register();
|
||||
|
||||
$container['errors'] = $errors;
|
||||
}
|
||||
}
|
||||
23
system/src/Grav/Common/Service/LoggerServiceProvider.php
Normal file
23
system/src/Grav/Common/Service/LoggerServiceProvider.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
namespace Grav\Common\Service;
|
||||
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use \Monolog\Logger;
|
||||
use \Monolog\Handler\StreamHandler;
|
||||
use \Monolog\Handler\RotatingFileHandler;
|
||||
|
||||
class LoggerServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
public function register(Container $container)
|
||||
{
|
||||
$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;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,45 @@
|
||||
<?php
|
||||
namespace Grav\Common\Service;
|
||||
|
||||
use Grav\Component\DI\ServiceProviderInterface;
|
||||
use Grav\Component\Filesystem\ResourceLocator;
|
||||
use Grav\Component\Filesystem\StreamWrapper\ReadOnlyStream;
|
||||
use Grav\Component\Filesystem\StreamWrapper\Stream;
|
||||
use Grav\Common\Config\Config;
|
||||
use Pimple\Container;
|
||||
use RocketTheme\Toolbox\DI\ServiceProviderInterface;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use RocketTheme\Toolbox\StreamWrapper\ReadOnlyStream;
|
||||
use RocketTheme\Toolbox\StreamWrapper\Stream;
|
||||
use RocketTheme\Toolbox\StreamWrapper\StreamBuilder;
|
||||
|
||||
class StreamsServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
protected $schemes = [];
|
||||
|
||||
public function register(Container $container)
|
||||
{
|
||||
$self = $this;
|
||||
|
||||
$container['locator'] = function($c) use ($self) {
|
||||
$locator = new ResourceLocator;
|
||||
$locator = new UniformResourceLocator(ROOT_DIR);
|
||||
$self->init($c, $locator);
|
||||
|
||||
return $locator;
|
||||
};
|
||||
|
||||
$container['streams'] = function($c) use ($self) {
|
||||
$locator = $c['locator'];
|
||||
|
||||
// Set locator to both streams.
|
||||
Stream::setLocator($locator);
|
||||
ReadOnlyStream::setLocator($locator);
|
||||
|
||||
return new StreamBuilder($this->schemes);
|
||||
};
|
||||
}
|
||||
|
||||
protected function init(Container $container, ResourceLocator $locator)
|
||||
protected function init(Container $container, UniformResourceLocator $locator)
|
||||
{
|
||||
$schemes = $container['config']->get('streams.schemes');
|
||||
|
||||
if (!$schemes) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set locator to both streams.
|
||||
Stream::setLocator($locator);
|
||||
ReadOnlyStream::setLocator($locator);
|
||||
|
||||
$registered = stream_get_wrappers();
|
||||
/** @var Config $config */
|
||||
$config = $container['config'];
|
||||
$schemes = (array) $config->get('streams.schemes', []);
|
||||
|
||||
foreach ($schemes as $scheme => $config) {
|
||||
if (isset($config['paths'])) {
|
||||
@@ -45,17 +51,12 @@ class StreamsServiceProvider implements ServiceProviderInterface
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($scheme, $registered)) {
|
||||
stream_wrapper_unregister($scheme);
|
||||
}
|
||||
$type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream';
|
||||
if ($type[0] != '\\') {
|
||||
$type = '\\Grav\\Component\\Filesystem\\StreamWrapper\\' . $type;
|
||||
$type = '\\RocketTheme\\Toolbox\\StreamWrapper\\' . $type;
|
||||
}
|
||||
|
||||
if (!stream_wrapper_register($scheme, $type)) {
|
||||
throw new \InvalidArgumentException("Stream '{$type}' could not be initialized.");
|
||||
}
|
||||
$this->schemes[$scheme] = $type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Session;
|
||||
|
||||
use Grav\Common\Getters;
|
||||
|
||||
/**
|
||||
* Session wide messages.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Message
|
||||
{
|
||||
/**
|
||||
* @var array|string[]
|
||||
*/
|
||||
protected $messages = array();
|
||||
|
||||
/**
|
||||
* Add message to the queue.
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $scope
|
||||
* @return $this
|
||||
*/
|
||||
public function add($message, $scope = 'default')
|
||||
{
|
||||
$message = array('message' => $message, 'scope' => $scope);
|
||||
|
||||
$this->messages[] = $message;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear message queue.
|
||||
*
|
||||
* @param string $scope
|
||||
* @return $this
|
||||
*/
|
||||
public function clear($scope = null)
|
||||
{
|
||||
if ($scope === null) {
|
||||
$this->messages = array();
|
||||
} else {
|
||||
foreach ($this->messages as $key => $message) {
|
||||
if ($message['scope'] == $scope) {
|
||||
unset($this->messages[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all messages.
|
||||
*
|
||||
* @param string $scope
|
||||
* @return array
|
||||
*/
|
||||
public function all($scope = null)
|
||||
{
|
||||
if ($scope === null) {
|
||||
return array_values($this->messages);
|
||||
}
|
||||
|
||||
$messages = array();
|
||||
foreach ($this->messages as $message) {
|
||||
if ($message['scope'] == $scope) {
|
||||
$messages[] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and clear message queue.
|
||||
*
|
||||
* @param string $scope
|
||||
* @return array
|
||||
*/
|
||||
public function fetch($scope = null)
|
||||
{
|
||||
$messages = $this->all($scope);
|
||||
$this->clear($scope);
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Session;
|
||||
|
||||
/**
|
||||
* Session handling.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Session implements \IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $started = false;
|
||||
|
||||
/**
|
||||
* @var Session
|
||||
*/
|
||||
static $instance;
|
||||
|
||||
/**
|
||||
* @param int $lifetime Defaults to 1800 seconds.
|
||||
* @param string $path Cookie path.
|
||||
*/
|
||||
public function __construct($lifetime, $path)
|
||||
{
|
||||
if (isset(self::$instance)) {
|
||||
throw new \RuntimeException("Session has already been initialized.", 500);
|
||||
}
|
||||
|
||||
// Destroy any existing sessions started with session.auto_start
|
||||
if (session_id())
|
||||
{
|
||||
session_unset();
|
||||
session_destroy();
|
||||
}
|
||||
|
||||
// Disable transparent sid support
|
||||
ini_set('session.use_trans_sid', '0');
|
||||
|
||||
// Only allow cookies
|
||||
ini_set('session.use_cookies', 1);
|
||||
|
||||
session_set_cookie_params($lifetime, $path);
|
||||
register_shutdown_function('session_write_close');
|
||||
session_cache_limiter('none');
|
||||
|
||||
self::$instance = $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current session instance.
|
||||
*
|
||||
* @return Session
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function instance()
|
||||
{
|
||||
if (!isset(self::$instance)) {
|
||||
throw new \RuntimeException("Session hasn't been initialized.", 500);
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the session storage
|
||||
*
|
||||
* @return $this
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function start()
|
||||
{
|
||||
if (!session_start()) {
|
||||
throw new \RuntimeException('Failed to start session');
|
||||
}
|
||||
|
||||
$this->started = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get session ID
|
||||
*
|
||||
* @return string Session ID
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return session_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set session Id
|
||||
*
|
||||
* @param string $id Session ID
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
session_id($id);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get session name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return session_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set session name
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
session_name($name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates the current session.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function invalidate()
|
||||
{
|
||||
$params = session_get_cookie_params();
|
||||
setcookie(session_name(), '', time() - 42000,
|
||||
$params['path'], $params['domain'],
|
||||
$params['secure'], $params['httponly']
|
||||
);
|
||||
|
||||
session_unset();
|
||||
session_destroy();
|
||||
|
||||
$this->started = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the session to be saved and closed
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
session_write_close();
|
||||
|
||||
$this->started = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an attribute is defined.
|
||||
*
|
||||
* @param string $name The attribute name
|
||||
*
|
||||
* @return bool True if the attribute is defined, false otherwise
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
return isset($_SESSION[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an attribute.
|
||||
*
|
||||
* @param string $name The attribute name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return isset($_SESSION[$name]) ? $_SESSION[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an attribute.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$_SESSION[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an attribute.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return mixed The removed value or null when it does not exist
|
||||
*/
|
||||
public function __unset($name)
|
||||
{
|
||||
unset($_SESSION[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns attributes.
|
||||
*
|
||||
* @return array Attributes
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
return $_SESSION;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve an external iterator
|
||||
*
|
||||
* @return \ArrayIterator Return an ArrayIterator of $_SESSION
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($_SESSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the session was started.
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function started()
|
||||
{
|
||||
return $this->started;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use \Grav\Common\Page;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Page\Collection;
|
||||
use Grav\Common\Page\Page;
|
||||
|
||||
/**
|
||||
* The Taxonomy object is a singleton that holds a reference to a 'taxonomy map'. This map is
|
||||
@@ -41,10 +43,10 @@ class Taxonomy
|
||||
* Takes an individual page and processes the taxonomies configured in its header. It
|
||||
* then adds those taxonomies to the map
|
||||
*
|
||||
* @param Page\Page $page the page to process
|
||||
* @param Page $page the page to process
|
||||
* @param array $page_taxonomy
|
||||
*/
|
||||
public function addTaxonomy(Page\Page $page, $page_taxonomy = null)
|
||||
public function addTaxonomy(Page $page, $page_taxonomy = null)
|
||||
{
|
||||
if (!$page_taxonomy) {
|
||||
$page_taxonomy = $page->taxonomy();
|
||||
@@ -69,21 +71,34 @@ class Taxonomy
|
||||
* particular taxonomy.
|
||||
*
|
||||
* @param array $taxonomies taxonomies to search, eg ['tag'=>['animal','cat']]
|
||||
* @return Page\Page page object with sub-pages set to contain matches found in the taxonomy map
|
||||
* @param string $operator can be 'or' or 'and' (defaults to 'or')
|
||||
* @return Colleciton Collection object set to contain matches found in the taxonomy map
|
||||
*/
|
||||
public function findTaxonomy($taxonomies)
|
||||
public function findTaxonomy($taxonomies, $operator = 'and')
|
||||
{
|
||||
$results = array();
|
||||
$matches = [];
|
||||
$results = [];
|
||||
|
||||
foreach ((array)$taxonomies as $taxonomy => $items) {
|
||||
foreach ((array) $items as $item) {
|
||||
if (isset($this->taxonomy_map[$taxonomy][$item])) {
|
||||
$results = array_merge($results, $this->taxonomy_map[$taxonomy][$item]);
|
||||
$matches[] = $this->taxonomy_map[$taxonomy][$item];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Page\Collection($results, ['taxonomies' => $taxonomies]);
|
||||
if (strtolower($operator) == 'or') {
|
||||
foreach ($matches as $match) {
|
||||
$results = array_merge($results, $match);
|
||||
}
|
||||
} else {
|
||||
$results = $matches ? array_pop($matches) : [];
|
||||
foreach ($matches as $match) {
|
||||
$results = array_intersect_key($results, $match);
|
||||
}
|
||||
}
|
||||
|
||||
return new Collection($results, ['taxonomies' => $taxonomies]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Filesystem\File\Yaml;
|
||||
use Grav\Component\Filesystem\ResourceLocator;
|
||||
use Grav\Common\Config\Config;
|
||||
|
||||
class Theme extends Plugin
|
||||
{
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Data\Blueprints;
|
||||
use Grav\Common\Filesystem\File\Yaml;
|
||||
use Grav\Component\Filesystem\ResourceLocator;
|
||||
use Grav\Common\Data\Data;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
/**
|
||||
* The Themes object holds an array of all the theme objects that Grav knows about.
|
||||
@@ -12,27 +15,51 @@ use Grav\Component\Filesystem\ResourceLocator;
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class Themes
|
||||
class Themes extends Iterator
|
||||
{
|
||||
/**
|
||||
* @var Grav
|
||||
*/
|
||||
/** @var Grav */
|
||||
protected $grav;
|
||||
|
||||
/** @var Config */
|
||||
protected $config;
|
||||
|
||||
public function __construct(Grav $grav)
|
||||
{
|
||||
$this->grav = $grav;
|
||||
$this->config = $grav['config'];
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
/** @var EventDispatcher $events */
|
||||
$events = $this->grav['events'];
|
||||
|
||||
/** @var Themes $themes */
|
||||
$themes = $this->grav['themes'];
|
||||
$themes->configure();
|
||||
|
||||
try {
|
||||
$instance = $themes->load();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
throw new \RuntimeException($this->current(). ' theme could not be found');
|
||||
}
|
||||
|
||||
if ($instance instanceof EventSubscriberInterface) {
|
||||
$events->addSubscriber($instance);
|
||||
}
|
||||
|
||||
$this->grav['theme'] = $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of all theme data with their blueprints.
|
||||
*
|
||||
* @return array|Data[]
|
||||
* @return array
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
$list = array();
|
||||
$iterator = new \DirectoryIterator(THEMES_DIR);
|
||||
$iterator = new \DirectoryIterator('themes://');
|
||||
|
||||
/** @var \DirectoryIterator $directory */
|
||||
foreach ($iterator as $directory) {
|
||||
@@ -50,9 +77,9 @@ class Themes
|
||||
}
|
||||
|
||||
/**
|
||||
* Get theme or throw exception if it cannot be found.
|
||||
* Get theme configuration or throw exception if it cannot be found.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $name
|
||||
* @return Data
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
@@ -62,25 +89,23 @@ class Themes
|
||||
throw new \RuntimeException('Theme name not provided.');
|
||||
}
|
||||
|
||||
$blueprints = new Blueprints("theme:///{$name}");
|
||||
$blueprints = new Blueprints("themes://{$name}");
|
||||
$blueprint = $blueprints->get('blueprints');
|
||||
$blueprint->name = $name;
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $this->grav['config'];
|
||||
|
||||
// Find thumbnail.
|
||||
$thumb = "theme:///{$name}/thumbnail.jpg";
|
||||
$thumb = "themes://{$name}/thumbnail.jpg";
|
||||
|
||||
if (file_exists($thumb)) {
|
||||
$blueprint->set('thumbnail', $config->get('system.base_url_relative') . "/user/themes/{$name}/thumbnail.jpg");
|
||||
$blueprint->set('thumbnail', $this->grav['base_url'] . "/user/themes/{$name}/thumbnail.jpg");
|
||||
}
|
||||
|
||||
// Load default configuration.
|
||||
$file = Yaml::instance("theme:///{$name}/{$name}.yaml");
|
||||
$file = CompiledYamlFile::instance("themes://{$name}/{$name}" . YAML_EXT);
|
||||
$obj = new Data($file->content(), $blueprint);
|
||||
|
||||
// Override with user configuration.
|
||||
$file = Yaml::instance("user://config/themes/{$name}.yaml");
|
||||
$file = CompiledYamlFile::instance("user://config/themes/{$name}" . YAML_EXT);
|
||||
$obj->merge($file->content());
|
||||
|
||||
// Save configuration always to user/config.
|
||||
@@ -89,32 +114,34 @@ class Themes
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function current($name = null)
|
||||
/**
|
||||
* Return name of the current theme.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = $this->grav['config'];
|
||||
|
||||
if (!$name) {
|
||||
$name = $config->get('system.pages.theme');
|
||||
}
|
||||
|
||||
return $name;
|
||||
return (string) $this->config->get('system.pages.theme');
|
||||
}
|
||||
|
||||
public function load($name = null)
|
||||
/**
|
||||
* Load current theme.
|
||||
*
|
||||
* @return Theme|object
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
$name = $this->current($name);
|
||||
// NOTE: ALL THE LOCAL VARIABLES ARE USED INSIDE INCLUDED FILE, DO NOT REMOVE THEM!
|
||||
$grav = $this->grav;
|
||||
$config = $this->config;
|
||||
$name = $this->current();
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $grav['config'];
|
||||
|
||||
/** @var ResourceLocator $locator */
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $grav['locator'];
|
||||
$file = $locator('theme://theme.php') ?: $locator("theme://{$name}.php");
|
||||
|
||||
$file = $locator("theme://theme.php") ?: $locator("theme://{$name}.php");
|
||||
if ($file) {
|
||||
// Local variables available in the file: $grav, $config, $name, $path, $file
|
||||
// Local variables available in the file: $grav, $config, $name, $file
|
||||
$class = include $file;
|
||||
|
||||
if (!is_object($class)) {
|
||||
@@ -124,6 +151,8 @@ class Themes
|
||||
$class = new $className($grav, $config, $name);
|
||||
}
|
||||
}
|
||||
} elseif (!$locator('theme://') && !defined('GRAV_CLI')) {
|
||||
exit("Theme '$name' does not exist, unable to display page.");
|
||||
}
|
||||
|
||||
if (empty($class)) {
|
||||
@@ -133,24 +162,26 @@ class Themes
|
||||
return $class;
|
||||
}
|
||||
|
||||
public function configure($name = null) {
|
||||
$name = $this->current($name);
|
||||
/**
|
||||
* Configure and prepare streams for current template.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function configure()
|
||||
{
|
||||
$name = $this->current();
|
||||
$config = $this->config;
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $this->grav['config'];
|
||||
$this->loadConfiguration($name, $config);
|
||||
|
||||
$themeConfig = Yaml::instance(THEMES_DIR . "{$name}/{$name}.yaml")->content();
|
||||
|
||||
$config->merge(['themes' => [$name => $themeConfig]]);
|
||||
|
||||
/** @var ResourceLocator $locator */
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $this->grav['locator'];
|
||||
|
||||
// TODO: move
|
||||
$registered = stream_get_wrappers();
|
||||
$schemes = $config->get(
|
||||
"themes.{$name}.streams.schemes",
|
||||
['theme' => ['paths' => ["user/themes/{$name}"]]]
|
||||
['theme' => ['paths' => $locator->findResources("themes://{$name}", false)]]
|
||||
);
|
||||
|
||||
foreach ($schemes as $scheme => $config) {
|
||||
@@ -168,13 +199,19 @@ class Themes
|
||||
}
|
||||
$type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream';
|
||||
if ($type[0] != '\\') {
|
||||
$type = '\\Grav\\Component\\Filesystem\\StreamWrapper\\' . $type;
|
||||
$type = '\\RocketTheme\\Toolbox\\StreamWrapper\\' . $type;
|
||||
}
|
||||
|
||||
if (!stream_wrapper_register($scheme, $type)) {
|
||||
throw new \InvalidArgumentException("Stream '{$type}' could not be initialized.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected function loadConfiguration($name, Config $config)
|
||||
{
|
||||
$themeConfig = CompiledYamlFile::instance("themes://{$name}/{$name}" . YAML_EXT)->content();
|
||||
|
||||
$config->joinDefaults("themes.{$name}", $themeConfig);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user