mirror of
https://github.com/getgrav/grav.git
synced 2025-12-05 15:29:57 +01:00
Compare commits
552 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2b07fec1f | ||
|
|
9601061f25 | ||
|
|
b1317e56ec | ||
|
|
353832c386 | ||
|
|
8bf04d4593 | ||
|
|
c9f0500c6f | ||
|
|
0dc465bb41 | ||
|
|
34e50aab21 | ||
|
|
5a2411a0e6 | ||
|
|
178f66c940 | ||
|
|
6d3a7a3989 | ||
|
|
2400eaf04e | ||
|
|
752a3ca5bd | ||
|
|
6dbb6eb432 | ||
|
|
e580bb9998 | ||
|
|
d309491f06 | ||
|
|
0e35048143 | ||
|
|
c919ed36b4 | ||
|
|
6d2a7c53dc | ||
|
|
a8582fc131 | ||
|
|
76b7bd855d | ||
|
|
5656bb3caf | ||
|
|
57bd4d8f22 | ||
|
|
e71cd5a7ad | ||
|
|
4132388dda | ||
|
|
1edabe3b00 | ||
|
|
019fdd65e9 | ||
|
|
fa432cd32f | ||
|
|
4935679659 | ||
|
|
835c64c173 | ||
|
|
4d6ecbe618 | ||
|
|
63456aad85 | ||
|
|
bec2ee91b5 | ||
|
|
027c9cdd04 | ||
|
|
8ecea3a8c1 | ||
|
|
69e6d57346 | ||
|
|
8127d9cf31 | ||
|
|
32810efcd9 | ||
|
|
76b463792e | ||
|
|
f779fc57df | ||
|
|
7afef9073c | ||
|
|
1147516dcc | ||
|
|
3f1661965b | ||
|
|
8afad07146 | ||
|
|
81bce07a6e | ||
|
|
e883b57ac6 | ||
|
|
ae2f95b1ae | ||
|
|
d8df9ffb53 | ||
|
|
3c51c0acd4 | ||
|
|
c085540143 | ||
|
|
d239dd56d5 | ||
|
|
8f9eb3b48b | ||
|
|
1f906e6a50 | ||
|
|
e0a4efe181 | ||
|
|
9382dc9c10 | ||
|
|
aa85f20aa9 | ||
|
|
73654a99f9 | ||
|
|
ea8add59b1 | ||
|
|
5078ae62c0 | ||
|
|
e026ba32f4 | ||
|
|
e9ebe3b533 | ||
|
|
cea130700d | ||
|
|
0ae7ebac68 | ||
|
|
cabec818e2 | ||
|
|
718d443d52 | ||
|
|
57c5885216 | ||
|
|
14767a3e11 | ||
|
|
fb31caefef | ||
|
|
7ceb0dd065 | ||
|
|
b2a78d587c | ||
|
|
16c3a3690b | ||
|
|
7f8e8f67a5 | ||
|
|
9792c9a84e | ||
|
|
13e9e6f5e1 | ||
|
|
e62133233c | ||
|
|
3ec855e28f | ||
|
|
4b56a05f57 | ||
|
|
845da953e1 | ||
|
|
565a76c317 | ||
|
|
e01a116173 | ||
|
|
4e07c294c5 | ||
|
|
58bb5a6993 | ||
|
|
2c51dd5fe1 | ||
|
|
ce8513d3ff | ||
|
|
08c4fd02d2 | ||
|
|
2e680cd35a | ||
|
|
f12ef84a98 | ||
|
|
0563b2b6e5 | ||
|
|
c68c39df27 | ||
|
|
41c00d7fbe | ||
|
|
750dfb60dc | ||
|
|
4305bbabd5 | ||
|
|
ecc12be531 | ||
|
|
b65280f3c9 | ||
|
|
e077b3d04c | ||
|
|
91a963f580 | ||
|
|
8404ba7a09 | ||
|
|
86b907c86c | ||
|
|
ca899072d4 | ||
|
|
bbfc63e943 | ||
|
|
9bce9ce026 | ||
|
|
3a25f028df | ||
|
|
491c6d6a1f | ||
|
|
64bb6ea2ad | ||
|
|
084e59dc90 | ||
|
|
f7ea2e95e4 | ||
|
|
7acdf231a4 | ||
|
|
5d38e0fa14 | ||
|
|
e364616730 | ||
|
|
5ccefee288 | ||
|
|
93f4ad6e5a | ||
|
|
c481acbb71 | ||
|
|
bbdb0189f1 | ||
|
|
e1d655a3ac | ||
|
|
d849f8a03e | ||
|
|
67c3d64275 | ||
|
|
1546783371 | ||
|
|
21cc9f86f3 | ||
|
|
2936d26f8d | ||
|
|
ae453dbc71 | ||
|
|
d25699397f | ||
|
|
d461fac089 | ||
|
|
4bda629a6a | ||
|
|
8a2817a305 | ||
|
|
8887f8862d | ||
|
|
4883a408c7 | ||
|
|
7b75171f79 | ||
|
|
dc42288fa0 | ||
|
|
f576f914ae | ||
|
|
88faa5040c | ||
|
|
1e5365ba51 | ||
|
|
eb93dacba0 | ||
|
|
1e79cbc945 | ||
|
|
d350bd31cb | ||
|
|
89b9b4e9b7 | ||
|
|
9549e5aa4c | ||
|
|
1517d0e40b | ||
|
|
f13593aac7 | ||
|
|
b8023b2444 | ||
|
|
bb49098a05 | ||
|
|
c368fbcda9 | ||
|
|
1c1bf86e9a | ||
|
|
5f1b190ba9 | ||
|
|
0860f53d68 | ||
|
|
1ffd1cb6e7 | ||
|
|
030d230312 | ||
|
|
974f9d52a4 | ||
|
|
1a238ea1b1 | ||
|
|
a049441048 | ||
|
|
20e771f121 | ||
|
|
cff4e225e6 | ||
|
|
2c69f539ae | ||
|
|
548081471c | ||
|
|
3688cfa397 | ||
|
|
20b2856dee | ||
|
|
7e94e46459 | ||
|
|
f3b4efb661 | ||
|
|
7947ba2442 | ||
|
|
ef63e993d2 | ||
|
|
29ae5b7aae | ||
|
|
6baf7e0b35 | ||
|
|
2e7ece17d7 | ||
|
|
2a56f21d13 | ||
|
|
d253c3c6c5 | ||
|
|
289a838ba1 | ||
|
|
f6f3e96106 | ||
|
|
7d22305678 | ||
|
|
7571d1d562 | ||
|
|
b4c06f537d | ||
|
|
830c723bae | ||
|
|
b83ab07374 | ||
|
|
8200cb9336 | ||
|
|
b1a38306af | ||
|
|
6ec0f4782f | ||
|
|
d25f9bcf1b | ||
|
|
cd3fd5a7b7 | ||
|
|
1ab1378259 | ||
|
|
21f87ade2d | ||
|
|
8f54e5739f | ||
|
|
21a6594573 | ||
|
|
01ce80fb1a | ||
|
|
174672c411 | ||
|
|
4785103081 | ||
|
|
7030422b11 | ||
|
|
2a06dc9bea | ||
|
|
66927043de | ||
|
|
7d16bafd52 | ||
|
|
ee340e2d6f | ||
|
|
66d9fd1a5e | ||
|
|
7c28de6ae5 | ||
|
|
c95f602ea2 | ||
|
|
6361280d99 | ||
|
|
b3a9d7cd41 | ||
|
|
071989c554 | ||
|
|
f956d7113f | ||
|
|
f0472fdd76 | ||
|
|
01899676a4 | ||
|
|
b13d572ca8 | ||
|
|
a588e08405 | ||
|
|
6c93483220 | ||
|
|
733c13102b | ||
|
|
688d6fe17a | ||
|
|
25ff1f230f | ||
|
|
094b58130a | ||
|
|
a18ec00962 | ||
|
|
d9188e76ed | ||
|
|
a04a79af3e | ||
|
|
0c84392d0f | ||
|
|
be12f350cb | ||
|
|
01c7eadc92 | ||
|
|
4b3abc282a | ||
|
|
81358e9984 | ||
|
|
8179d8eac1 | ||
|
|
c22a2579a9 | ||
|
|
0a26772e35 | ||
|
|
364fc7afa8 | ||
|
|
82bd54eb0b | ||
|
|
d92d9bafc6 | ||
|
|
9c541ee20b | ||
|
|
105dc34b2a | ||
|
|
0d8b46a157 | ||
|
|
6142a12f48 | ||
|
|
1bda3eb1e3 | ||
|
|
96b2b327b2 | ||
|
|
e014b12626 | ||
|
|
d473f72edd | ||
|
|
7139425812 | ||
|
|
f5722d7baa | ||
|
|
467eb00f0f | ||
|
|
0ce3646977 | ||
|
|
53dd6c0860 | ||
|
|
e4a130c919 | ||
|
|
0d9ddb92d5 | ||
|
|
68a561d4fb | ||
|
|
5e651dd0e5 | ||
|
|
1bb2965916 | ||
|
|
4ea650fc6d | ||
|
|
501b38c4ba | ||
|
|
ede958bd61 | ||
|
|
f3d099e655 | ||
|
|
db5c3ea400 | ||
|
|
504f57930a | ||
|
|
02b1b2cf9d | ||
|
|
99dbb3225b | ||
|
|
08a8d69be2 | ||
|
|
a029f89ad2 | ||
|
|
ed02fed866 | ||
|
|
06de6c8e17 | ||
|
|
9ddfcd2154 | ||
|
|
217e5e5a2c | ||
|
|
272bf357d4 | ||
|
|
9a5d14aa13 | ||
|
|
2b70b2ad4f | ||
|
|
9323385b25 | ||
|
|
fe90204772 | ||
|
|
1b66e3d2a1 | ||
|
|
a2e1b9e100 | ||
|
|
74ab81b524 | ||
|
|
01be2df935 | ||
|
|
62e5ea3bbd | ||
|
|
b8c274b7b8 | ||
|
|
e154b13b6e | ||
|
|
0f312a3c43 | ||
|
|
d8823a6b3a | ||
|
|
73d5f9da90 | ||
|
|
7707b042e5 | ||
|
|
ff95a116c2 | ||
|
|
2fe6dda365 | ||
|
|
f30a3c137e | ||
|
|
a5d31e7187 | ||
|
|
73c42313fa | ||
|
|
686ba8a3f6 | ||
|
|
3397d5d2b7 | ||
|
|
9ab3524fc5 | ||
|
|
3f10fa1b4c | ||
|
|
6811fbea3d | ||
|
|
3e245ef686 | ||
|
|
28cff4e1da | ||
|
|
2bf67e482d | ||
|
|
3976e4ce23 | ||
|
|
a1ab94ffdd | ||
|
|
d6bed5441d | ||
|
|
e505c409ac | ||
|
|
409742c078 | ||
|
|
6bd76028ce | ||
|
|
87dc53912b | ||
|
|
83d606c34a | ||
|
|
1f906326e7 | ||
|
|
ec67bf4c5b | ||
|
|
127fe7fa2a | ||
|
|
e0de6f8b5f | ||
|
|
e81e35b7c2 | ||
|
|
9bc365fe9e | ||
|
|
21772c5481 | ||
|
|
7b32cbe2e1 | ||
|
|
04a2f618bd | ||
|
|
73a104000a | ||
|
|
b8cb788324 | ||
|
|
690bb8a8be | ||
|
|
26b0b02de2 | ||
|
|
e5348ec8f1 | ||
|
|
5e22eee1a8 | ||
|
|
534bb57d3f | ||
|
|
f8c203c033 | ||
|
|
300155e82e | ||
|
|
3117a214d4 | ||
|
|
9959868022 | ||
|
|
9248c5b709 | ||
|
|
85fb5bccf3 | ||
|
|
bd595ca9f6 | ||
|
|
ace8823bd6 | ||
|
|
5051b15145 | ||
|
|
1c1f2c268a | ||
|
|
545c76088c | ||
|
|
b418a7fd1e | ||
|
|
2938848df1 | ||
|
|
17172f8920 | ||
|
|
aeb237633e | ||
|
|
d8a29dd639 | ||
|
|
5c139e4b3c | ||
|
|
d69a0a9b06 | ||
|
|
07ce9c83ac | ||
|
|
1d3559f0db | ||
|
|
be92a1da97 | ||
|
|
97b430a888 | ||
|
|
b696e0d790 | ||
|
|
5b08a8605e | ||
|
|
ab358dd820 | ||
|
|
573b3227c7 | ||
|
|
50785c2434 | ||
|
|
fb9705809d | ||
|
|
1aeac01284 | ||
|
|
0cc35b56e5 | ||
|
|
1423375312 | ||
|
|
640ba16f8b | ||
|
|
d4e17442b2 | ||
|
|
eb4eafd915 | ||
|
|
dc65475723 | ||
|
|
3f33e97f0c | ||
|
|
02508933d7 | ||
|
|
89ebf2b011 | ||
|
|
c747c4baf7 | ||
|
|
a3c848e4e2 | ||
|
|
345d61f04f | ||
|
|
f0585ddb4e | ||
|
|
a6790cace3 | ||
|
|
f244fc93c8 | ||
|
|
846a0baed8 | ||
|
|
26ebb8fa6d | ||
|
|
cb8ea7780f | ||
|
|
5400bd3951 | ||
|
|
03521cd3f6 | ||
|
|
1eb0e37214 | ||
|
|
3e769618ec | ||
|
|
9319d5c0aa | ||
|
|
eb94940df6 | ||
|
|
a4c8c53939 | ||
|
|
567dd0d2c6 | ||
|
|
6f06e0c424 | ||
|
|
8dfb9d08c4 | ||
|
|
25c44816c0 | ||
|
|
1a692b348e | ||
|
|
17ed48e677 | ||
|
|
685033bb02 | ||
|
|
b86c982ba1 | ||
|
|
a0148f36fd | ||
|
|
b88de0cd3b | ||
|
|
3207efd383 | ||
|
|
31dd235b50 | ||
|
|
bb1cdb17f5 | ||
|
|
f35287cb7a | ||
|
|
391f7d3be4 | ||
|
|
76d0583b00 | ||
|
|
7857568c92 | ||
|
|
bfe94bc5d0 | ||
|
|
cf3bcf6d7f | ||
|
|
d2aa775ee8 | ||
|
|
ce800ccd92 | ||
|
|
2c57453608 | ||
|
|
03f729602b | ||
|
|
21f064b1d0 | ||
|
|
b7f6b827e4 | ||
|
|
a33e2ed226 | ||
|
|
ef7806b509 | ||
|
|
e268cda1b5 | ||
|
|
3ae5e3c569 | ||
|
|
304c7519d1 | ||
|
|
a4a8a422a5 | ||
|
|
6700003dd2 | ||
|
|
93f99fcff1 | ||
|
|
a03f54902a | ||
|
|
4112d363dd | ||
|
|
3f1c6dd662 | ||
|
|
89b2da636f | ||
|
|
c010ae3a97 | ||
|
|
f619c8f49f | ||
|
|
a27a1a3fd1 | ||
|
|
f7470ace97 | ||
|
|
cb6b362e20 | ||
|
|
c6200f386a | ||
|
|
60423e4a28 | ||
|
|
f40410816e | ||
|
|
63a2ffc0b1 | ||
|
|
fad428b94b | ||
|
|
8b251ca350 | ||
|
|
05bd5cb964 | ||
|
|
76a6b77065 | ||
|
|
5049086062 | ||
|
|
f04dece44c | ||
|
|
621f38d856 | ||
|
|
ccc65aa420 | ||
|
|
7ddf35d3fa | ||
|
|
689c65c5e4 | ||
|
|
e571f3bcfd | ||
|
|
adf80c3f87 | ||
|
|
fed9862e44 | ||
|
|
7bc924b6d6 | ||
|
|
c881624801 | ||
|
|
3c63993db8 | ||
|
|
ac26c3ab4e | ||
|
|
2dedd9d1a0 | ||
|
|
c1ef8b6399 | ||
|
|
c2f2f7b009 | ||
|
|
c2c5afbb05 | ||
|
|
19eae2300f | ||
|
|
0ddd2941e9 | ||
|
|
6863d70f0b | ||
|
|
9f782a4aed | ||
|
|
bc844ba0d3 | ||
|
|
af97876794 | ||
|
|
92f319c188 | ||
|
|
010872fc1e | ||
|
|
259d8356b0 | ||
|
|
4cd4bb4202 | ||
|
|
fdb19b1a90 | ||
|
|
c9385632a9 | ||
|
|
0c90b5c7b2 | ||
|
|
ac4650632e | ||
|
|
aae644ca33 | ||
|
|
6aabb83204 | ||
|
|
593f7bfe27 | ||
|
|
77d1bf5ca8 | ||
|
|
bc8f742565 | ||
|
|
632c48e1e6 | ||
|
|
42b2f99c6d | ||
|
|
e518e3ca92 | ||
|
|
e013cf70db | ||
|
|
bdf80fd920 | ||
|
|
ce282c47cf | ||
|
|
23a25b8df2 | ||
|
|
8363da35c6 | ||
|
|
ff973a0634 | ||
|
|
2b504149f8 | ||
|
|
031df8de2c | ||
|
|
229d3acc99 | ||
|
|
195bdd71a2 | ||
|
|
f6da7c9344 | ||
|
|
50ebdeddad | ||
|
|
4f4974aae4 | ||
|
|
977b7d2936 | ||
|
|
c4797a2d6f | ||
|
|
e293050ea1 | ||
|
|
4c2ed0ee3d | ||
|
|
c550faa843 | ||
|
|
e433150a5a | ||
|
|
ceb3626c94 | ||
|
|
2d33f745ba | ||
|
|
4cf0a71a45 | ||
|
|
7f526cea76 | ||
|
|
6e39107d45 | ||
|
|
9ddfced969 | ||
|
|
dde6b2cd8b | ||
|
|
a9894902be | ||
|
|
62c668b32c | ||
|
|
f48c8fb50a | ||
|
|
0ac7c4b6bf | ||
|
|
5bdf22764f | ||
|
|
f48a05e247 | ||
|
|
dc0b7cd3d2 | ||
|
|
d40a71757b | ||
|
|
bd702b4c21 | ||
|
|
b843167d6b | ||
|
|
be7517def5 | ||
|
|
b3f085c27d | ||
|
|
c7647ef20b | ||
|
|
c56471fe73 | ||
|
|
c87279c350 | ||
|
|
ed4040c6d0 | ||
|
|
7345c75ac0 | ||
|
|
3b7bd3bb71 | ||
|
|
9f2ecfadf7 | ||
|
|
f7fa6f114d | ||
|
|
8c99758a7a | ||
|
|
9614932e07 | ||
|
|
b8715d9730 | ||
|
|
c9563ac6a3 | ||
|
|
0b20bf1909 | ||
|
|
45d39df057 | ||
|
|
b7ffd3c721 | ||
|
|
70f15de701 | ||
|
|
4446770dda | ||
|
|
e2389db483 | ||
|
|
7be42080da | ||
|
|
22e550ab40 | ||
|
|
c515996adc | ||
|
|
fb3ee1187d | ||
|
|
8cbba11955 | ||
|
|
00971dee24 | ||
|
|
e8b7b40535 | ||
|
|
15113d0acb | ||
|
|
5bd79dc072 | ||
|
|
0f75f6b1e1 | ||
|
|
33229f0f26 | ||
|
|
5bbedb40fc | ||
|
|
d1164a14c1 | ||
|
|
ce4b227328 | ||
|
|
5b2de3795f | ||
|
|
3b25cc1a79 | ||
|
|
a6e951a4dc | ||
|
|
ea734567ea | ||
|
|
d32ec013dd | ||
|
|
b568ad8f56 | ||
|
|
04541dac38 | ||
|
|
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 | ||
|
|
260f200d6f | ||
|
|
2d95b3bb09 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -7,6 +7,8 @@ vendor/
|
||||
.sass-cache
|
||||
|
||||
# Grav Specific
|
||||
backup/*
|
||||
!backup/.*
|
||||
cache/*
|
||||
!cache/.*
|
||||
assets/*
|
||||
|
||||
24
.htaccess
24
.htaccess
@@ -2,6 +2,17 @@
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
## 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 - 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
|
||||
@@ -19,17 +30,6 @@ 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
|
||||
@@ -44,7 +44,7 @@ RewriteRule .* index.php [L]
|
||||
|
||||
## Begin - Security
|
||||
# Block all direct access for these folders
|
||||
RewriteRule ^(cache|bin|logs)/(.*) error [L]
|
||||
RewriteRule ^(cache|bin|logs|backup)/(.*) error [L]
|
||||
# Block access to specific file types for these folders
|
||||
RewriteRule ^(system|user|vendor)/(.*)\.(txt|md|html|yaml|php|twig|sh|bat)$ error [L]
|
||||
## End - Security
|
||||
|
||||
78
.travis.yml
Normal file
78
.travis.yml
Normal file
@@ -0,0 +1,78 @@
|
||||
language: php
|
||||
php: 5.6
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- build_test
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
on_failure: always
|
||||
hipchat:
|
||||
# hipchat_api@grav
|
||||
rooms:
|
||||
- secure: "bqO0wM1B7bJnQw2fuhquSXEqI9gw6WmFytIh9sEWXzbYTzTUP5t0PcKOd3FT2BNMRaDxPJLVl+vG/oqmqDUBkEmOGcG504IQjeNzZqnMz0tXQMIcCc22Las9tFfc4Jf6RVi/qGomFtHGE9Wgii+TAN4zqZaufbNjwd8SyjO0+W8="
|
||||
template:
|
||||
- '%{repository}#%{build_number} (%{branch}): Grav Core and Skeletons packages have been uploaded. (<a href="%{build_url}">Details</a>)'
|
||||
format: html
|
||||
env:
|
||||
global:
|
||||
# Colors!
|
||||
- TEXTRESET=$(tput sgr0) # reset the foreground colour
|
||||
- RED=$(tput setaf 1)
|
||||
- GREEN=$(tput setaf 2)
|
||||
- YELLOW=$(tput setaf 3)
|
||||
- BLUE=$(tput setaf 4)
|
||||
- BOLD=$(tput bold)
|
||||
# User
|
||||
- GH_USER="getgrav"
|
||||
# Paths
|
||||
- RT_DEVTOOLS=$HOME/devtools
|
||||
- GOPATH="$HOME/go"
|
||||
- PATH="$GOPATH/bin:$PATH"
|
||||
# GH_TOKEN [API Key]
|
||||
- secure: "jS+c+g2v33vypG4VtqiSDW2qQ4dGJZlrUKBRCztoy1yrOrYRPvc5Vzi/AS3fDmZ4yizukEwmUNNzyZQcgFvLPpmCCml46Dovp8R9OXhbNe8OnULmaSn2Zkr71oblMYu6ZP+RpYvNq0BIdSB3u2TiFriHMiTIkX9UwZNaUCOX1ig="
|
||||
# BB_TOKEN value => "user:pass@"
|
||||
- secure: "einUtSEkUWy2IrqLXyVjwUU+mwaaoiOXRRVdLBpA3Zye6bZx8cm5h/5AplkPWhM/NmCJoW/MwNZHHkFhlr3mDRov5iOxVmTTYfnXB+I5lxYTSgduOLLErS7mU8hfADpVDU8bHNU44fNGD3UEiG1PD4qQBX4DMlqIFmR20mjs81k="
|
||||
# GH_API_USER [for curl]
|
||||
- secure: "Xbk/V9aIys0NxccJGR3Zrm2GRxDnA0RuazBs1puIboTYDhbi0Z7JTL+mOx3xp5Kfoniad/xAuijQESTM9MMrKqq/qCzhAMaC1+vcL4pCHZH4NSG6DBxB9BPkKVFq+1llu5FTEf8bkxHzwGR0l1ARW6TVRcgTHr5B58bCEIwEOrI="
|
||||
# Latest Release version
|
||||
- TRAVIS_TAG=$(curl --fail --user ${GH_API_USER} -s https://api.github.com/repos/getgrav/grav/releases/latest | grep tag_name | head -n 1 | cut -d '"' -f 4)
|
||||
|
||||
before_install:
|
||||
- composer self-update
|
||||
- go get github.com/aktau/github-release
|
||||
- git clone --quiet --depth=50 --branch=master https://${BB_TOKEN}bitbucket.org/rockettheme/grav-devtools.git $RT_DEVTOOLS &>/dev/null;
|
||||
- if [ ! -z "$TRAVIS_TAG" ]; then
|
||||
cd $RT_DEVTOOLS;
|
||||
./build-grav.sh skeletons.txt;
|
||||
fi
|
||||
script:
|
||||
- if [ ! -z "$TRAVIS_TAG" ]; then
|
||||
FILES="$RT_DEVTOOLS/grav-dist/*.zip";
|
||||
for file in ${FILES[@]}; do
|
||||
NAME=${file##*/};
|
||||
REPO="$(echo ${NAME} | rev | cut -f 2- -d "-" | rev)";
|
||||
if [[ $REPO == 'grav' || $REPO == 'grav-update' ]]; then
|
||||
REPO="grav";
|
||||
fi;
|
||||
API="$(curl --fail --user "${GH_API_USER}" -s https://api.github.com/repos/${GH_USER}/${REPO}/releases/latest)";
|
||||
ASSETS="$(echo "${API}" | node gh-assets.js)";
|
||||
TAG="$(echo "${API}" | grep tag_name | head -n 1 | cut -d '"' -f 4)";
|
||||
if [ $REPO == "grav" ]; then
|
||||
TAG=$TRAVIS_TAG;
|
||||
fi;
|
||||
if [ ! -z "$ASSETS" ]; then
|
||||
for asset in ${ASSETS[@]}; do
|
||||
asset_id=$(echo ${asset} | cut -d ':' -f 1);
|
||||
asset_name=$(echo ${asset} | cut -d ':' -f 2);
|
||||
if [ "${NAME}" == "${asset_name}" ]; then
|
||||
echo -e "\nAsset ${BOLD}${BLUE}${NAME}${TEXTRESET} already exists in ${YELLOW}${REPO}${TEXTRESET}@${BOLD}${YELLOW}${TAG}${TEXTRESET}... deleting id ${BOLD}${RED}${asset_id}${TEXTRESET}...";
|
||||
curl -X DELETE --fail --user "${GH_API_USER}" "https://api.github.com/repos/${GH_USER}/${REPO}/releases/assets/${asset_id}";
|
||||
fi;
|
||||
done;
|
||||
fi;
|
||||
echo "Uploading package ${BOLD}${BLUE}${NAME}${TEXTRESET} to ${YELLOW}${REPO}${TEXTRESET}@${YELLOW}${TAG}${TEXTRESET}";
|
||||
github-release upload --security-token $GH_TOKEN --user ${GH_USER} --repo $REPO --tag $TAG --name "$NAME" --file "$file";
|
||||
done;
|
||||
fi
|
||||
351
CHANGELOG.md
351
CHANGELOG.md
@@ -1,4 +1,335 @@
|
||||
# v0.9.8 beta
|
||||
# v0.9.29
|
||||
## 06/22/2015
|
||||
|
||||
1. [](#new)
|
||||
* New and improved Regex-powered redirect and route alias logic
|
||||
* Added new `onBuildPagesInitialized` event for memory critical or time-consuming plugins
|
||||
* Added a `setSummary()` method for pages
|
||||
2. [](#improved)
|
||||
* Improved `MergeConfig()` logic for more control
|
||||
* Travis skeleton build trigger implemented
|
||||
* Set composer.json versions to stable versions where possible
|
||||
* Disabled `last_modified` and `etag` page headers by default (causing too much page caching)
|
||||
3. [](#bugfix)
|
||||
* Preload classes during `bin/gpm selfupgrade` to avoid issues with updated classes
|
||||
* Fix for directory relative _down_ links
|
||||
|
||||
# v0.9.28
|
||||
## 06/16/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added method to set raw markdown on a page
|
||||
* Added ability to enabled system and page level `etag` and `last_modified` headers
|
||||
2. [](#improved)
|
||||
* Improved image path processing
|
||||
* Improved query string handling
|
||||
* Optimization to image handling supporting URL encoded filenames
|
||||
* Use global `composer` when available rather than Grv provided one
|
||||
* Use `PHP_BINARY` contant rather than `php` executable
|
||||
* Updated Doctrine Cache library
|
||||
* Updated Symfony libraries
|
||||
* Moved `convertUrl()` method to Uri object
|
||||
3. [](#bugfix)
|
||||
* Fix incorrect slug causing problems with CLI `uninstall`
|
||||
* Fix Twig runtime error with assets pipeline in sufolder installations
|
||||
* Fix for `+` in image filenames
|
||||
* Fix for dot files causing issues with page processing
|
||||
* Fix for Uri path detection on Windows platform
|
||||
* Fix for atlernative media resolutions
|
||||
* Fix for modularTypes key properties
|
||||
|
||||
# v0.9.27
|
||||
## 05/09/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added new composer CLI command
|
||||
* Added page-level summary header overrides
|
||||
* Added `size` back for Media objects
|
||||
* Refactored Backup command in preparation for admin plugin
|
||||
* Added a new `parseLinks` method to Plugins class
|
||||
* Added `starts_with` and `ends_with` Twig filters
|
||||
2. [](#improved)
|
||||
* Opitmized install of vendor libraries for speed improvement
|
||||
* Improved configuration handling in preparation for admin plugin
|
||||
* Cache optimization: Don't cache Twig templates when you pass dynamic params
|
||||
* Moved `Utils::rcopy` to `Folder::rcopy`
|
||||
* Improved `Folder::doDelete`
|
||||
* Added check for required Curl in GPM
|
||||
* Updated included composer.phar to latest version
|
||||
* Various blueprint fixes for admin plugin
|
||||
* Various PSR and code cleanup tasks
|
||||
3. [](#bugfix)
|
||||
* Fix issue with Gzip not working with `onShutDown()` event
|
||||
* Fix for URLs with trailing slashes
|
||||
* Handle condition where certain errors resulted in blank page
|
||||
* Fix for issue with theme name equal to base_url and asset pipeline
|
||||
* Fix to properly nomralize font rewrite path
|
||||
* Fix for absolute URLs below the current page
|
||||
* Fix for `..` page references
|
||||
|
||||
# v0.9.26
|
||||
## 04/24/2015
|
||||
|
||||
3. [](#bugfix)
|
||||
* Fixed issue with homepage routes failing with 'dirname' error
|
||||
|
||||
# v0.9.25
|
||||
## 04/24/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added support for E-Tag, Last-Modified, Cache-Control and Page-based expires headers
|
||||
2. [](#improved)
|
||||
* Refactored media image handling to make it more flexible and support absolute paths
|
||||
* Refactored page modification check process to make it faster
|
||||
* User account improvements in preparation for Admin plugin
|
||||
* Protect against timing attacks
|
||||
* Reset default system expires time to 0 seconds (can override if you need to)
|
||||
3. [](#bugfix)
|
||||
* Fix issues with spaces in webroot when using `bin/grav install`
|
||||
* Fix for spaces in relative directory
|
||||
* Bug fix in collection filtering
|
||||
|
||||
# v0.9.24
|
||||
## 04/15/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added support for chunked downloads of Assets
|
||||
* Added new `onBeforeDownload()` event
|
||||
* Added new `download()` and `getMimeType()` methods to Utils class
|
||||
* Added configuration option for supported page types
|
||||
* Added assets and media timestamp options (off by default)
|
||||
* Added page expires configuration option
|
||||
2. [](#bugfix)
|
||||
* Fixed issue with Nginx/Gzip and `ob_flush()` throwing error
|
||||
* Fixed assets actions on 'direct media' URLs
|
||||
* Fix for 'direct assets` with any parameters
|
||||
|
||||
# v0.9.23
|
||||
## 04/09/2015
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fix for broken GPM `selfupgrade` (Grav 0.9.21 and 0.9.22 will need to manually upgrade to this version)
|
||||
|
||||
# v0.9.22
|
||||
## 04/08/2015
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fix to normalize GRAV_ROOT path for Windows
|
||||
* Fix to normalize Media image paths for Windows
|
||||
* Fix for GPM `selfupgrade` when you are on latest version
|
||||
|
||||
# v0.9.21
|
||||
## 04/07/2015
|
||||
|
||||
1. [](#new)
|
||||
* Major Media functionality enhancements: SVG, Animated GIF, Video support!
|
||||
* Added ability to configure default image quality in system configuration
|
||||
* Added `sizes` attributes for custom retina image breakpoints
|
||||
2. [](#improved)
|
||||
* Don't scale @1x retina images
|
||||
* Add filter to Iterator class
|
||||
* Updated various composer packages
|
||||
* Various PSR fixes
|
||||
|
||||
# v0.9.20
|
||||
## 03/24/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added `addAsyncJs()` and `addDeferJs()` to Assets manager
|
||||
* Added support for extranal URL redirects
|
||||
2. [](#improved)
|
||||
* Fix unpredictable asset ordering when set from plugin/system
|
||||
* Updated `nginx.conf` to ensure system assets are accessible
|
||||
* Ensure images are served as static files in Nginx
|
||||
* Updated vendor libraries to latest versions
|
||||
* Updated included composer.phar to latest version
|
||||
3. [](#bugfix)
|
||||
* Fixed issue with markdown links to `#` breaking HTML
|
||||
|
||||
# v0.9.19
|
||||
## 02/28/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added named assets capability and bundled jQuery into Grav core
|
||||
* Added `first()` and `last()` to `Iterator` class
|
||||
2. [](#improved)
|
||||
* Improved page modification routine to skip _dot files_
|
||||
* Only use files to calculate page modification dates
|
||||
* Broke out Folder iterators into their own classes
|
||||
* Various Sensiolabs Insight fixes
|
||||
3. [](#bugfix)
|
||||
* Fixed `Iterator.nth()` method
|
||||
|
||||
# v0.9.18
|
||||
## 02/19/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added ability for GPM `install` to automatically install `_demo` content if found (w/backup)
|
||||
* Added ability for themes and plugins to have dependencies required to install via GPM
|
||||
* Added ability to override the system timezone rather than relying on server setting only
|
||||
* Added new Twig filter `random_string` for generating random id values
|
||||
* Added new Twig filter `markdown` for on-the-fly markdown processing
|
||||
* Added new Twig filter `absoluteUrl` to convert relative to absolute URLs
|
||||
* Added new `processTemplate()` method to Twig object for on-the-fly processing of twig template
|
||||
* Added `rcopy()` and `contains()` helper methods in Utils
|
||||
2. [](#improved)
|
||||
* Provided new `param_sep` variable to better support Apache on Windows
|
||||
* Moved parsedown configuration into the trait
|
||||
* Added optional **deep-copy** option to `mergeConfig()` for plugins
|
||||
* Updated bundled `composer.phar` package
|
||||
* Various Sensiolabs Insight fixes - Silver level now!
|
||||
* Various PSR Fixes
|
||||
3. [](#bugfix)
|
||||
* Fix for windows platforms not displaying installed themes/plugins via GPM
|
||||
* Fix page IDs not picking up folder-only pages
|
||||
|
||||
# v0.9.17
|
||||
## 02/05/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added **full HHVM support!** Get your speed on with Facebook's crazy fast PHP JIT compiler
|
||||
2. [](#improved)
|
||||
* More flexible page summary control
|
||||
* Support **CamelCase** plugin and theme class names. Replaces dashes and underscores
|
||||
* Moved summary delimiter into `site.yaml` so it can be configurable
|
||||
* Various PSR fixes
|
||||
3. [](#bugfix)
|
||||
* Fix for `mergeConfig()` not falling back to defaults
|
||||
* Fix for `addInlineCss()` and `addInlineJs()` Assets not working between Twig tags
|
||||
* Fix for Markdown adding HTML tags into inline CSS and JS
|
||||
|
||||
# v0.9.16
|
||||
## 01/30/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added **Retina** and **Responsive** image support via Grav media and `srcset` image attribute
|
||||
* Added image debug option that overlays responsive resolution
|
||||
* Added a new image cache stream
|
||||
2. [](#improved)
|
||||
* Improved the markdown Lightbox functionality to better mimic Twig version
|
||||
* Fullsize Lightbox can now have filters applied
|
||||
* Added a new `mergeConfig()` method to Plugin class to merge system + page header configuration
|
||||
* Added a new `disable()` method to Plugin class to programatically disable a plugin
|
||||
* Updated Parsedown and Parsedown Extra to address bugs
|
||||
* Various PSR fixes
|
||||
3. [](#bugfix)
|
||||
* Fix bug with image dispatch in traditionally _non-routable_ pages
|
||||
* Fix for markdown link not working on non-current pages
|
||||
* Fix for markdown images not being found on homepage
|
||||
|
||||
# v0.9.15
|
||||
## 01/23/2015
|
||||
|
||||
3. [](#bugfix)
|
||||
* Typo in video mime types
|
||||
* Fix for old `markdown_extra` system setting not getting picked up
|
||||
* Fix in regex for Markdown links with numeric values in path
|
||||
* Fix for broken image routing mechanism that got broken at some point
|
||||
* Fix for markdown images/links in pages with page slug override
|
||||
|
||||
# v0.9.14
|
||||
## 01/23/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added **GZip** support
|
||||
* Added multiple configurations via `setup.php`
|
||||
* Added base structure for unit tests
|
||||
* New `onPageContentRaw()` plugin event that processes before any page processing
|
||||
* Added ability to dynamically set Metadata on page
|
||||
* Added ability to dynamically configure Markdown processing via Parsedown options
|
||||
2. [](#improved)
|
||||
* Refactored `page.content()` method to be more flexible and reliable
|
||||
* Various updates and fixes for streams resulting in better multi-site support
|
||||
* Updated Twig, Parsedown, ParsedownExtra, DoctrineCache libraries
|
||||
* Refactored Parsedown trait
|
||||
* Force modular pages to be non-visible in menus
|
||||
* Moved RewriteBase before Exploits in `.htaccess`
|
||||
* Added standard video formats to Media support
|
||||
* Added priority for inline assets
|
||||
* Check for uniqueness when adding multiple inline assets
|
||||
* Improved support for Twig-based URLs inside Markdown links and images
|
||||
* Improved Twig `url()` function
|
||||
3. [](#bugfix)
|
||||
* Fix for HTML entities quotes in Metadata values
|
||||
* Fix for `published` setting to have precedent of `publish_date` and `unpublish_date`
|
||||
* Fix for `onShutdown()` events not closing connections properly in **php-fpm** environments
|
||||
|
||||
# v0.9.13
|
||||
## 01/09/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added new published `true|false` state in page headers
|
||||
* Added `publish_date` in page headers to automatically publish page
|
||||
* Added `unpublish_date` in page headers to automatically unpublish page
|
||||
* Added `dateRange()` capability for collections
|
||||
* Added ability to dynamically control Cache lifetime programatically
|
||||
* Added ability to sort by anything in the page header. E.g. `sort: header.taxonomy.year`
|
||||
* Added various helper methods to collections: `copy, nonVisible, modular, nonModular, published, nonPublished, nonRoutable`
|
||||
2. [](#improved)
|
||||
* Modified all Collection methods so they can be chained together: `$collection->published()->visible()`
|
||||
* Set default Cache lifetime to default of 1 week (604800 seconds) - was infinite
|
||||
* House-cleaning of some unused methods in Pages object
|
||||
3. [](#bugfix)
|
||||
* Fix `uninstall` GPM command that was broken in last release
|
||||
* Fix for intermittent `undefined index` error when working with Collections
|
||||
* Fix for date of some pages being set to incorrect future timestamps
|
||||
|
||||
# v0.9.12
|
||||
## 01/06/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added an all-access robots.txt file for search engines
|
||||
* Added new GPM `uninstall` command
|
||||
* Added support for **in-page** Twig processing in **modular** pages
|
||||
* Added configurable support for `undefined` Twig functions and filters
|
||||
2. [](#improved)
|
||||
* Fall back to default `.html` template if error occurs on non-html pages
|
||||
* Added ability to have PSR-1 friendly plugin names (CamelCase, no-dashes)
|
||||
* Fix to `composer.json` to deter API rate-limit errors
|
||||
* Added **non-exception-throwing** handler for undefined methods on `Medium` objects
|
||||
3. [](#bugfix)
|
||||
* Fix description for `self-upgrade` method of GPM command
|
||||
* Fix for incorrect version number when performing GPM `update`
|
||||
* Fix for argument description of GPM `install` command
|
||||
* Fix for recalcitrant CodeKit mac application
|
||||
|
||||
# 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)
|
||||
@@ -14,7 +345,7 @@
|
||||
3. [](#bugfix)
|
||||
* Fix issue with miscalculation of blog separator location `===`
|
||||
|
||||
# v0.9.7 beta
|
||||
# v0.9.7
|
||||
## 11/24/2014
|
||||
|
||||
1. [](#improved)
|
||||
@@ -27,7 +358,7 @@
|
||||
* 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 beta
|
||||
# v0.9.6
|
||||
## 11/17/2014
|
||||
|
||||
1. [](#improved)
|
||||
@@ -42,7 +373,7 @@
|
||||
* Fix for relative URLs in markdown on installs with no base_url
|
||||
* Fix for page media images with uppercase extension
|
||||
|
||||
# v0.9.5 beta
|
||||
# v0.9.5
|
||||
## 11/09/2014
|
||||
|
||||
1. [](#new)
|
||||
@@ -65,7 +396,7 @@
|
||||
* Fix for Data URLs in CSS being badly formed
|
||||
* Fix Markdown links with fragment and query elements
|
||||
|
||||
# v0.9.4 beta
|
||||
# v0.9.4
|
||||
## 10/29/2014
|
||||
|
||||
1. [](#new)
|
||||
@@ -90,7 +421,7 @@
|
||||
* Fix potential double // in assets
|
||||
* Load debugger as early as possible
|
||||
|
||||
# v0.9.3 beta
|
||||
# v0.9.3
|
||||
## 10/09/2014
|
||||
|
||||
1. [](#new)
|
||||
@@ -118,7 +449,7 @@
|
||||
* Switched debugger to PRODUCTION mode by default
|
||||
* Various fixes in URI class for increased reliability
|
||||
|
||||
# v0.9.2 beta
|
||||
# v0.9.2
|
||||
## 09/15/2014
|
||||
|
||||
1. [](#new)
|
||||
@@ -151,7 +482,7 @@
|
||||
* Fix broken password validation
|
||||
* Back to proper PSR-4 Autoloader
|
||||
|
||||
# v0.9.1 beta
|
||||
# v0.9.1
|
||||
## 09/02/2014
|
||||
|
||||
1. [](#new)
|
||||
@@ -172,7 +503,7 @@
|
||||
* Fixed template inheritance
|
||||
* Moved Browser class to proper location
|
||||
|
||||
# v0.9.0 beta
|
||||
# v0.9.0
|
||||
## 08/25/2014
|
||||
|
||||
1. [](#new)
|
||||
@@ -200,7 +531,7 @@
|
||||
* Various minor bug fixes
|
||||
|
||||
|
||||
# v0.8.0 beta
|
||||
# v0.8.0
|
||||
## 08/13/2014
|
||||
|
||||
1. [](#new)
|
||||
|
||||
35
README.md
35
README.md
@@ -1,9 +1,10 @@
|
||||
#  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 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.
|
||||
[](https://insight.sensiolabs.com/projects/cfd20465-d0f8-4a0a-8444-467f5b5f16ad) [](https://gitter.im/getgrav/grav?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
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:
|
||||
Grav is a **Fast**, **Simple**, and **Flexible**, file-based Web-platform. There is **Zero** installation required. Just extract the ZIP archive, and you are already up and running. It follows similar principles to other flat-file CMS platforms, but has a different design philosophy than most. Grav comes with a powerful **Package Management System** to allow for simple installation and upgrading of plugins and themes, as well as simple updating of Grav itself.
|
||||
|
||||
The underlying architecture of Grav is designed to use well-established and _best-in-class_ technologies, to ensure that Grav is simple to use and easy to extend. Some of these key technologies include:
|
||||
|
||||
* [Twig Templating](http://twig.sensiolabs.org/): for powerful control of the user interface
|
||||
* [Markdown](http://en.wikipedia.org/wiki/Markdown): for easy content creation
|
||||
@@ -39,6 +40,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.
|
||||
|
||||
0
backup/.gitkeep
Normal file
0
backup/.gitkeep
Normal file
Binary file not shown.
14
bin/gpm
14
bin/gpm
@@ -6,10 +6,17 @@ if (version_compare($ver = PHP_VERSION, $req = '5.4.0', '<')) {
|
||||
exit(sprintf("You are running PHP %s, but Grav needs at least PHP %s to run.\n", $ver, $req));
|
||||
}
|
||||
|
||||
if (!file_exists(__DIR__ . '/../vendor')){
|
||||
require_once __DIR__ . '/../system/src/Grav/Common/Composer.php';
|
||||
}
|
||||
|
||||
use Grav\Common\Composer;
|
||||
|
||||
if (!file_exists(__DIR__ . '/../vendor')){
|
||||
// Before we can even start, we need to run composer first
|
||||
$composer = Composer::getComposerExecutor();
|
||||
echo "Preparing to install vendor dependencies...\n\n";
|
||||
echo system('php bin/composer.phar --working-dir="'.__DIR__.'/../" --no-interaction install');
|
||||
echo system($composer.' --working-dir="'.__DIR__.'/../" --no-interaction --no-dev --prefer-dist -o install');
|
||||
echo "\n\n";
|
||||
}
|
||||
|
||||
@@ -26,6 +33,10 @@ if (!file_exists(ROOT_DIR . 'index.php')) {
|
||||
exit('FATAL: Must be run from ROOT directory of Grav!');
|
||||
}
|
||||
|
||||
if (!function_exists('curl_version')) {
|
||||
exit('FATAL: GPM requires PHP Curl module to be installed');
|
||||
}
|
||||
|
||||
$grav = Grav::instance(array('loader' => $autoload));
|
||||
$grav['config']->init();
|
||||
$grav['streams'];
|
||||
@@ -38,6 +49,7 @@ $app->addCommands(array(
|
||||
new \Grav\Console\Gpm\VersionCommand(),
|
||||
new \Grav\Console\Gpm\InfoCommand(),
|
||||
new \Grav\Console\Gpm\InstallCommand(),
|
||||
new \Grav\Console\Gpm\UninstallCommand(),
|
||||
new \Grav\Console\Gpm\UpdateCommand(),
|
||||
new \Grav\Console\Gpm\SelfupgradeCommand(),
|
||||
));
|
||||
|
||||
10
bin/grav
10
bin/grav
@@ -6,10 +6,17 @@ if (version_compare($ver = PHP_VERSION, $req = '5.4.0', '<')) {
|
||||
exit(sprintf("You are running PHP %s, but Grav needs at least PHP %s to run.\n", $ver, $req));
|
||||
}
|
||||
|
||||
if (!file_exists(__DIR__ . '/../vendor')){
|
||||
require_once __DIR__ . '/../system/src/Grav/Common/Composer.php';
|
||||
}
|
||||
|
||||
use Grav\Common\Composer;
|
||||
|
||||
if (!file_exists(__DIR__ . '/../vendor')){
|
||||
// Before we can even start, we need to run composer first
|
||||
$composer = Composer::getComposerExecutor();
|
||||
echo "Preparing to install vendor dependencies...\n\n";
|
||||
echo system('php bin/composer.phar --working-dir="'.__DIR__.'/../" --no-interaction install');
|
||||
echo system($composer.' --working-dir="'.__DIR__.'/../" --no-interaction --no-dev --prefer-dist -o install');
|
||||
echo "\n\n";
|
||||
}
|
||||
|
||||
@@ -28,6 +35,7 @@ if (!file_exists(ROOT_DIR . 'index.php')) {
|
||||
$app = new Application('Grav CLI Application', '0.1.0');
|
||||
$app->addCommands(array(
|
||||
new Grav\Console\Cli\InstallCommand(),
|
||||
new Grav\Console\Cli\ComposerCommand(),
|
||||
new Grav\Console\Cli\SandboxCommand(),
|
||||
new Grav\Console\Cli\CleanCommand(),
|
||||
new Grav\Console\Cli\ClearCacheCommand(),
|
||||
|
||||
@@ -8,27 +8,21 @@
|
||||
"require": {
|
||||
"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",
|
||||
"doctrine/cache": "~1.3",
|
||||
"erusev/parsedown-extra": "~0.7",
|
||||
"symfony/yaml": "2.7.*",
|
||||
"symfony/console": "2.7.*",
|
||||
"symfony/event-dispatcher": "2.7.*",
|
||||
"doctrine/cache": "~1.4",
|
||||
"maximebf/debugbar": "dev-master",
|
||||
"filp/whoops": "1.2.*@dev",
|
||||
"monolog/monolog": "~1.1",
|
||||
"gregwar/image": "~2.0",
|
||||
"monolog/monolog": "~1.0",
|
||||
"gregwar/image": "~2.0",
|
||||
"ircmaxell/password-compat": "1.0.*",
|
||||
"mrclay/minify": "dev-master",
|
||||
"donatj/phpuseragentparser": "dev-master",
|
||||
"mrclay/minify": "~2.2",
|
||||
"donatj/phpuseragentparser": "~0.3",
|
||||
"pimple/pimple": "~3.0",
|
||||
"rockettheme/toolbox": "dev-develop"
|
||||
"rockettheme/toolbox": "1.0.*"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/rockettheme/toolbox"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Grav\\": "system/src/Grav"
|
||||
|
||||
22
htaccess.txt
22
htaccess.txt
@@ -2,6 +2,17 @@
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
## 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 - 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
|
||||
@@ -19,17 +30,6 @@ 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
|
||||
|
||||
13
index.php
13
index.php
@@ -2,12 +2,13 @@
|
||||
namespace Grav;
|
||||
|
||||
if (version_compare($ver = PHP_VERSION, $req = '5.4.0', '<')) {
|
||||
exit(sprintf('You are running PHP %s, but Grav needs at least <strong>PHP %s</strong> to run.', $ver, $req));
|
||||
throw new \RuntimeException(sprintf('You are running PHP %s, but Grav needs at least <strong>PHP %s</strong> to run.', $ver, $req));
|
||||
}
|
||||
|
||||
// Ensure vendor libraries exist
|
||||
$autoload = __DIR__ . '/vendor/autoload.php';
|
||||
if (!is_file($autoload)) {
|
||||
exit('Please run: <i>bin/grav install</i>');
|
||||
throw new \RuntimeException("Please run: <i>bin/grav install</i>");
|
||||
}
|
||||
|
||||
use Grav\Common\Grav;
|
||||
@@ -15,19 +16,19 @@ use Grav\Common\Grav;
|
||||
// Register the auto-loader.
|
||||
$loader = require_once $autoload;
|
||||
|
||||
if (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('UTC');
|
||||
}
|
||||
// Set timezone to default, falls back to system if php.ini not set
|
||||
date_default_timezone_set(@date_default_timezone_get());
|
||||
|
||||
// Get the Grav instance
|
||||
$grav = Grav::instance(
|
||||
array(
|
||||
'loader' => $loader
|
||||
)
|
||||
);
|
||||
|
||||
// Process the page
|
||||
try {
|
||||
$grav->process();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$grav->fireEvent('onFatalException');
|
||||
throw $e;
|
||||
|
||||
41
nginx.conf
41
nginx.conf
@@ -26,27 +26,35 @@ http {
|
||||
if (!-e $request_filename){ rewrite ^(.*)$ /index.php last; }
|
||||
}
|
||||
|
||||
location /images/ {
|
||||
# Serve images as static
|
||||
}
|
||||
|
||||
location /user {
|
||||
rewrite ^/user/accounts/(.*)$ /error redirect;
|
||||
rewrite ^/user/config/(.*)$ /error redirect;
|
||||
rewrite ^/user/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
|
||||
}
|
||||
rewrite ^/user/accounts/(.*)$ /error redirect;
|
||||
rewrite ^/user/config/(.*)$ /error redirect;
|
||||
rewrite ^/user/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
|
||||
}
|
||||
|
||||
location /cache {
|
||||
rewrite ^/cache/(.*) /error redirect;
|
||||
}
|
||||
location /cache {
|
||||
rewrite ^/cache/(.*) /error redirect;
|
||||
}
|
||||
|
||||
location /bin {
|
||||
rewrite ^/bin/(.*)$ /error redirect;
|
||||
}
|
||||
location /bin {
|
||||
rewrite ^/bin/(.*)$ /error redirect;
|
||||
}
|
||||
|
||||
location /system {
|
||||
rewrite ^/system/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
|
||||
}
|
||||
location /backup {
|
||||
rewrite ^/backup/(.*) /error redirect;
|
||||
}
|
||||
|
||||
location /vendor {
|
||||
rewrite ^/vendor/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
|
||||
}
|
||||
location /system {
|
||||
rewrite ^/system/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
|
||||
}
|
||||
|
||||
location /vendor {
|
||||
rewrite ^/vendor/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
|
||||
}
|
||||
|
||||
# Remember to change 127.0.0.1:9000 to the Ip/port
|
||||
# you configured php-cgi.exe to run from
|
||||
@@ -60,7 +68,6 @@ http {
|
||||
include fastcgi_params;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
2
robots.txt
Normal file
2
robots.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow:
|
||||
4
system/assets/jquery/jquery-2.1.3.min.js
vendored
Normal file
4
system/assets/jquery/jquery-2.1.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
system/assets/responsive-overlays/1x.png
Normal file
BIN
system/assets/responsive-overlays/1x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
BIN
system/assets/responsive-overlays/2x.png
Normal file
BIN
system/assets/responsive-overlays/2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
BIN
system/assets/responsive-overlays/3x.png
Normal file
BIN
system/assets/responsive-overlays/3x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
system/assets/responsive-overlays/4x.png
Normal file
BIN
system/assets/responsive-overlays/4x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
system/assets/responsive-overlays/unknown.png
Normal file
BIN
system/assets/responsive-overlays/unknown.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
@@ -24,6 +24,8 @@ form:
|
||||
type: text
|
||||
size: large
|
||||
label: Default Email
|
||||
validate:
|
||||
type: email
|
||||
|
||||
taxonomies:
|
||||
type: text
|
||||
|
||||
@@ -27,7 +27,7 @@ form:
|
||||
label: Default Theme
|
||||
help: "Set the theme (defaults to 'default')"
|
||||
|
||||
pages.markdown_extra:
|
||||
pages.markdown.extra:
|
||||
type: toggle
|
||||
label: Markdown Extra
|
||||
highlight: 1
|
||||
@@ -101,6 +101,16 @@ form:
|
||||
validate:
|
||||
type: number
|
||||
min: 1
|
||||
pages.publish_dates:
|
||||
type: toggle
|
||||
label: Date-based publishing
|
||||
help: Automatically (un)publish posts based on their date
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
|
||||
|
||||
@@ -175,6 +185,36 @@ form:
|
||||
label: Cache Prefix
|
||||
placeholder: "Derived from base URL (override by entering random string)"
|
||||
|
||||
cache.gzip:
|
||||
type: toggle
|
||||
label: GZIP compression
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
assets.enable_asset_timestamp:
|
||||
type: toggle
|
||||
label: Enable timestamps on assets
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
media.enable_media_timestamp:
|
||||
type: toggle
|
||||
label: Enable timestamps on media
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
twig:
|
||||
type: section
|
||||
title: Twig Templating
|
||||
@@ -287,6 +327,32 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
errors:
|
||||
type: section
|
||||
title: Error handler
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
errors.display:
|
||||
type: toggle
|
||||
label: Display errors
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
errors.log:
|
||||
type: toggle
|
||||
label: Log errors
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
debugger:
|
||||
type: section
|
||||
title: Debugger
|
||||
@@ -362,3 +428,59 @@ form:
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
media:
|
||||
type: section
|
||||
title: Media
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
images.default_image_quality:
|
||||
type: text
|
||||
label: Default image quality
|
||||
classes: x-small
|
||||
validate:
|
||||
type: number
|
||||
min: 1
|
||||
max: 100
|
||||
|
||||
images.debug:
|
||||
type: toggle
|
||||
label: Image debug watermark
|
||||
highlight: 1
|
||||
options:
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
media.upload_limit:
|
||||
type: text
|
||||
label: File upload limit
|
||||
classes: small
|
||||
validate:
|
||||
type: number
|
||||
|
||||
system:
|
||||
type: section
|
||||
title: System
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
timezone:
|
||||
type: select
|
||||
label: Timezone
|
||||
classes: fancy
|
||||
@data-options: '\Grav\Common\Utils::timezones'
|
||||
default: ''
|
||||
options:
|
||||
'': '- None -'
|
||||
|
||||
param_sep:
|
||||
type: select
|
||||
label: Parameter separator
|
||||
classes: fancy
|
||||
default: ''
|
||||
options:
|
||||
':': ': (default)'
|
||||
';': '; (use this for apache on Windows)'
|
||||
|
||||
@@ -61,7 +61,7 @@ form:
|
||||
@data-options: '\Grav\Common\Page\Pages::parents'
|
||||
@data-default: '\Grav\Plugin\admin::route'
|
||||
options:
|
||||
'/': '- Root -'
|
||||
'': '- Select -'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -28,8 +28,6 @@ form:
|
||||
validate:
|
||||
required: true
|
||||
|
||||
|
||||
|
||||
fullname:
|
||||
type: text
|
||||
size: large
|
||||
@@ -41,46 +39,3 @@ form:
|
||||
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
|
||||
|
||||
@@ -20,10 +20,15 @@ png:
|
||||
thumb: media/thumb-png.png
|
||||
mime: image/png
|
||||
gif:
|
||||
type: image
|
||||
type: animated
|
||||
thumb: media/thumb-gif.png
|
||||
mime: image/gif
|
||||
|
||||
svg:
|
||||
type: vector
|
||||
thumb: media/thumb-gif.png
|
||||
mime: image/svg+xml
|
||||
|
||||
mp4:
|
||||
type: video
|
||||
thumb: media/thumb-mp4.png
|
||||
@@ -40,6 +45,31 @@ swf:
|
||||
type: video
|
||||
thumb: media/thumb-swf.png
|
||||
mime: video/x-flv
|
||||
flv:
|
||||
type: video
|
||||
thumb: media/thumb-flv.png
|
||||
mime: video/x-flv
|
||||
|
||||
mp3:
|
||||
type: audio
|
||||
thumb: media/thumb-mp3.png
|
||||
mime: audio/mp3
|
||||
ogg:
|
||||
type: audio
|
||||
thumb: media/thumb-ogg.png
|
||||
mime: audio/ogg
|
||||
wma:
|
||||
type: audio
|
||||
thumb: media/thumb-wma.png
|
||||
mime: audio/wma
|
||||
m4a:
|
||||
type: audio
|
||||
thumb: media/thumb-m4a.png
|
||||
mime: audio/m4a
|
||||
wav:
|
||||
type: audio
|
||||
thumb: media/thumb-wav.png
|
||||
mime: audio/wav
|
||||
|
||||
txt:
|
||||
type: file
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
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
|
||||
|
||||
blog:
|
||||
route: '/blog' # Route to blog
|
||||
route: '/blog' # Route to blog (deprecated)
|
||||
|
||||
metadata:
|
||||
description: 'My Grav Site' # Site description
|
||||
description: 'My Grav Site' # Site description
|
||||
|
||||
summary:
|
||||
enabled: true # enable or disable summary of page
|
||||
format: short # long = summary delimiter will be ignored; short = use the first occurence of delimter or size
|
||||
size: 300 # Maximum length of summary (characters)
|
||||
delimiter: === # The summary delimiter
|
||||
|
||||
routes:
|
||||
/something/else: '/blog/sample-3' # Alias for /blog/sample-3
|
||||
/another/one/here: '/blog/sample-3' # Another alias for /blog/sample-3
|
||||
/new/*: '/blog/*' # Wildcard any /new/my-page URL to /blog/my-page Route
|
||||
|
||||
#menu: # Sample Menu Example
|
||||
# - text: Source
|
||||
# icon: github
|
||||
# url: https://github.com/getgrav/grav
|
||||
# - icon: twitter
|
||||
# url: http://twitter.com/getgrav
|
||||
|
||||
@@ -8,6 +8,7 @@ schemes:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user://images
|
||||
- system://images
|
||||
|
||||
page:
|
||||
type: ReadOnlyStream
|
||||
|
||||
@@ -1,25 +1,39 @@
|
||||
absolute_urls: false # Absolute or relative URLs for `base_url`
|
||||
timezone: '' # Valid values: http://php.net/manual/en/timezones.php
|
||||
param_sep: ':' # Parameter separator, use ';' for Apache on windows
|
||||
|
||||
home:
|
||||
alias: '/home' # Default path for home, ie /
|
||||
|
||||
pages:
|
||||
theme: antimatter # Default theme (defaults to "antimatter" theme)
|
||||
markdown_extra: false # Enable support for Markdown Extra support (GFM by default)
|
||||
order:
|
||||
by: defaults # Order pages by "default", "alpha" or "date"
|
||||
by: default # Order pages by "default", "alpha" or "date"
|
||||
dir: asc # Default ordering direction, "asc" or "desc"
|
||||
list:
|
||||
count: 20 # Default item count per page
|
||||
dateformat:
|
||||
short: 'jS M Y' # Short date format
|
||||
long: 'F jS \a\t g:ia' # Long date format
|
||||
publish_dates: true # automatically publish/unpublish based on dates
|
||||
process:
|
||||
markdown: true # Process Markdown
|
||||
twig: false # Process Twig
|
||||
events:
|
||||
page: true # Enable page level events
|
||||
twig: true # Enable twig level events
|
||||
markdown:
|
||||
extra: false # Enable support for Markdown Extra support (GFM by default)
|
||||
auto_line_breaks: false # Enable automatic line breaks
|
||||
auto_url_links: false # Enable automatic HTML links
|
||||
escape_markup: false # Escape markup tags into entities
|
||||
special_chars: # List of special characters to automatically convert to entities
|
||||
'>': 'gt'
|
||||
'<': 'lt'
|
||||
types: 'txt|xml|html|json|rss|atom' # Pipe separated list of valid page types
|
||||
expires: 0 # Page expires time in seconds (604800 seconds = 7 days)
|
||||
last_modified: false # Set the last modified date header based on file modifcation timestamp
|
||||
etag: false # Set the etag header tag
|
||||
|
||||
cache:
|
||||
enabled: true # Set to true to enable caching
|
||||
@@ -27,13 +41,17 @@ cache:
|
||||
method: file # Method to check for updates in pages: file|folder|none
|
||||
driver: auto # One of: auto|file|apc|xcache|memcache|wincache
|
||||
prefix: 'g' # Cache prefix string (prevents cache conflicts)
|
||||
lifetime: 0 # Lifetime of cached data in seconds (0 = infinite)
|
||||
lifetime: 604800 # Lifetime of cached data in seconds (0 = infinite)
|
||||
gzip: false # GZip compress the page output
|
||||
|
||||
|
||||
twig:
|
||||
cache: true # Set to true to enable twig caching
|
||||
debug: false # Enable Twig debug
|
||||
auto_reload: true # Refresh cache on changes
|
||||
autoescape: false # Autoescape Twig vars
|
||||
undefined_functions: true # Allow undefined functions
|
||||
undefined_filters: true # Allow undefined filters
|
||||
|
||||
assets: # Configuration for Assets Manager (JS, CSS)
|
||||
css_pipeline: false # The CSS pipeline is the unification of multiple CSS resources into one file
|
||||
@@ -42,6 +60,9 @@ assets: # Configuration for Assets Manager (JS, C
|
||||
css_rewrite: true # Rewrite any CSS relative URLs during pipelining
|
||||
js_pipeline: false # The JS pipeline is the unification of multiple JS resources into one file
|
||||
js_minify: true # Minify the JS during pipelining
|
||||
enable_asset_timestamp: false # Enable asset timetsamps
|
||||
collections:
|
||||
jquery: system://assets/jquery/jquery-2.1.3.min.js
|
||||
|
||||
errors:
|
||||
display: true # Display full backtrace-style error page
|
||||
@@ -52,3 +73,14 @@ debugger:
|
||||
twig: true # Enable debugging of Twig templates
|
||||
shutdown:
|
||||
close_connection: true # Close the connection before calling onShutdown(). false for debugging
|
||||
|
||||
images:
|
||||
default_image_quality: 85 # Default image quality to use when resampling images (85%)
|
||||
debug: false # Show an overlay over images indicating the pixel depth of the image when working with retina for example
|
||||
|
||||
media:
|
||||
enable_media_timestamp: false # Enable media timetsamps
|
||||
upload_limit: 0 # Set maximum upload size in bytes (0 is unlimited)
|
||||
|
||||
security:
|
||||
default_hash: $2y$10$kwsyMVwM8/7j0K/6LHT.g.Fs49xOCTp2b8hh/S5.dPJuJcJB6T.UK
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
// Some standard defines
|
||||
define('GRAV', true);
|
||||
define('GRAV_VERSION', '0.9.8');
|
||||
define('GRAV_VERSION', '0.9.29');
|
||||
define('DS', '/');
|
||||
|
||||
// Directories and Paths
|
||||
if (!defined('GRAV_ROOT')) {
|
||||
define('GRAV_ROOT', getcwd());
|
||||
define('GRAV_ROOT', str_replace(DIRECTORY_SEPARATOR, DS, getcwd()));
|
||||
}
|
||||
define('ROOT_DIR', GRAV_ROOT . '/');
|
||||
define('USER_PATH', 'user/');
|
||||
@@ -17,14 +17,16 @@ define('ASSETS_DIR', ROOT_DIR . 'assets/');
|
||||
define('CACHE_DIR', ROOT_DIR . 'cache/');
|
||||
define('IMAGES_DIR', ROOT_DIR . 'images/');
|
||||
define('LOG_DIR', ROOT_DIR .'logs/');
|
||||
define('VENDOR_DIR', ROOT_DIR .'vendor/');
|
||||
define('LIB_DIR', SYSTEM_DIR .'src/');
|
||||
define('ACCOUNTS_DIR', USER_DIR .'accounts/');
|
||||
define('DATA_DIR', USER_DIR .'data/');
|
||||
define('PAGES_DIR', USER_DIR .'pages/');
|
||||
|
||||
// DEPRECATED: Do not use!
|
||||
define('DATA_DIR', USER_DIR .'data/');
|
||||
define('LIB_DIR', SYSTEM_DIR .'src/');
|
||||
define('PLUGINS_DIR', USER_DIR .'plugins/');
|
||||
define('THEMES_DIR', USER_DIR .'themes/');
|
||||
define('VENDOR_DIR', ROOT_DIR .'vendor/');
|
||||
// END DEPRECATED
|
||||
|
||||
// Some extensions
|
||||
define('CONTENT_EXT', '.md');
|
||||
@@ -38,6 +40,3 @@ define('RAW_CONTENT', 1);
|
||||
define('TWIG_CONTENT', 2);
|
||||
define('TWIG_CONTENT_LIST', 3);
|
||||
define('TWIG_TEMPLATES', 4);
|
||||
|
||||
// Misc Defines
|
||||
define('SUMMARY_DELIMITER', '===');
|
||||
|
||||
@@ -71,6 +71,7 @@ class Assets
|
||||
// Some configuration variables
|
||||
protected $config;
|
||||
protected $base_url;
|
||||
protected $timestamp = '';
|
||||
|
||||
// Default values for pipeline settings
|
||||
protected $css_minify = true;
|
||||
@@ -82,7 +83,6 @@ class Assets
|
||||
protected $css_no_pipeline = array();
|
||||
protected $js_no_pipeline = array();
|
||||
|
||||
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
// Forward config options
|
||||
@@ -120,7 +120,7 @@ class Assets
|
||||
}
|
||||
|
||||
// Set custom pipeline fetch command
|
||||
if (isset($config['fetch_command']) and ($config['fetch_command'] instanceof Closure)) {
|
||||
if (isset($config['fetch_command']) && ($config['fetch_command'] instanceof Closure)) {
|
||||
$this->fetch_command = $config['fetch_command'];
|
||||
}
|
||||
|
||||
@@ -143,17 +143,23 @@ class Assets
|
||||
}
|
||||
|
||||
// Set collections
|
||||
if (isset($config['collections']) and is_array($config['collections'])) {
|
||||
if (isset($config['collections']) && is_array($config['collections'])) {
|
||||
$this->collections = $config['collections'];
|
||||
}
|
||||
|
||||
// Autoload assets
|
||||
if (isset($config['autoload']) and is_array($config['autoload'])) {
|
||||
if (isset($config['autoload']) && is_array($config['autoload'])) {
|
||||
foreach ($config['autoload'] as $asset) {
|
||||
$this->add($asset);
|
||||
}
|
||||
}
|
||||
|
||||
// Set timestamp
|
||||
if (isset($config['enable_asset_timestamp']) && $config['enable_asset_timestamp'] === true) {
|
||||
$this->timestamp = '?' . self::getGrav()['cache']->getKey();
|
||||
}
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -163,12 +169,17 @@ class Assets
|
||||
public function init()
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
$base_url = self::$grav['base_url'];
|
||||
$config = self::getGrav()['config'];
|
||||
$base_url = self::getGrav()['base_url'];
|
||||
$asset_config = (array)$config->get('system.assets');
|
||||
|
||||
$this->config($asset_config);
|
||||
$this->base_url = $base_url . '/';
|
||||
|
||||
// Register any preconfigured collections
|
||||
foreach ($config->get('system.assets.collections') as $name => $collection) {
|
||||
$this->registerCollection($name, (array)$collection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,13 +204,15 @@ class Assets
|
||||
} elseif (isset($this->collections[$asset])) {
|
||||
$this->add($this->collections[$asset], $priority, $pipeline);
|
||||
} else {
|
||||
// Get extension
|
||||
$extension = pathinfo(parse_url($asset, PHP_URL_PATH), PATHINFO_EXTENSION);
|
||||
|
||||
// JavaScript or CSS
|
||||
$info = pathinfo($asset);
|
||||
if (isset($info['extension'])) {
|
||||
$ext = strtolower($info['extension']);
|
||||
if ($ext === 'css') {
|
||||
if (strlen($extension) > 0) {
|
||||
$extension = strtolower($extension);
|
||||
if ($extension === 'css') {
|
||||
$this->addCss($asset, $priority, $pipeline);
|
||||
} elseif ($ext === 'js') {
|
||||
} elseif ($extension === 'js') {
|
||||
$this->addJs($asset, $priority, $pipeline);
|
||||
}
|
||||
}
|
||||
@@ -226,7 +239,9 @@ class Assets
|
||||
foreach ($asset as $a) {
|
||||
$this->addCss($a, $priority, $pipeline);
|
||||
}
|
||||
|
||||
return $this;
|
||||
} elseif (isset($this->collections[$asset])) {
|
||||
$this->add($this->collections[$asset], $priority, $pipeline);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -234,8 +249,9 @@ class Assets
|
||||
$asset = $this->buildLocalLink($asset);
|
||||
}
|
||||
|
||||
if ($asset && !array_key_exists($asset, $this->css)) {
|
||||
$this->css[$asset] = [
|
||||
$key = md5($asset);
|
||||
if ($asset) {
|
||||
$this->css[$key] = [
|
||||
'asset' => $asset,
|
||||
'priority' => $priority,
|
||||
'order' => count($this->css),
|
||||
@@ -255,16 +271,19 @@ class Assets
|
||||
* @param mixed $asset
|
||||
* @param int $priority the priority, bigger comes first
|
||||
* @param bool $pipeline false if this should not be pipelined
|
||||
* @param string $loading how the asset is loaded (async/defer)
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addJs($asset, $priority = 10, $pipeline = true)
|
||||
public function addJs($asset, $priority = 10, $pipeline = true, $loading = '')
|
||||
{
|
||||
if (is_array($asset)) {
|
||||
foreach ($asset as $a) {
|
||||
$this->addJs($a, $priority, $pipeline);
|
||||
}
|
||||
|
||||
return $this;
|
||||
} elseif (isset($this->collections[$asset])) {
|
||||
$this->add($this->collections[$asset], $priority, $pipeline);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -272,18 +291,48 @@ class Assets
|
||||
$asset = $this->buildLocalLink($asset);
|
||||
}
|
||||
|
||||
if ($asset && !array_key_exists($asset, $this->js)) {
|
||||
$this->js[$asset] = [
|
||||
$key = md5($asset);
|
||||
if ($asset) {
|
||||
$this->js[$key] = [
|
||||
'asset' => $asset,
|
||||
'priority' => $priority,
|
||||
'order' => count($this->js),
|
||||
'pipeline' => $pipeline
|
||||
'pipeline' => $pipeline,
|
||||
'loading' => $loading
|
||||
];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience wrapper for async loading of JavaScript
|
||||
*
|
||||
* @param $asset
|
||||
* @param int $priority
|
||||
* @param bool $pipeline
|
||||
*
|
||||
* @return \Grav\Common\Assets
|
||||
*/
|
||||
public function addAsyncJs($asset, $priority = 10, $pipeline = true)
|
||||
{
|
||||
return $this->addJs($asset, $priority, $pipeline, 'async');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience wrapper for deferred loading of JavaScript
|
||||
*
|
||||
* @param $asset
|
||||
* @param int $priority
|
||||
* @param bool $pipeline
|
||||
*
|
||||
* @return \Grav\Common\Assets
|
||||
*/
|
||||
public function addDeferJs($asset, $priority = 10, $pipeline = true)
|
||||
{
|
||||
return $this->addJs($asset, $priority, $pipeline, 'defer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an inline CSS asset.
|
||||
*
|
||||
@@ -297,9 +346,16 @@ class Assets
|
||||
*/
|
||||
public function addInlineCss($asset, $priority = 10)
|
||||
{
|
||||
|
||||
if (is_string($asset) && !in_array($asset, $this->inline_css)) {
|
||||
$this->inline_css[] = $asset;
|
||||
if (is_a($asset, 'Twig_Markup')) {
|
||||
$asset = strip_tags((string)$asset);
|
||||
}
|
||||
$key = md5($asset);
|
||||
if (is_string($asset) && !array_key_exists($key, $this->inline_css)) {
|
||||
$this->inline_css[$key] = [
|
||||
'priority' => $priority,
|
||||
'order' => count($this->inline_css),
|
||||
'asset' => $asset
|
||||
];
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -312,14 +368,22 @@ class Assets
|
||||
* For adding chunks of string-based inline JS
|
||||
*
|
||||
* @param mixed $asset
|
||||
* @param int $priority the priority, bigger comes first
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addInlineJs($asset)
|
||||
public function addInlineJs($asset, $priority = 10)
|
||||
{
|
||||
|
||||
if (is_string($asset) && !in_array($asset, $this->inline_js)) {
|
||||
$this->inline_js[] = $asset;
|
||||
if (is_a($asset, 'Twig_Markup')) {
|
||||
$asset = strip_tags((string)$asset);
|
||||
}
|
||||
$key = md5($asset);
|
||||
if (is_string($asset) && !array_key_exists($key, $this->inline_js)) {
|
||||
$this->inline_js[$key] = [
|
||||
'priority' => $priority,
|
||||
'order' => count($this->inline_js),
|
||||
'asset' => $asset
|
||||
];
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -339,15 +403,23 @@ class Assets
|
||||
}
|
||||
|
||||
// Sort array by priorities (larger priority first)
|
||||
if (self::$grav) {
|
||||
if (self::getGrav()) {
|
||||
usort($this->css, function ($a, $b) {
|
||||
if ($a['priority'] == $b['priority']) {
|
||||
return $b['order'] - $a['order'];
|
||||
}
|
||||
return $a['priority'] - $b['priority'];
|
||||
});
|
||||
|
||||
usort($this->inline_css, function ($a, $b) {
|
||||
if ($a['priority'] == $b['priority']) {
|
||||
return $b['order'] - $a['order'];
|
||||
}
|
||||
return $a['priority'] - $b['priority'];
|
||||
});
|
||||
}
|
||||
$this->css = array_reverse($this->css);
|
||||
$this->inline_css = array_reverse($this->inline_css);
|
||||
|
||||
$attributes = $this->attributes(array_merge(['type' => 'text/css', 'rel' => 'stylesheet'], $attributes));
|
||||
|
||||
@@ -356,11 +428,11 @@ class Assets
|
||||
$output .= '<link href="' . $this->pipeline(CSS_ASSET) . '"' . $attributes . ' />' . "\n";
|
||||
|
||||
foreach ($this->css_no_pipeline as $file) {
|
||||
$output .= '<link href="' . $file['asset'] . '"' . $attributes . ' />' . "\n";
|
||||
$output .= '<link href="' . $file['asset'] . $this->timestamp . '"' . $attributes . ' />' . "\n";
|
||||
}
|
||||
} else {
|
||||
foreach ($this->css as $file) {
|
||||
$output .= '<link href="' . $file['asset'] . '"' . $attributes . ' />' . "\n";
|
||||
$output .= '<link href="' . $file['asset'] . $this->timestamp . '"' . $attributes . ' />' . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +440,7 @@ class Assets
|
||||
if (count($this->inline_css) > 0) {
|
||||
$output .= "<style>\n";
|
||||
foreach ($this->inline_css as $inline) {
|
||||
$output .= $inline . "\n";
|
||||
$output .= $inline['asset'] . "\n";
|
||||
}
|
||||
$output .= "</style>\n";
|
||||
}
|
||||
@@ -397,7 +469,16 @@ class Assets
|
||||
}
|
||||
return $a['priority'] - $b['priority'];
|
||||
});
|
||||
|
||||
usort($this->inline_js, function ($a, $b) {
|
||||
if ($a['priority'] == $b['priority']) {
|
||||
return $b['order'] - $a['order'];
|
||||
}
|
||||
return $a['priority'] - $b['priority'];
|
||||
});
|
||||
|
||||
$this->js = array_reverse($this->js);
|
||||
$this->inline_js = array_reverse($this->inline_js);
|
||||
|
||||
$attributes = $this->attributes(array_merge(['type' => 'text/javascript'], $attributes));
|
||||
|
||||
@@ -405,11 +486,11 @@ class Assets
|
||||
if ($this->js_pipeline) {
|
||||
$output .= '<script src="' . $this->pipeline(JS_ASSET) . '"' . $attributes . ' ></script>' . "\n";
|
||||
foreach ($this->js_no_pipeline as $file) {
|
||||
$output .= '<script src="' . $file['asset'] . '"' . $attributes . ' ></script>' . "\n";
|
||||
$output .= '<script src="' . $file['asset'] . $this->timestamp . '"' . $attributes . ' ' . $file['loading']. '></script>' . "\n";
|
||||
}
|
||||
} else {
|
||||
foreach ($this->js as $file) {
|
||||
$output .= '<script src="' . $file['asset'] . '"' . $attributes . ' ></script>' . "\n";
|
||||
$output .= '<script src="' . $file['asset'] . $this->timestamp . '"' . $attributes . ' ' . $file['loading'].'></script>' . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,7 +498,7 @@ class Assets
|
||||
if (count($this->inline_js) > 0) {
|
||||
$output .= "<script>\n";
|
||||
foreach ($this->inline_js as $inline) {
|
||||
$output .= $inline . "\n";
|
||||
$output .= $inline['asset'] . "\n";
|
||||
}
|
||||
$output .= "</script>\n";
|
||||
}
|
||||
@@ -435,7 +516,7 @@ class Assets
|
||||
protected function pipeline($css = true)
|
||||
{
|
||||
/** @var Cache $cache */
|
||||
$cache = self::$grav['cache'];
|
||||
$cache = self::getGrav()['cache'];
|
||||
$key = '?' . $cache->getKey();
|
||||
|
||||
if ($css) {
|
||||
@@ -493,17 +574,68 @@ class Assets
|
||||
return $relative_path . $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the array of all the registered CSS assets
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCss()
|
||||
{
|
||||
return $this->css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the array of all the registered JS assets
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJs()
|
||||
{
|
||||
return $this->js;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the array of all the registered collections
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCollections()
|
||||
{
|
||||
return $this->collections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an asset exists as a collection, CSS or JS reference
|
||||
*
|
||||
* @param $asset
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists($asset)
|
||||
{
|
||||
if (isset($this->collections[$asset]) ||
|
||||
isset($this->css[$asset]) ||
|
||||
isset($this->js[$asset])) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add/replace collection.
|
||||
*
|
||||
* @param string $collectionName
|
||||
* @param array $assets
|
||||
* @param bool $overwrite
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function registerCollection($collectionName, Array $assets)
|
||||
public function registerCollection($collectionName, Array $assets, $overwrite = false)
|
||||
{
|
||||
$this->collections[$collectionName] = $assets;
|
||||
if ($overwrite || !isset($this->collections[$collectionName])) {
|
||||
$this->collections[$collectionName] = $assets;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -542,26 +674,6 @@ class Assets
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all CSS assets already added.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCss()
|
||||
{
|
||||
return $this->css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all JavaScript assets already added.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJs()
|
||||
{
|
||||
return $this->js;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all CSS assets within $directory (relative to public dir).
|
||||
*
|
||||
@@ -615,9 +727,9 @@ class Assets
|
||||
$info = pathinfo($asset);
|
||||
if (isset($info['extension'])) {
|
||||
$ext = strtolower($info['extension']);
|
||||
if ($ext === 'css' and !in_array($asset, $this->css)) {
|
||||
if ($ext === 'css' && !in_array($asset, $this->css)) {
|
||||
$this->css[] = $asset;
|
||||
} elseif ($ext === 'js' and !in_array($asset, $this->js)) {
|
||||
} elseif ($ext === 'js' && !in_array($asset, $this->js)) {
|
||||
$this->js[] = $asset;
|
||||
}
|
||||
}
|
||||
@@ -637,8 +749,8 @@ class Assets
|
||||
*/
|
||||
protected function isRemoteLink($link)
|
||||
{
|
||||
return ('http://' === substr($link, 0, 7) or 'https://' === substr($link, 0, 8)
|
||||
or '//' === substr($link, 0, 2));
|
||||
return ('http://' === substr($link, 0, 7) || 'https://' === substr($link, 0, 8)
|
||||
|| '//' === substr($link, 0, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -651,7 +763,7 @@ class Assets
|
||||
protected function buildLocalLink($asset)
|
||||
{
|
||||
try {
|
||||
$asset = self::$grav['locator']->findResource($asset, false);
|
||||
$asset = self::getGrav()['locator']->findResource($asset, false);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
@@ -713,7 +825,8 @@ class Assets
|
||||
} else {
|
||||
// Fix to remove relative dir if grav is in one
|
||||
if (($this->base_url != '/') && (strpos($this->base_url, $link) == 0)) {
|
||||
$relative_path = str_replace($this->base_url, '/', $link);
|
||||
$base_url = '#' . preg_quote($this->base_url, '#') . '#';
|
||||
$relative_path = ltrim(preg_replace($base_url, '/', $link, 1), '/');
|
||||
}
|
||||
|
||||
$relative_dir = dirname($relative_path);
|
||||
@@ -769,18 +882,7 @@ class Assets
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
$newpath = array();
|
||||
$paths = explode('/', $old_url);
|
||||
|
||||
foreach ($paths as $path) {
|
||||
if ($path == '..') {
|
||||
$relative_path = dirname($relative_path);
|
||||
} else {
|
||||
$newpath[] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
$new_url = rtrim($this->base_url, '/') . $relative_path . '/' . implode('/', $newpath);
|
||||
$new_url = $this->base_url . ltrim(Utils::normalizePath($relative_path . '/' . $old_url), '/');
|
||||
|
||||
return str_replace($old_url, $new_url, $matches[0]);
|
||||
},
|
||||
|
||||
121
system/src/Grav/Common/Backup/ZipBackup.php
Normal file
121
system/src/Grav/Common/Backup/ZipBackup.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
namespace Grav\Common\Backup;
|
||||
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
|
||||
/**
|
||||
* The ZipBackup class lets you create simple zip-backups of a grav site
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class ZipBackup
|
||||
{
|
||||
use GravTrait;
|
||||
|
||||
protected static $ignorePaths = [
|
||||
'backup',
|
||||
'cache',
|
||||
'images',
|
||||
'logs'
|
||||
];
|
||||
|
||||
protected static $ignoreFolders = [
|
||||
'.git'
|
||||
];
|
||||
|
||||
public static function backup($destination = null, callable $messager = null)
|
||||
{
|
||||
if (!$destination) {
|
||||
$destination = self::getGrav()['locator']->findResource('backup://', true);
|
||||
|
||||
if (!$destination)
|
||||
throw new \RuntimeException('The backup folder is missing.');
|
||||
|
||||
Folder::mkdir($destination);
|
||||
}
|
||||
|
||||
$name = self::getGrav()['config']->get('site.title', basename(GRAV_ROOT));
|
||||
|
||||
if (is_dir($destination)) {
|
||||
$date = date('YmdHis', time());
|
||||
$filename = $name . '-' . $date . '.zip';
|
||||
$destination = rtrim($destination, DS) . DS . $filename;
|
||||
}
|
||||
|
||||
$messager && $messager([
|
||||
'type' => 'message',
|
||||
'level' => 'info',
|
||||
'message' => 'Creating new Backup "' . $destination . '"'
|
||||
]);
|
||||
$messager && $messager([
|
||||
'type' => 'message',
|
||||
'level' => 'info',
|
||||
'message' => ''
|
||||
]);
|
||||
|
||||
$zip = new \ZipArchive();
|
||||
$zip->open($destination, \ZipArchive::CREATE);
|
||||
|
||||
static::folderToZip(GRAV_ROOT, $zip, strlen(rtrim(GRAV_ROOT, DS) . DS), $messager);
|
||||
|
||||
$messager && $messager([
|
||||
'type' => 'progress',
|
||||
'percentage' => false,
|
||||
'complete' => true
|
||||
]);
|
||||
|
||||
$messager && $messager([
|
||||
'type' => 'message',
|
||||
'level' => 'info',
|
||||
'message' => ''
|
||||
]);
|
||||
$messager && $messager([
|
||||
'type' => 'message',
|
||||
'level' => 'info',
|
||||
'message' => 'Saving and compressing archive...'
|
||||
]);
|
||||
|
||||
$zip->close();
|
||||
|
||||
return $destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $folder
|
||||
* @param $zipFile
|
||||
* @param $exclusiveLength
|
||||
* @param $messager
|
||||
*/
|
||||
private static function folderToZip($folder, \ZipArchive &$zipFile, $exclusiveLength, callable $messager = null)
|
||||
{
|
||||
$handle = opendir($folder);
|
||||
while (false !== $f = readdir($handle)) {
|
||||
if ($f != '.' && $f != '..') {
|
||||
$filePath = "$folder/$f";
|
||||
// Remove prefix from file path before add to zip.
|
||||
$localPath = substr($filePath, $exclusiveLength);
|
||||
|
||||
if (in_array($f, static::$ignoreFolders) || in_array($localPath, static::$ignorePaths)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_file($filePath)) {
|
||||
$zipFile->addFile($filePath, $localPath);
|
||||
|
||||
$messager && $messager([
|
||||
'type' => 'progress',
|
||||
'percentage' => false,
|
||||
'complete' => false
|
||||
]);
|
||||
} elseif (is_dir($filePath)) {
|
||||
// Add sub-directory.
|
||||
$zipFile->addEmptyDir($localPath);
|
||||
static::folderToZip($filePath, $zipFile, $exclusiveLength, $messager);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($handle);
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -3,6 +3,7 @@ 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.
|
||||
@@ -25,6 +26,11 @@ class Cache extends Getters
|
||||
*/
|
||||
protected $key;
|
||||
|
||||
protected $lifetime;
|
||||
protected $now;
|
||||
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var DoctrineCache
|
||||
*/
|
||||
@@ -37,6 +43,33 @@ class Cache extends Getters
|
||||
|
||||
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
|
||||
*
|
||||
@@ -57,6 +90,7 @@ class Cache extends Getters
|
||||
{
|
||||
/** @var Config $config */
|
||||
$this->config = $grav['config'];
|
||||
$this->now = time();
|
||||
|
||||
$this->cache_dir = $grav['locator']->findResource('cache://doctrine', true, true);
|
||||
|
||||
@@ -154,9 +188,8 @@ 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;
|
||||
if ($lifetime === null) {
|
||||
$lifetime = $this->getLifetime();
|
||||
}
|
||||
$this->driver->save($id, $data, $lifetime);
|
||||
}
|
||||
@@ -169,4 +202,103 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the cache lifetime programatically
|
||||
*
|
||||
* @param int $future timestamp
|
||||
*/
|
||||
public function setLifetime($future)
|
||||
{
|
||||
if (!$future) {
|
||||
return;
|
||||
}
|
||||
|
||||
$interval = $future - $this->now;
|
||||
if ($interval > 0 && $interval < $this->getLifetime()) {
|
||||
$this->lifetime = $interval;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the cache lifetime (in seconds)
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLifetime()
|
||||
{
|
||||
if ($this->lifetime === null) {
|
||||
$this->lifetime = $this->config->get('system.cache.lifetime') ?: 604800; // 1 week default
|
||||
}
|
||||
|
||||
return $this->lifetime;
|
||||
}
|
||||
}
|
||||
|
||||
55
system/src/Grav/Common/Composer.php
Normal file
55
system/src/Grav/Common/Composer.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Grav\Common;
|
||||
|
||||
/**
|
||||
* Offers composer helper methods.
|
||||
*
|
||||
* @author eschmar
|
||||
* @license MIT
|
||||
*/
|
||||
class Composer
|
||||
{
|
||||
/** @const Default composer location */
|
||||
const DEFAULT_PATH = "bin/composer.phar";
|
||||
|
||||
/**
|
||||
* Returns the location of composer.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getComposerLocation()
|
||||
{
|
||||
if (!function_exists('shell_exec') || strtolower(substr(PHP_OS, 0, 3)) === 'win') {
|
||||
return self::DEFAULT_PATH;
|
||||
}
|
||||
|
||||
// check for global composer install
|
||||
$path = trim(shell_exec("command -v composer"));
|
||||
|
||||
// fall back to grav bundled composer
|
||||
if (!$path || !preg_match('/(composer|composer\.phar)$/', $path)) {
|
||||
$path = self::DEFAULT_PATH;
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
public static function getComposerExecutor()
|
||||
{
|
||||
$executor = PHP_BINARY . ' ';
|
||||
$composer = static::getComposerLocation();
|
||||
|
||||
if ($composer !== static::DEFAULT_PATH && is_executable($composer)) {
|
||||
$file = fopen($composer, 'r');
|
||||
$firstLine = fgets($file);
|
||||
fclose($file);
|
||||
|
||||
if (!preg_match('/^#!.+php/i', $firstLine)) {
|
||||
$executor = '';
|
||||
}
|
||||
}
|
||||
|
||||
return $executor . $composer;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use RocketTheme\Toolbox\Blueprints\Blueprints as BaseBlueprints;
|
||||
use RocketTheme\Toolbox\File\PhpFile;
|
||||
|
||||
@@ -3,10 +3,7 @@ namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use RocketTheme\Toolbox\Blueprints\Blueprints;
|
||||
use RocketTheme\Toolbox\File\PhpFile;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
@@ -21,6 +18,12 @@ class Config extends Data
|
||||
{
|
||||
protected $grav;
|
||||
protected $streams = [
|
||||
'system' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['system'],
|
||||
]
|
||||
],
|
||||
'user' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
@@ -60,7 +63,8 @@ class Config extends Data
|
||||
'cache' => [
|
||||
'type' => 'Stream',
|
||||
'prefixes' => [
|
||||
'' => ['cache']
|
||||
'' => ['cache'],
|
||||
'images' => ['images']
|
||||
]
|
||||
],
|
||||
'log' => [
|
||||
@@ -68,9 +72,17 @@ class Config extends Data
|
||||
'prefixes' => [
|
||||
'' => ['logs']
|
||||
]
|
||||
],
|
||||
'backup' => [
|
||||
'type' => 'Stream',
|
||||
'prefixes' => [
|
||||
'' => ['backup']
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
protected $setup = [];
|
||||
|
||||
protected $blueprintFiles = [];
|
||||
protected $configFiles = [];
|
||||
protected $checksum;
|
||||
@@ -84,33 +96,25 @@ class Config extends Data
|
||||
protected $environment;
|
||||
protected $messages = [];
|
||||
|
||||
public function __construct(array $items = array(), Grav $grav = null, $environment = null)
|
||||
public function __construct(array $setup = array(), Grav $grav = null, $environment = null)
|
||||
{
|
||||
$this->grav = $grav ?: Grav::instance();
|
||||
$this->finder = new ConfigFinder;
|
||||
$this->environment = $environment ?: 'localhost';
|
||||
$this->messages[] = 'Environment Name: ' . $this->environment;
|
||||
|
||||
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);
|
||||
// Make sure that
|
||||
if (!isset($setup['streams']['schemes'])) {
|
||||
$setup['streams']['schemes'] = [];
|
||||
}
|
||||
$setup['streams']['schemes'] += $this->streams;
|
||||
|
||||
$setup = $this->autoDetectEnvironmentConfig($setup);
|
||||
$this->messages[] = $setup['streams']['schemes']['config']['prefixes'][''];
|
||||
|
||||
$this->setup = $setup;
|
||||
parent::__construct($setup);
|
||||
|
||||
$this->check();
|
||||
}
|
||||
|
||||
@@ -121,8 +125,10 @@ class Config extends Data
|
||||
|
||||
public function reload()
|
||||
{
|
||||
$this->items = $this->setup;
|
||||
$this->check();
|
||||
$this->init();
|
||||
$this->debug();
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -146,6 +152,7 @@ class Config extends Data
|
||||
foreach ($this->messages as $message) {
|
||||
$this->grav['debugger']->addMessage($message);
|
||||
}
|
||||
$this->messages = [];
|
||||
}
|
||||
|
||||
public function init()
|
||||
@@ -157,17 +164,10 @@ class Config extends Data
|
||||
$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');
|
||||
|
||||
$this->initializeLocator($locator);
|
||||
}
|
||||
|
||||
public function checksum()
|
||||
@@ -249,7 +249,6 @@ class Config extends Data
|
||||
'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.';
|
||||
@@ -263,44 +262,51 @@ class Config extends 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);
|
||||
$checksum = $this->checksum();
|
||||
|
||||
// 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);
|
||||
$this->messages[] = 'No cached configuration, compiling new configuration..';
|
||||
} else if ($cache['checksum'] !== $checksum) {
|
||||
$this->messages[] = 'Configuration checksum mismatch, reloading configuration..';
|
||||
} else {
|
||||
$this->messages[] = 'Configuration checksum matches, using cached version.';
|
||||
|
||||
// Load configuration.
|
||||
foreach ($configFiles as $files) {
|
||||
$this->loadConfigFiles($files);
|
||||
}
|
||||
$cache = [
|
||||
'@class' => $class,
|
||||
'timestamp' => time(),
|
||||
'checksum' => $this->checksum(),
|
||||
'data' => $this->toArray()
|
||||
];
|
||||
$this->items = $cache['data'];
|
||||
return;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
$configFiles = $this->finder->locateConfigFiles($configs, $plugins);
|
||||
|
||||
// Attempt to lock the file for writing.
|
||||
$file->lock(false);
|
||||
|
||||
// Load configuration.
|
||||
foreach ($configFiles as $files) {
|
||||
$this->loadConfigFiles($files);
|
||||
}
|
||||
$cache = [
|
||||
'@class' => $class,
|
||||
'timestamp' => time(),
|
||||
'checksum' => $checksum,
|
||||
'data' => $this->toArray()
|
||||
];
|
||||
|
||||
// If compiled file wasn't already locked by another process, save it.
|
||||
if ($file->locked() !== false) {
|
||||
$this->messages[] = 'Saving compiled configuration.';
|
||||
$file->save($cache);
|
||||
$file->unlock();
|
||||
}
|
||||
|
||||
$this->items = $cache['data'];
|
||||
@@ -331,4 +337,47 @@ class Config extends Data
|
||||
$this->join($name, $file->content(), '/');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize resource locator by using the configuration.
|
||||
*
|
||||
* @param UniformResourceLocator $locator
|
||||
*/
|
||||
public function initializeLocator(UniformResourceLocator $locator)
|
||||
{
|
||||
$locator->reset();
|
||||
|
||||
$schemes = (array) $this->get('streams.schemes', []);
|
||||
|
||||
foreach ($schemes as $scheme => $config) {
|
||||
if (isset($config['paths'])) {
|
||||
$locator->addPath($scheme, '', $config['paths']);
|
||||
}
|
||||
if (isset($config['prefixes'])) {
|
||||
foreach ($config['prefixes'] as $prefix => $paths) {
|
||||
$locator->addPath($scheme, $prefix, $paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available streams and their types from the configuration.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getStreams()
|
||||
{
|
||||
$schemes = [];
|
||||
foreach ((array) $this->get('streams.schemes') as $scheme => $config) {
|
||||
$type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream';
|
||||
if ($type[0] != '\\') {
|
||||
$type = '\\RocketTheme\\Toolbox\\StreamWrapper\\' . $type;
|
||||
}
|
||||
|
||||
$schemes[$scheme] = $type;
|
||||
}
|
||||
|
||||
return $schemes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
*/
|
||||
class Blueprint
|
||||
{
|
||||
use Export;
|
||||
use Export, DataMutatorTrait;
|
||||
|
||||
public $name;
|
||||
|
||||
@@ -46,68 +46,6 @@ class Blueprint
|
||||
$this->filter = array_flip($filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
* @example $value = $data->get('this.is.my.nested.variable');
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $default Default value (or null).
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
*
|
||||
* @return mixed Value.
|
||||
*/
|
||||
public function get($name, $default = null, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = $this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current) && isset($current->{$field})) {
|
||||
$current = $current->{$field};
|
||||
} elseif (is_array($current) && isset($current[$field])) {
|
||||
$current = $current[$field];
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sey value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
* @example $value = $data->set('this.is.my.nested.variable', true);
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $value New value.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
*/
|
||||
public function set($name, $value, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = &$this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current)) {
|
||||
// Handle objects.
|
||||
if (!isset($current->{$field})) {
|
||||
$current->{$field} = array();
|
||||
}
|
||||
$current = &$current->{$field};
|
||||
} else {
|
||||
// Handle arrays and scalars.
|
||||
if (!is_array($current)) {
|
||||
$current = array($field => array());
|
||||
} elseif (!isset($current[$field])) {
|
||||
$current[$field] = array();
|
||||
}
|
||||
$current = &$current[$field];
|
||||
}
|
||||
}
|
||||
|
||||
$current = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all form fields.
|
||||
*
|
||||
@@ -146,10 +84,9 @@ class Blueprint
|
||||
*
|
||||
* @param array $data1
|
||||
* @param array $data2
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
public function mergeData(array $data1, array $data2, $name = null)
|
||||
public function mergeData(array $data1, array $data2)
|
||||
{
|
||||
// Initialize data
|
||||
$this->fields();
|
||||
@@ -213,7 +150,7 @@ class Blueprint
|
||||
$bref = array_merge($bref, array($key => $head[$key]));
|
||||
}
|
||||
}
|
||||
} while(count($head_stack));
|
||||
} while (count($head_stack));
|
||||
|
||||
$this->items = $blueprints;
|
||||
}
|
||||
@@ -478,14 +415,15 @@ class Blueprint
|
||||
* @throws \RuntimeException
|
||||
* @internal
|
||||
*/
|
||||
protected function checkRequired(array $data, array $fields) {
|
||||
protected function checkRequired(array $data, array $fields)
|
||||
{
|
||||
foreach ($fields as $name => $field) {
|
||||
if (!is_string($field)) {
|
||||
continue;
|
||||
}
|
||||
$field = $this->rules[$field];
|
||||
if (isset($field['validate']['required'])
|
||||
&& $field['validate']['required'] == true
|
||||
&& $field['validate']['required'] === true
|
||||
&& empty($data[$name])) {
|
||||
throw new \RuntimeException("Missing required field: {$field['name']}");
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ class Blueprints
|
||||
$file = CompiledYamlFile::instance($filename);
|
||||
$blueprints = $file->content();
|
||||
} else {
|
||||
// throw new \RuntimeException("Blueprints for '{$type}' cannot be found! {$this->search}{$type}");
|
||||
$blueprints = [];
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ use RocketTheme\Toolbox\File\FileInterface;
|
||||
*/
|
||||
class Data implements DataInterface
|
||||
{
|
||||
use ArrayAccessWithGetters, Countable, Export;
|
||||
use ArrayAccessWithGetters, Countable, Export, DataMutatorTrait;
|
||||
|
||||
protected $gettersVariable = 'items';
|
||||
protected $items;
|
||||
@@ -56,67 +56,6 @@ class Data implements DataInterface
|
||||
return $this->get($name, $default, $separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
* @example $value = $data->get('this.is.my.nested.variable');
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $default Default value (or null).
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return mixed Value.
|
||||
*/
|
||||
public function get($name, $default = null, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = $this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current) && isset($current->{$field})) {
|
||||
$current = $current->{$field};
|
||||
} elseif (is_array($current) && isset($current[$field])) {
|
||||
$current = $current[$field];
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sey value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
* @example $value = $data->set('this.is.my.nested.variable', true);
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $value New value.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
*/
|
||||
public function set($name, $value, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = &$this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current)) {
|
||||
// Handle objects.
|
||||
if (!isset($current->{$field})) {
|
||||
$current->{$field} = array();
|
||||
}
|
||||
$current = &$current->{$field};
|
||||
} else {
|
||||
// Handle arrays and scalars.
|
||||
if (!is_array($current)) {
|
||||
$current = array($field => array());
|
||||
} elseif (!isset($current[$field])) {
|
||||
$current[$field] = array();
|
||||
}
|
||||
$current = &$current[$field];
|
||||
}
|
||||
}
|
||||
|
||||
$current = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
|
||||
68
system/src/Grav/Common/Data/DataMutatorTrait.php
Normal file
68
system/src/Grav/Common/Data/DataMutatorTrait.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
trait DataMutatorTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* Get value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
* @example $value = $data->get('this.is.my.nested.variable');
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $default Default value (or null).
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return mixed Value.
|
||||
*/
|
||||
public function get($name, $default = null, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = $this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current) && isset($current->{$field})) {
|
||||
$current = $current->{$field};
|
||||
} elseif (is_array($current) && isset($current[$field])) {
|
||||
$current = $current[$field];
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sey value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
* @example $value = $data->set('this.is.my.nested.variable', true);
|
||||
*
|
||||
* @param string $name Dot separated path to the requested value.
|
||||
* @param mixed $value New value.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
*/
|
||||
public function set($name, $value, $separator = '.')
|
||||
{
|
||||
$path = explode($separator, $name);
|
||||
$current = &$this->items;
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current)) {
|
||||
// Handle objects.
|
||||
if (!isset($current->{$field})) {
|
||||
$current->{$field} = array();
|
||||
}
|
||||
$current = &$current->{$field};
|
||||
} else {
|
||||
// Handle arrays and scalars.
|
||||
if (!is_array($current)) {
|
||||
$current = array($field => array());
|
||||
} elseif (!isset($current[$field])) {
|
||||
$current[$field] = array();
|
||||
}
|
||||
$current = &$current[$field];
|
||||
}
|
||||
}
|
||||
|
||||
$current = $value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -375,7 +375,7 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
public static function typeDatetime_local($value, array $params, array $field)
|
||||
public static function typeDatetimeLocal($value, array $params, array $field)
|
||||
{
|
||||
return self::typeDatetime($value, $params, $field);
|
||||
}
|
||||
@@ -517,7 +517,7 @@ class Validation
|
||||
|
||||
public static function validateRequired($value, $params)
|
||||
{
|
||||
return (bool) $params != true || !empty($value);
|
||||
return (bool) $params !== true || !empty($value);
|
||||
}
|
||||
|
||||
public static function validatePattern($value, $params)
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use DebugBar\Bridge\Twig\TraceableTwigEnvironment;
|
||||
use DebugBar\JavascriptRenderer;
|
||||
use DebugBar\StandardDebugBar;
|
||||
//use \Tracy\Debugger as TracyDebugger;
|
||||
|
||||
/**
|
||||
* Class Debugger
|
||||
@@ -48,9 +46,11 @@ class Debugger
|
||||
public function addAssets()
|
||||
{
|
||||
if ($this->enabled()) {
|
||||
|
||||
$assets = $this->grav['assets'];
|
||||
|
||||
// Add jquery library
|
||||
$assets->add('jquery', 101);
|
||||
|
||||
$this->renderer = $this->debugbar->getJavascriptRenderer();
|
||||
$this->renderer->setIncludeVendors(false);
|
||||
|
||||
|
||||
@@ -4,9 +4,6 @@ namespace Grav\Common\Errors;
|
||||
use Grav\Common\Grav;
|
||||
use Whoops\Handler\CallbackHandler;
|
||||
use Whoops\Handler\HandlerInterface;
|
||||
use Whoops\Handler\JsonResponseHandler;
|
||||
use Whoops\Handler\PrettyPageHandler;
|
||||
use Whoops\Handler\PlainTextHandler;
|
||||
use Whoops\Run;
|
||||
|
||||
/**
|
||||
@@ -23,7 +20,7 @@ class Errors extends \Whoops\Run
|
||||
}
|
||||
|
||||
if (!$handler instanceof HandlerInterface) {
|
||||
throw new InvalidArgumentException(
|
||||
throw new \InvalidArgumentException(
|
||||
"Argument to " . __METHOD__ . " must be a callable, or instance of"
|
||||
. "Whoops\\Handler\\HandlerInterface"
|
||||
);
|
||||
|
||||
@@ -21,9 +21,7 @@ class SimplePageHandler extends Handler
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$exception = $this->getException();
|
||||
$inspector = $this->getInspector();
|
||||
$run = $this->getRun();
|
||||
|
||||
$helper = new TemplateHelper();
|
||||
$templateFile = $this->getResource("layout.html.php");
|
||||
@@ -46,6 +44,11 @@ class SimplePageHandler extends Handler
|
||||
return Handler::QUIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $resource
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getResource($resource)
|
||||
{
|
||||
// If the resource was found before, we can speed things up
|
||||
@@ -67,7 +70,7 @@ class SimplePageHandler extends Handler
|
||||
}
|
||||
|
||||
// If we got this far, nothing was found.
|
||||
throw new RuntimeException(
|
||||
throw new \RuntimeException(
|
||||
"Could not find resource '$resource' in any resource paths."
|
||||
. "(searched: " . join(", ", $this->searchPaths). ")"
|
||||
);
|
||||
@@ -76,7 +79,7 @@ class SimplePageHandler extends Handler
|
||||
public function addResourcePath($path)
|
||||
{
|
||||
if (!is_dir($path)) {
|
||||
throw new InvalidArgumentException(
|
||||
throw new \InvalidArgumentException(
|
||||
"'$path' is not a valid directory"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,12 +19,13 @@ abstract class Folder
|
||||
{
|
||||
$last_modified = 0;
|
||||
|
||||
$directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
$iterator = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
$dirItr = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
$filterItr = new RecursiveFolderFilterIterator($dirItr);
|
||||
$itr = new \RecursiveIteratorIterator($filterItr, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
|
||||
/** @var \RecursiveDirectoryIterator $file */
|
||||
foreach ($iterator as $file) {
|
||||
$dir_modified = $file->getMTime();
|
||||
foreach ($itr as $dir) {
|
||||
$dir_modified = $dir->getMTime();
|
||||
if ($dir_modified > $last_modified) {
|
||||
$last_modified = $dir_modified;
|
||||
}
|
||||
@@ -33,17 +34,6 @@ abstract class Folder
|
||||
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.
|
||||
*
|
||||
@@ -52,14 +42,16 @@ abstract class Folder
|
||||
*/
|
||||
public static function lastModifiedFile($path)
|
||||
{
|
||||
// pipe separated list of extensions to search for changes with
|
||||
$extensions = 'md|yaml';
|
||||
$last_modified = 0;
|
||||
|
||||
$dirItr = new \RecursiveDirectoryIterator($path);
|
||||
$filterItr = new GravRecursiveFilterIterator($dirItr);
|
||||
$itr = new \RecursiveIteratorIterator($filterItr, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
$dirItr = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
$itrItr = new \RecursiveIteratorIterator($dirItr, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
$itr = new \RegexIterator($itrItr, '/^.+\.'.$extensions.'$/i');
|
||||
|
||||
/** @var \RecursiveDirectoryIterator $file */
|
||||
foreach ($itr as $file) {
|
||||
foreach ($itr as $filepath => $file) {
|
||||
$file_modified = $file->getMTime();
|
||||
if ($file_modified > $last_modified) {
|
||||
$last_modified = $file_modified;
|
||||
@@ -69,6 +61,43 @@ abstract class Folder
|
||||
return $last_modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get relative path between target and base path. If path isn't relative, return full path.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $base
|
||||
* @return string
|
||||
*/
|
||||
public static function getRelativePath($path, $base = GRAV_ROOT)
|
||||
{
|
||||
if ($base) {
|
||||
$base = preg_replace('![\\\/]+!', '/', $base);
|
||||
$path = preg_replace('![\\\/]+!', '/', $path);
|
||||
if (strpos($path, $base) === 0) {
|
||||
$path = ltrim(substr($path, strlen($base)), '/');
|
||||
}
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shift first directory out of the path.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public static function shift(&$path)
|
||||
{
|
||||
$parts = explode('/', trim($path, '/'), 2);
|
||||
$result = array_shift($parts);
|
||||
$path = array_shift($parts);
|
||||
|
||||
return $result ?: null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return recursive list of all files and directories under given path.
|
||||
*
|
||||
@@ -208,8 +237,9 @@ abstract class Folder
|
||||
/**
|
||||
* Recursively delete directory from filesystem.
|
||||
*
|
||||
* @param string $target
|
||||
* @param string $target
|
||||
* @throws \RuntimeException
|
||||
* @return bool
|
||||
*/
|
||||
public static function delete($target)
|
||||
{
|
||||
@@ -248,6 +278,46 @@ abstract class Folder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive copy of one directory to another
|
||||
*
|
||||
* @param $src
|
||||
* @param $dest
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function rcopy($src, $dest)
|
||||
{
|
||||
|
||||
// If the src is not a directory do a simple file copy
|
||||
if (!is_dir($src)) {
|
||||
copy($src, $dest);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the destination directory does not exist create it
|
||||
if (!is_dir($dest)) {
|
||||
if (!mkdir($dest)) {
|
||||
// If the destination directory could not be created stop processing
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Open the source directory to read in files
|
||||
$i = new \DirectoryIterator($src);
|
||||
/** @var \DirectoryIterator $f */
|
||||
foreach ($i as $f) {
|
||||
if ($f->isFile()) {
|
||||
copy($f->getRealPath(), "$dest/" . $f->getFilename());
|
||||
} else {
|
||||
if (!$f->isDot() && $f->isDir()) {
|
||||
static::rcopy($f->getRealPath(), "$dest/$f");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $folder
|
||||
* @return bool
|
||||
@@ -260,30 +330,24 @@ abstract class Folder
|
||||
return @unlink($folder);
|
||||
}
|
||||
|
||||
// Go through all items in filesystem and recursively remove everything.
|
||||
$files = array_diff(scandir($folder), array('.', '..'));
|
||||
foreach ($files as $file) {
|
||||
$path = "{$folder}/{$file}";
|
||||
(is_dir($path)) ? self::doDelete($path) : @unlink($path);
|
||||
$files = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($folder, \RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
|
||||
/** @var \DirectoryIterator $fileinfo */
|
||||
foreach ($files as $fileinfo) {
|
||||
if ($fileinfo->isDir()) {
|
||||
if (false === rmdir($fileinfo->getRealPath())) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (false === unlink($fileinfo->getRealPath())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return @rmdir($folder);
|
||||
return rmdir($folder);
|
||||
}
|
||||
}
|
||||
|
||||
class GravRecursiveFilterIterator extends \RecursiveFilterIterator
|
||||
{
|
||||
public static $FILTERS = array(
|
||||
'..', '.DS_Store'
|
||||
);
|
||||
|
||||
public function accept()
|
||||
{
|
||||
return !in_array(
|
||||
$this->current()->getFilename(),
|
||||
self::$FILTERS,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
namespace Grav\Common\Filesystem;
|
||||
|
||||
class RecursiveFolderFilterIterator extends \RecursiveFilterIterator
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
// only accept directories
|
||||
return $this->current()->isDir();
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Local;
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Iterator;
|
||||
|
||||
class Collection extends Iterator
|
||||
{
|
||||
abstract class AbstractCollection extends Iterator {
|
||||
|
||||
use GravTrait;
|
||||
|
||||
public function toJson()
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $theme) {
|
||||
$items[$name] = $theme->toArray();
|
||||
foreach ($this->items as $name => $package) {
|
||||
$items[$name] = $package->toArray();
|
||||
}
|
||||
|
||||
return json_encode($items);
|
||||
@@ -23,8 +23,8 @@ class Collection extends Iterator
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $theme) {
|
||||
$items[$name] = $theme->toArray();
|
||||
foreach ($this->items as $name => $package) {
|
||||
$items[$name] = $package->toArray();
|
||||
}
|
||||
|
||||
return $items;
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Common;
|
||||
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Iterator;
|
||||
|
||||
abstract class AbstractPackageCollection extends Iterator {
|
||||
|
||||
use GravTrait;
|
||||
|
||||
protected $type;
|
||||
|
||||
public function toJson()
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $package) {
|
||||
$items[$name] = $package->toArray();
|
||||
}
|
||||
|
||||
return json_encode($items);
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $package) {
|
||||
$items[$name] = $package->toArray();
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
21
system/src/Grav/Common/GPM/Common/CachedCollection.php
Normal file
21
system/src/Grav/Common/GPM/Common/CachedCollection.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Common;
|
||||
|
||||
use Grav\Common\Iterator;
|
||||
|
||||
class CachedCollection extends Iterator {
|
||||
|
||||
protected static $cache;
|
||||
|
||||
public function __construct($items)
|
||||
{
|
||||
// local cache to speed things up
|
||||
if (!isset(self::$cache[get_called_class().__METHOD__])) {
|
||||
self::$cache[get_called_class().__METHOD__] = $items;
|
||||
}
|
||||
|
||||
foreach (self::$cache[get_called_class().__METHOD__] as $name => $item) {
|
||||
$this->append([$name => $item]);
|
||||
}
|
||||
}
|
||||
}
|
||||
42
system/src/Grav/Common/GPM/Common/Package.php
Normal file
42
system/src/Grav/Common/GPM/Common/Package.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Common;
|
||||
|
||||
use Grav\Common\Data\Data;
|
||||
|
||||
class Package {
|
||||
|
||||
protected $data;
|
||||
|
||||
public function __construct(Data $package, $type = null) {
|
||||
$this->data = $package;
|
||||
|
||||
if ($type) {
|
||||
$this->data->set('package_type', $type);
|
||||
}
|
||||
}
|
||||
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function __get($key) {
|
||||
return $this->data->get($key);
|
||||
}
|
||||
|
||||
public function __isset($key) {
|
||||
return isset($this->data->$key);
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
return $this->toJson();
|
||||
}
|
||||
|
||||
public function toJson() {
|
||||
return $this->data->toJson();
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
return $this->data->toArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\Inflector;
|
||||
use Grav\Common\Iterator;
|
||||
use Grav\Common\Utils;
|
||||
|
||||
class GPM extends Iterator
|
||||
{
|
||||
@@ -28,6 +30,8 @@ class GPM extends Iterator
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
protected $install_paths = ['plugins' => 'user/plugins/%name%', 'themes' => 'user/themes/%name%', 'skeletons' => 'user/'];
|
||||
|
||||
/**
|
||||
* Creates a new GPM instance with Local and Remote packages available
|
||||
* @param boolean $refresh Applies to Remote Packages only and forces a refetch of data
|
||||
@@ -347,7 +351,20 @@ class GPM extends Iterator
|
||||
$packages = ['total' => 0, 'not_found' => []];
|
||||
|
||||
foreach ($searches as $search) {
|
||||
$repository = '';
|
||||
// if this is an object, get the search data from the key
|
||||
if (is_object($search)) {
|
||||
$search = (array) $search;
|
||||
$key = key($search);
|
||||
$repository = $search[$key];
|
||||
$search = $key;
|
||||
}
|
||||
|
||||
if ($found = $this->findPackage($search)) {
|
||||
// set override respository if provided
|
||||
if ($repository) {
|
||||
$found->override_repository = $repository;
|
||||
}
|
||||
if (!isset($packages[$found->package_type])) {
|
||||
$packages[$found->package_type] = [];
|
||||
}
|
||||
@@ -355,7 +372,20 @@ class GPM extends Iterator
|
||||
$packages[$found->package_type][$found->slug] = $found;
|
||||
$packages['total']++;
|
||||
} else {
|
||||
$packages['not_found'][] = $search;
|
||||
// make a best guess at the type based on the repo URL
|
||||
if (Utils::contains($repository, '-theme')) {
|
||||
$type = 'themes';
|
||||
} else {
|
||||
$type = 'plugins';
|
||||
}
|
||||
|
||||
$not_found = new \stdClass();
|
||||
$not_found->name = Inflector::camelize($search);
|
||||
$not_found->slug = $search;
|
||||
$not_found->package_type = $type;
|
||||
$not_found->install_path = str_replace('%name%', $search, $this->install_paths[$type]);
|
||||
$not_found->override_repository = $repository;
|
||||
$packages['not_found'][$search] = $not_found;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,15 +61,12 @@ class Installer
|
||||
$options = array_merge(self::$options, $options);
|
||||
$install_path = rtrim($destination . DS . ltrim($options['install_path'], DS), DS);
|
||||
|
||||
if (!self::isGravInstance($destination) || !self::isValidDestination($install_path, $options['exclude_checks'])
|
||||
) {
|
||||
if (!self::isGravInstance($destination) || !self::isValidDestination($install_path, $options['exclude_checks'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
self::lastErrorCode() == self::IS_LINK && $options['ignore_symlinks'] ||
|
||||
self::lastErrorCode() == self::EXISTS && !$options['overwrite']
|
||||
) {
|
||||
if (self::lastErrorCode() == self::IS_LINK && $options['ignore_symlinks'] ||
|
||||
self::lastErrorCode() == self::EXISTS && !$options['overwrite']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -112,7 +109,7 @@ class Installer
|
||||
|
||||
}
|
||||
|
||||
public static function nonSophisticatedInstall($zip, $install_path, $tmp)
|
||||
public static function nonSophisticatedInstall(\ZipArchive $zip, $install_path, $tmp)
|
||||
{
|
||||
$container = $zip->getNameIndex(0); // TODO: better way of determining if zip has container folder
|
||||
if (file_exists($install_path)) {
|
||||
@@ -124,7 +121,7 @@ class Installer
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function sophisticatedInstall($zip, $install_path, $tmp)
|
||||
public static function sophisticatedInstall(\ZipArchive $zip, $install_path, $tmp)
|
||||
{
|
||||
for ($i = 0, $l = $zip->numFiles; $i < $l; $i++) {
|
||||
$filename = $zip->getNameIndex($i);
|
||||
@@ -159,6 +156,26 @@ class Installer
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unnstalls one or more given package
|
||||
*
|
||||
* @param string $path The slug of the package(s)
|
||||
* @param array $options Options to use for uninstalling
|
||||
*
|
||||
* @return boolean True if everything went fine, False otherwise.
|
||||
*/
|
||||
public static function uninstall($path, $options = [])
|
||||
{
|
||||
$options = array_merge(self::$options, $options);
|
||||
if (!self::isValidDestination($path, $options['exclude_checks'])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Folder::delete($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a set of checks on the destination and sets the Error if any
|
||||
*
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
use Grav\Common\GPM\Common\AbstractPackageCollection as BaseCollection;
|
||||
use Grav\Common\GPM\Local\Package;
|
||||
|
||||
abstract class AbstractPackageCollection extends BaseCollection {
|
||||
|
||||
public function __construct($items)
|
||||
{
|
||||
foreach ($items as $name => $data) {
|
||||
$this->items[$name] = new Package($data, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,38 +2,24 @@
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\GPM\Common\Package as BasePackage;
|
||||
|
||||
/**
|
||||
* Class Package
|
||||
* @package Grav\Common\GPM\Local
|
||||
*/
|
||||
class Package
|
||||
class Package extends BasePackage
|
||||
{
|
||||
/**
|
||||
* @var Data
|
||||
*/
|
||||
protected $data;
|
||||
/**
|
||||
* @var \Grav\Common\Data\Blueprint
|
||||
*/
|
||||
protected $blueprints;
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @param Data $package
|
||||
* @param bool $package_type
|
||||
*/
|
||||
public function __construct(Data $package, $package_type = false)
|
||||
public function __construct(Data $package, $package_type = null)
|
||||
{
|
||||
$this->data = $package;
|
||||
$this->blueprints = $this->data->blueprints();
|
||||
$data = new Data($package->blueprints()->toArray());
|
||||
parent::__construct($data, $package_type);
|
||||
|
||||
if ($package_type) {
|
||||
$html_description = \Parsedown::instance()->line($this->blueprints->get('description'));
|
||||
$this->blueprints->set('package_type', $package_type);
|
||||
$this->blueprints->set('description_html', $html_description);
|
||||
$this->blueprints->set('description_plain', strip_tags($html_description));
|
||||
$this->blueprints->set('symlink', is_link(USER_DIR . $package_type . DS . $this->blueprints->name));
|
||||
}
|
||||
$this->settings = $package->toArray();
|
||||
|
||||
$html_description = \Parsedown::instance()->line($this->description);
|
||||
$this->data->set('slug', $this->name);
|
||||
$this->data->set('description_html', $html_description);
|
||||
$this->data->set('description_plain', strip_tags($html_description));
|
||||
$this->data->set('symlink', is_link(USER_DIR . $package_type . DS . $this->name));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,47 +27,6 @@ class Package
|
||||
*/
|
||||
public function isEnabled()
|
||||
{
|
||||
return $this->data['enabled'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Data
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
return $this->blueprints->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->toJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function toJson()
|
||||
{
|
||||
return $this->blueprints->toJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->blueprints->toArray();
|
||||
return $this->settings['enabled'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,17 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
use Grav\Common\Iterator;
|
||||
use Grav\Common\GPM\Common\CachedCollection;
|
||||
|
||||
class Packages extends Iterator
|
||||
class Packages extends CachedCollection
|
||||
{
|
||||
private $plugins;
|
||||
private $themes;
|
||||
protected static $cache;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// local cache to speed things up
|
||||
if (!isset(self::$cache[__METHOD__])) {
|
||||
self::$cache[__METHOD__] = [
|
||||
'plugins' => new Plugins(),
|
||||
'themes' => new Themes()
|
||||
];
|
||||
}
|
||||
$items = [
|
||||
'plugins' => new Plugins(),
|
||||
'themes' => new Themes()
|
||||
];
|
||||
|
||||
$this->plugins = self::$cache[__METHOD__]['plugins'];
|
||||
$this->themes = self::$cache[__METHOD__]['themes'];
|
||||
|
||||
$this->append(['plugins' => $this->plugins]);
|
||||
$this->append(['themes' => $this->themes]);
|
||||
parent::__construct($items);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,22 +5,18 @@ namespace Grav\Common\GPM\Local;
|
||||
* Class Plugins
|
||||
* @package Grav\Common\GPM\Local
|
||||
*/
|
||||
class Plugins extends Collection
|
||||
class Plugins extends AbstractPackageCollection
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $type = 'plugins';
|
||||
protected $type = 'plugins';
|
||||
|
||||
/**
|
||||
* Local Plugins Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$grav = self::$grav;
|
||||
|
||||
foreach ($grav['plugins']->all() as $name => $data) {
|
||||
$this->items[$name] = new Package($data, $this->type);
|
||||
}
|
||||
parent::__construct(self::getGrav()['plugins']->all());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Local;
|
||||
|
||||
class Themes extends Collection
|
||||
/**
|
||||
* Class Themes
|
||||
* @package Grav\Common\GPM\Local
|
||||
*/
|
||||
class Themes extends AbstractPackageCollection
|
||||
{
|
||||
private $type = 'themes';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'themes';
|
||||
|
||||
/**
|
||||
* Local Themes Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$grav = self::$grav;
|
||||
|
||||
foreach ($grav['themes']->all() as $name => $data) {
|
||||
$this->items[$name] = new Package($data, $this->type);
|
||||
}
|
||||
parent::__construct(self::getGrav()['themes']->all());
|
||||
}
|
||||
}
|
||||
|
||||
58
system/src/Grav/Common/GPM/PackageInterface.php
Normal file
58
system/src/Grav/Common/GPM/PackageInterface.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\Data\Data;
|
||||
|
||||
/**
|
||||
* Interface Package
|
||||
* @package Grav\Common\GPM
|
||||
*/
|
||||
class Package
|
||||
{
|
||||
/**
|
||||
* @var Data
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @var \Grav\Common\Data\Blueprint
|
||||
*/
|
||||
protected $blueprints;
|
||||
|
||||
/**
|
||||
* @param Data $package
|
||||
* @param bool $package_type
|
||||
*/
|
||||
public function __construct(Data $package, $package_type = false);
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function isEnabled();
|
||||
|
||||
/**
|
||||
* @return Data
|
||||
*/
|
||||
public function getData();
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($key);
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function toJson();
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray();
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
use Grav\Common\GPM\Common\AbstractPackageCollection as BaseCollection;
|
||||
use Grav\Common\GPM\Response;
|
||||
|
||||
use \Doctrine\Common\Cache\FilesystemCache;
|
||||
|
||||
class AbstractPackageCollection extends BaseCollection
|
||||
{
|
||||
/**
|
||||
* The cached data previously fetched
|
||||
* @var string
|
||||
*/
|
||||
protected $raw;
|
||||
|
||||
/**
|
||||
* The lifetime to store the entry in seconds
|
||||
* @var integer
|
||||
*/
|
||||
private $lifetime = 86400;
|
||||
|
||||
protected $repository;
|
||||
|
||||
protected $cache;
|
||||
|
||||
public function __construct($repository = null, $refresh = false, $callback = null)
|
||||
{
|
||||
if ($repository === null) {
|
||||
throw new \RuntimeException("A repository is required to indicate the origin of the remote collection");
|
||||
}
|
||||
|
||||
$cache_dir = self::getGrav()['locator']->findResource('cache://gpm', true, true);
|
||||
$this->cache = new FilesystemCache($cache_dir);
|
||||
|
||||
$this->repository = $repository;
|
||||
$this->raw = $this->cache->fetch(md5($this->repository));
|
||||
|
||||
$this->fetch($refresh, $callback);
|
||||
foreach (json_decode($this->raw, true) as $slug => $data) {
|
||||
$this->items[$slug] = new Package($data, $this->type);
|
||||
}
|
||||
}
|
||||
|
||||
public function fetch($refresh = false, $callback = null)
|
||||
{
|
||||
if (!$this->raw || $refresh) {
|
||||
$response = Response::get($this->repository, [], $callback);
|
||||
$this->raw = $response;
|
||||
$this->cache->save(md5($this->repository), $this->raw, $this->lifetime);
|
||||
}
|
||||
|
||||
return $this->raw;
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
use Grav\Common\GPM\Response;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Iterator;
|
||||
use \Doctrine\Common\Cache\Cache as DoctrineCache;
|
||||
use \Doctrine\Common\Cache\FilesystemCache;
|
||||
|
||||
class Collection extends Iterator {
|
||||
use GravTrait;
|
||||
|
||||
/**
|
||||
* The cached data previously fetched
|
||||
* @var string
|
||||
*/
|
||||
protected $raw;
|
||||
|
||||
/**
|
||||
* The lifetime to store the entry in seconds
|
||||
* @var integer
|
||||
*/
|
||||
private $lifetime = 86400;
|
||||
private $repository;
|
||||
private $cache;
|
||||
|
||||
private $plugins, $themes;
|
||||
|
||||
public function __construct($repository = null) {
|
||||
if ($repository == null) {
|
||||
throw new \RuntimeException("A repository is required for storing the cache");
|
||||
}
|
||||
|
||||
$cache_dir = self::$grav['locator']->findResource('cache://gpm', true, true);
|
||||
$this->cache = new FilesystemCache($cache_dir);
|
||||
|
||||
$this->repository = $repository;
|
||||
$this->raw = $this->cache->fetch(md5($this->repository));
|
||||
}
|
||||
|
||||
public function toJson() {
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $theme) {
|
||||
$items[$name] = $theme->toArray();
|
||||
}
|
||||
|
||||
return json_encode($items);
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $name => $theme) {
|
||||
$items[$name] = $theme->toArray();
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function fetch($refresh = false, $callback = null) {
|
||||
if (!$this->raw || $refresh) {
|
||||
$response = Response::get($this->repository, [], $callback);
|
||||
$this->raw = $response;
|
||||
$this->cache->save(md5($this->repository), $this->raw, $this->lifetime);
|
||||
}
|
||||
|
||||
return $this->raw;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
class Grav extends Collection
|
||||
use \Doctrine\Common\Cache\FilesystemCache;
|
||||
|
||||
class Grav extends AbstractPackageCollection
|
||||
{
|
||||
private $repository = 'http://getgrav.org/downloads/grav.json';
|
||||
protected $repository = 'http://getgrav.org/downloads/grav.json';
|
||||
private $data;
|
||||
|
||||
private $version;
|
||||
@@ -15,15 +17,17 @@ class Grav extends Collection
|
||||
*/
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
parent::__construct($this->repository);
|
||||
$cache_dir = self::getGrav()['locator']->findResource('cache://gpm', true, true);
|
||||
$this->cache = new FilesystemCache($cache_dir);
|
||||
$this->raw = $this->cache->fetch(md5($this->repository));
|
||||
|
||||
$this->fetch($refresh, $callback);
|
||||
$this->data = json_decode($this->raw);
|
||||
|
||||
$this->version = @$this->data->version ?: '-';
|
||||
$this->date = @$this->data->date ?: '-';
|
||||
$this->data = json_decode($this->raw, true);
|
||||
$this->version = isset($this->data['version']) ? $this->data['version'] : '-';
|
||||
$this->date = isset($this->data['date']) ? $this->data['date'] : '-';
|
||||
|
||||
foreach ($this->data->assets as $slug => $data) {
|
||||
foreach ($this->data['assets'] as $slug => $data) {
|
||||
$this->items[$slug] = new Package($data);
|
||||
}
|
||||
}
|
||||
@@ -34,7 +38,7 @@ class Grav extends Collection
|
||||
*/
|
||||
public function getAssets()
|
||||
{
|
||||
return $this->data->assets;
|
||||
return $this->data['assets'];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,11 +50,11 @@ class Grav extends Collection
|
||||
public function getChangelog($diff = null)
|
||||
{
|
||||
if (!$diff) {
|
||||
return $this->data->changelog;
|
||||
return $this->data['changelog'];
|
||||
}
|
||||
|
||||
$diffLog = [];
|
||||
foreach ($this->data->changelog as $version => $changelog) {
|
||||
foreach ($this->data['changelog'] as $version => $changelog) {
|
||||
preg_match("/[\d\.]+/", $version, $cleanVersion);
|
||||
|
||||
if (!$cleanVersion || version_compare($diff, $cleanVersion[0], ">=")) { continue; }
|
||||
|
||||
@@ -1,32 +1,12 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
class Package {
|
||||
public function __construct($package, $package_type = false) {
|
||||
$this->data = $package;
|
||||
if ($package_type) {
|
||||
$this->data->package_type = $package_type;
|
||||
}
|
||||
}
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\GPM\Common\Package as BasePackage;
|
||||
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
class Package extends BasePackage {
|
||||
public function __construct($package, $package_type = null) {
|
||||
$data = new Data($package);
|
||||
parent::__construct($data, $package_type);
|
||||
}
|
||||
|
||||
public function __get($key) {
|
||||
return $this->data->$key;
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
return $this->toJson();
|
||||
}
|
||||
|
||||
public function toJson() {
|
||||
return json_encode($this->data);
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,28 +1,17 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
use Grav\Common\Iterator;
|
||||
use Grav\Common\GPM\Common\CachedCollection;
|
||||
|
||||
class Packages extends Iterator
|
||||
class Packages extends CachedCollection
|
||||
{
|
||||
private $plugins;
|
||||
private $themes;
|
||||
protected static $cache;
|
||||
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
// local cache to speed things up
|
||||
if (!isset(self::$cache[__METHOD__])) {
|
||||
self::$cache[__METHOD__] = [
|
||||
'plugins' => new Plugins($refresh, $callback),
|
||||
'themes' => new Themes($refresh, $callback)
|
||||
];
|
||||
}
|
||||
$items = [
|
||||
'plugins' => new Plugins($refresh, $callback),
|
||||
'themes' => new Themes($refresh, $callback)
|
||||
];
|
||||
|
||||
$this->plugins = self::$cache[__METHOD__]['plugins']->toArray();
|
||||
$this->themes = self::$cache[__METHOD__]['themes']->toArray();
|
||||
|
||||
$this->append(['plugins' => $this->plugins]);
|
||||
$this->append(['themes' => $this->themes]);
|
||||
parent::__construct($items);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
class Plugins extends Collection
|
||||
/**
|
||||
* Class Plugins
|
||||
* @package Grav\Common\GPM\Remote
|
||||
*/
|
||||
class Plugins extends AbstractPackageCollection
|
||||
{
|
||||
private $repository = 'http://getgrav.org/downloads/plugins.json';
|
||||
private $type = 'plugins';
|
||||
private $data;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'plugins';
|
||||
|
||||
protected $repository = 'http://getgrav.org/downloads/plugins.json';
|
||||
|
||||
/**
|
||||
* Local Plugins Constructor
|
||||
*/
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
parent::__construct($this->repository);
|
||||
|
||||
$this->fetch($refresh, $callback);
|
||||
$this->data = json_decode($this->raw);
|
||||
|
||||
foreach ($this->data as $slug => $data) {
|
||||
$this->items[$slug] = new Package($data, $this->type);
|
||||
}
|
||||
parent::__construct($this->repository, $refresh, $callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM\Remote;
|
||||
|
||||
class Themes extends Collection
|
||||
/**
|
||||
* Class Themes
|
||||
* @package Grav\Common\GPM\Remote
|
||||
*/
|
||||
class Themes extends AbstractPackageCollection
|
||||
{
|
||||
private $repository = 'http://getgrav.org/downloads/themes.json';
|
||||
private $type = 'themes';
|
||||
private $data;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'themes';
|
||||
|
||||
protected $repository = 'http://getgrav.org/downloads/themes.json';
|
||||
|
||||
/**
|
||||
* Local Themes Constructor
|
||||
*/
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
parent::__construct($this->repository);
|
||||
|
||||
$this->fetch($refresh, $callback);
|
||||
$this->data = json_decode($this->raw);
|
||||
|
||||
foreach ($this->data as $slug => $data) {
|
||||
$this->items[$slug] = new Package($data, $this->type);
|
||||
}
|
||||
parent::__construct($this->repository, $refresh, $callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,6 @@ class Response
|
||||
$method = 'get' . ucfirst(strtolower(self::$method));
|
||||
|
||||
self::$callback = $callback;
|
||||
|
||||
return static::$method($uri, $options, $callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
<?php
|
||||
namespace Grav\Common\GPM;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\GPM\Installer;
|
||||
|
||||
class Upgrader
|
||||
{
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Page\Medium\ImageMedium;
|
||||
use Grav\Common\Page\Pages;
|
||||
use Grav\Common\Service\ConfigServiceProvider;
|
||||
use Grav\Common\Service\ErrorServiceProvider;
|
||||
@@ -9,7 +10,6 @@ use Grav\Common\Service\StreamsServiceProvider;
|
||||
use RocketTheme\Toolbox\DI\Container;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use Grav\Common\Page\Medium;
|
||||
|
||||
/**
|
||||
* Grav
|
||||
@@ -77,7 +77,7 @@ class Grav extends Container
|
||||
return new Cache($c);
|
||||
};
|
||||
$container['plugins'] = function ($c) {
|
||||
return new Plugins($c);
|
||||
return new Plugins();
|
||||
};
|
||||
$container['themes'] = function ($c) {
|
||||
return new Themes($c);
|
||||
@@ -97,30 +97,36 @@ class Grav extends Container
|
||||
$container['page'] = function ($c) {
|
||||
/** @var Pages $pages */
|
||||
$pages = $c['pages'];
|
||||
$page = $pages->dispatch($c['uri']->route());
|
||||
|
||||
/** @var Uri $uri */
|
||||
$uri = $c['uri'];
|
||||
|
||||
$path = rtrim($uri->path(), '/');
|
||||
$path = $path ?: '/';
|
||||
|
||||
$page = $pages->dispatch($path);
|
||||
|
||||
if (!$page || !$page->routable()) {
|
||||
$path_parts = pathinfo($path);
|
||||
$page = $c['pages']->dispatch($path_parts['dirname'], true);
|
||||
if ($page) {
|
||||
$media = $page->media()->all();
|
||||
|
||||
// special case where a media file is requested
|
||||
if (!$page) {
|
||||
$path_parts = pathinfo($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];
|
||||
$parsed_url = parse_url(urldecode($uri->basename()));
|
||||
|
||||
// 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));
|
||||
}
|
||||
$media_file = $parsed_url['path'];
|
||||
|
||||
// if this is a media object, try actions first
|
||||
if (isset($media[$media_file])) {
|
||||
$medium = $media[$media_file];
|
||||
foreach ($uri->query(null, true) as $action => $params) {
|
||||
if (in_array($action, ImageMedium::$magic_actions)) {
|
||||
call_user_func_array(array(&$medium, $action), explode(',', $params));
|
||||
}
|
||||
header('Content-type: '. $mime = $medium->get('mime'));
|
||||
echo file_get_contents($medium->path());
|
||||
die;
|
||||
}
|
||||
Utils::download($medium->path(), false);
|
||||
} else {
|
||||
Utils::download($page->path() . DIRECTORY_SEPARATOR . $uri->basename(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,20 +168,29 @@ class Grav extends Container
|
||||
|
||||
public function process()
|
||||
{
|
||||
// Use output buffering to prevent headers from being sent too early.
|
||||
ob_start();
|
||||
|
||||
/** @var Debugger $debugger */
|
||||
$debugger = $this['debugger'];
|
||||
|
||||
// Initialize configuration.
|
||||
$debugger->startTimer('_config', 'Configuration');
|
||||
$this['config']->init();
|
||||
$this['uri']->init();
|
||||
$this['errors']->resetHandlers();
|
||||
$debugger->init();
|
||||
$this['config']->debug();
|
||||
$debugger->stopTimer('_config');
|
||||
|
||||
// Use output buffering to prevent headers from being sent too early.
|
||||
ob_start();
|
||||
if ($this['config']->get('system.cache.gzip')) {
|
||||
ob_start('ob_gzhandler');
|
||||
}
|
||||
|
||||
// Initialize the timezone
|
||||
if ($this['config']->get('system.timezone')) {
|
||||
date_default_timezone_set($this['config']->get('system.timezone'));
|
||||
}
|
||||
|
||||
$debugger->startTimer('streams', 'Streams');
|
||||
$this['streams'];
|
||||
$debugger->stopTimer('streams');
|
||||
@@ -242,7 +257,13 @@ class Grav extends Container
|
||||
$this['session']->close();
|
||||
}
|
||||
|
||||
header("Location: " . rtrim($uri->rootUrl(), '/') .'/'. trim($route, '/'), true, $code);
|
||||
if ($this['uri']->isExternal($route)) {
|
||||
$url = $route;
|
||||
} else {
|
||||
$url = rtrim($uri->rootUrl(), '/') .'/'. trim($route, '/');
|
||||
}
|
||||
|
||||
header("Location: {$url}", true, $code);
|
||||
exit();
|
||||
}
|
||||
|
||||
@@ -275,12 +296,35 @@ class Grav extends Container
|
||||
public function header()
|
||||
{
|
||||
$extension = $this['uri']->extension();
|
||||
|
||||
/** @var Page $page */
|
||||
$page = $this['page'];
|
||||
|
||||
header('Content-type: ' . $this->mime($extension));
|
||||
|
||||
// Calculate Expires Headers if set to > 0
|
||||
$expires = $page->expires();
|
||||
|
||||
if ($expires > 0) {
|
||||
$expires_date = gmdate('D, d M Y H:i:s', time() + $expires) . ' GMT';
|
||||
header('Cache-Control: max-age=' . $expires_date);
|
||||
header('Expires: '. $expires_date);
|
||||
}
|
||||
|
||||
// Set the last modified time
|
||||
if ($page->lastModified()) {
|
||||
$last_modified_date = gmdate('D, d M Y H:i:s', $page->modified()) . ' GMT';
|
||||
header('Last-Modified: ' . $last_modified_date);
|
||||
}
|
||||
|
||||
// Calculate a Hash based on the raw file
|
||||
if ($page->eTag()) {
|
||||
header('ETag: ' . md5($page->raw() . $page->modified()));
|
||||
}
|
||||
|
||||
// Set debugger data in headers
|
||||
if (!($extension == null || $extension == 'html')) {
|
||||
if (!($extension === null || $extension == 'html')) {
|
||||
$this['debugger']->enabled(false);
|
||||
// $this['debugger']->sendDataInHeaders();
|
||||
}
|
||||
|
||||
// Set HTTP response code
|
||||
@@ -310,21 +354,35 @@ class Grav extends Container
|
||||
public function shutdown()
|
||||
{
|
||||
if ($this['config']->get('system.debugger.shutdown.close_connection')) {
|
||||
|
||||
//stop user abort
|
||||
if (function_exists('ignore_user_abort')) {
|
||||
@ignore_user_abort(true);
|
||||
}
|
||||
|
||||
// close the session
|
||||
if (isset($this['session'])) {
|
||||
$this['session']->close();
|
||||
}
|
||||
|
||||
header('Content-length: ' . ob_get_length());
|
||||
header("Connection: close\r\n");
|
||||
// flush buffer if gzip buffer was started
|
||||
if ($this['config']->get('system.cache.gzip')) {
|
||||
ob_end_flush(); // gzhandler buffer
|
||||
}
|
||||
|
||||
// get lengh and close the connection
|
||||
header('Content-Length: ' . ob_get_length());
|
||||
header("Connection: close");
|
||||
|
||||
// flush the regular buffer
|
||||
ob_end_flush();
|
||||
ob_flush();
|
||||
@ob_flush();
|
||||
flush();
|
||||
|
||||
// fix for fastcgi close connection issue
|
||||
if (function_exists('fastcgi_finish_request')) {
|
||||
@fastcgi_finish_request();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->fireEvent('onShutdown');
|
||||
|
||||
@@ -11,8 +11,11 @@ trait GravTrait
|
||||
/**
|
||||
* @return Grav
|
||||
*/
|
||||
public function getGrav()
|
||||
public static function getGrav()
|
||||
{
|
||||
if (!self::$grav) {
|
||||
self::$grav = Grav::instance();
|
||||
}
|
||||
return self::$grav;
|
||||
}
|
||||
|
||||
|
||||
@@ -84,10 +84,43 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
|
||||
*/
|
||||
public function nth($key)
|
||||
{
|
||||
$items = array_values($this->items);
|
||||
$items = array_keys($this->items);
|
||||
return (isset($items[$key])) ? $this->offsetGet($items[$key]) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first item
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function first()
|
||||
{
|
||||
$items = array_keys($this->items);
|
||||
return $this->offsetGet(array_shift($items));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last item
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function last()
|
||||
{
|
||||
$items = array_keys($this->items);
|
||||
return $this->offsetGet(array_pop($items));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the Iterator
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reverse()
|
||||
{
|
||||
$this->items = array_reverse($this->items);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $needle Searched value.
|
||||
* @return string|bool Key if found, otherwise false.
|
||||
@@ -164,4 +197,23 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter elements from the list
|
||||
* @param callable|null $callback A function the receives ($value, $key) and must return a boolean to indicate filter status
|
||||
* @return $this
|
||||
*/
|
||||
public function filter(callable $callback = null)
|
||||
{
|
||||
foreach ($this->items as $key => $value) {
|
||||
if (
|
||||
($callback && !call_user_func($callback, $value, $key)) ||
|
||||
(!$callback && !(bool) $value)
|
||||
) {
|
||||
unset($this->items[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Markdown;
|
||||
|
||||
class Markdown extends \Parsedown
|
||||
{
|
||||
use MarkdownGravLinkTrait;
|
||||
|
||||
public function __construct($page)
|
||||
{
|
||||
$this->page = $page;
|
||||
$this->BlockTypes['{'] [] = "TwigTag";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Markdown;
|
||||
|
||||
class MarkdownExtra extends \ParsedownExtra
|
||||
{
|
||||
use MarkdownGravLinkTrait;
|
||||
|
||||
public function __construct($page)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->page = $page;
|
||||
$this->BlockTypes['{'] [] = "TwigTag";
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
<?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
|
||||
*/
|
||||
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 = 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 scheme, the file is local
|
||||
if (!isset($url['scheme'])) {
|
||||
|
||||
// convert the URl is required
|
||||
$Excerpt['element']['attributes']['href'] = $this->convertUrl(Uri::build_url($url));
|
||||
}
|
||||
}
|
||||
|
||||
// if this is an image
|
||||
if (isset($Excerpt['element']['attributes']['src'])) {
|
||||
|
||||
$alt = isset($Excerpt['element']['attributes']['alt']) ? $Excerpt['element']['attributes']['alt'] : '';
|
||||
$title = isset($Excerpt['element']['attributes']['title']) ? $Excerpt['element']['attributes']['title'] : '';
|
||||
|
||||
//get the url and parse it
|
||||
$url = parse_url(htmlspecialchars_decode($Excerpt['element']['attributes']['src']));
|
||||
|
||||
// if there is no host set but there is a path, the file is local
|
||||
if (!isset($url['host']) && isset($url['path'])) {
|
||||
// get the media objects for this page
|
||||
$media = $this->page->media();
|
||||
|
||||
// if there is a media file that matches the path referenced..
|
||||
if (isset($media->images()[$url['path']])) {
|
||||
// get the medium object
|
||||
$medium = $media->images()[$url['path']];
|
||||
|
||||
// if there is a query, then parse it and build action calls
|
||||
if (isset($url['query'])) {
|
||||
parse_str($url['query'], $actions);
|
||||
}
|
||||
|
||||
// loop through actions for the image and call them
|
||||
foreach ($actions as $action => $params) {
|
||||
// as long as it's a valid action
|
||||
if (in_array($action, Medium::$valid_actions)) {
|
||||
call_user_func_array(array(&$medium, $action), explode(',', $params));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the URL for regular images, or an array of bits needed to put together
|
||||
// the lightbox HTML
|
||||
if (!isset($actions['lightbox'])) {
|
||||
$src = $medium->url();
|
||||
} else {
|
||||
$src = $medium->lightboxRaw();
|
||||
}
|
||||
|
||||
// set the src element with the new generated url
|
||||
if (!isset($actions['lightbox']) && !is_array($src)) {
|
||||
$Excerpt['element']['attributes']['src'] = $src;
|
||||
} else {
|
||||
|
||||
// Create the custom lightbox element
|
||||
$Element = array(
|
||||
'name' => 'a',
|
||||
'attributes' => array('rel' => $src['a_rel'], 'href' => $src['a_url']),
|
||||
'handler' => 'element',
|
||||
'text' => array(
|
||||
'name' => 'img',
|
||||
'attributes' => array('src' => $src['img_url'], 'alt' => $alt, 'title' => $title)
|
||||
),
|
||||
);
|
||||
|
||||
// Set the lightbox element on the Excerpt
|
||||
$Excerpt['element'] = $Element;
|
||||
}
|
||||
} else {
|
||||
// not a current page media file, see if it needs converting to relative
|
||||
$Excerpt['element']['attributes']['src'] = $this->convertUrl(Uri::build_url($url));
|
||||
}
|
||||
}
|
||||
}
|
||||
return $Excerpt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts links from absolute '/' or relative (../..) to a grav friendly format
|
||||
* @param string $markdown_url the URL as it was written in the markdown
|
||||
* @return string the more friendly formatted url
|
||||
*/
|
||||
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) {
|
||||
$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();
|
||||
|
||||
// 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), '/')));
|
||||
}
|
||||
|
||||
// else its a relative path already
|
||||
$newpath = array();
|
||||
$paths = explode('/', $markdown_url);
|
||||
|
||||
// remove the updirectory references (..)
|
||||
foreach ($paths as $path) {
|
||||
if ($path == '..') {
|
||||
$relative_path = dirname($relative_path);
|
||||
} else {
|
||||
$newpath[] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
// build the new url
|
||||
$new_url = $relative_path . '/' . implode('/', $newpath);
|
||||
}
|
||||
|
||||
return $new_url;
|
||||
}
|
||||
}
|
||||
13
system/src/Grav/Common/Markdown/Parsedown.php
Normal file
13
system/src/Grav/Common/Markdown/Parsedown.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
namespace Grav\Common\Markdown;
|
||||
|
||||
class Parsedown extends \Parsedown
|
||||
{
|
||||
use ParsedownGravTrait;
|
||||
|
||||
public function __construct($page, $defaults)
|
||||
{
|
||||
$this->init($page, $defaults);
|
||||
}
|
||||
|
||||
}
|
||||
13
system/src/Grav/Common/Markdown/ParsedownExtra.php
Normal file
13
system/src/Grav/Common/Markdown/ParsedownExtra.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
namespace Grav\Common\Markdown;
|
||||
|
||||
class ParsedownExtra extends \ParsedownExtra
|
||||
{
|
||||
use ParsedownGravTrait;
|
||||
|
||||
public function __construct($page, $defaults)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->init($page, $defaults);
|
||||
}
|
||||
}
|
||||
213
system/src/Grav/Common/Markdown/ParsedownGravTrait.php
Normal file
213
system/src/Grav/Common/Markdown/ParsedownGravTrait.php
Normal file
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
namespace Grav\Common\Markdown;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Debugger;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Page\Medium\Medium;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Utils;
|
||||
|
||||
/**
|
||||
* A trait to add some custom processing to the identifyLink() method in Parsedown and ParsedownExtra
|
||||
*/
|
||||
trait ParsedownGravTrait
|
||||
{
|
||||
use GravTrait;
|
||||
protected $page;
|
||||
protected $pages;
|
||||
protected $base_url;
|
||||
protected $pages_dir;
|
||||
protected $special_chars;
|
||||
|
||||
protected $twig_link_regex = '/\!*\[(?:.*)\]\((\{([\{%#])\s*(.*?)\s*(?:\2|\})\})\)/';
|
||||
|
||||
/**
|
||||
* Initialiazation function to setup key variables needed by the MarkdownGravLinkTrait
|
||||
*
|
||||
* @param $page
|
||||
* @param $defaults
|
||||
*/
|
||||
protected function init($page, $defaults)
|
||||
{
|
||||
$this->page = $page;
|
||||
$this->pages = self::getGrav()['pages'];
|
||||
$this->BlockTypes['{'] [] = "TwigTag";
|
||||
$this->base_url = rtrim(self::getGrav()['base_url'] . self::getGrav()['pages']->base(), '/');
|
||||
$this->pages_dir = self::getGrav()['locator']->findResource('page://');
|
||||
$this->special_chars = array('>' => 'gt', '<' => 'lt', '"' => 'quot');
|
||||
|
||||
if ($defaults === null) {
|
||||
$defaults = self::getGrav()['config']->get('system.pages.markdown');
|
||||
}
|
||||
|
||||
$this->setBreaksEnabled($defaults['auto_line_breaks']);
|
||||
$this->setUrlsLinked($defaults['auto_url_links']);
|
||||
$this->setMarkupEscaped($defaults['escape_markup']);
|
||||
$this->setSpecialChars($defaults['special_chars']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the element function publicly accessible, Medium uses this to render from Twig
|
||||
*
|
||||
* @param array $Element
|
||||
* @return string markup
|
||||
*/
|
||||
public function elementToHtml(array $Element)
|
||||
{
|
||||
return $this->element($Element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for special chars
|
||||
*
|
||||
* @param $special_chars
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
function setSpecialChars($special_chars)
|
||||
{
|
||||
$this->special_chars = $special_chars;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure Twig tags are treated as block level items with no <p></p> tags
|
||||
*/
|
||||
protected function blockTwigTag($Line)
|
||||
{
|
||||
if (preg_match('/[{%|{{|{#].*[#}|}}|%}]/', $Line['body'], $matches)) {
|
||||
$Block = array(
|
||||
'markup' => $Line['body'],
|
||||
);
|
||||
return $Block;
|
||||
}
|
||||
}
|
||||
|
||||
protected function inlineSpecialCharacter($Excerpt)
|
||||
{
|
||||
if ($Excerpt['text'][0] === '&' && ! preg_match('/^&#?\w+;/', $Excerpt['text'])) {
|
||||
return array(
|
||||
'markup' => '&',
|
||||
'extent' => 1,
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($this->special_chars[$Excerpt['text'][0]])) {
|
||||
return array(
|
||||
'markup' => '&'.$this->special_chars[$Excerpt['text'][0]].';',
|
||||
'extent' => 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function inlineImage($excerpt)
|
||||
{
|
||||
if (preg_match($this->twig_link_regex, $excerpt['text'], $matches)) {
|
||||
$excerpt['text'] = str_replace($matches[1], '/', $excerpt['text']);
|
||||
$excerpt = parent::inlineImage($excerpt);
|
||||
$excerpt['element']['attributes']['src'] = $matches[1];
|
||||
$excerpt['extent'] = $excerpt['extent'] + strlen($matches[1]) - 1;
|
||||
return $excerpt;
|
||||
} else {
|
||||
$excerpt = parent::inlineImage($excerpt);
|
||||
}
|
||||
|
||||
// Some stuff we will need
|
||||
$actions = array();
|
||||
$media = null;
|
||||
|
||||
// if this is an image
|
||||
if (isset($excerpt['element']['attributes']['src'])) {
|
||||
$alt = $excerpt['element']['attributes']['alt'] ?: '';
|
||||
$title = $excerpt['element']['attributes']['title'] ?: '';
|
||||
$class = isset($excerpt['element']['attributes']['class']) ? $excerpt['element']['attributes']['class'] : '';
|
||||
|
||||
//get the url and parse it
|
||||
$url = parse_url(htmlspecialchars_decode($excerpt['element']['attributes']['src']));
|
||||
|
||||
// if there is no host set but there is a path, the file is local
|
||||
if (!isset($url['host']) && isset($url['path'])) {
|
||||
$path_parts = pathinfo($url['path']);
|
||||
|
||||
// get the local path to page media if possible
|
||||
if ($path_parts['dirname'] == $this->page->url()) {
|
||||
$url['path'] = $path_parts['basename'];
|
||||
// get the media objects for this page
|
||||
$media = $this->page->media();
|
||||
} else {
|
||||
// see if this is an external page to this one
|
||||
$page_route = str_replace($this->base_url, '', $path_parts['dirname']);
|
||||
|
||||
$ext_page = $this->pages->dispatch($page_route, true);
|
||||
if ($ext_page) {
|
||||
$media = $ext_page->media();
|
||||
$url['path'] = $path_parts['basename'];
|
||||
}
|
||||
}
|
||||
|
||||
// if there is a media file that matches the path referenced..
|
||||
if ($media && isset($media->all()[$url['path']])) {
|
||||
// get the medium object
|
||||
$medium = $media->all()[$url['path']];
|
||||
|
||||
// if there is a query, then parse it and build action calls
|
||||
if (isset($url['query'])) {
|
||||
$actions = array_reduce(explode('&', $url['query']), function ($carry, $item) {
|
||||
$parts = explode('=', $item, 2);
|
||||
$value = isset($parts[1]) ? $parts[1] : null;
|
||||
$carry[] = [ 'method' => $parts[0], 'params' => $value ];
|
||||
|
||||
return $carry;
|
||||
}, []);
|
||||
}
|
||||
|
||||
// loop through actions for the image and call them
|
||||
foreach ($actions as $action) {
|
||||
$medium = call_user_func_array(array($medium, $action['method']), explode(',', $action['params']));
|
||||
}
|
||||
|
||||
if (isset($url['fragment'])) {
|
||||
$medium->urlHash($url['fragment']);
|
||||
}
|
||||
|
||||
$excerpt['element'] = $medium->parseDownElement($title, $alt, $class);
|
||||
|
||||
} else {
|
||||
// not a current page media file, see if it needs converting to relative
|
||||
$excerpt['element']['attributes']['src'] = Uri::buildUrl($url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $excerpt;
|
||||
}
|
||||
|
||||
protected function inlineLink($excerpt)
|
||||
{
|
||||
// do some trickery to get around Parsedown requirement for valid URL if its Twig in there
|
||||
if (preg_match($this->twig_link_regex, $excerpt['text'], $matches)) {
|
||||
$excerpt['text'] = str_replace($matches[1], '/', $excerpt['text']);
|
||||
$excerpt = parent::inlineLink($excerpt);
|
||||
$excerpt['element']['attributes']['href'] = $matches[1];
|
||||
$excerpt['extent'] = $excerpt['extent'] + strlen($matches[1]) - 1;
|
||||
return $excerpt;
|
||||
} else {
|
||||
$excerpt = parent::inlineLink($excerpt);
|
||||
}
|
||||
|
||||
// if this is a link
|
||||
if (isset($excerpt['element']['attributes']['href'])) {
|
||||
$url = parse_url(htmlspecialchars_decode($excerpt['element']['attributes']['href']));
|
||||
|
||||
// if there is no scheme, the file is local
|
||||
if (!isset($url['scheme']) && (count($url) > 0)) {
|
||||
// convert the URl is required
|
||||
$excerpt['element']['attributes']['href'] = Uri::convertUrl($this->page, Uri::buildUrl($url));
|
||||
}
|
||||
}
|
||||
|
||||
return $excerpt;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -34,6 +35,17 @@ class Collection extends Iterator
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Create a copy of this collection
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function copy()
|
||||
{
|
||||
return new static($this->items, $this->params, $this->pages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set parameters to the Collection
|
||||
*
|
||||
@@ -175,12 +187,16 @@ class Collection extends Iterator
|
||||
*/
|
||||
public function adjacentSibling($path, $direction = 1)
|
||||
{
|
||||
|
||||
$values = array_keys($this->items);
|
||||
$keys = array_flip($values);
|
||||
$index = $keys[$path] - $direction;
|
||||
|
||||
return isset($values[$index]) ? $this->offsetGet($values[$index]) : $this;
|
||||
if (array_key_exists($path, $keys)) {
|
||||
$index = $keys[$path] - $direction;
|
||||
|
||||
return isset($values[$index]) ? $this->offsetGet($values[$index]) : $this;
|
||||
}
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,10 +205,39 @@ 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the items between a set of date ranges where second value is optional
|
||||
* Dates can be passed in as text that strtotime() can process
|
||||
* http://php.net/manual/en/function.strtotime.php
|
||||
*
|
||||
* @param $startDate
|
||||
* @param bool $endDate
|
||||
*
|
||||
* @return $this
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function dateRange($startDate, $endDate = false)
|
||||
{
|
||||
$start = strtotime($startDate);
|
||||
$end = $endDate ? strtotime($endDate) : strtotime("now +1000 years");
|
||||
|
||||
$date_range = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page->date() > $start && $page->date() < $end) {
|
||||
$date_range[$path] = $slug;
|
||||
}
|
||||
}
|
||||
$this->items = $date_range;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only visible pages
|
||||
*
|
||||
@@ -205,10 +250,106 @@ class Collection extends Iterator
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page->visible()) {
|
||||
$visible[$path] = $slug;
|
||||
$visible[$path] = $slug;
|
||||
}
|
||||
}
|
||||
return new static($visible, $this->params, $this->pages);
|
||||
$this->items = $visible;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only non-visible pages
|
||||
*
|
||||
* @return Collection The collection with only non-visible pages
|
||||
*/
|
||||
public function nonVisible()
|
||||
{
|
||||
$visible = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if (!$page->visible()) {
|
||||
$visible[$path] = $slug;
|
||||
}
|
||||
}
|
||||
$this->items = $visible;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only modular pages
|
||||
*
|
||||
* @return Collection The collection with only modular pages
|
||||
*/
|
||||
public function modular()
|
||||
{
|
||||
$modular = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page->modular()) {
|
||||
$modular[$path] = $slug;
|
||||
}
|
||||
}
|
||||
$this->items = $modular;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only non-modular pages
|
||||
*
|
||||
* @return Collection The collection with only non-modular pages
|
||||
*/
|
||||
public function nonModular()
|
||||
{
|
||||
$modular = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if (!$page->modular()) {
|
||||
$modular[$path] = $slug;
|
||||
}
|
||||
}
|
||||
$this->items = $modular;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only published pages
|
||||
*
|
||||
* @return Collection The collection with only published pages
|
||||
*/
|
||||
public function published()
|
||||
{
|
||||
$published = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page->published()) {
|
||||
$published[$path] = $slug;
|
||||
}
|
||||
}
|
||||
$this->items = $published;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only non-published pages
|
||||
*
|
||||
* @return Collection The collection with only non-published pages
|
||||
*/
|
||||
public function nonPublished()
|
||||
{
|
||||
$published = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if (!$page->published()) {
|
||||
$published[$path] = $slug;
|
||||
}
|
||||
}
|
||||
$this->items = $published;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,12 +361,33 @@ class Collection extends Iterator
|
||||
{
|
||||
$routable = [];
|
||||
|
||||
foreach (array_keys($this->items) as $path => $slug) {
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page->routable()) {
|
||||
$routable[$path] = $slug;
|
||||
}
|
||||
}
|
||||
return new static($routable, $this->params, $this->pages);
|
||||
|
||||
$this->items = $routable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only non-routable pages
|
||||
*
|
||||
* @return Collection The collection with only non-routable pages
|
||||
*/
|
||||
public function nonRoutable()
|
||||
{
|
||||
$routable = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if (!$page->routable()) {
|
||||
$routable[$path] = $slug;
|
||||
}
|
||||
}
|
||||
$this->items = $routable;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
11
system/src/Grav/Common/Page/Header.php
Normal file
11
system/src/Grav/Common/Page/Header.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
use RocketTheme\Toolbox\ArrayTraits\Constructor;
|
||||
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccess;
|
||||
|
||||
class Header implements \ArrayAccess
|
||||
{
|
||||
use NestedArrayAccess, Constructor;
|
||||
}
|
||||
@@ -5,6 +5,8 @@ use Grav\Common\Getters;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Page\Medium\Medium;
|
||||
use Grav\Common\Page\Medium\MediumFactory;
|
||||
|
||||
/**
|
||||
* Media is a holder object that contains references to the media of page. This object is created and
|
||||
@@ -23,6 +25,7 @@ class Media extends Getters
|
||||
protected $instances = array();
|
||||
protected $images = array();
|
||||
protected $videos = array();
|
||||
protected $audios = array();
|
||||
protected $files = array();
|
||||
|
||||
/**
|
||||
@@ -37,88 +40,102 @@ class Media extends Getters
|
||||
|
||||
$this->path = $path;
|
||||
|
||||
$iterator = new \DirectoryIterator($path);
|
||||
$iterator = new \FilesystemIterator($path, \FilesystemIterator::UNIX_PATHS | \FilesystemIterator::SKIP_DOTS);
|
||||
|
||||
$media = [];
|
||||
|
||||
/** @var \DirectoryIterator $info */
|
||||
foreach ($iterator as $info) {
|
||||
foreach ($iterator as $path => $info) {
|
||||
// Ignore folders and Markdown files.
|
||||
if ($info->isDot() || !$info->isFile() || $info->getExtension() == 'md') {
|
||||
if (!$info->isFile() || $info->getExtension() == 'md' || $info->getBasename() === '.DS_Store') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find out the real filename, in case of we are at the metadata.
|
||||
$filename = $info->getFilename();
|
||||
list($basename, $ext, $meta) = $this->getFileParts($filename);
|
||||
// Find out what type we're dealing with
|
||||
list($basename, $ext, $type, $extra) = $this->getFileParts($info->getFilename());
|
||||
|
||||
$media["{$basename}.{$ext}"] = isset($media["{$basename}.{$ext}"]) ? $media["{$basename}.{$ext}"] : [];
|
||||
|
||||
if ($type === 'alternative') {
|
||||
$media["{$basename}.{$ext}"][$type] = isset($media["{$basename}.{$ext}"][$type]) ? $media["{$basename}.{$ext}"][$type] : [];
|
||||
$media["{$basename}.{$ext}"][$type][$extra] = [ 'file' => $path, 'size' => $info->getSize() ];
|
||||
} else {
|
||||
$media["{$basename}.{$ext}"][$type] = [ 'file' => $path, 'size' => $info->getSize() ];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($media as $name => $types) {
|
||||
// First prepare the alternatives in case there is no base medium
|
||||
if (!empty($types['alternative'])) {
|
||||
foreach ($types['alternative'] as $ratio => &$alt) {
|
||||
$alt['file'] = MediumFactory::fromFile($alt['file']);
|
||||
|
||||
if (!$alt['file']) {
|
||||
unset($types['alternative'][$ratio]);
|
||||
} else {
|
||||
$alt['file']->set('size', $alt['size']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the base medium
|
||||
if (!empty($types['base'])) {
|
||||
$medium = MediumFactory::fromFile($types['base']['file']);
|
||||
$medium && $medium->set('size', $types['base']['size']);
|
||||
} else if (!empty($types['alternative'])) {
|
||||
$altMedium = reset($types['alternative']);
|
||||
$ratio = key($types['alternative']);
|
||||
|
||||
$altMedium = $altMedium['file'];
|
||||
|
||||
$medium = MediumFactory::scaledFromMedium($altMedium, $ratio, 1);
|
||||
}
|
||||
|
||||
// Get medium instance creating it if it didn't exist.
|
||||
$medium = $this->get("{$basename}.{$ext}", true);
|
||||
if (!$medium) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//set file size
|
||||
$medium->set('size', $info->getSize());
|
||||
|
||||
// Assign meta files to the medium.
|
||||
if ($meta) {
|
||||
$medium->addMetaFile($meta);
|
||||
if (!empty($types['meta'])) {
|
||||
$medium->addMetaFile($types['meta']['file']);
|
||||
}
|
||||
|
||||
if (!empty($types['thumb'])) {
|
||||
// We will not turn it into medium yet because user might never request the thumbnail
|
||||
// not wasting any resources on that, maybe we should do this for medium in general?
|
||||
$medium->set('thumbnails.page', $types['thumb']['file']);
|
||||
}
|
||||
|
||||
// Build missing alternatives
|
||||
if (!empty($types['alternative'])) {
|
||||
$alternatives = $types['alternative'];
|
||||
|
||||
$max = max(array_keys($alternatives));
|
||||
|
||||
for ($i=2; $i < $max; $i++) {
|
||||
if (isset($alternatives[$i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$types['alternative'][$i] = MediumFactory::scaledFromMedium($alternatives[$max], $max, $i);
|
||||
}
|
||||
|
||||
foreach ($types['alternative'] as $ratio => $altMedium) {
|
||||
$medium->addAlternative($ratio, $altMedium['file']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->add($name, $medium);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get medium by basename and extension.
|
||||
* Get medium by filename.
|
||||
*
|
||||
* @param string $filename
|
||||
* @param bool $create
|
||||
* @return Medium|null
|
||||
*/
|
||||
public function get($filename, $create = false)
|
||||
public function get($filename)
|
||||
{
|
||||
if ($create && !isset($this->instances[$filename])) {
|
||||
$parts = explode('.', $filename);
|
||||
$ext = array_pop($parts);
|
||||
$basename = implode('.', $parts);
|
||||
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
|
||||
// Check if medium type has been configured.
|
||||
$params = $config->get("media.".strtolower($ext));
|
||||
if (!$params) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$filePath = $this->path . '/' . $filename;
|
||||
|
||||
// Add default settings for undefined variables.
|
||||
$params += $config->get('media.defaults');
|
||||
$params += array(
|
||||
'type' => 'file',
|
||||
'thumb' => 'media/thumb.png',
|
||||
'mime' => 'application/octet-stream',
|
||||
'name' => $filename,
|
||||
'filename' => $filename,
|
||||
'basename' => $basename,
|
||||
'extension' => $ext,
|
||||
'path' => $this->path,
|
||||
'modified' => filemtime($filePath),
|
||||
);
|
||||
|
||||
$lookup = array(
|
||||
USER_DIR . 'images/',
|
||||
SYSTEM_DIR . 'images/',
|
||||
);
|
||||
foreach ($lookup as $path) {
|
||||
if (is_file($path . $params['thumb'])) {
|
||||
$params['thumb'] = $path . $params['thumb'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->add(new Medium($params));
|
||||
}
|
||||
|
||||
return isset($this->instances[$filename]) ? $this->instances[$filename] : null;
|
||||
}
|
||||
|
||||
@@ -155,6 +172,17 @@ class Media extends Getters
|
||||
return $this->videos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all audio media.
|
||||
*
|
||||
* @return array|Medium[]
|
||||
*/
|
||||
public function audios()
|
||||
{
|
||||
ksort($this->audios, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
return $this->audios;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all file media.
|
||||
*
|
||||
@@ -169,18 +197,21 @@ class Media extends Getters
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function add($file)
|
||||
protected function add($name, $file)
|
||||
{
|
||||
$this->instances[$file->filename] = $file;
|
||||
$this->instances[$name] = $file;
|
||||
switch ($file->type) {
|
||||
case 'image':
|
||||
$this->images[$file->filename] = $file;
|
||||
$this->images[$name] = $file;
|
||||
break;
|
||||
case 'video':
|
||||
$this->videos[$file->filename] = $file;
|
||||
$this->videos[$name] = $file;
|
||||
break;
|
||||
case 'audio':
|
||||
$this->audios[$name] = $file;
|
||||
break;
|
||||
default:
|
||||
$this->files[$file->filename] = $file;
|
||||
$this->files[$name] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,19 +226,35 @@ class Media extends Getters
|
||||
$fileParts = explode('.', $filename);
|
||||
|
||||
$name = array_shift($fileParts);
|
||||
$extension = null;
|
||||
while (($part = array_shift($fileParts)) !== null) {
|
||||
if ($part != 'meta') {
|
||||
if (isset($extension)) {
|
||||
$name .= '.' . $extension;
|
||||
$type = 'base';
|
||||
$extra = null;
|
||||
|
||||
if (preg_match('/(.*)@(\d+)x\.(.*)$/', $filename, $matches)) {
|
||||
$name = $matches[1];
|
||||
$extension = $matches[3];
|
||||
$extra = (int) $matches[2];
|
||||
$type = 'alternative';
|
||||
|
||||
if ($extra === 1) {
|
||||
$type = 'base';
|
||||
$extra = null;
|
||||
}
|
||||
} else {
|
||||
$extension = null;
|
||||
while (($part = array_shift($fileParts)) !== null) {
|
||||
if ($part != 'meta' && $part != 'thumb') {
|
||||
if (isset($extension)) {
|
||||
$name .= '.' . $extension;
|
||||
}
|
||||
$extension = $part;
|
||||
} else {
|
||||
$type = $part;
|
||||
$extra = '.' . $part . '.' . implode('.', $fileParts);
|
||||
break;
|
||||
}
|
||||
$extension = $part;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$meta = implode('.', $fileParts);
|
||||
|
||||
return array($name, $extension, $meta);
|
||||
return array($name, $extension, $type, $extra);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,377 +0,0 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Data\Data;
|
||||
use Gregwar\Image\Image as ImageFile;
|
||||
|
||||
/**
|
||||
* The Image medium holds information related to an individual image. These are then stored in the Media object.
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*
|
||||
* @property string $file_name
|
||||
* @property string $type
|
||||
* @property string $name Alias of file_name
|
||||
* @property string $description
|
||||
* @property string $url
|
||||
* @property string $path
|
||||
* @property string $thumb
|
||||
* @property int $width
|
||||
* @property int $height
|
||||
* @property string $mime
|
||||
* @property int $modified
|
||||
*
|
||||
* Medium can have up to 3 files:
|
||||
* - video.mov Medium file itself.
|
||||
* - video.mov.meta.yaml Metadata for the medium.
|
||||
* - video.mov.thumb.jpg Thumbnail image for the medium.
|
||||
*
|
||||
*/
|
||||
class Medium extends Data
|
||||
{
|
||||
use GravTrait;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var ImageFile
|
||||
*/
|
||||
protected $image;
|
||||
|
||||
protected $type = 'guess';
|
||||
protected $quality = 85;
|
||||
|
||||
public static $valid_actions = ['resize', 'forceResize', 'cropResize', 'crop', 'cropZoom',
|
||||
'negate', 'brightness', 'contrast', 'grayscale', 'emboss', 'smooth', 'sharp', 'edge', 'colorize', 'sepia' ];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $meta = array();
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $linkTarget;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $linkAttributes;
|
||||
|
||||
public function __construct($items = array(), Blueprint $blueprint = null)
|
||||
{
|
||||
parent::__construct($items, $blueprint);
|
||||
|
||||
$file_path = $this->get('path') . '/' . $this->get('filename');
|
||||
$file_parts = pathinfo($file_path);
|
||||
|
||||
$this->set('thumb', $file_path);
|
||||
$this->set('extension', $file_parts['extension']);
|
||||
$this->set('filename', $this->get('filename'));
|
||||
|
||||
if ($this->get('type') == 'image') {
|
||||
$image_info = getimagesize($file_path);
|
||||
$this->def('width', $image_info[0]);
|
||||
$this->def('height', $image_info[1]);
|
||||
$this->def('mime', $image_info['mime']);
|
||||
$this->reset();
|
||||
} else {
|
||||
$this->def('mime', 'application/octet-stream');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return string representation of the object (html or url).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->linkImage ? $this->html() : $this->url();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return PATH to file.
|
||||
*
|
||||
* @return string path to file
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
|
||||
if ($this->image) {
|
||||
$output = $this->image->cacheFile($this->type, $this->quality);
|
||||
$this->reset();
|
||||
$output = ROOT_DIR . $output;
|
||||
} else {
|
||||
$output = $this->get('path') . '/' . $this->get('filename');
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the quality of the image
|
||||
* @param Int $quality 0-100 quality
|
||||
* @return Medium
|
||||
*/
|
||||
public function quality($quality)
|
||||
{
|
||||
$this->quality = $quality;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return URL to file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function url()
|
||||
{
|
||||
if ($this->image) {
|
||||
$output = $this->image->cacheFile($this->type, $this->quality);
|
||||
$this->reset();
|
||||
} else {
|
||||
$relPath = preg_replace('|^' . ROOT_DIR . '|', '', $this->get('path'));
|
||||
$output = $relPath . '/' . $this->get('filename');
|
||||
}
|
||||
|
||||
return self::$grav['base_url'] . '/'. $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets image output format.
|
||||
*
|
||||
* @param string $type
|
||||
* @param int $quality
|
||||
* @return $this
|
||||
*/
|
||||
public function format($type = null, $quality = 80)
|
||||
{
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
}
|
||||
|
||||
$this->type = $type;
|
||||
$this->quality = $quality;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <img> tag from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $class
|
||||
* @param string $type
|
||||
* @param int $quality
|
||||
* @return string
|
||||
*/
|
||||
public function img($title = null, $class = null, $type = null, $quality = 80)
|
||||
{
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
}
|
||||
|
||||
$output = $this->html($title, $class, $type, $quality);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return HTML markup from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $class
|
||||
* @param string $type
|
||||
* @param int $quality
|
||||
* @return string
|
||||
*/
|
||||
public function html($title = null, $class = null, $type = null, $quality = 80)
|
||||
{
|
||||
$title = $title ? $title : $this->get('title');
|
||||
$class = $class ? $class : '';
|
||||
|
||||
if ($this->image) {
|
||||
$type = $type ? $type : $this->type;
|
||||
$quality = $quality ? $quality : $this->quality;
|
||||
|
||||
$url = $this->url($type, $quality);
|
||||
$this->reset();
|
||||
|
||||
$output = '<img src="' . $url . '" class="'. $class . '" alt="' . $title . '" />';
|
||||
} else {
|
||||
$output = $title;
|
||||
}
|
||||
|
||||
if ($this->linkTarget) {
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
|
||||
$output = '<a href="' . self::$grav['base_url'] . '/'. $this->linkTarget
|
||||
. '"' . $this->linkAttributes. ' class="'. $class . '">' . $output . '</a>';
|
||||
|
||||
$this->linkTarget = $this->linkAttributes = null;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return lightbox HTML for the medium.
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @return $this
|
||||
*/
|
||||
public function lightbox($width = null, $height = null)
|
||||
{
|
||||
$this->linkAttributes = ' rel="lightbox"';
|
||||
|
||||
return $this->link($width, $height);
|
||||
}
|
||||
|
||||
public function lightboxRaw($width = null, $height = null)
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
$url = $this->url();
|
||||
$this->link($width, $height);
|
||||
$lightbox_url = self::$grav['base_url'] . '/'. $this->linkTarget;
|
||||
|
||||
return array('a_url' => $lightbox_url, 'a_rel' => 'lightbox', 'img_url' => $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return link HTML for the medium.
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @return $this
|
||||
*/
|
||||
public function link($width = null, $height = null)
|
||||
{
|
||||
if ($this->image) {
|
||||
$image = clone $this->image;
|
||||
if ($width && $height) {
|
||||
$image->cropResize($width, $height);
|
||||
}
|
||||
$this->linkTarget = $image->cacheFile($this->type, $this->quality);
|
||||
} else {
|
||||
// TODO: we need to find out URI in a bit better way.
|
||||
$relPath = preg_replace('|^' . ROOT_DIR . '|', '', $this->get('path'));
|
||||
$this->linkTarget = $relPath. '/' . $this->get('filename');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset image.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->image = null;
|
||||
|
||||
if ($this->get('type') == 'image') {
|
||||
$this->image();
|
||||
$this->filter();
|
||||
}
|
||||
$this->type = 'guess';
|
||||
$this->quality = 80;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward the call to the image processing method.
|
||||
*
|
||||
* @param string $method
|
||||
* @param mixed $args
|
||||
* @return $this|mixed
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
if ($method == 'cropZoom') {
|
||||
$method = 'zoomCrop';
|
||||
}
|
||||
|
||||
// Always initialize image.
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
}
|
||||
$result = call_user_func_array(array($this->image, $method), $args);
|
||||
|
||||
// Returns either current object or result of the action.
|
||||
return $result instanceof ImageFile ? $this : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets medium image, resets image manipulation operations.
|
||||
*
|
||||
* @param string $variable
|
||||
* @return $this
|
||||
*/
|
||||
public function image($variable = 'thumb')
|
||||
{
|
||||
// TODO: add default file
|
||||
$file = $this->get($variable);
|
||||
$this->image = ImageFile::open($file)
|
||||
->setCacheDir(basename(IMAGES_DIR))
|
||||
->setActualCacheDir(IMAGES_DIR)
|
||||
->setPrettyName(basename($this->get('basename')));
|
||||
|
||||
$this->filter();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add meta file for the medium.
|
||||
*
|
||||
* @param $type
|
||||
* @return $this
|
||||
*/
|
||||
public function addMetaFile($type)
|
||||
{
|
||||
$this->meta[$type] = $type;
|
||||
|
||||
$path = $this->get('path') . '/' . $this->get('filename') . '.meta.' . $type;
|
||||
if ($type == 'yaml') {
|
||||
$this->merge(CompiledYamlFile::instance($path)->content());
|
||||
} elseif (in_array($type, array('jpg', 'jpeg', 'png', 'gif'))) {
|
||||
$this->set('thumb', $path);
|
||||
}
|
||||
$this->reset();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter image by using user defined filter parameters.
|
||||
*
|
||||
* @param string $filter Filter to be used.
|
||||
*/
|
||||
public function filter($filter = 'image.filters.default')
|
||||
{
|
||||
$filters = (array) $this->get($filter, array());
|
||||
foreach ($filters as $params) {
|
||||
$params = (array) $params;
|
||||
$method = array_shift($params);
|
||||
$this->__call($method, $params);
|
||||
}
|
||||
}
|
||||
}
|
||||
397
system/src/Grav/Common/Page/Medium/ImageMedium.php
Normal file
397
system/src/Grav/Common/Page/Medium/ImageMedium.php
Normal file
@@ -0,0 +1,397 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Gregwar\Image\Image as ImageFile;
|
||||
|
||||
class ImageMedium extends Medium
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $thumbnailTypes = [ 'page', 'media', 'default' ];
|
||||
|
||||
/**
|
||||
* @var ImageFile
|
||||
*/
|
||||
protected $image;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $format = 'guess';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $quality;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $default_quality;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected $debug_watermarked = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $magic_actions = [
|
||||
'resize', 'forceResize', 'cropResize', 'crop', 'zoomCrop',
|
||||
'negate', 'brightness', 'contrast', 'grayscale', 'emboss',
|
||||
'smooth', 'sharp', 'edge', 'colorize', 'sepia', 'enableProgressive'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $magic_resize_actions = [
|
||||
'resize' => [ 0, 1 ],
|
||||
'forceResize' => [ 0, 1 ],
|
||||
'cropResize' => [ 0, 1 ],
|
||||
'crop' => [ 0, 1, 2, 3 ],
|
||||
'cropResize' => [ 0, 1 ],
|
||||
'zoomCrop' => [ 0, 1 ]
|
||||
];
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param array $items
|
||||
* @param Blueprint $blueprint
|
||||
*/
|
||||
public function __construct($items = [], Blueprint $blueprint = null)
|
||||
{
|
||||
parent::__construct($items, $blueprint);
|
||||
|
||||
$image_info = getimagesize($this->get('filepath'));
|
||||
$this->def('width', $image_info[0]);
|
||||
$this->def('height', $image_info[1]);
|
||||
$this->def('mime', $image_info['mime']);
|
||||
$this->def('debug', self::$grav['config']->get('system.images.debug'));
|
||||
|
||||
$this->set('thumbnails.media', $this->get('filepath'));
|
||||
|
||||
$this->default_quality = self::$grav['config']->get('system.images.default_image_quality', 85);
|
||||
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add meta file for the medium.
|
||||
*
|
||||
* @param $filepath
|
||||
* @return $this
|
||||
*/
|
||||
public function addMetaFile($filepath)
|
||||
{
|
||||
parent::addMetaFile($filepath);
|
||||
|
||||
// Apply filters in meta file
|
||||
$this->reset();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return PATH to image.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string path to image
|
||||
*/
|
||||
public function path($reset = true)
|
||||
{
|
||||
$output = $this->saveImage();
|
||||
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return URL to image.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function url($reset = true)
|
||||
{
|
||||
$output = preg_replace('|^' . GRAV_ROOT . '|', '', $this->saveImage());
|
||||
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return self::$grav['base_url'] . $output . $this->querystring() . $this->urlHash();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return srcset string for this Medium and its alternatives.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function srcset($reset = true)
|
||||
{
|
||||
if (empty($this->alternatives)) {
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
$srcset = [ $this->url($reset) . ' ' . $this->get('width') . 'w' ];
|
||||
|
||||
foreach ($this->alternatives as $ratio => $medium) {
|
||||
$srcset[] = $medium->url($reset) . ' ' . $medium->get('width') . 'w';
|
||||
}
|
||||
|
||||
return implode(', ', $srcset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsedown element for source display mode
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
public function sourceParsedownElement(array $attributes, $reset = true)
|
||||
{
|
||||
empty($attributes['src']) && $attributes['src'] = $this->url(false);
|
||||
|
||||
$srcset = $this->srcset($reset);
|
||||
if ($srcset) {
|
||||
empty($attributes['srcset']) && $attributes['srcset'] = $srcset;
|
||||
empty($attributes['sizes']) && $attributes['sizes'] = $this->sizes();
|
||||
}
|
||||
|
||||
return [ 'name' => 'img', 'attributes' => $attributes ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset image.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
parent::reset();
|
||||
|
||||
if ($this->image) {
|
||||
$this->image();
|
||||
$this->filter();
|
||||
}
|
||||
|
||||
$this->format = 'guess';
|
||||
$this->quality = $this->default_quality;
|
||||
|
||||
$this->debug_watermarked = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the current Medium into a Link
|
||||
*
|
||||
* @param boolean $reset
|
||||
* @param array $attributes
|
||||
* @return Link
|
||||
*/
|
||||
public function link($reset = true, array $attributes = [])
|
||||
{
|
||||
$attributes['href'] = $this->url(false);
|
||||
$srcset = $this->srcset(false);
|
||||
if ($srcset) {
|
||||
$attributes['data-srcset'] = $srcset;
|
||||
}
|
||||
|
||||
return parent::link($reset, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the current Medium inta a Link with lightbox enabled
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @param boolean $reset
|
||||
* @return Link
|
||||
*/
|
||||
public function lightbox($width = null, $height = null, $reset = true)
|
||||
{
|
||||
if ($this->mode !== 'source') {
|
||||
$this->display('source');
|
||||
}
|
||||
|
||||
if ($width && $height) {
|
||||
$this->cropResize($width, $height);
|
||||
}
|
||||
|
||||
return parent::lightbox($width, $height, $reset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the quality of the image
|
||||
*
|
||||
* @param int $quality 0-100 quality
|
||||
* @return Medium
|
||||
*/
|
||||
public function quality($quality)
|
||||
{
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
}
|
||||
|
||||
$this->quality = $quality;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets image output format.
|
||||
*
|
||||
* @param string $format
|
||||
* @return $this
|
||||
*/
|
||||
public function format($format)
|
||||
{
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
}
|
||||
|
||||
$this->format = $format;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or get sizes parameter for srcset media action
|
||||
*
|
||||
* @param string $sizes
|
||||
* @return $this
|
||||
*/
|
||||
public function sizes($sizes = null)
|
||||
{
|
||||
|
||||
if ($sizes) {
|
||||
$this->attributes['sizes'] = $sizes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
return empty($this->attributes['sizes']) ? '100vw' : $this->attributes['sizes'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward the call to the image processing method.
|
||||
*
|
||||
* @param string $method
|
||||
* @param mixed $args
|
||||
* @return $this|mixed
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
if ($method == 'cropZoom') {
|
||||
$method = 'zoomCrop';
|
||||
}
|
||||
|
||||
if (!in_array($method, self::$magic_actions)) {
|
||||
return parent::__call($method, $args);
|
||||
}
|
||||
|
||||
// Always initialize image.
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
}
|
||||
|
||||
try {
|
||||
call_user_func_array([$this->image, $method], $args);
|
||||
|
||||
foreach ($this->alternatives as $ratio => $medium) {
|
||||
$args_copy = $args;
|
||||
|
||||
// regular image: resize 400x400 -> 200x200
|
||||
// --> @2x: resize 800x800->400x400
|
||||
if (isset(self::$magic_resize_actions[$method])) {
|
||||
foreach (self::$magic_resize_actions[$method] as $param) {
|
||||
if (isset($args_copy[$param])) {
|
||||
$args_copy[$param] = (int) $args_copy[$param] * $ratio;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
call_user_func_array([$medium, $method], $args_copy);
|
||||
}
|
||||
} catch (\BadFunctionCallException $e) {
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets medium image, resets image manipulation operations.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function image()
|
||||
{
|
||||
$locator = self::$grav['locator'];
|
||||
|
||||
$file = $this->get('filepath');
|
||||
$cacheDir = $locator->findResource('cache://images', true);
|
||||
|
||||
$this->image = ImageFile::open($file)
|
||||
->setCacheDir($cacheDir)
|
||||
->setActualCacheDir($cacheDir)
|
||||
->setPrettyName(basename($this->get('basename')));
|
||||
|
||||
$this->filter();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the image with cache.
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
protected function saveImage()
|
||||
{
|
||||
if (!$this->image) {
|
||||
return parent::path(false);
|
||||
}
|
||||
|
||||
if ($this->get('debug') && !$this->debug_watermarked) {
|
||||
$ratio = $this->get('ratio');
|
||||
if (!$ratio) {
|
||||
$ratio = 1;
|
||||
}
|
||||
|
||||
$locator = self::$grav['locator'];
|
||||
$overlay = $locator->findResource("system://assets/responsive-overlays/{$ratio}x.png") ?: $locator->findResource('system://assets/responsive-overlays/unknown.png');
|
||||
$this->image->merge(ImageFile::open($overlay));
|
||||
}
|
||||
|
||||
$result = $this->image->cacheFile($this->format, $this->quality);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter image by using user defined filter parameters.
|
||||
*
|
||||
* @param string $filter Filter to be used.
|
||||
*/
|
||||
public function filter($filter = 'image.filters.default')
|
||||
{
|
||||
$filters = (array) $this->get($filter, []);
|
||||
foreach ($filters as $params) {
|
||||
$params = (array) $params;
|
||||
$method = array_shift($params);
|
||||
$this->__call($method, $params);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
system/src/Grav/Common/Page/Medium/Link.php
Normal file
65
system/src/Grav/Common/Page/Medium/Link.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\GravTrait;
|
||||
|
||||
class Link implements RenderableInterface
|
||||
{
|
||||
use GravTrait;
|
||||
use ParsedownHtmlTrait;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [];
|
||||
protected $source;
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
* @param array $attributes
|
||||
* @param Medium $medium
|
||||
*/
|
||||
public function __construct(array $attributes, Medium $medium)
|
||||
{
|
||||
$this->attributes = $attributes;
|
||||
$this->source = $medium->reset()->thumbnail('auto')->display('thumbnail');
|
||||
$this->source->linked = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an element (is array) that can be rendered by the Parsedown engine
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
public function parsedownElement($title = null, $alt = null, $class = null, $reset = true)
|
||||
{
|
||||
$innerElement = $this->source->parsedownElement($title, $alt, $class, $reset);
|
||||
|
||||
return [
|
||||
'name' => 'a',
|
||||
'attributes' => $this->attributes,
|
||||
'handler' => is_string($innerElement) ? 'line' : 'element',
|
||||
'text' => $innerElement
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward the call to the source element
|
||||
*
|
||||
* @param string $method
|
||||
* @param mixed $args
|
||||
* @return $this|mixed
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
$this->source = call_user_func_array(array($this->source, $method), $args);
|
||||
|
||||
// Don't start nesting links, if user has multiple link calls in his
|
||||
// actions, we will drop the previous links.
|
||||
return $this->source instanceof Link ? $this->source : $this;
|
||||
}
|
||||
}
|
||||
415
system/src/Grav/Common/Page/Medium/Medium.php
Normal file
415
system/src/Grav/Common/Page/Medium/Medium.php
Normal file
@@ -0,0 +1,415 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
|
||||
/**
|
||||
* The Medium is a general class for multimedia objects in Grav pages, specific implementations will derive from
|
||||
*
|
||||
* @author Grav
|
||||
* @license MIT
|
||||
*
|
||||
*/
|
||||
class Medium extends Data implements RenderableInterface
|
||||
{
|
||||
use GravTrait;
|
||||
use ParsedownHtmlTrait;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $mode = 'source';
|
||||
|
||||
/**
|
||||
* @var Medium
|
||||
*/
|
||||
protected $_thumbnail = null;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $thumbnailTypes = [ 'page', 'default' ];
|
||||
|
||||
protected $thumbnailType = null;
|
||||
|
||||
/**
|
||||
* @var Medium[]
|
||||
*/
|
||||
protected $alternatives = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $styleAttributes = [];
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param array $items
|
||||
* @param Blueprint $blueprint
|
||||
*/
|
||||
public function __construct($items = [], Blueprint $blueprint = null)
|
||||
{
|
||||
parent::__construct($items, $blueprint);
|
||||
|
||||
if (self::getGrav()['config']->get('media.enable_media_timestamp', true)) {
|
||||
$this->querystring('&' . self::getGrav()['cache']->getKey());
|
||||
}
|
||||
|
||||
$this->def('mime', 'application/octet-stream');
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add meta file for the medium.
|
||||
*
|
||||
* @param $filepath
|
||||
*/
|
||||
public function addMetaFile($filepath)
|
||||
{
|
||||
$this->merge(CompiledYamlFile::instance($filepath)->content());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add alternative Medium to this Medium.
|
||||
*
|
||||
* @param $ratio
|
||||
* @param Medium $alternative
|
||||
*/
|
||||
public function addAlternative($ratio, Medium $alternative)
|
||||
{
|
||||
if (!is_numeric($ratio) || $ratio === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$alternative->set('ratio', $ratio);
|
||||
$this->alternatives[(float) $ratio] = $alternative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return string representation of the object (html).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->html();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return PATH to file.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string path to file
|
||||
*/
|
||||
public function path($reset = true)
|
||||
{
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return $this->get('filepath');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return URL to file.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function url($reset = true)
|
||||
{
|
||||
$output = preg_replace('|^' . GRAV_ROOT . '|', '', $this->get('filepath'));
|
||||
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return self::$grav['base_url'] . $output . $this->querystring() . $this->urlHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set querystring for the file's url
|
||||
*
|
||||
* @param string $hash
|
||||
* @param boolean $withHash
|
||||
* @return string
|
||||
*/
|
||||
public function querystring($querystring = null, $withQuestionmark = true)
|
||||
{
|
||||
if ($querystring) {
|
||||
$this->set('querystring', ltrim($querystring, '?&'));
|
||||
|
||||
foreach ($this->alternatives as $alt) {
|
||||
$alt->querystring($querystring, $withQuestionmark);
|
||||
}
|
||||
}
|
||||
|
||||
$querystring = $this->get('querystring', '');
|
||||
|
||||
if ($withQuestionmark && !empty($querystring)) {
|
||||
return '?' . $querystring;
|
||||
} else {
|
||||
return $querystring;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set hash for the file's url
|
||||
*
|
||||
* @param string $hash
|
||||
* @param boolean $withHash
|
||||
* @return string
|
||||
*/
|
||||
public function urlHash($hash = null, $withHash = true)
|
||||
{
|
||||
if ($hash) {
|
||||
$this->set('urlHash', ltrim($hash, '#'));
|
||||
}
|
||||
|
||||
$hash = $this->get('urlHash', '');
|
||||
|
||||
if ($withHash && !empty($hash)) {
|
||||
return '#' . $hash;
|
||||
} else {
|
||||
return $hash;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an element (is array) that can be rendered by the Parsedown engine
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
public function parsedownElement($title = null, $alt = null, $class = null, $reset = true)
|
||||
{
|
||||
$attributes = $this->attributes;
|
||||
|
||||
$style = '';
|
||||
foreach ($this->styleAttributes as $key => $value) {
|
||||
$style .= $key . ': ' . $value . ';';
|
||||
}
|
||||
$attributes['style'] = $style;
|
||||
|
||||
!empty($title) && empty($attributes['title']) && $attributes['title'] = $title;
|
||||
!empty($alt) && empty($attributes['alt']) && $attributes['alt'] = $alt;
|
||||
!empty($class) && empty($attributes['class']) && $attributes['class'] = $class;
|
||||
|
||||
switch ($this->mode) {
|
||||
case 'text':
|
||||
$element = $this->textParsedownElement($attributes, false);
|
||||
break;
|
||||
case 'thumbnail':
|
||||
$element = $this->getThumbnail()->sourceParsedownElement($attributes, false);
|
||||
break;
|
||||
case 'source':
|
||||
$element = $this->sourceParsedownElement($attributes, false);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
$this->display('source');
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsedown element for source display mode
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
protected function sourceParsedownElement(array $attributes, $reset = true)
|
||||
{
|
||||
return $this->textParsedownElement($attributes, $reset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsedown element for text display mode
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
protected function textParsedownElement(array $attributes, $reset = true)
|
||||
{
|
||||
$text = empty($attributes['title']) ? empty($attributes['alt']) ? $this->get('filename') : $attributes['alt'] : $attributes['title'];
|
||||
|
||||
$element = [
|
||||
'name' => 'p',
|
||||
'attributes' => $attributes,
|
||||
'text' => $text
|
||||
];
|
||||
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset medium.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->attributes = [];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch display mode.
|
||||
*
|
||||
* @param string $mode
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function display($mode = 'source')
|
||||
{
|
||||
if ($this->mode === $mode) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
$this->mode = $mode;
|
||||
|
||||
return $mode === 'thumbnail' ? $this->getThumbnail()->reset() : $this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch thumbnail.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function thumbnail($type = 'auto')
|
||||
{
|
||||
if ($type !== 'auto' && !in_array($type, $this->thumbnailTypes)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($this->thumbnailType !== $type) {
|
||||
$this->_thumbnail = null;
|
||||
}
|
||||
|
||||
$this->thumbnailType = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the current Medium into a Link
|
||||
*
|
||||
* @param boolean $reset
|
||||
* @param array $attributes
|
||||
* @return Link
|
||||
*/
|
||||
public function link($reset = true, array $attributes = [])
|
||||
{
|
||||
if ($this->mode !== 'source') {
|
||||
$this->display('source');
|
||||
}
|
||||
|
||||
foreach ($this->attributes as $key => $value) {
|
||||
empty($attributes['data-' . $key]) && $attributes['data-' . $key] = $value;
|
||||
}
|
||||
|
||||
empty($attributes['href']) && $attributes['href'] = $this->url();
|
||||
|
||||
return new Link($attributes, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the current Medium inta a Link with lightbox enabled
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @param boolean $reset
|
||||
* @return Link
|
||||
*/
|
||||
public function lightbox($width = null, $height = null, $reset = true)
|
||||
{
|
||||
$attributes = ['rel' => 'lightbox'];
|
||||
|
||||
if ($width && $height) {
|
||||
$attributes['data-width'] = $width;
|
||||
$attributes['data-height'] = $height;
|
||||
}
|
||||
|
||||
return $this->link($reset, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow any action to be called on this medium from twig or markdown
|
||||
*
|
||||
* @param string $method
|
||||
* @param mixed $args
|
||||
* @return $this
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
$qs = $method;
|
||||
if (count($args) > 1 || (count($args) == 1 && !empty($args[0]))) {
|
||||
$qs .= '=' . implode(',', array_map(function ($a) { return urlencode($a); }, $args));
|
||||
}
|
||||
|
||||
if (!empty($qs)) {
|
||||
$this->querystring($this->querystring(null, false) . '&' . $qs);
|
||||
}
|
||||
|
||||
self::$grav['debugger']->addMessage($this->querystring());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the thumbnail Medium object
|
||||
*
|
||||
* @return ThumbnailImageMedium
|
||||
*/
|
||||
protected function getThumbnail()
|
||||
{
|
||||
if (!$this->_thumbnail) {
|
||||
$types = $this->thumbnailTypes;
|
||||
|
||||
if ($this->thumbnailType !== 'auto') {
|
||||
array_unshift($types, $this->thumbnailType);
|
||||
}
|
||||
|
||||
foreach ($types as $type) {
|
||||
$thumb = $this->get('thumbnails.' . $type, false);
|
||||
|
||||
if ($thumb) {
|
||||
$thumb = $thumb instanceof ThumbnailImageMedium ? $thumb : MediumFactory::fromFile($thumb, ['type' => 'thumbnail']);
|
||||
$thumb->parent = $this;
|
||||
}
|
||||
|
||||
if ($thumb) {
|
||||
$this->_thumbnail = $thumb;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_thumbnail;
|
||||
}
|
||||
}
|
||||
145
system/src/Grav/Common/Page/Medium/MediumFactory.php
Normal file
145
system/src/Grav/Common/Page/Medium/MediumFactory.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
|
||||
/**
|
||||
* MediumFactory can be used to more easily create various Medium objects from files or arrays, it should
|
||||
* contain most logic for instantiating a Medium object.
|
||||
*
|
||||
* @author Grav
|
||||
* @license MIT
|
||||
*
|
||||
*/
|
||||
class MediumFactory
|
||||
{
|
||||
use GravTrait;
|
||||
|
||||
/**
|
||||
* Create Medium from a file
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $params
|
||||
* @return Medium
|
||||
*/
|
||||
public static function fromFile($file, array $params = [])
|
||||
{
|
||||
if (!file_exists($file)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$path = dirname($file);
|
||||
$filename = basename($file);
|
||||
$parts = explode('.', $filename);
|
||||
$ext = array_pop($parts);
|
||||
$basename = implode('.', $parts);
|
||||
|
||||
$config = self::getGrav()['config'];
|
||||
|
||||
$media_params = $config->get("media.".strtolower($ext));
|
||||
if (!$media_params) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$params += $media_params;
|
||||
|
||||
// Add default settings for undefined variables.
|
||||
$params += $config->get('media.defaults');
|
||||
$params += [
|
||||
'type' => 'file',
|
||||
'thumb' => 'media/thumb.png',
|
||||
'mime' => 'application/octet-stream',
|
||||
'filepath' => $file,
|
||||
'filename' => $filename,
|
||||
'basename' => $basename,
|
||||
'extension' => $ext,
|
||||
'path' => $path,
|
||||
'modified' => filemtime($file),
|
||||
'thumbnails' => []
|
||||
];
|
||||
|
||||
$locator = self::getGrav()['locator'];
|
||||
|
||||
$lookup = $locator->findResources('image://');
|
||||
foreach ($lookup as $lookupPath) {
|
||||
if (is_file($lookupPath . '/' . $params['thumb'])) {
|
||||
$params['thumbnails']['default'] = $lookupPath . '/' . $params['thumb'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return static::fromArray($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Medium from array of parameters
|
||||
*
|
||||
* @param array $items
|
||||
* @param Blueprint|null $blueprint
|
||||
* @return Medium
|
||||
*/
|
||||
public static function fromArray(array $items = [], Blueprint $blueprint = null)
|
||||
{
|
||||
$type = isset($items['type']) ? $items['type'] : null;
|
||||
|
||||
switch ($type) {
|
||||
case 'image':
|
||||
return new ImageMedium($items, $blueprint);
|
||||
break;
|
||||
case 'thumbnail':
|
||||
return new ThumbnailImageMedium($items, $blueprint);
|
||||
break;
|
||||
case 'animated':
|
||||
case 'vector':
|
||||
return new StaticImageMedium($items, $blueprint);
|
||||
break;
|
||||
case 'video':
|
||||
return new VideoMedium($items, $blueprint);
|
||||
break;
|
||||
default:
|
||||
return new Medium($items, $blueprint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ImageMedium by scaling another ImageMedium object.
|
||||
*
|
||||
* @param ImageMedium $medium
|
||||
* @param int $from
|
||||
* @param int $to
|
||||
* @return Medium
|
||||
*/
|
||||
public static function scaledFromMedium($medium, $from, $to)
|
||||
{
|
||||
if (! $medium instanceof ImageMedium) {
|
||||
return $medium;
|
||||
}
|
||||
|
||||
if ($to > $from) {
|
||||
return $medium;
|
||||
}
|
||||
|
||||
$ratio = $to / $from;
|
||||
$width = (int) ($medium->get('width') * $ratio);
|
||||
$height = (int) ($medium->get('height') * $ratio);
|
||||
|
||||
$basename = $medium->get('basename');
|
||||
$basename = str_replace('@'.$from.'x', '@'.$to.'x', $basename);
|
||||
|
||||
$debug = $medium->get('debug');
|
||||
$medium->set('debug', false);
|
||||
|
||||
$file = $medium->resize($width, $height)->path();
|
||||
|
||||
$medium->set('debug', $debug);
|
||||
|
||||
$size = filesize($file);
|
||||
|
||||
$medium = self::fromFile($file);
|
||||
$medium->set('size', $size);
|
||||
|
||||
return $medium;
|
||||
}
|
||||
}
|
||||
31
system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php
Normal file
31
system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\Markdown\Parsedown;
|
||||
|
||||
trait ParsedownHtmlTrait
|
||||
{
|
||||
/**
|
||||
* @var \Grav\Common\Markdown\Parsedown
|
||||
*/
|
||||
protected $parsedown = null;
|
||||
|
||||
/**
|
||||
* Return HTML markup from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $class
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function html($title = null, $alt = null, $class = null, $reset = true)
|
||||
{
|
||||
$element = $this->parsedownElement($title, $alt, $class, $reset);
|
||||
|
||||
if (!$this->parsedown) {
|
||||
$this->parsedown = new Parsedown(null, null);
|
||||
}
|
||||
|
||||
return $this->parsedown->elementToHtml($element);
|
||||
}
|
||||
}
|
||||
34
system/src/Grav/Common/Page/Medium/RenderableInterface.php
Normal file
34
system/src/Grav/Common/Page/Medium/RenderableInterface.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
/**
|
||||
* Renderable Medium objects can be rendered to HTML markup and Parsedown objects
|
||||
*
|
||||
* @author Grav
|
||||
* @license MIT
|
||||
*
|
||||
*/
|
||||
interface RenderableInterface
|
||||
{
|
||||
/**
|
||||
* Return HTML markup from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function html($title = null, $alt = null, $class = null, $reset = true);
|
||||
|
||||
/**
|
||||
* Return Parsedown Element from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function parsedownElement($title = null, $alt = null, $class = null, $reset = true);
|
||||
}
|
||||
28
system/src/Grav/Common/Page/Medium/StaticImageMedium.php
Normal file
28
system/src/Grav/Common/Page/Medium/StaticImageMedium.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
/**
|
||||
* The Image medium holds information related to an individual image. These are then stored in the Media object.
|
||||
*
|
||||
* @author Grav
|
||||
* @license MIT
|
||||
*
|
||||
*/
|
||||
class StaticImageMedium extends Medium
|
||||
{
|
||||
use StaticResizeTrait;
|
||||
|
||||
/**
|
||||
* Parsedown element for source display mode
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
protected function sourceParsedownElement(array $attributes, $reset = true)
|
||||
{
|
||||
empty($attributes['src']) && $attributes['src'] = $this->url($reset);
|
||||
|
||||
return [ 'name' => 'image', 'attributes' => $attributes ];
|
||||
}
|
||||
}
|
||||
20
system/src/Grav/Common/Page/Medium/StaticResizeTrait.php
Normal file
20
system/src/Grav/Common/Page/Medium/StaticResizeTrait.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
trait StaticResizeTrait
|
||||
{
|
||||
/**
|
||||
* Resize media by setting attributes
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @return Medium
|
||||
*/
|
||||
public function resize($width = null, $height = null)
|
||||
{
|
||||
$this->styleAttributes['width'] = $width . 'px';
|
||||
$this->styleAttributes['height'] = $height . 'px';
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
121
system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php
Normal file
121
system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
class ThumbnailImageMedium extends ImageMedium
|
||||
{
|
||||
/**
|
||||
* @var Medium
|
||||
*/
|
||||
public $parent = null;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
public $linked = false;
|
||||
|
||||
/**
|
||||
* Return srcset string for this Medium and its alternatives.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function srcset($reset = true)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an element (is array) that can be rendered by the Parsedown engine
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
public function parsedownElement($title = null, $alt = null, $class = null, $reset = true)
|
||||
{
|
||||
return $this->bubble('parsedownElement', [$title, $alt, $class, $reset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return HTML markup from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function html($title = null, $alt = null, $class = null, $reset = true)
|
||||
{
|
||||
return $this->bubble('html', [$title, $alt, $class, $reset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch display mode.
|
||||
*
|
||||
* @param string $mode
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function display($mode = 'source')
|
||||
{
|
||||
return $this->bubble('display', [$mode], false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch thumbnail.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function thumbnail($type = 'auto')
|
||||
{
|
||||
$this->bubble('thumbnail', [$type], false);
|
||||
return $this->bubble('getThumbnail', [], false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the current Medium into a Link
|
||||
*
|
||||
* @param boolean $reset
|
||||
* @param array $attributes
|
||||
* @return Link
|
||||
*/
|
||||
public function link($reset = true, array $attributes = [])
|
||||
{
|
||||
return $this->bubble('link', [$reset, $attributes], false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the current Medium inta a Link with lightbox enabled
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @param boolean $reset
|
||||
* @return Link
|
||||
*/
|
||||
public function lightbox($width = null, $height = null, $reset = true)
|
||||
{
|
||||
return $this->bubble('lightbox', [$width, $height, $reset], false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bubble a function call up to either the superclass function or the parent Medium instance
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @param boolean $testLinked
|
||||
* @return Medium
|
||||
*/
|
||||
protected function bubble($method, array $arguments = [], $testLinked = true)
|
||||
{
|
||||
if (!$testLinked || $this->linked) {
|
||||
return $this->parent ? call_user_func_array(array($this->parent, $method), $arguments) : $this;
|
||||
} else {
|
||||
return call_user_func_array(array($this, 'parent::' . $method), $arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
46
system/src/Grav/Common/Page/Medium/VideoMedium.php
Normal file
46
system/src/Grav/Common/Page/Medium/VideoMedium.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Data\Data;
|
||||
use Gregwar\Image\Image as ImageFile;
|
||||
|
||||
class VideoMedium extends Medium
|
||||
{
|
||||
use StaticResizeTrait;
|
||||
|
||||
/**
|
||||
* Parsedown element for source display mode
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
protected function sourceParsedownElement(array $attributes, $reset = true)
|
||||
{
|
||||
$location = $this->url($reset);
|
||||
|
||||
return [
|
||||
'name' => 'video',
|
||||
'text' => '<source src="' . $location . '">Your browser does not support the video tag.',
|
||||
'attributes' => $attributes
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset medium.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
parent::reset();
|
||||
|
||||
$this->attributes['controls'] = true;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,8 @@ use Grav\Common\Twig;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Taxonomy;
|
||||
use Grav\Common\Markdown\Markdown;
|
||||
use Grav\Common\Markdown\MarkdownExtra;
|
||||
use Grav\Common\Markdown\Parsedown;
|
||||
use Grav\Common\Markdown\ParsedownExtra;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\File\MarkdownFile;
|
||||
@@ -30,11 +30,6 @@ 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.
|
||||
*/
|
||||
@@ -52,7 +47,11 @@ class Page
|
||||
|
||||
protected $parent;
|
||||
protected $template;
|
||||
protected $expires;
|
||||
protected $visible;
|
||||
protected $published;
|
||||
protected $publish_date;
|
||||
protected $unpublish_date;
|
||||
protected $slug;
|
||||
protected $route;
|
||||
protected $routable;
|
||||
@@ -62,6 +61,7 @@ class Page
|
||||
protected $header;
|
||||
protected $frontmatter;
|
||||
protected $content;
|
||||
protected $summary;
|
||||
protected $raw_content;
|
||||
protected $pagination;
|
||||
protected $media;
|
||||
@@ -79,6 +79,8 @@ class Page
|
||||
protected $process;
|
||||
protected $summary_size;
|
||||
protected $markdown_extra;
|
||||
protected $etag;
|
||||
protected $last_modified;
|
||||
|
||||
/**
|
||||
* @var Page Unmodified (original) version of the page. Used for copying and moving the page.
|
||||
@@ -92,17 +94,16 @@ class Page
|
||||
|
||||
/**
|
||||
* Page Object Constructor
|
||||
*
|
||||
* @param array $array An array of existing page objects
|
||||
*/
|
||||
public function __construct($array = array())
|
||||
public function __construct()
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
$config = self::getGrav()['config'];
|
||||
|
||||
$this->routable = true;
|
||||
$this->taxonomy = array();
|
||||
$this->process = $config->get('system.pages.process');
|
||||
$this->published = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,16 +112,36 @@ class Page
|
||||
* @param \SplFileInfo $file The file information for the .md file that the page represents
|
||||
* @return void
|
||||
*/
|
||||
public function init($file)
|
||||
public function init(\SplFileInfo $file)
|
||||
{
|
||||
$this->filePath($file->getPathName());
|
||||
$this->modified($file->getMTime());
|
||||
$this->id($this->modified().md5($this->filePath()));
|
||||
$this->header();
|
||||
$this->date();
|
||||
$this->metadata();
|
||||
$this->slug();
|
||||
$this->visible();
|
||||
$this->modularTwig($this->slug[0] == '_');
|
||||
|
||||
// Handle publishing dates if no explict published option set
|
||||
if (self::getGrav()['config']->get('system.pages.publish_dates') && !isset($this->header->published)) {
|
||||
// unpublish if required, if not clear cache right before page should be unpublished
|
||||
if ($this->unpublishDate()) {
|
||||
if ($this->unpublishDate() < time()) {
|
||||
$this->published(false);
|
||||
} else {
|
||||
$this->published();
|
||||
self::getGrav()['cache']->setLifeTime($this->unpublishDate());
|
||||
}
|
||||
}
|
||||
// publish if required, if not clear cache right before page is published
|
||||
if ($this->publishDate() != $this->modified() && $this->publishDate() > time()) {
|
||||
$this->published(false);
|
||||
self::getGrav()['cache']->setLifeTime($this->publishDate());
|
||||
}
|
||||
}
|
||||
$this->published();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,6 +165,7 @@ class Page
|
||||
$this->id($this->modified().md5($this->filePath()));
|
||||
$this->header = null;
|
||||
$this->content = null;
|
||||
$this->summary = null;
|
||||
}
|
||||
return $file ? $file->raw() : '';
|
||||
}
|
||||
@@ -247,42 +269,103 @@ class Page
|
||||
$this->process[$process] = $status;
|
||||
}
|
||||
}
|
||||
if (isset($this->header->published)) {
|
||||
$this->published = $this->header->published;
|
||||
}
|
||||
if (isset($this->header->publish_date)) {
|
||||
$this->publish_date = strtotime($this->header->publish_date);
|
||||
}
|
||||
if (isset($this->header->unpublish_date)) {
|
||||
$this->unpublish_date = strtotime($this->header->unpublish_date);
|
||||
}
|
||||
if (isset($this->header->expires)) {
|
||||
$this->expires = intval($this->header->expires);
|
||||
}
|
||||
if (isset($this->header->etag)) {
|
||||
$this->etag = (bool)$this->header->etag;
|
||||
}
|
||||
if (isset($this->header->last_modified)) {
|
||||
$this->last_modified = (bool)$this->header->last_modified;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify a header value directly
|
||||
*
|
||||
* @param $key
|
||||
* @param $value
|
||||
*/
|
||||
public function modifyHeader($key, $value)
|
||||
{
|
||||
$this->header->$key = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the summary.
|
||||
*
|
||||
* @param int $size Max summary size.
|
||||
* @param int $size Max summary size.
|
||||
* @return string
|
||||
*/
|
||||
public function summary($size = null)
|
||||
{
|
||||
$content = $this->content();
|
||||
/** @var Config $config */
|
||||
$config = self::getGrav()['config']->get('site.summary');
|
||||
if (isset($this->header->summary)) {
|
||||
$config = array_merge($config, $this->header->summary);
|
||||
}
|
||||
|
||||
// Return summary based on settings in site config file
|
||||
if (!$config['enabled']) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
// Set up variables to process summary from page or from custom summary
|
||||
if ($this->summary === null) {
|
||||
$content = $this->content();
|
||||
$summary_size = $this->summary_size;
|
||||
} else {
|
||||
$content = $this->summary;
|
||||
$summary_size = mb_strlen($this->summary);
|
||||
}
|
||||
|
||||
// Return calculated summary based on summary divider's position
|
||||
if (!$size && isset($this->summary_size)) {
|
||||
return substr($content, 0, $this->summary_size);
|
||||
$format = $config['format'];
|
||||
// Return entire page content on wrong/ unknown format
|
||||
if (!in_array($format, array('short', 'long'))) {
|
||||
return $content;
|
||||
} elseif (($format === 'short') && isset($summary_size)) {
|
||||
return mb_substr($content, 0, $summary_size);
|
||||
}
|
||||
|
||||
// Return calculated summary based on setting in site config file
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
if (!$size && $config->get('site.summary.size')) {
|
||||
$size = $config->get('site.summary.size');
|
||||
// Get summary size from site config's file
|
||||
if (is_null($size)) {
|
||||
$size = $config['size'];
|
||||
}
|
||||
|
||||
// If the size is zero, return the entire page content
|
||||
if ($size === 0) {
|
||||
return $content;
|
||||
// Return calculated summary based on defaults
|
||||
if (!$size) {
|
||||
} elseif (!is_numeric($size) || ($size < 0)) {
|
||||
$size = 300;
|
||||
}
|
||||
|
||||
return Utils::truncateHTML($content, $size);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the summary of the page
|
||||
*
|
||||
* @param string $var Summary
|
||||
*/
|
||||
public function setSummary($summary)
|
||||
{
|
||||
$this->summary = $summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and Sets the content based on content portion of the .md file
|
||||
@@ -305,58 +388,68 @@ class Page
|
||||
$this->id(time().md5($this->filePath()));
|
||||
$this->content = null;
|
||||
}
|
||||
|
||||
// If no content, process it
|
||||
if ($this->content === null) {
|
||||
|
||||
// Get media
|
||||
$this->media();
|
||||
|
||||
// Load cached content
|
||||
/** @var Cache $cache */
|
||||
$cache = self::$grav['cache'];
|
||||
$cache = self::getGrav()['cache'];
|
||||
$cache_id = md5('page'.$this->id());
|
||||
$this->content = $cache->fetch($cache_id);
|
||||
|
||||
$update_cache = false;
|
||||
$process_markdown = $this->shouldProcess('markdown');
|
||||
$process_twig = $this->shouldProcess('twig');
|
||||
$cache_twig = isset($this->header->cache_enable) ? $this->header->cache_enable : true;
|
||||
$twig_first = isset($this->header->twig_first) ? $this->header->twig_first : false;
|
||||
$twig_already_processed = false;
|
||||
|
||||
// if no cached-content run everything
|
||||
if ($this->content === false) {
|
||||
// Process Markdown
|
||||
$this->content = $this->processMarkdown();
|
||||
$update_cache = true;
|
||||
$this->content = $this->raw_content;
|
||||
self::getGrav()->fireEvent('onPageContentRaw', new Event(['page' => $this]));
|
||||
|
||||
if ($twig_first) {
|
||||
if ($process_twig) {
|
||||
$this->processTwig();
|
||||
$twig_already_processed = true;
|
||||
}
|
||||
if ($process_markdown) {
|
||||
$this->processMarkdown();
|
||||
}
|
||||
if ($cache_twig) {
|
||||
$this->cachePageContent();
|
||||
}
|
||||
} else {
|
||||
if ($process_markdown) {
|
||||
$this->processMarkdown();
|
||||
}
|
||||
if (!$cache_twig) {
|
||||
$this->cachePageContent();
|
||||
}
|
||||
if ($process_twig) {
|
||||
$this->processTwig();
|
||||
$twig_already_processed = true;
|
||||
}
|
||||
if ($cache_twig) {
|
||||
$this->cachePageContent();
|
||||
}
|
||||
}
|
||||
// content cached, but twig cache off
|
||||
}
|
||||
|
||||
// Process Twig if enabled
|
||||
if ($this->shouldProcess('twig')) {
|
||||
|
||||
// Always process twig if caching in the page is disabled
|
||||
$process_twig = (isset($this->header->cache_enable) && !$this->header->cache_enable);
|
||||
|
||||
// Do we want to cache markdown, but process twig in each page?
|
||||
if ($update_cache && $process_twig) {
|
||||
$cache->save($cache_id, $this->content);
|
||||
$update_cache = false;
|
||||
}
|
||||
|
||||
// Do we need to process twig this time?
|
||||
if ($update_cache || $process_twig) {
|
||||
/** @var Twig $twig */
|
||||
$twig = self::$grav['twig'];
|
||||
$this->content = $twig->processPage($this, $this->content);
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the whole page, including processed content
|
||||
if ($update_cache) {
|
||||
// Process any post-processing but pre-caching functionality
|
||||
self::$grav->fireEvent('onPageContentProcessed', new Event(['page' => $this]));
|
||||
$cache->save($cache_id, $this->content);
|
||||
// only markdown content cached, process twig if required and not already processed
|
||||
if ($process_twig && !$cache_twig && !$twig_already_processed) {
|
||||
$this->processTwig();
|
||||
}
|
||||
|
||||
// Handle summary divider
|
||||
$divider_pos = strpos($this->content, '<p>'.SUMMARY_DELIMITER.'</p>');
|
||||
$delimiter = self::getGrav()['config']->get('site.summary.delimiter', '===');
|
||||
$divider_pos = strpos($this->content, "<p>{$delimiter}</p>");
|
||||
if ($divider_pos !== false) {
|
||||
$this->summary_size = $divider_pos;
|
||||
$this->content = str_replace('<p>'.SUMMARY_DELIMITER.'</p>', '', $this->content);
|
||||
$this->content = str_replace("<p>{$delimiter}</p>", '', $this->content);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -364,6 +457,56 @@ class Page
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the Markdown content. Uses Parsedown or Parsedown Extra depending on configuration
|
||||
*/
|
||||
protected function processMarkdown()
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::getGrav()['config'];
|
||||
|
||||
$defaults = (array) $config->get('system.pages.markdown');
|
||||
if (isset($this->header()->markdown)) {
|
||||
$defaults = array_merge($defaults, $this->header()->markdown);
|
||||
}
|
||||
|
||||
// pages.markdown_extra is deprecated, but still check it...
|
||||
if (isset($this->markdown_extra) || $config->get('system.pages.markdown_extra') !== null) {
|
||||
$defaults['extra'] = $this->markdown_extra ?: $config->get('system.pages.markdown_extra');
|
||||
}
|
||||
|
||||
// Initialize the preferred variant of Parsedown
|
||||
if ($defaults['extra']) {
|
||||
$parsedown = new ParsedownExtra($this, $defaults);
|
||||
} else {
|
||||
$parsedown = new Parsedown($this, $defaults);
|
||||
}
|
||||
|
||||
$this->content = $parsedown->text($this->content);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the Twig page content.
|
||||
*/
|
||||
private function processTwig()
|
||||
{
|
||||
$twig = self::getGrav()['twig'];
|
||||
$this->content = $twig->processPage($this, $this->content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires the onPageContentProcessed event, and caches the page content using a unique ID for the page
|
||||
*/
|
||||
private function cachePageContent()
|
||||
{
|
||||
$cache = self::getGrav()['cache'];
|
||||
$cache_id = md5('page'.$this->id());
|
||||
|
||||
self::getGrav()->fireEvent('onPageContentProcessed', new Event(['page' => $this]));
|
||||
$cache->save($cache_id, $this->content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed by the onPageContentProcessed event to get the raw page content
|
||||
*
|
||||
@@ -422,6 +565,9 @@ class Page
|
||||
if ($name == 'media.image') {
|
||||
return $this->media()->images();
|
||||
}
|
||||
if ($name == 'media.audio') {
|
||||
return $this->media()->audios();
|
||||
}
|
||||
|
||||
$path = explode('.', $name);
|
||||
$scope = array_shift($path);
|
||||
@@ -448,6 +594,15 @@ class Page
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function rawMarkdown($var = null)
|
||||
{
|
||||
if ($var !== null) {
|
||||
$this->raw_content = $var;
|
||||
}
|
||||
|
||||
return $this->raw_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file object to the page.
|
||||
*
|
||||
@@ -532,7 +687,7 @@ class Page
|
||||
public function blueprints()
|
||||
{
|
||||
/** @var Pages $pages */
|
||||
$pages = self::$grav['pages'];
|
||||
$pages = self::getGrav()['pages'];
|
||||
|
||||
return $pages->blueprints($this->template());
|
||||
}
|
||||
@@ -611,7 +766,7 @@ class Page
|
||||
public function media($var = null)
|
||||
{
|
||||
/** @var Cache $cache */
|
||||
$cache = self::$grav['cache'];
|
||||
$cache = self::getGrav()['cache'];
|
||||
|
||||
if ($var) {
|
||||
$this->media = $var;
|
||||
@@ -670,6 +825,20 @@ class Page
|
||||
return $this->template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and sets the expires field. If not set will return the default
|
||||
*
|
||||
* @param string $var The name of this page.
|
||||
* @return string The name of this page.
|
||||
*/
|
||||
public function expires($var = null)
|
||||
{
|
||||
if ($var !== null) {
|
||||
$this->expires = $var;
|
||||
}
|
||||
return empty($this->expires) ? self::getGrav()['config']->get('system.pages.expires') : $this->expires;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and sets the title for this Page. If no title is set, it will use the slug() to get a name
|
||||
*
|
||||
@@ -723,11 +892,67 @@ class Page
|
||||
$regex = '/^[0-9]+\./u';
|
||||
if (preg_match($regex, $this->folder)) {
|
||||
$this->visible = true;
|
||||
} else {
|
||||
$this->visible = false;
|
||||
}
|
||||
}
|
||||
return $this->visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and Sets whether or not this Page is considered published
|
||||
*
|
||||
* @param bool $var true if the page is published
|
||||
* @return bool true if the page is published
|
||||
*/
|
||||
public function published($var = null)
|
||||
{
|
||||
if ($var !== null) {
|
||||
$this->published = (bool) $var;
|
||||
}
|
||||
|
||||
// If not published, should not be visible in menus either
|
||||
if ($this->published === false) {
|
||||
$this->visible = false;
|
||||
}
|
||||
|
||||
return $this->published;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and Sets the Page publish date
|
||||
*
|
||||
* @param string $var string representation of a date
|
||||
* @return int unix timestamp representation of the date
|
||||
*/
|
||||
public function publishDate($var = null)
|
||||
{
|
||||
if ($var !== null) {
|
||||
$this->publish_date = strtotime($var);
|
||||
}
|
||||
|
||||
if ($this->publish_date === null) {
|
||||
$this->publish_date = $this->date();
|
||||
}
|
||||
|
||||
return $this->publish_date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and Sets the Page unpublish date
|
||||
*
|
||||
* @param string $var string representation of a date
|
||||
* @return int|null unix timestamp representation of the date
|
||||
*/
|
||||
public function unpublishDate($var = null)
|
||||
{
|
||||
if ($var !== null) {
|
||||
$this->unpublish_date = strtotime($var);
|
||||
}
|
||||
|
||||
return $this->unpublish_date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and Sets whether or not this Page is routable, ie you can reach it
|
||||
* via a URL
|
||||
@@ -761,12 +986,18 @@ class Page
|
||||
/**
|
||||
* Function to merge page metadata tags and build an array of Metadata objects
|
||||
* that can then be rendered in the page.
|
||||
*
|
||||
* @param array $var an Array of metadata values to set
|
||||
* @return array an Array of metadata values for the page
|
||||
*/
|
||||
public function metadata()
|
||||
public function metadata($var = null)
|
||||
{
|
||||
if ($var !== null) {
|
||||
$this->metadata = (array) $var;
|
||||
}
|
||||
|
||||
// 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;
|
||||
@@ -777,7 +1008,7 @@ class Page
|
||||
// Safety check to ensure we have a header
|
||||
if ($page_header) {
|
||||
// Merge any site.metadata settings in with page metadata
|
||||
$defaults = (array) self::$grav['config']->get('site.metadata');
|
||||
$defaults = (array) self::getGrav()['config']->get('site.metadata');
|
||||
|
||||
if (isset($page_header->metadata)) {
|
||||
$page_header->metadata = array_merge($defaults, $page_header->metadata);
|
||||
@@ -787,19 +1018,18 @@ class Page
|
||||
|
||||
// Build an array of meta objects..
|
||||
foreach ((array)$page_header->metadata as $key => $value) {
|
||||
|
||||
// If this is a property type metadata: "og", "twitter", "facebook" etc
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $property => $prop_value) {
|
||||
$prop_key = $key.":".$property;
|
||||
$this->metadata[$prop_key] = array('property'=>$prop_key, 'content'=>$prop_value);
|
||||
$this->metadata[$prop_key] = array('property'=>$prop_key, 'content'=>htmlspecialchars($prop_value, ENT_QUOTES));
|
||||
}
|
||||
// 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);
|
||||
$this->metadata[$key] = array('http_equiv'=>$key, 'content'=>htmlspecialchars($value, ENT_QUOTES));
|
||||
} else {
|
||||
$this->metadata[$key] = array('name'=>$key, 'content'=>$value);
|
||||
$this->metadata[$key] = array('name'=>$key, 'content'=>htmlspecialchars($value, ENT_QUOTES));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -879,9 +1109,13 @@ class Page
|
||||
*/
|
||||
public function url($include_host = false)
|
||||
{
|
||||
/** @var Pages $pages */
|
||||
$pages = self::getGrav()['pages'];
|
||||
|
||||
/** @var Uri $uri */
|
||||
$uri = self::$grav['uri'];
|
||||
$rootUrl = $uri->rootUrl($include_host);
|
||||
$uri = self::getGrav()['uri'];
|
||||
|
||||
$rootUrl = $uri->rootUrl($include_host) . $pages->base();
|
||||
$url = $rootUrl.'/'.trim($this->route(), '/');
|
||||
|
||||
// trim trailing / if not root
|
||||
@@ -935,6 +1169,40 @@ class Page
|
||||
return $this->modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and sets the option to show the etag header for the page.
|
||||
*
|
||||
* @param boolean $var show etag header
|
||||
* @return boolean show etag header
|
||||
*/
|
||||
public function eTag($var = null)
|
||||
{
|
||||
if ($var !== null) {
|
||||
$this->etag = $var;
|
||||
}
|
||||
if (!isset($this->etag)) {
|
||||
$this->etag = (bool) self::getGrav()['config']->get('system.pages.etag');
|
||||
}
|
||||
return $this->etag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and sets the option to show the last_modified header for the page.
|
||||
*
|
||||
* @param boolean $var show last_modified header
|
||||
* @return boolean show last_modified header
|
||||
*/
|
||||
public function lastModified($var = null)
|
||||
{
|
||||
if ($var !== null) {
|
||||
$this->last_modified = $var;
|
||||
}
|
||||
if (!isset($this->last_modified)) {
|
||||
$this->last_modified = (bool) self::getGrav()['config']->get('system.pages.last_modified');
|
||||
}
|
||||
return $this->last_modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and sets the path to the .md file for this Page object.
|
||||
*
|
||||
@@ -951,7 +1219,7 @@ class Page
|
||||
// Path to the page.
|
||||
$this->path = dirname(dirname($var));
|
||||
}
|
||||
return $this->name ? $this->path . '/' . $this->folder . '/' . $this->name : null;
|
||||
return $this->path . '/' . $this->folder . '/' . ($this->name ?: '');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1007,9 +1275,11 @@ class Page
|
||||
if ($var !== null) {
|
||||
$this->date = strtotime($var);
|
||||
}
|
||||
|
||||
if (!$this->date) {
|
||||
$this->date = $this->modified;
|
||||
}
|
||||
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
@@ -1076,7 +1346,7 @@ class Page
|
||||
}
|
||||
if (empty($this->max_count)) {
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
$config = self::getGrav()['config'];
|
||||
$this->max_count = (int) $config->get('system.pages.list.count');
|
||||
}
|
||||
return $this->max_count;
|
||||
@@ -1120,6 +1390,7 @@ class Page
|
||||
$this->modular_twig = (bool) $var;
|
||||
if ($var) {
|
||||
$this->process['twig'] = true;
|
||||
$this->visible(false);
|
||||
}
|
||||
}
|
||||
return $this->modular_twig;
|
||||
@@ -1150,7 +1421,7 @@ class Page
|
||||
}
|
||||
|
||||
/** @var Pages $pages */
|
||||
$pages = self::$grav['pages'];
|
||||
$pages = self::getGrav()['pages'];
|
||||
|
||||
return $pages->get($this->parent);
|
||||
}
|
||||
@@ -1158,27 +1429,16 @@ class Page
|
||||
/**
|
||||
* Returns children of this page.
|
||||
*
|
||||
* @param bool $modular|null whether or not to return modular children
|
||||
* @return Collection
|
||||
* @return \Grav\Common\Page\Collection
|
||||
*/
|
||||
public function children($type = Page::STANDARD_PAGES)
|
||||
public function children()
|
||||
{
|
||||
/** @var Pages $pages */
|
||||
$pages = self::$grav['pages'];
|
||||
$children = $pages->children($this->path());
|
||||
|
||||
// Filter out modular pages on regular call
|
||||
// Filter out non-modular pages when all you want is modular
|
||||
foreach ($children as $child) {
|
||||
$is_modular_page = $child->modular();
|
||||
if (($is_modular_page && $type == Page::STANDARD_PAGES) || (!$is_modular_page && $type == Page::MODULAR_PAGES)) {
|
||||
$children->remove($child->path());
|
||||
}
|
||||
}
|
||||
|
||||
return $children;
|
||||
$pages = self::getGrav()['pages'];
|
||||
return $pages->children($this->path());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check to see if this item is the first in an array of sub-pages.
|
||||
*
|
||||
@@ -1186,15 +1446,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());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1204,15 +1457,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());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1243,16 +1489,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1263,7 +1501,7 @@ class Page
|
||||
public function active()
|
||||
{
|
||||
/** @var Uri $uri */
|
||||
$uri = self::$grav['uri'];
|
||||
$uri = self::getGrav()['uri'];
|
||||
if ($this->url() == $uri->url()) {
|
||||
return true;
|
||||
}
|
||||
@@ -1279,14 +1517,14 @@ class Page
|
||||
public function activeChild()
|
||||
{
|
||||
/** @var Uri $uri */
|
||||
$uri = self::$grav['uri'];
|
||||
$config = self::$grav['config'];
|
||||
$uri = self::getGrav()['uri'];
|
||||
$config = self::getGrav()['config'];
|
||||
|
||||
// Special check when item is home
|
||||
if ($this->home()) {
|
||||
$paths = $uri->paths();
|
||||
$home = ltrim($config->get('system.home.alias'), '/');
|
||||
if ($paths[0] == $home) {
|
||||
if (isset($paths[0]) && $paths[0] == $home) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
@@ -1315,7 +1553,7 @@ class Page
|
||||
*/
|
||||
public function root()
|
||||
{
|
||||
if (!$this->parent && !$this->name and !$this->visible) {
|
||||
if (!$this->parent && !$this->name && !$this->visible) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -1326,24 +1564,27 @@ class Page
|
||||
* Helper method to return a page.
|
||||
*
|
||||
* @param string $url the url of the page
|
||||
* @return Page page you were looking for if it exists
|
||||
* @param bool $all
|
||||
*
|
||||
* @return \Grav\Common\Page\Page page you were looking for if it exists
|
||||
* @deprecated
|
||||
*/
|
||||
public function find($url)
|
||||
public function find($url, $all = false)
|
||||
{
|
||||
/** @var Pages $pages */
|
||||
$pages = self::$grav['pages'];
|
||||
return $pages->dispatch($url);
|
||||
$pages = self::getGrav()['pages'];
|
||||
return $pages->dispatch($url, $all);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
@@ -1363,9 +1604,9 @@ class Page
|
||||
|
||||
// TODO: MOVE THIS INTO SOMEWHERE ELSE?
|
||||
/** @var Uri $uri */
|
||||
$uri = self::$grav['uri'];
|
||||
$uri = self::getGrav()['uri'];
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
$config = self::getGrav()['config'];
|
||||
|
||||
foreach ((array) $config->get('site.taxonomies') as $taxonomy) {
|
||||
if ($uri->param($taxonomy)) {
|
||||
@@ -1387,6 +1628,12 @@ class Page
|
||||
}
|
||||
// TODO: END OF MOVE
|
||||
|
||||
if (isset($params['dateRange'])) {
|
||||
$start = isset($params['dateRange']['start']) ? $params['dateRange']['start'] : 0;
|
||||
$end = isset($params['dateRange']['end']) ? $params['dateRange']['end'] : false;
|
||||
$collection->dateRange($start, $end);
|
||||
}
|
||||
|
||||
if (isset($params['order'])) {
|
||||
$by = isset($params['order']['by']) ? $params['order']['by'] : 'default';
|
||||
$dir = isset($params['order']['dir']) ? $params['order']['dir'] : 'asc';
|
||||
@@ -1395,18 +1642,21 @@ class Page
|
||||
}
|
||||
|
||||
/** @var Grav $grav */
|
||||
$grav = self::$grav['grav'];
|
||||
$grav = self::getGrav()['grav'];
|
||||
|
||||
// New Custom event to handle things like pagination.
|
||||
$grav->fireEvent('onCollectionProcessed', new Event(['collection' => $collection]));
|
||||
|
||||
$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;
|
||||
@@ -1414,6 +1664,7 @@ class Page
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*
|
||||
* @return mixed
|
||||
* @internal
|
||||
*/
|
||||
@@ -1446,14 +1697,24 @@ class Page
|
||||
if (!empty($parts)) {
|
||||
switch ($parts[0]) {
|
||||
case 'modular':
|
||||
$results = $this->children(Page::MODULAR_PAGES);
|
||||
$results = $this->children()->modular()->published();
|
||||
break;
|
||||
case 'children':
|
||||
$results = $this->children(Page::STANDARD_PAGES);
|
||||
$results = $this->children()->nonModular()->published();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '@page':
|
||||
if (!empty($params)) {
|
||||
$page = $this->find($params[0]);
|
||||
if ($page) {
|
||||
$results = $page->children()->nonModular()->published();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '@taxonomy':
|
||||
// Gets a collection of pages by using one of the following formats:
|
||||
// @taxonomy.category: blog
|
||||
@@ -1461,7 +1722,7 @@ class Page
|
||||
// @taxonomy: { category: [ blog, featured ], level: 1 }
|
||||
|
||||
/** @var Taxonomy $taxonomy_map */
|
||||
$taxonomy_map = self::$grav['taxonomy'];
|
||||
$taxonomy_map = self::getGrav()['taxonomy'];
|
||||
|
||||
if (!empty($parts)) {
|
||||
$params = [implode('.', $parts) => $params];
|
||||
@@ -1473,32 +1734,6 @@ class Page
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
* @deprecated
|
||||
*/
|
||||
public function subPages()
|
||||
{
|
||||
throw new \Exception('Use $page->collection() instead.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorting of sub-pages based on how to sort and the order.
|
||||
*
|
||||
* default - is the order based on the filesystem, ie 01.Home before 02.Advark
|
||||
* title - is the order based on the title set in the pages
|
||||
* date - is the order based on the date set in the pages
|
||||
* modified - is the order based on the last modified date of the pages
|
||||
* slug - is the order based on the URL slug
|
||||
*
|
||||
* @param string $order_by The order by which the sub-pages should be sorted "default", "title", "date", "folder"
|
||||
* @param string $order_dir The order, either "asc" or "desc"
|
||||
* @return $this|bool This Page object if sub-pages exist, else false
|
||||
*/
|
||||
public function sort($order_by = null, $order_dir = null)
|
||||
{
|
||||
throw new \Exception('Use $page->children()->sort() instead.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this Page object has a .md file associated with it or if its just a directory.
|
||||
@@ -1534,61 +1769,6 @@ class Page
|
||||
return $file && $file->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function hasSubPages()
|
||||
{
|
||||
throw new \Exception('Use $page->collection()->count() instead.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the Markdown if processing is enabled for it. If not, process as 'raw' which simply strips the
|
||||
* header YAML from the raw, and sends back the content portion. i.e. the bit below the header.
|
||||
*
|
||||
* @return string the content for the page
|
||||
*/
|
||||
protected function processMarkdown()
|
||||
{
|
||||
// Process Markdown if required
|
||||
$process_method = $this->shouldProcess('markdown') ? 'parseMarkdownContent' : 'rawContent';
|
||||
$content = $this->$process_method($this->raw_content);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the raw content. Basically just strips the headers out and returns the rest.
|
||||
*
|
||||
* @param string $content Input raw content
|
||||
* @return string Output content after headers have been stripped
|
||||
*/
|
||||
protected function rawContent($content)
|
||||
{
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the Markdown content. This strips the headers, the process the resulting content as Markdown.
|
||||
*
|
||||
* @param string $content Input raw content
|
||||
* @return string Output content that has been processed as Markdown
|
||||
*/
|
||||
protected function parseMarkdownContent($content)
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
|
||||
// get the appropriate setting for markdown extra
|
||||
if (isset($this->markdown_extra) ? $this->markdown_extra : $config->get('system.pages.markdown_extra')) {
|
||||
$parsedown = new MarkdownExtra($this);
|
||||
} else {
|
||||
$parsedown = new Markdown($this);
|
||||
}
|
||||
$content = $parsedown->text($content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans the path.
|
||||
*
|
||||
@@ -1618,7 +1798,7 @@ class Page
|
||||
// Do reordering.
|
||||
if ($reorder && $this->order() != $this->_original->order()) {
|
||||
/** @var Pages $pages */
|
||||
$pages = self::$grav['pages'];
|
||||
$pages = self::getGrav()['pages'];
|
||||
|
||||
$parent = $this->parent();
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Data\Blueprints;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use Whoops\Exception\ErrorException;
|
||||
|
||||
/**
|
||||
* GravPages is the class that is the entry point into the hierarchy of pages
|
||||
@@ -34,6 +36,11 @@ class Pages
|
||||
*/
|
||||
protected $children;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $base;
|
||||
|
||||
/**
|
||||
* @var array|string[]
|
||||
*/
|
||||
@@ -67,6 +74,23 @@ class Pages
|
||||
public function __construct(Grav $c)
|
||||
{
|
||||
$this->grav = $c;
|
||||
$this->base = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set base path for the pages.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public function base($path = null)
|
||||
{
|
||||
if ($path !== null) {
|
||||
$path = trim($path, '/');
|
||||
$this->base = $path ? '/' . $path : null;
|
||||
}
|
||||
|
||||
return $this->base;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,7 +260,7 @@ 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 + wildcards
|
||||
// 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'];
|
||||
@@ -246,15 +270,29 @@ class Pages
|
||||
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;
|
||||
// Try Regex style redirects
|
||||
foreach ((array)$config->get("site.redirects") as $pattern => $replace) {
|
||||
$pattern = '#' . $pattern . '#';
|
||||
try {
|
||||
$found = preg_replace($pattern, $replace, $url);
|
||||
if ($found != $url) {
|
||||
$this->grav->redirect($found);
|
||||
}
|
||||
} catch (ErrorException $e) {
|
||||
$this->grav['log']->error('site.redirects: '. $pattern . '-> ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Try Regex style routes
|
||||
foreach ((array)$config->get("site.routes") as $pattern => $replace) {
|
||||
$pattern = '#' . $pattern . '#';
|
||||
try {
|
||||
$found = preg_replace($pattern, $replace, $url);
|
||||
if ($found != $url) {
|
||||
$page = $this->dispatch($found, $all);
|
||||
}
|
||||
} catch (ErrorException $e) {
|
||||
$this->grav['log']->error('site.routes: '. $pattern . '-> ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,7 +308,10 @@ class Pages
|
||||
*/
|
||||
public function root()
|
||||
{
|
||||
return $this->instances[rtrim(PAGES_DIR, DS)];
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $this->grav['locator'];
|
||||
|
||||
return $this->instances[rtrim($locator->findResource('page://'), DS)];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,6 +340,28 @@ class Pages
|
||||
return $blueprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all pages
|
||||
*
|
||||
* @param \Grav\Common\Page\Page $current
|
||||
* @return \Grav\Common\Page\Collection
|
||||
*/
|
||||
public function all(Page $current = null)
|
||||
{
|
||||
$all = new Collection();
|
||||
$current = $current ?: $this->root();
|
||||
|
||||
if ($current->routable()) {
|
||||
$all[$current->path()] = [ 'slug' => $current->slug() ];
|
||||
}
|
||||
|
||||
foreach ($current->children() as $next) {
|
||||
$all->append($this->all($next));
|
||||
}
|
||||
|
||||
return $all;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of route/title of all pages.
|
||||
*
|
||||
@@ -400,6 +463,10 @@ class Pages
|
||||
/** @var Config $config */
|
||||
$config = $this->grav['config'];
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $this->grav['locator'];
|
||||
$pagesDir = $locator->findResource('page://');
|
||||
|
||||
if ($config->get('system.cache.enabled')) {
|
||||
/** @var Cache $cache */
|
||||
$cache = $this->grav['cache'];
|
||||
@@ -413,10 +480,10 @@ class Pages
|
||||
$last_modified = 0;
|
||||
break;
|
||||
case 'folder':
|
||||
$last_modified = Folder::lastModifiedFolder(PAGES_DIR);
|
||||
$last_modified = Folder::lastModifiedFolder($pagesDir);
|
||||
break;
|
||||
default:
|
||||
$last_modified = Folder::lastModifiedFile(PAGES_DIR);
|
||||
$last_modified = Folder::lastModifiedFile($pagesDir);
|
||||
}
|
||||
|
||||
$page_cache_id = md5(USER_DIR.$last_modified.$config->checksum());
|
||||
@@ -424,7 +491,8 @@ class Pages
|
||||
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->recurse($pagesDir);
|
||||
$this->buildRoutes();
|
||||
|
||||
// save pages, routes, taxonomy, and sort to cache
|
||||
@@ -438,7 +506,7 @@ class Pages
|
||||
$taxonomy->taxonomy($taxonomy_map);
|
||||
}
|
||||
} else {
|
||||
$this->recurse();
|
||||
$this->recurse($pagesDir);
|
||||
$this->buildRoutes();
|
||||
}
|
||||
}
|
||||
@@ -452,7 +520,7 @@ class Pages
|
||||
* @throws \RuntimeException
|
||||
* @internal
|
||||
*/
|
||||
protected function recurse($directory = PAGES_DIR, Page &$parent = null)
|
||||
protected function recurse($directory, Page &$parent = null)
|
||||
{
|
||||
$directory = rtrim($directory, DS);
|
||||
$iterator = new \DirectoryIterator($directory);
|
||||
@@ -461,6 +529,11 @@ class Pages
|
||||
/** @var Config $config */
|
||||
$config = $this->grav['config'];
|
||||
|
||||
// Fire event for memory and time consuming plugins...
|
||||
if ($parent === null && $config->get('system.pages.events.page')) {
|
||||
$this->grav->fireEvent('onBuildPagesInitialized');
|
||||
}
|
||||
|
||||
$page->path($directory);
|
||||
if ($parent) {
|
||||
$page->parent($parent);
|
||||
@@ -487,20 +560,27 @@ class Pages
|
||||
|
||||
/** @var \DirectoryIterator $file */
|
||||
foreach ($iterator as $file) {
|
||||
if ($file->isDot()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $file->getFilename();
|
||||
$modified = $file->getMTime();
|
||||
|
||||
if ($file->isFile() && Utils::endsWith($name, CONTENT_EXT)) {
|
||||
|
||||
$page->init($file);
|
||||
$content_exists = true;
|
||||
|
||||
if ($config->get('system.pages.events.page')) {
|
||||
$this->grav->fireEvent('onPageProcessed', new Event(['page' => $page]));
|
||||
if ($file->isFile()) {
|
||||
// Update the last modified if it's newer than already found
|
||||
if ($file->getBasename() !== '.DS_Store' && ($modified = $file->getMTime()) > $last_modified) {
|
||||
$last_modified = $modified;
|
||||
}
|
||||
|
||||
} elseif ($file->isDir() && !$file->isDot()) {
|
||||
if (preg_match('/^[^.].*'.CONTENT_EXT.'$/', $name)) {
|
||||
$page->init($file);
|
||||
$content_exists = true;
|
||||
|
||||
if ($config->get('system.pages.events.page')) {
|
||||
$this->grav->fireEvent('onPageProcessed', new Event(['page' => $page]));
|
||||
}
|
||||
}
|
||||
} elseif ($file->isDir()) {
|
||||
if (!$page->path()) {
|
||||
$page->path($file->getPath());
|
||||
}
|
||||
@@ -514,20 +594,10 @@ class Pages
|
||||
|
||||
$this->children[$page->path()][$child->path()] = array('slug' => $child->slug());
|
||||
|
||||
// set the modified time if not already set
|
||||
if (!$page->date()) {
|
||||
$page->date($modified);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Set routability to false if no page found
|
||||
@@ -556,7 +626,6 @@ class Pages
|
||||
// Build routes and taxonomy map.
|
||||
/** @var $page Page */
|
||||
foreach ($this->instances as $page) {
|
||||
|
||||
$parent = $page->parent();
|
||||
|
||||
if ($parent) {
|
||||
@@ -594,9 +663,18 @@ class Pages
|
||||
protected function buildSort($path, array $pages, $order_by = 'default', $manual = null)
|
||||
{
|
||||
$list = array();
|
||||
$header_default = null;
|
||||
$header_query = null;
|
||||
|
||||
// do this headery query work only once
|
||||
if (strpos($order_by, 'header.') === 0) {
|
||||
$header_query = explode('|', str_replace('header.', '', $order_by));
|
||||
if (isset($header_query[1])) {
|
||||
$header_default = $header_query[1];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($pages as $key => $info) {
|
||||
|
||||
$child = isset($this->instances[$key]) ? $this->instances[$key] : null;
|
||||
if (!$child) {
|
||||
throw new \RuntimeException("Page does not exist: {$key}");
|
||||
@@ -613,11 +691,20 @@ class Pages
|
||||
$list[$key] = $child->modified();
|
||||
break;
|
||||
case 'slug':
|
||||
$list[$key] = $info['slug'];
|
||||
$list[$key] = $child->slug();
|
||||
break;
|
||||
case 'basename':
|
||||
$list[$key] = basename($key);
|
||||
break;
|
||||
case (is_string($header_query[0])):
|
||||
$child_header = new Header((array)$child->header());
|
||||
$header_value = $child_header->get($header_query[0]);
|
||||
if ($header_value) {
|
||||
$list[$key] = $header_value;
|
||||
} else {
|
||||
$list[$key] = $header_default ?: $key;
|
||||
}
|
||||
break;
|
||||
case 'manual':
|
||||
case 'default':
|
||||
default:
|
||||
|
||||
@@ -78,7 +78,7 @@ class Types implements \ArrayAccess, \Iterator, \Countable
|
||||
if (strpos($name, 'modular/') !== 0) {
|
||||
continue;
|
||||
}
|
||||
$list[$name] = trim(ucfirst(strtr(basename($name), '_', ' ')));
|
||||
$list[basename($name)] = trim(ucfirst(strtr(basename($name), '_', ' ')));
|
||||
}
|
||||
ksort($list);
|
||||
return $list;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Page\Page;
|
||||
use Grav\Common\Config\Config;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
|
||||
@@ -24,6 +26,10 @@ class Plugin implements EventSubscriberInterface
|
||||
protected $config;
|
||||
|
||||
protected $active = true;
|
||||
/**
|
||||
* @var \Grav\Common\string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* By default assign all methods as listeners using the default priority.
|
||||
@@ -47,13 +53,15 @@ class Plugin implements EventSubscriberInterface
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Grav $grav
|
||||
* @param Config $config
|
||||
* @param string $name
|
||||
* @param Grav $grav
|
||||
* @param Config $config
|
||||
*/
|
||||
public function __construct(Grav $grav, Config $config)
|
||||
public function __construct($name, Grav $grav, Config $config)
|
||||
{
|
||||
$this->grav = $grav;
|
||||
$this->config = $config;
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function isAdmin()
|
||||
@@ -84,4 +92,94 @@ class Plugin implements EventSubscriberInterface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $events
|
||||
*/
|
||||
protected function disable(array $events)
|
||||
{
|
||||
/** @var EventDispatcher $dispatcher */
|
||||
$dispatcher = $this->grav['events'];
|
||||
|
||||
foreach ($events as $eventName => $params) {
|
||||
if (is_string($params)) {
|
||||
$dispatcher->removeListener($eventName, array($this, $params));
|
||||
} elseif (is_string($params[0])) {
|
||||
$dispatcher->removeListener($eventName, array($this, $params[0]));
|
||||
} else {
|
||||
foreach ($params as $listener) {
|
||||
$dispatcher->removeListener($eventName, array($this, $listener[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will search a string for markdown links in a specific format. The link value can be
|
||||
* optionally compared against via the $internal_regex and operated on by the callback $function
|
||||
* provided.
|
||||
*
|
||||
* format: [plugin:myplugin_name](function_data)
|
||||
*
|
||||
* @param $content The string to perform operations upon
|
||||
* @param $function The anonymous callback function
|
||||
* @param string $internal_regex Optional internal regex to extra data from
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function parseLinks($content, $function, $internal_regex = '(.*)')
|
||||
{
|
||||
$regex = '/\[plugin:(?:'.$this->name.')\]\('.$internal_regex.'\)/i';
|
||||
return preg_replace_callback($regex, $function, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge global and page configurations.
|
||||
*
|
||||
* @param Page $page The page to merge the configurations with the
|
||||
* plugin settings.
|
||||
* @param bool $deep Should you use deep or shallow merging
|
||||
* @param array $params Array of additional configuration options to
|
||||
* merge with the plugin settings.
|
||||
*
|
||||
* @return \Grav\Common\Data\Data
|
||||
*/
|
||||
protected function mergeConfig(Page $page, $deep = false, $params = [])
|
||||
{
|
||||
$class_name = $this->name;
|
||||
$class_name_merged = $class_name . '.merged';
|
||||
$defaults = $this->config->get('plugins.'. $class_name, []);
|
||||
$page_header = $page->header();
|
||||
$header = [];
|
||||
if (!isset($page_header->$class_name_merged) && isset($page_header->$class_name)) {
|
||||
// Get default plugin configurations and retrieve page header configuration
|
||||
$config = $page_header->$class_name;
|
||||
if (is_bool($config)) {
|
||||
// Overwrite enabled option with boolean value in page header
|
||||
$config = ['enabled' => $config];
|
||||
}
|
||||
// Merge page header settings using deep or shallow merging technique
|
||||
if ($deep) {
|
||||
$header = array_replace_recursive($defaults, $config);
|
||||
} else {
|
||||
$header = array_merge($defaults, $config);
|
||||
}
|
||||
// Create new config object and set it on the page object so it's cached for next time
|
||||
$page->modifyHeader($class_name_merged, new Data($header));
|
||||
} else if (isset($page_header->$class_name_merged)) {
|
||||
$merged = $page_header->$class_name_merged;
|
||||
$header = $merged->toArray();
|
||||
}
|
||||
if (empty($header)) {
|
||||
$header = $defaults;
|
||||
}
|
||||
// Merge additional parameter with configuration options
|
||||
if ($deep) {
|
||||
$header = array_replace_recursive($header, $params);
|
||||
} else {
|
||||
$header = array_merge($header, $params);
|
||||
}
|
||||
// Return configurations as a new data config class
|
||||
return new Data($header);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Grav\Common;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Data\Blueprints;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
|
||||
@@ -17,12 +18,7 @@ use RocketTheme\Toolbox\Event\EventSubscriberInterface;
|
||||
*/
|
||||
class Plugins extends Iterator
|
||||
{
|
||||
protected $grav;
|
||||
|
||||
public function __construct(Grav $grav)
|
||||
{
|
||||
$this->grav = $grav;
|
||||
}
|
||||
use GravTrait;
|
||||
|
||||
/**
|
||||
* Recurses through the plugins directory creating Plugin objects for each plugin it finds.
|
||||
@@ -33,11 +29,11 @@ class Plugins extends Iterator
|
||||
public function init()
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = $this->grav['config'];
|
||||
$config = self::getGrav()['config'];
|
||||
$plugins = (array) $config->get('plugins');
|
||||
|
||||
/** @var EventDispatcher $events */
|
||||
$events = $this->grav['events'];
|
||||
$events = self::getGrav()['events'];
|
||||
|
||||
foreach ($plugins as $plugin => $data) {
|
||||
if (empty($data['enabled'])) {
|
||||
@@ -45,20 +41,33 @@ class Plugins extends Iterator
|
||||
continue;
|
||||
}
|
||||
|
||||
$filePath = $this->grav['locator']('plugins://' . $plugin . DS . $plugin . PLUGIN_EXT);
|
||||
$locator = self::getGrav()['locator'];
|
||||
$filePath = $locator->findResource('plugins://' . $plugin . DS . $plugin . PLUGIN_EXT);
|
||||
if (!is_file($filePath)) {
|
||||
throw new \RuntimeException(sprintf("Plugin '%s' enabled but not found! Try clearing cache with `bin/grav clear-cache`", $plugin));
|
||||
self::getGrav()['log']->addWarning(sprintf("Plugin '%s' enabled but not found! Try clearing cache with `bin/grav clear-cache`", $plugin));
|
||||
continue;
|
||||
}
|
||||
|
||||
require_once $filePath;
|
||||
|
||||
$pluginClass = 'Grav\\Plugin\\'.ucfirst($plugin).'Plugin';
|
||||
$pluginClassFormat = [
|
||||
'Grav\\Plugin\\'.ucfirst($plugin).'Plugin',
|
||||
'Grav\\Plugin\\'.Inflector::camelize($plugin).'Plugin'
|
||||
];
|
||||
$pluginClassName = false;
|
||||
|
||||
if (!class_exists($pluginClass)) {
|
||||
foreach ($pluginClassFormat as $pluginClass) {
|
||||
if (class_exists($pluginClass)) {
|
||||
$pluginClassName = $pluginClass;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (false === $pluginClassName) {
|
||||
throw new \RuntimeException(sprintf("Plugin '%s' class not found! Try reinstalling this plugin.", $plugin));
|
||||
}
|
||||
|
||||
$instance = new $pluginClass($this->grav, $config);
|
||||
$instance = new $pluginClassName($plugin, self::getGrav(), $config);
|
||||
if ($instance instanceof EventSubscriberInterface) {
|
||||
$events->addSubscriber($instance);
|
||||
}
|
||||
@@ -82,7 +91,8 @@ class Plugins extends Iterator
|
||||
public static function all()
|
||||
{
|
||||
$list = array();
|
||||
$iterator = new \DirectoryIterator('plugins://');
|
||||
$locator = Grav::instance()['locator'];
|
||||
$iterator = new \DirectoryIterator($locator->findResource('plugins://', false));
|
||||
|
||||
/** @var \DirectoryIterator $directory */
|
||||
foreach ($iterator as $directory) {
|
||||
@@ -110,10 +120,10 @@ class Plugins extends Iterator
|
||||
$obj = new Data($file->content(), $blueprint);
|
||||
|
||||
// Override with user configuration.
|
||||
$file = CompiledYamlFile::instance("user://config/plugins/{$name}.yaml");
|
||||
$obj->merge($file->content());
|
||||
$obj->merge(self::getGrav()['config']->get('plugins.' . $name) ?: []);
|
||||
|
||||
// Save configuration always to user/config.
|
||||
$file = CompiledYamlFile::instance("config://plugins/{$name}.yaml");
|
||||
$obj->file($file);
|
||||
|
||||
return $obj;
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
namespace Grav\Common\Service;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use RocketTheme\Toolbox\Blueprints\Blueprints;
|
||||
@@ -18,11 +15,17 @@ use RocketTheme\Toolbox\Blueprints\Blueprints;
|
||||
class ConfigServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
private $environment;
|
||||
private $setup;
|
||||
|
||||
public function register(Container $container)
|
||||
{
|
||||
$self = $this;
|
||||
|
||||
// Pre-load setup.php as it contains our initial configuration.
|
||||
$file = GRAV_ROOT . '/setup.php';
|
||||
$this->setup = is_file($file) ? (array) include $file : [];
|
||||
$this->environment = isset($this->setup['environment']) ? $this->setup['environment'] : null;
|
||||
|
||||
$container['blueprints'] = function ($c) use ($self) {
|
||||
return $self->loadMasterBlueprints($c);
|
||||
};
|
||||
@@ -35,20 +38,8 @@ class ConfigServiceProvider implements ServiceProviderInterface
|
||||
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);
|
||||
}
|
||||
$config = new Config($this->setup, $container, $environment);
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ use Pimple\ServiceProviderInterface;
|
||||
use Whoops\Handler\JsonResponseHandler;
|
||||
use Whoops\Handler\PrettyPageHandler;
|
||||
use Whoops\Handler\PlainTextHandler;
|
||||
use Whoops\Run;
|
||||
|
||||
class ErrorServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
@@ -30,8 +29,12 @@ class ErrorServiceProvider implements ServiceProviderInterface
|
||||
$errors->pushHandler($json_page, 'json');
|
||||
|
||||
$logger = $container['log'];
|
||||
$errors->pushHandler(function ($exception, $inspector, $run) use($logger) {
|
||||
$logger->addCritical($exception->getMessage(). ' - Trace: '. $exception->getTraceAsString());
|
||||
$errors->pushHandler(function (\Exception $exception, $inspector, $run) use ($logger) {
|
||||
try {
|
||||
$logger->addCritical($exception->getMessage() . ' - Trace: ' . $exception->getTraceAsString());
|
||||
} catch (\Exception $e) {
|
||||
echo $e;
|
||||
}
|
||||
}, 'log');
|
||||
|
||||
$errors->register();
|
||||
|
||||
@@ -5,7 +5,6 @@ use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use \Monolog\Logger;
|
||||
use \Monolog\Handler\StreamHandler;
|
||||
use \Monolog\Handler\RotatingFileHandler;
|
||||
|
||||
class LoggerServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
@@ -13,9 +12,7 @@ class LoggerServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
$log = new Logger('grav');
|
||||
$log_file = LOG_DIR.'grav.log';
|
||||
$log_days = 14;
|
||||
|
||||
// $log->pushHandler(new RotatingFileHandler($log_file, $log_days, Logger::WARNING));
|
||||
$log->pushHandler(new StreamHandler($log_file, Logger::WARNING));
|
||||
|
||||
$container['log'] = $log;
|
||||
|
||||
@@ -11,52 +11,32 @@ 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 UniformResourceLocator(ROOT_DIR);
|
||||
$self->init($c, $locator);
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $c['config'];
|
||||
$config->initializeLocator($locator);
|
||||
|
||||
return $locator;
|
||||
};
|
||||
|
||||
$container['streams'] = function($c) use ($self) {
|
||||
/** @var Config $config */
|
||||
$config = $c['config'];
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $c['locator'];
|
||||
|
||||
// Set locator to both streams.
|
||||
Stream::setLocator($locator);
|
||||
ReadOnlyStream::setLocator($locator);
|
||||
|
||||
return new StreamBuilder($this->schemes);
|
||||
return new StreamBuilder($config->getStreams($c));
|
||||
};
|
||||
}
|
||||
|
||||
protected function init(Container $container, UniformResourceLocator $locator)
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = $container['config'];
|
||||
$schemes = (array) $config->get('streams.schemes', []);
|
||||
|
||||
foreach ($schemes as $scheme => $config) {
|
||||
if (isset($config['paths'])) {
|
||||
$locator->addPath($scheme, '', $config['paths']);
|
||||
}
|
||||
if (isset($config['prefixes'])) {
|
||||
foreach ($config['prefixes'] as $prefix => $paths) {
|
||||
$locator->addPath($scheme, $prefix, $paths);
|
||||
}
|
||||
}
|
||||
|
||||
$type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream';
|
||||
if ($type[0] != '\\') {
|
||||
$type = '\\RocketTheme\\Toolbox\\StreamWrapper\\' . $type;
|
||||
}
|
||||
|
||||
$this->schemes[$scheme] = $type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,10 @@ class Taxonomy
|
||||
$page_taxonomy = $page->taxonomy();
|
||||
}
|
||||
|
||||
if (!$page->published()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $this->grav['config'];
|
||||
if ($config->get('site.taxonomies') && count($page_taxonomy) > 0) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user