mirror of
https://github.com/getgrav/grav.git
synced 2025-12-05 15:29:57 +01:00
Compare commits
403 Commits
1.6.13
...
1.7.0-rc.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cb678bdb6 | ||
|
|
14110d5475 | ||
|
|
c742eee1cc | ||
|
|
86c87929ec | ||
|
|
ffeb5648c6 | ||
|
|
e0e92b843c | ||
|
|
a996137317 | ||
|
|
9fb55d8d0f | ||
|
|
861f53c5dc | ||
|
|
a588395ba9 | ||
|
|
e80b7fe19e | ||
|
|
4e23f07fcd | ||
|
|
a571f42e1b | ||
|
|
2fd5492286 | ||
|
|
4e227fcae5 | ||
|
|
99c6a78134 | ||
|
|
a93ef3f752 | ||
|
|
0814d5e3bb | ||
|
|
2e8be3c67f | ||
|
|
191bf8730a | ||
|
|
09e8dfdbfd | ||
|
|
a1ea841034 | ||
|
|
f9f836959c | ||
|
|
c4ce2d1648 | ||
|
|
0f66032c9b | ||
|
|
8678f22f6b | ||
|
|
4ca1f1f4ca | ||
|
|
913efdbd6a | ||
|
|
9c123f7d3d | ||
|
|
2b00e93f22 | ||
|
|
8322a0cfa3 | ||
|
|
ab6b82eaaa | ||
|
|
b16e8066ca | ||
|
|
09e10d16ea | ||
|
|
ef8e1c2fdf | ||
|
|
e2843e6477 | ||
|
|
bc1dd2a7b4 | ||
|
|
3e52101bf8 | ||
|
|
d11772b681 | ||
|
|
32a9acc62e | ||
|
|
d568ddfa5c | ||
|
|
1656f0160b | ||
|
|
14b33824b1 | ||
|
|
7902912269 | ||
|
|
2db04e43d1 | ||
|
|
467c33d3e1 | ||
|
|
87f17d296d | ||
|
|
feeee9ef86 | ||
|
|
7ba964f161 | ||
|
|
77205fb3d8 | ||
|
|
4bba284bf5 | ||
|
|
a6e78de197 | ||
|
|
8fc7755e90 | ||
|
|
fdfb5bb8ac | ||
|
|
d0800136b7 | ||
|
|
d368aeafd4 | ||
|
|
e59c596886 | ||
|
|
411656a9d6 | ||
|
|
8599faaf3b | ||
|
|
90e55ead29 | ||
|
|
f85c94b403 | ||
|
|
b4fa7e38b9 | ||
|
|
411871f636 | ||
|
|
b01d1baa53 | ||
|
|
c3c33e954c | ||
|
|
edd0bc25a2 | ||
|
|
6c57104658 | ||
|
|
26d7181eca | ||
|
|
1fb611655f | ||
|
|
b561c2b47c | ||
|
|
9beefb3162 | ||
|
|
41851b73b9 | ||
|
|
c325c24897 | ||
|
|
efd4e52571 | ||
|
|
565f26db8e | ||
|
|
b641b0e442 | ||
|
|
2a6276a941 | ||
|
|
eb1b9567df | ||
|
|
7dafa2a207 | ||
|
|
5253aa6aef | ||
|
|
1b34530a4a | ||
|
|
ae41b6f5ff | ||
|
|
4d09a345a4 | ||
|
|
8ac3451fbc | ||
|
|
15f1f2a03d | ||
|
|
ffa9ef6a7e | ||
|
|
a75c0cbe62 | ||
|
|
c795ead402 | ||
|
|
91270c9c66 | ||
|
|
9259c2f660 | ||
|
|
138d1e93e3 | ||
|
|
5db9f16174 | ||
|
|
342eac1047 | ||
|
|
88c859fde2 | ||
|
|
8b5bce0b6d | ||
|
|
09c4cf66f5 | ||
|
|
46b859b8df | ||
|
|
a7c23c58e5 | ||
|
|
d206711354 | ||
|
|
0007100a97 | ||
|
|
3d999501a0 | ||
|
|
52f83775e3 | ||
|
|
a35cf5a830 | ||
|
|
39837d0826 | ||
|
|
66bd6a4046 | ||
|
|
4e9d3395e0 | ||
|
|
248c7764f0 | ||
|
|
f72eb1b002 | ||
|
|
25caa5138a | ||
|
|
dffb227df6 | ||
|
|
7c161d5cbc | ||
|
|
7b2313ef0b | ||
|
|
1ad2a3f212 | ||
|
|
1f2363b623 | ||
|
|
d1c069c2ee | ||
|
|
67e772a5ce | ||
|
|
59bb167bd4 | ||
|
|
5c9eb1cdb8 | ||
|
|
94a54d2a82 | ||
|
|
f201d48112 | ||
|
|
df51b64b35 | ||
|
|
93ff915737 | ||
|
|
e30ab9a043 | ||
|
|
8e59a08f9e | ||
|
|
5a874006b4 | ||
|
|
5d639cc633 | ||
|
|
ab7038b49e | ||
|
|
651b354d3e | ||
|
|
90c2079529 | ||
|
|
fa064301a2 | ||
|
|
5424047d02 | ||
|
|
ce2b80aeb9 | ||
|
|
e91ae5542d | ||
|
|
922f263005 | ||
|
|
01af81a0a6 | ||
|
|
bd8c274f38 | ||
|
|
05fb69daa7 | ||
|
|
21f95eba53 | ||
|
|
4ef56054ee | ||
|
|
bec9292a5c | ||
|
|
0fa6328816 | ||
|
|
b30240c340 | ||
|
|
59eb3b4cb2 | ||
|
|
5fa047c1b7 | ||
|
|
30481da51d | ||
|
|
beb8f09a9d | ||
|
|
603bb6c878 | ||
|
|
dac3e57fd4 | ||
|
|
b601d2c8fd | ||
|
|
ab75201f11 | ||
|
|
45b1b0a2ef | ||
|
|
ca2f657c98 | ||
|
|
5c33882f5b | ||
|
|
0f85214693 | ||
|
|
4570c00041 | ||
|
|
4a0c8846e2 | ||
|
|
41e51cd86d | ||
|
|
e2ed3098a3 | ||
|
|
4e9ca82a0f | ||
|
|
f032f310b5 | ||
|
|
6b665b112c | ||
|
|
d9dbe5520d | ||
|
|
5b8674122a | ||
|
|
2e245cd36f | ||
|
|
e36a2ea1b0 | ||
|
|
53b7c95b0d | ||
|
|
46975cca22 | ||
|
|
a418acc32b | ||
|
|
eeb35fc521 | ||
|
|
90ca9f9d49 | ||
|
|
ba267a389e | ||
|
|
0ab99806db | ||
|
|
c300a3b8f8 | ||
|
|
8480fb68ac | ||
|
|
8e82056afa | ||
|
|
3e34d54b9a | ||
|
|
9ad7b208ba | ||
|
|
1fa62d2bdc | ||
|
|
b659c56aec | ||
|
|
3bd02b95fe | ||
|
|
9e5ad84a48 | ||
|
|
40a6c4bf72 | ||
|
|
22acffac5c | ||
|
|
ede749821d | ||
|
|
f26e518c03 | ||
|
|
0f6a517589 | ||
|
|
3530f4fdef | ||
|
|
b92476d40d | ||
|
|
0d0bb2c229 | ||
|
|
de59bad0f8 | ||
|
|
72ad49610c | ||
|
|
dcd1f3b10d | ||
|
|
52cf554ea2 | ||
|
|
f30f6485ba | ||
|
|
dd8b503aa0 | ||
|
|
dab30673e0 | ||
|
|
13689c2065 | ||
|
|
6e23627f26 | ||
|
|
7db85cc79c | ||
|
|
9b6f218f33 | ||
|
|
829da9ee3a | ||
|
|
033b54104e | ||
|
|
e5cedd074b | ||
|
|
a6741cb761 | ||
|
|
8cbc2a27cd | ||
|
|
5f1639dc63 | ||
|
|
ed87faad92 | ||
|
|
8d8b803e66 | ||
|
|
e4ed00d84a | ||
|
|
239f34d40c | ||
|
|
20b9ca56fa | ||
|
|
647ae0fda3 | ||
|
|
806dbd9ee5 | ||
|
|
1ab8442630 | ||
|
|
040c34d693 | ||
|
|
a2ea6faf4d | ||
|
|
8942aa8afc | ||
|
|
256cbe3f12 | ||
|
|
8b31ee173e | ||
|
|
182970eb78 | ||
|
|
9ed3da3df2 | ||
|
|
14eaa4d00a | ||
|
|
e134e3dbd9 | ||
|
|
5bfb168cd7 | ||
|
|
5aef09a410 | ||
|
|
76732ab671 | ||
|
|
f54d9af758 | ||
|
|
f883191d99 | ||
|
|
de5ead78d1 | ||
|
|
44bbdf7e39 | ||
|
|
4b4eedf467 | ||
|
|
bb477fd3b1 | ||
|
|
758e316a65 | ||
|
|
2c38e24d00 | ||
|
|
ca24e63d22 | ||
|
|
f1909d80db | ||
|
|
7718dd7e98 | ||
|
|
cc66070e85 | ||
|
|
bbdc54b406 | ||
|
|
c013f63b26 | ||
|
|
aa007badb5 | ||
|
|
bb2e7a720b | ||
|
|
c36e6abd66 | ||
|
|
c2b1142b7a | ||
|
|
e03fb200a6 | ||
|
|
a964a34a6f | ||
|
|
1c28fd4c4c | ||
|
|
e8529e7d0b | ||
|
|
a6032af594 | ||
|
|
ea49415e14 | ||
|
|
7b26022f9f | ||
|
|
443fecfeb6 | ||
|
|
c3324e3702 | ||
|
|
9e60408769 | ||
|
|
3737bc9371 | ||
|
|
3d2360c995 | ||
|
|
08b8505b6d | ||
|
|
9607a99a7d | ||
|
|
7e63935001 | ||
|
|
3d767a4d25 | ||
|
|
ee53e1be6e | ||
|
|
676924cce5 | ||
|
|
36a3a95ed9 | ||
|
|
95c58c8361 | ||
|
|
66c17a8f53 | ||
|
|
7172da8ed6 | ||
|
|
c55ea919ef | ||
|
|
53216631a6 | ||
|
|
69d6b52a0e | ||
|
|
ea1e0a76c1 | ||
|
|
833fe8b729 | ||
|
|
23d508b390 | ||
|
|
e8b24479b9 | ||
|
|
1485c23aba | ||
|
|
d43357f366 | ||
|
|
c8e5aa05f9 | ||
|
|
04ccce1f67 | ||
|
|
5826821895 | ||
|
|
3ed341304b | ||
|
|
025e73affd | ||
|
|
5635ba2bb7 | ||
|
|
95637a243c | ||
|
|
cd417a1509 | ||
|
|
631ae3d3d5 | ||
|
|
7c34224304 | ||
|
|
6062e47377 | ||
|
|
a94abb4fb2 | ||
|
|
8ab317b49a | ||
|
|
44dda3d607 | ||
|
|
75ed986437 | ||
|
|
3ac785b9ce | ||
|
|
de367e1558 | ||
|
|
4fead303d1 | ||
|
|
41898af46f | ||
|
|
fe8833876c | ||
|
|
e116998914 | ||
|
|
053f96dec1 | ||
|
|
94494c3c96 | ||
|
|
5afae3c3f2 | ||
|
|
eed3d84a10 | ||
|
|
e7b996104f | ||
|
|
3f176c1924 | ||
|
|
91d20d8840 | ||
|
|
6998505e3c | ||
|
|
ffa2e0a6f6 | ||
|
|
b54e4fb71b | ||
|
|
8f391d327a | ||
|
|
e0e29f468b | ||
|
|
89b9cc5367 | ||
|
|
f6c30cbeae | ||
|
|
cca7b6b1d4 | ||
|
|
c765787102 | ||
|
|
dff3872b43 | ||
|
|
3c91cea232 | ||
|
|
a2e9c013ad | ||
|
|
dd82ab45bc | ||
|
|
46b710b435 | ||
|
|
a6d3e1ee8e | ||
|
|
2b29b17044 | ||
|
|
5316f0f28c | ||
|
|
ac4d6cc8d0 | ||
|
|
565947e074 | ||
|
|
25d1767e6c | ||
|
|
c079f9b95b | ||
|
|
20cfb45c14 | ||
|
|
5314558a8e | ||
|
|
02b93d510a | ||
|
|
8dfb0ca993 | ||
|
|
574df5cf80 | ||
|
|
34dcd8c346 | ||
|
|
5079077e1d | ||
|
|
1f120a0127 | ||
|
|
fe05c9b87b | ||
|
|
f795a634b5 | ||
|
|
888b93926c | ||
|
|
687f29f912 | ||
|
|
273bc9d970 | ||
|
|
d593d5a392 | ||
|
|
18fb688cde | ||
|
|
d8fccb0edd | ||
|
|
5843347c46 | ||
|
|
fedb0625b8 | ||
|
|
72d012e401 | ||
|
|
8d77b50055 | ||
|
|
42eefbd34c | ||
|
|
e5e5bf1bd8 | ||
|
|
01ec334127 | ||
|
|
916469a903 | ||
|
|
55f205c801 | ||
|
|
8a3fc57cd8 | ||
|
|
6cd1cca4fc | ||
|
|
1c9926ff38 | ||
|
|
638477b06d | ||
|
|
7e3ca73b0e | ||
|
|
57a3a20868 | ||
|
|
4e45c5be95 | ||
|
|
ca89156c4f | ||
|
|
dc5390b3dc | ||
|
|
0e8c7eae99 | ||
|
|
115bdb7e10 | ||
|
|
9738c55633 | ||
|
|
23921c1a35 | ||
|
|
1f3547b15b | ||
|
|
6974a24669 | ||
|
|
2cf35ec2c7 | ||
|
|
d21bb6b8c7 | ||
|
|
9b8b480c8c | ||
|
|
ac654d56d0 | ||
|
|
db9e1a197e | ||
|
|
19bb9a6966 | ||
|
|
b7a1c7b72a | ||
|
|
042486bc73 | ||
|
|
42cc1c6b01 | ||
|
|
407d2c8323 | ||
|
|
8ab14c0639 | ||
|
|
48c281024f | ||
|
|
f7e1bec0cf | ||
|
|
41c7973fc7 | ||
|
|
bc93e70d11 | ||
|
|
320ab41435 | ||
|
|
7213c393fd | ||
|
|
6caadc8396 | ||
|
|
e0d1385061 | ||
|
|
e1eed973a2 | ||
|
|
f4645fc77e | ||
|
|
136ec07450 | ||
|
|
e01605de55 | ||
|
|
5cc48c4e01 | ||
|
|
5e2545c606 | ||
|
|
0fd2627cea | ||
|
|
cfcd955cc2 | ||
|
|
7d59b69709 | ||
|
|
e8eb1d9a44 | ||
|
|
2117748f18 | ||
|
|
e76733e8c3 | ||
|
|
2da5f90e9c | ||
|
|
81b100cd3b | ||
|
|
19be8f5104 | ||
|
|
ad64a9d857 | ||
|
|
331f416e36 | ||
|
|
3a48c79b21 | ||
|
|
0c66de25c4 | ||
|
|
0bd227c22d |
44
.phan/config.php
Normal file
44
.phan/config.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
return [
|
||||
"target_php_version" => null,
|
||||
'pretend_newer_core_functions_exist' => true,
|
||||
'allow_missing_properties' => false,
|
||||
'null_casts_as_any_type' => false,
|
||||
'null_casts_as_array' => false,
|
||||
'array_casts_as_null' => false,
|
||||
'strict_method_checking' => true,
|
||||
'quick_mode' => false,
|
||||
'simplify_ast' => false,
|
||||
'directory_list' => [
|
||||
'.',
|
||||
],
|
||||
"exclude_analysis_directory_list" => [
|
||||
'vendor/'
|
||||
],
|
||||
'exclude_file_list' => [
|
||||
'system/src/Grav/Common/Errors/Resources/layout.html.php',
|
||||
'tests/_support/AcceptanceTester.php',
|
||||
'tests/_support/FunctionalTester.php',
|
||||
'tests/_support/UnitTester.php',
|
||||
],
|
||||
'autoload_internal_extension_signatures' => [
|
||||
'memcached' => '.phan/internal_stubs/memcached.phan_php',
|
||||
'memcache' => '.phan/internal_stubs/memcache.phan_php',
|
||||
'redis' => '.phan/internal_stubs/Redis.phan_php',
|
||||
],
|
||||
'plugins' => [
|
||||
'AlwaysReturnPlugin',
|
||||
'UnreachableCodePlugin',
|
||||
'DuplicateArrayKeyPlugin',
|
||||
'PregRegexCheckerPlugin',
|
||||
'PrintfCheckerPlugin',
|
||||
],
|
||||
'suppress_issue_types' => [
|
||||
'PhanUnreferencedUseNormal',
|
||||
'PhanTypeObjectUnsetDeclaredProperty',
|
||||
'PhanTraitParentReference',
|
||||
'PhanTypeInvalidThrowsIsInterface',
|
||||
'PhanRequiredTraitNotAdded',
|
||||
'PhanDeprecatedFunction', // Uncomment this to see all the deprecated calls
|
||||
]
|
||||
];
|
||||
5153
.phan/internal_stubs/Redis.phan_php
Normal file
5153
.phan/internal_stubs/Redis.phan_php
Normal file
File diff suppressed because it is too large
Load Diff
460
.phan/internal_stubs/memcache.phan_php
Normal file
460
.phan/internal_stubs/memcache.phan_php
Normal file
@@ -0,0 +1,460 @@
|
||||
<?php
|
||||
|
||||
// Start of memcache v.3.0.8
|
||||
|
||||
class MemcachePool {
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Open memcached server connection
|
||||
* @link https://php.net/manual/en/memcache.connect.php
|
||||
* @param string $host <p>
|
||||
* Point to the host where memcached is listening for connections. This parameter
|
||||
* may also specify other transports like <em>unix:///path/to/memcached.sock</em>
|
||||
* to use UNIX domain sockets, in this case <b>port</b> must also
|
||||
* be set to <em>0</em>.
|
||||
* </p>
|
||||
* @param int $port [optional] <p>
|
||||
* Point to the port where memcached is listening for connections. Set this
|
||||
* parameter to <em>0</em> when using UNIX domain sockets.
|
||||
* </p>
|
||||
* <p>
|
||||
* Please note: <b>port</b> defaults to
|
||||
* {@link https://php.net/manual/ru/memcache.ini.php#ini.memcache.default-port memcache.default_port}
|
||||
* if not specified. For this reason it is wise to specify the port
|
||||
* explicitly in this method call.
|
||||
* </p>
|
||||
* @param int $timeout [optional] <p>Value in seconds which will be used for connecting to the daemon. Think twice before changing the default value of 1 second - you can lose all the advantages of caching if your connection is too slow.</p>
|
||||
* @return bool <p>Returns <b>TRUE</b> on success or <b>FALSE</b> on failure.</p>
|
||||
*/
|
||||
public function connect ($host, $port, $timeout = 1) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 2.0.0)<br/>
|
||||
* Add a memcached server to connection pool
|
||||
* @link https://php.net/manual/en/memcache.addserver.php
|
||||
* @param string $host <p>
|
||||
* Point to the host where memcached is listening for connections. This parameter
|
||||
* may also specify other transports like unix:///path/to/memcached.sock
|
||||
* to use UNIX domain sockets, in this case <i>port</i> must also
|
||||
* be set to 0.
|
||||
* </p>
|
||||
* @param int $port [optional] <p>
|
||||
* Point to the port where memcached is listening for connections.
|
||||
* Set this
|
||||
* parameter to 0 when using UNIX domain sockets.
|
||||
* </p>
|
||||
* <p>
|
||||
* Please note: <i>port</i> defaults to
|
||||
* memcache.default_port
|
||||
* if not specified. For this reason it is wise to specify the port
|
||||
* explicitly in this method call.
|
||||
* </p>
|
||||
* @param bool $persistent [optional] <p>
|
||||
* Controls the use of a persistent connection. Default to <b>TRUE</b>.
|
||||
* </p>
|
||||
* @param int $weight [optional] <p>
|
||||
* Number of buckets to create for this server which in turn control its
|
||||
* probability of it being selected. The probability is relative to the
|
||||
* total weight of all servers.
|
||||
* </p>
|
||||
* @param int $timeout [optional] <p>
|
||||
* Value in seconds which will be used for connecting to the daemon. Think
|
||||
* twice before changing the default value of 1 second - you can lose all
|
||||
* the advantages of caching if your connection is too slow.
|
||||
* </p>
|
||||
* @param int $retry_interval [optional] <p>
|
||||
* Controls how often a failed server will be retried, the default value
|
||||
* is 15 seconds. Setting this parameter to -1 disables automatic retry.
|
||||
* Neither this nor the <i>persistent</i> parameter has any
|
||||
* effect when the extension is loaded dynamically via <b>dl</b>.
|
||||
* </p>
|
||||
* <p>
|
||||
* Each failed connection struct has its own timeout and before it has expired
|
||||
* the struct will be skipped when selecting backends to serve a request. Once
|
||||
* expired the connection will be successfully reconnected or marked as failed
|
||||
* for another <i>retry_interval</i> seconds. The typical
|
||||
* effect is that each web server child will retry the connection about every
|
||||
* <i>retry_interval</i> seconds when serving a page.
|
||||
* </p>
|
||||
* @param bool $status [optional] <p>
|
||||
* Controls if the server should be flagged as online. Setting this parameter
|
||||
* to <b>FALSE</b> and <i>retry_interval</i> to -1 allows a failed
|
||||
* server to be kept in the pool so as not to affect the key distribution
|
||||
* algorithm. Requests for this server will then failover or fail immediately
|
||||
* depending on the <i>memcache.allow_failover</i> setting.
|
||||
* Default to <b>TRUE</b>, meaning the server should be considered online.
|
||||
* </p>
|
||||
* @param callable $failure_callback [optional] <p>
|
||||
* Allows the user to specify a callback function to run upon encountering an
|
||||
* error. The callback is run before failover is attempted. The function takes
|
||||
* two parameters, the hostname and port of the failed server.
|
||||
* </p>
|
||||
* @param int $timeoutms [optional] <p>
|
||||
* </p>
|
||||
* @return bool <b>TRUE</b> on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function addServer ($host, $port = 11211, $persistent = true, $weight = null, $timeout = 1, $retry_interval = 15, $status = true, callable $failure_callback = null, $timeoutms = null) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 2.1.0)<br/>
|
||||
* Changes server parameters and status at runtime
|
||||
* @link https://secure.php.net/manual/en/memcache.setserverparams.php
|
||||
* @param string $host <p>Point to the host where memcached is listening for connections.</p.
|
||||
* @param int $port [optional] <p>
|
||||
* Point to the port where memcached is listening for connections.
|
||||
* </p>
|
||||
* @param int $timeout [optional] <p>
|
||||
* Value in seconds which will be used for connecting to the daemon. Think twice before changing the default value of 1 second - you can lose all the advantages of caching if your connection is too slow.
|
||||
* </p>
|
||||
* @param int $retry_interval [optional] <p>
|
||||
* Controls how often a failed server will be retried, the default value
|
||||
* is 15 seconds. Setting this parameter to -1 disables automatic retry.
|
||||
* Neither this nor the <b>persistent</b> parameter has any
|
||||
* effect when the extension is loaded dynamically via {@link https://secure.php.net/manual/en/function.dl.php dl()}.
|
||||
* </p>
|
||||
* @param bool $status [optional] <p>
|
||||
* Controls if the server should be flagged as online. Setting this parameter
|
||||
* to <b>FALSE</b> and <b>retry_interval</b> to -1 allows a failed
|
||||
* server to be kept in the pool so as not to affect the key distribution
|
||||
* algorithm. Requests for this server will then failover or fail immediately
|
||||
* depending on the <b>memcache.allow_failover</b> setting.
|
||||
* Default to <b>TRUE</b>, meaning the server should be considered online.
|
||||
* </p>
|
||||
* @param callable $failure_callback [optional] <p>
|
||||
* Allows the user to specify a callback function to run upon encountering an error. The callback is run before failover is attempted.
|
||||
* The function takes two parameters, the hostname and port of the failed server.
|
||||
* </p>
|
||||
* @return bool <p>Returns <b>TRUE</b> on success or <b>FALSE</b> on failure.</p>
|
||||
*/
|
||||
public function setServerParams ($host, $port = 11211, $timeout = 1, $retry_interval = 15, $status = true, callable $failure_callback = null) {}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setFailureCallback () {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 2.1.0)<br/>
|
||||
* Returns server status
|
||||
* @link https://php.net/manual/en/memcache.getserverstatus.php
|
||||
* @param string $host Point to the host where memcached is listening for connections.
|
||||
* @param int $port Point to the port where memcached is listening for connections.
|
||||
* @return int Returns a the servers status. 0 if server is failed, non-zero otherwise
|
||||
*/
|
||||
public function getServerStatus ($host, $port = 11211) {}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function findServer () {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Return version of the server
|
||||
* @link https://php.net/manual/en/memcache.getversion.php
|
||||
* @return string|false Returns a string of server version number or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function getVersion () {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 2.0.0)<br/>
|
||||
* Add an item to the server. If the key already exists, the value will not be added and <b>FALSE</b> will be returned.
|
||||
* @link https://php.net/manual/en/memcache.add.php
|
||||
* @param string $key The key that will be associated with the item.
|
||||
* @param mixed $var The variable to store. Strings and integers are stored as is, other types are stored serialized.
|
||||
* @param int $flag [optional] <p>
|
||||
* Use <b>MEMCACHE_COMPRESSED</b> to store the item
|
||||
* compressed (uses zlib).
|
||||
* </p>
|
||||
* @param int $expire [optional] <p>Expiration time of the item.
|
||||
* If it's equal to zero, the item will never expire.
|
||||
* You can also use Unix timestamp or a number of seconds starting from current time, but in the latter case the number of seconds may not exceed 2592000 (30 days).</p>
|
||||
* @return bool Returns <b>TRUE</b> on success or <b>FALSE</b> on failure. Returns <b>FALSE</b> if such key already exist. For the rest Memcache::add() behaves similarly to Memcache::set().
|
||||
*/
|
||||
public function add ($key , $var, $flag = null, $expire = null) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Stores an item var with key on the memcached server. Parameter expire is expiration time in seconds.
|
||||
* If it's 0, the item never expires (but memcached server doesn't guarantee this item to be stored all the time,
|
||||
* it could be deleted from the cache to make place for other items).
|
||||
* You can use MEMCACHE_COMPRESSED constant as flag value if you want to use on-the-fly compression (uses zlib).
|
||||
* @link https://php.net/manual/en/memcache.set.php
|
||||
* @param string $key The key that will be associated with the item.
|
||||
* @param mixed $var The variable to store. Strings and integers are stored as is, other types are stored serialized.
|
||||
* @param int $flag [optional] Use MEMCACHE_COMPRESSED to store the item compressed (uses zlib).
|
||||
* @param int $expire [optional] Expiration time of the item. If it's equal to zero, the item will never expire. You can also use Unix timestamp or a number of seconds starting from current time, but in the latter case the number of seconds may not exceed 2592000 (30 days).
|
||||
* @return bool Returns <b>TRUE</b> on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function set ($key, $var, $flag = null, $expire = null) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Replace value of the existing item
|
||||
* @link https://php.net/manual/en/memcache.replace.php
|
||||
* @param string $key <p>The key that will be associated with the item.</p>
|
||||
* @param mixed $var <p>The variable to store. Strings and integers are stored as is, other types are stored serialized.</p>
|
||||
* @param int $flag [optional] <p>Use <b>MEMCACHE_COMPRESSED</b> to store the item compressed (uses zlib).</p>
|
||||
* @param int $expire [optional] <p>Expiration time of the item. If it's equal to zero, the item will never expire. You can also use Unix timestamp or a number of seconds starting from current time, but in the latter case the number of seconds may not exceed 2592000 (30 days).</p>
|
||||
* @return bool Returns TRUE on success or FALSE on failure.
|
||||
*/
|
||||
public function replace ($key, $var, $flag = null, $expire = null) {}
|
||||
|
||||
public function cas () {}
|
||||
|
||||
public function append () {}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function prepend () {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Retrieve item from the server
|
||||
* @link https://php.net/manual/en/memcache.get.php
|
||||
* @param string|array $key <p>
|
||||
* The key or array of keys to fetch.
|
||||
* </p>
|
||||
* @param int|array $flags [optional] <p>
|
||||
* If present, flags fetched along with the values will be written to this parameter. These
|
||||
* flags are the same as the ones given to for example {@link https://php.net/manual/en/memcache.set.php Memcache::set()}.
|
||||
* The lowest byte of the int is reserved for pecl/memcache internal usage (e.g. to indicate
|
||||
* compression and serialization status).
|
||||
* </p>
|
||||
* @return string|array|false <p>
|
||||
* Returns the string associated with the <b>key</b> or
|
||||
* an array of found key-value pairs when <b>key</b> is an {@link https://php.net/manual/en/language.types.array.php array}.
|
||||
* Returns <b>FALSE</b> on failure, <b>key</b> is not found or
|
||||
* <b>key</b> is an empty {@link https://php.net/manual/en/language.types.array.php array}.
|
||||
* </p>
|
||||
*/
|
||||
public function get ($key, &$flags = null) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Delete item from the server
|
||||
* https://secure.php.net/manual/ru/memcache.delete.php
|
||||
* @param $key string The key associated with the item to delete.
|
||||
* @param $timeout int [optional] This deprecated parameter is not supported, and defaults to 0 seconds. Do not use this parameter.
|
||||
* @return bool Returns <b>TRUE</b> on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function delete ($key, $timeout = 0 ) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Get statistics of the server
|
||||
* @link https://php.net/manual/ru/memcache.getstats.php
|
||||
* @param string $type [optional] <p>
|
||||
* The type of statistics to fetch.
|
||||
* Valid values are {reset, malloc, maps, cachedump, slabs, items, sizes}.
|
||||
* According to the memcached protocol spec these additional arguments "are subject to change for the convenience of memcache developers".</p>
|
||||
* @param int $slabid [optional] <p>
|
||||
* Used in conjunction with <b>type</b> set to
|
||||
* cachedump to identify the slab to dump from. The cachedump
|
||||
* command ties up the server and is strictly to be used for
|
||||
* debugging purposes.
|
||||
* </p>
|
||||
* @param int $limit [optional] <p>
|
||||
* Used in conjunction with <b>type</b> set to cachedump to limit the number of entries to dump.
|
||||
* </p>
|
||||
* @return array|false Returns an associative array of server statistics or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function getStats ($type = null, $slabid = null, $limit = 100) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 2.0.0)<br/>
|
||||
* Get statistics from all servers in pool
|
||||
* @link https://php.net/manual/en/memcache.getextendedstats.php
|
||||
* @param string $type [optional] <p>The type of statistics to fetch. Valid values are {reset, malloc, maps, cachedump, slabs, items, sizes}. According to the memcached protocol spec these additional arguments "are subject to change for the convenience of memcache developers".</p>
|
||||
* @param int $slabid [optional] <p>
|
||||
* Used in conjunction with <b>type</b> set to
|
||||
* cachedump to identify the slab to dump from. The cachedump
|
||||
* command ties up the server and is strictly to be used for
|
||||
* debugging purposes.
|
||||
* </p>
|
||||
* @param int $limit Used in conjunction with type set to cachedump to limit the number of entries to dump.
|
||||
* @return array|false Returns a two-dimensional associative array of server statistics or <b>FALSE</b>
|
||||
* Returns a two-dimensional associative array of server statistics or <b>FALSE</b>
|
||||
* on failure.
|
||||
*/
|
||||
public function getExtendedStats ($type = null, $slabid = null, $limit = 100) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 2.0.0)<br/>
|
||||
* Enable automatic compression of large values
|
||||
* @link https://php.net/manual/en/memcache.setcompressthreshold.php
|
||||
* @param int $thresold <p>Controls the minimum value length before attempting to compress automatically.</p>
|
||||
* @param float $min_saving [optional] <p>Specifies the minimum amount of savings to actually store the value compressed. The supplied value must be between 0 and 1. Default value is 0.2 giving a minimum 20% compression savings.</p>
|
||||
* @return bool Returns <b>TRUE</b> on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function setCompressThreshold ($thresold, $min_saving = 0.2) {}
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Increment item's value
|
||||
* @link https://php.net/manual/en/memcache.increment.php
|
||||
* @param $key string Key of the item to increment.
|
||||
* @param $value int [optional] increment the item by <b>value</b>
|
||||
* @return int|false Returns new items value on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function increment ($key, $value = 1) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Decrement item's value
|
||||
* @link https://php.net/manual/en/memcache.decrement.php
|
||||
* @param $key string Key of the item do decrement.
|
||||
* @param $value int Decrement the item by <b>value</b>.
|
||||
* @return int|false Returns item's new value on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function decrement ($key, $value = 1) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.4.0)<br/>
|
||||
* Close memcached server connection
|
||||
* @link https://php.net/manual/en/memcache.close.php
|
||||
* @return bool Returns <b>TRUE</b> on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function close () {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 1.0.0)<br/>
|
||||
* Flush all existing items at the server
|
||||
* @link https://php.net/manual/en/memcache.flush.php
|
||||
* @return bool Returns <b>TRUE</b> on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function flush () {}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a connection to a set of memcache servers.
|
||||
* @link https://php.net/manual/en/class.memcache.php
|
||||
*/
|
||||
class Memcache extends MemcachePool {
|
||||
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.4.0)<br/>
|
||||
* Open memcached server persistent connection
|
||||
* @link https://php.net/manual/en/memcache.pconnect.php
|
||||
* @param string $host <p>
|
||||
* Point to the host where memcached is listening for connections. This parameter
|
||||
* may also specify other transports like unix:///path/to/memcached.sock
|
||||
* to use UNIX domain sockets, in this case <i>port</i> must also
|
||||
* be set to 0.
|
||||
* </p>
|
||||
* @param int $port [optional] <p>
|
||||
* Point to the port where memcached is listening for connections. Set this
|
||||
* parameter to 0 when using UNIX domain sockets.
|
||||
* </p>
|
||||
* @param int $timeout [optional] <p>
|
||||
* Value in seconds which will be used for connecting to the daemon. Think
|
||||
* twice before changing the default value of 1 second - you can lose all
|
||||
* the advantages of caching if your connection is too slow.
|
||||
* </p>
|
||||
* @return mixed a Memcache object or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function pconnect ($host, $port, $timeout = 1) {}
|
||||
}
|
||||
|
||||
// string $host [, int $port [, int $timeout ]]
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)</br>
|
||||
* Memcache::connect — Open memcached server connection
|
||||
* @link https://php.net/manual/en/memcache.connect.php
|
||||
* @param string $host <p>
|
||||
* Point to the host where memcached is listening for connections.
|
||||
* This parameter may also specify other transports like
|
||||
* unix:///path/to/memcached.sock to use UNIX domain sockets,
|
||||
* in this case port must also be set to 0.
|
||||
* </p>
|
||||
* @param int $port [optional] <p>
|
||||
* Point to the port where memcached is listening for connections.
|
||||
* Set this parameter to 0 when using UNIX domain sockets.
|
||||
* Note: port defaults to memcache.default_port if not specified.
|
||||
* For this reason it is wise to specify the port explicitly in this method call.
|
||||
* </p>
|
||||
* @param int $timeout [optional] <p>
|
||||
* Value in seconds which will be used for connecting to the daemon.
|
||||
* </p>
|
||||
* @return bool Returns <b>TRUE</b> on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
function memcache_connect ($host, $port, $timeout = 1) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.4.0)
|
||||
* Memcache::pconnect — Open memcached server persistent connection
|
||||
*
|
||||
* @link https://php.net/manual/en/memcache.pconnect.php#example-5242
|
||||
* @param $host
|
||||
* @param null $port
|
||||
* @param int $timeout
|
||||
* @return Memcache
|
||||
*/
|
||||
function memcache_pconnect ($host, $port=null, $timeout=1) {}
|
||||
|
||||
function memcache_add_server () {}
|
||||
|
||||
function memcache_set_server_params () {}
|
||||
|
||||
function memcache_set_failure_callback () {}
|
||||
|
||||
function memcache_get_server_status () {}
|
||||
|
||||
function memcache_get_version () {}
|
||||
|
||||
function memcache_add () {}
|
||||
|
||||
function memcache_set () {}
|
||||
|
||||
function memcache_replace () {}
|
||||
|
||||
function memcache_cas () {}
|
||||
|
||||
function memcache_append () {}
|
||||
|
||||
function memcache_prepend () {}
|
||||
|
||||
function memcache_get () {}
|
||||
|
||||
function memcache_delete () {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Turn debug output on/off
|
||||
* @link https://php.net/manual/en/function.memcache-debug.php
|
||||
* @param bool $on_off <p>
|
||||
* Turns debug output on if equals to <b>TRUE</b>.
|
||||
* Turns debug output off if equals to <b>FALSE</b>.
|
||||
* </p>
|
||||
* @return bool <b>TRUE</b> if PHP was built with --enable-debug option, otherwise
|
||||
* returns <b>FALSE</b>.
|
||||
*/
|
||||
function memcache_debug ($on_off) {}
|
||||
|
||||
function memcache_get_stats () {}
|
||||
|
||||
function memcache_get_extended_stats () {}
|
||||
|
||||
function memcache_set_compress_threshold () {}
|
||||
|
||||
function memcache_increment () {}
|
||||
|
||||
function memcache_decrement () {}
|
||||
|
||||
function memcache_close () {}
|
||||
|
||||
function memcache_flush () {}
|
||||
|
||||
define ('MEMCACHE_COMPRESSED', 2);
|
||||
define ('MEMCACHE_USER1', 65536);
|
||||
define ('MEMCACHE_USER2', 131072);
|
||||
define ('MEMCACHE_USER3', 262144);
|
||||
define ('MEMCACHE_USER4', 524288);
|
||||
define ('MEMCACHE_HAVE_SESSION', 1);
|
||||
|
||||
// End of memcache v.3.0.8
|
||||
?>
|
||||
1308
.phan/internal_stubs/memcached.phan_php
Normal file
1308
.phan/internal_stubs/memcached.phan_php
Normal file
File diff suppressed because it is too large
Load Diff
214
CHANGELOG.md
214
CHANGELOG.md
@@ -1,11 +1,221 @@
|
||||
# v1.7.0-rc.1
|
||||
## 11/06/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added `Flex Pages` to Grav core and removed Flex Objects plugin dependency
|
||||
* Added `Utils::simpleTemplate()` method for very simple variable templating
|
||||
* Added `array_diff()` twig function
|
||||
* Added `template_from_string()` twig function
|
||||
* Updated Symfony Components to 4.3
|
||||
1. [](#improved)
|
||||
* Improved `Scheduler` cron command check and more useful CLI information
|
||||
* Improved `Flex Users`: obey blueprints and allow Flex to be used in admin only
|
||||
* Improved `Flex` to support custom site template paths
|
||||
* Changed Twig `{% cache %}` tag to not need unique key, and `lifetime` is now optional
|
||||
* Added mime support for file formatters
|
||||
* Updated built-in `composer.phar` to latest `1.9.0`
|
||||
* Updated vendor libraries
|
||||
* Use `Symfony EventDispatcher` directly and not rockettheme/toolbox wrapper
|
||||
1. [](#bugfix)
|
||||
* Fixed exception caused by missing template type based on `Accept:` header [#2705](https://github.com/getgrav/grav/issues/2705)
|
||||
* Fixed `Page::untranslatedLanguages()` not being symmetrical to `Page::translatedLanguages()`
|
||||
* Fixed `Flex Pages` not calling `onPageProcessed` event when cached
|
||||
* Fixed phpstan issues in Framework up to level 7
|
||||
* Fixed issue with duplicate configuration settings in Flex Directory
|
||||
* Fixed fatal error if there are numeric folders in `Flex Pages`
|
||||
* Fixed error on missing `Flex` templates in if `Flex Objects` plugin isn't installed
|
||||
* Fixed `PageTranslateTrait::getAllLanguages()` and `getAllLanguages()` to include default language
|
||||
* Fixed multi-language saving issues with default language in `Flex Pages`
|
||||
* Selfupgrade CLI: Fixed broken selfupgrade assets reference [#2681](https://github.com/getgrav/grav/issues/2681)
|
||||
* Grav 1.7: Fixed PHP 7.1 compatibility issues
|
||||
* Grav 1.7: Fixed fatal error in multi-site setups
|
||||
* Grav 1.7: Fixed `Flex Pages` routing if using translated slugs or `system.hide_in_urls` setting
|
||||
* Grav 1.7: Fixed bug where Flex index file couldn't be disabled
|
||||
|
||||
# v1.7.0-beta.10
|
||||
## 10/03/2019
|
||||
|
||||
1. [](#improved)
|
||||
* Flex: Removed extra exists check when creating object (messes up "non-existing" pages)
|
||||
* Support customizable null character replacement in `CSVFormatter::decode()`
|
||||
1. [](#bugfix)
|
||||
* Fixed wrong Grav param separator when using `Route` class
|
||||
* Fixed Flex User Avatar not fully backwards compatible with old user
|
||||
* Grav 1.7: Fixed prev/next page missing pages if pagination was turned on in page header
|
||||
* Grav 1.7: Reverted setting language for every page during initialization
|
||||
* Grav 1.7: Fixed numeric language inconsistencies
|
||||
|
||||
# v1.7.0-beta.9
|
||||
## 09/26/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added a new `{% cache %}` Twig tag eliminating need for `twigcache` extension.
|
||||
1. [](#improved)
|
||||
* Improved blueprint initialization in Flex Objects (fixes content aware fields)
|
||||
* Improved Flex FolderStorage class to better hide storage specific logic
|
||||
* Exception will output a badly formatted line in `CsvFormatter::decode()`
|
||||
1. [](#bugfix)
|
||||
* Fixed error when activating Flex Accounts in GRAV system configuration (PHP 7.1)
|
||||
* Fixed Grav parameter handling in `RouteFactory::createFromString()`
|
||||
|
||||
# v1.7.0-beta.8
|
||||
## 09/19/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added new `Security::sanitizeSVG()` function
|
||||
* Backwards compatibility break: `FlexStorageInterface::getStoragePath()` and `getMediaPath()` can now return null
|
||||
1. [](#improved)
|
||||
* Several FlexObject loading improvements
|
||||
* Added `bin/grav page-system-validator [-r|--record] [-c|--check]` to test Flex Pages
|
||||
* Improved language support for `Route` class
|
||||
1. [](#bugfix)
|
||||
* Regression: Fixed language fallback
|
||||
* Regression: Fixed translations when language code is used for non-language purposes
|
||||
* Regression: Allow SVG avatar images for users
|
||||
* Fixed error in `Session::getFlashObject()` if Flex Form is being used
|
||||
* Fixed broken Twig `dump()`
|
||||
* Fixed `Page::modular()` and `Page::modularTwig()` returning `null` for folders and other non-initialized pages
|
||||
* Fixed 404 error when you click to non-routable menu item with children: redirect to the first child instead
|
||||
* Fixed wrong `Pages::dispatch()` calls (with redirect) when we really meant to call `Pages::find()`
|
||||
* Fixed avatars not being displayed with flex users [#2431](https://github.com/getgrav/grav/issues/2431)
|
||||
* Fixed initial Flex Object state when creating a new objects in a form
|
||||
|
||||
# v1.7.0-beta.7
|
||||
## 08/30/2019
|
||||
|
||||
1. [](#improved)
|
||||
* Improved language support
|
||||
1. [](#bugfix)
|
||||
* `FlexForm`: Fixed some compatibility issues with Form plugin
|
||||
|
||||
# v1.7.0-beta.6
|
||||
## 08/29/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added experimental support for `Flex Pages` (**Flex Objects** plugin required)
|
||||
1. [](#improved)
|
||||
* Improved `bin/grav yamllinter` CLI command by adding an option to find YAML Linting issues from the whole site or custom folder
|
||||
* Added support for not instantiating pages, useful to speed up tasks
|
||||
* Greatly improved speed of loading Flex collections
|
||||
1. [](#bugfix)
|
||||
* Fixed `$page->summary()` always striping HTML tags if the summary was set by `$page->setSummary()`
|
||||
* Fixed `Flex->getObject()` when using Flex Key
|
||||
* Grav 1.7: Fixed enabling PHP Debug Bar causes fatal error in Gantry [#2634](https://github.com/getgrav/grav/issues/2634)
|
||||
* Grav 1.7: Fixed broken taxonomies [#2633](https://github.com/getgrav/grav/issues/2633)
|
||||
* Grav 1.7: Fixed unpublished blog posts being displayed on the front-end [#2650](https://github.com/getgrav/grav/issues/2650)
|
||||
|
||||
# v1.7.0-beta.5
|
||||
## 08/11/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added a new `bin/grav server` CLI command to easily run Symfony or PHP built-in webservers
|
||||
* Added `hasFlexFeature()` method to test if `FlexObject` or `FlexCollection` implements a given feature
|
||||
* Added `getFlexFeatures()` method to return all features that `FlexObject` or `FlexCollection` implements
|
||||
* Deprecated `FlexDirectory::update()` and `FlexDirectory::remove()`
|
||||
* Added `FlexStorage::getMetaData()` to get updated object meta information for listed keys
|
||||
* Added `Language::getPageExtensions()` to get full list of supported page language extensions
|
||||
* Added `$grav->close()` method to properly terminate the request with a response
|
||||
* Added `Pages::getCollection()` method
|
||||
1. [](#improved)
|
||||
* Better support for Symfony local server `symfony server:start`
|
||||
* Make `Route` objects immutable
|
||||
* `FlexDirectory::getObject()` can now be called without any parameters to create a new object
|
||||
* Flex objects no longer return temporary key if they do not have one; empty key is returned instead
|
||||
* Updated vendor libraries
|
||||
* Moved `collection()` and `evaluate()` logic from `Page` class into `Pages` class
|
||||
1. [](#bugfix)
|
||||
* Fixed `Form` not to use deleted flash object until the end of the request fixing issues with reset
|
||||
* Fixed `FlexForm` to allow multiple form instances with non-existing objects
|
||||
* Fixed `FlexObject` search by using `key`
|
||||
* Grav 1.7: Fixed clockwork messages with arrays and objects
|
||||
|
||||
# v1.7.0-beta.4
|
||||
## 07/01/2019
|
||||
|
||||
1. [](#new)
|
||||
* Updated with Grav 1.6.12 features, improvements & fixes
|
||||
* Added new configuration option `system.debugger.censored` to hide potentially sensitive information
|
||||
* Added new configuration option `system.languages.include_default_lang_file_extension` to keep default language in `.md` files if set to `false`
|
||||
* Added configuration option to set fallback content languages individually for every language
|
||||
1. [](#improved)
|
||||
* Updated Vendor libraries
|
||||
1. [](#bugfix)
|
||||
* Fixed `.md` page to be assigned to the default language and to be listed in translated/untranslated page list
|
||||
* Fixed `Language::getFallbackPageExtensions()` to fall back only to default language instead of going through all languages
|
||||
* Fixed `Language::getFallbackPageExtensions()` returning wrong file extensions when passing custom page extension
|
||||
|
||||
# v1.7.0-beta.3
|
||||
## 06/24/2019
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed Clockwork on Windows machines
|
||||
* Fixed parent field issues on Windows machines
|
||||
* Fixed unreliable Clockwork calls in sub-folders
|
||||
|
||||
# v1.7.0-beta.2
|
||||
## 06/21/2019
|
||||
|
||||
1. [](#new)
|
||||
* Updated with Grav 1.6.11 fixes
|
||||
1. [](#improved)
|
||||
* Updated the Clockwork text
|
||||
|
||||
# v1.7.0-beta.1
|
||||
## 06/14/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added support for [Clockwork](https://underground.works/clockwork) developer tools (now default debugger)
|
||||
* Added support for [Tideways XHProf](https://github.com/tideways/php-xhprof-extension) PHP Extension for profiling method calls
|
||||
* Added Twig profiling for Clockwork debugger
|
||||
* Added support for Twig 2.11 (compatible with Twig 1.40+)
|
||||
* Optimization: Initialize debugbar only after the configuration has been loaded
|
||||
* Optimization: Combine some early Grav processors into a single one
|
||||
|
||||
# v1.6.17
|
||||
## 11/06/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added working ETag (304 Not Modified) support based on the final rendered HTML
|
||||
1. [](#improved)
|
||||
* Safer file handling + customizable null char replacement in `CsvFormatter::decode()`
|
||||
* Change of Behavior: `Inflector::hyphenize` will now automatically trim dashes at beginning and end of a string.
|
||||
* Change in Behavior for `Folder::all()` so no longer fails if trying to copy non-existent dot file [#2581](https://github.com/getgrav/grav/pull/2581)
|
||||
* renamed composer `test-plugins` script to `phpstan-plugins` to be more explicit [#2637](https://github.com/getgrav/grav/pull/2637)
|
||||
1. [](#bugfix)
|
||||
* Fixed PHP 7.1 bug in FlexMedia
|
||||
* Fix cache image generation when using cropResize [#2639](https://github.com/getgrav/grav/pull/2639)
|
||||
* Fix `array_merge()` exception with non-array page header metadata [#2701](https://github.com/getgrav/grav/pull/2701)
|
||||
|
||||
# v1.6.16
|
||||
## 09/19/2019
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed Flex user creation if file storage is being used [#2444](https://github.com/getgrav/grav/issues/2444)
|
||||
* Fixed `Badly encoded JSON data` warning when uploading files [#2663](https://github.com/getgrav/grav/issues/2663)
|
||||
|
||||
# v1.6.15
|
||||
## 08/20/2019
|
||||
|
||||
1. [](#improved)
|
||||
* Improved robots.txt [#2632](https://github.com/getgrav/grav/issues/2632)
|
||||
1. [](#bugfix)
|
||||
* Fixed broken markdown Twig tag [#2635](https://github.com/getgrav/grav/issues/2635)
|
||||
* Force Symfony 4.2 in Grav 1.6 to remove a bunch of deprecated messages
|
||||
|
||||
# v1.6.14
|
||||
## 08/18/2019
|
||||
|
||||
1. [](#bugfix)
|
||||
* Actually include fix for `system\router.php` [#2627](https://github.com/getgrav/grav/issues/2627)
|
||||
|
||||
# v1.6.13
|
||||
## 08/12/2019
|
||||
## 08/16/2019
|
||||
|
||||
1. [](#bugfix)
|
||||
* Regression fix for `system\router.php` [#2627](https://github.com/getgrav/grav/issues/2627)
|
||||
|
||||
# v1.6.12
|
||||
## 08/11/2019
|
||||
## 08/14/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added support for custom `FormFlash` save locations
|
||||
|
||||
Binary file not shown.
26
bin/grav
26
bin/grav
@@ -3,7 +3,7 @@
|
||||
|
||||
use Grav\Common\Composer;
|
||||
use Grav\Common\Grav;
|
||||
use League\CLImate\CLImate;
|
||||
use Grav\Console\Cli;
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
\define('GRAV_CLI', true);
|
||||
@@ -52,16 +52,18 @@ if (!file_exists(GRAV_ROOT . '/index.php')) {
|
||||
|
||||
$app = new Application('Grav CLI Application', GRAV_VERSION);
|
||||
$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(),
|
||||
new \Grav\Console\Cli\BackupCommand(),
|
||||
new \Grav\Console\Cli\NewProjectCommand(),
|
||||
new \Grav\Console\Cli\SchedulerCommand(),
|
||||
new \Grav\Console\Cli\SecurityCommand(),
|
||||
new \Grav\Console\Cli\LogViewerCommand(),
|
||||
new \Grav\Console\Cli\YamlLinterCommand(),
|
||||
new Cli\InstallCommand(),
|
||||
new Cli\ComposerCommand(),
|
||||
new Cli\SandboxCommand(),
|
||||
new Cli\CleanCommand(),
|
||||
new Cli\ClearCacheCommand(),
|
||||
new Cli\BackupCommand(),
|
||||
new Cli\NewProjectCommand(),
|
||||
new Cli\SchedulerCommand(),
|
||||
new Cli\SecurityCommand(),
|
||||
new Cli\LogViewerCommand(),
|
||||
new Cli\YamlLinterCommand(),
|
||||
new Cli\ServerCommand(),
|
||||
new Cli\PageSystemValidatorCommand(),
|
||||
));
|
||||
$app->run();
|
||||
|
||||
@@ -2,7 +2,13 @@
|
||||
"name": "getgrav/grav",
|
||||
"type": "project",
|
||||
"description": "Modern, Crazy Fast, Ridiculously Easy and Amazingly Powerful Flat-File CMS",
|
||||
"keywords": ["cms","flat-file cms","flat cms","flatfile cms","php"],
|
||||
"keywords": [
|
||||
"cms",
|
||||
"flat-file cms",
|
||||
"flat cms",
|
||||
"flatfile cms",
|
||||
"php"
|
||||
],
|
||||
"homepage": "https://getgrav.org",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
@@ -16,41 +22,40 @@
|
||||
"symfony/polyfill-iconv": "^1.9",
|
||||
"symfony/polyfill-php72": "^1.9",
|
||||
"symfony/polyfill-php73": "^1.9",
|
||||
|
||||
"psr/simple-cache": "^1.0",
|
||||
"psr/http-message": "^1.0",
|
||||
"psr/http-server-middleware": "^1.0",
|
||||
|
||||
"kodus/psr7-server": "*",
|
||||
"nyholm/psr7": "^1.0",
|
||||
|
||||
"twig/twig": "~1.40",
|
||||
"twig/twig": "~1.0",
|
||||
"erusev/parsedown": "1.6.4",
|
||||
"erusev/parsedown-extra": "~0.7",
|
||||
"symfony/yaml": "~4.2",
|
||||
"symfony/console": "~4.2",
|
||||
"symfony/event-dispatcher": "~4.2",
|
||||
"symfony/var-dumper": "~4.2",
|
||||
"symfony/process": "~4.2",
|
||||
"symfony/contracts": "~1.0",
|
||||
"symfony/yaml": "~4.3.0",
|
||||
"symfony/console": "~4.3.0",
|
||||
"symfony/event-dispatcher": "~4.3.0",
|
||||
"symfony/var-dumper": "~4.3.0",
|
||||
"symfony/process": "~4.3.0",
|
||||
"doctrine/cache": "^1.8",
|
||||
"doctrine/collections": "^1.5",
|
||||
"guzzlehttp/psr7": "^1.4",
|
||||
"filp/whoops": "~2.2",
|
||||
|
||||
"matthiasmullie/minify": "^1.3",
|
||||
"monolog/monolog": "~1.0",
|
||||
"gregwar/image": "2.*",
|
||||
"donatj/phpuseragentparser": "~0.10",
|
||||
"pimple/pimple": "~3.2",
|
||||
"rockettheme/toolbox": "~1.4",
|
||||
"maximebf/debugbar": "~1.15",
|
||||
"maximebf/debugbar": "~1.0",
|
||||
"league/climate": "^3.4",
|
||||
"antoligy/dom-string-iterators": "^1.0",
|
||||
"miljar/php-exif": "^0.6.4",
|
||||
"composer/ca-bundle": "^1.0",
|
||||
"dragonmantank/cron-expression": "^1.2",
|
||||
"phive/twig-extensions-deferred": "^1.0",
|
||||
"willdurand/negotiation": "^2.3"
|
||||
"willdurand/negotiation": "^2.3",
|
||||
"itsgoingd/clockwork": "~4.0",
|
||||
"enshrined/svg-sanitize": "^0.10.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "^2.4",
|
||||
@@ -77,24 +82,32 @@
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/trilbymedia/PHP-Markdown-Documentation-Generator"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/itsgoingd/clockwork"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Grav\\": "system/src/Grav"
|
||||
},
|
||||
"files": ["system/defines.php"]
|
||||
"files": [
|
||||
"system/defines.php"
|
||||
]
|
||||
},
|
||||
"archive": {
|
||||
"exclude": ["VERSION"]
|
||||
"exclude": [
|
||||
"VERSION"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"api-16": "vendor/bin/phpdoc-md generate system/src > user/pages/14.api/default.16.md",
|
||||
"api-15": "vendor/bin/phpdoc-md generate system/src > user/pages/14.api/default.md",
|
||||
"post-create-project-cmd": "bin/grav install",
|
||||
"phpstan": "vendor/bin/phpstan analyse -l 2 -c ./tests/phpstan/phpstan.neon system/src --memory-limit=256M",
|
||||
"phpstan-framework": "vendor/bin/phpstan analyse -l 5 -c ./tests/phpstan/phpstan.neon system/src/Grav/Framework --memory-limit=256M",
|
||||
"test-plugins": "vendor/bin/phpstan analyse -l 0 -c ./tests/phpstan/plugins.neon user/plugins --memory-limit=256M",
|
||||
"phpstan": "vendor/bin/phpstan analyse -l 2 -c ./tests/phpstan/phpstan.neon system/src --memory-limit=300M",
|
||||
"phpstan-framework": "vendor/bin/phpstan analyse -l 7 -c ./tests/phpstan/phpstan.neon system/src/Grav/Framework --memory-limit=300M",
|
||||
"test-plugins": "vendor/bin/phpstan analyse -l 0 -c ./tests/phpstan/plugins.neon user/plugins --memory-limit=300M",
|
||||
"test": "vendor/bin/codecept run unit",
|
||||
"test-windows": "vendor\\bin\\codecept run unit"
|
||||
},
|
||||
|
||||
553
composer.lock
generated
553
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -16,8 +16,11 @@ if (version_compare($ver = PHP_VERSION, $req = GRAV_PHP_MIN, '<')) {
|
||||
die(sprintf('You are running PHP %s, but Grav needs at least <strong>PHP %s</strong> to run.', $ver, $req));
|
||||
}
|
||||
|
||||
if (PHP_SAPI === 'cli-server' && !isset($_SERVER['PHP_CLI_ROUTER'])) {
|
||||
die("PHP webserver requires a router to run Grav, please use: <pre>php -S {$_SERVER['SERVER_NAME']}:{$_SERVER['SERVER_PORT']} system/router.php</pre>");
|
||||
if (PHP_SAPI === 'cli-server') {
|
||||
$symfony_server = strpos(getenv('_'), 'symfony') !== false;
|
||||
if (!isset($_SERVER['PHP_CLI_ROUTER']) && !$symfony_server) {
|
||||
die("PHP webserver requires a router to run Grav, please use: <pre>php -S {$_SERVER['SERVER_NAME']}:{$_SERVER['SERVER_PORT']} system/router.php</pre>");
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure vendor libraries exist
|
||||
|
||||
@@ -10,3 +10,4 @@ Disallow: /user/
|
||||
Allow: /user/pages/
|
||||
Allow: /user/themes/
|
||||
Allow: /user/images/
|
||||
Allow: /
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
div.phpdebugbar {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.phpdebugbar pre {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header > div > * {
|
||||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header > div.phpdebugbar-header-right > * {
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header, .phpdebugbar a.phpdebugbar-restore-btn {
|
||||
background-image: url(grav.png);
|
||||
}
|
||||
|
||||
.phpdebugbar a.phpdebugbar-restore-btn {
|
||||
width: 13px;
|
||||
}
|
||||
|
||||
.phpdebugbar a.phpdebugbar-tab.phpdebugbar-active {
|
||||
background: #3DB9EC;
|
||||
color: #fff;
|
||||
margin-top: -1px;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.phpdebugbar .phpdebugbar-widgets-toolbar {
|
||||
border-top: 1px solid #ddd;
|
||||
padding-left: 5px;
|
||||
padding-right: 2px;
|
||||
padding-top: 2px;
|
||||
background-color: #fafafa !important;
|
||||
width: auto !important;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.phpdebugbar .phpdebugbar-widgets-toolbar input {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.phpdebugbar .phpdebugbar-widgets-toolbar .phpdebugbar-widgets-filter {
|
||||
|
||||
}
|
||||
|
||||
|
||||
.phpdebugbar input[type=text] {
|
||||
padding: 0;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.phpdebugbar dl.phpdebugbar-widgets-varlist, ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label {
|
||||
font-family: "DejaVu Sans Mono", Menlo, Monaco, Consolas, Courier, monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label {
|
||||
text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.phpdebugbar pre, .phpdebugbar code {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
2
system/assets/debugger/clockwork.css
Normal file
2
system/assets/debugger/clockwork.css
Normal file
@@ -0,0 +1,2 @@
|
||||
/** Clockwork Debugger CSS **/
|
||||
.clockwork-badge{position:fixed;z-index:10;bottom:0;left:0;padding:2px 4px;background-color:#eee;border:1px solid #ccc;border-bottom:0;border-left:0;display:flex;align-items:center}.clockwork-badge:hover{width:auto}.clockwork-badge:hover:after{content:'Grav Clockwork debugger enabled. Install Clockwork Browser extension (Chrome or Firefox), open your Developer tools and then select the Clockwork tab.'}.clockwork-badge:after{margin-left:10px;font-family:Monaco,Consolas,"Lucida Console",monospace;font-size:12px;line-height:1.5;color:#666}.clockwork-badge i{display:block;float:left;height:22px;width:22px;min-width:22px;background-size:contain;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAA/1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHh4AAAD///8EBAT7+/sLCwv29vYVFRUvLy/t7e3m5ubCwsKxsbE/Pz+mpqZMTEwcHBzy8vLp6emfn5+AgIA2Njbi4uLf39+rq6tzc3NWVlYhISHa2trW1tbS0tLMzMy7u7uZmZmUlJSMjIxvb29kZGRHR0c7Ozt5eXkqKiq1tbWQkJBqampbW1tSUlLHx8eHh4ckJCRDQ0M3wD42AAAAI3RSTlMA/PibTbQ0x76TVAlw4LhZLOuEYCAN9Hjx0a2ppGZEGYw97djhXHwAAATZSURBVFjDlVcHW+MwDO1eFCjj2McNOzvdpXTTXVbL/P+/5SQ7QSSX5Di1X1onfi/Sk+Q4sTDbKqWK+YuznZ2zi3wxVdqK/Zf92M1nT9gnO8rmd398GX6Z3xaoOFoiAQcx3E5efgmeSuN8F6Xg1x3G06l/wjNpMR1B0uif4EhnIuFb+0diIoFXk3IVfokisR+h52GO4JKgyjmfaMhAFNlSaPR7DpwI+lzn/E4QKIqmKIJirxCMP4izBPPZPXhgXwMBYgULw0nfg/BF5scDbslb7QeJ08yqqTEmGYoB95d4H8ETL8+n9wBqrLu6ao3bBsMwAnxISf/9BHcqxNB8Y7cWl3Zz7TAUfPrvAT6AoNEFFXvsjutL01yOuMrtBxnFXsmT/1wQHmdWAFNnI3uI48Yj0FUcHbKf62GfUfr8eeQt7Uk3mQZpZNoVRPEui5vtEz5zFEpgWnyqVBZMc6oaGNriH2hGVZ0OxEvInPeMaZWJBA7vmPbCr5jjws5HBnAUxvDMH40aCIf4G5BjRQSs8E8HFFYf8bGxgDvD55bzGhwWkoBcuIyHR/AMdaCagxXDhtL6tSqoWpd4BMnlIR+Or+rYTK/a3EAGcc6e4AWHISnWv20iCCojsHoVlQdjrMexFF2C7UMg2A2WEGWbQhXN6l3eXC6XGp4b9qxbuEB2EBGBwtocrK90cVG5mbRXm6vmx/0phq1sIAGKDgLOBiN1MrO5a9aDl+D0W6x0Ar9BCTRuIIANa90Y7LrLVRXzwVtDInCqMRWcf2bUOEAsa4wJqFowQALL9EiAtVRk8QC4OW+1pOM9jIaVASwYagyNXDj+W0NcfuZNzjtXOiL0Zzg30Llj+ptfxQs4+vBPNiL5PawFCBkgXpUaVtqGl+A8dgZHL34BcBUQrwPptToW+o37Ku+UH9eYByJIx3YkAeFnMFuGO7S5gEp7YhXxa5OOAM39RXDPXb0qmpROsswZe+twXdU55oUIZAiEv3bD1UFwIYKkmGqytPCDCwKFQCKK0yL7qtSAPX54UAbtsLuBHkb9zyLmPQSNjsSgmQwKUOIfEY8F8t4B34DvndJY9BA8tNBJq1Nev9axmaStFcQLhgYoCTo0salkIaW8OUDdWjMTR2sHPhrAFZqx6cqcKE4pl2BJJ4K6hfwvqNgAnXfKX/HU6X3Zrhnu0k7tLNZtTBRv1hkwTDBY1NzFU6doDYjJbWdQkQhWwuU7/LvhTh3SDoco4ECL4i5dwURbc8NdDZz2IwKicE8d0KIqWetLE3+lL4hvUuGSeRfVWNLfj/gpOw4smBJBkKQHCzlHGwvAj4woB1gq5NGGLSXtORBPnUQPV5/MPVkDMxbpwG7w4x0xL6Ltxka0A/4NBvV09UVk4DoSn/jl2+JQS9q9KYawisAD4CfhsZ4TH3htylsdEHARIQBusqCKyUpymycgbbkkXEXjT3z7/oKQFTFVuZD2FMJHZIDsO5x2d4aAr2jR+GLwZhtAb028/0yJ9J8dE87jQyKObcjtTXT8dH+fDuKF4/eiPwzH44wTf/yUi6wrpRIOZ9lM1EtXAifFI+CJn9+iX/t2xMQwOMth/UZbASi8btAwR9FHWSpJr75g9Oqbin3VDg+SpwlP6k6TB4ex/7JvmcJx8jydy6XPk8eFTKhyfwCgX71MSvaBHgAAAABJRU5ErkJggg==)}
|
||||
3
system/assets/debugger/clockwork.js
Normal file
3
system/assets/debugger/clockwork.js
Normal file
@@ -0,0 +1,3 @@
|
||||
/** Clockwork Debugger JS **/
|
||||
document.addEventListener("DOMContentLoaded",function () {
|
||||
var e=document.createElement("div");e.appendChild(document.createElement("i")),e.className="clockwork-badge",document.body.appendChild(e)});
|
||||
70
system/assets/debugger/phpdebugbar.css
Normal file
70
system/assets/debugger/phpdebugbar.css
Normal file
@@ -0,0 +1,70 @@
|
||||
div.phpdebugbar {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.phpdebugbar pre {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header > div > * {
|
||||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header > div.phpdebugbar-header-right > * {
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header, .phpdebugbar a.phpdebugbar-restore-btn {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAA/1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHh4AAAD///8EBAT7+/sLCwv29vYVFRUvLy/t7e3m5ubCwsKxsbE/Pz+mpqZMTEwcHBzy8vLp6emfn5+AgIA2Njbi4uLf39+rq6tzc3NWVlYhISHa2trW1tbS0tLMzMy7u7uZmZmUlJSMjIxvb29kZGRHR0c7Ozt5eXkqKiq1tbWQkJBqampbW1tSUlLHx8eHh4ckJCRDQ0M3wD42AAAAI3RSTlMA/PibTbQ0x76TVAlw4LhZLOuEYCAN9Hjx0a2ppGZEGYw97djhXHwAAATZSURBVFjDlVcHW+MwDO1eFCjj2McNOzvdpXTTXVbL/P+/5SQ7QSSX5Di1X1onfi/Sk+Q4sTDbKqWK+YuznZ2zi3wxVdqK/Zf92M1nT9gnO8rmd398GX6Z3xaoOFoiAQcx3E5efgmeSuN8F6Xg1x3G06l/wjNpMR1B0uif4EhnIuFb+0diIoFXk3IVfokisR+h52GO4JKgyjmfaMhAFNlSaPR7DpwI+lzn/E4QKIqmKIJirxCMP4izBPPZPXhgXwMBYgULw0nfg/BF5scDbslb7QeJ08yqqTEmGYoB95d4H8ETL8+n9wBqrLu6ao3bBsMwAnxISf/9BHcqxNB8Y7cWl3Zz7TAUfPrvAT6AoNEFFXvsjutL01yOuMrtBxnFXsmT/1wQHmdWAFNnI3uI48Yj0FUcHbKf62GfUfr8eeQt7Uk3mQZpZNoVRPEui5vtEz5zFEpgWnyqVBZMc6oaGNriH2hGVZ0OxEvInPeMaZWJBA7vmPbCr5jjws5HBnAUxvDMH40aCIf4G5BjRQSs8E8HFFYf8bGxgDvD55bzGhwWkoBcuIyHR/AMdaCagxXDhtL6tSqoWpd4BMnlIR+Or+rYTK/a3EAGcc6e4AWHISnWv20iCCojsHoVlQdjrMexFF2C7UMg2A2WEGWbQhXN6l3eXC6XGp4b9qxbuEB2EBGBwtocrK90cVG5mbRXm6vmx/0phq1sIAGKDgLOBiN1MrO5a9aDl+D0W6x0Ar9BCTRuIIANa90Y7LrLVRXzwVtDInCqMRWcf2bUOEAsa4wJqFowQALL9EiAtVRk8QC4OW+1pOM9jIaVASwYagyNXDj+W0NcfuZNzjtXOiL0Zzg30Llj+ptfxQs4+vBPNiL5PawFCBkgXpUaVtqGl+A8dgZHL34BcBUQrwPptToW+o37Ku+UH9eYByJIx3YkAeFnMFuGO7S5gEp7YhXxa5OOAM39RXDPXb0qmpROsswZe+twXdU55oUIZAiEv3bD1UFwIYKkmGqytPCDCwKFQCKK0yL7qtSAPX54UAbtsLuBHkb9zyLmPQSNjsSgmQwKUOIfEY8F8t4B34DvndJY9BA8tNBJq1Nev9axmaStFcQLhgYoCTo0salkIaW8OUDdWjMTR2sHPhrAFZqx6cqcKE4pl2BJJ4K6hfwvqNgAnXfKX/HU6X3Zrhnu0k7tLNZtTBRv1hkwTDBY1NzFU6doDYjJbWdQkQhWwuU7/LvhTh3SDoco4ECL4i5dwURbc8NdDZz2IwKicE8d0KIqWetLE3+lL4hvUuGSeRfVWNLfj/gpOw4smBJBkKQHCzlHGwvAj4woB1gq5NGGLSXtORBPnUQPV5/MPVkDMxbpwG7w4x0xL6Ltxka0A/4NBvV09UVk4DoSn/jl2+JQS9q9KYawisAD4CfhsZ4TH3htylsdEHARIQBusqCKyUpymycgbbkkXEXjT3z7/oKQFTFVuZD2FMJHZIDsO5x2d4aAr2jR+GLwZhtAb028/0yJ9J8dE87jQyKObcjtTXT8dH+fDuKF4/eiPwzH44wTf/yUi6wrpRIOZ9lM1EtXAifFI+CJn9+iX/t2xMQwOMth/UZbASi8btAwR9FHWSpJr75g9Oqbin3VDg+SpwlP6k6TB4ex/7JvmcJx8jydy6XPk8eFTKhyfwCgX71MSvaBHgAAAABJRU5ErkJggg==);
|
||||
}
|
||||
|
||||
.phpdebugbar a.phpdebugbar-restore-btn {
|
||||
width: 13px;
|
||||
}
|
||||
|
||||
.phpdebugbar a.phpdebugbar-tab.phpdebugbar-active {
|
||||
background: #3DB9EC;
|
||||
color: #fff;
|
||||
margin-top: -1px;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.phpdebugbar .phpdebugbar-widgets-toolbar {
|
||||
border-top: 1px solid #ddd;
|
||||
padding-left: 5px;
|
||||
padding-right: 2px;
|
||||
padding-top: 2px;
|
||||
background-color: #fafafa !important;
|
||||
width: auto !important;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.phpdebugbar .phpdebugbar-widgets-toolbar input {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.phpdebugbar .phpdebugbar-widgets-toolbar .phpdebugbar-widgets-filter {
|
||||
|
||||
}
|
||||
|
||||
|
||||
.phpdebugbar input[type=text] {
|
||||
padding: 0;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.phpdebugbar dl.phpdebugbar-widgets-varlist, ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label {
|
||||
font-family: "DejaVu Sans Mono", Menlo, Monaco, Consolas, Courier, monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label {
|
||||
text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.phpdebugbar pre, .phpdebugbar code {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 1.6 KiB |
4
system/assets/jquery/jquery-3.x.min.js
vendored
4
system/assets/jquery/jquery-3.x.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -105,3 +105,15 @@ form:
|
||||
validate:
|
||||
type: commalist
|
||||
|
||||
|
||||
sanitize_svg:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.SANITIZE_SVG
|
||||
help: PLUGIN_ADMIN.SANITIZE_SVG_HELP
|
||||
highlight: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
default: true
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
77
system/blueprints/flex/accounts.yaml
Normal file
77
system/blueprints/flex/accounts.yaml
Normal file
@@ -0,0 +1,77 @@
|
||||
title: Flex Accounts
|
||||
description: Manage your User Accounts in Flex.
|
||||
type: flex-objects
|
||||
|
||||
extends@:
|
||||
type: account
|
||||
context: blueprints://user
|
||||
|
||||
config:
|
||||
admin:
|
||||
menu:
|
||||
list:
|
||||
route: '/accounts'
|
||||
title: PLUGIN_ADMIN.ACCOUNTS
|
||||
icon: fa-users
|
||||
authorize: ['admin.users', 'admin.accounts', 'admin.super']
|
||||
priority: 6
|
||||
|
||||
template: grav-accounts
|
||||
|
||||
list:
|
||||
fields:
|
||||
username:
|
||||
link: edit
|
||||
search: true
|
||||
email:
|
||||
search: true
|
||||
fullname:
|
||||
search: true
|
||||
options:
|
||||
per_page: 20
|
||||
order:
|
||||
by: username
|
||||
dir: asc
|
||||
|
||||
site:
|
||||
hidden: true
|
||||
templates:
|
||||
collection:
|
||||
paths:
|
||||
- 'flex/{TYPE}/collection/{LAYOUT}{EXT}'
|
||||
object:
|
||||
paths:
|
||||
- 'flex/{TYPE}/object/{LAYOUT}{EXT}'
|
||||
defaults:
|
||||
type: accounts
|
||||
layout: default
|
||||
|
||||
data:
|
||||
object: 'Grav\Common\User\FlexUser\User'
|
||||
collection: 'Grav\Common\User\FlexUser\UserCollection'
|
||||
index: 'Grav\Common\User\FlexUser\UserIndex'
|
||||
storage:
|
||||
class: 'Grav\Common\User\FlexUser\Storage\UserFileStorage'
|
||||
options:
|
||||
formatter:
|
||||
class: 'Grav\Framework\File\Formatter\YamlFormatter'
|
||||
folder: 'account://'
|
||||
pattern: '{FOLDER}/{KEY}{EXT}'
|
||||
key: storage_key
|
||||
indexed: true
|
||||
search:
|
||||
options:
|
||||
contains: 1
|
||||
fields:
|
||||
- key
|
||||
- email
|
||||
|
||||
form:
|
||||
fields:
|
||||
username:
|
||||
flex-disabled@: exists
|
||||
disabled: false
|
||||
flex-readonly@: exists
|
||||
readonly: false
|
||||
validate:
|
||||
required: true
|
||||
139
system/blueprints/flex/pages.yaml
Normal file
139
system/blueprints/flex/pages.yaml
Normal file
@@ -0,0 +1,139 @@
|
||||
title: Flex Pages
|
||||
description: Manage your Grav Pages in Flex.
|
||||
type: flex-objects
|
||||
|
||||
extends@:
|
||||
type: default
|
||||
context: blueprints://pages
|
||||
|
||||
config:
|
||||
admin:
|
||||
menu:
|
||||
list:
|
||||
route: '/pages'
|
||||
title: PLUGIN_ADMIN.PAGES
|
||||
icon: fa-file-text
|
||||
authorize: ['admin.pages', 'admin.super']
|
||||
priority: 5
|
||||
|
||||
template: grav-pages
|
||||
|
||||
list:
|
||||
fields:
|
||||
published:
|
||||
width: 8
|
||||
alias: header.published
|
||||
visible:
|
||||
width: 8
|
||||
field:
|
||||
label: Visible
|
||||
type: toggle
|
||||
menu:
|
||||
link: edit
|
||||
alias: header.menu
|
||||
full_route:
|
||||
field:
|
||||
label: Route
|
||||
type: text
|
||||
link: edit
|
||||
sort:
|
||||
field: key
|
||||
name:
|
||||
width: 8
|
||||
field:
|
||||
label: Type
|
||||
type: text
|
||||
translations:
|
||||
width: 8
|
||||
field:
|
||||
label: Translations
|
||||
type: text
|
||||
# updated_date:
|
||||
# alias: header.update_date
|
||||
|
||||
options:
|
||||
per_page: 20
|
||||
order:
|
||||
by: key
|
||||
dir: asc
|
||||
|
||||
# TODO: not used yet
|
||||
buttons:
|
||||
back:
|
||||
icon: reply
|
||||
title: PLUGIN_ADMIN.BACK
|
||||
add:
|
||||
icon: plus
|
||||
label: PLUGIN_ADMIN.ADD
|
||||
|
||||
edit:
|
||||
# TODO: not used yet
|
||||
buttons:
|
||||
back:
|
||||
icon: reply
|
||||
title: PLUGIN_ADMIN.BACK
|
||||
preview:
|
||||
icon: eye
|
||||
title: PLUGIN_ADMIN.PREVIEW
|
||||
add:
|
||||
icon: plus
|
||||
label: PLUGIN_ADMIN.ADD
|
||||
copy:
|
||||
icon: copy
|
||||
label: PLUGIN_ADMIN.COPY
|
||||
move:
|
||||
icon: arrows
|
||||
label: PLUGIN_ADMIN.MOVE
|
||||
delete:
|
||||
icon: close
|
||||
label: PLUGIN_ADMIN.DELETE
|
||||
save:
|
||||
icon: check
|
||||
label: PLUGIN_ADMIN.SAVE
|
||||
|
||||
# Preview View
|
||||
preview:
|
||||
enabled: true
|
||||
|
||||
site:
|
||||
hidden: true
|
||||
templates:
|
||||
collection:
|
||||
paths:
|
||||
- 'flex/{TYPE}/collection/{LAYOUT}{EXT}'
|
||||
object:
|
||||
paths:
|
||||
- 'flex/{TYPE}/object/{LAYOUT}{EXT}'
|
||||
defaults:
|
||||
type: pages
|
||||
layout: default
|
||||
filter:
|
||||
- withPublished
|
||||
|
||||
data:
|
||||
object: 'Grav\Common\Page\Flex\PageObject'
|
||||
collection: 'Grav\Common\Page\Flex\PageCollection'
|
||||
index: 'Grav\Common\Page\Flex\PageIndex'
|
||||
storage:
|
||||
class: 'Grav\Common\Page\Flex\PageStorage'
|
||||
options:
|
||||
formatter:
|
||||
class: 'Grav\Framework\File\Formatter\MarkdownFormatter'
|
||||
folder: 'page://'
|
||||
indexed: true
|
||||
ordering:
|
||||
key: ASC
|
||||
search:
|
||||
options:
|
||||
contains: 1
|
||||
fields:
|
||||
- key
|
||||
- menu
|
||||
- title
|
||||
- name
|
||||
|
||||
form:
|
||||
fields:
|
||||
lang:
|
||||
type: hidden
|
||||
value: ''
|
||||
@@ -1,39 +0,0 @@
|
||||
title: User Accounts
|
||||
description: User Accounts
|
||||
type: flex-objects
|
||||
|
||||
extends@: 'user/account'
|
||||
|
||||
config:
|
||||
admin:
|
||||
list:
|
||||
fields:
|
||||
username:
|
||||
link: edit
|
||||
search: true
|
||||
email:
|
||||
search: true
|
||||
fullname:
|
||||
search: true
|
||||
options:
|
||||
per_page: 20
|
||||
order:
|
||||
by: username
|
||||
dir: asc
|
||||
|
||||
menu:
|
||||
list:
|
||||
route: '/accounts'
|
||||
title: Accounts
|
||||
icon: fa-users
|
||||
authorize: ['admin.users', 'admin.accounts', 'admin.super']
|
||||
|
||||
form:
|
||||
fields:
|
||||
username:
|
||||
flex-disabled@: exists
|
||||
disabled: false
|
||||
flex-readonly@: exists
|
||||
readonly: false
|
||||
validate:
|
||||
required: true
|
||||
@@ -36,3 +36,4 @@ uploads_dangerous_extensions:
|
||||
- htm
|
||||
- js
|
||||
- exe
|
||||
sanitize_svg: true
|
||||
|
||||
@@ -27,6 +27,7 @@ home:
|
||||
hide_in_urls: false # Hide the home route in URLs
|
||||
|
||||
pages:
|
||||
type: page # EXPERIMENTAL: Page type: page or flex
|
||||
theme: quark # Default theme (defaults to "quark" theme)
|
||||
order:
|
||||
by: default # Order pages by "default", "alpha" or "date"
|
||||
@@ -125,6 +126,8 @@ log:
|
||||
|
||||
debugger:
|
||||
enabled: false # Enable Grav debugger and following settings
|
||||
provider: clockwork # Debugger provider: debugbar | clockwork
|
||||
censored: false # Censor potentially sensitive information (POST parameters, cookies, files, configuration and most array/object data in log messages)
|
||||
shutdown:
|
||||
close_connection: true # Close the connection before calling onShutdown(). false for debugging
|
||||
|
||||
@@ -154,15 +157,15 @@ session:
|
||||
path:
|
||||
|
||||
gpm:
|
||||
releases: stable # Set to either 'stable' or 'testing'
|
||||
releases: testing # Set to either 'stable' or 'testing'
|
||||
proxy_url: # Configure a manual proxy URL for GPM (eg 127.0.0.1:3128)
|
||||
method: 'auto' # Either 'curl', 'fopen' or 'auto'. 'auto' will try fopen first and if not available cURL
|
||||
verify_peer: true # Sometimes on some systems (Windows most commonly) GPM is unable to connect because the SSL certificate cannot be verified. Disabling this setting might help.
|
||||
official_gpm_only: true # By default GPM direct-install will only allow URLs via the official GPM proxy to ensure security
|
||||
|
||||
accounts:
|
||||
type: data # Account type: data or flex
|
||||
storage: file # Flex storage type: file or folder
|
||||
type: data # EXPERIMENTAL: Account type: data or flex
|
||||
storage: file # EXPERIMENTAL: Flex storage type: file or folder
|
||||
|
||||
strict_mode:
|
||||
yaml_compat: true # Grav 1.5+: Enables YAML backwards compatibility
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
// Some standard defines
|
||||
define('GRAV', true);
|
||||
define('GRAV_VERSION', '1.6.13');
|
||||
define('GRAV_TESTING', false);
|
||||
define('GRAV_VERSION', '1.7.0-rc.1');
|
||||
define('GRAV_TESTING', true);
|
||||
define('DS', '/');
|
||||
|
||||
if (!defined('GRAV_PHP_MIN')) {
|
||||
|
||||
@@ -14,4 +14,4 @@ use Grav\Installer\Install;
|
||||
|
||||
require_once __DIR__ . '/src/Grav/Installer/Install.php';
|
||||
|
||||
return Install::instance();
|
||||
return Install::instance();
|
||||
|
||||
@@ -20,9 +20,9 @@ if (is_file($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . $_SERVER['SCRIPT_N
|
||||
$grav_index = 'index.php';
|
||||
|
||||
/* Check the GRAV_BASEDIR environment variable and use if set */
|
||||
$grav_basedir = getenv('GRAV_BASEDIR') ?: '';
|
||||
|
||||
if (isset($grav_basedir)) {
|
||||
$grav_basedir = getenv('GRAV_BASEDIR') ?: '';
|
||||
if ($grav_basedir) {
|
||||
$grav_index = ltrim($grav_basedir, '/') . DIRECTORY_SEPARATOR . $grav_index;
|
||||
$grav_basedir = DIRECTORY_SEPARATOR . trim($grav_basedir, DIRECTORY_SEPARATOR);
|
||||
define('GRAV_ROOT', str_replace(DIRECTORY_SEPARATOR, '/', getcwd()) . $grav_basedir);
|
||||
|
||||
@@ -202,7 +202,6 @@ class Assets extends PropertyObject
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,7 +211,7 @@ class Assets extends PropertyObject
|
||||
*/
|
||||
public function addCss($asset)
|
||||
{
|
||||
return $this->addType(Assets::CSS_COLLECTION,Assets::CSS_TYPE, $asset, $this->unifyLegacyArguments(\func_get_args(), Assets::CSS_TYPE));
|
||||
return $this->addType(Assets::CSS_COLLECTION, Assets::CSS_TYPE, $asset, $this->unifyLegacyArguments(\func_get_args(), Assets::CSS_TYPE));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,13 +265,12 @@ class Assets extends PropertyObject
|
||||
|
||||
protected function filterAssets($assets, $key, $value, $sort = false)
|
||||
{
|
||||
$results = array_filter($assets, function($asset) use ($key, $value) {
|
||||
$results = array_filter($assets, function ($asset) use ($key, $value) {
|
||||
|
||||
if ($key === 'position' && $value === 'pipeline') {
|
||||
|
||||
$type = $asset->getType();
|
||||
|
||||
if ($asset->getRemote() && $this->{$type . '_pipeline_include_externals'} === false && $asset['position'] === 'pipeline' ) {
|
||||
if ($asset->getRemote() && $this->{$type . '_pipeline_include_externals'} === false && $asset['position'] === 'pipeline') {
|
||||
if ($this->{$type . '_pipeline_before_excludes'}) {
|
||||
$asset->setPosition('after');
|
||||
} else {
|
||||
@@ -280,10 +278,11 @@ class Assets extends PropertyObject
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($asset[$key] === $value) return true;
|
||||
if ($asset[$key] === $value) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -297,7 +296,7 @@ class Assets extends PropertyObject
|
||||
|
||||
protected function sortAssets($assets)
|
||||
{
|
||||
uasort ($assets, function($a, $b) {
|
||||
uasort($assets, function ($a, $b) {
|
||||
if ($a['priority'] == $b['priority']) {
|
||||
return $a['order'] - $b['order'];
|
||||
}
|
||||
|
||||
@@ -88,7 +88,6 @@ abstract class BaseAsset extends PropertyObject
|
||||
|
||||
// Move this to render?
|
||||
if (!$this->remote) {
|
||||
|
||||
$asset_parts = parse_url($asset);
|
||||
if (isset($asset_parts['query'])) {
|
||||
$this->query = $asset_parts['query'];
|
||||
@@ -163,7 +162,7 @@ abstract class BaseAsset extends PropertyObject
|
||||
*
|
||||
* @param string $asset the asset string reference
|
||||
*
|
||||
* @return string the final link url to the asset
|
||||
* @return string|false the final link url to the asset
|
||||
*/
|
||||
protected function buildLocalLink($asset)
|
||||
{
|
||||
|
||||
@@ -31,7 +31,7 @@ class Css extends BaseAsset
|
||||
public function render()
|
||||
{
|
||||
if (isset($this->attributes['loading']) && $this->attributes['loading'] === 'inline') {
|
||||
$buffer = $this->gatherLinks( [$this], self::CSS_ASSET);
|
||||
$buffer = $this->gatherLinks([$this], self::CSS_ASSET);
|
||||
return "<style>\n" . trim($buffer) . "\n</style>\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class Js extends BaseAsset
|
||||
public function render()
|
||||
{
|
||||
if (isset($this->attributes['loading']) && $this->attributes['loading'] === 'inline') {
|
||||
$buffer = $this->gatherLinks( [$this], self::JS_ASSET);
|
||||
$buffer = $this->gatherLinks([$this], self::JS_ASSET);
|
||||
return '<script' . $this->renderAttributes() . ">\n" . trim($buffer) . "\n</script>\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -39,12 +39,12 @@ trait LegacyAssetsTrait
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case(Assets::JS_TYPE):
|
||||
case (Assets::JS_TYPE):
|
||||
$defaults = ['priority' => null, 'pipeline' => true, 'loading' => null, 'group' => null];
|
||||
$arguments = $this->createArgumentsFromLegacy($args, $defaults);
|
||||
break;
|
||||
|
||||
case(Assets::INLINE_JS_TYPE):
|
||||
case (Assets::INLINE_JS_TYPE):
|
||||
$defaults = ['priority' => null, 'group' => null, 'attributes' => null];
|
||||
$arguments = $this->createArgumentsFromLegacy($args, $defaults);
|
||||
|
||||
@@ -57,13 +57,13 @@ trait LegacyAssetsTrait
|
||||
|
||||
break;
|
||||
|
||||
case(Assets::INLINE_CSS_TYPE):
|
||||
case (Assets::INLINE_CSS_TYPE):
|
||||
$defaults = ['priority' => null, 'group' => null];
|
||||
$arguments = $this->createArgumentsFromLegacy($args, $defaults);
|
||||
break;
|
||||
|
||||
default:
|
||||
case(Assets::CSS_TYPE):
|
||||
case (Assets::CSS_TYPE):
|
||||
$defaults = ['priority' => null, 'pipeline' => true, 'group' => null, 'loading' => null];
|
||||
$arguments = $this->createArgumentsFromLegacy($args, $defaults);
|
||||
}
|
||||
@@ -121,5 +121,4 @@ trait LegacyAssetsTrait
|
||||
|
||||
return $this->addJs($asset, $priority, $pipeline, 'defer', $group);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ trait TestingAssetsTrait
|
||||
* Get the timestamp for assets
|
||||
*
|
||||
* @param bool $include_join
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getTimestamp($include_join = true)
|
||||
{
|
||||
@@ -327,8 +327,10 @@ trait TestingAssetsTrait
|
||||
*/
|
||||
protected function rglob($directory, $pattern, $ltrim = null)
|
||||
{
|
||||
$iterator = new \RegexIterator(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory,
|
||||
\FilesystemIterator::SKIP_DOTS)), $pattern);
|
||||
$iterator = new \RegexIterator(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(
|
||||
$directory,
|
||||
\FilesystemIterator::SKIP_DOTS
|
||||
)), $pattern);
|
||||
$offset = \strlen($ltrim);
|
||||
$files = [];
|
||||
|
||||
@@ -338,6 +340,4 @@ trait TestingAssetsTrait
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@ use Grav\Common\Scheduler\Scheduler;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Common\Grav;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use RocketTheme\Toolbox\File\JsonFile;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
|
||||
class Backups
|
||||
{
|
||||
@@ -60,7 +60,7 @@ class Backups
|
||||
$name = $inflector::hyphenize($profile['name']);
|
||||
$logs = 'logs/backup-' . $name . '.out';
|
||||
/** @var Job $job */
|
||||
$job = $scheduler->addFunction('Grav\Common\Backup\Backups::backup', [$id], $name );
|
||||
$job = $scheduler->addFunction('Grav\Common\Backup\Backups::backup', [$id], $name);
|
||||
$job->at($at);
|
||||
$job->output($logs);
|
||||
$job->backlink('/tools/backups');
|
||||
@@ -71,8 +71,10 @@ class Backups
|
||||
{
|
||||
$param_sep = $param_sep = Grav::instance()['config']->get('system.param_sep', ':');
|
||||
$download = urlencode(base64_encode($backup));
|
||||
$url = rtrim(Grav::instance()['uri']->rootUrl(true), '/') . '/' . trim($base_url,
|
||||
'/') . '/task' . $param_sep . 'backup/download' . $param_sep . $download . '/admin-nonce' . $param_sep . Utils::getNonce('admin-form');
|
||||
$url = rtrim(Grav::instance()['uri']->rootUrl(true), '/') . '/' . trim(
|
||||
$base_url,
|
||||
'/'
|
||||
) . '/task' . $param_sep . 'backup/download' . $param_sep . $download . '/admin-nonce' . $param_sep . Utils::getNonce('admin-form');
|
||||
|
||||
return $url;
|
||||
}
|
||||
@@ -113,7 +115,6 @@ class Backups
|
||||
* @var \SplFileInfo $file
|
||||
*/
|
||||
foreach ($backups_itr as $name => $file) {
|
||||
|
||||
if (preg_match(static::BACKUP_FILENAME_REGEXZ, $name, $matches)) {
|
||||
$date = \DateTime::createFromFormat(static::BACKUP_DATE_FORMAT, $matches[2]);
|
||||
$timestamp = $date->getTimestamp();
|
||||
@@ -158,7 +159,7 @@ class Backups
|
||||
$date = date(static::BACKUP_DATE_FORMAT, time());
|
||||
$filename = trim($name, '_') . '--' . $date . '.zip';
|
||||
$destination = static::$backup_dir . DS . $filename;
|
||||
$max_execution_time = ini_set('max_execution_time', 600);
|
||||
$max_execution_time = ini_set('max_execution_time', '600');
|
||||
$backup_root = $backup->root;
|
||||
|
||||
if ($locator->isStream($backup_root)) {
|
||||
@@ -220,13 +221,12 @@ class Backups
|
||||
$trigger = $purge_config['trigger'];
|
||||
$backups = static::getAvailableBackups(true);
|
||||
|
||||
switch ($trigger)
|
||||
{
|
||||
switch ($trigger) {
|
||||
case 'number':
|
||||
$backups_count = count($backups);
|
||||
if ($backups_count > $purge_config['max_backups_count']) {
|
||||
$last = end($backups);
|
||||
unlink ($last->path);
|
||||
unlink($last->path);
|
||||
static::purge();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -108,7 +108,7 @@ class Browser
|
||||
/**
|
||||
* Get the current major version identifier
|
||||
*
|
||||
* @return string the browser major version identifier
|
||||
* @return int the browser major version identifier
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@ use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Scheduler\Scheduler;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
|
||||
/**
|
||||
* The GravCache object is used throughout Grav to store and retrieve cached data.
|
||||
@@ -260,8 +260,10 @@ class Cache extends Getters
|
||||
case 'memcache':
|
||||
if (extension_loaded('memcache')) {
|
||||
$memcache = new \Memcache();
|
||||
$memcache->connect($this->config->get('system.cache.memcache.server', 'localhost'),
|
||||
$this->config->get('system.cache.memcache.port', 11211));
|
||||
$memcache->connect(
|
||||
$this->config->get('system.cache.memcache.server', 'localhost'),
|
||||
$this->config->get('system.cache.memcache.port', 11211)
|
||||
);
|
||||
$driver = new DoctrineCache\MemcacheCache();
|
||||
$driver->setMemcache($memcache);
|
||||
} else {
|
||||
@@ -272,8 +274,10 @@ class Cache extends Getters
|
||||
case 'memcached':
|
||||
if (extension_loaded('memcached')) {
|
||||
$memcached = new \Memcached();
|
||||
$memcached->addServer($this->config->get('system.cache.memcached.server', 'localhost'),
|
||||
$this->config->get('system.cache.memcached.port', 11211));
|
||||
$memcached->addServer(
|
||||
$this->config->get('system.cache.memcached.server', 'localhost'),
|
||||
$this->config->get('system.cache.memcached.port', 11211)
|
||||
);
|
||||
$driver = new DoctrineCache\MemcachedCache();
|
||||
$driver->setMemcached($memcached);
|
||||
} else {
|
||||
@@ -290,8 +294,10 @@ class Cache extends Getters
|
||||
if ($socket) {
|
||||
$redis->connect($socket);
|
||||
} else {
|
||||
$redis->connect($this->config->get('system.cache.redis.server', 'localhost'),
|
||||
$this->config->get('system.cache.redis.port', 6379));
|
||||
$redis->connect(
|
||||
$this->config->get('system.cache.redis.server', 'localhost'),
|
||||
$this->config->get('system.cache.redis.port', 6379)
|
||||
);
|
||||
}
|
||||
|
||||
// Authenticate with password if set
|
||||
@@ -319,7 +325,7 @@ class Cache extends Getters
|
||||
*
|
||||
* @param string $id the id of the cached entry
|
||||
*
|
||||
* @return object|bool returns the cached entry, can be any type, or false if doesn't exist
|
||||
* @return mixed|bool returns the cached entry, can be any type, or false if doesn't exist
|
||||
*/
|
||||
public function fetch($id)
|
||||
{
|
||||
@@ -334,7 +340,7 @@ class Cache extends Getters
|
||||
* Stores a new cached entry.
|
||||
*
|
||||
* @param string $id the id of the cached entry
|
||||
* @param array|object $data the data for the cached entry to store
|
||||
* @param array|object|int $data the data for the cached entry to store
|
||||
* @param int $lifetime the lifetime to store the entry in seconds
|
||||
*/
|
||||
public function save($id, $data, $lifetime = null)
|
||||
@@ -446,7 +452,6 @@ class Cache extends Getters
|
||||
} else {
|
||||
$remove_paths = self::$standard_remove_no_images;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Delete entries in the doctrine cache if required
|
||||
@@ -459,11 +464,12 @@ class Cache extends Getters
|
||||
Grav::instance()->fireEvent('onBeforeCacheClear', new Event(['remove' => $remove, 'paths' => &$remove_paths]));
|
||||
|
||||
foreach ($remove_paths as $stream) {
|
||||
|
||||
// Convert stream to a real path
|
||||
try {
|
||||
$path = $locator->findResource($stream, true, true);
|
||||
if($path === false) continue;
|
||||
if ($path === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$anything = false;
|
||||
$files = glob($path . '/*');
|
||||
@@ -528,7 +534,6 @@ class Cache extends Getters
|
||||
if (function_exists('opcache_reset')) {
|
||||
@opcache_reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -634,7 +639,7 @@ class Cache extends Getters
|
||||
$name = 'cache-purge';
|
||||
$logs = 'logs/' . $name . '.out';
|
||||
|
||||
$job = $scheduler->addFunction('Grav\Common\Cache::purgeJob', [], $name );
|
||||
$job = $scheduler->addFunction('Grav\Common\Cache::purgeJob', [], $name);
|
||||
$job->at($at);
|
||||
$job->output($logs);
|
||||
$job->backlink('/config/system#caching');
|
||||
@@ -645,12 +650,9 @@ class Cache extends Getters
|
||||
$name = 'cache-clear';
|
||||
$logs = 'logs/' . $name . '.out';
|
||||
|
||||
$job = $scheduler->addFunction('Grav\Common\Cache::clearJob', [$clear_type], $name );
|
||||
$job = $scheduler->addFunction('Grav\Common\Cache::clearJob', [$clear_type], $name);
|
||||
$job->at($at);
|
||||
$job->output($logs);
|
||||
$job->backlink('/config/system#caching');
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class Composer
|
||||
}
|
||||
|
||||
// check for global composer install
|
||||
$path = trim(shell_exec('command -v composer'));
|
||||
$path = trim((string)shell_exec('command -v composer'));
|
||||
|
||||
// fall back to grav bundled composer
|
||||
if (!$path || !preg_match('/(composer|composer\.phar)$/', $path)) {
|
||||
|
||||
@@ -29,7 +29,7 @@ abstract class CompiledBase
|
||||
public $checksum;
|
||||
|
||||
/**
|
||||
* @var string Timestamp of compiled configuration
|
||||
* @var int Timestamp of compiled configuration
|
||||
*/
|
||||
public $timestamp;
|
||||
|
||||
@@ -89,7 +89,9 @@ abstract class CompiledBase
|
||||
/**
|
||||
* Function gets called when cached configuration is saved.
|
||||
*/
|
||||
public function modified() {}
|
||||
public function modified()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get timestamp of compiled configuration
|
||||
@@ -157,7 +159,7 @@ abstract class CompiledBase
|
||||
* Load single configuration file and append it to the correct position.
|
||||
*
|
||||
* @param string $name Name of the position.
|
||||
* @param string $filename File to be loaded.
|
||||
* @param string|string[] $filename File(s) to be loaded.
|
||||
*/
|
||||
abstract protected function loadFile($name, $filename);
|
||||
|
||||
@@ -197,8 +199,7 @@ abstract class CompiledBase
|
||||
}
|
||||
|
||||
$cache = include $filename;
|
||||
if (
|
||||
!\is_array($cache)
|
||||
if (!\is_array($cache)
|
||||
|| !isset($cache['checksum'], $cache['data'], $cache['@class'])
|
||||
|| $cache['@class'] !== \get_class($this)
|
||||
) {
|
||||
|
||||
@@ -25,7 +25,10 @@ class Blueprint extends BlueprintForm
|
||||
/** @var BlueprintSchema */
|
||||
protected $blueprintSchema;
|
||||
|
||||
/** @var array */
|
||||
/** @var object */
|
||||
protected $object;
|
||||
|
||||
/** @var array|null */
|
||||
protected $defaults;
|
||||
|
||||
protected $handlers = [];
|
||||
@@ -42,6 +45,11 @@ class Blueprint extends BlueprintForm
|
||||
$this->scope = $scope;
|
||||
}
|
||||
|
||||
public function setObject($object)
|
||||
{
|
||||
$this->object = $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default values for field types.
|
||||
*
|
||||
@@ -111,6 +119,7 @@ class Blueprint extends BlueprintForm
|
||||
foreach ($data as $property => $call) {
|
||||
$action = $call['action'];
|
||||
$method = 'dynamic' . ucfirst($action);
|
||||
$call['object'] = $this->object;
|
||||
|
||||
if (isset($this->handlers[$action])) {
|
||||
$callable = $this->handlers[$action];
|
||||
|
||||
@@ -54,7 +54,6 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
{
|
||||
try {
|
||||
$messages = $this->validateArray($data, $this->nested);
|
||||
|
||||
} catch (\RuntimeException $e) {
|
||||
throw (new ValidationException($e->getMessage(), $e->getCode(), $e))->setMessages();
|
||||
}
|
||||
@@ -201,7 +200,6 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
} elseif (\is_array($field) && \is_array($val)) {
|
||||
// Array has been defined in blueprints.
|
||||
$field = $this->filterArray($field, $val, $missingValuesAsNull, $keepEmptyValues);
|
||||
|
||||
} elseif (isset($rules['validation']) && $rules['validation'] === 'strict') {
|
||||
// Skip any extra data.
|
||||
continue;
|
||||
@@ -244,8 +242,8 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
|| !empty($field['disabled'])
|
||||
// Field validation is set to be ignored
|
||||
|| !empty($field['validate']['ignore'])
|
||||
// Field is toggleable and the toggle is turned off
|
||||
|| (!empty($field['toggleable']) && empty($toggles[$key]))
|
||||
// Field is overridable and the toggle is turned off
|
||||
|| (!empty($field['overridable']) && empty($toggles[$key]))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
@@ -279,10 +277,15 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip overridable fields without value.
|
||||
// TODO: We need better overridable support, which is not just ignoring required values but also looking if defaults are good.
|
||||
if (!empty($field['overridable']) && !isset($data[$name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if required.
|
||||
if (isset($field['validate']['required'])
|
||||
&& $field['validate']['required'] === true) {
|
||||
|
||||
if (isset($data[$name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
|
||||
|
||||
/**
|
||||
* @param array $items
|
||||
* @param Blueprint|callable $blueprints
|
||||
* @param Blueprint|callable|null $blueprints
|
||||
*/
|
||||
public function __construct(array $items = [], $blueprints = null)
|
||||
{
|
||||
@@ -227,7 +227,7 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
|
||||
*/
|
||||
public function blueprints()
|
||||
{
|
||||
if (!$this->blueprints){
|
||||
if (!$this->blueprints) {
|
||||
$this->blueprints = new Blueprint;
|
||||
} elseif (\is_callable($this->blueprints)) {
|
||||
// Lazy load blueprints.
|
||||
@@ -280,7 +280,7 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
|
||||
/**
|
||||
* Set or get the data storage.
|
||||
*
|
||||
* @param FileInterface $storage Optionally enter a new storage.
|
||||
* @param FileInterface|null $storage Optionally enter a new storage.
|
||||
* @return FileInterface
|
||||
*/
|
||||
public function file(FileInterface $storage = null)
|
||||
|
||||
@@ -63,7 +63,7 @@ interface DataInterface
|
||||
/**
|
||||
* Set or get the data storage.
|
||||
*
|
||||
* @param FileInterface $storage Optionally enter a new storage.
|
||||
* @param FileInterface|null $storage Optionally enter a new storage.
|
||||
* @return FileInterface
|
||||
*/
|
||||
public function file(FileInterface $storage = null);
|
||||
|
||||
@@ -674,7 +674,6 @@ class Validation
|
||||
}
|
||||
|
||||
return (array) Yaml::parse($value);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -794,7 +793,9 @@ class Validation
|
||||
|
||||
public static function filterItem_List($value, $params)
|
||||
{
|
||||
return array_values(array_filter($value, function($v) { return !empty($v); } ));
|
||||
return array_values(array_filter($value, function ($v) {
|
||||
return !empty($v);
|
||||
}));
|
||||
}
|
||||
|
||||
public static function validateJson($value, $params)
|
||||
|
||||
@@ -15,7 +15,8 @@ class ValidationException extends \RuntimeException
|
||||
{
|
||||
protected $messages = [];
|
||||
|
||||
public function setMessages(array $messages = []) {
|
||||
public function setMessages(array $messages = [])
|
||||
{
|
||||
$this->messages = $messages;
|
||||
|
||||
$language = Grav::instance()['language'];
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
namespace Grav\Common;
|
||||
|
||||
use Clockwork\Clockwork;
|
||||
use Clockwork\DataSource\MonologDataSource;
|
||||
use Clockwork\DataSource\PsrMessageDataSource;
|
||||
use Clockwork\DataSource\XdebugDataSource;
|
||||
use Clockwork\Helpers\ServerTiming;
|
||||
use Clockwork\Request\UserData;
|
||||
use Clockwork\Storage\FileStorage;
|
||||
use DebugBar\DataCollector\ConfigCollector;
|
||||
use DebugBar\DataCollector\DataCollectorInterface;
|
||||
use DebugBar\DataCollector\ExceptionsCollector;
|
||||
@@ -22,11 +29,22 @@ use DebugBar\JavascriptRenderer;
|
||||
use DebugBar\StandardDebugBar;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Processors\ProcessorInterface;
|
||||
use Grav\Common\Twig\TwigClockworkDataSource;
|
||||
use Grav\Framework\Psr7\Response;
|
||||
use Monolog\Logger;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Twig\Template;
|
||||
use Twig\TemplateWrapper;
|
||||
|
||||
class Debugger
|
||||
{
|
||||
/** @var static */
|
||||
protected static $instance;
|
||||
|
||||
/** @var Grav $grav */
|
||||
protected $grav;
|
||||
|
||||
@@ -39,8 +57,11 @@ class Debugger
|
||||
/** @var StandardDebugBar $debugbar */
|
||||
protected $debugbar;
|
||||
|
||||
/** @var Clockwork */
|
||||
protected $clockwork;
|
||||
|
||||
/** @var bool */
|
||||
protected $enabled;
|
||||
protected $enabled = false;
|
||||
|
||||
protected $initialized = false;
|
||||
|
||||
@@ -50,39 +71,41 @@ class Debugger
|
||||
/** @var array $deprecations */
|
||||
protected $deprecations = [];
|
||||
|
||||
/** @var callable */
|
||||
/** @var callable|null */
|
||||
protected $errorHandler;
|
||||
|
||||
protected $requestTime;
|
||||
protected $currentTime;
|
||||
|
||||
/** @var int */
|
||||
protected $profiling = 0;
|
||||
|
||||
protected $censored = false;
|
||||
|
||||
/**
|
||||
* Debugger constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$currentTime = microtime(true);
|
||||
static::$instance = $this;
|
||||
|
||||
$this->currentTime = microtime(true);
|
||||
|
||||
if (!\defined('GRAV_REQUEST_TIME')) {
|
||||
\define('GRAV_REQUEST_TIME', $currentTime);
|
||||
\define('GRAV_REQUEST_TIME', $this->currentTime);
|
||||
}
|
||||
|
||||
// Enable debugger until $this->init() gets called.
|
||||
$this->enabled = true;
|
||||
|
||||
$debugbar = new DebugBar();
|
||||
$debugbar->addCollector(new PhpInfoCollector());
|
||||
$debugbar->addCollector(new MessagesCollector());
|
||||
$debugbar->addCollector(new RequestDataCollector());
|
||||
$debugbar->addCollector(new TimeDataCollector($_SERVER['REQUEST_TIME_FLOAT'] ?? GRAV_REQUEST_TIME));
|
||||
|
||||
$debugbar['time']->addMeasure('Server', $debugbar['time']->getRequestStartTime(), GRAV_REQUEST_TIME);
|
||||
$debugbar['time']->addMeasure('Loading', GRAV_REQUEST_TIME, $currentTime);
|
||||
$debugbar['time']->addMeasure('Debugger', $currentTime, microtime(true));
|
||||
|
||||
$this->debugbar = $debugbar;
|
||||
$this->requestTime = $_SERVER['REQUEST_TIME_FLOAT'] ?? GRAV_REQUEST_TIME;
|
||||
|
||||
// Set deprecation collector.
|
||||
$this->setErrorHandler();
|
||||
}
|
||||
|
||||
public function getClockwork(): ?Clockwork
|
||||
{
|
||||
return $this->enabled ? $this->clockwork : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the debugger
|
||||
*
|
||||
@@ -100,25 +123,236 @@ class Debugger
|
||||
|
||||
// Enable/disable debugger based on configuration.
|
||||
$this->enabled = (bool)$this->config->get('system.debugger.enabled');
|
||||
$this->censored = (bool)$this->config->get('system.debugger.censored', false);
|
||||
|
||||
if ($this->enabled()) {
|
||||
if ($this->enabled) {
|
||||
$this->initialized = true;
|
||||
|
||||
$plugins_config = (array)$this->config->get('plugins');
|
||||
$clockwork = $debugbar = null;
|
||||
|
||||
switch ($this->config->get('system.debugger.provider', 'debugbar')) {
|
||||
case 'clockwork':
|
||||
$this->clockwork = $clockwork = new Clockwork();
|
||||
break;
|
||||
default:
|
||||
$this->debugbar = $debugbar = new DebugBar();
|
||||
}
|
||||
|
||||
$plugins_config = (array)$this->config->get('plugins');
|
||||
ksort($plugins_config);
|
||||
|
||||
$debugbar = $this->debugbar;
|
||||
$debugbar->addCollector(new MemoryCollector());
|
||||
$debugbar->addCollector(new ExceptionsCollector());
|
||||
$debugbar->addCollector(new ConfigCollector((array)$this->config->get('system'), 'Config'));
|
||||
$debugbar->addCollector(new ConfigCollector($plugins_config, 'Plugins'));
|
||||
$this->addMessage('Grav v' . GRAV_VERSION);
|
||||
if ($clockwork) {
|
||||
$log = $this->grav['log'];
|
||||
$clockwork->setStorage(new FileStorage('cache://clockwork'));
|
||||
if (extension_loaded('xdebug')) {
|
||||
$clockwork->addDataSource(new XdebugDataSource());
|
||||
}
|
||||
if ($log instanceof Logger) {
|
||||
$clockwork->addDataSource(new MonologDataSource($log));
|
||||
}
|
||||
|
||||
$clockwork->addDataSource(new TwigClockworkDataSource());
|
||||
|
||||
$timeLine = $clockwork->getTimeline();
|
||||
if ($this->requestTime !== GRAV_REQUEST_TIME) {
|
||||
$timeLine->addEvent('server', 'Server', $this->requestTime, GRAV_REQUEST_TIME);
|
||||
}
|
||||
if ($this->currentTime !== GRAV_REQUEST_TIME) {
|
||||
$timeLine->addEvent('loading', 'Loading', GRAV_REQUEST_TIME, $this->currentTime);
|
||||
}
|
||||
$timeLine->addEvent('setup', 'Site Setup', $this->currentTime, microtime(true));
|
||||
}
|
||||
|
||||
if ($this->censored) {
|
||||
$censored = ['CENSORED' => true];
|
||||
}
|
||||
|
||||
if ($debugbar) {
|
||||
$debugbar->addCollector(new PhpInfoCollector());
|
||||
$debugbar->addCollector(new MessagesCollector());
|
||||
if (!$this->censored) {
|
||||
$debugbar->addCollector(new RequestDataCollector());
|
||||
}
|
||||
$debugbar->addCollector(new TimeDataCollector($this->requestTime));
|
||||
$debugbar->addCollector(new MemoryCollector());
|
||||
$debugbar->addCollector(new ExceptionsCollector());
|
||||
$debugbar->addCollector(new ConfigCollector($censored ?? (array)$this->config->get('system'), 'Config'));
|
||||
$debugbar->addCollector(new ConfigCollector($censored ?? $plugins_config, 'Plugins'));
|
||||
$debugbar->addCollector(new ConfigCollector($this->config->get('streams.schemes'), 'Streams'));
|
||||
|
||||
if ($this->requestTime !== GRAV_REQUEST_TIME) {
|
||||
$debugbar['time']->addMeasure('Server', $debugbar['time']->getRequestStartTime(), GRAV_REQUEST_TIME);
|
||||
}
|
||||
if ($this->currentTime !== GRAV_REQUEST_TIME) {
|
||||
$debugbar['time']->addMeasure('Loading', GRAV_REQUEST_TIME, $this->currentTime);
|
||||
}
|
||||
$debugbar['time']->addMeasure('Site Setup', $this->currentTime, microtime(true));
|
||||
}
|
||||
|
||||
$this->addMessage('Grav v' . GRAV_VERSION . ' - PHP ' . PHP_VERSION);
|
||||
$this->config->debug();
|
||||
|
||||
if ($clockwork) {
|
||||
$clockwork->info('System Configuration', $censored ?? $this->config->get('system'));
|
||||
$clockwork->info('Plugins Configuration', $censored ?? $plugins_config);
|
||||
$clockwork->info('Streams', $this->config->get('streams.schemes'));
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function finalize(): void
|
||||
{
|
||||
if ($this->clockwork && $this->enabled) {
|
||||
$this->stopProfiling('Profiler Analysis');
|
||||
$this->addMeasures();
|
||||
|
||||
$deprecations = $this->getDeprecations();
|
||||
$count = count($deprecations);
|
||||
if (!$count) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var UserData $userData */
|
||||
$userData = $this->clockwork->userData('Deprecated');
|
||||
$userData->counters([
|
||||
'Deprecated' => count($deprecations)
|
||||
]);
|
||||
foreach ($deprecations as &$deprecation) {
|
||||
if (0) {
|
||||
$d = $deprecation;
|
||||
unset($d['message']);
|
||||
$this->clockwork->log('deprecated', $deprecation['message'], $d);
|
||||
}
|
||||
}
|
||||
unset($deprecation);
|
||||
|
||||
$userData->table('Your site is using following deprecated features', $deprecations);
|
||||
}
|
||||
}
|
||||
|
||||
public function logRequest(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
if (!$this->enabled || !$this->clockwork) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$clockwork = $this->clockwork;
|
||||
|
||||
$this->finalize();
|
||||
|
||||
$clockwork->getTimeline()->finalize($request->getAttribute('request_time'));
|
||||
|
||||
if ($this->censored) {
|
||||
$censored = 'CENSORED';
|
||||
$request = $request
|
||||
->withCookieParams([$censored => ''])
|
||||
->withUploadedFiles([])
|
||||
->withHeader('cookie', $censored);
|
||||
if ($request->getBody()) {
|
||||
$request = $request->withParsedBody([$censored => '']);
|
||||
}
|
||||
}
|
||||
|
||||
$clockwork->addDataSource(new PsrMessageDataSource($request, $response));
|
||||
|
||||
$clockwork->resolveRequest();
|
||||
$clockwork->storeRequest();
|
||||
|
||||
$clockworkRequest = $clockwork->getRequest();
|
||||
|
||||
$response = $response
|
||||
->withHeader('X-Clockwork-Id', $clockworkRequest->id)
|
||||
->withHeader('X-Clockwork-Version', $clockwork::VERSION);
|
||||
|
||||
$basePath = Grav::instance()['uri']->rootUrl();
|
||||
if ($basePath) {
|
||||
$response = $response->withHeader('X-Clockwork-Path', $basePath . '/__clockwork/');
|
||||
}
|
||||
|
||||
return $response->withHeader('Server-Timing', ServerTiming::fromRequest($clockworkRequest)->value());
|
||||
}
|
||||
|
||||
|
||||
public function debuggerRequest(RequestInterface $request): Response
|
||||
{
|
||||
$clockwork = $this->clockwork;
|
||||
|
||||
$headers = [
|
||||
'Content-Type' => 'application/json',
|
||||
'Grav-Internal-SkipShutdown' => 1
|
||||
];
|
||||
|
||||
$path = $request->getUri()->getPath();
|
||||
$clockworkDataUri = '#/__clockwork(?:/(?<id>[0-9-]+))?(?:/(?<direction>(?:previous|next)))?(?:/(?<count>\d+))?#';
|
||||
if (preg_match($clockworkDataUri, $path, $matches) === false) {
|
||||
$response = ['message' => 'Bad Input'];
|
||||
|
||||
return new Response(400, $headers, json_encode($response));
|
||||
}
|
||||
|
||||
$id = $matches['id'] ?? null;
|
||||
$direction = $matches['direction'] ?? null;
|
||||
$count = $matches['count'] ?? null;
|
||||
|
||||
$storage = $clockwork->getStorage();
|
||||
|
||||
if ($direction === 'previous') {
|
||||
$data = $storage->previous($id, $count);
|
||||
} elseif ($direction === 'next') {
|
||||
$data = $storage->next($id, $count);
|
||||
} elseif ($id === 'latest') {
|
||||
$data = $storage->latest();
|
||||
} else {
|
||||
$data = $storage->find($id);
|
||||
}
|
||||
|
||||
if (preg_match('#(?<id>[0-9-]+|latest)/extended#', $path)) {
|
||||
$clockwork->extendRequest($data);
|
||||
}
|
||||
|
||||
if (!$data) {
|
||||
$response = ['message' => 'Not Found'];
|
||||
|
||||
return new Response(404, $headers, json_encode($response));
|
||||
}
|
||||
|
||||
$data = is_array($data) ? array_map(function ($item) {
|
||||
return $item->toArray();
|
||||
}, $data) : $data->toArray();
|
||||
|
||||
return new Response(200, $headers, json_encode($data));
|
||||
}
|
||||
|
||||
protected function addMeasures()
|
||||
{
|
||||
if (!$this->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
$nowTime = microtime(true);
|
||||
$clkTimeLine = $this->clockwork ? $this->clockwork->getTimeline() : null;
|
||||
$debTimeLine = $this->debugbar ? $this->debugbar['time'] : null;
|
||||
foreach ($this->timers as $name => $data) {
|
||||
$description = $data[0];
|
||||
$startTime = $data[1] ?? null;
|
||||
$endTime = $data[2] ?? $nowTime;
|
||||
if ($endTime - $startTime < 0.001) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($clkTimeLine) {
|
||||
$clkTimeLine->addEvent($name, $description ?? $name, $startTime, $endTime);
|
||||
}
|
||||
|
||||
if ($debTimeLine) {
|
||||
$debTimeLine->addMeasure($description ?? $name, $startTime, $endTime);
|
||||
}
|
||||
}
|
||||
$this->timers = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set/get the enabled state of the debugger
|
||||
*
|
||||
@@ -142,8 +376,7 @@ class Debugger
|
||||
*/
|
||||
public function addAssets()
|
||||
{
|
||||
if ($this->enabled()) {
|
||||
|
||||
if ($this->enabled) {
|
||||
// Only add assets if Page is HTML
|
||||
$page = $this->grav['page'];
|
||||
if ($page->templateFormat() !== 'html') {
|
||||
@@ -153,22 +386,32 @@ class Debugger
|
||||
/** @var Assets $assets */
|
||||
$assets = $this->grav['assets'];
|
||||
|
||||
// Add jquery library
|
||||
$assets->add('jquery', 101);
|
||||
|
||||
$this->renderer = $this->debugbar->getJavascriptRenderer();
|
||||
$this->renderer->setIncludeVendors(false);
|
||||
|
||||
// Get the required CSS files
|
||||
list($css_files, $js_files) = $this->renderer->getAssets(null, JavascriptRenderer::RELATIVE_URL);
|
||||
foreach ((array)$css_files as $css) {
|
||||
$assets->addCss($css);
|
||||
// Clockwork specific assets
|
||||
if ($this->clockwork) {
|
||||
$assets->addCss('/system/assets/debugger/clockwork.css', ['loading' => 'inline']);
|
||||
$assets->addJs('/system/assets/debugger/clockwork.js', ['loading' => 'inline']);
|
||||
}
|
||||
|
||||
$assets->addCss('/system/assets/debugger.css');
|
||||
|
||||
foreach ((array)$js_files as $js) {
|
||||
$assets->addJs($js);
|
||||
// Debugbar specific assets
|
||||
if ($this->debugbar) {
|
||||
// Add jquery library
|
||||
$assets->add('jquery', 101);
|
||||
|
||||
$this->renderer = $this->debugbar->getJavascriptRenderer();
|
||||
$this->renderer->setIncludeVendors(false);
|
||||
|
||||
list($css_files, $js_files) = $this->renderer->getAssets(null, JavascriptRenderer::RELATIVE_URL);
|
||||
|
||||
foreach ((array)$css_files as $css) {
|
||||
$assets->addCss($css);
|
||||
}
|
||||
|
||||
$assets->addCss('/system/assets/debugger/phpdebugbar.css', ['loading' => 'inline']);
|
||||
|
||||
foreach ((array)$js_files as $js) {
|
||||
$assets->addJs($js);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +435,9 @@ class Debugger
|
||||
*/
|
||||
public function addCollector($collector)
|
||||
{
|
||||
$this->debugbar->addCollector($collector);
|
||||
if ($this->debugbar && !$this->debugbar->hasCollector($collector->getName())) {
|
||||
$this->debugbar->addCollector($collector);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -200,14 +445,18 @@ class Debugger
|
||||
/**
|
||||
* Returns a data collector
|
||||
*
|
||||
* @param DataCollectorInterface $collector
|
||||
* @param string $name
|
||||
*
|
||||
* @return DataCollectorInterface
|
||||
* @throws \DebugBar\DebugBarException
|
||||
*/
|
||||
public function getCollector($collector)
|
||||
public function getCollector($name)
|
||||
{
|
||||
return $this->debugbar->getCollector($collector);
|
||||
if ($this->debugbar && $this->debugbar->hasCollector($name)) {
|
||||
return $this->debugbar->getCollector($name);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,13 +466,14 @@ class Debugger
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
if ($this->enabled()) {
|
||||
if ($this->enabled && $this->debugbar) {
|
||||
// Only add assets if Page is HTML
|
||||
$page = $this->grav['page'];
|
||||
if (!$this->renderer || $page->templateFormat() !== 'html') {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->addMeasures();
|
||||
$this->addDeprecations();
|
||||
|
||||
echo $this->renderer->render();
|
||||
@@ -239,7 +489,8 @@ class Debugger
|
||||
*/
|
||||
public function sendDataInHeaders()
|
||||
{
|
||||
if ($this->enabled()) {
|
||||
if ($this->enabled && $this->debugbar) {
|
||||
$this->addMeasures();
|
||||
$this->addDeprecations();
|
||||
$this->debugbar->sendDataInHeaders();
|
||||
}
|
||||
@@ -250,20 +501,150 @@ class Debugger
|
||||
/**
|
||||
* Returns collected debugger data.
|
||||
*
|
||||
* @return array
|
||||
* @return array|null
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
if (!$this->enabled()) {
|
||||
if (!$this->enabled || !$this->debugbar) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->addMeasures();
|
||||
$this->addDeprecations();
|
||||
$this->timers = [];
|
||||
|
||||
return $this->debugbar->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hierarchical Profiler support.
|
||||
*
|
||||
* @param callable $callable
|
||||
* @param string $message
|
||||
* @return mixed
|
||||
*/
|
||||
public function profile(callable $callable, string $message = null)
|
||||
{
|
||||
$this->startProfiling();
|
||||
$response = $callable();
|
||||
$this->stopProfiling($message);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start profiling code.
|
||||
*/
|
||||
public function startProfiling(): void
|
||||
{
|
||||
if ($this->enabled && extension_loaded('tideways_xhprof')) {
|
||||
$this->profiling++;
|
||||
if ($this->profiling === 1) {
|
||||
\tideways_xhprof_enable(TIDEWAYS_XHPROF_FLAGS_NO_BUILTINS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop profiling code. Returns profiling array or null if profiling couldn't be done.
|
||||
*
|
||||
* @param string $message
|
||||
* @return array|null
|
||||
*/
|
||||
public function stopProfiling(string $message = null): ?array
|
||||
{
|
||||
$timings = null;
|
||||
if ($this->enabled && extension_loaded('tideways_xhprof')) {
|
||||
$profiling = $this->profiling - 1;
|
||||
if ($profiling === 0) {
|
||||
$timings = \tideways_xhprof_disable();
|
||||
$timings = $this->buildProfilerTimings($timings);
|
||||
|
||||
if ($this->clockwork) {
|
||||
/** @var UserData $userData */
|
||||
$userData = $this->clockwork->userData('Profiler');
|
||||
$userData->counters([
|
||||
'Calls' => count($timings)
|
||||
]);
|
||||
$userData->table('Profiler', $timings);
|
||||
} else {
|
||||
$this->addMessage($message ?? 'Profiler Analysis', 'debug', $timings);
|
||||
}
|
||||
}
|
||||
$this->profiling = max(0, $profiling);
|
||||
}
|
||||
|
||||
return $timings;
|
||||
}
|
||||
|
||||
protected function buildProfilerTimings(array $timings): array
|
||||
{
|
||||
// Filter method calls which take almost no time.
|
||||
$timings = array_filter($timings, function ($value) {
|
||||
return $value['wt'] > 50;
|
||||
});
|
||||
|
||||
uasort($timings, function (array $a, array $b) {
|
||||
return $b['wt'] <=> $a['wt'];
|
||||
});
|
||||
|
||||
$table = [];
|
||||
foreach ($timings as $key => $timing) {
|
||||
$parts = explode('==>', $key);
|
||||
$method = $this->parseProfilerCall(array_pop($parts));
|
||||
$context = $this->parseProfilerCall(array_pop($parts));
|
||||
|
||||
// Skip redundant method calls.
|
||||
if ($context === 'Grav\Framework\RequestHandler\RequestHandler::handle()') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do not profile library calls.
|
||||
if (strpos($context, 'Grav\\') !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$table[] = [
|
||||
'Context' => $context,
|
||||
'Method' => $method,
|
||||
'Calls' => $timing['ct'],
|
||||
'Time (ms)' => $timing['wt'] / 1000,
|
||||
];
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
protected function parseProfilerCall(?string $call)
|
||||
{
|
||||
if (null === $call) {
|
||||
return '';
|
||||
}
|
||||
if (strpos($call, '@')) {
|
||||
[$call,] = explode('@', $call);
|
||||
}
|
||||
if (strpos($call, '::')) {
|
||||
[$class, $call] = explode('::', $call);
|
||||
}
|
||||
|
||||
if (!isset($class)) {
|
||||
return $call;
|
||||
}
|
||||
|
||||
// It is also possible to display twig files, but they are being logged in views.
|
||||
/*
|
||||
if (strpos($class, '__TwigTemplate_') === 0 && class_exists($class)) {
|
||||
$env = new Environment();
|
||||
/ ** @var Template $template * /
|
||||
$template = new $class($env);
|
||||
|
||||
return $template->getTemplateName();
|
||||
}
|
||||
*/
|
||||
|
||||
return "{$class}::{$call}()";
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a timer with an associated name and description
|
||||
*
|
||||
@@ -274,10 +655,7 @@ class Debugger
|
||||
*/
|
||||
public function startTimer($name, $description = null)
|
||||
{
|
||||
if (strpos($name, '_') === 0 || $this->enabled()) {
|
||||
$this->debugbar['time']->startMeasure($name, $description);
|
||||
$this->timers[] = $name;
|
||||
}
|
||||
$this->timers[$name] = [$description, microtime(true)];
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -291,8 +669,9 @@ class Debugger
|
||||
*/
|
||||
public function stopTimer($name)
|
||||
{
|
||||
if (\in_array($name, $this->timers, true) && (strpos($name, '_') === 0 || $this->enabled())) {
|
||||
$this->debugbar['time']->stopMeasure($name);
|
||||
if (isset($this->timers[$name])) {
|
||||
$endTime = microtime(true);
|
||||
$this->timers[$name][] = $endTime;
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -303,14 +682,60 @@ class Debugger
|
||||
*
|
||||
* @param mixed $message
|
||||
* @param string $label
|
||||
* @param bool $isString
|
||||
* @param mixed|bool $isString
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addMessage($message, $label = 'info', $isString = true)
|
||||
{
|
||||
if ($this->enabled()) {
|
||||
$this->debugbar['messages']->addMessage($message, $label, $isString);
|
||||
if ($this->enabled) {
|
||||
if ($this->censored) {
|
||||
if (!is_scalar($message)) {
|
||||
$message = 'CENSORED';
|
||||
}
|
||||
if (!is_scalar($isString)) {
|
||||
$isString = ['CENSORED'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->debugbar) {
|
||||
if (is_array($isString)) {
|
||||
$message = $isString;
|
||||
$isString = false;
|
||||
} elseif (is_string($isString)) {
|
||||
$message = $isString;
|
||||
$isString = true;
|
||||
}
|
||||
$this->debugbar['messages']->addMessage($message, $label, $isString);
|
||||
}
|
||||
|
||||
if ($this->clockwork) {
|
||||
if (!is_scalar($message)) {
|
||||
$isString = $message;
|
||||
$message = '';
|
||||
} elseif (is_bool($isString)) {
|
||||
$isString = [];
|
||||
}
|
||||
if (!is_array($isString)) {
|
||||
$isString = [gettype($isString) => $isString];
|
||||
}
|
||||
$this->clockwork->log($label, $message, $isString);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addEvent(string $name, ?Event $event, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if ($this->enabled) {
|
||||
if ($this->clockwork) {
|
||||
$listeners = [];
|
||||
foreach ($dispatcher->getListeners($name) as $listener) {
|
||||
$listeners[] = $this->resolveCallable($listener);
|
||||
}
|
||||
$this->clockwork->addEvent($name, null, microtime(true), ['listeners' => $listeners]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -319,13 +744,23 @@ class Debugger
|
||||
/**
|
||||
* Dump exception into the Messages tab of the Debug Bar
|
||||
*
|
||||
* @param \Exception $e
|
||||
* @param \Throwable $e
|
||||
* @return Debugger
|
||||
*/
|
||||
public function addException(\Exception $e)
|
||||
public function addException(\Throwable $e)
|
||||
{
|
||||
if ($this->initialized && $this->enabled()) {
|
||||
$this->debugbar['exceptions']->addException($e);
|
||||
if ($this->initialized && $this->enabled) {
|
||||
if ($this->debugbar) {
|
||||
$this->debugbar['exceptions']->addException($e);
|
||||
}
|
||||
|
||||
if ($this->clockwork) {
|
||||
/** @var UserData $exceptions */
|
||||
$exceptions = $this->clockwork->userData('Exceptions');
|
||||
$exceptions->data(['message' => $e->getMessage()]);
|
||||
|
||||
$this->clockwork->alert($e->getMessage(), ['exception' => $e]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -355,7 +790,7 @@ class Debugger
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$this->enabled()) {
|
||||
if (!$this->enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -540,6 +975,21 @@ class Debugger
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getDeprecations(): array
|
||||
{
|
||||
if (!$this->deprecations) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$list = [];
|
||||
/** @var array $deprecated */
|
||||
foreach ($this->deprecations as $deprecated) {
|
||||
$list[] = $this->getDepracatedMessage($deprecated)[0];
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
protected function addDeprecations()
|
||||
{
|
||||
if (!$this->deprecations) {
|
||||
@@ -603,4 +1053,13 @@ class Debugger
|
||||
|
||||
return $trace['function'] . '(' . implode(', ', $trace['args'] ?? []) . ')';
|
||||
}
|
||||
|
||||
protected function resolveCallable(callable $callable)
|
||||
{
|
||||
if (is_array($callable)) {
|
||||
return get_class($callable[0]) . '->' . $callable[1] . '()';
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,10 @@ class BareHandler extends Handler
|
||||
{
|
||||
$inspector = $this->getInspector();
|
||||
$code = $inspector->getException()->getCode();
|
||||
if ( ($code >= 400) && ($code < 600) )
|
||||
{
|
||||
$this->getRun()->sendHttpCode($code);
|
||||
if (($code >= 400) && ($code < 600)) {
|
||||
$this->getRun()->sendHttpCode($code);
|
||||
}
|
||||
|
||||
return Handler::QUIT;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,23 +40,23 @@ class Errors
|
||||
$error_page->setPageTitle('Crikey! There was an error...');
|
||||
$error_page->addResourcePath(GRAV_ROOT . '/system/assets');
|
||||
$error_page->addCustomCss('whoops.css');
|
||||
$whoops->pushHandler($error_page);
|
||||
$whoops->prependHandler($error_page);
|
||||
break;
|
||||
case -1:
|
||||
$whoops->pushHandler(new BareHandler);
|
||||
$whoops->prependHandler(new BareHandler);
|
||||
break;
|
||||
default:
|
||||
$whoops->pushHandler(new SimplePageHandler);
|
||||
$whoops->prependHandler(new SimplePageHandler);
|
||||
break;
|
||||
}
|
||||
|
||||
if (Whoops\Util\Misc::isAjaxRequest() || $jsonRequest) {
|
||||
$whoops->pushHandler(new Whoops\Handler\JsonResponseHandler);
|
||||
$whoops->prependHandler(new Whoops\Handler\JsonResponseHandler);
|
||||
}
|
||||
|
||||
if (isset($config['log']) && $config['log']) {
|
||||
$logger = $grav['log'];
|
||||
$whoops->pushHandler(function($exception, $inspector, $run) use ($logger) {
|
||||
$whoops->prependHandler(function ($exception, $inspector, $run) use ($logger) {
|
||||
try {
|
||||
$logger->addCritical($exception->getMessage() . ' - Trace: ' . $exception->getTraceAsString());
|
||||
} catch (\Exception $e) {
|
||||
|
||||
@@ -36,9 +36,8 @@ class SimplePageHandler extends Handler
|
||||
$cssFile = $this->getResource('error.css');
|
||||
|
||||
$code = $inspector->getException()->getCode();
|
||||
if ( ($code >= 400) && ($code < 600) )
|
||||
{
|
||||
$this->getRun()->sendHttpCode($code);
|
||||
if (($code >= 400) && ($code < 600)) {
|
||||
$this->getRun()->sendHttpCode($code);
|
||||
}
|
||||
$message = $inspector->getException()->getMessage();
|
||||
|
||||
|
||||
@@ -38,8 +38,7 @@ trait CompiledFile
|
||||
$cache = $file->exists() ? $file->content() : null;
|
||||
|
||||
// Load real file if cache isn't up to date (or is invalid).
|
||||
if (
|
||||
!isset($cache['@class'])
|
||||
if (!isset($cache['@class'])
|
||||
|| $cache['@class'] !== $class
|
||||
|| $cache['modified'] !== $modified
|
||||
|| $cache['filename'] !== $this->filename
|
||||
@@ -76,7 +75,6 @@ trait CompiledFile
|
||||
|
||||
$this->content = $cache['data'];
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
throw new \RuntimeException(sprintf('Failed to read %s: %s', basename($this->filename), $e->getMessage()), 500, $e);
|
||||
}
|
||||
|
||||
@@ -46,11 +46,11 @@ abstract class Archiver
|
||||
return $this;
|
||||
}
|
||||
|
||||
public abstract function compress($folder, callable $status = null);
|
||||
abstract public function compress($folder, callable $status = null);
|
||||
|
||||
public abstract function extract($destination, callable $status = null);
|
||||
abstract public function extract($destination, callable $status = null);
|
||||
|
||||
public abstract function addEmptyFolders($folders, callable $status = null);
|
||||
abstract public function addEmptyFolders($folders, callable $status = null);
|
||||
|
||||
protected function getArchiveFiles($rootPath)
|
||||
{
|
||||
@@ -62,5 +62,4 @@ abstract class Archiver
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ abstract class Folder
|
||||
/** @var \RecursiveDirectoryIterator $file */
|
||||
foreach ($iterator as $file) {
|
||||
// Ignore hidden files.
|
||||
if (strpos($file->getFilename(), '.') === 0) {
|
||||
if (strpos($file->getFilename(), '.') === 0 && $file->isFile()) {
|
||||
continue;
|
||||
}
|
||||
if (!$folders && $file->isDir()) {
|
||||
|
||||
@@ -97,7 +97,7 @@ class ZipArchiver extends Archiver
|
||||
'message' => 'Adding empty folders...'
|
||||
]);
|
||||
|
||||
foreach($folders as $folder) {
|
||||
foreach ($folders as $folder) {
|
||||
$zip->addEmptyDir($folder);
|
||||
$status && $status([
|
||||
'type' => 'progress',
|
||||
|
||||
@@ -11,6 +11,9 @@ namespace Grav\Common\GPM\Common;
|
||||
|
||||
use Grav\Common\Data\Data;
|
||||
|
||||
/**
|
||||
* @property string $name
|
||||
*/
|
||||
class Package
|
||||
{
|
||||
/**
|
||||
@@ -42,7 +45,7 @@ class Package
|
||||
|
||||
public function __set($key, $value)
|
||||
{
|
||||
return $this->data->set($key, $value);
|
||||
$this->data->set($key, $value);
|
||||
}
|
||||
|
||||
public function __isset($key)
|
||||
|
||||
@@ -110,7 +110,7 @@ class GPM extends Iterator
|
||||
* Return the instance of a specific Package
|
||||
*
|
||||
* @param string $slug The slug of the Package
|
||||
* @return Local\Package The instance of the Package
|
||||
* @return Local\Package|null The instance of the Package
|
||||
*/
|
||||
public function getInstalledPackage($slug)
|
||||
{
|
||||
@@ -561,8 +561,7 @@ class GPM extends Iterator
|
||||
$plugin_regex = '/^class\\s{1,}[a-zA-Z0-9]{1,}\\s{1,}extends.+Plugin/m';
|
||||
$theme_regex = '/^class\\s{1,}[a-zA-Z0-9]{1,}\\s{1,}extends.+Theme/m';
|
||||
|
||||
if (
|
||||
file_exists($source . 'system/defines.php') &&
|
||||
if (file_exists($source . 'system/defines.php') &&
|
||||
file_exists($source . 'system/config/system.yaml')
|
||||
) {
|
||||
return 'grav';
|
||||
@@ -784,18 +783,24 @@ class GPM extends Iterator
|
||||
|
||||
if (count($dependent_packages)) {
|
||||
foreach ($dependent_packages as $dependent_package) {
|
||||
$other_dependency_version_with_operator = $this->getVersionOfDependencyRequiredByPackage($dependent_package,
|
||||
$slug);
|
||||
$other_dependency_version_with_operator = $this->getVersionOfDependencyRequiredByPackage(
|
||||
$dependent_package,
|
||||
$slug
|
||||
);
|
||||
$other_dependency_version = $this->calculateVersionNumberFromDependencyVersion($other_dependency_version_with_operator);
|
||||
|
||||
// check version is compatible with the one needed by the current package
|
||||
if ($this->versionFormatIsNextSignificantRelease($other_dependency_version_with_operator)) {
|
||||
$compatible = $this->checkNextSignificantReleasesAreCompatible($version,
|
||||
$other_dependency_version);
|
||||
$compatible = $this->checkNextSignificantReleasesAreCompatible(
|
||||
$version,
|
||||
$other_dependency_version
|
||||
);
|
||||
if (!$compatible) {
|
||||
if (!in_array($dependent_package, $ignore_packages_list, true)) {
|
||||
throw new \RuntimeException("Package <cyan>$slug</cyan> is required in an older version by package <cyan>$dependent_package</cyan>. This package needs a newer version, and because of this it cannot be installed. The <cyan>$dependent_package</cyan> package must be updated to use a newer release of <cyan>$slug</cyan>.",
|
||||
2);
|
||||
throw new \RuntimeException(
|
||||
"Package <cyan>$slug</cyan> is required in an older version by package <cyan>$dependent_package</cyan>. This package needs a newer version, and because of this it cannot be installed. The <cyan>$dependent_package</cyan> package must be updated to use a newer release of <cyan>$slug</cyan>.",
|
||||
2
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -815,8 +820,11 @@ class GPM extends Iterator
|
||||
public function checkPackagesCanBeInstalled($packages_names_list)
|
||||
{
|
||||
foreach ($packages_names_list as $package_name) {
|
||||
$this->checkNoOtherPackageNeedsThisDependencyInALowerVersion($package_name,
|
||||
$this->getLatestVersionOfPackage($package_name), $packages_names_list);
|
||||
$this->checkNoOtherPackageNeedsThisDependencyInALowerVersion(
|
||||
$package_name,
|
||||
$this->getLatestVersionOfPackage($package_name),
|
||||
$packages_names_list
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -845,8 +853,10 @@ class GPM extends Iterator
|
||||
// Check PHP version
|
||||
if ($dependency_slug === 'php') {
|
||||
$current_php_version = phpversion();
|
||||
if (version_compare($this->calculateVersionNumberFromDependencyVersion($dependencyVersionWithOperator),
|
||||
$current_php_version) === 1
|
||||
if (version_compare(
|
||||
$this->calculateVersionNumberFromDependencyVersion($dependencyVersionWithOperator),
|
||||
$current_php_version
|
||||
) === 1
|
||||
) {
|
||||
//Needs a Grav update first
|
||||
throw new \RuntimeException("<red>One of the packages require PHP {$dependencies['php']}. Please update PHP to resolve this");
|
||||
@@ -858,8 +868,10 @@ class GPM extends Iterator
|
||||
|
||||
//First, check for Grav dependency. If a dependency requires Grav > the current version, abort and tell.
|
||||
if ($dependency_slug === 'grav') {
|
||||
if (version_compare($this->calculateVersionNumberFromDependencyVersion($dependencyVersionWithOperator),
|
||||
GRAV_VERSION) === 1
|
||||
if (version_compare(
|
||||
$this->calculateVersionNumberFromDependencyVersion($dependencyVersionWithOperator),
|
||||
GRAV_VERSION
|
||||
) === 1
|
||||
) {
|
||||
//Needs a Grav update first
|
||||
throw new \RuntimeException("<red>One of the packages require Grav {$dependencies['grav']}. Please update Grav to the latest release.");
|
||||
@@ -888,12 +900,16 @@ class GPM extends Iterator
|
||||
// if requirement is next significant release, check is compatible with currently installed version, might not be
|
||||
if ($this->versionFormatIsNextSignificantRelease($dependencyVersionWithOperator)) {
|
||||
if ($this->firstVersionIsLower($dependencyVersion, $currentlyInstalledVersion)) {
|
||||
$compatible = $this->checkNextSignificantReleasesAreCompatible($dependencyVersion,
|
||||
$currentlyInstalledVersion);
|
||||
$compatible = $this->checkNextSignificantReleasesAreCompatible(
|
||||
$dependencyVersion,
|
||||
$currentlyInstalledVersion
|
||||
);
|
||||
|
||||
if (!$compatible) {
|
||||
throw new \RuntimeException('Dependency <cyan>' . $dependency_slug . '</cyan> is required in an older version than the one installed. This package must be updated. Please get in touch with its developer.',
|
||||
2);
|
||||
throw new \RuntimeException(
|
||||
'Dependency <cyan>' . $dependency_slug . '</cyan> is required in an older version than the one installed. This package must be updated. Please get in touch with its developer.',
|
||||
2
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -903,8 +919,10 @@ class GPM extends Iterator
|
||||
|
||||
if ($this->firstVersionIsLower($latestRelease, $dependencyVersion)) {
|
||||
//throw an exception if a required version cannot be found in the GPM yet
|
||||
throw new \RuntimeException('Dependency <cyan>' . $package_yaml['name'] . '</cyan> is required in version <cyan>' . $dependencyVersion . '</cyan> which is higher than the latest release, <cyan>' . $latestRelease . '</cyan>. Try running `bin/gpm -f index` to force a refresh of the GPM cache',
|
||||
1);
|
||||
throw new \RuntimeException(
|
||||
'Dependency <cyan>' . $package_yaml['name'] . '</cyan> is required in version <cyan>' . $dependencyVersion . '</cyan> which is higher than the latest release, <cyan>' . $latestRelease . '</cyan>. Try running `bin/gpm -f index` to force a refresh of the GPM cache',
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->firstVersionIsLower($currentlyInstalledVersion, $dependencyVersion)) {
|
||||
@@ -924,12 +942,16 @@ class GPM extends Iterator
|
||||
if ($this->versionFormatIsNextSignificantRelease($dependencyVersionWithOperator)) {
|
||||
$latestVersionOfPackage = $this->getLatestVersionOfPackage($dependency_slug);
|
||||
if ($this->firstVersionIsLower($dependencyVersion, $latestVersionOfPackage)) {
|
||||
$compatible = $this->checkNextSignificantReleasesAreCompatible($dependencyVersion,
|
||||
$latestVersionOfPackage);
|
||||
$compatible = $this->checkNextSignificantReleasesAreCompatible(
|
||||
$dependencyVersion,
|
||||
$latestVersionOfPackage
|
||||
);
|
||||
|
||||
if (!$compatible) {
|
||||
throw new \Exception('Dependency <cyan>' . $dependency_slug . '</cyan> is required in an older version than the latest release available, and it cannot be installed. This package must be updated. Please get in touch with its developer.',
|
||||
2);
|
||||
throw new \Exception(
|
||||
'Dependency <cyan>' . $dependency_slug . '</cyan> is required in an older version than the latest release available, and it cannot be installed. This package must be updated. Please get in touch with its developer.',
|
||||
2
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -947,8 +969,11 @@ class GPM extends Iterator
|
||||
public function checkNoOtherPackageNeedsTheseDependenciesInALowerVersion($dependencies_slugs)
|
||||
{
|
||||
foreach ($dependencies_slugs as $dependency_slug) {
|
||||
$this->checkNoOtherPackageNeedsThisDependencyInALowerVersion($dependency_slug,
|
||||
$this->getLatestVersionOfPackage($dependency_slug), $dependencies_slugs);
|
||||
$this->checkNoOtherPackageNeedsThisDependencyInALowerVersion(
|
||||
$dependency_slug,
|
||||
$this->getLatestVersionOfPackage($dependency_slug),
|
||||
$dependencies_slugs
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -994,7 +1019,6 @@ class GPM extends Iterator
|
||||
// Dependency already added by another package
|
||||
//if this package requires a version higher than the currently stored one, store this requirement instead
|
||||
if (isset($current_package_version_information) && $current_package_version_information !== '*') {
|
||||
|
||||
$currently_stored_version_information = $dependencies[$current_package_name];
|
||||
$currently_stored_version_number = $this->calculateVersionNumberFromDependencyVersion($currently_stored_version_information);
|
||||
|
||||
@@ -1009,8 +1033,10 @@ class GPM extends Iterator
|
||||
|
||||
$current_package_version_number = $this->calculateVersionNumberFromDependencyVersion($current_package_version_information);
|
||||
if (!$current_package_version_number) {
|
||||
throw new \RuntimeException('Bad format for version of dependency ' . $current_package_name . ' for package ' . $packageName,
|
||||
1);
|
||||
throw new \RuntimeException(
|
||||
'Bad format for version of dependency ' . $current_package_name . ' for package ' . $packageName,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
$current_package_version_is_in_next_significant_release_format = false;
|
||||
@@ -1024,17 +1050,23 @@ class GPM extends Iterator
|
||||
} else {
|
||||
if (!$currently_stored_version_is_in_next_significant_release_format && !$current_package_version_is_in_next_significant_release_format) {
|
||||
//Comparing versions equals or higher, a simple version_compare is enough
|
||||
if (version_compare($currently_stored_version_number,
|
||||
$current_package_version_number) === -1
|
||||
if (version_compare(
|
||||
$currently_stored_version_number,
|
||||
$current_package_version_number
|
||||
) === -1
|
||||
) { //Current package version is higher
|
||||
$dependencies[$current_package_name] = $current_package_version_information;
|
||||
}
|
||||
} else {
|
||||
$compatible = $this->checkNextSignificantReleasesAreCompatible($currently_stored_version_number,
|
||||
$current_package_version_number);
|
||||
$compatible = $this->checkNextSignificantReleasesAreCompatible(
|
||||
$currently_stored_version_number,
|
||||
$current_package_version_number
|
||||
);
|
||||
if (!$compatible) {
|
||||
throw new \RuntimeException('Dependency ' . $current_package_name . ' is required in two incompatible versions',
|
||||
2);
|
||||
throw new \RuntimeException(
|
||||
'Dependency ' . $current_package_name . ' is required in two incompatible versions',
|
||||
2
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1156,5 +1188,4 @@ class GPM extends Iterator
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class Installer
|
||||
protected static $target;
|
||||
|
||||
/**
|
||||
* @var int Error Code
|
||||
* @var int|string Error code or string
|
||||
*/
|
||||
protected static $error = 0;
|
||||
|
||||
@@ -84,8 +84,10 @@ 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;
|
||||
}
|
||||
@@ -160,7 +162,6 @@ class Installer
|
||||
self::$error = self::OK;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -297,7 +298,6 @@ class Installer
|
||||
public static function sophisticatedInstall($source_path, $install_path, $ignores = [], $keep_source = false)
|
||||
{
|
||||
foreach (new \DirectoryIterator($source_path) as $file) {
|
||||
|
||||
if ($file->isLink() || $file->isDot() || \in_array($file->getFilename(), $ignores, true)) {
|
||||
continue;
|
||||
}
|
||||
@@ -410,8 +410,7 @@ class Installer
|
||||
self::$error = 0;
|
||||
self::$target = $target;
|
||||
|
||||
if (
|
||||
!file_exists($target . DS . 'index.php') ||
|
||||
if (!file_exists($target . DS . 'index.php') ||
|
||||
!file_exists($target . DS . 'bin') ||
|
||||
!file_exists($target . DS . 'user') ||
|
||||
!file_exists($target . DS . 'system' . DS . 'config' . DS . 'system.yaml')
|
||||
@@ -473,7 +472,7 @@ class Installer
|
||||
case self::ZIP_EXTRACT_ERROR:
|
||||
$msg = 'Unable to extract the package. ';
|
||||
if (self::$error_zip) {
|
||||
switch(self::$error_zip) {
|
||||
switch (self::$error_zip) {
|
||||
case \ZipArchive::ER_EXISTS:
|
||||
$msg .= 'File already exists.';
|
||||
break;
|
||||
|
||||
@@ -108,7 +108,6 @@ class Licenses
|
||||
* @return \RocketTheme\Toolbox\File\FileInterface
|
||||
*/
|
||||
public static function getLicenseFile()
|
||||
|
||||
{
|
||||
if (!isset(self::$file)) {
|
||||
$path = Grav::instance()['locator']->findResource('user-data://') . '/licenses.yaml';
|
||||
|
||||
@@ -15,6 +15,7 @@ use \Doctrine\Common\Cache\FilesystemCache;
|
||||
class GravCore extends AbstractPackageCollection
|
||||
{
|
||||
protected $repository = 'https://getgrav.org/downloads/grav.json';
|
||||
/** @var array */
|
||||
private $data;
|
||||
|
||||
private $version;
|
||||
@@ -23,7 +24,7 @@ class GravCore extends AbstractPackageCollection
|
||||
|
||||
/**
|
||||
* @param bool $refresh
|
||||
* @param null $callback
|
||||
* @param callable|null $callback
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
|
||||
@@ -135,7 +135,8 @@ class Response
|
||||
if (!$settings['verify_peer']) {
|
||||
$overrides = array_replace_recursive([], $overrides, [
|
||||
'curl' => [
|
||||
CURLOPT_SSL_VERIFYPEER => $settings['verify_peer']
|
||||
CURLOPT_SSL_VERIFYPEER => $settings['verify_peer'],
|
||||
CURLOPT_SSL_VERIFYHOST => false
|
||||
],
|
||||
'fopen' => [
|
||||
'ssl' => [
|
||||
@@ -233,7 +234,6 @@ class Response
|
||||
|
||||
if ($bytes_transferred > 0) {
|
||||
if ($notification_code == STREAM_NOTIFY_PROGRESS | STREAM_NOTIFY_COMPLETED || $isCurlResource) {
|
||||
|
||||
$progress = [
|
||||
'code' => $notification_code,
|
||||
'filesize' => $filesize,
|
||||
@@ -251,7 +251,7 @@ class Response
|
||||
/**
|
||||
* Automatically picks the preferred method
|
||||
*
|
||||
* @return string The response of the request
|
||||
* @return string|null The response of the request
|
||||
*/
|
||||
private static function getAuto()
|
||||
{
|
||||
|
||||
@@ -109,7 +109,7 @@ class Upgrader
|
||||
/**
|
||||
* Get minimum PHP version from remote
|
||||
*
|
||||
* @return null
|
||||
* @return string
|
||||
*/
|
||||
public function minPHPVersion()
|
||||
{
|
||||
|
||||
@@ -11,17 +11,14 @@ namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Config\Setup;
|
||||
use Grav\Common\Helpers\Exif;
|
||||
use Grav\Common\Page\Interfaces\PageInterface;
|
||||
use Grav\Common\Page\Medium\ImageMedium;
|
||||
use Grav\Common\Page\Medium\Medium;
|
||||
use Grav\Common\Processors\AssetsProcessor;
|
||||
use Grav\Common\Processors\BackupsProcessor;
|
||||
use Grav\Common\Processors\ConfigurationProcessor;
|
||||
use Grav\Common\Processors\DebuggerAssetsProcessor;
|
||||
use Grav\Common\Processors\DebuggerProcessor;
|
||||
use Grav\Common\Processors\ErrorsProcessor;
|
||||
use Grav\Common\Processors\InitializeProcessor;
|
||||
use Grav\Common\Processors\LoggerProcessor;
|
||||
use Grav\Common\Processors\PagesProcessor;
|
||||
use Grav\Common\Processors\PluginsProcessor;
|
||||
use Grav\Common\Processors\RenderProcessor;
|
||||
@@ -30,13 +27,16 @@ use Grav\Common\Processors\SchedulerProcessor;
|
||||
use Grav\Common\Processors\TasksProcessor;
|
||||
use Grav\Common\Processors\ThemesProcessor;
|
||||
use Grav\Common\Processors\TwigProcessor;
|
||||
use Grav\Common\Scheduler\Scheduler;
|
||||
use Grav\Common\Twig\Twig;
|
||||
use Grav\Framework\DI\Container;
|
||||
use Grav\Framework\Psr7\Response;
|
||||
use Grav\Framework\RequestHandler\RequestHandler;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* Grav container is the heart of Grav.
|
||||
@@ -51,7 +51,7 @@ class Grav extends Container
|
||||
public $output;
|
||||
|
||||
/**
|
||||
* @var static The singleton instance
|
||||
* @var static|null The singleton instance
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
@@ -74,26 +74,22 @@ class Grav extends Container
|
||||
'Grav\Common\Service\SessionServiceProvider',
|
||||
'Grav\Common\Service\StreamsServiceProvider',
|
||||
'Grav\Common\Service\TaskServiceProvider',
|
||||
'browser' => 'Grav\Common\Browser',
|
||||
'cache' => 'Grav\Common\Cache',
|
||||
'events' => 'RocketTheme\Toolbox\Event\EventDispatcher',
|
||||
'exif' => 'Grav\Common\Helpers\Exif',
|
||||
'plugins' => 'Grav\Common\Plugins',
|
||||
'scheduler' => 'Grav\Common\Scheduler\Scheduler',
|
||||
'taxonomy' => 'Grav\Common\Taxonomy',
|
||||
'themes' => 'Grav\Common\Themes',
|
||||
'twig' => 'Grav\Common\Twig\Twig',
|
||||
'uri' => 'Grav\Common\Uri',
|
||||
'browser' => Browser::class,
|
||||
'cache' => Cache::class,
|
||||
'events' => EventDispatcher::class,
|
||||
'exif' => Exif::class,
|
||||
'plugins' => Plugins::class,
|
||||
'scheduler' => Scheduler::class,
|
||||
'taxonomy' => Taxonomy::class,
|
||||
'themes' => Themes::class,
|
||||
'twig' => Twig::class,
|
||||
'uri' => Uri::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array All middleware processors that are processed in $this->process()
|
||||
*/
|
||||
protected $middleware = [
|
||||
'configurationProcessor',
|
||||
'loggerProcessor',
|
||||
'errorsProcessor',
|
||||
'debuggerProcessor',
|
||||
'initializeProcessor',
|
||||
'pluginsProcessor',
|
||||
'themesProcessor',
|
||||
@@ -129,7 +125,7 @@ class Grav extends Container
|
||||
*/
|
||||
public static function instance(array $values = [])
|
||||
{
|
||||
if (!self::$instance) {
|
||||
if (null === self::$instance) {
|
||||
self::$instance = static::load($values);
|
||||
} elseif ($values) {
|
||||
$instance = self::$instance;
|
||||
@@ -157,15 +153,14 @@ class Grav extends Container
|
||||
|
||||
$this->initialized['setup'] = true;
|
||||
|
||||
$this->measureTime('_setup', 'Site Setup', function () use ($environment) {
|
||||
// Force environment if passed to the method.
|
||||
if ($environment) {
|
||||
Setup::$environment = $environment;
|
||||
}
|
||||
// Force environment if passed to the method.
|
||||
if ($environment) {
|
||||
Setup::$environment = $environment;
|
||||
}
|
||||
|
||||
$this['setup'];
|
||||
$this['streams'];
|
||||
});
|
||||
// Initialize setup and streams.
|
||||
$this['setup'];
|
||||
$this['streams'];
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -186,18 +181,6 @@ class Grav extends Container
|
||||
|
||||
$container = new Container(
|
||||
[
|
||||
'configurationProcessor' => function () {
|
||||
return new ConfigurationProcessor($this);
|
||||
},
|
||||
'loggerProcessor' => function () {
|
||||
return new LoggerProcessor($this);
|
||||
},
|
||||
'errorsProcessor' => function () {
|
||||
return new ErrorsProcessor($this);
|
||||
},
|
||||
'debuggerProcessor' => function () {
|
||||
return new DebuggerProcessor($this);
|
||||
},
|
||||
'initializeProcessor' => function () {
|
||||
return new InitializeProcessor($this);
|
||||
},
|
||||
@@ -237,46 +220,100 @@ class Grav extends Container
|
||||
]
|
||||
);
|
||||
|
||||
$default = function (ServerRequestInterface $request) {
|
||||
$default = static function () {
|
||||
return new Response(404);
|
||||
};
|
||||
|
||||
/** @var Debugger $debugger */
|
||||
$debugger = $this['debugger'];
|
||||
|
||||
$collection = new RequestHandler($this->middleware, $default, $container);
|
||||
|
||||
$response = $collection->handle($this['request']);
|
||||
$body = $response->getBody();
|
||||
|
||||
// Handle ETag and If-None-Match headers.
|
||||
if ($response->getHeaderLine('ETag') === '1') {
|
||||
$etag = md5($body);
|
||||
$response = $response->withHeader('ETag', $etag);
|
||||
|
||||
if ($this['request']->getHeaderLine('If-None-Match') === $etag) {
|
||||
$response = $response->withStatus(304);
|
||||
$body = '';
|
||||
}
|
||||
}
|
||||
|
||||
$this->header($response);
|
||||
echo $response->getBody();
|
||||
echo $body;
|
||||
|
||||
$debugger->render();
|
||||
$this['debugger']->render();
|
||||
|
||||
register_shutdown_function([$this, 'shutdown']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the system locale based on the language and configuration
|
||||
*/
|
||||
public function setLocale()
|
||||
{
|
||||
// Initialize Locale if set and configured.
|
||||
if ($this['language']->enabled() && $this['config']->get('system.languages.override_locale')) {
|
||||
$language = $this['language']->getLanguage();
|
||||
setlocale(LC_ALL, \strlen($language) < 3 ? ($language . '_' . strtoupper($language)) : $language);
|
||||
} elseif ($this['config']->get('system.default_locale')) {
|
||||
setlocale(LC_ALL, $this['config']->get('system.default_locale'));
|
||||
// Response object can turn off all shutdown processing. This can be used for example to speed up AJAX responses.
|
||||
// Note that using this feature will also turn off response compression.
|
||||
if ($response->getHeaderLine('Grav-Internal-SkipShutdown') !== '1') {
|
||||
register_shutdown_function([$this, 'shutdown']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect browser to another location.
|
||||
* Terminates Grav request with a response.
|
||||
*
|
||||
* Please use this method instead of calling `die();` or `exit();`. Note that you need to create a response object.
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
*/
|
||||
public function close(ResponseInterface $response): void
|
||||
{
|
||||
// Make sure nothing extra gets written to the response.
|
||||
while (ob_get_level()) {
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
// Close the session.
|
||||
if (isset($this['session'])) {
|
||||
$this['session']->close();
|
||||
}
|
||||
|
||||
/** @var ServerRequestInterface $request */
|
||||
$request = $this['request'];
|
||||
|
||||
/** @var Debugger $debugger */
|
||||
$debugger = $this['debugger'];
|
||||
$response = $debugger->logRequest($request, $response);
|
||||
|
||||
$body = $response->getBody();
|
||||
|
||||
// Handle ETag and If-None-Match headers.
|
||||
if ($response->getHeaderLine('ETag') === '1') {
|
||||
$etag = md5($body);
|
||||
$response = $response->withHeader('ETag', $etag);
|
||||
|
||||
if ($request->getHeaderLine('If-None-Match') === $etag) {
|
||||
$response = $response->withStatus(304);
|
||||
$body = '';
|
||||
}
|
||||
}
|
||||
|
||||
$this->header($response);
|
||||
echo $body;
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ResponseInterface $response
|
||||
* @deprecated 1.7 Do not use
|
||||
*/
|
||||
public function exit(ResponseInterface $response): void
|
||||
{
|
||||
$this->close($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates Grav request and redirects browser to another location.
|
||||
*
|
||||
* Please use this method instead of calling `header("Location: {$url}", true, 302); exit();`.
|
||||
*
|
||||
* @param string $route Internal route.
|
||||
* @param int $code Redirection code (30x)
|
||||
*/
|
||||
public function redirect($route, $code = null)
|
||||
public function redirect($route, $code = null): void
|
||||
{
|
||||
/** @var Uri $uri */
|
||||
$uri = $this['uri'];
|
||||
@@ -293,11 +330,7 @@ class Grav extends Container
|
||||
$code = $this['config']->get('system.pages.redirect_default_code', 302);
|
||||
}
|
||||
|
||||
if (isset($this['session'])) {
|
||||
$this['session']->close();
|
||||
}
|
||||
|
||||
if ($uri->isExternal($route)) {
|
||||
if ($uri::isExternal($route)) {
|
||||
$url = $route;
|
||||
} else {
|
||||
$url = rtrim($uri->rootUrl(), '/') . '/';
|
||||
@@ -309,8 +342,9 @@ class Grav extends Container
|
||||
}
|
||||
}
|
||||
|
||||
header("Location: {$url}", true, $code);
|
||||
exit();
|
||||
$response = new Response($code, ['Location' => $url]);
|
||||
|
||||
$this->close($response);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -343,26 +377,53 @@ class Grav extends Container
|
||||
|
||||
header("HTTP/{$response->getProtocolVersion()} {$response->getStatusCode()} {$response->getReasonPhrase()}");
|
||||
foreach ($response->getHeaders() as $key => $values) {
|
||||
// Skip internal Grav headers.
|
||||
if (strpos($key, 'Grav-Internal-') === 0) {
|
||||
continue;
|
||||
}
|
||||
foreach ($values as $i => $value) {
|
||||
header($key . ': ' . $value, $i === 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the system locale based on the language and configuration
|
||||
*/
|
||||
public function setLocale()
|
||||
{
|
||||
// Initialize Locale if set and configured.
|
||||
if ($this['language']->enabled() && $this['config']->get('system.languages.override_locale')) {
|
||||
$language = $this['language']->getLanguage();
|
||||
setlocale(LC_ALL, \strlen($language) < 3 ? ($language . '_' . strtoupper($language)) : $language);
|
||||
} elseif ($this['config']->get('system.default_locale')) {
|
||||
setlocale(LC_ALL, $this['config']->get('system.default_locale'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires an event with optional parameters.
|
||||
*
|
||||
* @param string $eventName
|
||||
* @param Event $event
|
||||
* @param Event|null $event
|
||||
*
|
||||
* @return Event
|
||||
*/
|
||||
public function fireEvent($eventName, Event $event = null)
|
||||
{
|
||||
/** @var EventDispatcher $events */
|
||||
/** @var EventDispatcherInterface $events */
|
||||
$events = $this['events'];
|
||||
if (null === $event) {
|
||||
$event = new Event();
|
||||
}
|
||||
|
||||
return $events->dispatch($eventName, $event);
|
||||
/** @var Debugger $debugger */
|
||||
$debugger = $this['debugger'];
|
||||
$debugger->addEvent($eventName, $event, $events);
|
||||
|
||||
$events->dispatch($event, $eventName);
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -394,7 +455,6 @@ class Grav extends Container
|
||||
if ($this['config']->get('system.cache.gzip')) {
|
||||
// Flush gzhandler buffer if gzip setting was enabled.
|
||||
ob_end_flush();
|
||||
|
||||
} else {
|
||||
// Without gzip we have no other choice than to prevent server from compressing the output.
|
||||
// This action turns off mod_deflate which would prevent us from closing the connection.
|
||||
@@ -403,7 +463,6 @@ class Grav extends Container
|
||||
} else {
|
||||
header('Content-Encoding: none');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -470,9 +529,7 @@ class Grav extends Container
|
||||
return $container;
|
||||
};
|
||||
|
||||
$container->measureTime('_services', 'Services', function () use ($container) {
|
||||
$container->registerServices();
|
||||
});
|
||||
$container->registerServices();
|
||||
|
||||
return $container;
|
||||
}
|
||||
@@ -528,7 +585,7 @@ class Grav extends Container
|
||||
$path_parts = pathinfo($path);
|
||||
|
||||
/** @var PageInterface $page */
|
||||
$page = $this['pages']->dispatch($path_parts['dirname'], true);
|
||||
$page = $this['pages']->find($path_parts['dirname'], true);
|
||||
|
||||
if ($page) {
|
||||
$media = $page->media()->all();
|
||||
|
||||
@@ -33,7 +33,8 @@ class Base32
|
||||
*/
|
||||
public static function encode($bytes)
|
||||
{
|
||||
$i = 0; $index = 0;
|
||||
$i = 0;
|
||||
$index = 0;
|
||||
$base32 = '';
|
||||
$bytesLen = \strlen($bytes);
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class Excerpts
|
||||
$excerpt = static::processLinkExcerpt($excerpt, $page, 'image');
|
||||
|
||||
$excerpt['element']['attributes']['src'] = $excerpt['element']['attributes']['href'];
|
||||
unset ($excerpt['element']['attributes']['href']);
|
||||
unset($excerpt['element']['attributes']['href']);
|
||||
|
||||
$excerpt = static::processImageExcerpt($excerpt, $page);
|
||||
|
||||
|
||||
@@ -41,15 +41,20 @@ class LogViewer
|
||||
* @param int $lines
|
||||
* @return bool|string
|
||||
*/
|
||||
public function tail($filepath, $lines = 1) {
|
||||
public function tail($filepath, $lines = 1)
|
||||
{
|
||||
|
||||
$f = @fopen($filepath, "rb");
|
||||
if ($f === false) return false;
|
||||
|
||||
else $buffer = ($lines < 2 ? 64 : ($lines < 10 ? 512 : 4096));
|
||||
if ($f === false) {
|
||||
return false;
|
||||
} else {
|
||||
$buffer = ($lines < 2 ? 64 : ($lines < 10 ? 512 : 4096));
|
||||
}
|
||||
|
||||
fseek($f, -1, SEEK_END);
|
||||
if (fread($f, 1) != "\n") $lines -= 1;
|
||||
if (fread($f, 1) != "\n") {
|
||||
$lines -= 1;
|
||||
}
|
||||
|
||||
// Start reading
|
||||
$output = '';
|
||||
@@ -108,7 +113,7 @@ class LogViewer
|
||||
*/
|
||||
public function parse($line)
|
||||
{
|
||||
if( !is_string($line) || strlen($line) === 0) {
|
||||
if (!is_string($line) || strlen($line) === 0) {
|
||||
return array();
|
||||
}
|
||||
preg_match($this->pattern, $line, $data);
|
||||
@@ -145,5 +150,4 @@ class LogViewer
|
||||
$lines = array_filter(preg_split('/#\d*/m', $trace));
|
||||
return array_slice($lines, 0, $rows);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@ use DOMLettersIterator;
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
class Truncator {
|
||||
class Truncator
|
||||
{
|
||||
|
||||
/**
|
||||
* Safely truncates HTML by a given number of words.
|
||||
@@ -49,10 +50,8 @@ class Truncator {
|
||||
$words = new DOMWordsIterator($container);
|
||||
$truncated = false;
|
||||
foreach ($words as $word) {
|
||||
|
||||
// If we have exceeded the limit, we delete the remainder of the content.
|
||||
if ($words->key() >= $limit) {
|
||||
|
||||
// Grab current position.
|
||||
$currentWordPosition = $words->currentWordPosition();
|
||||
$curNode = $currentWordPosition[0];
|
||||
@@ -75,7 +74,6 @@ class Truncator {
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Return original HTML if not truncated.
|
||||
@@ -107,10 +105,8 @@ class Truncator {
|
||||
$letters = new DOMLettersIterator($container);
|
||||
$truncated = false;
|
||||
foreach ($letters as $letter) {
|
||||
|
||||
// If we have exceeded the limit, we want to delete the remainder of this document.
|
||||
if ($letters->key() >= $limit) {
|
||||
|
||||
$currentText = $letters->currentTextPosition();
|
||||
$currentText[0]->nodeValue = mb_substr($currentText[0]->nodeValue, 0, $currentText[1] + 1);
|
||||
self::removeProceedingNodes($currentText[0], $container);
|
||||
@@ -136,7 +132,7 @@ class Truncator {
|
||||
/**
|
||||
* Builds a DOMDocument object from a string containing HTML.
|
||||
* @param string $html HTML to load
|
||||
* @returns DOMDocument Returns a DOMDocument object.
|
||||
* @return DOMDocument Returns a DOMDocument object.
|
||||
*/
|
||||
public static function htmlToDomDocument($html)
|
||||
{
|
||||
@@ -190,7 +186,7 @@ class Truncator {
|
||||
* Clean extra code
|
||||
*
|
||||
* @param DOMDocument $doc
|
||||
* @param $container
|
||||
* @param DOMDocument $container
|
||||
* @return string
|
||||
*/
|
||||
private static function getCleanedHTML(DOMDocument $doc, $container)
|
||||
@@ -199,12 +195,11 @@ class Truncator {
|
||||
$doc->removeChild($doc->firstChild);
|
||||
}
|
||||
|
||||
while ($container->firstChild ) {
|
||||
while ($container->firstChild) {
|
||||
$doc->appendChild($container->firstChild);
|
||||
}
|
||||
|
||||
$html = trim($doc->saveHTML());
|
||||
return $html;
|
||||
return trim($doc->saveHTML());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +221,6 @@ class Truncator {
|
||||
} else {
|
||||
$domNode->parentNode->parentNode->appendChild($textNode);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Append to current node
|
||||
$domNode->nodeValue = rtrim($domNode->nodeValue) . $ellipsis;
|
||||
@@ -248,11 +242,13 @@ class Truncator {
|
||||
if (strlen(preg_replace('/<.*?>/', '', $text)) <= $length) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
// splits all html-tags to scanable lines
|
||||
preg_match_all('/(<.+?>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER);
|
||||
$total_length = strlen($ending);
|
||||
$open_tags = array();
|
||||
$truncate = '';
|
||||
$open_tags = [];
|
||||
|
||||
foreach ($lines as $line_matchings) {
|
||||
// if there is any html-tag in this line, handle it and add it (uncounted) to the output
|
||||
if (!empty($line_matchings[1])) {
|
||||
@@ -260,14 +256,14 @@ class Truncator {
|
||||
if (preg_match('/^<(\s*.+?\/\s*|\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\s.+?)?)>$/is', $line_matchings[1])) {
|
||||
// do nothing
|
||||
// if tag is a closing tag
|
||||
} else if (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $line_matchings[1], $tag_matchings)) {
|
||||
} elseif (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $line_matchings[1], $tag_matchings)) {
|
||||
// delete tag from $open_tags list
|
||||
$pos = array_search($tag_matchings[1], $open_tags);
|
||||
if ($pos !== false) {
|
||||
unset($open_tags[$pos]);
|
||||
}
|
||||
// if tag is an opening tag
|
||||
} else if (preg_match('/^<\s*([^\s>!]+).*?>$/s', $line_matchings[1], $tag_matchings)) {
|
||||
} elseif (preg_match('/^<\s*([^\s>!]+).*?>$/s', $line_matchings[1], $tag_matchings)) {
|
||||
// add tag to the beginning of $open_tags list
|
||||
array_unshift($open_tags, strtolower($tag_matchings[1]));
|
||||
}
|
||||
@@ -301,29 +297,29 @@ class Truncator {
|
||||
$total_length += $content_length;
|
||||
}
|
||||
// if the maximum length is reached, get off the loop
|
||||
if($total_length>= $length) {
|
||||
if ($total_length>= $length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (strlen($text) <= $length) {
|
||||
return $text;
|
||||
} else {
|
||||
$truncate = substr($text, 0, $length - strlen($ending));
|
||||
}
|
||||
|
||||
$truncate = substr($text, 0, $length - strlen($ending));
|
||||
}
|
||||
// if the words shouldn't be cut in the middle...
|
||||
if (!$exact) {
|
||||
// ...search the last occurance of a space...
|
||||
$spacepos = strrpos($truncate, ' ');
|
||||
if (isset($spacepos)) {
|
||||
if (false !== $spacepos) {
|
||||
// ...and cut the text in this position
|
||||
$truncate = substr($truncate, 0, $spacepos);
|
||||
}
|
||||
}
|
||||
// add the defined ending to the text
|
||||
$truncate .= $ending;
|
||||
if($considerHtml) {
|
||||
if (isset($open_tags)) {
|
||||
// close all unclosed html-tags
|
||||
foreach ($open_tags as $tag) {
|
||||
$truncate .= '</' . $tag . '>';
|
||||
@@ -331,5 +327,4 @@ class Truncator {
|
||||
}
|
||||
return $truncate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,13 +16,15 @@ use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class YamlLinter
|
||||
{
|
||||
public static function lint()
|
||||
public static function lint(string $folder = null)
|
||||
{
|
||||
$errors = static::lintConfig();
|
||||
$errors = $errors + static::lintPages();
|
||||
$errors = $errors + static::lintBlueprints();
|
||||
|
||||
return $errors;
|
||||
if (null !== $folder) {
|
||||
$folder = $folder ?: GRAV_ROOT;
|
||||
|
||||
return static::recurseFolder($folder);
|
||||
}
|
||||
|
||||
return array_merge(static::lintConfig(), static::lintPages(), static::lintBlueprints());
|
||||
}
|
||||
|
||||
public static function lintPages()
|
||||
@@ -47,7 +49,7 @@ class YamlLinter
|
||||
return static::recurseFolder('blueprints://');
|
||||
}
|
||||
|
||||
public static function recurseFolder($path, $extensions = 'md|yaml')
|
||||
public static function recurseFolder($path, $extensions = '(md|yaml)')
|
||||
{
|
||||
$lint_errors = [];
|
||||
|
||||
@@ -85,5 +87,4 @@ class YamlLinter
|
||||
}
|
||||
return $contents;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Language\Language;
|
||||
|
||||
/**
|
||||
* This file was originally part of the Akelos Framework
|
||||
*/
|
||||
@@ -24,6 +26,7 @@ class Inflector
|
||||
public static function init()
|
||||
{
|
||||
if (empty(static::$plural)) {
|
||||
/** @var Language $language */
|
||||
$language = Grav::instance()['language'];
|
||||
static::$plural = $language->translate('GRAV.INFLECTOR_PLURALS', null, true) ?: [];
|
||||
static::$singular = $language->translate('GRAV.INFLECTOR_SINGULAR', null, true) ?: [];
|
||||
@@ -70,7 +73,6 @@ class Inflector
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,6 +195,8 @@ class Inflector
|
||||
$regex3 = preg_replace('/([0-9])([A-Z])/', '\1-\2', $regex2);
|
||||
$regex4 = preg_replace('/[^A-Z^a-z^0-9]+/', '-', $regex3);
|
||||
|
||||
$regex4 = trim($regex4, '-');
|
||||
|
||||
return strtolower($regex4);
|
||||
}
|
||||
|
||||
|
||||
@@ -228,8 +228,7 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
|
||||
public function filter(callable $callback = null)
|
||||
{
|
||||
foreach ($this->items as $key => $value) {
|
||||
if (
|
||||
(!$callback && !(bool)$value) ||
|
||||
if ((!$callback && !(bool)$value) ||
|
||||
($callback && !$callback($value, $key))
|
||||
) {
|
||||
unset($this->items[$key]);
|
||||
@@ -248,7 +247,6 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
|
||||
* @param bool $desc
|
||||
*
|
||||
* @return $this|array
|
||||
* @internal param bool $asc
|
||||
*
|
||||
*/
|
||||
public function sort(callable $callback = null, $desc = false)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace Grav\Common\Language;
|
||||
|
||||
use Grav\Common\Debugger;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Config\Config;
|
||||
use Negotiation\AcceptLanguage;
|
||||
@@ -16,20 +17,22 @@ use Negotiation\LanguageNegotiator;
|
||||
|
||||
class Language
|
||||
{
|
||||
/** @var Grav */
|
||||
protected $grav;
|
||||
protected $enabled = true;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $languages = [];
|
||||
protected $page_extensions = [];
|
||||
protected $fallback_languages = [];
|
||||
protected $default;
|
||||
protected $active = null;
|
||||
|
||||
/** @var Config $config */
|
||||
/** @var Config */
|
||||
protected $config;
|
||||
|
||||
protected $enabled = true;
|
||||
|
||||
/** @var array */
|
||||
protected $languages = [];
|
||||
protected $fallback_languages = [];
|
||||
protected $fallback_extensions = [];
|
||||
protected $page_extesions = [];
|
||||
protected $default;
|
||||
protected $active;
|
||||
|
||||
protected $http_accept_language;
|
||||
protected $lang_in_url = false;
|
||||
|
||||
@@ -42,7 +45,12 @@ class Language
|
||||
{
|
||||
$this->grav = $grav;
|
||||
$this->config = $grav['config'];
|
||||
$this->languages = $this->config->get('system.languages.supported', []);
|
||||
$languages = $this->config->get('system.languages.supported', []);
|
||||
foreach ($languages as &$language) {
|
||||
$language = (string)$language;
|
||||
}
|
||||
$this->languages = $languages;
|
||||
|
||||
$this->init();
|
||||
}
|
||||
|
||||
@@ -58,7 +66,7 @@ class Language
|
||||
$this->default = reset($this->languages);
|
||||
}
|
||||
|
||||
$this->page_extensions = null;
|
||||
$this->resetFallbackPageExtensions();
|
||||
|
||||
if (empty($this->languages)) {
|
||||
$this->enabled = false;
|
||||
@@ -93,20 +101,22 @@ class Language
|
||||
public function setLanguages($langs)
|
||||
{
|
||||
$this->languages = $langs;
|
||||
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a pipe-separated string of available languages
|
||||
*
|
||||
* @param string|null $delimiter Delimiter to be quoted.
|
||||
* @return string
|
||||
*/
|
||||
public function getAvailable()
|
||||
public function getAvailable($delimiter = null)
|
||||
{
|
||||
$languagesArray = $this->languages; //Make local copy
|
||||
|
||||
$languagesArray = array_map(function($value) {
|
||||
return preg_quote($value);
|
||||
$languagesArray = array_map(function ($value) use ($delimiter) {
|
||||
return preg_quote($value, $delimiter);
|
||||
}, $languagesArray);
|
||||
|
||||
sort($languagesArray);
|
||||
@@ -143,6 +153,7 @@ class Language
|
||||
*/
|
||||
public function setDefault($lang)
|
||||
{
|
||||
$lang = (string)$lang;
|
||||
if ($this->validate($lang)) {
|
||||
$this->default = $lang;
|
||||
|
||||
@@ -171,7 +182,12 @@ class Language
|
||||
*/
|
||||
public function setActive($lang)
|
||||
{
|
||||
$lang = (string)$lang;
|
||||
if ($this->validate($lang)) {
|
||||
/** @var Debugger $debugger */
|
||||
$debugger = $this->grav['debugger'];
|
||||
$debugger->addMessage('Active language set to ' . $lang, 'debug');
|
||||
|
||||
$this->active = $lang;
|
||||
|
||||
return $lang;
|
||||
@@ -196,7 +212,7 @@ class Language
|
||||
// Try setting language from prefix of URL (/en/blah/blah).
|
||||
if (preg_match($regex, $uri, $matches)) {
|
||||
$this->lang_in_url = true;
|
||||
$this->active = $matches[2];
|
||||
$this->setActive($matches[2]);
|
||||
$uri = preg_replace("/\\" . $matches[1] . '/', '', $uri, 1);
|
||||
|
||||
// Store in session if language is different.
|
||||
@@ -210,22 +226,20 @@ class Language
|
||||
// Try getting language from the session, else no active.
|
||||
if (isset($this->grav['session']) && $this->grav['session']->isStarted() &&
|
||||
$this->config->get('system.languages.session_store_active', true)) {
|
||||
$this->active = $this->grav['session']->active_language ?: null;
|
||||
$this->setActive($this->grav['session']->active_language ?: null);
|
||||
}
|
||||
// if still null, try from http_accept_language header
|
||||
if ($this->active === null &&
|
||||
$this->config->get('system.languages.http_accept_language') &&
|
||||
$accept = $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? false) {
|
||||
|
||||
$negotiator = new LanguageNegotiator();
|
||||
$best_language = $negotiator->getBest($accept, $this->languages);
|
||||
|
||||
if ($best_language instanceof AcceptLanguage) {
|
||||
$this->active = $best_language->getType();
|
||||
$this->setActive($best_language->getType());
|
||||
} else {
|
||||
$this->active = $this->getDefault();
|
||||
$this->setActive($this->getDefault());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -275,52 +289,67 @@ class Language
|
||||
return (bool) $this->lang_in_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get full list of used language page extensions: [''=>'.md', 'en'=>'.en.md', ...]
|
||||
*
|
||||
* @param string|null $fileExtension
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPageExtensions($fileExtension = null)
|
||||
{
|
||||
$fileExtension = $fileExtension ?: CONTENT_EXT;
|
||||
|
||||
if (!isset($this->fallback_extensions[$fileExtension])) {
|
||||
$extensions[''] = $fileExtension;
|
||||
foreach ($this->languages as $code) {
|
||||
$extensions[$code] = ".{$code}{$fileExtension}";
|
||||
}
|
||||
|
||||
$this->fallback_extensions[$fileExtension] = $extensions;
|
||||
}
|
||||
|
||||
return $this->fallback_extensions[$fileExtension];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of valid extensions with active first, then fallback extensions
|
||||
*
|
||||
* @param string|null $file_ext
|
||||
*
|
||||
* @return array
|
||||
* @param string|null $fileExtension
|
||||
* @param string|null $languageCode
|
||||
* @param bool $assoc Return values in ['en' => '.en.md', ...] format.
|
||||
* @return array Key is the language code, value is the file extension to be used.
|
||||
*/
|
||||
public function getFallbackPageExtensions($file_ext = null)
|
||||
public function getFallbackPageExtensions(string $fileExtension = null, string $languageCode = null, bool $assoc = false)
|
||||
{
|
||||
if (empty($this->page_extensions)) {
|
||||
if (!$file_ext) {
|
||||
$file_ext = CONTENT_EXT;
|
||||
$fileExtension = $fileExtension ?: CONTENT_EXT;
|
||||
$key = $fileExtension . '-' . ($languageCode ?? 'default') . '-' . (int)$assoc;
|
||||
|
||||
if (!isset($this->fallback_extensions[$key])) {
|
||||
$all = $this->getPageExtensions($fileExtension);
|
||||
$list = [];
|
||||
$fallback = $this->getFallbackLanguages($languageCode, true);
|
||||
foreach ($fallback as $code) {
|
||||
$ext = $all[$code] ?? null;
|
||||
if (null !== $ext) {
|
||||
$list[$code] = $ext;
|
||||
}
|
||||
}
|
||||
if (!$assoc) {
|
||||
$list = array_values($list);
|
||||
}
|
||||
|
||||
if ($this->enabled()) {
|
||||
$valid_lang_extensions = [];
|
||||
foreach ($this->languages as $lang) {
|
||||
$valid_lang_extensions[] = '.' . $lang . $file_ext;
|
||||
}
|
||||
$this->fallback_extensions[$key] = $list;
|
||||
|
||||
if ($this->active) {
|
||||
$active_extension = '.' . $this->active . $file_ext;
|
||||
$key = \array_search($active_extension, $valid_lang_extensions, true);
|
||||
|
||||
// Default behavior is to find any language other than active
|
||||
if ($this->config->get('system.languages.pages_fallback_only')) {
|
||||
$slice = \array_slice($valid_lang_extensions, 0, $key+1);
|
||||
$valid_lang_extensions = array_reverse($slice);
|
||||
} else {
|
||||
unset($valid_lang_extensions[$key]);
|
||||
array_unshift($valid_lang_extensions, $active_extension);
|
||||
}
|
||||
}
|
||||
$valid_lang_extensions[] = $file_ext;
|
||||
$this->page_extensions = $valid_lang_extensions;
|
||||
} else {
|
||||
$this->page_extensions = (array)$file_ext;
|
||||
}
|
||||
/** @var Debugger $debugger */
|
||||
//$debugger = $this->grav['debugger'];
|
||||
//$debugger->addMessage("Language fallback extensions for {$languageCode}", 'debug', $list);
|
||||
}
|
||||
|
||||
return $this->page_extensions;
|
||||
return $this->fallback_extensions[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the page_extensions value.
|
||||
* Resets the fallback_languages value.
|
||||
*
|
||||
* Useful to re-initialize the pages and change site language at runtime, example:
|
||||
*
|
||||
@@ -332,33 +361,76 @@ class Language
|
||||
*/
|
||||
public function resetFallbackPageExtensions()
|
||||
{
|
||||
$this->page_extensions = null;
|
||||
$this->fallback_languages = [];
|
||||
$this->fallback_extensions = [];
|
||||
$this->page_extesions = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of languages with active first, then fallback languages
|
||||
* Gets an array of languages with active first, then fallback languages.
|
||||
*
|
||||
*
|
||||
* @param string|null $languageCode
|
||||
* @param bool $includeDefault If true, list contains '', which can be used for default
|
||||
* @return array
|
||||
*/
|
||||
public function getFallbackLanguages()
|
||||
public function getFallbackLanguages(string $languageCode = null, bool $includeDefault = false)
|
||||
{
|
||||
if (empty($this->fallback_languages)) {
|
||||
if ($this->enabled()) {
|
||||
$fallback_languages = $this->languages;
|
||||
|
||||
if ($this->active) {
|
||||
$active_extension = $this->active;
|
||||
$key = \array_search($active_extension, $fallback_languages, true);
|
||||
unset($fallback_languages[$key]);
|
||||
array_unshift($fallback_languages, $active_extension);
|
||||
}
|
||||
$this->fallback_languages = $fallback_languages;
|
||||
}
|
||||
// always add english in case a translation doesn't exist
|
||||
$this->fallback_languages[] = 'en';
|
||||
// Handle default.
|
||||
if ($languageCode === '' || !$this->enabled()) {
|
||||
return [''];
|
||||
}
|
||||
|
||||
return $this->fallback_languages;
|
||||
$default = $this->getDefault() ?? 'en';
|
||||
$active = $languageCode ?? $this->getActive() ?? $default;
|
||||
$key = $active . '-' . (int)$includeDefault;
|
||||
|
||||
if (!isset($this->fallback_languages[$key])) {
|
||||
$fallback = $this->config->get('system.languages.content_fallback.' . $active);
|
||||
$fallback_languages = [];
|
||||
|
||||
if (null === $fallback && $this->config->get('system.languages.pages_fallback_only', false)) {
|
||||
// Special fallback list returns itself and all the previous items in reverse order:
|
||||
// active: 'v2', languages: ['v1', 'v2', 'v3', 'v4'] => ['v2', 'v1', '']
|
||||
if ($includeDefault) {
|
||||
$fallback_languages[''] = '';
|
||||
}
|
||||
foreach ($this->languages as $code) {
|
||||
$fallback_languages[$code] = $code;
|
||||
if ($code === $active) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$fallback_languages = array_reverse($fallback_languages);
|
||||
} else {
|
||||
if (null === $fallback) {
|
||||
$fallback = [$default];
|
||||
} elseif (!is_array($fallback)) {
|
||||
$fallback = is_string($fallback) && $fallback !== '' ? explode(',', $fallback) : [];
|
||||
}
|
||||
array_unshift($fallback, $active);
|
||||
$fallback = array_unique($fallback);
|
||||
|
||||
foreach ($fallback as $code) {
|
||||
// Default fallback list has active language followed by default language and extensionless file:
|
||||
// active: 'fi', default: 'en', languages: ['sv', 'en', 'de', 'fi'] => ['fi', 'en', '']
|
||||
$fallback_languages[$code] = $code;
|
||||
if ($includeDefault && $code === $default) {
|
||||
$fallback_languages[''] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$fallback_languages = array_values($fallback_languages);
|
||||
|
||||
$this->fallback_languages[$key] = $fallback_languages;
|
||||
|
||||
/** @var Debugger $debugger */
|
||||
//$debugger = $this->grav['debugger'];
|
||||
//$debugger->addMessage("Language fallback for {$active}", 'debug', $fallback_languages);
|
||||
}
|
||||
|
||||
return $this->fallback_languages[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,7 +450,7 @@ class Language
|
||||
*
|
||||
* @param string|array $args The first argument is the lookup key value
|
||||
* Other arguments can be passed and replaced in the translation with sprintf syntax
|
||||
* @param array $languages
|
||||
* @param array|null $languages
|
||||
* @param bool $array_support
|
||||
* @param bool $html_out
|
||||
*
|
||||
@@ -394,18 +466,12 @@ class Language
|
||||
}
|
||||
|
||||
if ($this->config->get('system.languages.translations', true)) {
|
||||
if ($this->enabled() && $lookup) {
|
||||
if (empty($languages)) {
|
||||
if ($this->config->get('system.languages.translations_fallback', true)) {
|
||||
$languages = $this->getFallbackLanguages();
|
||||
} else {
|
||||
$languages = (array)$this->getLanguage();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$languages = ['en'];
|
||||
if ($this->enabled() && $lookup && empty($languages)) {
|
||||
$languages = $this->getTranslatedLanguages();
|
||||
}
|
||||
|
||||
$languages = $languages ?: ['en'];
|
||||
|
||||
foreach ((array)$languages as $lang) {
|
||||
$translation = $this->getTranslation($lang, $lookup, $array_support);
|
||||
|
||||
@@ -439,18 +505,12 @@ class Language
|
||||
public function translateArray($key, $index, $languages = null, $html_out = false)
|
||||
{
|
||||
if ($this->config->get('system.languages.translations', true)) {
|
||||
if ($this->enabled() && $key) {
|
||||
if (empty($languages)) {
|
||||
if ($this->config->get('system.languages.translations_fallback', true)) {
|
||||
$languages = $this->getFallbackLanguages();
|
||||
} else {
|
||||
$languages = (array)$this->getDefault();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$languages = ['en'];
|
||||
if ($this->enabled() && $key && empty($languages)) {
|
||||
$languages = $this->getTranslatedLanguages();
|
||||
}
|
||||
|
||||
$languages = $languages ?: ['en'];
|
||||
|
||||
foreach ((array)$languages as $lang) {
|
||||
$translation_array = (array)Grav::instance()['languages']->get($lang . '.' . $key, null);
|
||||
if ($translation_array && array_key_exists($index, $translation_array)) {
|
||||
@@ -534,4 +594,27 @@ class Language
|
||||
return LanguageCodes::get($code, $type);
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$vars = get_object_vars($this);
|
||||
unset($vars['grav'], $vars['config']);
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getTranslatedLanguages(): array
|
||||
{
|
||||
if ($this->config->get('system.languages.translations_fallback', true)) {
|
||||
$languages = $this->getFallbackLanguages();
|
||||
} else {
|
||||
$languages = [$this->getLanguage()];
|
||||
}
|
||||
|
||||
$languages[] = 'en';
|
||||
|
||||
return array_values(array_unique($languages));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,4 +202,13 @@ class LanguageCodes
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function getList($native = true)
|
||||
{
|
||||
$list = [];
|
||||
foreach (static::$codes as $key => $names) {
|
||||
$list[$key] = $native ? $names['nativeName'] : $names['name'];
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,5 +35,4 @@ class Parsedown extends \Parsedown
|
||||
|
||||
$this->init($excerpts, $defaults);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,4 +20,25 @@ interface MediaCollectionInterface extends \Grav\Framework\Media\Interfaces\Medi
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPath();
|
||||
|
||||
/**
|
||||
* Get a list of all media.
|
||||
*
|
||||
* @return MediaObjectInterface[]
|
||||
*/
|
||||
public function all();
|
||||
|
||||
/**
|
||||
* Set file modification timestamps (query params) for all the media files.
|
||||
*
|
||||
* @param string|int|null $timestamp
|
||||
* @return $this
|
||||
*/
|
||||
public function setTimestamps($timestamp = null);
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param MediaObjectInterface $file
|
||||
*/
|
||||
public function add($name, $file);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
trait MediaTrait
|
||||
{
|
||||
protected $media;
|
||||
protected $_loadMedia = true;
|
||||
|
||||
/**
|
||||
* Get filesystem path to the associated media.
|
||||
@@ -40,30 +41,33 @@ trait MediaTrait
|
||||
/**
|
||||
* Get URI ot the associated media. Method will return null if path isn't URI.
|
||||
*
|
||||
* @return null|string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getMediaUri()
|
||||
{
|
||||
$folder = $this->getMediaFolder();
|
||||
$folder = $this->getMediaFolder();
|
||||
if (!$folder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (strpos($folder, '://')) {
|
||||
return $folder;
|
||||
}
|
||||
if (strpos($folder, '://')) {
|
||||
return $folder;
|
||||
}
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
$user = $locator->findResource('user://');
|
||||
if (strpos($folder, $user) === 0) {
|
||||
return 'user://' . substr($folder, \strlen($user)+1);
|
||||
}
|
||||
$locator = Grav::instance()['locator'];
|
||||
$user = $locator->findResource('user://');
|
||||
if (strpos($folder, $user) === 0) {
|
||||
return 'user://' . substr($folder, \strlen($user)+1);
|
||||
}
|
||||
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the associated media collection.
|
||||
*
|
||||
* @return MediaCollectionInterface Representation of associated media.
|
||||
* @return MediaCollectionInterface|Media Representation of associated media.
|
||||
*/
|
||||
public function getMedia()
|
||||
{
|
||||
@@ -73,7 +77,7 @@ trait MediaTrait
|
||||
// Use cached media if possible.
|
||||
$cacheKey = md5('media' . $this->getCacheKey());
|
||||
if (!$media = $cache->get($cacheKey)) {
|
||||
$media = new Media($this->getMediaFolder(), $this->getMediaOrder());
|
||||
$media = new Media($this->getMediaFolder(), $this->getMediaOrder(), $this->_loadMedia);
|
||||
$cache->set($cacheKey, $media);
|
||||
}
|
||||
$this->media = $media;
|
||||
@@ -85,7 +89,7 @@ trait MediaTrait
|
||||
/**
|
||||
* Sets the associated media collection.
|
||||
*
|
||||
* @param MediaCollectionInterface $media Representation of associated media.
|
||||
* @param MediaCollectionInterface|Media $media Representation of associated media.
|
||||
* @return $this
|
||||
*/
|
||||
protected function setMedia(MediaCollectionInterface $media)
|
||||
@@ -125,5 +129,5 @@ trait MediaTrait
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getCacheKey();
|
||||
abstract protected function getCacheKey(): string;
|
||||
}
|
||||
|
||||
@@ -11,10 +11,11 @@ namespace Grav\Common\Page;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Iterator;
|
||||
use Grav\Common\Page\Interfaces\PageCollectionInterface;
|
||||
use Grav\Common\Page\Interfaces\PageInterface;
|
||||
use Grav\Common\Utils;
|
||||
|
||||
class Collection extends Iterator
|
||||
class Collection extends Iterator implements PageCollectionInterface
|
||||
{
|
||||
/**
|
||||
* @var Pages
|
||||
@@ -51,6 +52,20 @@ class Collection extends Iterator
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set parameters to the Collection
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setParams(array $params)
|
||||
{
|
||||
$this->params = array_merge($this->params, $params);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single page to a collection
|
||||
*
|
||||
@@ -94,12 +109,12 @@ class Collection extends Iterator
|
||||
*
|
||||
* Merge another collection with the current collection
|
||||
*
|
||||
* @param Collection $collection
|
||||
* @param PageCollectionInterface $collection
|
||||
* @return $this
|
||||
*/
|
||||
public function merge(Collection $collection)
|
||||
public function merge(PageCollectionInterface $collection)
|
||||
{
|
||||
foreach($collection as $page) {
|
||||
foreach ($collection as $page) {
|
||||
$this->addPage($page);
|
||||
}
|
||||
|
||||
@@ -109,35 +124,21 @@ class Collection extends Iterator
|
||||
/**
|
||||
* Intersect another collection with the current collection
|
||||
*
|
||||
* @param Collection $collection
|
||||
* @param PageCollectionInterface $collection
|
||||
* @return $this
|
||||
*/
|
||||
public function intersect(Collection $collection)
|
||||
public function intersect(PageCollectionInterface $collection)
|
||||
{
|
||||
$array1 = $this->items;
|
||||
$array2 = $collection->toArray();
|
||||
|
||||
$this->items = array_uintersect($array1, $array2, function($val1, $val2) {
|
||||
$this->items = array_uintersect($array1, $array2, function ($val1, $val2) {
|
||||
return strcmp($val1['slug'], $val2['slug']);
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set parameters to the Collection
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setParams(array $params)
|
||||
{
|
||||
$this->params = array_merge($this->params, $params);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current page.
|
||||
*
|
||||
@@ -240,7 +241,7 @@ class Collection extends Iterator
|
||||
*
|
||||
* @return bool True if item is first.
|
||||
*/
|
||||
public function isFirst($path)
|
||||
public function isFirst($path): bool
|
||||
{
|
||||
return $this->items && $path === array_keys($this->items)[0];
|
||||
}
|
||||
@@ -252,7 +253,7 @@ class Collection extends Iterator
|
||||
*
|
||||
* @return bool True if item is last.
|
||||
*/
|
||||
public function isLast($path)
|
||||
public function isLast($path): bool
|
||||
{
|
||||
return $this->items && $path === array_keys($this->items)[\count($this->items) - 1];
|
||||
}
|
||||
@@ -301,7 +302,6 @@ class Collection extends Iterator
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -309,11 +309,13 @@ class Collection extends Iterator
|
||||
*
|
||||
* @param string $path the path the item
|
||||
*
|
||||
* @return int the index of the current page.
|
||||
* @return int|null The index of the current page, null if not found.
|
||||
*/
|
||||
public function currentPosition($path)
|
||||
public function currentPosition($path): ?int
|
||||
{
|
||||
return \array_search($path, \array_keys($this->items), true);
|
||||
$pos = \array_search($path, \array_keys($this->items), true);
|
||||
|
||||
return $pos !== false ? $pos : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -600,7 +602,6 @@ class Collection extends Iterator
|
||||
$items[$path] = $slug;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
476
system/src/Grav/Common/Page/Flex/PageCollection.php
Normal file
476
system/src/Grav/Common/Page/Flex/PageCollection.php
Normal file
@@ -0,0 +1,476 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @package Grav\Common\Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Flex;
|
||||
|
||||
use Grav\Common\Page\Interfaces\PageCollectionInterface;
|
||||
use Grav\Common\Page\Interfaces\PageInterface;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Framework\Flex\Interfaces\FlexObjectInterface;
|
||||
use Grav\Framework\Flex\Pages\FlexPageCollection;
|
||||
|
||||
/**
|
||||
* Class GravPageCollection
|
||||
* @package Grav\Plugin\FlexObjects\Types\GravPages
|
||||
*
|
||||
* Incompatibilities with Grav\Common\Page\Collection:
|
||||
* $page = $collection->key() will not work at all
|
||||
* $clone = clone $collection does not clone objects inside the collection, does it matter?
|
||||
* $string = (string)$collection returns collection id instead of comma separated list
|
||||
* $collection->add() incompatible method signature
|
||||
* $collection->remove() incompatible method signature
|
||||
* $collection->filter() incompatible method signature (takes closure instead of callable)
|
||||
* $collection->prev() does not rewind the internal pointer
|
||||
* AND most methods are immutable; they do not update the current collection, but return updated one
|
||||
*/
|
||||
class PageCollection extends FlexPageCollection implements PageCollectionInterface
|
||||
{
|
||||
protected $_params;
|
||||
|
||||
/**
|
||||
* @return PageInterface
|
||||
*/
|
||||
public function getRoot()
|
||||
{
|
||||
/** @var PageIndex $index */
|
||||
$index = $this->getIndex();
|
||||
|
||||
return $index->getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the collection params
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getParams(): array
|
||||
{
|
||||
return $this->_params ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set parameters to the Collection
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setParams(array $params)
|
||||
{
|
||||
$this->_params = $this->_params ? array_merge($this->_params, $params) : $params;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the collection params
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function params(): array
|
||||
{
|
||||
return $this->getParams();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single page to a collection
|
||||
*
|
||||
* @param PageInterface $page
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function addPage(PageInterface $page)
|
||||
{
|
||||
if (!$page instanceof FlexObjectInterface) {
|
||||
throw new \InvalidArgumentException('$page is not a flex page.');
|
||||
}
|
||||
|
||||
// FIXME: support other keys.
|
||||
$this->set($page->getKey(), $page);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Merge another collection with the current collection
|
||||
*
|
||||
* @param PageCollectionInterface $collection
|
||||
* @return static
|
||||
*/
|
||||
public function merge(PageCollectionInterface $collection)
|
||||
{
|
||||
throw new \RuntimeException(__METHOD__ . '(): Not Implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersect another collection with the current collection
|
||||
*
|
||||
* @param PageCollectionInterface $collection
|
||||
* @return static
|
||||
*/
|
||||
public function intersect(PageCollectionInterface $collection)
|
||||
{
|
||||
throw new \RuntimeException(__METHOD__ . '(): Not Implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return previous item.
|
||||
*
|
||||
* @return PageInterface|false
|
||||
*/
|
||||
public function prev()
|
||||
{
|
||||
// FIXME: this method does not rewind the internal pointer!
|
||||
$key = $this->key();
|
||||
$prev = $this->prevSibling($key);
|
||||
|
||||
return $prev !== $this->current() ? $prev : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return nth item.
|
||||
* @param int $key
|
||||
*
|
||||
* @return PageInterface|bool
|
||||
*/
|
||||
public function nth($key)
|
||||
{
|
||||
return $this->slice($key, 1)[0] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick one or more random entries.
|
||||
*
|
||||
* @param int $num Specifies how many entries should be picked.
|
||||
* @return static
|
||||
*/
|
||||
public function random($num = 1)
|
||||
{
|
||||
return $this->createFrom($this->shuffle()->slice(0, $num));
|
||||
}
|
||||
|
||||
/**
|
||||
* Append new elements to the list.
|
||||
*
|
||||
* @param array $items Items to be appended. Existing keys will be overridden with the new values.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function append($items)
|
||||
{
|
||||
throw new \RuntimeException(__METHOD__ . '(): Not Implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Split collection into array of smaller collections.
|
||||
*
|
||||
* @param int $size
|
||||
* @return static[]
|
||||
*/
|
||||
public function batch($size): array
|
||||
{
|
||||
throw new \RuntimeException(__METHOD__ . '(): Not Implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorder collection.
|
||||
*
|
||||
* @param string $by
|
||||
* @param string $dir
|
||||
* @param array $manual
|
||||
* @param string $sort_flags
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function order($by, $dir = 'asc', $manual = null, $sort_flags = null)
|
||||
{
|
||||
throw new \RuntimeException(__METHOD__ . '(): Not Implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the items between a set of date ranges of either the page date field (default) or
|
||||
* an arbitrary datetime page field where end date is optional
|
||||
* Dates can be passed in as text that strtotime() can process
|
||||
* http://php.net/manual/en/function.strtotime.php
|
||||
*
|
||||
* @param string $startDate
|
||||
* @param string|bool $endDate
|
||||
* @param string|null $field
|
||||
*
|
||||
* @return static
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function dateRange($startDate, $endDate = false, $field = null)
|
||||
{
|
||||
$start = Utils::date2timestamp($startDate);
|
||||
$end = $endDate ? Utils::date2timestamp($endDate) : false;
|
||||
|
||||
$entries = [];
|
||||
foreach ($this as $key => $object) {
|
||||
if (!$object) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$date = $field ? strtotime($object->getNestedProperty($field)) : $object->date();
|
||||
|
||||
if ($date >= $start && (!$end || $date <= $end)) {
|
||||
$entries[$key] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->createFrom($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only visible pages
|
||||
*
|
||||
* @return static The collection with only visible pages
|
||||
*/
|
||||
public function visible()
|
||||
{
|
||||
$entries = [];
|
||||
foreach ($this as $key => $object) {
|
||||
if ($object && $object->visible()) {
|
||||
$entries[$key] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->createFrom($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only non-visible pages
|
||||
*
|
||||
* @return static The collection with only non-visible pages
|
||||
*/
|
||||
public function nonVisible()
|
||||
{
|
||||
$entries = [];
|
||||
foreach ($this as $key => $object) {
|
||||
if ($object && !$object->visible()) {
|
||||
$entries[$key] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->createFrom($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only modular pages
|
||||
*
|
||||
* @return static The collection with only modular pages
|
||||
*/
|
||||
public function modular()
|
||||
{
|
||||
$entries = [];
|
||||
foreach ($this as $key => $object) {
|
||||
if ($object && $object->modular()) {
|
||||
$entries[$key] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->createFrom($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only non-modular pages
|
||||
*
|
||||
* @return static The collection with only non-modular pages
|
||||
*/
|
||||
public function nonModular()
|
||||
{
|
||||
$entries = [];
|
||||
foreach ($this as $key => $object) {
|
||||
if ($object && !$object->modular()) {
|
||||
$entries[$key] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->createFrom($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only published pages
|
||||
*
|
||||
* @return static The collection with only published pages
|
||||
*/
|
||||
public function published()
|
||||
{
|
||||
$entries = [];
|
||||
foreach ($this as $key => $object) {
|
||||
if ($object && $object->published()) {
|
||||
$entries[$key] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->createFrom($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only non-published pages
|
||||
*
|
||||
* @return static The collection with only non-published pages
|
||||
*/
|
||||
public function nonPublished()
|
||||
{
|
||||
$entries = [];
|
||||
foreach ($this as $key => $object) {
|
||||
if ($object && !$object->published()) {
|
||||
$entries[$key] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->createFrom($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only routable pages
|
||||
*
|
||||
* @return static The collection with only routable pages
|
||||
*/
|
||||
public function routable()
|
||||
{
|
||||
$entries = [];
|
||||
foreach ($this as $key => $object) {
|
||||
if ($object && $object->routable()) {
|
||||
$entries[$key] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->createFrom($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only non-routable pages
|
||||
*
|
||||
* @return static The collection with only non-routable pages
|
||||
*/
|
||||
public function nonRoutable()
|
||||
{
|
||||
$entries = [];
|
||||
foreach ($this as $key => $object) {
|
||||
if ($object && !$object->routable()) {
|
||||
$entries[$key] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->createFrom($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only pages of the specified type
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return static The collection
|
||||
*/
|
||||
public function ofType($type)
|
||||
{
|
||||
$entries = [];
|
||||
foreach ($this as $key => $object) {
|
||||
if ($object && $object->template() === $type) {
|
||||
$entries[$key] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->createFrom($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only pages of one of the specified types
|
||||
*
|
||||
* @param string[] $types
|
||||
*
|
||||
* @return static The collection
|
||||
*/
|
||||
public function ofOneOfTheseTypes($types)
|
||||
{
|
||||
$entries = [];
|
||||
foreach ($this as $key => $object) {
|
||||
if ($object && \in_array($object->template(), $types, true)) {
|
||||
$entries[$key] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->createFrom($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only pages of one of the specified access levels
|
||||
*
|
||||
* @param array $accessLevels
|
||||
*
|
||||
* @return static The collection
|
||||
*/
|
||||
public function ofOneOfTheseAccessLevels($accessLevels)
|
||||
{
|
||||
$entries = [];
|
||||
foreach ($this as $key => $object) {
|
||||
if ($object && isset($object->header()->access)) {
|
||||
if (\is_array($object->header()->access)) {
|
||||
//Multiple values for access
|
||||
$valid = false;
|
||||
|
||||
foreach ($object->header()->access as $index => $accessLevel) {
|
||||
if (\is_array($accessLevel)) {
|
||||
foreach ($accessLevel as $innerIndex => $innerAccessLevel) {
|
||||
if (\in_array($innerAccessLevel, $accessLevels)) {
|
||||
$valid = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (\in_array($index, $accessLevels)) {
|
||||
$valid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($valid) {
|
||||
$entries[$key] = $object;
|
||||
}
|
||||
} else {
|
||||
//Single value for access
|
||||
if (\in_array($object->header()->access, $accessLevels)) {
|
||||
$entries[$key] = $object;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->createFrom($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the extended version of this Collection with each page keyed by route
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function toExtendedArray(): array
|
||||
{
|
||||
$entries = [];
|
||||
foreach ($this as $key => $object) {
|
||||
if ($object) {
|
||||
$entries[$object->route()] = $object->toArray();
|
||||
}
|
||||
}
|
||||
return $entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function getLevelListing(array $options): array
|
||||
{
|
||||
return $this->getIndex()->getLevelListing($options);
|
||||
}
|
||||
}
|
||||
403
system/src/Grav/Common/Page/Flex/PageIndex.php
Normal file
403
system/src/Grav/Common/Page/Flex/PageIndex.php
Normal file
@@ -0,0 +1,403 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @package Grav\Common\Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Flex;
|
||||
|
||||
use Grav\Common\File\CompiledJsonFile;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Page\Interfaces\PageInterface;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Framework\Flex\FlexDirectory;
|
||||
use Grav\Framework\Flex\Interfaces\FlexCollectionInterface;
|
||||
use Grav\Framework\Flex\Interfaces\FlexObjectInterface;
|
||||
use Grav\Framework\Flex\Interfaces\FlexStorageInterface;
|
||||
use Grav\Framework\Flex\Pages\FlexPageIndex;
|
||||
|
||||
/**
|
||||
* Class GravPageObject
|
||||
* @package Grav\Plugin\FlexObjects\Types\GravPages
|
||||
*/
|
||||
class PageIndex extends FlexPageIndex
|
||||
{
|
||||
const VERSION = parent::VERSION . '.5';
|
||||
const ORDER_LIST_REGEX = '/(\/\d+)\.[^\/]+/u';
|
||||
const PAGE_ROUTE_REGEX = '/\/\d+\./u';
|
||||
|
||||
/** @var FlexObjectInterface */
|
||||
protected $_root;
|
||||
protected $_params;
|
||||
|
||||
/**
|
||||
* @param array $entries
|
||||
* @param FlexDirectory|null $directory
|
||||
*/
|
||||
public function __construct(array $entries = [], FlexDirectory $directory = null)
|
||||
{
|
||||
// Remove root if it's taken.
|
||||
if (isset($entries[''])) {
|
||||
$this->_root = $entries[''];
|
||||
unset($entries['']);
|
||||
}
|
||||
|
||||
parent::__construct($entries, $directory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $entries
|
||||
* @param string|null $keyField
|
||||
* @return $this|FlexPageIndex
|
||||
*/
|
||||
protected function createFrom(array $entries, string $keyField = null)
|
||||
{
|
||||
/** @var static $index */
|
||||
$index = parent::createFrom($entries, $keyField);
|
||||
$index->_root = $this->getRoot();
|
||||
|
||||
return $index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FlexStorageInterface $storage
|
||||
* @return array
|
||||
*/
|
||||
public static function loadEntriesFromStorage(FlexStorageInterface $storage) : array
|
||||
{
|
||||
// Load saved index.
|
||||
$index = static::loadIndex($storage);
|
||||
|
||||
$timestamp = $index['timestamp'] ?? 0;
|
||||
if ($timestamp && $timestamp > time() - 2) {
|
||||
return $index['index'];
|
||||
}
|
||||
|
||||
// Load up to date index.
|
||||
$entries = parent::loadEntriesFromStorage($storage);
|
||||
|
||||
return static::updateIndexFile($storage, $index['index'], $entries, ['include_missing' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return FlexObjectInterface|PageInterface|null
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
if (mb_strpos($key, '|') !== false) {
|
||||
[$key, $params] = explode('|', $key, 2);
|
||||
}
|
||||
|
||||
$element = parent::get($key);
|
||||
if (isset($params)) {
|
||||
$element = $element->getTranslation(ltrim($params, '.'));
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FlexObjectInterface|PageInterface
|
||||
*/
|
||||
public function getRoot()
|
||||
{
|
||||
$root = $this->_root;
|
||||
if (is_array($root)) {
|
||||
$this->_root = $this->getFlexDirectory()->createObject(['__META' => $root], '/');
|
||||
}
|
||||
|
||||
return $this->_root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the collection params
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getParams(): array
|
||||
{
|
||||
return $this->_params ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set parameters to the Collection
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setParams(array $params)
|
||||
{
|
||||
$this->_params = $this->_params ? array_merge($this->_params, $params) : $params;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the collection params
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function params(): array
|
||||
{
|
||||
return $this->getParams();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function getLevelListing(array $options): array
|
||||
{
|
||||
$options += [
|
||||
'field' => null,
|
||||
'route' => null,
|
||||
'leaf_route' => null,
|
||||
'sortby' => null,
|
||||
'order' => SORT_ASC,
|
||||
'lang' => null,
|
||||
'filters' => [],
|
||||
];
|
||||
|
||||
$options['filters'] += [
|
||||
'type' => ['root', 'dir'],
|
||||
];
|
||||
|
||||
return $this->getLevelListingRecurse($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
protected function getLevelListingRecurse(array $options): array
|
||||
{
|
||||
$filters = $options['filters'];
|
||||
$filter_type = (array)$filters['type'];
|
||||
|
||||
$field = $options['field'];
|
||||
$route = $options['route'];
|
||||
$leaf_route = $options['leaf_route'];
|
||||
$sortby = $options['sortby'];
|
||||
$order = $options['order'];
|
||||
$language = $options['lang'];
|
||||
|
||||
$status = 'error';
|
||||
$msg = null;
|
||||
$response = [];
|
||||
$children = null;
|
||||
$sub_route = null;
|
||||
$extra = null;
|
||||
|
||||
// Handle leaf_route
|
||||
$leaf = null;
|
||||
if ($leaf_route && $route !== $leaf_route) {
|
||||
$nodes = explode('/', $leaf_route);
|
||||
$sub_route = '/' . implode('/', array_slice($nodes, 1, $options['level']++));
|
||||
$options['route'] = $sub_route;
|
||||
|
||||
[$status,,$leaf,$extra] = $this->getLevelListingRecurse($options);
|
||||
}
|
||||
|
||||
// Handle no route, assume page tree root
|
||||
if (!$route) {
|
||||
$page = $this->getRoot();
|
||||
} else {
|
||||
$page = $this->get(trim($route, '/'));
|
||||
}
|
||||
$path = $page ? $page->path() : null;
|
||||
|
||||
if ($field) {
|
||||
$blueprint = $page ? $page->getBlueprint() : $this->getFlexDirectory()->getBlueprint();
|
||||
$settings = $blueprint->schema()->getProperty($field);
|
||||
$filters = array_merge([], $filters, $settings['filters'] ?? []);
|
||||
$filter_type = $filters['type'] ?? $filter_type;
|
||||
}
|
||||
|
||||
if ($page) {
|
||||
if ($page->root() && (!$filters['type'] || in_array('root', $filter_type, true))) {
|
||||
if ($field) {
|
||||
$response[] = [
|
||||
'name' => '<root>',
|
||||
'value' => '/',
|
||||
'item-key' => '',
|
||||
'filename' => '.',
|
||||
'extension' => '',
|
||||
'type' => 'root',
|
||||
'modified' => $page->modified(),
|
||||
'size' => 0,
|
||||
'symlink' => false
|
||||
];
|
||||
} else {
|
||||
$response[] = [
|
||||
'item-key' => '',
|
||||
'icon' => 'root',
|
||||
'title' => '<root>',
|
||||
'route' => '/',
|
||||
'raw_route' => null,
|
||||
'modified' => $page->modified(),
|
||||
'child_count' => 0,
|
||||
'extras' => [
|
||||
'template' => null,
|
||||
'langs' => [],
|
||||
'published' => false,
|
||||
'published_date' => null,
|
||||
'unpublished_date' => null,
|
||||
'visible' => false,
|
||||
'routable' => false,
|
||||
'tags' => ['non-routable'],
|
||||
'actions' => [],
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$status = 'success';
|
||||
$msg = 'PLUGIN_ADMIN.PAGE_ROUTE_FOUND';
|
||||
|
||||
$children = $page->children();
|
||||
|
||||
/** @var PageObject $child */
|
||||
foreach ($children as $child) {
|
||||
$selected = $child->path() === $extra;
|
||||
$includeChildren = \is_array($leaf) && !empty($leaf) && $selected;
|
||||
if (!$selected && !$child->filterBy($filters)) {
|
||||
continue;
|
||||
}
|
||||
if ($field) {
|
||||
$payload = [
|
||||
'name' => $child->title(),
|
||||
'value' => $child->rawRoute(),
|
||||
'item-key' => basename($child->rawRoute()),
|
||||
'filename' => $child->folder(),
|
||||
'extension' => $child->extension(),
|
||||
'type' => 'dir',
|
||||
'modified' => $child->modified(),
|
||||
'size' => count($child->children()),
|
||||
'symlink' => false
|
||||
];
|
||||
} else {
|
||||
// TODO: all these features are independent from each other, we cannot just have one icon/color to catch all.
|
||||
// TODO: maybe icon by home/modular/page/folder (or even from blueprints) and color by visibility etc..
|
||||
if ($child->home()) {
|
||||
$icon = 'home';
|
||||
} elseif ($child->modular()) {
|
||||
$icon = 'modular';
|
||||
} elseif ($child->visible()) {
|
||||
$icon = 'visible';
|
||||
} elseif ($child->isPage()) {
|
||||
$icon = 'page';
|
||||
} else {
|
||||
// TODO: add support
|
||||
$icon = 'folder';
|
||||
}
|
||||
$tags = [
|
||||
$child->published() ? 'published' : 'non-published',
|
||||
$child->visible() ? 'visible' : 'non-visible',
|
||||
$child->routable() ? 'routable' : 'non-routable'
|
||||
];
|
||||
$lang = $child->findTranslation($language) ?? 'n/a';
|
||||
$child = $child->getTranslation($language) ?? $child;
|
||||
$extras = [
|
||||
'template' => $child->template(),
|
||||
'lang' => $lang ?: null,
|
||||
'translated' => $lang ? $child->hasTranslation($language, false) : null,
|
||||
'langs' => $child->getAllLanguages(true) ?: null,
|
||||
'published' => $child->published(),
|
||||
'published_date' => $this->jsDate($child->publishDate()),
|
||||
'unpublished_date' => $this->jsDate($child->unpublishDate()),
|
||||
'visible' => $child->visible(),
|
||||
'routable' => $child->routable(),
|
||||
'tags' => $tags,
|
||||
'actions' => null,
|
||||
];
|
||||
$extras = array_filter($extras, static function ($v) {
|
||||
return $v !== null;
|
||||
});
|
||||
$payload = [
|
||||
'item-key' => basename($child->rawRoute()),
|
||||
'icon' => $icon,
|
||||
'title' => $child->title(),
|
||||
'route' => [
|
||||
'display' => $child->getRoute()->toString(false) ?: '/',
|
||||
'raw' => $child->rawRoute(),
|
||||
],
|
||||
'modified' => $this->jsDate($child->modified()),
|
||||
'child_count' => count($child->children()) ?: null,
|
||||
'extras' => $extras
|
||||
];
|
||||
$payload = array_filter($payload, static function ($v) {
|
||||
return $v !== null;
|
||||
});
|
||||
}
|
||||
|
||||
// Add children if any
|
||||
if ($includeChildren) {
|
||||
$payload['children'] = array_values($leaf);
|
||||
}
|
||||
|
||||
$response[] = $payload;
|
||||
}
|
||||
} else {
|
||||
$msg = 'PLUGIN_ADMIN.PAGE_ROUTE_NOT_FOUND';
|
||||
}
|
||||
|
||||
// Sorting
|
||||
if ($sortby) {
|
||||
$response = Utils::sortArrayByKey($response, $sortby, $order);
|
||||
}
|
||||
|
||||
if ($field) {
|
||||
$temp_array = [];
|
||||
foreach ($response as $index => $item) {
|
||||
$temp_array[$item['type']][$index] = $item;
|
||||
}
|
||||
|
||||
$sorted = Utils::sortArrayByArray($temp_array, $filter_type);
|
||||
$response = Utils::arrayFlatten($sorted);
|
||||
}
|
||||
|
||||
return [$status, $msg ?? 'PLUGIN_ADMIN.NO_ROUTE_PROVIDED', $response, $path];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FlexStorageInterface $storage
|
||||
* @return CompiledJsonFile|\Grav\Common\File\CompiledYamlFile|null
|
||||
*/
|
||||
protected static function getIndexFile(FlexStorageInterface $storage)
|
||||
{
|
||||
if (!method_exists($storage, 'isIndexed') || !$storage->isIndexed()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Load saved index file.
|
||||
$grav = Grav::instance();
|
||||
$locator = $grav['locator'];
|
||||
|
||||
$filename = $locator->findResource('user-data://flex/indexes/pages.json', true, true);
|
||||
|
||||
return CompiledJsonFile::instance($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $timestamp
|
||||
* @return string|null
|
||||
*/
|
||||
private function jsDate(int $timestamp = null): ?string
|
||||
{
|
||||
if (!$timestamp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$config = Grav::instance()['config'];
|
||||
$dateFormat = $config->get('system.pages.dateformat.long');
|
||||
|
||||
return date($dateFormat, $timestamp) ?: null;
|
||||
}
|
||||
}
|
||||
407
system/src/Grav/Common/Page/Flex/PageObject.php
Normal file
407
system/src/Grav/Common/Page/Flex/PageObject.php
Normal file
@@ -0,0 +1,407 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @package Grav\Common\Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Flex;
|
||||
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Page\Flex\Traits\PageContentTrait;
|
||||
use Grav\Common\Page\Flex\Traits\PageLegacyTrait;
|
||||
use Grav\Common\Page\Flex\Traits\PageRoutableTrait;
|
||||
use Grav\Common\Page\Flex\Traits\PageTranslateTrait;
|
||||
use Grav\Common\Page\Pages;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Framework\Flex\FlexObject;
|
||||
use Grav\Framework\Flex\Pages\FlexPageObject;
|
||||
use Grav\Framework\Route\Route;
|
||||
use Grav\Framework\Route\RouteFactory;
|
||||
use Grav\Plugin\Admin\Admin;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
|
||||
/**
|
||||
* Class GravPageObject
|
||||
* @package Grav\Plugin\FlexObjects\Types\GravPages
|
||||
*
|
||||
* @property string $name
|
||||
* @property string $route
|
||||
* @property string $folder
|
||||
* @property int|false $order
|
||||
* @property string $template
|
||||
* @property string $language
|
||||
*/
|
||||
class PageObject extends FlexPageObject
|
||||
{
|
||||
use PageContentTrait;
|
||||
use PageLegacyTrait;
|
||||
use PageRoutableTrait;
|
||||
use PageTranslateTrait;
|
||||
|
||||
/** @var string Language code, eg: 'en' */
|
||||
protected $language;
|
||||
|
||||
/** @var string File format, eg. 'md' */
|
||||
protected $format;
|
||||
|
||||
private $_initialized = false;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getCachedMethods(): array
|
||||
{
|
||||
return [
|
||||
'path' => true,
|
||||
'full_order' => true
|
||||
] + parent::getCachedMethods();
|
||||
}
|
||||
|
||||
public function initialize(): void
|
||||
{
|
||||
if (!$this->_initialized) {
|
||||
Grav::instance()->fireEvent('onPageProcessed', new Event(['page' => $this]));
|
||||
$this->_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $query
|
||||
* @return Route
|
||||
*/
|
||||
public function getRoute($query = []): Route
|
||||
{
|
||||
$route = RouteFactory::createFromString($this->route());
|
||||
if (\is_array($query)) {
|
||||
foreach ($query as $key => $value) {
|
||||
$route = $route->withQueryParam($key, $value);
|
||||
}
|
||||
} else {
|
||||
$route = $route->withAddedPath($query);
|
||||
}
|
||||
|
||||
return $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc PageInterface
|
||||
*/
|
||||
public function getFormValue(string $name, $default = null, string $separator = null)
|
||||
{
|
||||
$test = new \stdClass();
|
||||
|
||||
$value = $this->pageContentValue($name, $test);
|
||||
if ($value !== $test) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
switch ($name) {
|
||||
case 'name':
|
||||
// TODO: this should not be template!
|
||||
return $this->getProperty('template');
|
||||
case 'route':
|
||||
$key = dirname($this->hasKey() ? '/' . $this->getKey() : '/');
|
||||
return $key !== '/' ? $key : null;
|
||||
case 'full_route':
|
||||
return $this->hasKey() ? '/' . $this->getKey() : '';
|
||||
case 'full_order':
|
||||
return $this->full_order();
|
||||
case 'lang':
|
||||
return $this->getLanguage() ?? '';
|
||||
case 'translations':
|
||||
return $this->getLanguages();
|
||||
}
|
||||
|
||||
return parent::getFormValue($name, $default, $separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|bool $reorder
|
||||
* @return FlexObject|\Grav\Framework\Flex\Interfaces\FlexObjectInterface
|
||||
*/
|
||||
public function save($reorder = true)
|
||||
{
|
||||
// Reorder siblings.
|
||||
if ($reorder === true) {
|
||||
$reorder = $this->_reorder ?: false;
|
||||
}
|
||||
$siblings = is_array($reorder) ? $this->reorderSiblings($reorder) : [];
|
||||
|
||||
/** @var static $instance */
|
||||
$instance = parent::save();
|
||||
|
||||
foreach ($siblings as $sibling) {
|
||||
$sibling->save(false);
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $ordering
|
||||
* @return PageCollection
|
||||
*/
|
||||
protected function reorderSiblings(array $ordering)
|
||||
{
|
||||
$storageKey = $this->getStorageKey();
|
||||
$oldParentKey = ltrim(dirname("/$storageKey"), '/');
|
||||
$newParentKey = $this->getProperty('parent_key');
|
||||
|
||||
$slug = basename($this->getKey());
|
||||
$order = $oldParentKey === $newParentKey ? $this->order() : false;
|
||||
$k = $slug !== '' ? array_search($slug, $ordering, true) : false;
|
||||
if ($order === false) {
|
||||
if ($k !== false) {
|
||||
unset($ordering[$k]);
|
||||
}
|
||||
} elseif ($k === false) {
|
||||
$ordering[999999] = $slug;
|
||||
}
|
||||
$ordering = array_values($ordering);
|
||||
|
||||
$parent = $this->parent();
|
||||
|
||||
/** @var PageCollection $siblings */
|
||||
$siblings = $parent ? $parent->children() : null;
|
||||
$siblings = $siblings ? $siblings->withVisible()->getCollection() : null;
|
||||
if ($siblings) {
|
||||
$ordering = array_flip($ordering);
|
||||
if ($storageKey !== null) {
|
||||
$siblings->remove($storageKey);
|
||||
if (isset($ordering[$slug])) {
|
||||
$siblings->set($storageKey, $this);
|
||||
}
|
||||
}
|
||||
$count = count($ordering);
|
||||
foreach ($siblings as $sibling) {
|
||||
$newOrder = $ordering[basename($sibling->getKey())] ?? null;
|
||||
$oldOrder = $sibling->order();
|
||||
$sibling->order(null !== $newOrder ? $newOrder + 1 : $oldOrder + $count);
|
||||
}
|
||||
$siblings = $siblings->orderBy(['order' => 'ASC']);
|
||||
$siblings->removeElement($this);
|
||||
} else {
|
||||
$siblings = $this->getFlexDirectory()->createCollection([]);
|
||||
}
|
||||
|
||||
return $siblings;
|
||||
}
|
||||
|
||||
public function full_order(): string
|
||||
{
|
||||
$path = $this->path();
|
||||
|
||||
return preg_replace(PageIndex::ORDER_LIST_REGEX, '\\1', $path . '/' . $this->folder());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return Blueprint
|
||||
*/
|
||||
protected function doGetBlueprint(string $name = ''): Blueprint
|
||||
{
|
||||
try {
|
||||
// Make sure that pages has been initialized.
|
||||
Pages::getTypes();
|
||||
|
||||
// TODO: We need to move raw blueprint logic to Grav itself to remove admin dependency here.
|
||||
if ($name === 'raw') {
|
||||
// Admin RAW mode.
|
||||
/** @var Admin $admin */
|
||||
$admin = Grav::instance()['admin'] ?? null;
|
||||
if ($admin) {
|
||||
$template = $this->modular() ? 'modular_raw' : 'raw';
|
||||
|
||||
return $admin->blueprints("admin/pages/{$template}");
|
||||
}
|
||||
}
|
||||
|
||||
$template = $this->getProperty('template') . ($name ? '.' . $name : '');
|
||||
|
||||
$blueprint = $this->getFlexDirectory()->getBlueprint($template, 'blueprints://pages');
|
||||
} catch (\RuntimeException $e) {
|
||||
$template = 'default' . ($name ? '.' . $name : '');
|
||||
|
||||
$blueprint = $this->getFlexDirectory()->getBlueprint($template, 'blueprints://pages');
|
||||
}
|
||||
|
||||
return $blueprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function getLevelListing(array $options): array
|
||||
{
|
||||
return $this->getFlexDirectory()->getIndex()->getLevelListing($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter page (true/false) by given filters. If filter value is either null or '', it will be skipped.
|
||||
*
|
||||
* - search: string
|
||||
* - extension: string
|
||||
* - modular: bool
|
||||
* - visible: bool
|
||||
* - routable: bool
|
||||
* - published: bool
|
||||
* - page: bool
|
||||
* - translated: bool
|
||||
*
|
||||
* @param array $filters
|
||||
* @return bool
|
||||
*/
|
||||
public function filterBy(array $filters): bool
|
||||
{
|
||||
foreach ($filters as $key => $value) {
|
||||
if ($value === null || $value === '') {
|
||||
continue;
|
||||
}
|
||||
switch ($key) {
|
||||
case 'search':
|
||||
$matches = $this->search((string)$value);
|
||||
break;
|
||||
case 'page_type': // filename
|
||||
$matches = true;
|
||||
break;
|
||||
case 'extension': // .en.md
|
||||
$matches = Utils::contains((string)$value, $this->extension());
|
||||
break;
|
||||
case 'modular': // _name (filename)
|
||||
case 'visible': // order
|
||||
case 'routable':
|
||||
case 'published':
|
||||
$matches = $this->{$key}() === (bool)$value;
|
||||
break;
|
||||
case 'page': // markdown in index
|
||||
$matches = $this->isPage() === (bool)$value;
|
||||
break;
|
||||
case 'translated': // markdown in index
|
||||
$matches = $this->hasTranslation() === (bool)$value;
|
||||
break;
|
||||
default:
|
||||
$matches = true;
|
||||
break;
|
||||
}
|
||||
if ($matches === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
$list = parent::__debugInfo();
|
||||
|
||||
return $list + [
|
||||
'_content_meta:private' => $this->getContentMeta(),
|
||||
'_content:private' => $this->getRawContent()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $elements
|
||||
* @param bool $extended
|
||||
*/
|
||||
protected function filterElements(array &$elements, bool $extended = false): void
|
||||
{
|
||||
// Deal with ordering=bool and order=page1,page2,page3.
|
||||
if (array_key_exists('ordering', $elements) && array_key_exists('order', $elements)) {
|
||||
$ordering = (bool)($elements['ordering'] ?? false);
|
||||
$slug = preg_replace(PAGE_ORDER_PREFIX_REGEX, '', $this->getProperty('folder'));
|
||||
$list = !empty($elements['order']) ? explode(',', $elements['order']) : [];
|
||||
if ($ordering) {
|
||||
$order = array_search($slug, $list, true);
|
||||
if ($order !== false) {
|
||||
$order++;
|
||||
} else {
|
||||
$order = $this->getProperty('order') ?: 1;
|
||||
}
|
||||
} else {
|
||||
$order = false;
|
||||
}
|
||||
|
||||
$this->_reorder = $list;
|
||||
$elements['order'] = $order;
|
||||
}
|
||||
|
||||
// Change storage location if needed.
|
||||
if (array_key_exists('route', $elements) && isset($elements['folder'], $elements['name'])) {
|
||||
$elements['template'] = $elements['name'];
|
||||
$parentRoute = $elements['route'] ?? '';
|
||||
|
||||
// Figure out storage path to the new route.
|
||||
$parentKey = trim($parentRoute, '/');
|
||||
if ($parentKey !== '') {
|
||||
// Make sure page isn't being moved under itself.
|
||||
$key = $this->getKey();
|
||||
if ($key === $parentKey || strpos($parentKey, $key . '/') === 0) {
|
||||
throw new \RuntimeException(sprintf('Page %s cannot be moved to %s', '/' . $key, $parentRoute));
|
||||
}
|
||||
|
||||
/** @var PageObject $parent */
|
||||
$parent = $this->getFlexDirectory()->getObject($parentKey);
|
||||
if (!$parent) {
|
||||
// Page cannot be moved to non-existing location.
|
||||
throw new \RuntimeException(sprintf('Page %s cannot be moved to non-existing path %s', '/' . $key, $parentRoute));
|
||||
}
|
||||
|
||||
// If parent changes and page is visible, move it to be the last item.
|
||||
if ($parent && !empty($elements['order']) && $parent !== $this->parent()) {
|
||||
$siblings = $parent->children();
|
||||
$siblings = $siblings ? $siblings->visible()->sort(['order' => 'ASC']) : null;
|
||||
if ($siblings && $siblings->count()) {
|
||||
$elements['order'] = ((int)$siblings->last()->order()) + 1;
|
||||
} else {
|
||||
$elements['order'] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$parentKey = $parent->getStorageKey();
|
||||
}
|
||||
|
||||
$elements['parent_key'] = $parentKey;
|
||||
}
|
||||
parent::filterElements($elements, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function prepareStorage(): array
|
||||
{
|
||||
$meta = $this->getMetaData();
|
||||
$oldLang = $meta['lang'] ?? '';
|
||||
$newLang = $this->getProperty('lang');
|
||||
|
||||
// Always clone the page to the new language.
|
||||
if ($oldLang !== $newLang) {
|
||||
$meta['clone'] = true;
|
||||
}
|
||||
|
||||
// Make sure that certain elements are always sent to the storage layer.
|
||||
$elements = [
|
||||
'__META' => $meta,
|
||||
'storage_key' => $this->getStorageKey(),
|
||||
'parent_key' => $this->getProperty('parent_key'),
|
||||
'order' => $this->getProperty('order'),
|
||||
'folder' => preg_replace('|^\d+\.|', '', $this->getProperty('folder')),
|
||||
'template' => preg_replace('|modular/|', '', $this->getProperty('template')),
|
||||
'lang' => $newLang
|
||||
] + parent::prepareStorage();
|
||||
|
||||
return $elements;
|
||||
}
|
||||
}
|
||||
590
system/src/Grav/Common/Page/Flex/PageStorage.php
Normal file
590
system/src/Grav/Common/Page/Flex/PageStorage.php
Normal file
@@ -0,0 +1,590 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @package Grav\Common\Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Flex;
|
||||
|
||||
use Grav\Common\Debugger;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Language\Language;
|
||||
use Grav\Framework\File\MarkdownFile;
|
||||
use Grav\Framework\File\YamlFile;
|
||||
use Grav\Framework\Flex\Storage\FolderStorage;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
/**
|
||||
* Class GravPageStorage
|
||||
* @package Grav\Plugin\FlexObjects\Types\GravPages
|
||||
*/
|
||||
class PageStorage extends FolderStorage
|
||||
{
|
||||
protected $ignore_files;
|
||||
protected $ignore_folders;
|
||||
protected $ignore_hidden;
|
||||
protected $include_default_lang_file_extension;
|
||||
protected $recurse;
|
||||
protected $base_path;
|
||||
|
||||
protected $flags;
|
||||
protected $regex;
|
||||
|
||||
protected function initOptions(array $options): void
|
||||
{
|
||||
parent::initOptions($options);
|
||||
|
||||
$this->flags = \FilesystemIterator::KEY_AS_FILENAME | \FilesystemIterator::CURRENT_AS_FILEINFO
|
||||
| \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS;
|
||||
|
||||
$grav = Grav::instance();
|
||||
|
||||
$config = $grav['config'];
|
||||
$this->ignore_hidden = (bool)$config->get('system.pages.ignore_hidden');
|
||||
$this->ignore_files = (array)$config->get('system.pages.ignore_files');
|
||||
$this->ignore_folders = (array)$config->get('system.pages.ignore_folders');
|
||||
$this->include_default_lang_file_extension = $config->get('system.languages.include_default_lang_file_extension', true);
|
||||
$this->recurse = $options['recurse'] ?? true;
|
||||
$this->regex = '/(\.([\w\d_-]+))?\.md$/D';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param bool $variations
|
||||
* @return array
|
||||
*/
|
||||
public function parseKey(string $key, bool $variations = true): array
|
||||
{
|
||||
if (mb_strpos($key, '|') !== false) {
|
||||
[$key, $params] = explode('|', $key, 2);
|
||||
} else {
|
||||
$params = '';
|
||||
}
|
||||
$key = ltrim($key, '/');
|
||||
|
||||
$keys = parent::parseKey($key, false) + ['params' => $params];
|
||||
|
||||
if ($variations) {
|
||||
$keys += $this->parseParams($key, $params);
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
public function readFrontmatter(string $key): string
|
||||
{
|
||||
$path = $this->getPathFromKey($key);
|
||||
$file = $this->getFile($path);
|
||||
try {
|
||||
if ($file instanceof MarkdownFile) {
|
||||
$frontmatter = $file->frontmatter();
|
||||
} else {
|
||||
$frontmatter = $file->raw();
|
||||
}
|
||||
} catch (\RuntimeException $e) {
|
||||
$frontmatter = 'ERROR: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
return $frontmatter;
|
||||
}
|
||||
|
||||
public function readRaw(string $key): string
|
||||
{
|
||||
$path = $this->getPathFromKey($key);
|
||||
$file = $this->getFile($path);
|
||||
try {
|
||||
$raw = $file->raw();
|
||||
} catch (\RuntimeException $e) {
|
||||
$raw = 'ERROR: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
return $raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $keys
|
||||
* @param bool $includeParams
|
||||
* @return string
|
||||
*/
|
||||
public function buildStorageKey(array $keys, bool $includeParams = true): string
|
||||
{
|
||||
$key = $keys['key'] ?? null;
|
||||
if (null === $key) {
|
||||
$key = $keys['parent_key'] ?? '';
|
||||
if ($key !== '') {
|
||||
$key .= '/';
|
||||
}
|
||||
$order = $keys['order'] ?? 0;
|
||||
$folder = $keys['folder'] ?? 'undefined';
|
||||
$key .= $order ? sprintf('%02d.%s', $order, $folder) : $folder;
|
||||
}
|
||||
|
||||
$params = $includeParams ? $this->buildStorageKeyParams($keys) : '';
|
||||
|
||||
return $params ? "{$key}|{$params}" : $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $keys
|
||||
* @return string
|
||||
*/
|
||||
public function buildStorageKeyParams(array $keys): string
|
||||
{
|
||||
$params = $keys['template'] ?? '';
|
||||
$language = $keys['lang'] ?? '';
|
||||
if ($language) {
|
||||
$params .= '.' . $language;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $keys
|
||||
* @return string
|
||||
*/
|
||||
public function buildFolder(array $keys): string
|
||||
{
|
||||
return $this->dataFolder . '/' . $this->buildStorageKey($keys, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $keys
|
||||
* @return string
|
||||
*/
|
||||
public function buildFilename(array $keys): string
|
||||
{
|
||||
$file = $this->buildStorageKeyParams($keys);
|
||||
|
||||
// Template is optional; if it is missing, we need to have to load the object metadata.
|
||||
if ($file && $file[0] === '.') {
|
||||
$meta = $this->getObjectMeta($this->buildStorageKey($keys, false));
|
||||
$file = ($meta['template'] ?? 'folder') . $file;
|
||||
}
|
||||
|
||||
return $file . $this->dataExt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $keys
|
||||
* @return string
|
||||
*/
|
||||
public function buildFilepath(array $keys): string
|
||||
{
|
||||
return $this->buildFolder($keys) . '/' . $this->buildFilename($keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $row
|
||||
* @return array
|
||||
*/
|
||||
public function extractKeysFromRow(array $row): array
|
||||
{
|
||||
$meta = $row['__META'] ?? null;
|
||||
$storageKey = $row['storage_key'] ?? $meta['storage_key'] ?? '';
|
||||
$keyMeta = $storageKey !== '' ? $this->extractKeysFromStorageKey($storageKey) : null;
|
||||
$parentKey = $row['parent_key'] ?? $meta['parent_key'] ?? $keyMeta['parent_key'] ?? '';
|
||||
$order = $row['order'] ?? $meta['order'] ?? $keyMeta['order'] ?? '';
|
||||
$folder = $row['folder'] ?? $meta['folder'] ?? $keyMeta['folder'] ?? '';
|
||||
$template = $row['template'] ?? $meta['template'] ?? $keyMeta['template'] ?? '';
|
||||
$lang = $row['lang'] ?? $meta['lang'] ?? $keyMeta['lang'] ?? '';
|
||||
|
||||
// Handle default language, if it should be saved without language extension.
|
||||
if ($lang && !$this->include_default_lang_file_extension && empty($meta['markdown'][$lang])) {
|
||||
$grav = Grav::instance();
|
||||
|
||||
/** @var Language $langauge */
|
||||
$language = $grav['language'];
|
||||
if ($lang === $language->getDefault()) {
|
||||
$lang = '';
|
||||
}
|
||||
}
|
||||
|
||||
$keys = [
|
||||
'key' => null,
|
||||
'params' => null,
|
||||
'parent_key' => $parentKey,
|
||||
'order' => (int)$order,
|
||||
'folder' => $folder,
|
||||
'template' => $template,
|
||||
'lang' => $lang
|
||||
];
|
||||
|
||||
$keys['key'] = $this->buildStorageKey($keys, false);
|
||||
$keys['params'] = $this->buildStorageKeyParams($keys);
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return array
|
||||
*/
|
||||
public function extractKeysFromStorageKey(string $key): array
|
||||
{
|
||||
if (mb_strpos($key, '|') !== false) {
|
||||
[$key, $params] = explode('|', $key, 2);
|
||||
[$template, $language] = mb_strpos($params, '.') !== false ? explode('.', $params, 2) : [$params, ''];
|
||||
} else {
|
||||
$params = $template = $language = '';
|
||||
}
|
||||
$objectKey = basename($key);
|
||||
if (preg_match('|^(\d+)\.(.+)$|', $objectKey, $matches)) {
|
||||
[, $order, $folder] = $matches;
|
||||
} else {
|
||||
[$order, $folder] = ['', $objectKey];
|
||||
}
|
||||
$parentKey = ltrim(dirname('/' . $key), '/');
|
||||
|
||||
return [
|
||||
'key' => $key,
|
||||
'params' => $params,
|
||||
'parent_key' => $parentKey,
|
||||
'order' => (int)$order,
|
||||
'folder' => $folder,
|
||||
'template' => $template,
|
||||
'lang' => $language
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param string $params
|
||||
* @return array
|
||||
*/
|
||||
protected function parseParams(string $key, string $params): array
|
||||
{
|
||||
if (mb_strpos($params, '.') !== false) {
|
||||
[$template, $language] = explode('.', $params, 2);
|
||||
} else {
|
||||
$template = $params;
|
||||
$language = '';
|
||||
}
|
||||
|
||||
if ($template === '') {
|
||||
$meta = $this->getObjectMeta($key);
|
||||
$template = $meta['template'] ?? 'folder';
|
||||
}
|
||||
|
||||
return [
|
||||
'file' => $template . ($language ? '.' . $language : ''),
|
||||
'template' => $template,
|
||||
'lang' => $language
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the row for saving and returns the storage key for the record.
|
||||
*
|
||||
* @param array $row
|
||||
*/
|
||||
protected function prepareRow(array &$row): void
|
||||
{
|
||||
// Remove keys used in the filesystem.
|
||||
unset($row['parent_key'], $row['order'], $row['folder'], $row['template'], $row['lang']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Page storage supports moving and copying the pages and their languages.
|
||||
*
|
||||
* $row['__META']['copy'] = true Use this if you want to copy the whole folder, otherwise it will be moved
|
||||
* $row['__META']['clone'] = true Use this if you want to clone the file, otherwise it will be renamed
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $row
|
||||
* @return array
|
||||
*/
|
||||
protected function saveRow(string $key, array $row): array
|
||||
{
|
||||
// Initialize all key-related variables.
|
||||
$newKeys = $this->extractKeysFromRow($row);
|
||||
$newKey = $this->buildStorageKey($newKeys);
|
||||
$newFolder = $this->buildFolder($newKeys);
|
||||
$newFilename = $this->buildFilename($newKeys);
|
||||
$newFilepath = "{$newFolder}/{$newFilename}";
|
||||
|
||||
try {
|
||||
$grav = Grav::instance();
|
||||
|
||||
/** @var Debugger $debugger */
|
||||
$debugger = $grav['debugger'];
|
||||
$debugger->addMessage("Save page: {$newKey}", 'debug');
|
||||
|
||||
// Check if the row already exists.
|
||||
$oldKey = $row['__META']['storage_key'] ?? null;
|
||||
if (is_string($oldKey)) {
|
||||
// Initialize all old key-related variables.
|
||||
$oldKeys = $this->extractKeysFromRow(['__META' => $row['__META']]);
|
||||
$oldFolder = $this->buildFolder($oldKeys);
|
||||
$oldFilename = $this->buildFilename($oldKeys);
|
||||
|
||||
// Check if folder has changed.
|
||||
if ($oldFolder !== $newFolder && file_exists($oldFolder)) {
|
||||
$isCopy = $row['__META']['copy'] ?? false;
|
||||
if ($isCopy) {
|
||||
$this->copyRow($oldKey, $newKey);
|
||||
$debugger->addMessage("Page copied: {$oldFolder} => {$newFolder}", 'debug');
|
||||
} else {
|
||||
$this->renameRow($oldKey, $newKey);
|
||||
$debugger->addMessage("Page moved: {$oldFolder} => {$newFolder}", 'debug');
|
||||
}
|
||||
}
|
||||
|
||||
// Check if filename has changed.
|
||||
if ($oldFilename !== $newFilename) {
|
||||
// Get instance of the old file (we have already copied/moved it).
|
||||
$oldFilepath = "{$newFolder}/{$oldFilename}";
|
||||
$file = $this->getFile($oldFilepath);
|
||||
|
||||
// Rename the file if we aren't supposed to clone it.
|
||||
$isClone = $row['__META']['clone'] ?? false;
|
||||
if (!$isClone && $file->exists()) {
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $grav['locator'];
|
||||
$toPath = $locator->isStream($newFilepath) ? $locator->findResource($newFilepath, true, true) : $newFilepath;
|
||||
$success = $file->rename($toPath);
|
||||
if (!$success) {
|
||||
throw new \RuntimeException("Changing page template failed: {$oldFilepath} => {$newFilepath}");
|
||||
}
|
||||
$debugger->addMessage("Page template changed: {$oldFilename} => {$newFilename}", 'debug');
|
||||
} else {
|
||||
$file = null;
|
||||
$debugger->addMessage("Page template created: {$newFilename}", 'debug');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up the data to be saved.
|
||||
$this->prepareRow($row);
|
||||
unset($row['__META'], $row['__ERROR']);
|
||||
|
||||
if (!isset($file)) {
|
||||
$file = $this->getFile($newFilepath);
|
||||
}
|
||||
|
||||
// Compare existing file content to the new one and save the file only if content has been changed.
|
||||
$file->free();
|
||||
$oldRaw = $file->raw();
|
||||
$file->content($row);
|
||||
$newRaw = $file->raw();
|
||||
if ($oldRaw !== $newRaw) {
|
||||
$file->save($row);
|
||||
$debugger->addMessage("Page content saved: {$newFilepath}", 'debug');
|
||||
} else {
|
||||
$debugger->addMessage('Page content has not been changed, do not update the file', 'debug');
|
||||
}
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
if ($locator->isStream($newFolder)) {
|
||||
$locator->clearCache();
|
||||
}
|
||||
} catch (\RuntimeException $e) {
|
||||
$name = isset($file) ? $file->filename() : $newKey;
|
||||
|
||||
throw new \RuntimeException(sprintf('Flex saveRow(%s): %s', $name, $e->getMessage()));
|
||||
}
|
||||
|
||||
$row['__META'] = $this->getObjectMeta($newKey, true);
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
protected function canDeleteFolder(string $key): bool
|
||||
{
|
||||
$keys = $this->extractKeysFromStorageKey($key);
|
||||
if ($keys['lang']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get key from the filesystem path.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
protected function getKeyFromPath(string $path): string
|
||||
{
|
||||
if ($this->base_path) {
|
||||
$path = $this->base_path . '/' . $path;
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all stored keys in [key => timestamp] pairs.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function buildIndex(): array
|
||||
{
|
||||
return $this->getIndexMeta();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param bool $reload
|
||||
* @return array
|
||||
*/
|
||||
protected function getObjectMeta(string $key, bool $reload = false): array
|
||||
{
|
||||
$keys = $this->extractKeysFromStorageKey($key);
|
||||
$key = $keys['key'];
|
||||
|
||||
if ($reload || !isset($this->meta[$key])) {
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
if (mb_strpos($key, '@@') === false) {
|
||||
$path = $locator->findResource($this->getStoragePath($key), true, true);
|
||||
} else {
|
||||
$path = null;
|
||||
}
|
||||
|
||||
$modified = 0;
|
||||
$markdown = [];
|
||||
$children = [];
|
||||
|
||||
if ($path && file_exists($path)) {
|
||||
$modified = filemtime($path);
|
||||
$iterator = new \FilesystemIterator($path, $this->flags);
|
||||
|
||||
/** @var \SplFileInfo $info */
|
||||
foreach ($iterator as $k => $info) {
|
||||
// Ignore all hidden files if set.
|
||||
if ($k === '' || ($this->ignore_hidden && $k[0] === '.')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($info->isDir()) {
|
||||
// Ignore all folders in ignore list.
|
||||
if ($this->ignore_folders && \in_array($k, $this->ignore_folders, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$children[$k] = false;
|
||||
} else {
|
||||
// Ignore all files in ignore list.
|
||||
if ($this->ignore_files && \in_array($k, $this->ignore_files, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$timestamp = $info->getMTime();
|
||||
|
||||
// Page is the one that matches to $page_extensions list with the lowest index number.
|
||||
if (preg_match($this->regex, $k, $matches)) {
|
||||
$mark = $matches[2] ?? '';
|
||||
$ext = $matches[1] ?? '';
|
||||
$ext .= $this->dataExt;
|
||||
$markdown[$mark][basename($k, $ext)] = $timestamp;
|
||||
}
|
||||
|
||||
$modified = max($modified, $timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$rawRoute = trim(preg_replace(PageIndex::PAGE_ROUTE_REGEX, '/', "/{$key}"), '/');
|
||||
$route = PageIndex::normalizeRoute($rawRoute);
|
||||
|
||||
ksort($markdown, SORT_NATURAL);
|
||||
ksort($children, SORT_NATURAL);
|
||||
|
||||
$file = array_key_first($markdown[''] ?? reset($markdown) ?: []);
|
||||
|
||||
$meta = [
|
||||
'key' => $route,
|
||||
'storage_key' => $key,
|
||||
'template' => $file,
|
||||
'storage_timestamp' => $modified,
|
||||
];
|
||||
if ($markdown) {
|
||||
$meta['markdown'] = $markdown;
|
||||
}
|
||||
if ($children) {
|
||||
$meta['children'] = $children;
|
||||
}
|
||||
$meta['checksum'] = md5(json_encode($meta));
|
||||
|
||||
// Cache meta as copy.
|
||||
$this->meta[$key] = $meta;
|
||||
} else {
|
||||
$meta = $this->meta[$key];
|
||||
}
|
||||
|
||||
$params = $keys['params'];
|
||||
if ($params) {
|
||||
$language = $keys['lang'];
|
||||
$template = $keys['template'] ?: array_key_first($meta['markdown'][$language]) ?? $meta['template'];
|
||||
$meta['exists'] = ($template && !empty($meta['children'])) || isset($meta['markdown'][$language][$template]);
|
||||
$meta['storage_key'] .= '|' . $params;
|
||||
$meta['template'] = $template;
|
||||
$meta['lang'] = $language;
|
||||
}
|
||||
|
||||
return $meta;
|
||||
}
|
||||
|
||||
protected function getIndexMeta(): array
|
||||
{
|
||||
$queue = [''];
|
||||
$list = [];
|
||||
do {
|
||||
$current = array_pop($queue);
|
||||
$meta = $this->getObjectMeta($current);
|
||||
$storage_key = $meta['storage_key'];
|
||||
|
||||
if (!empty($meta['children'])) {
|
||||
$prefix = $storage_key . ($storage_key !== '' ? '/' : '');
|
||||
|
||||
foreach ($meta['children'] as $child => $value) {
|
||||
$queue[] = $prefix . $child;
|
||||
}
|
||||
}
|
||||
|
||||
$list[$storage_key] = $meta;
|
||||
} while ($queue);
|
||||
|
||||
ksort($list, SORT_NATURAL);
|
||||
|
||||
// Update parent timestamps.
|
||||
foreach (array_reverse($list) as $storage_key => $meta) {
|
||||
if ($storage_key !== '') {
|
||||
$storage_key = (string)$storage_key;
|
||||
$parentKey = dirname($storage_key);
|
||||
if ($parentKey === '.') {
|
||||
$parentKey = '';
|
||||
}
|
||||
|
||||
$parent = &$list[$parentKey];
|
||||
$basename = basename($storage_key);
|
||||
|
||||
if (isset($parent['children'][$basename])) {
|
||||
$timestamp = $meta['storage_timestamp'];
|
||||
$parent['children'][$basename] = $timestamp;
|
||||
if ($basename && $basename[0] === '_') {
|
||||
$parent['storage_timestamp'] = max($parent['storage_timestamp'], $timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getNewKey(): string
|
||||
{
|
||||
throw new \RuntimeException('Generating random key is disabled for pages');
|
||||
}
|
||||
}
|
||||
73
system/src/Grav/Common/Page/Flex/Traits/PageContentTrait.php
Normal file
73
system/src/Grav/Common/Page/Flex/Traits/PageContentTrait.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @package Grav\Common\Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Flex\Traits;
|
||||
|
||||
use Grav\Common\Utils;
|
||||
|
||||
/**
|
||||
* Implements PageContentInterface.
|
||||
*/
|
||||
trait PageContentTrait
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function id($var = null): string
|
||||
{
|
||||
$property = 'id';
|
||||
$value = null === $var ? $this->getProperty($property) : null;
|
||||
if (null === $value) {
|
||||
$value = $this->language() . ($var ?? ($this->modified() . md5($this->filePath())));
|
||||
|
||||
$this->setProperty($property, $value);
|
||||
if ($this->doHasProperty($property)) {
|
||||
$value = $this->getProperty($property);
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function date($var = null): int
|
||||
{
|
||||
return $this->loadHeaderProperty(
|
||||
'date',
|
||||
$var,
|
||||
function ($value) {
|
||||
$value = $value ? Utils::date2timestamp($value, $this->getProperty('dateformat')) : false;
|
||||
|
||||
if (!$value) {
|
||||
// Get the specific translation updated date.
|
||||
$meta = $this->getMetaData();
|
||||
$language = $meta['lang'] ?? '';
|
||||
$template = $this->getProperty('template');
|
||||
$value = $meta['markdown'][$language][$template] ?? 0;
|
||||
}
|
||||
|
||||
return $value ?: $this->modified();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function isPage(): bool
|
||||
{
|
||||
// FIXME: needs to be better
|
||||
return !$this->exists() || !empty($this->getLanguages()) || $this->modular();
|
||||
}
|
||||
}
|
||||
233
system/src/Grav/Common/Page/Flex/Traits/PageLegacyTrait.php
Normal file
233
system/src/Grav/Common/Page/Flex/Traits/PageLegacyTrait.php
Normal file
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @package Grav\Common\Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Flex\Traits;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Page\Collection;
|
||||
use Grav\Common\Page\Interfaces\PageCollectionInterface;
|
||||
use Grav\Common\Page\Interfaces\PageInterface;
|
||||
use Grav\Common\Page\Pages;
|
||||
use Grav\Common\Utils;
|
||||
|
||||
/**
|
||||
* Implements PageLegacyInterface.
|
||||
*/
|
||||
trait PageLegacyTrait
|
||||
{
|
||||
/**
|
||||
* Returns children of this page.
|
||||
*
|
||||
* @return PageCollectionInterface|Collection
|
||||
*/
|
||||
public function children()
|
||||
{
|
||||
if (Utils::isAdminPlugin()) {
|
||||
return parent::children();
|
||||
}
|
||||
|
||||
/** @var Pages $pages */
|
||||
$pages = Grav::instance()['pages'];
|
||||
|
||||
return $pages->children($this->path());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if this item is the first in an array of sub-pages.
|
||||
*
|
||||
* @return bool True if item is first.
|
||||
*/
|
||||
public function isFirst(): bool
|
||||
{
|
||||
if (Utils::isAdminPlugin()) {
|
||||
return parent::isFirst();
|
||||
}
|
||||
|
||||
$parent = $this->parent();
|
||||
$collection = $parent ? $parent->collection('content', false) : null;
|
||||
if ($collection instanceof PageCollectionInterface) {
|
||||
return $collection->isFirst($this->path());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if this item is the last in an array of sub-pages.
|
||||
*
|
||||
* @return bool True if item is last
|
||||
*/
|
||||
public function isLast(): bool
|
||||
{
|
||||
if (Utils::isAdminPlugin()) {
|
||||
return parent::isLast();
|
||||
}
|
||||
|
||||
$parent = $this->parent();
|
||||
$collection = $parent ? $parent->collection('content', false) : null;
|
||||
if ($collection instanceof PageCollectionInterface) {
|
||||
return $collection->isLast($this->path());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the adjacent sibling based on a direction.
|
||||
*
|
||||
* @param int $direction either -1 or +1
|
||||
*
|
||||
* @return PageInterface|bool the sibling page
|
||||
*/
|
||||
public function adjacentSibling($direction = 1)
|
||||
{
|
||||
if (Utils::isAdminPlugin()) {
|
||||
return parent::adjacentSibling($direction);
|
||||
}
|
||||
|
||||
$parent = $this->parent();
|
||||
$collection = $parent ? $parent->collection('content', false) : null;
|
||||
if ($collection instanceof PageCollectionInterface) {
|
||||
return $collection->adjacentSibling($this->path(), $direction);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to return an ancestor page.
|
||||
*
|
||||
* @param string|null $lookup Name of the parent folder
|
||||
*
|
||||
* @return PageInterface|null page you were looking for if it exists
|
||||
*/
|
||||
public function ancestor($lookup = null)
|
||||
{
|
||||
if (Utils::isAdminPlugin()) {
|
||||
return parent::ancestor($lookup);
|
||||
}
|
||||
|
||||
/** @var Pages $pages */
|
||||
$pages = Grav::instance()['pages'];
|
||||
|
||||
return $pages->ancestor($this->getProperty('parent_route'), $lookup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that contains shared logic for inherited() and inheritedField()
|
||||
*
|
||||
* @param string $field Name of the parent folder
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getInheritedParams($field): array
|
||||
{
|
||||
if (Utils::isAdminPlugin()) {
|
||||
return parent::getInheritedParams($field);
|
||||
}
|
||||
|
||||
/** @var Pages $pages */
|
||||
$pages = Grav::instance()['pages'];
|
||||
|
||||
$inherited = $pages->inherited($this->getProperty('parent_route'), $field);
|
||||
$inheritedParams = $inherited ? (array)$inherited->value('header.' . $field) : [];
|
||||
$currentParams = (array)$this->getFormValue('header.' . $field);
|
||||
if ($inheritedParams && is_array($inheritedParams)) {
|
||||
$currentParams = array_replace_recursive($inheritedParams, $currentParams);
|
||||
}
|
||||
|
||||
return [$inherited, $currentParams];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to return a page.
|
||||
*
|
||||
* @param string $url the url of the page
|
||||
* @param bool $all
|
||||
*
|
||||
* @return PageInterface|null page you were looking for if it exists
|
||||
*/
|
||||
public function find($url, $all = false)
|
||||
{
|
||||
if (Utils::isAdminPlugin()) {
|
||||
return parent::find($url, $all);
|
||||
}
|
||||
|
||||
/** @var Pages $pages */
|
||||
$pages = Grav::instance()['pages'];
|
||||
|
||||
return $pages->find($url, $all);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of pages in the current context.
|
||||
*
|
||||
* @param string|array $params
|
||||
* @param bool $pagination
|
||||
*
|
||||
* @return Collection
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function collection($params = 'content', $pagination = true)
|
||||
{
|
||||
if (Utils::isAdminPlugin()) {
|
||||
return parent::collection($params, $pagination);
|
||||
}
|
||||
|
||||
if (is_string($params)) {
|
||||
// Look into a page header field.
|
||||
$params = (array)$this->getFormValue('header.' . $params);
|
||||
} elseif (!is_array($params)) {
|
||||
throw new \InvalidArgumentException('Argument should be either header variable name or array of parameters');
|
||||
}
|
||||
|
||||
if (!$pagination) {
|
||||
$params['pagination'] = false;
|
||||
}
|
||||
$context = [
|
||||
'pagination' => $pagination,
|
||||
'self' => $this
|
||||
];
|
||||
|
||||
/** @var Pages $pages */
|
||||
$pages = Grav::instance()['pages'];
|
||||
|
||||
return $pages->getCollection($params, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $value
|
||||
* @param bool $only_published
|
||||
* @return Collection
|
||||
*/
|
||||
public function evaluate($value, $only_published = true)
|
||||
{
|
||||
if (Utils::isAdminPlugin()) {
|
||||
return parent::collection($value, $only_published);
|
||||
}
|
||||
|
||||
$params = [
|
||||
'items' => $value,
|
||||
'published' => $only_published
|
||||
];
|
||||
$context = [
|
||||
'event' => false,
|
||||
'pagination' => false,
|
||||
'url_taxonomy_filters' => false,
|
||||
'self' => $this
|
||||
];
|
||||
|
||||
/** @var Pages $pages */
|
||||
$pages = Grav::instance()['pages'];
|
||||
|
||||
return $pages->getCollection($params, $context);
|
||||
}
|
||||
}
|
||||
112
system/src/Grav/Common/Page/Flex/Traits/PageRoutableTrait.php
Normal file
112
system/src/Grav/Common/Page/Flex/Traits/PageRoutableTrait.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @package Grav\Common\Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Flex\Traits;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Page\Interfaces\PageCollectionInterface;
|
||||
use Grav\Common\Page\Interfaces\PageInterface;
|
||||
use Grav\Common\Page\Pages;
|
||||
use Grav\Common\Utils;
|
||||
|
||||
/**
|
||||
* Implements PageRoutableInterface.
|
||||
*/
|
||||
trait PageRoutableTrait
|
||||
{
|
||||
/**
|
||||
* Gets and Sets the parent object for this page
|
||||
*
|
||||
* @param PageInterface|null $var the parent page object
|
||||
*
|
||||
* @return PageInterface|null the parent page object if it exists.
|
||||
*/
|
||||
|
||||
public function parent(PageInterface $var = null)
|
||||
{
|
||||
if (Utils::isAdminPlugin()) {
|
||||
return parent::parent();
|
||||
}
|
||||
|
||||
if (null !== $var) {
|
||||
throw new \RuntimeException('Not Implemented');
|
||||
}
|
||||
|
||||
/** @var Pages $pages */
|
||||
$pages = Grav::instance()['pages'];
|
||||
|
||||
// FIXME: this does not work, needs to use $pages->get() with cached parent id!
|
||||
$key = $this->getKey();
|
||||
$parent_route = dirname('/' . $key);
|
||||
|
||||
return $parent_route !== '/' ? $pages->find($parent_route) : $pages->root();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the item in the current position.
|
||||
*
|
||||
* @return int|null the index of the current page.
|
||||
*/
|
||||
public function currentPosition(): ?int
|
||||
{
|
||||
$parent = $this->parent();
|
||||
$collection = $parent ? $parent->collection('content', false) : null;
|
||||
if ($collection instanceof PageCollectionInterface) {
|
||||
return $collection->currentPosition($this->path()) ?? null;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this page is the currently active page requested via the URL.
|
||||
*
|
||||
* @return bool True if it is active
|
||||
*/
|
||||
public function active(): bool
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
$uri_path = rtrim(urldecode($grav['uri']->path()), '/') ?: '/';
|
||||
$routes = $grav['pages']->routes();
|
||||
|
||||
return isset($routes[$uri_path]) && $routes[$uri_path] === $this->path();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this URI's URL contains the URL of the active page.
|
||||
* Or in other words, is this page's URL in the current URL
|
||||
*
|
||||
* @return bool True if active child exists
|
||||
*/
|
||||
public function activeChild(): bool
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
$uri = $grav['uri'];
|
||||
$pages = $grav['pages'];
|
||||
$uri_path = rtrim(urldecode($uri->path()), '/');
|
||||
$routes = $pages->routes();
|
||||
|
||||
if (isset($routes[$uri_path])) {
|
||||
/** @var PageInterface $child_page */
|
||||
$child_page = $pages->dispatch($uri->route(), false, false)->parent();
|
||||
if ($child_page) {
|
||||
while (!$child_page->root()) {
|
||||
if ($this->path() === $child_page->path()) {
|
||||
return true;
|
||||
}
|
||||
$child_page = $child_page->parent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @package Grav\Common\Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Flex\Traits;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Language\Language;
|
||||
use Grav\Common\Page\Page;
|
||||
use Grav\Common\Utils;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
/**
|
||||
* Implements PageTranslateInterface
|
||||
*/
|
||||
trait PageTranslateTrait
|
||||
{
|
||||
/**
|
||||
* Return an array with the routes of other translated languages
|
||||
*
|
||||
* @param bool $onlyPublished only return published translations
|
||||
*
|
||||
* @return array the page translated languages
|
||||
*/
|
||||
public function translatedLanguages($onlyPublished = false): array
|
||||
{
|
||||
if (Utils::isAdminPlugin()) {
|
||||
return parent::translatedLanguages();
|
||||
}
|
||||
|
||||
$translated = $this->getLanguageTemplates();
|
||||
if (!$translated) {
|
||||
return $translated;
|
||||
}
|
||||
|
||||
$grav = Grav::instance();
|
||||
|
||||
/** @var Language $language */
|
||||
$language = $grav['language'];
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $grav['locator'];
|
||||
|
||||
$languages = $language->getLanguages();
|
||||
$languages[] = '';
|
||||
$defaultCode = $language->getDefault();
|
||||
|
||||
if (isset($translated[$defaultCode])) {
|
||||
unset($translated['']);
|
||||
}
|
||||
|
||||
foreach ($translated as $key => &$template) {
|
||||
$template .= $key !== '' ? ".{$key}.md" : '.md';
|
||||
}
|
||||
unset($template);
|
||||
|
||||
$translated = array_intersect_key($translated, array_flip($languages));
|
||||
|
||||
$folder = $this->getStorageFolder();
|
||||
if (!$folder) {
|
||||
return [];
|
||||
}
|
||||
$folder = $locator($folder);
|
||||
|
||||
$list = array_fill_keys($languages, null);
|
||||
foreach ($translated as $languageCode => $languageFile) {
|
||||
$languageExtension = $languageCode ? ".{$languageCode}.md" : '.md';
|
||||
$path = "{$folder}/{$languageFile}";
|
||||
|
||||
// FIXME: use flex, also rawRoute() does not fully work?
|
||||
$aPage = new Page();
|
||||
$aPage->init(new \SplFileInfo($path), $languageExtension);
|
||||
if ($onlyPublished && !$aPage->published()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$route = $aPage->header()->routes['default'] ?? $aPage->rawRoute();
|
||||
if (!$route) {
|
||||
$route = $aPage->route();
|
||||
}
|
||||
|
||||
$list[$languageCode ?: $defaultCode] = $route ?? '';
|
||||
}
|
||||
|
||||
return array_filter($list, static function ($var) {
|
||||
return null !== $var;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,18 @@
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
use RocketTheme\Toolbox\ArrayTraits\Constructor;
|
||||
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccess;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
|
||||
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
|
||||
|
||||
class Header implements \ArrayAccess
|
||||
class Header implements \ArrayAccess, ExportInterface, \JsonSerializable
|
||||
{
|
||||
use NestedArrayAccess, Constructor;
|
||||
use NestedArrayAccessWithGetters, Constructor, Export;
|
||||
|
||||
protected $items;
|
||||
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Grav\Common\Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Interfaces;
|
||||
|
||||
interface PageCollectionInterface extends \Traversable, \ArrayAccess, \Countable, \Serializable
|
||||
{
|
||||
/**
|
||||
* Get the collection params
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function params();
|
||||
|
||||
/**
|
||||
* Set parameters to the Collection
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setParams(array $params);
|
||||
|
||||
/**
|
||||
* Add a single page to a collection
|
||||
*
|
||||
* @param PageInterface $page
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addPage(PageInterface $page);
|
||||
|
||||
/**
|
||||
* Add a page with path and slug
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $slug
|
||||
* @return $this
|
||||
*/
|
||||
//public function add($path, $slug);
|
||||
|
||||
/**
|
||||
*
|
||||
* Create a copy of this collection
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function copy();
|
||||
|
||||
/**
|
||||
*
|
||||
* Merge another collection with the current collection
|
||||
*
|
||||
* @param PageCollectionInterface $collection
|
||||
* @return $this
|
||||
*/
|
||||
public function merge(PageCollectionInterface $collection);
|
||||
|
||||
/**
|
||||
* Intersect another collection with the current collection
|
||||
*
|
||||
* @param PageCollectionInterface $collection
|
||||
* @return $this
|
||||
*/
|
||||
public function intersect(PageCollectionInterface $collection);
|
||||
|
||||
/**
|
||||
* Split collection into array of smaller collections.
|
||||
*
|
||||
* @param int $size
|
||||
* @return PageCollectionInterface[]
|
||||
*/
|
||||
public function batch($size);
|
||||
|
||||
/**
|
||||
* Remove item from the list.
|
||||
*
|
||||
* @param PageInterface|string|null $key
|
||||
*
|
||||
* @return $this
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
//public function remove($key = null);
|
||||
|
||||
/**
|
||||
* Reorder collection.
|
||||
*
|
||||
* @param string $by
|
||||
* @param string $dir
|
||||
* @param array $manual
|
||||
* @param string $sort_flags
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function order($by, $dir = 'asc', $manual = null, $sort_flags = null);
|
||||
|
||||
/**
|
||||
* Check to see if this item is the first in the collection.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool True if item is first.
|
||||
*/
|
||||
public function isFirst($path): bool;
|
||||
|
||||
/**
|
||||
* Check to see if this item is the last in the collection.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool True if item is last.
|
||||
*/
|
||||
public function isLast($path): bool;
|
||||
|
||||
/**
|
||||
* Gets the previous sibling based on current position.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return PageInterface The previous item.
|
||||
*/
|
||||
public function prevSibling($path);
|
||||
|
||||
/**
|
||||
* Gets the next sibling based on current position.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return PageInterface The next item.
|
||||
*/
|
||||
public function nextSibling($path);
|
||||
|
||||
/**
|
||||
* Returns the adjacent sibling based on a direction.
|
||||
*
|
||||
* @param string $path
|
||||
* @param int $direction either -1 or +1
|
||||
*
|
||||
* @return PageInterface|PageCollectionInterface The sibling item.
|
||||
*/
|
||||
public function adjacentSibling($path, $direction = 1);
|
||||
|
||||
/**
|
||||
* Returns the item in the current position.
|
||||
*
|
||||
* @param string $path the path the item
|
||||
*
|
||||
* @return int|null The index of the current page, null if not found.
|
||||
*/
|
||||
public function currentPosition($path): ?int;
|
||||
|
||||
/**
|
||||
* Returns the items between a set of date ranges of either the page date field (default) or
|
||||
* an arbitrary datetime page field where end date is optional
|
||||
* Dates can be passed in as text that strtotime() can process
|
||||
* http://php.net/manual/en/function.strtotime.php
|
||||
*
|
||||
* @param string $startDate
|
||||
* @param bool $endDate
|
||||
* @param string|null $field
|
||||
*
|
||||
* @return $this
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function dateRange($startDate, $endDate = false, $field = null);
|
||||
|
||||
/**
|
||||
* Creates new collection with only visible pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only visible pages
|
||||
*/
|
||||
public function visible();
|
||||
|
||||
/**
|
||||
* Creates new collection with only non-visible pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only non-visible pages
|
||||
*/
|
||||
public function nonVisible();
|
||||
|
||||
/**
|
||||
* Creates new collection with only modular pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only modular pages
|
||||
*/
|
||||
public function modular();
|
||||
|
||||
/**
|
||||
* Creates new collection with only non-modular pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only non-modular pages
|
||||
*/
|
||||
public function nonModular();
|
||||
|
||||
/**
|
||||
* Creates new collection with only published pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only published pages
|
||||
*/
|
||||
public function published();
|
||||
|
||||
/**
|
||||
* Creates new collection with only non-published pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only non-published pages
|
||||
*/
|
||||
public function nonPublished();
|
||||
|
||||
/**
|
||||
* Creates new collection with only routable pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only routable pages
|
||||
*/
|
||||
public function routable();
|
||||
|
||||
/**
|
||||
* Creates new collection with only non-routable pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only non-routable pages
|
||||
*/
|
||||
public function nonRoutable();
|
||||
|
||||
/**
|
||||
* Creates new collection with only pages of the specified type
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return PageCollectionInterface The collection
|
||||
*/
|
||||
public function ofType($type);
|
||||
|
||||
/**
|
||||
* Creates new collection with only pages of one of the specified types
|
||||
*
|
||||
* @param string[] $types
|
||||
*
|
||||
* @return PageCollectionInterface The collection
|
||||
*/
|
||||
public function ofOneOfTheseTypes($types);
|
||||
|
||||
/**
|
||||
* Creates new collection with only pages of one of the specified access levels
|
||||
*
|
||||
* @param array $accessLevels
|
||||
*
|
||||
* @return PageCollectionInterface The collection
|
||||
*/
|
||||
public function ofOneOfTheseAccessLevels($accessLevels);
|
||||
|
||||
/**
|
||||
* Converts collection into an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray();
|
||||
|
||||
/**
|
||||
* Get the extended version of this Collection with each page keyed by route
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function toExtendedArray();
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
namespace Grav\Common\Page\Interfaces;
|
||||
|
||||
use Grav\Common\Page\Media;
|
||||
use Grav\Common\Media\Interfaces\MediaCollectionInterface;
|
||||
|
||||
/**
|
||||
* Methods currently implemented in Flex Page emulation layer.
|
||||
@@ -20,7 +20,6 @@ interface PageContentInterface
|
||||
* Gets and Sets the header based on the YAML configuration at the top of the .md file
|
||||
*
|
||||
* @param object|array $var a YAML object representing the configuration for the file
|
||||
*
|
||||
* @return object the current YAML configuration
|
||||
*/
|
||||
public function header($var = null);
|
||||
@@ -28,19 +27,23 @@ interface PageContentInterface
|
||||
/**
|
||||
* Get the summary.
|
||||
*
|
||||
* @param int $size Max summary size.
|
||||
*
|
||||
* @param int|null $size Max summary size.
|
||||
* @param bool $textOnly Only count text size.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function summary($size = null, $textOnly = false);
|
||||
|
||||
/**
|
||||
* Sets the summary of the page
|
||||
*
|
||||
* @param string $summary Summary
|
||||
*/
|
||||
public function setSummary($summary);
|
||||
|
||||
/**
|
||||
* Gets and Sets the content based on content portion of the .md file
|
||||
*
|
||||
* @param string $var Content
|
||||
*
|
||||
* @param string|null $var Content
|
||||
* @return string Content
|
||||
*/
|
||||
public function content($var = null);
|
||||
@@ -63,8 +66,7 @@ interface PageContentInterface
|
||||
* Gets and Sets the Page raw content
|
||||
*
|
||||
* @param string|null $var
|
||||
*
|
||||
* @return null
|
||||
* @return string
|
||||
*/
|
||||
public function rawMarkdown($var = null);
|
||||
|
||||
@@ -72,7 +74,7 @@ interface PageContentInterface
|
||||
* Get value from a page variable (used mostly for creating edit forms).
|
||||
*
|
||||
* @param string $name Variable name.
|
||||
* @param mixed $default
|
||||
* @param mixed|null $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -81,18 +83,16 @@ interface PageContentInterface
|
||||
/**
|
||||
* Gets and sets the associated media as found in the page folder.
|
||||
*
|
||||
* @param Media $var Representation of associated media.
|
||||
*
|
||||
* @return Media Representation of associated media.
|
||||
* @param MediaCollectionInterface|null $var New media object.
|
||||
* @return MediaCollectionInterface Representation of associated media.
|
||||
*/
|
||||
public function media($var = null);
|
||||
|
||||
/**
|
||||
* Gets and sets the title for this Page. If no title is set, it will use the slug() to get a name
|
||||
*
|
||||
* @param string $var the title of the Page
|
||||
*
|
||||
* @return string the title of the Page
|
||||
* @param string|null $var New title of the Page
|
||||
* @return string The title of the Page
|
||||
*/
|
||||
public function title($var = null);
|
||||
|
||||
@@ -100,45 +100,40 @@ interface PageContentInterface
|
||||
* Gets and sets the menu name for this Page. This is the text that can be used specifically for navigation.
|
||||
* If no menu field is set, it will use the title()
|
||||
*
|
||||
* @param string $var the menu field for the page
|
||||
*
|
||||
* @return string the menu field for the page
|
||||
* @param string|null $var New menu field for the page
|
||||
* @return string The menu field for the page
|
||||
*/
|
||||
public function menu($var = null);
|
||||
|
||||
/**
|
||||
* Gets and Sets whether or not this Page is visible for navigation
|
||||
*
|
||||
* @param bool $var true if the page is visible
|
||||
*
|
||||
* @return bool true if the page is visible
|
||||
* @param bool|null $var New value
|
||||
* @return bool True if the page is visible
|
||||
*/
|
||||
public function visible($var = null);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param bool|null $var New value
|
||||
* @return bool True if the page is published
|
||||
*/
|
||||
public function published($var = null);
|
||||
|
||||
/**
|
||||
* Gets and Sets the Page publish date
|
||||
*
|
||||
* @param string $var string representation of a date
|
||||
*
|
||||
* @return int unix timestamp representation of the date
|
||||
* @param string|null $var String representation of the new date
|
||||
* @return int Unix timestamp representation of the date
|
||||
*/
|
||||
public function publishDate($var = null);
|
||||
|
||||
/**
|
||||
* Gets and Sets the Page unpublish date
|
||||
*
|
||||
* @param string $var string representation of a date
|
||||
*
|
||||
* @return int|null unix timestamp representation of the date
|
||||
* @param string|null $var String representation of the new date
|
||||
* @return int|null Unix timestamp representation of the date
|
||||
*/
|
||||
public function unpublishDate($var = null);
|
||||
|
||||
@@ -146,9 +141,8 @@ interface PageContentInterface
|
||||
* Gets and Sets the process setup for this Page. This is multi-dimensional array that consists of
|
||||
* a simple array of arrays with the form array("markdown"=>true) for example
|
||||
*
|
||||
* @param array $var an Array of name value pairs where the name is the process and value is true or false
|
||||
*
|
||||
* @return array an Array of name value pairs where the name is the process and value is true or false
|
||||
* @param array|null $var New array of name value pairs where the name is the process and value is true or false
|
||||
* @return array Array of name value pairs where the name is the process and value is true or false
|
||||
*/
|
||||
public function process($var = null);
|
||||
|
||||
@@ -156,63 +150,56 @@ interface PageContentInterface
|
||||
* Gets and Sets the slug for the Page. The slug is used in the URL routing. If not set it uses
|
||||
* the parent folder from the path
|
||||
*
|
||||
* @param string $var the slug, e.g. 'my-blog'
|
||||
*
|
||||
* @return string the slug
|
||||
* @param string|null $var New slug, e.g. 'my-blog'
|
||||
* @return string The slug
|
||||
*/
|
||||
public function slug($var = null);
|
||||
|
||||
/**
|
||||
* Get/set order number of this page.
|
||||
*
|
||||
* @param int $var
|
||||
*
|
||||
* @return int|bool
|
||||
* @param int|null $var New order as a number
|
||||
* @return string|bool Order in a form of '02.' or false if not set
|
||||
*/
|
||||
public function order($var = null);
|
||||
|
||||
/**
|
||||
* Gets and sets the identifier for this Page object.
|
||||
*
|
||||
* @param string $var the identifier
|
||||
*
|
||||
* @return string the identifier
|
||||
* @param string|null $var New identifier
|
||||
* @return string The identifier
|
||||
*/
|
||||
public function id($var = null);
|
||||
|
||||
/**
|
||||
* Gets and sets the modified timestamp.
|
||||
*
|
||||
* @param int $var modified unix timestamp
|
||||
*
|
||||
* @return int modified unix timestamp
|
||||
* @param int|null $var New modified unix timestamp
|
||||
* @return int Modified unix timestamp
|
||||
*/
|
||||
public function modified($var = null);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param bool|null $var New last_modified header value
|
||||
* @return bool Show last_modified header
|
||||
*/
|
||||
public function lastModified($var = null);
|
||||
|
||||
/**
|
||||
* Get/set the folder.
|
||||
*
|
||||
* @param string $var Optional path
|
||||
*
|
||||
* @return string|null
|
||||
* @param string|null $var New folder
|
||||
* @return string|null The folder
|
||||
*/
|
||||
public function folder($var = null);
|
||||
|
||||
/**
|
||||
* Gets and sets the date for this Page object. This is typically passed in via the page headers
|
||||
*
|
||||
* @param string $var string representation of a date
|
||||
*
|
||||
* @return int unix timestamp representation of the date
|
||||
* @param string|null $var New string representation of a date
|
||||
* @return int Unix timestamp representation of the date
|
||||
*/
|
||||
public function date($var = null);
|
||||
|
||||
@@ -220,27 +207,24 @@ interface PageContentInterface
|
||||
* Gets and sets the date format for this Page object. This is typically passed in via the page headers
|
||||
* using typical PHP date string structure - http://php.net/manual/en/function.date.php
|
||||
*
|
||||
* @param string $var string representation of a date format
|
||||
*
|
||||
* @return string string representation of a date format
|
||||
* @param string|null $var New string representation of a date format
|
||||
* @return string String representation of a date format
|
||||
*/
|
||||
public function dateformat($var = null);
|
||||
|
||||
/**
|
||||
* Gets and sets the taxonomy array which defines which taxonomies this page identifies itself with.
|
||||
*
|
||||
* @param array $var an array of taxonomies
|
||||
*
|
||||
* @return array an array of taxonomies
|
||||
* @param array|null $var New array of taxonomies
|
||||
* @return array An array of taxonomies
|
||||
*/
|
||||
public function taxonomy($var = null);
|
||||
|
||||
/**
|
||||
* Gets the configured state of the processing method.
|
||||
*
|
||||
* @param string $process the process, eg "twig" or "markdown"
|
||||
*
|
||||
* @return bool whether or not the processing method is enabled for this Page
|
||||
* @param string $process The process name, eg "twig" or "markdown"
|
||||
* @return bool Whether or not the processing method is enabled for this Page
|
||||
*/
|
||||
public function shouldProcess($process);
|
||||
|
||||
|
||||
29
system/src/Grav/Common/Page/Interfaces/PageFormInterface.php
Normal file
29
system/src/Grav/Common/Page/Interfaces/PageFormInterface.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Interfaces;
|
||||
|
||||
interface PageFormInterface
|
||||
{
|
||||
/**
|
||||
* Return all the forms which are associated to this page.
|
||||
*
|
||||
* Forms are returned as [name => blueprint, ...], where blueprint follows the regular form blueprint format.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
//public function getForms(): array;
|
||||
|
||||
/**
|
||||
* Add forms to this page.
|
||||
*
|
||||
* @param array $new
|
||||
* @return $this
|
||||
*/
|
||||
public function addForms(array $new/*, $override = true*/);
|
||||
|
||||
/**
|
||||
* Alias of $this->getForms();
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function forms();//: array;
|
||||
}
|
||||
@@ -14,6 +14,12 @@ use Grav\Common\Media\Interfaces\MediaInterface;
|
||||
/**
|
||||
* Class implements page interface.
|
||||
*/
|
||||
interface PageInterface extends PageContentInterface, PageRoutableInterface, PageTranslateInterface, MediaInterface, PageLegacyInterface
|
||||
interface PageInterface extends
|
||||
PageContentInterface,
|
||||
PageFormInterface,
|
||||
PageRoutableInterface,
|
||||
PageTranslateInterface,
|
||||
MediaInterface,
|
||||
PageLegacyInterface
|
||||
{
|
||||
}
|
||||
|
||||
@@ -51,13 +51,6 @@ interface PageLegacyInterface
|
||||
|
||||
public function httpHeaders();
|
||||
|
||||
/**
|
||||
* Sets the summary of the page
|
||||
*
|
||||
* @param string $summary Summary
|
||||
*/
|
||||
public function setSummary($summary);
|
||||
|
||||
/**
|
||||
* Get the contentMeta array and initialize content first if it's not already
|
||||
*
|
||||
@@ -69,7 +62,7 @@ interface PageLegacyInterface
|
||||
* Add an entry to the page's contentMeta array
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function addContentMeta($name, $value);
|
||||
|
||||
@@ -229,9 +222,9 @@ interface PageLegacyInterface
|
||||
* Allows a page to override the output render format, usually the extension provided
|
||||
* in the URL. (e.g. `html`, `json`, `xml`, etc).
|
||||
*
|
||||
* @param null $var
|
||||
* @param string|null $var
|
||||
*
|
||||
* @return null
|
||||
* @return string
|
||||
*/
|
||||
public function templateFormat($var = null);
|
||||
|
||||
@@ -288,7 +281,7 @@ interface PageLegacyInterface
|
||||
*
|
||||
* @return bool show etag header
|
||||
*/
|
||||
public function eTag($var = null);
|
||||
public function eTag($var = null): bool;
|
||||
|
||||
/**
|
||||
* Gets and sets the path to the .md file for this Page object.
|
||||
|
||||
@@ -119,7 +119,7 @@ interface PageRoutableInterface
|
||||
* Gets and sets the path to the folder where the .md for this Page object resides.
|
||||
* This is equivalent to the filePath but without the filename.
|
||||
*
|
||||
* @param string $var the path
|
||||
* @param string|null $var the path
|
||||
*
|
||||
* @return string|null the path
|
||||
*/
|
||||
@@ -137,7 +137,7 @@ interface PageRoutableInterface
|
||||
/**
|
||||
* Gets and Sets the parent object for this page
|
||||
*
|
||||
* @param PageInterface $var the parent page object
|
||||
* @param PageInterface|null $var the parent page object
|
||||
*
|
||||
* @return PageInterface|null the parent page object if it exists.
|
||||
*/
|
||||
@@ -153,7 +153,7 @@ interface PageRoutableInterface
|
||||
/**
|
||||
* Returns the item in the current position.
|
||||
*
|
||||
* @return int the index of the current page.
|
||||
* @return int|null The index of the current page.
|
||||
*/
|
||||
public function currentPosition();
|
||||
|
||||
|
||||
@@ -108,12 +108,12 @@ class Excerpts
|
||||
}
|
||||
}
|
||||
|
||||
$url_parts['query'] = http_build_query($actions, null, '&', PHP_QUERY_RFC3986);
|
||||
$url_parts['query'] = http_build_query($actions, '', '&', PHP_QUERY_RFC3986);
|
||||
}
|
||||
|
||||
// If no query elements left, unset query.
|
||||
if (empty($url_parts['query'])) {
|
||||
unset ($url_parts['query']);
|
||||
unset($url_parts['query']);
|
||||
}
|
||||
|
||||
// Set path to / if not set.
|
||||
@@ -162,7 +162,6 @@ class Excerpts
|
||||
$filename = $url_parts['scheme'] . '://' . ($url_parts['path'] ?? '');
|
||||
|
||||
$media = $this->page->getMedia();
|
||||
|
||||
} else {
|
||||
$grav = Grav::instance();
|
||||
|
||||
@@ -185,7 +184,7 @@ class Excerpts
|
||||
$page_route = '/' . ltrim(str_replace($base_url, '', $folder), '/');
|
||||
|
||||
/** @var PageInterface $ext_page */
|
||||
$ext_page = $grav['pages']->dispatch($page_route, true);
|
||||
$ext_page = $grav['pages']->find($page_route, true);
|
||||
if ($ext_page) {
|
||||
$media = $ext_page->getMedia();
|
||||
} else {
|
||||
@@ -211,7 +210,6 @@ class Excerpts
|
||||
$id = $element_excerpt['id'] ?? '';
|
||||
|
||||
$excerpt['element'] = $medium->parsedownElement($title, $alt, $class, $id, true);
|
||||
|
||||
} else {
|
||||
// Not a current page media file, see if it needs converting to relative.
|
||||
$excerpt['element']['attributes']['src'] = Uri::buildUrl($url_parts);
|
||||
|
||||
@@ -25,7 +25,7 @@ class Media extends AbstractMedia
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $media_order
|
||||
* @param array|null $media_order
|
||||
* @param bool $load
|
||||
*/
|
||||
public function __construct($path, array $media_order = null, $load = true)
|
||||
@@ -78,21 +78,21 @@ class Media extends AbstractMedia
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
$config = Grav::instance()['config'];
|
||||
$locator = Grav::instance()['locator'];
|
||||
$exif_reader = isset(Grav::instance()['exif']) ? Grav::instance()['exif']->getReader() : false;
|
||||
$media_types = array_keys(Grav::instance()['config']->get('media.types'));
|
||||
$path = $this->getPath();
|
||||
|
||||
// Handle special cases where page doesn't exist in filesystem.
|
||||
if (!is_dir($this->getPath())) {
|
||||
if (!$path || !is_dir($path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$iterator = new \FilesystemIterator($this->getPath(), \FilesystemIterator::UNIX_PATHS | \FilesystemIterator::SKIP_DOTS);
|
||||
$iterator = new \FilesystemIterator($path, \FilesystemIterator::UNIX_PATHS | \FilesystemIterator::SKIP_DOTS);
|
||||
|
||||
$media = [];
|
||||
|
||||
/** @var \DirectoryIterator $info */
|
||||
foreach ($iterator as $path => $info) {
|
||||
foreach ($iterator as $file => $info) {
|
||||
// Ignore folders and Markdown files.
|
||||
if (!$info->isFile() || $info->getExtension() === 'md' || strpos($info->getFilename(), '.') === 0) {
|
||||
continue;
|
||||
@@ -106,9 +106,9 @@ class Media extends AbstractMedia
|
||||
}
|
||||
|
||||
if ($type === 'alternative') {
|
||||
$media["{$basename}.{$ext}"][$type][$extra] = ['file' => $path, 'size' => $info->getSize()];
|
||||
$media["{$basename}.{$ext}"][$type][$extra] = ['file' => $file, 'size' => $info->getSize()];
|
||||
} else {
|
||||
$media["{$basename}.{$ext}"][$type] = ['file' => $path, 'size' => $info->getSize()];
|
||||
$media["{$basename}.{$ext}"][$type] = ['file' => $file, 'size' => $info->getSize()];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,12 +118,13 @@ class Media extends AbstractMedia
|
||||
foreach ($types['alternative'] as $ratio => &$alt) {
|
||||
$alt['file'] = MediumFactory::fromFile($alt['file']);
|
||||
|
||||
if (!$alt['file']) {
|
||||
if (empty($alt['file'])) {
|
||||
unset($types['alternative'][$ratio]);
|
||||
} else {
|
||||
$alt['file']->set('size', $alt['size']);
|
||||
}
|
||||
}
|
||||
unset($alt);
|
||||
}
|
||||
|
||||
$file_path = null;
|
||||
@@ -141,7 +142,7 @@ class Media extends AbstractMedia
|
||||
} else {
|
||||
$medium = MediumFactory::fromFile($types['base']['file']);
|
||||
$medium && $medium->set('size', $types['base']['size']);
|
||||
$file_path = $medium->path();
|
||||
$file_path = $medium ? $medium->path() : null;
|
||||
}
|
||||
|
||||
if (empty($medium)) {
|
||||
@@ -154,7 +155,6 @@ class Media extends AbstractMedia
|
||||
if (file_exists($meta_path)) {
|
||||
$types['meta']['file'] = $meta_path;
|
||||
} elseif ($file_path && $exif_reader && $medium->get('mime') === 'image/jpeg' && empty($types['meta']) && $config->get('system.media.auto_metadata_exif')) {
|
||||
|
||||
$meta = $exif_reader->read($file_path);
|
||||
|
||||
if ($meta) {
|
||||
|
||||
@@ -39,7 +39,7 @@ class AudioMedium extends Medium
|
||||
*/
|
||||
public function controls($display = true)
|
||||
{
|
||||
if($display) {
|
||||
if ($display) {
|
||||
$this->attributes['controls'] = true;
|
||||
} else {
|
||||
unset($this->attributes['controls']);
|
||||
@@ -88,7 +88,7 @@ class AudioMedium extends Medium
|
||||
*/
|
||||
public function muted($status = false)
|
||||
{
|
||||
if($status) {
|
||||
if ($status) {
|
||||
$this->attributes['muted'] = true;
|
||||
} else {
|
||||
unset($this->attributes['muted']);
|
||||
@@ -105,7 +105,7 @@ class AudioMedium extends Medium
|
||||
*/
|
||||
public function loop($status = false)
|
||||
{
|
||||
if($status) {
|
||||
if ($status) {
|
||||
$this->attributes['loop'] = true;
|
||||
} else {
|
||||
unset($this->attributes['loop']);
|
||||
@@ -122,7 +122,7 @@ class AudioMedium extends Medium
|
||||
*/
|
||||
public function autoplay($status = false)
|
||||
{
|
||||
if($status) {
|
||||
if ($status) {
|
||||
$this->attributes['autoplay'] = true;
|
||||
} else {
|
||||
unset($this->attributes['autoplay']);
|
||||
|
||||
@@ -17,7 +17,7 @@ class GlobalMedia extends AbstractMedia
|
||||
/**
|
||||
* Return media path.
|
||||
*
|
||||
* @return null
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
|
||||
@@ -109,8 +109,8 @@ class ImageFile extends Image
|
||||
* Gets the hash.
|
||||
* @param string $type
|
||||
* @param int $quality
|
||||
* @param [] $extras
|
||||
* @return null
|
||||
* @param array $extras
|
||||
* @return string
|
||||
*/
|
||||
public function getHash($type = 'guess', $quality = 80, $extras = [])
|
||||
{
|
||||
@@ -141,5 +141,4 @@ class ImageFile extends Image
|
||||
|
||||
$this->hash = sha1(serialize($datas));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ class ImageMedium extends Medium
|
||||
* Construct.
|
||||
*
|
||||
* @param array $items
|
||||
* @param Blueprint $blueprint
|
||||
* @param Blueprint|null $blueprint
|
||||
*/
|
||||
public function __construct($items = [], Blueprint $blueprint = null)
|
||||
{
|
||||
@@ -626,6 +626,7 @@ class ImageMedium extends Medium
|
||||
* Filter image by using user defined filter parameters.
|
||||
*
|
||||
* @param string $filter Filter to be used.
|
||||
* @return $this
|
||||
*/
|
||||
public function filter($filter = 'image.filters.default')
|
||||
{
|
||||
@@ -635,6 +636,8 @@ class ImageMedium extends Medium
|
||||
$method = array_shift($params);
|
||||
$this->__call($method, $params);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -646,10 +649,8 @@ class ImageMedium extends Medium
|
||||
{
|
||||
if ($this->alternatives) {
|
||||
$max = reset($this->alternatives);
|
||||
foreach($this->alternatives as $alternative)
|
||||
{
|
||||
if($alternative->quality() > $max->quality())
|
||||
{
|
||||
foreach ($this->alternatives as $alternative) {
|
||||
if ($alternative->quality() > $max->quality()) {
|
||||
$max = $alternative;
|
||||
}
|
||||
}
|
||||
@@ -659,5 +660,4 @@ class ImageMedium extends Medium
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,10 +34,10 @@ class Link implements RenderableInterface
|
||||
/**
|
||||
* Get an element (is array) that can be rendered by the Parsedown engine
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param string $id
|
||||
* @param string|null $title
|
||||
* @param string|null $alt
|
||||
* @param string|null $class
|
||||
* @param string|null $id
|
||||
* @param bool $reset
|
||||
* @return array
|
||||
*/
|
||||
|
||||
@@ -32,7 +32,7 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
|
||||
protected $mode = 'source';
|
||||
|
||||
/**
|
||||
* @var Medium
|
||||
* @var Medium|null
|
||||
*/
|
||||
protected $_thumbnail = null;
|
||||
|
||||
@@ -74,7 +74,7 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
|
||||
* Construct.
|
||||
*
|
||||
* @param array $items
|
||||
* @param Blueprint $blueprint
|
||||
* @param Blueprint|null $blueprint
|
||||
*/
|
||||
public function __construct($items = [], Blueprint $blueprint = null)
|
||||
{
|
||||
@@ -281,7 +281,7 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
|
||||
/**
|
||||
* Get/set querystring for the file's url
|
||||
*
|
||||
* @param string $querystring
|
||||
* @param string|null $querystring
|
||||
* @param bool $withQuestionmark
|
||||
* @return string
|
||||
*/
|
||||
@@ -345,10 +345,10 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
|
||||
/**
|
||||
* Get an element (is array) that can be rendered by the Parsedown engine
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param string $id
|
||||
* @param string|null $title
|
||||
* @param string|null $alt
|
||||
* @param string|null $class
|
||||
* @param string|null $id
|
||||
* @param bool $reset
|
||||
* @return array
|
||||
*/
|
||||
@@ -358,10 +358,11 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
|
||||
|
||||
$style = '';
|
||||
foreach ($this->styleAttributes as $key => $value) {
|
||||
if (is_numeric($key)) // Special case for inline style attributes, refer to style() method
|
||||
if (is_numeric($key)) { // Special case for inline style attributes, refer to style() method
|
||||
$style .= $value;
|
||||
else
|
||||
} else {
|
||||
$style .= $key . ': ' . $value . ';';
|
||||
}
|
||||
}
|
||||
if ($style) {
|
||||
$attributes['style'] = $style;
|
||||
@@ -478,7 +479,7 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
|
||||
*
|
||||
* @param string $mode
|
||||
*
|
||||
* @return $this
|
||||
* @return $this|null
|
||||
*/
|
||||
public function display($mode = 'source')
|
||||
{
|
||||
@@ -675,5 +676,4 @@ class Medium extends Data implements RenderableInterface, MediaObjectInterface
|
||||
|
||||
return $this->_thumbnail;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class MediumFactory
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $params
|
||||
* @return Medium
|
||||
* @return Medium|null
|
||||
*/
|
||||
public static function fromFile($file, array $params = [])
|
||||
{
|
||||
@@ -73,7 +73,7 @@ class MediumFactory
|
||||
*
|
||||
* @param FormFlashFile $uploadedFile
|
||||
* @param array $params
|
||||
* @return Medium
|
||||
* @return Medium|null
|
||||
*/
|
||||
public static function fromUploadedFile(FormFlashFile $uploadedFile, array $params = [])
|
||||
{
|
||||
@@ -82,7 +82,7 @@ class MediumFactory
|
||||
$ext = $parts['extension'];
|
||||
$basename = $parts['filename'];
|
||||
$file = $uploadedFile->getTmpFile();
|
||||
$path = dirname($file);
|
||||
$path = $file ? dirname($file) : '';
|
||||
|
||||
$config = Grav::instance()['config'];
|
||||
|
||||
@@ -104,7 +104,7 @@ class MediumFactory
|
||||
'basename' => $basename,
|
||||
'extension' => $ext,
|
||||
'path' => $path,
|
||||
'modified' => filemtime($file),
|
||||
'modified' => $file ? filemtime($file) : 0,
|
||||
'thumbnails' => []
|
||||
];
|
||||
|
||||
@@ -149,7 +149,7 @@ class MediumFactory
|
||||
/**
|
||||
* Create a new ImageMedium by scaling another ImageMedium object.
|
||||
*
|
||||
* @param ImageMedium $medium
|
||||
* @param ImageMedium|Medium $medium
|
||||
* @param int $from
|
||||
* @param int $to
|
||||
* @return Medium|array
|
||||
|
||||
@@ -22,10 +22,10 @@ trait ParsedownHtmlTrait
|
||||
/**
|
||||
* Return HTML markup from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param string $id
|
||||
* @param string|null $title
|
||||
* @param string|null $alt
|
||||
* @param string|null $class
|
||||
* @param string|null $id
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -14,23 +14,24 @@ interface RenderableInterface
|
||||
/**
|
||||
* Return HTML markup from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param string|null $title
|
||||
* @param string|null $alt
|
||||
* @param string|null $class
|
||||
* @param string|null $id
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function html($title = null, $alt = null, $class = null, $reset = true);
|
||||
public function html($title = null, $alt = null, $class = null, $id = null, $reset = true);
|
||||
|
||||
/**
|
||||
* Return Parsedown Element from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param string $id
|
||||
* @param string|null $title
|
||||
* @param string|null $alt
|
||||
* @param string|null $class
|
||||
* @param string|null $id
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
* @return array
|
||||
*/
|
||||
public function parsedownElement($title = null, $alt = null, $class = null, $id = null, $reset = true);
|
||||
}
|
||||
|
||||
@@ -35,11 +35,11 @@ class ThumbnailImageMedium extends ImageMedium
|
||||
/**
|
||||
* Get an element (is array) that can be rendered by the Parsedown engine
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param string $id
|
||||
* @param bool $reset
|
||||
* @param string|null $title
|
||||
* @param string|null $alt
|
||||
* @param string|null $class
|
||||
* @param string|null $id
|
||||
* @param bool $reset
|
||||
* @return array
|
||||
*/
|
||||
public function parsedownElement($title = null, $alt = null, $class = null, $id = null, $reset = true)
|
||||
@@ -50,10 +50,10 @@ class ThumbnailImageMedium extends ImageMedium
|
||||
/**
|
||||
* Return HTML markup from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param string $id
|
||||
* @param string|null $title
|
||||
* @param string|null $alt
|
||||
* @param string|null $class
|
||||
* @param string|null $id
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
@@ -119,7 +119,7 @@ class ThumbnailImageMedium extends ImageMedium
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @param bool $testLinked
|
||||
* @return Medium
|
||||
* @return Medium|Link|array
|
||||
*/
|
||||
protected function bubble($method, array $arguments = [], $testLinked = true)
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user