mirror of
https://github.com/getgrav/grav.git
synced 2025-12-05 23:39:58 +01:00
Compare commits
170 Commits
1.7.0-beta
...
1.6.24
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ddc98b2b6 | ||
|
|
d16a88e731 | ||
|
|
39d0d640e6 | ||
|
|
586105907d | ||
|
|
15af5bfae7 | ||
|
|
d550a016a9 | ||
|
|
9c8df27bf1 | ||
|
|
1ce0176ab6 | ||
|
|
ccac8a93bc | ||
|
|
bcbfa0e32a | ||
|
|
453cd62a51 | ||
|
|
56e1cbc78e | ||
|
|
db92c7b32d | ||
|
|
2eae104c7a | ||
|
|
6f2be2a2d2 | ||
|
|
e75960fee3 | ||
|
|
ec71ccdd0d | ||
|
|
152c987ed4 | ||
|
|
19311c7ec1 | ||
|
|
eec2d122cc | ||
|
|
62f39fe39c | ||
|
|
40bc980084 | ||
|
|
1196e06dd6 | ||
|
|
64b33d60f4 | ||
|
|
8ccbcf0488 | ||
|
|
244c34a536 | ||
|
|
52a704e53d | ||
|
|
addecbbb22 | ||
|
|
ec8bf9357a | ||
|
|
432b4b1e68 | ||
|
|
ea5935b1d1 | ||
|
|
fa3c9095c7 | ||
|
|
95442ef0b5 | ||
|
|
f81503dd70 | ||
|
|
48170d2fa0 | ||
|
|
ef80e28d1d | ||
|
|
e55b239536 | ||
|
|
c2f374f0db | ||
|
|
29c6a70611 | ||
|
|
e507300134 | ||
|
|
e38c5cac4a | ||
|
|
463a55897c | ||
|
|
192cc4eb9b | ||
|
|
a592f6fe0b | ||
|
|
6b887a98cd | ||
|
|
18a26b42e2 | ||
|
|
a47e446b60 | ||
|
|
864a938f8d | ||
|
|
53bd1641bb | ||
|
|
7c0dcd6808 | ||
|
|
ea8b7b7a3a | ||
|
|
8714aa9202 | ||
|
|
3731d61b78 | ||
|
|
7b5d6f7031 | ||
|
|
a269d49392 | ||
|
|
3a8775f545 | ||
|
|
842dc0d49e | ||
|
|
1532de8f20 | ||
|
|
95bd217c3c | ||
|
|
f633c921cc | ||
|
|
e8c79ffd97 | ||
|
|
e842eb9d9e | ||
|
|
3e8572dbe9 | ||
|
|
ad8d0a2ab1 | ||
|
|
4a1e16449d | ||
|
|
41d31cb5ea | ||
|
|
909e2cbf89 | ||
|
|
1290503895 | ||
|
|
a204b24d78 | ||
|
|
86c969998f | ||
|
|
158874039a | ||
|
|
de6c35f4ab | ||
|
|
46816a74e9 | ||
|
|
1111c3d1b1 | ||
|
|
e919685ad3 | ||
|
|
238ba9b9b4 | ||
|
|
1d966a0c92 | ||
|
|
e3a6436031 | ||
|
|
f59441eb55 | ||
|
|
fcc0c5e345 | ||
|
|
c862b0bc26 | ||
|
|
575a1e4603 | ||
|
|
a74ccad282 | ||
|
|
ffeb5648c6 | ||
|
|
86c87929ec | ||
|
|
e0e92b843c | ||
|
|
8678f22f6b | ||
|
|
8322a0cfa3 | ||
|
|
ab6b82eaaa | ||
|
|
b16e8066ca | ||
|
|
bc1dd2a7b4 | ||
|
|
d11772b681 | ||
|
|
feeee9ef86 | ||
|
|
eb1b9567df | ||
|
|
c795ead402 | ||
|
|
91270c9c66 | ||
|
|
342eac1047 | ||
|
|
f72eb1b002 | ||
|
|
25caa5138a | ||
|
|
dffb227df6 | ||
|
|
5c9eb1cdb8 | ||
|
|
e30ab9a043 | ||
|
|
651b354d3e | ||
|
|
dd8b503aa0 | ||
|
|
dab30673e0 | ||
|
|
13689c2065 | ||
|
|
6e23627f26 | ||
|
|
7db85cc79c | ||
|
|
e5cedd074b | ||
|
|
ed87faad92 | ||
|
|
239f34d40c | ||
|
|
20b9ca56fa | ||
|
|
647ae0fda3 | ||
|
|
806dbd9ee5 | ||
|
|
1ab8442630 | ||
|
|
505661404b | ||
|
|
a2ea6faf4d | ||
|
|
ce51491b4d | ||
|
|
d241223aa3 | ||
|
|
d16f83fdd8 | ||
|
|
02e10ff8fe | ||
|
|
6a44d8f286 | ||
|
|
4b614d871f | ||
|
|
3286d70092 | ||
|
|
9fc37e46fa | ||
|
|
f304f429c5 | ||
|
|
65c73f639f | ||
|
|
d2833a1997 | ||
|
|
aa8f764436 | ||
|
|
618a59921a | ||
|
|
039f71dc61 | ||
|
|
27b8db4c10 | ||
|
|
afd53d76c2 | ||
|
|
57c65ad881 | ||
|
|
a372ae90c2 | ||
|
|
c8739c40a5 | ||
|
|
00ff9ac42d | ||
|
|
e13a8304e6 | ||
|
|
3c2b17853c | ||
|
|
288b2a1953 | ||
|
|
86b1f1fbac | ||
|
|
b5e26133a7 | ||
|
|
c97faa0238 | ||
|
|
69b39b4b21 | ||
|
|
02f544f813 | ||
|
|
69b5a779e4 | ||
|
|
fa5c1e495d | ||
|
|
7fdb2c10cb | ||
|
|
e422eebd3c | ||
|
|
3dca7e3539 | ||
|
|
75210b102e | ||
|
|
f0e97a7277 | ||
|
|
e16c81516e | ||
|
|
140c9a941f | ||
|
|
eb58fe9e97 | ||
|
|
5d4ea87402 | ||
|
|
9f1d7240a9 | ||
|
|
84894274f0 | ||
|
|
ea09002012 | ||
|
|
601ec5cb7a | ||
|
|
85ec2ee3a0 | ||
|
|
965f69f680 | ||
|
|
13a56dd4da | ||
|
|
4cf1b8c400 | ||
|
|
9c805a4317 | ||
|
|
b7b1182e14 | ||
|
|
f695aaaea4 | ||
|
|
b398d04d96 | ||
|
|
ac9ef4da76 | ||
|
|
a10893eaad |
@@ -13,5 +13,5 @@ indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# 2 space indentation
|
||||
[*.{yaml,.yml}]
|
||||
[*.{yaml,yml}]
|
||||
indent_size = 2
|
||||
|
||||
@@ -3,6 +3,7 @@ php:
|
||||
- '7.1'
|
||||
- '7.2'
|
||||
- '7.3'
|
||||
- '7.4'
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
@@ -56,7 +57,7 @@ before_install:
|
||||
fi;
|
||||
fi
|
||||
before_script:
|
||||
- if [ $TRAVIS_PHP_VERSION != 'hhvm' ]; then phpenv config-rm xdebug.ini; fi
|
||||
- phpenv config-rm xdebug.ini
|
||||
script:
|
||||
- if [ $TRAVIS_BRANCH == 'develop' ] || [ $TRAVIS_PULL_REQUEST != 'false' ]; then
|
||||
vendor/bin/codecept run;
|
||||
|
||||
169
CHANGELOG.md
169
CHANGELOG.md
@@ -1,3 +1,172 @@
|
||||
# v1.6.24
|
||||
## 04/27/2020
|
||||
|
||||
1. [](#improved)
|
||||
* Added support for `X-Forwarded-Host` [#2891](https://github.com/getgrav/grav/pull/2891)
|
||||
* Disable XDebug in Travis builds
|
||||
|
||||
# v1.6.23
|
||||
## 03/19/2020
|
||||
|
||||
1. [](#new)
|
||||
* Moved `Parsedown` 1.6 and `ParsedownExtra` 0.7 into `Grav\Framework\Parsedown` to allow fixes
|
||||
* Added `aliases.php` with references to direct `\Parsedown` and `\ParsedownExtra` references
|
||||
1. [](#improved)
|
||||
* Upgraded `jQuery` to latest 3.4.1 version [#2859](https://github.com/getgrav/grav/issues/2859)
|
||||
1. [](#bugfix)
|
||||
* Fixed PHP 7.4 issue in ParsedownExtra [#2832](https://github.com/getgrav/grav/issues/2832)
|
||||
* Fix for [user reported](https://twitter.com/OriginalSicksec) CVE path-based open redirect
|
||||
* Fix for `stream_set_option` error with PHP 7.4 via Toolbox#28 [#2850](https://github.com/getgrav/grav/issues/2850)
|
||||
|
||||
# v1.6.22
|
||||
## 03/05/2020
|
||||
|
||||
1. [](#new)
|
||||
* Added `Pages::reset()` method
|
||||
1. [](#improved)
|
||||
* Updated Negotiation library to address issues [#2513](https://github.com/getgrav/grav/issues/2513)
|
||||
1. [](#bugfix)
|
||||
* Fixed issue with search plugins not being able to switch between page translations
|
||||
* Fixed issues with `Pages::baseRoute()` not picking up active language reliably
|
||||
* Reverted `validation: strict` fix as it breaks sites, see [#1273](https://github.com/getgrav/grav/issues/1273)
|
||||
|
||||
# v1.6.21
|
||||
## 02/11/2020
|
||||
|
||||
1. [](#new)
|
||||
* Added `ConsoleCommand::setLanguage()` method to set language to be used from CLI
|
||||
* Added `ConsoleCommand::initializeGrav()` method to properly set up Grav instance to be used from CLI
|
||||
* Added `ConsoleCommand::initializePlugins()`method to properly set up all plugins to be used from CLI
|
||||
* Added `ConsoleCommand::initializeThemes()`method to properly set up current theme to be used from CLI
|
||||
* Added `ConsoleCommand::initializePages()` method to properly set up pages to be used from CLI
|
||||
1. [](#improved)
|
||||
* Vendor updates
|
||||
1. [](#bugfix)
|
||||
* Fixed `bin/plugin` CLI calling `$themes->init()` way too early (removed it, use above methods instead)
|
||||
* Fixed call to `$grav['page']` crashing CLI
|
||||
* Fixed encoding problems when PHP INI setting `default_charset` is not `utf-8` [#2154](https://github.com/getgrav/grav/issues/2154)
|
||||
|
||||
# v1.6.20
|
||||
## 02/03/2020
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed incorrect routing caused by `str_replace()` in `Uri::init()` [#2754](https://github.com/getgrav/grav/issues/2754)
|
||||
* Fixed session cookie is being set twice in the HTTP header [#2745](https://github.com/getgrav/grav/issues/2745)
|
||||
* Fixed session not restarting if user was invalid (downgrading from Grav 1.7)
|
||||
* Fixed filesystem iterator calls with non-existing folders
|
||||
* Fixed `checkbox` field not being saved, requires also Form v4.0.2 [#1225](https://github.com/getgrav/grav/issues/1225)
|
||||
* Fixed `validation: strict` not working in blueprints [#1273](https://github.com/getgrav/grav/issues/1273)
|
||||
* Fixed `Data::filter()` removing empty fields (such as empty list) by default [#2805](https://github.com/getgrav/grav/issues/2805)
|
||||
* Fixed fatal error with non-integer page param value [#2803](https://github.com/getgrav/grav/issues/2803)
|
||||
* Fixed `Assets::addInlineJs()` parameter type mismatch between v1.5 and v1.6 [#2659](https://github.com/getgrav/grav/issues/2659)
|
||||
* Fixed `site.metadata` saving issues [#2615](https://github.com/getgrav/grav/issues/2615)
|
||||
|
||||
# v1.6.19
|
||||
## 12/04/2019
|
||||
|
||||
1. [](#new)
|
||||
* Catch PHP 7.4 deprecation messages and report them in debugbar instead of throwing fatal error
|
||||
1. [](#bugfix)
|
||||
* Fixed fatal error when calling `{{ grav.undefined }}`
|
||||
* Fixed multiple issues when there are no pages in the site
|
||||
* PHP 7.4 fix for [#2750](https://github.com/getgrav/grav/issues/2750)
|
||||
|
||||
# v1.6.18
|
||||
## 12/02/2019
|
||||
|
||||
1. [](#bugfix)
|
||||
* PHP 7.4 fix in `Pages::buildSort()`
|
||||
* Updated vendor libraries for PHP 7.4 fixes in Twig and other libraries
|
||||
* Fixed fatal error when `$page->id()` is null [#2731](https://github.com/getgrav/grav/pull/2731)
|
||||
* Fixed cache conflicts on pages with no set id
|
||||
* Fix rewrite rule for for `lighttpd` default config [#721](https://github.com/getgrav/grav/pull/2721)
|
||||
|
||||
# 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/16/2019
|
||||
|
||||
1. [](#bugfix)
|
||||
* Regression fix for `system\router.php` [#2627](https://github.com/getgrav/grav/issues/2627)
|
||||
|
||||
# v1.6.12
|
||||
## 08/14/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added support for custom `FormFlash` save locations
|
||||
* Added a new `Utils::arrayLower()` method for lowercasing arrays
|
||||
* Support new GRAV_BASEDIR environment variable [#2541](https://github.com/getgrav/grav/pull/2541)
|
||||
* Allow users to override plugin handler priorities [#2165](https://github.com/getgrav/grav/pull/2165)
|
||||
1. [](#improved)
|
||||
* Use new `Utils::getSupportedPageTypes()` to enforce `html,htm` at the front of the list [#2531](https://github.com/getgrav/grav/issues/2531)
|
||||
* Updated vendor libraries
|
||||
* Markdown filter is now page-aware so that it works with modular references [admin#1731](https://github.com/getgrav/grav-plugin-admin/issues/1731)
|
||||
* Check of `GRAV_USER_INSTANCE` constant is already defined [#2621](https://github.com/getgrav/grav/pull/2621)
|
||||
1. [](#bugfix)
|
||||
* Fixed some potential issues when `$grav['user']` is not set
|
||||
* Fixed error when calling `Media::add($name, null)`
|
||||
* Fixed `url()` returning wrong path if using stream with grav root path in it, eg: `user-data://shop` when Grav is in `/shop`
|
||||
* Fixed `url()` not returning a path to non-existing file (`user-data://shop` => `/user/data/shop`) if it is set to fail gracefully
|
||||
* Fixed `url()` returning false on unknown streams, such as `ftp://domain.com`, they should be treated as external URL
|
||||
* Fixed Flex User to have permissions to save and delete his own user
|
||||
* Fixed new Flex User creation not being possible because of username could not be given
|
||||
* Fixed fatal error 'Expiration date must be an integer, a DateInterval or null, "double" given' [#2529](https://github.com/getgrav/grav/issues/2529)
|
||||
* Fixed non-existing Flex object having a bad media folder
|
||||
* Fixed collections using `page@.self:` should allow modular pages if requested
|
||||
* Fixed an error when trying to delete a file from non-existing Flex Object
|
||||
* Fixed `FlexObject::exists()` failing sometimes just after the object has been saved
|
||||
* Fixed CSV formatter not encoding strings with `"` and `,` properly
|
||||
* Fixed var order in `Validation.php` [#2610](https://github.com/getgrav/grav/issues/2610)
|
||||
|
||||
# v1.6.11
|
||||
## 06/21/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added `FormTrait::getAllFlashes()` method to get all the available form flash objects for the form
|
||||
* Added creation and update timestamps to `FormFlash` objects
|
||||
1. [](#improved)
|
||||
* Added `FormFlashInterface`, changed constructor to take `$config` array
|
||||
1. [](#bugfix)
|
||||
* Fixed error in `ImageMedium::url()` if the image cache folder does not exist
|
||||
* Fixed empty form flash name after file upload or form state update
|
||||
* Fixed a bug in `Route::withParam()` method
|
||||
* Fixed issue with `FormFlash` objects when there is no session initialized
|
||||
|
||||
# v1.6.10
|
||||
## 06/14/2019
|
||||
|
||||
|
||||
9
bin/gpm
9
bin/gpm
@@ -28,6 +28,13 @@ if (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('UTC');
|
||||
}
|
||||
|
||||
// Set internal encoding.
|
||||
if (!\extension_loaded('mbstring')) {
|
||||
die("'mbstring' extension is not loaded. This is required for Grav to run correctly");
|
||||
}
|
||||
@ini_set('default_charset', 'UTF-8');
|
||||
mb_internal_encoding('UTF-8');
|
||||
|
||||
if (!file_exists(GRAV_ROOT . '/index.php')) {
|
||||
exit('FATAL: Must be run from ROOT directory of Grav!');
|
||||
}
|
||||
@@ -55,7 +62,7 @@ $grav->setup($environment);
|
||||
|
||||
$grav['config']->init();
|
||||
$grav['uri']->init();
|
||||
$grav['users'];
|
||||
$grav['accounts'];
|
||||
|
||||
$app = new Application('Grav Package Manager', GRAV_VERSION);
|
||||
$app->addCommands(array(
|
||||
|
||||
15
bin/grav
15
bin/grav
@@ -25,6 +25,17 @@ if (version_compare($ver = PHP_VERSION, $req = GRAV_PHP_MIN, '<')) {
|
||||
exit(sprintf("You are running PHP %s, but Grav needs at least PHP %s to run.\n", $ver, $req));
|
||||
}
|
||||
|
||||
if (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('UTC');
|
||||
}
|
||||
|
||||
// Set internal encoding.
|
||||
if (!\extension_loaded('mbstring')) {
|
||||
die("'mbstring' extension is not loaded. This is required for Grav to run correctly");
|
||||
}
|
||||
@ini_set('default_charset', 'UTF-8');
|
||||
mb_internal_encoding('UTF-8');
|
||||
|
||||
$climate = new League\CLImate\CLImate;
|
||||
$climate->arguments->add([
|
||||
'environment' => [
|
||||
@@ -42,10 +53,6 @@ $environment = $climate->arguments->get('environment');
|
||||
$grav = Grav::instance(array('loader' => $autoload));
|
||||
$grav->setup($environment);
|
||||
|
||||
if (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('UTC');
|
||||
}
|
||||
|
||||
if (!file_exists(GRAV_ROOT . '/index.php')) {
|
||||
exit('FATAL: Must be run from ROOT directory of Grav!');
|
||||
}
|
||||
|
||||
14
bin/plugin
14
bin/plugin
@@ -32,6 +32,13 @@ if (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('UTC');
|
||||
}
|
||||
|
||||
// Set internal encoding.
|
||||
if (!\extension_loaded('mbstring')) {
|
||||
die("'mbstring' extension is not loaded. This is required for Grav to run correctly");
|
||||
}
|
||||
@ini_set('default_charset', 'UTF-8');
|
||||
mb_internal_encoding('UTF-8');
|
||||
|
||||
if (!file_exists(GRAV_ROOT . '/index.php')) {
|
||||
exit('FATAL: Must be run from ROOT directory of Grav!');
|
||||
}
|
||||
@@ -51,12 +58,7 @@ $environment = $climate->arguments->get('environment');
|
||||
|
||||
$grav = Grav::instance(array('loader' => $autoload));
|
||||
$grav->setup($environment);
|
||||
|
||||
$grav['config']->init();
|
||||
$grav['uri']->init();
|
||||
$grav['users'];
|
||||
$grav['plugins']->init();
|
||||
$grav['themes']->init();
|
||||
$grav->initializeCli();
|
||||
|
||||
$app = new Application('Grav Plugins Commands', GRAV_VERSION);
|
||||
$pattern = '([A-Z]\w+Command\.php)';
|
||||
|
||||
@@ -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,33 +22,27 @@
|
||||
"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",
|
||||
"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/yaml": "~4.2.0",
|
||||
"symfony/console": "~4.2.0",
|
||||
"symfony/event-dispatcher": "~4.2.0",
|
||||
"symfony/var-dumper": "~4.2.0",
|
||||
"symfony/process": "~4.2.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",
|
||||
"rockettheme/toolbox": "~1.4.0",
|
||||
"maximebf/debugbar": "~1.15",
|
||||
"league/climate": "^3.4",
|
||||
"antoligy/dom-string-iterators": "^1.0",
|
||||
@@ -50,7 +50,7 @@
|
||||
"composer/ca-bundle": "^1.0",
|
||||
"dragonmantank/cron-expression": "^1.2",
|
||||
"phive/twig-extensions-deferred": "^1.0",
|
||||
"willdurand/negotiation": "^2.3"
|
||||
"willdurand/negotiation": "2.x-dev"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "^2.4",
|
||||
@@ -83,10 +83,15 @@
|
||||
"psr-4": {
|
||||
"Grav\\": "system/src/Grav"
|
||||
},
|
||||
"files": ["system/defines.php"]
|
||||
"files": [
|
||||
"system/defines.php",
|
||||
"system/aliases.php"
|
||||
]
|
||||
},
|
||||
"archive": {
|
||||
"exclude": ["VERSION"]
|
||||
"exclude": [
|
||||
"VERSION"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"api-16": "vendor/bin/phpdoc-md generate system/src > user/pages/14.api/default.16.md",
|
||||
@@ -94,7 +99,7 @@
|
||||
"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-plugins": "vendor/bin/phpstan analyse -l 0 -c ./tests/phpstan/plugins.neon user/plugins --memory-limit=256M",
|
||||
"test": "vendor/bin/codecept run unit",
|
||||
"test-windows": "vendor\\bin\\codecept run unit"
|
||||
},
|
||||
|
||||
1212
composer.lock
generated
1212
composer.lock
generated
File diff suppressed because it is too large
Load Diff
19
index.php
19
index.php
@@ -20,6 +20,16 @@ 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>");
|
||||
}
|
||||
|
||||
// Set timezone to default, falls back to system if php.ini not set
|
||||
date_default_timezone_set(@date_default_timezone_get());
|
||||
|
||||
// Set internal encoding.
|
||||
if (!\extension_loaded('mbstring')) {
|
||||
die("'mbstring' extension is not loaded. This is required for Grav to run correctly");
|
||||
}
|
||||
@ini_set('default_charset', 'UTF-8');
|
||||
mb_internal_encoding('UTF-8');
|
||||
|
||||
// Ensure vendor libraries exist
|
||||
$autoload = __DIR__ . '/vendor/autoload.php';
|
||||
if (!is_file($autoload)) {
|
||||
@@ -32,15 +42,6 @@ $loader = require $autoload;
|
||||
use Grav\Common\Grav;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
|
||||
// Set timezone to default, falls back to system if php.ini not set
|
||||
date_default_timezone_set(@date_default_timezone_get());
|
||||
|
||||
// Set internal encoding if mbstring loaded
|
||||
if (!\extension_loaded('mbstring')) {
|
||||
die("'mbstring' extension is not loaded. This is required for Grav to run correctly");
|
||||
}
|
||||
mb_internal_encoding('UTF-8');
|
||||
|
||||
// Get the Grav instance
|
||||
$grav = Grav::instance(
|
||||
array(
|
||||
|
||||
@@ -10,3 +10,4 @@ Disallow: /user/
|
||||
Allow: /user/pages/
|
||||
Allow: /user/themes/
|
||||
Allow: /user/images/
|
||||
Allow: /
|
||||
|
||||
5
system/aliases.php
Normal file
5
system/aliases.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
/** Moved from non-namespaced classes to Grav Framework */
|
||||
class_alias(Grav\Framework\Parsedown\Parsedown::class, '\Parsedown');
|
||||
class_alias(Grav\Framework\Parsedown\ParsedownExtra::class, '\ParsedownExtra');
|
||||
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
@@ -12,5 +12,7 @@ form:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.USERNAME
|
||||
help: PLUGIN_ADMIN.USERNAME_HELP
|
||||
unset-disabled@: true
|
||||
unset-readonly@: true
|
||||
validate:
|
||||
required: true
|
||||
|
||||
@@ -27,3 +27,13 @@ config:
|
||||
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
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
// Some standard defines
|
||||
define('GRAV', true);
|
||||
define('GRAV_VERSION', '1.6.10');
|
||||
define('GRAV_VERSION', '1.6.24');
|
||||
define('GRAV_TESTING', false);
|
||||
define('DS', '/');
|
||||
|
||||
|
||||
@@ -17,11 +17,21 @@ if (is_file($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . $_SERVER['SCRIPT_N
|
||||
return false;
|
||||
}
|
||||
|
||||
$grav_index = 'index.php';
|
||||
|
||||
/* Check the GRAV_BASEDIR environment variable and use if set */
|
||||
$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);
|
||||
}
|
||||
|
||||
$_SERVER = array_merge($_SERVER, $_ENV);
|
||||
$_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . 'index.php';
|
||||
$_SERVER['SCRIPT_NAME'] = DIRECTORY_SEPARATOR . 'index.php';
|
||||
$_SERVER['PHP_SELF'] = DIRECTORY_SEPARATOR . 'index.php';
|
||||
$_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'] . $grav_basedir .DIRECTORY_SEPARATOR . 'index.php';
|
||||
$_SERVER['SCRIPT_NAME'] = $grav_basedir . DIRECTORY_SEPARATOR . 'index.php';
|
||||
$_SERVER['PHP_SELF'] = $grav_basedir . DIRECTORY_SEPARATOR . 'index.php';
|
||||
|
||||
error_log(sprintf('%s:%d [%d]: %s', $_SERVER['REMOTE_ADDR'], $_SERVER['REMOTE_PORT'], http_response_code(), $_SERVER['REQUEST_URI']), 4);
|
||||
|
||||
require 'index.php';
|
||||
require $grav_index;
|
||||
|
||||
@@ -51,7 +51,11 @@ trait LegacyAssetsTrait
|
||||
// special case to handle old attributes being passed in
|
||||
if (isset($arguments['attributes'])) {
|
||||
$old_attributes = $arguments['attributes'];
|
||||
$arguments = array_merge($arguments, $old_attributes);
|
||||
if (is_array($old_attributes)) {
|
||||
$arguments = array_merge($arguments, $old_attributes);
|
||||
} else {
|
||||
$arguments['type'] = $old_attributes;
|
||||
}
|
||||
}
|
||||
unset($arguments['attributes']);
|
||||
|
||||
|
||||
@@ -531,7 +531,6 @@ class Cache extends Getters
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the cache lifetime programmatically
|
||||
*
|
||||
@@ -543,7 +542,7 @@ class Cache extends Getters
|
||||
return;
|
||||
}
|
||||
|
||||
$interval = $future - $this->now;
|
||||
$interval = (int)($future - $this->now);
|
||||
if ($interval > 0 && $interval < $this->getLifetime()) {
|
||||
$this->lifetime = $interval;
|
||||
}
|
||||
@@ -558,7 +557,7 @@ class Cache extends Getters
|
||||
public function getLifetime()
|
||||
{
|
||||
if ($this->lifetime === null) {
|
||||
$this->lifetime = $this->config->get('system.cache.lifetime') ?: 604800; // 1 week default
|
||||
$this->lifetime = (int)($this->config->get('system.cache.lifetime') ?: 604800); // 1 week default
|
||||
}
|
||||
|
||||
return $this->lifetime;
|
||||
|
||||
@@ -378,14 +378,12 @@ class Blueprint extends BlueprintForm
|
||||
$grav = Grav::instance();
|
||||
$actions = (array)$call['params'];
|
||||
|
||||
/** @var UserInterface $user */
|
||||
if (isset($grav['user'])) {
|
||||
$user = Grav::instance()['user'];
|
||||
foreach ($actions as $action) {
|
||||
if (!$user->authorize($action)) {
|
||||
$this->addPropertyRecursive($field, 'validate', ['ignore' => true]);
|
||||
return;
|
||||
}
|
||||
/** @var UserInterface|null $user */
|
||||
$user = $grav['user'] ?? null;
|
||||
foreach ($actions as $action) {
|
||||
if (!$user || !$user->authorize($action)) {
|
||||
$this->addPropertyRecursive($field, 'validate', ['ignore' => true]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
{
|
||||
$messages = $this->checkRequired($data, $rules);
|
||||
|
||||
foreach ($data as $key => $field) {
|
||||
foreach ($data as $key => $child) {
|
||||
$val = $rules[$key] ?? $rules['*'] ?? null;
|
||||
$rule = \is_string($val) ? $this->items[$val] : null;
|
||||
|
||||
@@ -147,10 +147,10 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
continue;
|
||||
}
|
||||
|
||||
$messages += Validation::validate($field, $rule);
|
||||
} elseif (\is_array($field) && \is_array($val)) {
|
||||
$messages += Validation::validate($child, $rule);
|
||||
} elseif (\is_array($child) && \is_array($val)) {
|
||||
// Array has been defined in blueprints.
|
||||
$messages += $this->validateArray($field, $val);
|
||||
$messages += $this->validateArray($child, $val);
|
||||
} elseif (isset($rules['validation']) && $rules['validation'] === 'strict') {
|
||||
// Undefined/extra item.
|
||||
throw new \RuntimeException(sprintf('%s is not defined in blueprints', $key));
|
||||
|
||||
@@ -32,6 +32,12 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
|
||||
/** @var File */
|
||||
protected $storage;
|
||||
|
||||
/** @var bool */
|
||||
private $missingValuesAsNull = false;
|
||||
|
||||
/** @var bool */
|
||||
private $keepEmptyValues = true;
|
||||
|
||||
/**
|
||||
* @param array $items
|
||||
* @param Blueprint|callable $blueprints
|
||||
@@ -42,6 +48,28 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
|
||||
$this->blueprints = $blueprints;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
* @return $this
|
||||
*/
|
||||
public function setKeepEmptyValues(bool $value)
|
||||
{
|
||||
$this->keepEmptyValues = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
* @return $this
|
||||
*/
|
||||
public function setMissingValuesAsNull(bool $value)
|
||||
{
|
||||
$this->missingValuesAsNull = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value by using dot notation for nested arrays/objects.
|
||||
*
|
||||
@@ -202,8 +230,8 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
|
||||
public function filter()
|
||||
{
|
||||
$args = func_get_args();
|
||||
$missingValuesAsNull = (bool)(array_shift($args) ?: false);
|
||||
$keepEmptyValues = (bool)(array_shift($args) ?: false);
|
||||
$missingValuesAsNull = (bool)(array_shift($args) ?? $this->missingValuesAsNull);
|
||||
$keepEmptyValues = (bool)(array_shift($args) ?? $this->keepEmptyValues);
|
||||
|
||||
$this->items = $this->blueprints()->filter($this->items, $missingValuesAsNull, $keepEmptyValues);
|
||||
|
||||
|
||||
@@ -27,8 +27,9 @@ class Validation
|
||||
if (!isset($field['type'])) {
|
||||
$field['type'] = 'text';
|
||||
}
|
||||
$type = $validate['type'] ?? $field['type'];
|
||||
|
||||
$validate = (array)($field['validate'] ?? null);
|
||||
$type = $validate['type'] ?? $field['type'];
|
||||
$required = $validate['required'] ?? false;
|
||||
|
||||
// If value isn't required, we will stop validation if empty value is given.
|
||||
@@ -165,9 +166,18 @@ class Validation
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return string|null
|
||||
*/
|
||||
protected static function filterCheckbox($value, array $params, array $field)
|
||||
{
|
||||
return (bool) $value;
|
||||
$value = (string)$value;
|
||||
$field_value = (string)($field['value'] ?? '1');
|
||||
|
||||
return $value === $field_value ? $value : null;
|
||||
}
|
||||
|
||||
protected static function filterCommaList($value, array $params, array $field)
|
||||
|
||||
@@ -347,7 +347,7 @@ class Debugger
|
||||
*/
|
||||
public function deprecatedErrorHandler($errno, $errstr, $errfile, $errline)
|
||||
{
|
||||
if ($errno !== E_USER_DEPRECATED) {
|
||||
if ($errno !== E_USER_DEPRECATED && $errno !== E_DEPRECATED) {
|
||||
if ($this->errorHandler) {
|
||||
return \call_user_func($this->errorHandler, $errno, $errstr, $errfile, $errline);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ abstract class Folder
|
||||
*/
|
||||
public static function lastModifiedFolder($path)
|
||||
{
|
||||
if (!file_exists($path)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$last_modified = 0;
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
@@ -56,6 +60,10 @@ abstract class Folder
|
||||
*/
|
||||
public static function lastModifiedFile($path, $extensions = 'md|yaml')
|
||||
{
|
||||
if (!file_exists($path)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$last_modified = 0;
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
@@ -92,21 +100,24 @@ abstract class Folder
|
||||
*/
|
||||
public static function hashAllFiles($path)
|
||||
{
|
||||
$flags = \RecursiveDirectoryIterator::SKIP_DOTS;
|
||||
$files = [];
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
if ($locator->isStream($path)) {
|
||||
$directory = $locator->getRecursiveIterator($path, $flags);
|
||||
} else {
|
||||
$directory = new \RecursiveDirectoryIterator($path, $flags);
|
||||
}
|
||||
if (file_exists($path)) {
|
||||
$flags = \RecursiveDirectoryIterator::SKIP_DOTS;
|
||||
|
||||
$iterator = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
if ($locator->isStream($path)) {
|
||||
$directory = $locator->getRecursiveIterator($path, $flags);
|
||||
} else {
|
||||
$directory = new \RecursiveDirectoryIterator($path, $flags);
|
||||
}
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
$files[] = $file->getPathname() . '?'. $file->getMTime();
|
||||
$iterator = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
$files[] = $file->getPathname() . '?'. $file->getMTime();
|
||||
}
|
||||
}
|
||||
|
||||
return md5(serialize($files));
|
||||
@@ -199,6 +210,9 @@ abstract class Folder
|
||||
if ($path === false) {
|
||||
throw new \RuntimeException("Path doesn't exist.");
|
||||
}
|
||||
if (!file_exists($path)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$compare = isset($params['compare']) ? 'get' . $params['compare'] : null;
|
||||
$pattern = $params['pattern'] ?? null;
|
||||
@@ -235,7 +249,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()) {
|
||||
|
||||
@@ -10,54 +10,10 @@
|
||||
namespace Grav\Common\Form;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\User\Interfaces\UserInterface;
|
||||
use RocketTheme\Toolbox\File\YamlFile;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use Grav\Framework\Form\FormFlash as FrameworkFormFlash;
|
||||
|
||||
class FormFlash extends \Grav\Framework\Form\FormFlash
|
||||
class FormFlash extends FrameworkFormFlash
|
||||
{
|
||||
/**
|
||||
* @param string $sessionId
|
||||
*/
|
||||
public static function clearSession(string $sessionId): void
|
||||
{
|
||||
$folder = static::getSessionTmpDir($sessionId);
|
||||
if (is_dir($folder)) {
|
||||
Folder::delete($folder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sessionId
|
||||
* @return string
|
||||
*/
|
||||
public static function getSessionTmpDir(string $sessionId): string
|
||||
{
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
return $locator->findResource("tmp://forms/{$sessionId}", true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UserInterface|null $user
|
||||
* @return $this
|
||||
*/
|
||||
public function setUser(UserInterface $user = null)
|
||||
{
|
||||
if ($user && $user->username) {
|
||||
$this->user = [
|
||||
'username' => $user->username,
|
||||
'email' => $user->email ?? ''
|
||||
];
|
||||
} else {
|
||||
$this->user = null;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @deprecated 1.6 For backwards compatibility only, do not use
|
||||
@@ -89,8 +45,11 @@ class FormFlash extends \Grav\Framework\Form\FormFlash
|
||||
*/
|
||||
public function uploadFile(string $field, string $filename, array $upload): bool
|
||||
{
|
||||
$tmp_dir = $this->getTmpDir();
|
||||
if (!$this->uniqueId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tmp_dir = $this->getTmpDir();
|
||||
Folder::create($tmp_dir);
|
||||
|
||||
$tmp_file = $upload['file']['tmp_name'];
|
||||
@@ -118,8 +77,11 @@ class FormFlash extends \Grav\Framework\Form\FormFlash
|
||||
*/
|
||||
public function cropFile(string $field, string $filename, array $upload, array $crop): bool
|
||||
{
|
||||
$tmp_dir = $this->getTmpDir();
|
||||
if (!$this->uniqueId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tmp_dir = $this->getTmpDir();
|
||||
Folder::create($tmp_dir);
|
||||
|
||||
$tmp_file = $upload['file']['tmp_name'];
|
||||
@@ -136,32 +98,4 @@ class FormFlash extends \Grav\Framework\Form\FormFlash
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return YamlFile
|
||||
*/
|
||||
protected function getTmpIndex(): YamlFile
|
||||
{
|
||||
// Do not use CompiledYamlFile as the file can change multiple times per second.
|
||||
return YamlFile::instance($this->getTmpDir() . '/index.yaml');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
protected function removeTmpFile(string $name): void
|
||||
{
|
||||
$filename = $this->getTmpDir() . '/' . $name;
|
||||
if ($name && is_file($filename)) {
|
||||
unlink($filename);
|
||||
}
|
||||
}
|
||||
|
||||
protected function removeTmpDir(): void
|
||||
{
|
||||
$tmpDir = $this->getTmpDir();
|
||||
if (file_exists($tmpDir)) {
|
||||
Folder::delete($tmpDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ class Licenses
|
||||
}
|
||||
|
||||
/**
|
||||
* Get's the License File object
|
||||
* Get the License File object
|
||||
*
|
||||
* @return \RocketTheme\Toolbox\File\FileInterface
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Grav\Common\GPM\Local;
|
||||
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\GPM\Common\Package as BasePackage;
|
||||
use Grav\Framework\Parsedown\Parsedown;
|
||||
|
||||
class Package extends BasePackage
|
||||
{
|
||||
@@ -23,7 +24,7 @@ class Package extends BasePackage
|
||||
|
||||
$this->settings = $package->toArray();
|
||||
|
||||
$html_description = \Parsedown::instance()->line($this->__get('description'));
|
||||
$html_description = Parsedown::instance()->line($this->__get('description'));
|
||||
$this->data->set('slug', $package->__get('slug'));
|
||||
$this->data->set('description_html', $html_description);
|
||||
$this->data->set('description_plain', strip_tags($html_description));
|
||||
|
||||
@@ -170,6 +170,29 @@ class Grav extends Container
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize CLI environment.
|
||||
*
|
||||
* Call after `$grav->setup($environment)`
|
||||
*
|
||||
* - Load configuration
|
||||
* - Disable debugger
|
||||
* - Set timezone, locale
|
||||
* - Load plugins
|
||||
* - Set Users type to be used in the site
|
||||
*
|
||||
* This method WILL NOT initialize assets, twig or pages.
|
||||
*
|
||||
* @param string|null $environment
|
||||
* @return $this
|
||||
*/
|
||||
public function initializeCli()
|
||||
{
|
||||
InitializeProcessor::initializeCli($this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a request
|
||||
*/
|
||||
@@ -247,9 +270,21 @@ class Grav extends Container
|
||||
$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();
|
||||
|
||||
@@ -281,7 +316,10 @@ class Grav extends Container
|
||||
/** @var Uri $uri */
|
||||
$uri = $this['uri'];
|
||||
|
||||
//Check for code in route
|
||||
// Clean route for redirect
|
||||
$route = preg_replace("#^\/[\\\/]+\/#", '/', $route);
|
||||
|
||||
// Check for code in route
|
||||
$regex = '/.*(\[(30[1-7])\])$/';
|
||||
preg_match($regex, $route, $matches);
|
||||
if ($matches) {
|
||||
@@ -427,11 +465,16 @@ class Grav extends Container
|
||||
* Used to call closures.
|
||||
*
|
||||
* Source: http://stackoverflow.com/questions/419804/closures-as-class-members
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
* @return
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
$closure = $this->{$method};
|
||||
\call_user_func_array($closure, $args);
|
||||
$closure = $this->{$method} ?? null;
|
||||
|
||||
return is_callable($closure) ? $closure(...$args) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -193,6 +193,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ class Language
|
||||
}
|
||||
|
||||
/**
|
||||
* Get's a URL prefix based on configuration
|
||||
* Get a URL prefix based on configuration
|
||||
*
|
||||
* @param string|null $lang
|
||||
* @return string
|
||||
|
||||
@@ -11,8 +11,9 @@ namespace Grav\Common\Markdown;
|
||||
|
||||
use Grav\Common\Page\Interfaces\PageInterface;
|
||||
use Grav\Common\Page\Markdown\Excerpts;
|
||||
use Grav\Framework\Parsedown\Parsedown as ParsedownLib;
|
||||
|
||||
class Parsedown extends \Parsedown
|
||||
class Parsedown extends ParsedownLib
|
||||
{
|
||||
use ParsedownGravTrait;
|
||||
|
||||
|
||||
@@ -11,8 +11,9 @@ namespace Grav\Common\Markdown;
|
||||
|
||||
use Grav\Common\Page\Interfaces\PageInterface;
|
||||
use Grav\Common\Page\Markdown\Excerpts;
|
||||
use Grav\Framework\Parsedown\ParsedownExtra as ParsedownExtraLib;
|
||||
|
||||
class ParsedownExtra extends \ParsedownExtra
|
||||
class ParsedownExtra extends ParsedownExtraLib
|
||||
{
|
||||
use ParsedownGravTrait;
|
||||
|
||||
|
||||
@@ -125,5 +125,5 @@ trait MediaTrait
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getCacheKey();
|
||||
abstract protected function getCacheKey(): string;
|
||||
}
|
||||
|
||||
@@ -154,6 +154,9 @@ abstract class AbstractMedia implements ExportInterface, MediaCollectionInterfac
|
||||
*/
|
||||
public function add($name, $file)
|
||||
{
|
||||
if (!$file) {
|
||||
return;
|
||||
}
|
||||
$this->offsetSet($name, $file);
|
||||
switch ($file->type) {
|
||||
case 'image':
|
||||
|
||||
@@ -170,8 +170,7 @@ class ImageMedium extends Medium
|
||||
{
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
$image_path = $locator->findResource('cache://images', true);
|
||||
$image_dir = $locator->findResource('cache://images', false);
|
||||
$image_path = $locator->findResource('cache://images', true) ?: $locator->findResource('cache://images', true, true);
|
||||
$saved_image_path = $this->saveImage();
|
||||
|
||||
$output = preg_replace('|^' . preg_quote(GRAV_ROOT, '|') . '|', '', $saved_image_path);
|
||||
@@ -181,6 +180,7 @@ class ImageMedium extends Medium
|
||||
}
|
||||
|
||||
if (Utils::startsWith($output, $image_path)) {
|
||||
$image_dir = $locator->findResource('cache://images', false);
|
||||
$output = '/' . $image_dir . preg_replace('|^' . preg_quote($image_path, '|') . '|', '', $output);
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ class ImageMedium extends Medium
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the ability to override the Inmage's Pretty name stored in cache
|
||||
* Allows the ability to override the image's pretty name stored in cache
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
|
||||
@@ -529,9 +529,9 @@ class Page implements PageInterface
|
||||
$headers['Last-Modified'] = $last_modified_date;
|
||||
}
|
||||
|
||||
// Calculate ETag based on the raw file
|
||||
// Ask Grav to calculate ETag from the final content.
|
||||
if ($this->eTag()) {
|
||||
$headers['ETag'] = '"' . md5($this->raw() . $this->modified()).'"';
|
||||
$headers['ETag'] = '1';
|
||||
}
|
||||
|
||||
// Set Vary: Accept-Encoding header
|
||||
@@ -608,12 +608,12 @@ class Page implements PageInterface
|
||||
return $content;
|
||||
}
|
||||
|
||||
return mb_strimwidth($content, 0, $size, '...', 'utf-8');
|
||||
return mb_strimwidth($content, 0, $size, '...', 'UTF-8');
|
||||
}
|
||||
|
||||
$summary = Utils::truncateHtml($content, $size);
|
||||
|
||||
return html_entity_decode($summary);
|
||||
return html_entity_decode($summary, ENT_COMPAT | ENT_HTML401, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -659,7 +659,7 @@ class Page implements PageInterface
|
||||
// Load cached content
|
||||
/** @var Cache $cache */
|
||||
$cache = Grav::instance()['cache'];
|
||||
$cache_id = md5('page' . $this->id());
|
||||
$cache_id = md5('page' . $this->getCacheKey());
|
||||
$content_obj = $cache->fetch($cache_id);
|
||||
|
||||
if (is_array($content_obj)) {
|
||||
@@ -865,7 +865,7 @@ class Page implements PageInterface
|
||||
public function cachePageContent()
|
||||
{
|
||||
$cache = Grav::instance()['cache'];
|
||||
$cache_id = md5('page' . $this->id());
|
||||
$cache_id = md5('page' . $this->getCacheKey());
|
||||
$cache->save($cache_id, ['content' => $this->content, 'content_meta' => $this->content_meta]);
|
||||
}
|
||||
|
||||
@@ -1200,7 +1200,7 @@ class Page implements PageInterface
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheKey()
|
||||
public function getCacheKey(): string
|
||||
{
|
||||
return $this->id();
|
||||
}
|
||||
@@ -1410,7 +1410,7 @@ class Page implements PageInterface
|
||||
if (is_string($http_accept)) {
|
||||
$negotiator = new Negotiator();
|
||||
|
||||
$supported_types = Grav::instance()['config']->get('system.pages.types', ['html', 'json']);
|
||||
$supported_types = Utils::getSupportPageTypes(['html', 'json']);
|
||||
$priorities = Utils::getMimeTypes($supported_types);
|
||||
|
||||
$media_type = $negotiator->getBest($http_accept, $priorities);
|
||||
@@ -1694,9 +1694,9 @@ class Page implements PageInterface
|
||||
$metadata['generator'] = 'GravCMS';
|
||||
|
||||
// Get initial metadata for the page
|
||||
$metadata = array_merge($metadata, Grav::instance()['config']->get('site.metadata'));
|
||||
$metadata = array_merge($metadata, Grav::instance()['config']->get('site.metadata', []));
|
||||
|
||||
if (isset($this->header->metadata)) {
|
||||
if (isset($this->header->metadata) && is_array($this->header->metadata)) {
|
||||
// Merge any site.metadata settings in with page metadata
|
||||
$metadata = array_merge($metadata, $this->header->metadata);
|
||||
}
|
||||
@@ -2009,6 +2009,10 @@ class Page implements PageInterface
|
||||
*/
|
||||
public function id($var = null)
|
||||
{
|
||||
if (null === $this->id) {
|
||||
// We need to set unique id to avoid potential cache conflicts between pages.
|
||||
$var = time() . md5($this->filePath());
|
||||
}
|
||||
if ($var !== null) {
|
||||
// store unique per language
|
||||
$active_lang = Grav::instance()['language']->getLanguage() ?: '';
|
||||
@@ -2824,7 +2828,7 @@ class Page implements PageInterface
|
||||
if ($pagination) {
|
||||
$params = $collection->params();
|
||||
|
||||
$limit = $params['limit'] ?? 0;
|
||||
$limit = (int)($params['limit'] ?? 0);
|
||||
$start = !empty($params['pagination']) ? ($uri->currentPage() - 1) * $limit : 0;
|
||||
|
||||
if ($limit && $collection->count() > $limit) {
|
||||
@@ -2855,9 +2859,9 @@ class Page implements PageInterface
|
||||
$result = [];
|
||||
foreach ((array)$value as $key => $val) {
|
||||
if (is_int($key)) {
|
||||
$result = $result + $this->evaluate($val)->toArray();
|
||||
$result = $result + $this->evaluate($val, $only_published)->toArray();
|
||||
} else {
|
||||
$result = $result + $this->evaluate([$key => $val])->toArray();
|
||||
$result = $result + $this->evaluate([$key => $val], $only_published)->toArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2938,7 +2942,7 @@ class Page implements PageInterface
|
||||
case 'page':
|
||||
case 'self':
|
||||
$results = new Collection();
|
||||
$results = $results->addPage($page)->nonModular();
|
||||
$results = $results->addPage($page);
|
||||
break;
|
||||
|
||||
case 'descendants':
|
||||
|
||||
@@ -91,6 +91,12 @@ class Pages
|
||||
/** @var string */
|
||||
protected $check_method;
|
||||
|
||||
protected $pages_cache_id;
|
||||
|
||||
protected $initialized = false;
|
||||
|
||||
protected $active_lang;
|
||||
|
||||
/**
|
||||
* @var Types
|
||||
*/
|
||||
@@ -101,8 +107,6 @@ class Pages
|
||||
*/
|
||||
static protected $home_route;
|
||||
|
||||
protected $pages_cache_id;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@@ -141,7 +145,7 @@ class Pages
|
||||
*/
|
||||
public function baseRoute($lang = null)
|
||||
{
|
||||
$key = $lang ?: 'default';
|
||||
$key = $lang ?: $this->active_lang ?: 'default';
|
||||
|
||||
if (!isset($this->baseRoute[$key])) {
|
||||
/** @var Language $language */
|
||||
@@ -234,11 +238,25 @@ class Pages
|
||||
$this->check_method = strtolower($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset pages (used in search indexing etc).
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->initialized = false;
|
||||
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class initialization. Must be called before using this class.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
if ($this->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
$config = $this->grav['config'];
|
||||
$this->ignore_files = $config->get('system.pages.ignore_files');
|
||||
$this->ignore_folders = $config->get('system.pages.ignore_folders');
|
||||
@@ -952,6 +970,9 @@ class Pages
|
||||
|
||||
$pages_dir = $locator->findResource('page://');
|
||||
|
||||
// Set active language
|
||||
$this->active_lang = $language->getActive();
|
||||
|
||||
if ($config->get('system.cache.enabled')) {
|
||||
/** @var Cache $cache */
|
||||
$cache = $this->grav['cache'];
|
||||
@@ -976,17 +997,19 @@ class Pages
|
||||
|
||||
$this->pages_cache_id = md5($pages_dir . $hash . $language->getActive() . $config->checksum());
|
||||
|
||||
list($this->instances, $this->routes, $this->children, $taxonomy_map, $this->sort) = $cache->fetch($this->pages_cache_id);
|
||||
if (!$this->instances) {
|
||||
$cached = $cache->fetch($this->pages_cache_id);
|
||||
if ($cached) {
|
||||
$this->grav['debugger']->addMessage('Page cache hit.');
|
||||
|
||||
list($this->instances, $this->routes, $this->children, $taxonomy_map, $this->sort) = $cached;
|
||||
|
||||
// If pages was found in cache, set the taxonomy
|
||||
$taxonomy->taxonomy($taxonomy_map);
|
||||
} else {
|
||||
$this->grav['debugger']->addMessage('Page cache missed, rebuilding pages..');
|
||||
|
||||
// recurse pages and cache result
|
||||
$this->resetPages($pages_dir);
|
||||
|
||||
} else {
|
||||
// If pages was found in cache, set the taxonomy
|
||||
$this->grav['debugger']->addMessage('Page cache hit.');
|
||||
$taxonomy->taxonomy($taxonomy_map);
|
||||
}
|
||||
} else {
|
||||
$this->recurse($pages_dir);
|
||||
@@ -1255,14 +1278,13 @@ class Pages
|
||||
{
|
||||
$list = [];
|
||||
$header_default = null;
|
||||
$header_query = null;
|
||||
$header_query = [];
|
||||
|
||||
// do this header query work only once
|
||||
if (strpos($order_by, 'header.') === 0) {
|
||||
$header_query = explode('|', str_replace('header.', '', $order_by));
|
||||
if (isset($header_query[1])) {
|
||||
$header_default = $header_query[1];
|
||||
}
|
||||
$query = explode('|', str_replace('header.', '', $order_by), 2);
|
||||
$header_query = array_shift($query) ?? '';
|
||||
$header_default = array_shift($query);
|
||||
}
|
||||
|
||||
foreach ($pages as $key => $info) {
|
||||
@@ -1300,11 +1322,17 @@ class Pages
|
||||
case 'folder':
|
||||
$list[$key] = $child->folder();
|
||||
break;
|
||||
case (is_string($header_query[0])):
|
||||
$child_header = new Header((array)$child->header());
|
||||
$header_value = $child_header->get($header_query[0]);
|
||||
case 'manual':
|
||||
case 'default':
|
||||
default:
|
||||
if (is_string($header_query)) {
|
||||
$child_header = $child->header();
|
||||
if (!$child_header instanceof Header) {
|
||||
$child_header = new Header((array)$child_header);
|
||||
}
|
||||
$header_value = $child_header->get($header_query);
|
||||
if (is_array($header_value)) {
|
||||
$list[$key] = implode(',',$header_value);
|
||||
$list[$key] = implode(',', $header_value);
|
||||
} elseif ($header_value) {
|
||||
$list[$key] = $header_value;
|
||||
} else {
|
||||
@@ -1312,11 +1340,9 @@ class Pages
|
||||
}
|
||||
$sort_flags = $sort_flags ?: SORT_REGULAR;
|
||||
break;
|
||||
case 'manual':
|
||||
case 'default':
|
||||
default:
|
||||
$list[$key] = $key;
|
||||
$sort_flags = $sort_flags ?: SORT_REGULAR;
|
||||
}
|
||||
$list[$key] = $key;
|
||||
$sort_flags = $sort_flags ?: SORT_REGULAR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -151,15 +151,32 @@ class Plugin implements EventSubscriberInterface, \ArrayAccess
|
||||
if (\is_string($params)) {
|
||||
$dispatcher->addListener($eventName, [$this, $params]);
|
||||
} elseif (\is_string($params[0])) {
|
||||
$dispatcher->addListener($eventName, [$this, $params[0]], $params[1] ?? 0);
|
||||
$dispatcher->addListener($eventName, [$this, $params[0]], $this->getPriority($params, $eventName));
|
||||
} else {
|
||||
foreach ($params as $listener) {
|
||||
$dispatcher->addListener($eventName, [$this, $listener[0]], $listener[1] ?? 0);
|
||||
$dispatcher->addListener($eventName, [$this, $listener[0]], $this->getPriority($listener, $eventName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @param string $eventName
|
||||
*/
|
||||
private function getPriority($params, $eventName)
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
$override = implode('.', ["priorities", $this->name, $eventName, $params[0]]);
|
||||
if ($grav['config']->get($override) !== null)
|
||||
{
|
||||
return $grav['config']->get($override);
|
||||
} elseif (isset($params[1])) {
|
||||
return $params[1];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $events
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace Grav\Common\Processors;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Framework\Session\Exceptions\SessionException;
|
||||
@@ -22,6 +23,22 @@ class InitializeProcessor extends ProcessorBase
|
||||
public $id = 'init';
|
||||
public $title = 'Initialize';
|
||||
|
||||
/** @var bool */
|
||||
private static $cli_initialized = false;
|
||||
|
||||
/**
|
||||
* @param Grav $grav
|
||||
*/
|
||||
public static function initializeCli(Grav $grav)
|
||||
{
|
||||
if (!static::$cli_initialized) {
|
||||
static::$cli_initialized = true;
|
||||
|
||||
$instance = new static($grav);
|
||||
$instance->processCli();
|
||||
}
|
||||
}
|
||||
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
|
||||
{
|
||||
$this->startTimer();
|
||||
@@ -77,4 +94,35 @@ class InitializeProcessor extends ProcessorBase
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
public function processCli(): void
|
||||
{
|
||||
// Load configuration.
|
||||
$this->container['config']->init();
|
||||
$this->container['plugins']->setup();
|
||||
|
||||
// Disable debugger.
|
||||
$this->container['debugger']->enabled(false);
|
||||
|
||||
// Set timezone, locale.
|
||||
/** @var Config $config */
|
||||
$config = $this->container['config'];
|
||||
$timezone = $config->get('system.timezone');
|
||||
if ($timezone) {
|
||||
date_default_timezone_set($timezone);
|
||||
}
|
||||
$this->container->setLocale();
|
||||
|
||||
// Load plugins.
|
||||
$this->container['plugins']->init();
|
||||
|
||||
// Initialize URI.
|
||||
/** @var Uri $uri */
|
||||
$uri = $this->container['uri'];
|
||||
$uri->init();
|
||||
|
||||
// Load accounts.
|
||||
// TODO: remove in 2.0.
|
||||
$this->container['accounts'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,10 @@ class AccountsServiceProvider implements ServiceProviderInterface
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container['accounts'] = function (Container $container) {
|
||||
/** @var Debugger $debugger */
|
||||
$debugger = $container['debugger'];
|
||||
if ($container['config']->get('system.accounts.type') === 'flex') {
|
||||
$type = strtolower(defined('GRAV_USER_INSTANCE') ? GRAV_USER_INSTANCE : $container['config']->get('system.accounts.type', 'data'));
|
||||
if ($type === 'flex') {
|
||||
/** @var Debugger $debugger */
|
||||
$debugger = $container['debugger'];
|
||||
$debugger->addMessage('User Accounts: Flex Directory');
|
||||
return $this->flexAccounts($container);
|
||||
}
|
||||
@@ -46,7 +47,9 @@ class AccountsServiceProvider implements ServiceProviderInterface
|
||||
|
||||
protected function dataAccounts(Container $container)
|
||||
{
|
||||
define('GRAV_USER_INSTANCE', 'DATA');
|
||||
if (!defined('GRAV_USER_INSTANCE')) {
|
||||
define('GRAV_USER_INSTANCE', 'DATA');
|
||||
}
|
||||
|
||||
// Use User class for backwards compatibility.
|
||||
return new DataUser\UserCollection(User::class);
|
||||
@@ -54,7 +57,9 @@ class AccountsServiceProvider implements ServiceProviderInterface
|
||||
|
||||
protected function flexAccounts(Container $container)
|
||||
{
|
||||
define('GRAV_USER_INSTANCE', 'FLEX');
|
||||
if (!defined('GRAV_USER_INSTANCE')) {
|
||||
define('GRAV_USER_INSTANCE', 'FLEX');
|
||||
}
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $container['config'];
|
||||
|
||||
@@ -26,6 +26,19 @@ class PagesServiceProvider implements ServiceProviderInterface
|
||||
return new Pages($c);
|
||||
};
|
||||
|
||||
if (\defined('GRAV_CLI')) {
|
||||
$container['page'] = static function ($c) {
|
||||
$path = $c['locator']->findResource('system://pages/notfound.md');
|
||||
$page = new Page();
|
||||
$page->init(new \SplFileInfo($path));
|
||||
$page->routable(false);
|
||||
|
||||
return $page;
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$container['page'] = function ($c) {
|
||||
/** @var Grav $c */
|
||||
|
||||
|
||||
@@ -41,6 +41,6 @@ class TwigNodeMarkdown extends Node implements NodeOutputInterface
|
||||
->write('$lines = explode("\n", $content);' . PHP_EOL)
|
||||
->write('$content = preg_replace(\'/^\' . $matches[0]. \'/\', "", $lines);' . PHP_EOL)
|
||||
->write('$content = join("\n", $content);' . PHP_EOL)
|
||||
->write('echo $this->env->getExtension(\'Grav\Common\Twig\TwigExtension\')->markdownFunction($content);' . PHP_EOL);
|
||||
->write('echo $this->env->getExtension(\'Grav\Common\Twig\TwigExtension\')->markdownFunction($context, $content);' . PHP_EOL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
|
||||
new \Twig_SimpleFilter('fieldName', [$this, 'fieldNameFilter']),
|
||||
new \Twig_SimpleFilter('ksort', [$this, 'ksortFilter']),
|
||||
new \Twig_SimpleFilter('ltrim', [$this, 'ltrimFilter']),
|
||||
new \Twig_SimpleFilter('markdown', [$this, 'markdownFunction'], ['is_safe' => ['html']]),
|
||||
new \Twig_SimpleFilter('markdown', [$this, 'markdownFunction'], ['needs_context' => true, 'is_safe' => ['html']]),
|
||||
new \Twig_SimpleFilter('md5', [$this, 'md5Filter']),
|
||||
new \Twig_SimpleFilter('base32_encode', [$this, 'base32EncodeFilter']),
|
||||
new \Twig_SimpleFilter('base32_decode', [$this, 'base32DecodeFilter']),
|
||||
@@ -455,7 +455,7 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a human readable output for cron sytnax
|
||||
* Gets a human readable output for cron syntax
|
||||
*
|
||||
* @param $at
|
||||
* @return string
|
||||
@@ -613,12 +613,14 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
|
||||
/**
|
||||
* @param string $string
|
||||
*
|
||||
* @param array $context
|
||||
* @param bool $block Block or Line processing
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function markdownFunction($string, $block = true)
|
||||
public function markdownFunction($context, $string, $block = true)
|
||||
{
|
||||
return Utils::processMarkdown($string, $block);
|
||||
$page = $context['page'] ?? null;
|
||||
return Utils::processMarkdown($string, $block, $page);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1004,10 +1006,10 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
|
||||
*/
|
||||
public function authorize($action)
|
||||
{
|
||||
/** @var UserInterface $user */
|
||||
$user = $this->grav['user'];
|
||||
/** @var UserInterface|null $user */
|
||||
$user = $this->grav['user'] ?? null;
|
||||
|
||||
if (!$user->authenticated || (isset($user->authorized) && !$user->authorized)) {
|
||||
if (!$user || !$user->authenticated || (isset($user->authorized) && !$user->authorized)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1053,7 +1055,7 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
|
||||
*/
|
||||
public function jsonDecodeFilter($str, $assoc = false, $depth = 512, $options = 0)
|
||||
{
|
||||
return json_decode(html_entity_decode($str), $assoc, $depth, $options);
|
||||
return json_decode(html_entity_decode($str, ENT_COMPAT | ENT_HTML401, 'UTF-8'), $assoc, $depth, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1136,7 +1138,7 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
|
||||
}
|
||||
|
||||
/**
|
||||
* Get's the Exif data for a file
|
||||
* Get the Exif data for a file
|
||||
*
|
||||
* @param string $image
|
||||
* @param bool $raw
|
||||
|
||||
@@ -151,7 +151,7 @@ class Uri
|
||||
|
||||
$this->url = $this->base . $this->uri;
|
||||
|
||||
$uri = str_replace(static::filterPath($this->root), '', $this->url);
|
||||
$uri = Utils::replaceFirstOccurrence(static::filterPath($this->root), '', $this->url);
|
||||
|
||||
// remove the setup.php based base if set:
|
||||
$setup_base = $grav['pages']->base();
|
||||
@@ -195,7 +195,7 @@ class Uri
|
||||
// set the new url
|
||||
$this->url = $this->root . $path;
|
||||
$this->path = static::cleanPath($path);
|
||||
$this->content_path = trim(str_replace($this->base, '', $this->path), '/');
|
||||
$this->content_path = trim(Utils::replaceFirstOccurrence($this->base, '', $this->path), '/');
|
||||
if ($this->content_path !== '') {
|
||||
$this->paths = explode('/', $this->content_path);
|
||||
}
|
||||
@@ -306,7 +306,7 @@ class Uri
|
||||
public function param($id)
|
||||
{
|
||||
if (isset($this->params[$id])) {
|
||||
return html_entity_decode(rawurldecode($this->params[$id]));
|
||||
return html_entity_decode(rawurldecode($this->params[$id]), ENT_COMPAT | ENT_HTML401, 'UTF-8');
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -340,7 +340,7 @@ class Uri
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
$url = str_replace($this->base, '', rtrim($this->url, '/'));
|
||||
$url = Utils::replaceFirstOccurrence($this->base, '', rtrim($this->url, '/'));
|
||||
|
||||
return $url ?: '/';
|
||||
}
|
||||
@@ -489,7 +489,7 @@ class Uri
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
return str_replace($this->root_path, '', $this->uri);
|
||||
return Utils::replaceFirstOccurrence($this->root_path, '', $this->uri);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -531,7 +531,7 @@ class Uri
|
||||
return $this->root;
|
||||
}
|
||||
|
||||
return str_replace($this->base, '', $this->root);
|
||||
return Utils::replaceFirstOccurrence($this->base, '', $this->root);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -541,7 +541,9 @@ class Uri
|
||||
*/
|
||||
public function currentPage()
|
||||
{
|
||||
return $this->params['page'] ?? 1;
|
||||
$page = (int)($this->params['page'] ?? 1);
|
||||
|
||||
return max(1, $page);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -783,7 +785,7 @@ class Uri
|
||||
}
|
||||
|
||||
// special check to see if path checking is required.
|
||||
$just_path = str_replace($normalized_url, '', $normalized_path);
|
||||
$just_path = Utils::replaceFirstOccurrence($normalized_url, '', $normalized_path);
|
||||
if ($normalized_url === '/' || $just_path === $page->path()) {
|
||||
$url_path = $normalized_url;
|
||||
} else {
|
||||
@@ -852,7 +854,7 @@ class Uri
|
||||
}
|
||||
|
||||
// strip base from this path
|
||||
$target_path = str_replace($uri->rootUrl(), '', $target_path);
|
||||
$target_path = Utils::replaceFirstOccurrence($uri->rootUrl(), '', $target_path);
|
||||
|
||||
// set to / if root
|
||||
if (empty($target_path)) {
|
||||
@@ -877,7 +879,7 @@ class Uri
|
||||
|
||||
// Handle route only
|
||||
if ($route_only) {
|
||||
$url_path = str_replace(static::filterPath($base_url), '', $url_path);
|
||||
$url_path = Utils::replaceFirstOccurrence(static::filterPath($base_url), '', $url_path);
|
||||
}
|
||||
|
||||
// transform back to string/array as needed
|
||||
@@ -998,7 +1000,7 @@ class Uri
|
||||
}
|
||||
|
||||
// special check to see if path checking is required.
|
||||
$just_path = str_replace($normalized_url, '', $normalized_path);
|
||||
$just_path = Utils::replaceFirstOccurrence($normalized_url, '', $normalized_path);
|
||||
if ($just_path === $page->path()) {
|
||||
return $normalized_url;
|
||||
}
|
||||
@@ -1167,7 +1169,9 @@ class Uri
|
||||
|
||||
// Build host.
|
||||
$hostname = 'localhost';
|
||||
if (isset($env['HTTP_HOST'])) {
|
||||
if (isset($env['HTTP_X_FORWARDED_HOST'])) {
|
||||
$hostname = $env['HTTP_X_FORWARDED_HOST'];
|
||||
} else if (isset($env['HTTP_HOST'])) {
|
||||
$hostname = $env['HTTP_HOST'];
|
||||
} elseif (isset($env['SERVER_NAME'])) {
|
||||
$hostname = $env['SERVER_NAME'];
|
||||
@@ -1286,7 +1290,7 @@ class Uri
|
||||
}
|
||||
|
||||
/**
|
||||
* Get's post from either $_POST or JSON response object
|
||||
* Get post from either $_POST or JSON response object
|
||||
* By default returns all data, or can return a single item
|
||||
*
|
||||
* @param string $element
|
||||
@@ -1345,7 +1349,7 @@ class Uri
|
||||
*/
|
||||
public function isValidExtension($extension)
|
||||
{
|
||||
$valid_page_types = implode('|', Grav::instance()['config']->get('system.pages.types'));
|
||||
$valid_page_types = implode('|', Utils::getSupportPageTypes());
|
||||
|
||||
// Strip the file extension for valid page types
|
||||
if (preg_match('/(' . $valid_page_types . ')/', $extension)) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace Grav\Common\User\FlexUser;
|
||||
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Media\Interfaces\MediaCollectionInterface;
|
||||
use Grav\Common\Page\Media;
|
||||
@@ -21,6 +22,7 @@ use Grav\Framework\File\Formatter\JsonFormatter;
|
||||
use Grav\Framework\File\Formatter\YamlFormatter;
|
||||
use Grav\Framework\Flex\FlexDirectory;
|
||||
use Grav\Framework\Flex\FlexObject;
|
||||
use Grav\Framework\Flex\Storage\FileStorage;
|
||||
use Grav\Framework\Flex\Traits\FlexAuthorizeTrait;
|
||||
use Grav\Framework\Flex\Traits\FlexMediaTrait;
|
||||
use Grav\Framework\Form\FormFlashFile;
|
||||
@@ -381,6 +383,31 @@ class User extends FlexObject implements UserInterface, MediaManipulationInterfa
|
||||
return $this->getBlueprint()->extra($this->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return Blueprint
|
||||
*/
|
||||
public function getBlueprint(string $name = '')
|
||||
{
|
||||
$blueprint = clone parent::getBlueprint($name);
|
||||
|
||||
$blueprint->addDynamicHandler('flex', function (array &$field, $property, array &$call) {
|
||||
$params = (array)$call['params'];
|
||||
$method = array_shift($params);
|
||||
|
||||
if (method_exists($this, $method)) {
|
||||
$value = $this->{$method}(...$params);
|
||||
if (\is_array($value) && isset($field[$property]) && \is_array($field[$property])) {
|
||||
$field[$property] = array_merge_recursive($field[$property], $value);
|
||||
} else {
|
||||
$field[$property] = $value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return $blueprint->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return unmodified data as raw string.
|
||||
*
|
||||
@@ -420,6 +447,15 @@ class User extends FlexObject implements UserInterface, MediaManipulationInterfa
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
// TODO: We may want to handle this in the storage layer in the future.
|
||||
$key = $this->getStorageKey();
|
||||
if (!$key || strpos($key, '@@')) {
|
||||
$storage = $this->getFlexDirectory()->getStorage();
|
||||
if ($storage instanceof FileStorage) {
|
||||
$this->setStorageKey($this->getKey());
|
||||
}
|
||||
}
|
||||
|
||||
$password = $this->getProperty('password');
|
||||
if (null !== $password) {
|
||||
$this->unsetProperty('password');
|
||||
@@ -431,6 +467,20 @@ class User extends FlexObject implements UserInterface, MediaManipulationInterfa
|
||||
return parent::save();
|
||||
}
|
||||
|
||||
public function isAuthorized(string $action, string $scope = null, UserInterface $user = null): bool
|
||||
{
|
||||
if (null === $user) {
|
||||
/** @var UserInterface $user */
|
||||
$user = Grav::instance()['user'] ?? null;
|
||||
}
|
||||
|
||||
if ($user instanceof User && $user->getStorageKey() === $this->getStorageKey()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return parent::isAuthorized($action, $scope, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
|
||||
@@ -28,7 +28,7 @@ abstract class Utils
|
||||
/**
|
||||
* Simple helper method to make getting a Grav URL easier
|
||||
*
|
||||
* @param string $input
|
||||
* @param string|object $input
|
||||
* @param bool $domain
|
||||
* @param bool $fail_gracefully
|
||||
* @return bool|null|string
|
||||
@@ -43,47 +43,80 @@ abstract class Utils
|
||||
}
|
||||
}
|
||||
|
||||
if (Grav::instance()['config']->get('system.absolute_urls', false)) {
|
||||
$domain = true;
|
||||
}
|
||||
$input = (string)$input;
|
||||
|
||||
if (Uri::isExternal($input)) {
|
||||
return $input;
|
||||
}
|
||||
|
||||
$grav = Grav::instance();
|
||||
|
||||
/** @var Uri $uri */
|
||||
$uri = Grav::instance()['uri'];
|
||||
$uri = $grav['uri'];
|
||||
|
||||
$root = $uri->rootUrl();
|
||||
$input = Utils::replaceFirstOccurrence($root, '', $input);
|
||||
|
||||
$input = ltrim((string)$input, '/');
|
||||
|
||||
if (Utils::contains((string)$input, '://')) {
|
||||
if (static::contains((string)$input, '://')) {
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
$locator = $grav['locator'];
|
||||
|
||||
$parts = Uri::parseUrl($input);
|
||||
|
||||
if ($parts) {
|
||||
try {
|
||||
$resource = $locator->findResource("{$parts['scheme']}://{$parts['host']}{$parts['path']}", false);
|
||||
} catch (\Exception $e) {
|
||||
if ($fail_gracefully) {
|
||||
return $input;
|
||||
} else {
|
||||
return false;
|
||||
if (is_array($parts)) {
|
||||
// Make sure we always have scheme, host, port and path.
|
||||
$scheme = $parts['scheme'] ?? '';
|
||||
$host = $parts['host'] ?? '';
|
||||
$port = $parts['port'] ?? '';
|
||||
$path = $parts['path'] ?? '';
|
||||
|
||||
if ($scheme && !$port) {
|
||||
// If URL has a scheme, we need to check if it's one of Grav streams.
|
||||
if (!$locator->schemeExists($scheme)) {
|
||||
// If scheme does not exists as a stream, assume it's external.
|
||||
return str_replace(' ', '%20', $input);
|
||||
}
|
||||
|
||||
// Attempt to find the resource (because of parse_url() we need to put host back to path).
|
||||
$resource = $locator->findResource("{$scheme}://{$host}{$path}", false);
|
||||
|
||||
if ($resource === false) {
|
||||
if (!$fail_gracefully) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return location where the file would be if it was saved.
|
||||
$resource = $locator->findResource("{$scheme}://{$host}{$path}", false, true);
|
||||
}
|
||||
|
||||
} elseif ($host || $port) {
|
||||
// If URL doesn't have scheme but has host or port, it is external.
|
||||
return str_replace(' ', '%20', $input);
|
||||
}
|
||||
|
||||
if (!empty($resource)) {
|
||||
// Add query string back.
|
||||
if (isset($parts['query'])) {
|
||||
$resource .= '?' . $parts['query'];
|
||||
}
|
||||
|
||||
// Add fragment back.
|
||||
if (isset($parts['fragment'])) {
|
||||
$resource .= '#' . $parts['fragment'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($resource && isset($parts['query'])) {
|
||||
$resource = $resource . '?' . $parts['query'];
|
||||
}
|
||||
} else {
|
||||
// Not a valid URL (can still be a stream).
|
||||
$resource = $locator->findResource($input, false);
|
||||
}
|
||||
|
||||
} else {
|
||||
$root = $uri->rootUrl();
|
||||
|
||||
if (static::startsWith($input, $root)) {
|
||||
$input = static::replaceFirstOccurrence($root, '', $input);
|
||||
}
|
||||
|
||||
$input = ltrim($input, '/');
|
||||
|
||||
$resource = $input;
|
||||
}
|
||||
|
||||
@@ -91,6 +124,8 @@ abstract class Utils
|
||||
return false;
|
||||
}
|
||||
|
||||
$domain = $domain ?: $grav['config']->get('system.absolute_urls', false);
|
||||
|
||||
return rtrim($uri->rootUrl($domain), '/') . '/' . ($resource ?? '');
|
||||
}
|
||||
|
||||
@@ -288,6 +323,35 @@ abstract class Utils
|
||||
return (object)array_merge((array)$obj1, (array)$obj2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lowercase an entire array. Useful when combined with `in_array()`
|
||||
*
|
||||
* @param array $a
|
||||
* @return array|false
|
||||
*/
|
||||
public static function arrayLower(Array $a)
|
||||
{
|
||||
return array_map('mb_strtolower', $a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple function to remove item/s in an array by value
|
||||
*
|
||||
* @param $search array
|
||||
* @param $value string|array
|
||||
* @return array
|
||||
*/
|
||||
public static function arrayRemoveValue(Array $search, $value)
|
||||
{
|
||||
foreach ((array) $value as $val) {
|
||||
$key = array_search($val, $search);
|
||||
if ($key !== false) {
|
||||
unset($search[$key]);
|
||||
}
|
||||
}
|
||||
return $search;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive Merge with uniqueness
|
||||
*
|
||||
@@ -1070,12 +1134,9 @@ abstract class Utils
|
||||
*/
|
||||
private static function generateNonceString($action, $previousTick = false)
|
||||
{
|
||||
$username = '';
|
||||
if (isset(Grav::instance()['user'])) {
|
||||
$user = Grav::instance()['user'];
|
||||
$username = $user->username;
|
||||
}
|
||||
$grav = Grav::instance();
|
||||
|
||||
$username = isset($grav['user']) ? $grav['user']->username : '';
|
||||
$token = session_id();
|
||||
$i = self::nonceTick();
|
||||
|
||||
@@ -1083,7 +1144,7 @@ abstract class Utils
|
||||
$i--;
|
||||
}
|
||||
|
||||
return ($i . '|' . $action . '|' . $username . '|' . $token . '|' . Grav::instance()['config']->get('security.salt'));
|
||||
return ($i . '|' . $action . '|' . $username . '|' . $token . '|' . $grav['config']->get('security.salt'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1297,7 +1358,7 @@ abstract class Utils
|
||||
}
|
||||
|
||||
/**
|
||||
* Get's path based on a token
|
||||
* Get path based on a token
|
||||
*
|
||||
* @param string $path
|
||||
* @param PageInterface|null $page
|
||||
@@ -1465,13 +1526,15 @@ abstract class Utils
|
||||
*
|
||||
* @param string $string
|
||||
*
|
||||
* @param bool $block Block or Line processing
|
||||
* @param bool $block Block or Line processing
|
||||
* @param null $page
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function processMarkdown($string, $block = true)
|
||||
public static function processMarkdown($string, $block = true, $page = null)
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
$page = $grav['page'] ?? null;
|
||||
$page = $page ?? $grav['page'] ?? null;
|
||||
$defaults = [
|
||||
'markdown' => $grav['config']->get('system.pages.markdown', []),
|
||||
'images' => $grav['config']->get('system.images', [])
|
||||
@@ -1534,4 +1597,23 @@ abstract class Utils
|
||||
|
||||
return $subnet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper to ensure html, htm in the front of the supported page types
|
||||
*
|
||||
* @param array|null $defaults
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function getSupportPageTypes(array $defaults = null)
|
||||
{
|
||||
$types = Grav::instance()['config']->get('system.pages.types', $defaults);
|
||||
|
||||
// remove html/htm
|
||||
$types = static::arrayRemoveValue($types, ['html', 'htm']);
|
||||
|
||||
// put them back at the front
|
||||
$types = array_merge(['html', 'htm'], $types);
|
||||
|
||||
return $types;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
namespace Grav\Console;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Language\Language;
|
||||
use Grav\Common\Page\Page;
|
||||
use Grav\Common\Processors\InitializeProcessor;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
@@ -18,6 +22,13 @@ class ConsoleCommand extends Command
|
||||
{
|
||||
use ConsoleTrait;
|
||||
|
||||
/** @var bool */
|
||||
private $plugins_initialized = false;
|
||||
/** @var bool */
|
||||
private $themes_initialized = false;
|
||||
/** @var bool */
|
||||
private $pages_initialized = false;
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
@@ -31,12 +42,140 @@ class ConsoleCommand extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Override with your implementation.
|
||||
*/
|
||||
protected function serve()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Grav.
|
||||
*
|
||||
* - Load configuration
|
||||
* - Disable debugger
|
||||
* - Set timezone, locale
|
||||
* - Load plugins
|
||||
* - Set Users type to be used in the site
|
||||
*
|
||||
* Safe to be called multiple times.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final protected function initializeGrav()
|
||||
{
|
||||
InitializeProcessor::initializeCli(Grav::instance());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set language to be used in CLI.
|
||||
*
|
||||
* @param string|null $code
|
||||
*/
|
||||
final protected function setLanguage(string $code = null)
|
||||
{
|
||||
$this->initializeGrav();
|
||||
|
||||
$grav = Grav::instance();
|
||||
/** @var Language $language */
|
||||
$language = $grav['language'];
|
||||
if ($language->enabled()) {
|
||||
if ($code && $language->validate($code)) {
|
||||
$language->setActive($code);
|
||||
} else {
|
||||
$language->setActive($language->getDefault());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Properly initialize plugins.
|
||||
*
|
||||
* - call $this->initializeGrav()
|
||||
* - call onPluginsInitialized event
|
||||
*
|
||||
* Safe to be called multiple times.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final protected function initializePlugins()
|
||||
{
|
||||
if (!$this->plugins_initialized) {
|
||||
$this->plugins_initialized = true;
|
||||
|
||||
$this->initializeGrav();
|
||||
|
||||
// Initialize plugins.
|
||||
$grav = Grav::instance();
|
||||
$grav->fireEvent('onPluginsInitialized');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properly initialize themes.
|
||||
*
|
||||
* - call $this->initializePlugins()
|
||||
* - initialize theme (call onThemeInitialized event)
|
||||
*
|
||||
* Safe to be called multiple times.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final protected function initializeThemes()
|
||||
{
|
||||
if (!$this->themes_initialized) {
|
||||
$this->themes_initialized = true;
|
||||
|
||||
$this->initializePlugins();
|
||||
|
||||
// Initialize themes.
|
||||
$grav = Grav::instance();
|
||||
$grav['themes']->init();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properly initialize pages.
|
||||
*
|
||||
* - call $this->initializeThemes()
|
||||
* - initialize assets (call onAssetsInitialized event)
|
||||
* - initialize twig (calls the twig events)
|
||||
* - initialize pages (calls onPagesInitialized event)
|
||||
*
|
||||
* Safe to be called multiple times.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final protected function initializePages()
|
||||
{
|
||||
if (!$this->pages_initialized) {
|
||||
$this->pages_initialized = true;
|
||||
|
||||
$this->initializeThemes();
|
||||
|
||||
$grav = Grav::instance();
|
||||
|
||||
// Initialize assets.
|
||||
$grav['assets']->init();
|
||||
$grav->fireEvent('onAssetsInitialized');
|
||||
|
||||
// Initialize twig.
|
||||
$grav['twig']->init();
|
||||
|
||||
// Initialize pages.
|
||||
$pages = $grav['pages'];
|
||||
$pages->init();
|
||||
$grav->fireEvent('onPagesInitialized', new Event(['pages' => $pages]));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function displayGPMRelease()
|
||||
{
|
||||
$this->output->writeln('');
|
||||
|
||||
@@ -177,7 +177,7 @@ class SelfupgradeCommand extends ConsoleCommand
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Package $package
|
||||
* @param array $package
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -437,7 +437,7 @@ abstract class AbstractIndexCollection implements CollectionInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementes JsonSerializable interface.
|
||||
* Implements JsonSerializable interface.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
||||
@@ -84,7 +84,7 @@ class ArrayCollection extends BaseArrayCollection implements CollectionInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementes JsonSerializable interface.
|
||||
* Implements JsonSerializable interface.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
||||
@@ -53,11 +53,11 @@ class CsvFormatter extends AbstractFormatter
|
||||
$header = array_keys(reset($data));
|
||||
|
||||
// Encode the field names
|
||||
$string = implode($delimiter, $header). "\n";
|
||||
$string = $this->encodeLine($header, $delimiter);
|
||||
|
||||
// Encode the data
|
||||
foreach ($data as $row) {
|
||||
$string .= implode($delimiter, $row). "\n";
|
||||
$string .= $this->encodeLine($row, $delimiter);
|
||||
}
|
||||
|
||||
return $string;
|
||||
@@ -79,12 +79,49 @@ class CsvFormatter extends AbstractFormatter
|
||||
// Get the field names
|
||||
$header = str_getcsv(array_shift($lines), $delimiter);
|
||||
|
||||
// Allow for replacing a null string with null/empty value
|
||||
$null_replace = $this->getConfig('null');
|
||||
|
||||
// Get the data
|
||||
$list = [];
|
||||
foreach ($lines as $line) {
|
||||
$list[] = array_combine($header, str_getcsv($line, $delimiter));
|
||||
$line = null;
|
||||
try {
|
||||
foreach ($lines as $line) {
|
||||
if (!empty($line)) {
|
||||
$csv_line = str_getcsv($line, $delimiter);
|
||||
|
||||
if ($null_replace) {
|
||||
array_walk($csv_line, function(&$el) use ($null_replace) {
|
||||
$el = str_replace($null_replace, "\0", $el);
|
||||
});
|
||||
}
|
||||
|
||||
$list[] = array_combine($header, $csv_line);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception('Badly formatted CSV line: ' . $line);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
protected function encodeLine(array $line, $delimiter = null): string
|
||||
{
|
||||
foreach ($line as $key => &$value) {
|
||||
$value = $this->escape((string)$value);
|
||||
}
|
||||
unset($value);
|
||||
|
||||
return implode($delimiter, $line). "\n";
|
||||
}
|
||||
|
||||
protected function escape(string $value)
|
||||
{
|
||||
if (preg_match('/[,"\r\n]/u', $value)) {
|
||||
$value = '"' . preg_replace('/"/', '""', $value) . '"';
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ class YamlFormatter extends AbstractFormatter
|
||||
@ini_set('yaml.decode_php', $saved);
|
||||
|
||||
if ($decoded !== false) {
|
||||
return $decoded;
|
||||
return (array) $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -550,6 +550,15 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: For some reason locator caching isn't cleared for the file, investigate!
|
||||
$locator = Grav::instance()['locator'];
|
||||
$locator->clearCache();
|
||||
|
||||
// Make sure that the object exists before continuing (just in case).
|
||||
if (!$this->exists()) {
|
||||
throw new \RuntimeException('Saving failed: Object does not exist!');
|
||||
}
|
||||
|
||||
if (method_exists($this, 'saveUpdatedMedia')) {
|
||||
$this->saveUpdatedMedia();
|
||||
}
|
||||
|
||||
@@ -27,10 +27,10 @@ trait FlexAuthorizeTrait
|
||||
{
|
||||
if (null === $user) {
|
||||
/** @var UserInterface $user */
|
||||
$user = Grav::instance()['user'];
|
||||
$user = Grav::instance()['user'] ?? null;
|
||||
}
|
||||
|
||||
return $this->isAuthorizedAction($user, $action, $scope) || $this->isAuthorizedSuperAdmin($user);
|
||||
return $user && ($this->isAuthorizedAction($user, $action, $scope) || $this->isAuthorizedSuperAdmin($user));
|
||||
}
|
||||
|
||||
protected function isAuthorizedSuperAdmin(UserInterface $user): bool
|
||||
|
||||
@@ -49,7 +49,7 @@ trait FlexMediaTrait
|
||||
*/
|
||||
public function getStorageFolder()
|
||||
{
|
||||
return $this->getFlexDirectory()->getStorageFolder($this->getStorageKey());
|
||||
return $this->exists() ? $this->getFlexDirectory()->getStorageFolder($this->getStorageKey()) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,7 +57,7 @@ trait FlexMediaTrait
|
||||
*/
|
||||
public function getMediaFolder()
|
||||
{
|
||||
return $this->getFlexDirectory()->getMediaFolder($this->getStorageKey());
|
||||
return $this->exists() ? $this->getFlexDirectory()->getMediaFolder($this->getStorageKey()) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,6 +153,12 @@ trait FlexMediaTrait
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $grav['locator'];
|
||||
$path = $media->getPath();
|
||||
if (!$path) {
|
||||
$language = $grav['language'];
|
||||
|
||||
throw new RuntimeException($language->translate('PLUGIN_ADMIN.FAILED_TO_MOVE_UPLOADED_FILE'), 400);
|
||||
}
|
||||
|
||||
if ($locator->isStream($path)) {
|
||||
$path = $locator->findResource($path, true, true);
|
||||
$locator->clearCache($path);
|
||||
@@ -202,12 +208,16 @@ trait FlexMediaTrait
|
||||
}
|
||||
|
||||
$media = $this->getMedia();
|
||||
$path = $media->getPath();
|
||||
if (!$path) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $grav['locator'];
|
||||
|
||||
$targetPath = $media->getPath() . '/' . $dirname;
|
||||
$targetFile = $media->getPath() . '/' . $filename;
|
||||
$targetPath = $path . '/' . $dirname;
|
||||
$targetFile = $path . '/' . $filename;
|
||||
if ($locator->isStream($targetFile)) {
|
||||
$targetPath = $locator->findResource($targetPath, true, true);
|
||||
$targetFile = $locator->findResource($targetFile, true, true);
|
||||
@@ -332,5 +342,5 @@ trait FlexMediaTrait
|
||||
|
||||
abstract public function getFlexDirectory(): FlexDirectory;
|
||||
|
||||
abstract public function getStorageKey();
|
||||
abstract public function getStorageKey(): string;
|
||||
}
|
||||
|
||||
@@ -10,12 +10,18 @@
|
||||
namespace Grav\Framework\Form;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\User\Interfaces\UserInterface;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Framework\Form\Interfaces\FormFlashInterface;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use RocketTheme\Toolbox\File\YamlFile;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
class FormFlash implements \JsonSerializable
|
||||
class FormFlash implements FormFlashInterface
|
||||
{
|
||||
/** @var bool */
|
||||
protected $exists;
|
||||
/** @var string */
|
||||
protected $sessionId;
|
||||
/** @var string */
|
||||
@@ -26,6 +32,10 @@ class FormFlash implements \JsonSerializable
|
||||
protected $url;
|
||||
/** @var array */
|
||||
protected $user;
|
||||
/** @var int */
|
||||
protected $createdTimestamp;
|
||||
/** @var int */
|
||||
protected $updatedTimestamp;
|
||||
/** @var array */
|
||||
protected $data;
|
||||
/** @var array */
|
||||
@@ -34,29 +44,35 @@ class FormFlash implements \JsonSerializable
|
||||
protected $uploadedFiles;
|
||||
/** @var string[] */
|
||||
protected $uploadObjects;
|
||||
/** @var bool */
|
||||
protected $exists;
|
||||
/** @var string */
|
||||
protected $folder;
|
||||
|
||||
/**
|
||||
* @param string $sessionId
|
||||
* @return string
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function getSessionTmpDir(string $sessionId): string
|
||||
public function __construct($config)
|
||||
{
|
||||
return "tmp://forms/{$sessionId}";
|
||||
}
|
||||
// Backwards compatibility with Grav 1.6 plugins.
|
||||
if (!is_array($config)) {
|
||||
user_error(__CLASS__ . '::' . __FUNCTION__ . '($sessionId, $uniqueId, $formName) is deprecated since Grav 1.6.11, use $config parameter instead', E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* FormFlashObject constructor.
|
||||
* @param string $sessionId
|
||||
* @param string $uniqueId
|
||||
* @param string|null $formName
|
||||
*/
|
||||
public function __construct(string $sessionId, string $uniqueId, string $formName = null)
|
||||
{
|
||||
$this->sessionId = $sessionId;
|
||||
$this->uniqueId = $uniqueId;
|
||||
$args = func_get_args();
|
||||
$config = [
|
||||
'session_id' => $args[0],
|
||||
'unique_id' => $args[1] ?? null,
|
||||
'form_name' => $args[2] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
$this->sessionId = $config['session_id'] ?? 'no-session';
|
||||
$this->uniqueId = $config['unique_id'] ?? '';
|
||||
|
||||
$folder = $config['folder'] ?? ($this->sessionId ? 'tmp://forms/' . $this->sessionId : '');
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
$this->folder = $folder && $locator->isStream($folder) ? $locator->findResource($folder, true, true) : $folder;
|
||||
$file = $this->getTmpIndex();
|
||||
$this->exists = $file->exists();
|
||||
|
||||
@@ -66,65 +82,58 @@ class FormFlash implements \JsonSerializable
|
||||
} catch (\Exception $e) {
|
||||
$data = [];
|
||||
}
|
||||
$this->formName = null !== $formName ? $content['form'] ?? '' : '';
|
||||
$this->formName = $content['form'] ?? $config['form_name'] ?? '';
|
||||
$this->url = $data['url'] ?? '';
|
||||
$this->user = $data['user'] ?? null;
|
||||
$this->updatedTimestamp = $data['timestamps']['updated'] ?? time();
|
||||
$this->createdTimestamp = $data['timestamps']['created'] ?? $this->updatedTimestamp;
|
||||
$this->data = $data['data'] ?? null;
|
||||
$this->files = $data['files'] ?? [];
|
||||
} else {
|
||||
$this->formName = $formName;
|
||||
$this->formName = $config['form_name'] ?? '';
|
||||
$this->url = '';
|
||||
$this->createdTimestamp = $this->updatedTimestamp = time();
|
||||
$this->files = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getSessionId(): string
|
||||
{
|
||||
return $this->sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getUniqueId(): string
|
||||
{
|
||||
return $this->uniqueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 1.6.11 Use '->getUniqueId()' method instead.
|
||||
*/
|
||||
public function getUniqieId(): string
|
||||
{
|
||||
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.6.11, use ->getUniqueId() method instead', E_USER_DEPRECATED);
|
||||
|
||||
return $this->getUniqueId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getFormName(): string
|
||||
{
|
||||
return $this->formName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUniqieId(): string
|
||||
{
|
||||
return $this->uniqueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(): bool
|
||||
{
|
||||
return $this->exists;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function save(): self
|
||||
{
|
||||
$file = $this->getTmpIndex();
|
||||
$file->save($this->jsonSerialize());
|
||||
$this->exists = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function delete(): self
|
||||
{
|
||||
$this->removeTmpDir();
|
||||
$this->files = [];
|
||||
$this->exists = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getUrl(): string
|
||||
{
|
||||
@@ -132,18 +141,7 @@ class FormFlash implements \JsonSerializable
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @return $this
|
||||
*/
|
||||
public function setUrl(string $url): self
|
||||
{
|
||||
$this->url = $url;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getUsername(): string
|
||||
{
|
||||
@@ -151,7 +149,7 @@ class FormFlash implements \JsonSerializable
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getUserEmail(): string
|
||||
{
|
||||
@@ -159,40 +157,84 @@ class FormFlash implements \JsonSerializable
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $username
|
||||
* @return $this
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function setUserName(string $username = null): self
|
||||
public function getCreatedTimestamp(): int
|
||||
{
|
||||
$this->user['username'] = $username;
|
||||
|
||||
return $this;
|
||||
return $this->createdTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $email
|
||||
* @return $this
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function setUserEmail(string $email = null): self
|
||||
public function getUpdatedTimestamp(): int
|
||||
{
|
||||
$this->user['email'] = $email;
|
||||
|
||||
return $this;
|
||||
return $this->updatedTimestamp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getData(): ?array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function setData(?array $data): void
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @return array
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function exists(): bool
|
||||
{
|
||||
return $this->exists;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function save(): self
|
||||
{
|
||||
if (!($this->folder && $this->uniqueId)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($this->data || $this->files) {
|
||||
// Only save if there is data or files to be saved.
|
||||
$file = $this->getTmpIndex();
|
||||
$file->save($this->jsonSerialize());
|
||||
$this->exists = true;
|
||||
} elseif ($this->exists) {
|
||||
// Delete empty form flash if it exists (it carries no information).
|
||||
return $this->delete();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function delete(): self
|
||||
{
|
||||
if ($this->folder && $this->uniqueId) {
|
||||
$this->removeTmpDir();
|
||||
$this->files = [];
|
||||
$this->exists = false;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getFilesByField(string $field): array
|
||||
{
|
||||
@@ -208,8 +250,7 @@ class FormFlash implements \JsonSerializable
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $includeOriginal
|
||||
* @return array
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getFilesByFields($includeOriginal = false): array
|
||||
{
|
||||
@@ -225,12 +266,7 @@ class FormFlash implements \JsonSerializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Add uploaded file to the form flash.
|
||||
*
|
||||
* @param UploadedFileInterface $upload
|
||||
* @param string|null $field
|
||||
* @param array|null $crop
|
||||
* @return string Return name of the file
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function addUploadedFile(UploadedFileInterface $upload, string $field = null, array $crop = null): string
|
||||
{
|
||||
@@ -256,12 +292,7 @@ class FormFlash implements \JsonSerializable
|
||||
|
||||
|
||||
/**
|
||||
* Add existing file to the form flash.
|
||||
*
|
||||
* @param string $filename
|
||||
* @param string $field
|
||||
* @param array $crop
|
||||
* @return bool
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function addFile(string $filename, string $field, array $crop = null): bool
|
||||
{
|
||||
@@ -282,11 +313,7 @@ class FormFlash implements \JsonSerializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any file from form flash.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $field
|
||||
* @return bool
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function removeFile(string $name, string $field = null): bool
|
||||
{
|
||||
@@ -318,7 +345,7 @@ class FormFlash implements \JsonSerializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear form flash from all uploaded files.
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function clearFiles()
|
||||
{
|
||||
@@ -332,7 +359,7 @@ class FormFlash implements \JsonSerializable
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
@@ -341,17 +368,72 @@ class FormFlash implements \JsonSerializable
|
||||
'unique_id' => $this->uniqueId,
|
||||
'url' => $this->url,
|
||||
'user' => $this->user,
|
||||
'timestamps' => [
|
||||
'created' => $this->createdTimestamp,
|
||||
'updated' => time(),
|
||||
],
|
||||
'data' => $this->data,
|
||||
'files' => $this->files
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @return $this
|
||||
*/
|
||||
public function setUrl(string $url): self
|
||||
{
|
||||
$this->url = $url;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UserInterface|null $user
|
||||
* @return $this
|
||||
*/
|
||||
public function setUser(UserInterface $user = null)
|
||||
{
|
||||
if ($user && $user->username) {
|
||||
$this->user = [
|
||||
'username' => $user->username,
|
||||
'email' => $user->email ?? ''
|
||||
];
|
||||
} else {
|
||||
$this->user = null;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $username
|
||||
* @return $this
|
||||
*/
|
||||
public function setUserName(string $username = null): self
|
||||
{
|
||||
$this->user['username'] = $username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $email
|
||||
* @return $this
|
||||
*/
|
||||
public function setUserEmail(string $email = null): self
|
||||
{
|
||||
$this->user['email'] = $email;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTmpDir(): string
|
||||
{
|
||||
return static::getSessionTmpDir($this->sessionId) . '/' . $this->uniqueId;
|
||||
return $this->folder && $this->uniqueId ? "{$this->folder}/{$this->uniqueId}" : '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -368,8 +450,9 @@ class FormFlash implements \JsonSerializable
|
||||
*/
|
||||
protected function removeTmpFile(string $name): void
|
||||
{
|
||||
$filename = $this->getTmpDir() . '/' . $name;
|
||||
if ($name && is_file($filename)) {
|
||||
$tmpDir = $this->getTmpDir();
|
||||
$filename = $tmpDir ? $tmpDir . '/' . $name : '';
|
||||
if ($name && $filename && is_file($filename)) {
|
||||
unlink($filename);
|
||||
}
|
||||
}
|
||||
@@ -377,7 +460,7 @@ class FormFlash implements \JsonSerializable
|
||||
protected function removeTmpDir(): void
|
||||
{
|
||||
$tmpDir = $this->getTmpDir();
|
||||
if (file_exists($tmpDir)) {
|
||||
if ($tmpDir && file_exists($tmpDir)) {
|
||||
Folder::delete($tmpDir);
|
||||
}
|
||||
}
|
||||
@@ -390,6 +473,10 @@ class FormFlash implements \JsonSerializable
|
||||
*/
|
||||
protected function addFileInternal(?string $field, string $name, array $data, array $crop = null): void
|
||||
{
|
||||
if (!($this->folder && $this->uniqueId)) {
|
||||
throw new \RuntimeException('Cannot upload files: form flash folder not defined');
|
||||
}
|
||||
|
||||
$field = $field ?: 'undefined';
|
||||
if (!isset($this->files[$field])) {
|
||||
$this->files[$field] = [];
|
||||
|
||||
165
system/src/Grav/Framework/Form/Interfaces/FormFlashInterface.php
Normal file
165
system/src/Grav/Framework/Form/Interfaces/FormFlashInterface.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Grav\Framework\Form
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Framework\Form\Interfaces;
|
||||
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
|
||||
interface FormFlashInterface extends \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @param array $config Available configuration keys: session_id, unique_id, form_name
|
||||
*/
|
||||
public function __construct($config);
|
||||
|
||||
/**
|
||||
* Get session Id associated to this form instance.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSessionId(): string;
|
||||
|
||||
/**
|
||||
* Get unique identifier associated to this form instance.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUniqueId(): string;
|
||||
|
||||
/**
|
||||
* Get form name associated to this form instance.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFormName(): string;
|
||||
|
||||
/**
|
||||
* Get URL associated to this form instance.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl(): string;
|
||||
|
||||
/**
|
||||
* Get username from the user who was associated to this form instance.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername(): string;
|
||||
|
||||
/**
|
||||
* Get email from the user who was associated to this form instance.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(): string;
|
||||
|
||||
|
||||
/**
|
||||
* Get creation timestamp for this form flash.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCreatedTimestamp(): int;
|
||||
|
||||
/**
|
||||
* Get last updated timestamp for this form flash.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getUpdatedTimestamp(): int;
|
||||
|
||||
/**
|
||||
* Get raw form data.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getData(): ?array;
|
||||
|
||||
/**
|
||||
* Set raw form data.
|
||||
*
|
||||
* @param array|null $data
|
||||
*/
|
||||
public function setData(?array $data): void;
|
||||
|
||||
/**
|
||||
* Check if this form flash exists.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(): bool;
|
||||
|
||||
/**
|
||||
* Save this form flash.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function save();
|
||||
|
||||
/**
|
||||
* Delete this form flash.
|
||||
*/
|
||||
public function delete();
|
||||
|
||||
/**
|
||||
* Get all files associated to a form field.
|
||||
*
|
||||
* @param string $field
|
||||
* @return array
|
||||
*/
|
||||
public function getFilesByField(string $field): array;
|
||||
|
||||
/**
|
||||
* Get all files grouped by the associated form fields.
|
||||
*
|
||||
* @param bool $includeOriginal
|
||||
* @return array
|
||||
*/
|
||||
public function getFilesByFields($includeOriginal = false): array;
|
||||
|
||||
/**
|
||||
* Add uploaded file to the form flash.
|
||||
*
|
||||
* @param UploadedFileInterface $upload
|
||||
* @param string|null $field
|
||||
* @param array|null $crop
|
||||
* @return string Return name of the file
|
||||
*/
|
||||
public function addUploadedFile(UploadedFileInterface $upload, string $field = null, array $crop = null): string;
|
||||
|
||||
/**
|
||||
* Add existing file to the form flash.
|
||||
*
|
||||
* @param string $filename
|
||||
* @param string $field
|
||||
* @param array $crop
|
||||
* @return bool
|
||||
*/
|
||||
public function addFile(string $filename, string $field, array $crop = null): bool;
|
||||
|
||||
/**
|
||||
* Remove any file from form flash.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $field
|
||||
* @return bool
|
||||
*/
|
||||
public function removeFile(string $name, string $field = null): bool;
|
||||
|
||||
/**
|
||||
* Clear form flash from all uploaded files.
|
||||
*/
|
||||
public function clearFiles();
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function jsonSerialize(): array;
|
||||
}
|
||||
@@ -15,9 +15,11 @@ use Grav\Common\Data\ValidationException;
|
||||
use Grav\Common\Form\FormFlash;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Twig\Twig;
|
||||
use Grav\Common\User\Interfaces\UserInterface;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Framework\ContentBlock\HtmlBlock;
|
||||
use Grav\Framework\Form\Interfaces\FormInterface;
|
||||
use Grav\Framework\Session\SessionInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use Twig\Error\LoaderError;
|
||||
@@ -296,12 +298,12 @@ trait FormTrait
|
||||
|
||||
public function getButtons(): array
|
||||
{
|
||||
return $this->getBlueprint()['form']['buttons'] ?? [];
|
||||
return $this->getBlueprint()->get('form/buttons') ?? [];
|
||||
}
|
||||
|
||||
public function getTasks(): array
|
||||
{
|
||||
return $this->getBlueprint()['form']['tasks'] ?? [];
|
||||
return $this->getBlueprint()->get('form/tasks') ?? [];
|
||||
}
|
||||
|
||||
abstract public function getBlueprint(): Blueprint;
|
||||
@@ -336,35 +338,56 @@ trait FormTrait
|
||||
public function getFlash(): FormFlash
|
||||
{
|
||||
if (null === $this->flash) {
|
||||
/** @var Grav $grav */
|
||||
$grav = Grav::instance();
|
||||
$id = null;
|
||||
|
||||
$user = $grav['user'] ?? null;
|
||||
if (isset($user)) {
|
||||
$rememberState = $this->getBlueprint()->get('form/remember_state');
|
||||
if ($rememberState === 'user') {
|
||||
$id = $user->username;
|
||||
}
|
||||
}
|
||||
|
||||
// Session Required for flash form
|
||||
$session = $grav['session'] ?? null;
|
||||
if (isset($session)) {
|
||||
// By default store flash by the session id.
|
||||
if (null === $id) {
|
||||
$id = $session->getId();
|
||||
}
|
||||
$config = [
|
||||
'session_id' => $this->getSessionId(),
|
||||
'unique_id' => $this->getUniqueId(),
|
||||
'form_name' => $this->getName(),
|
||||
'folder' => $this->getFlashFolder()
|
||||
];
|
||||
|
||||
|
||||
$this->flash = new FormFlash($id, $this->getUniqueId(), $this->getName());
|
||||
$this->flash->setUrl($grav['uri']->url)->setUser($user);
|
||||
}
|
||||
$this->flash = new FormFlash($config);
|
||||
$this->flash->setUrl($grav['uri']->url)->setUser($grav['user'] ?? null);
|
||||
}
|
||||
|
||||
return $this->flash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available form flash objects for this form.
|
||||
*
|
||||
* @return FormFlash[]
|
||||
*/
|
||||
public function getAllFlashes(): array
|
||||
{
|
||||
$folder = $this->getFlashFolder();
|
||||
if (!$folder || !is_dir($folder)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$name = $this->getName();
|
||||
|
||||
$list = [];
|
||||
/** @var \SplFileInfo $file */
|
||||
foreach (new \FilesystemIterator($folder) as $file) {
|
||||
$uniqueId = $file->getFilename();
|
||||
$config = [
|
||||
'session_id' => $this->getSessionId(),
|
||||
'unique_id' => $uniqueId,
|
||||
'form_name' => $name,
|
||||
'folder' => $this->getFlashFolder()
|
||||
];
|
||||
$flash = new FormFlash($config);
|
||||
if ($flash->exists() && $flash->getFormName() === $name) {
|
||||
$list[] = $flash;
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @see FormInterface::render()
|
||||
@@ -389,11 +412,51 @@ trait FormTrait
|
||||
return $block;
|
||||
}
|
||||
|
||||
protected function getSessionId(): string
|
||||
{
|
||||
/** @var Grav $grav */
|
||||
$grav = Grav::instance();
|
||||
|
||||
/** @var SessionInterface $session */
|
||||
$session = $grav['session'] ?? null;
|
||||
|
||||
return $session ? ($session->getId() ?? '') : '';
|
||||
}
|
||||
|
||||
protected function unsetFlash(): void
|
||||
{
|
||||
$this->flash = null;
|
||||
}
|
||||
|
||||
protected function getFlashFolder(): ?string
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
|
||||
/** @var UserInterface $user */
|
||||
$user = $grav['user'] ?? null;
|
||||
$userExists = $user && $user->exists();
|
||||
$username = $userExists ? $user->username : null;
|
||||
$mediaFolder = $userExists ? $user->getMediaFolder() : null;
|
||||
$session = $grav['session'] ?? null;
|
||||
$sessionId = $session ? $session->getId() : null;
|
||||
|
||||
// Fill template token keys/value pairs.
|
||||
$dataMap = [
|
||||
'[FORM_NAME]' => $this->getName(),
|
||||
'[SESSIONID]' => $sessionId ?? '!!',
|
||||
'[USERNAME]' => $username ?? '!!',
|
||||
'[USERNAME_OR_SESSIONID]' => $username ?? $sessionId ?? '!!',
|
||||
'[ACCOUNT]' => $mediaFolder ?? '!!'
|
||||
];
|
||||
|
||||
$flashFolder = $this->getBlueprint()->get('form/flash_folder', 'tmp://forms/[SESSIONID]');
|
||||
|
||||
$path = str_replace(array_keys($dataMap), array_values($dataMap), $flashFolder);
|
||||
|
||||
// Make sure we only return valid paths.
|
||||
return strpos($path, '!!') === false ? rtrim($path, '/') : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a single error.
|
||||
*
|
||||
@@ -577,7 +640,7 @@ trait FormTrait
|
||||
foreach ($data as $key => &$value) {
|
||||
if (\is_array($value)) {
|
||||
$value = $this->jsonDecode($value);
|
||||
} elseif ($value === '') {
|
||||
} elseif (trim($value) === '') {
|
||||
unset($data[$key]);
|
||||
} else {
|
||||
$value = json_decode($value, true);
|
||||
|
||||
1554
system/src/Grav/Framework/Parsedown/Parsedown.php
Normal file
1554
system/src/Grav/Framework/Parsedown/Parsedown.php
Normal file
File diff suppressed because it is too large
Load Diff
532
system/src/Grav/Framework/Parsedown/ParsedownExtra.php
Normal file
532
system/src/Grav/Framework/Parsedown/ParsedownExtra.php
Normal file
@@ -0,0 +1,532 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Grav\Framework\Parsedown
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Framework\Parsedown;
|
||||
|
||||
/*
|
||||
* Parsedown Extra
|
||||
* http://parsedown.org
|
||||
*
|
||||
* (c) Emanuil Rusev
|
||||
* http://erusev.com
|
||||
*
|
||||
* This file ported from officiall ParsedownExtra repo and kept for compatibility.
|
||||
*/
|
||||
|
||||
class ParsedownExtra extends Parsedown
|
||||
{
|
||||
# ~
|
||||
|
||||
const version = '0.7.0';
|
||||
|
||||
# ~
|
||||
|
||||
function __construct()
|
||||
{
|
||||
if (parent::version < '1.5.0')
|
||||
{
|
||||
throw new Exception('ParsedownExtra requires a later version of Parsedown');
|
||||
}
|
||||
|
||||
$this->BlockTypes[':'] []= 'DefinitionList';
|
||||
$this->BlockTypes['*'] []= 'Abbreviation';
|
||||
|
||||
# identify footnote definitions before reference definitions
|
||||
array_unshift($this->BlockTypes['['], 'Footnote');
|
||||
|
||||
# identify footnote markers before before links
|
||||
array_unshift($this->InlineTypes['['], 'FootnoteMarker');
|
||||
}
|
||||
|
||||
#
|
||||
# ~
|
||||
|
||||
function text($text)
|
||||
{
|
||||
$markup = parent::text($text);
|
||||
|
||||
# merge consecutive dl elements
|
||||
|
||||
$markup = preg_replace('/<\/dl>\s+<dl>\s+/', '', $markup);
|
||||
|
||||
# add footnotes
|
||||
|
||||
if (isset($this->DefinitionData['Footnote']))
|
||||
{
|
||||
$Element = $this->buildFootnoteElement();
|
||||
|
||||
$markup .= "\n" . $this->element($Element);
|
||||
}
|
||||
|
||||
return $markup;
|
||||
}
|
||||
|
||||
#
|
||||
# Blocks
|
||||
#
|
||||
|
||||
#
|
||||
# Abbreviation
|
||||
|
||||
protected function blockAbbreviation($Line)
|
||||
{
|
||||
if (preg_match('/^\*\[(.+?)\]:[ ]*(.+?)[ ]*$/', $Line['text'], $matches))
|
||||
{
|
||||
$this->DefinitionData['Abbreviation'][$matches[1]] = $matches[2];
|
||||
|
||||
$Block = array(
|
||||
'hidden' => true,
|
||||
);
|
||||
|
||||
return $Block;
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Footnote
|
||||
|
||||
protected function blockFootnote($Line)
|
||||
{
|
||||
if (preg_match('/^\[\^(.+?)\]:[ ]?(.*)$/', $Line['text'], $matches))
|
||||
{
|
||||
$Block = array(
|
||||
'label' => $matches[1],
|
||||
'text' => $matches[2],
|
||||
'hidden' => true,
|
||||
);
|
||||
|
||||
return $Block;
|
||||
}
|
||||
}
|
||||
|
||||
protected function blockFootnoteContinue($Line, $Block)
|
||||
{
|
||||
if ($Line['text'][0] === '[' and preg_match('/^\[\^(.+?)\]:/', $Line['text']))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($Block['interrupted']))
|
||||
{
|
||||
if ($Line['indent'] >= 4)
|
||||
{
|
||||
$Block['text'] .= "\n\n" . $Line['text'];
|
||||
|
||||
return $Block;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$Block['text'] .= "\n" . $Line['text'];
|
||||
|
||||
return $Block;
|
||||
}
|
||||
}
|
||||
|
||||
protected function blockFootnoteComplete($Block)
|
||||
{
|
||||
$this->DefinitionData['Footnote'][$Block['label']] = array(
|
||||
'text' => $Block['text'],
|
||||
'count' => null,
|
||||
'number' => null,
|
||||
);
|
||||
|
||||
return $Block;
|
||||
}
|
||||
|
||||
#
|
||||
# Definition List
|
||||
|
||||
protected function blockDefinitionList($Line, $Block)
|
||||
{
|
||||
if ( ! isset($Block) or isset($Block['type']))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$Element = array(
|
||||
'name' => 'dl',
|
||||
'handler' => 'elements',
|
||||
'text' => array(),
|
||||
);
|
||||
|
||||
$terms = explode("\n", $Block['element']['text']);
|
||||
|
||||
foreach ($terms as $term)
|
||||
{
|
||||
$Element['text'] []= array(
|
||||
'name' => 'dt',
|
||||
'handler' => 'line',
|
||||
'text' => $term,
|
||||
);
|
||||
}
|
||||
|
||||
$Block['element'] = $Element;
|
||||
|
||||
$Block = $this->addDdElement($Line, $Block);
|
||||
|
||||
return $Block;
|
||||
}
|
||||
|
||||
protected function blockDefinitionListContinue($Line, array $Block)
|
||||
{
|
||||
if ($Line['text'][0] === ':')
|
||||
{
|
||||
$Block = $this->addDdElement($Line, $Block);
|
||||
|
||||
return $Block;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isset($Block['interrupted']) and $Line['indent'] === 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($Block['interrupted']))
|
||||
{
|
||||
$Block['dd']['handler'] = 'text';
|
||||
$Block['dd']['text'] .= "\n\n";
|
||||
|
||||
unset($Block['interrupted']);
|
||||
}
|
||||
|
||||
$text = substr($Line['body'], min($Line['indent'], 4));
|
||||
|
||||
$Block['dd']['text'] .= "\n" . $text;
|
||||
|
||||
return $Block;
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Header
|
||||
|
||||
protected function blockHeader($Line)
|
||||
{
|
||||
$Block = parent::blockHeader($Line);
|
||||
|
||||
if ($Block !== null && preg_match('/[ #]*{('.$this->regexAttribute.'+)}[ ]*$/', $Block['element']['text'], $matches, PREG_OFFSET_CAPTURE))
|
||||
{
|
||||
$attributeString = $matches[1][0];
|
||||
|
||||
$Block['element']['attributes'] = $this->parseAttributeData($attributeString);
|
||||
|
||||
$Block['element']['text'] = substr($Block['element']['text'], 0, $matches[0][1]);
|
||||
}
|
||||
|
||||
return $Block;
|
||||
}
|
||||
|
||||
#
|
||||
# Markup
|
||||
|
||||
protected function blockMarkupComplete($Block)
|
||||
{
|
||||
if ( ! isset($Block['void']))
|
||||
{
|
||||
$Block['markup'] = $this->processTag($Block['markup']);
|
||||
}
|
||||
|
||||
return $Block;
|
||||
}
|
||||
|
||||
#
|
||||
# Setext
|
||||
|
||||
protected function blockSetextHeader($Line, array $Block = null)
|
||||
{
|
||||
$Block = parent::blockSetextHeader($Line, $Block);
|
||||
|
||||
if ($Block !== null && preg_match('/[ ]*{('.$this->regexAttribute.'+)}[ ]*$/', $Block['element']['text'], $matches, PREG_OFFSET_CAPTURE))
|
||||
{
|
||||
$attributeString = $matches[1][0];
|
||||
|
||||
$Block['element']['attributes'] = $this->parseAttributeData($attributeString);
|
||||
|
||||
$Block['element']['text'] = substr($Block['element']['text'], 0, $matches[0][1]);
|
||||
}
|
||||
|
||||
return $Block;
|
||||
}
|
||||
|
||||
#
|
||||
# Inline Elements
|
||||
#
|
||||
|
||||
#
|
||||
# Footnote Marker
|
||||
|
||||
protected function inlineFootnoteMarker($Excerpt)
|
||||
{
|
||||
if (preg_match('/^\[\^(.+?)\]/', $Excerpt['text'], $matches))
|
||||
{
|
||||
$name = $matches[1];
|
||||
|
||||
if ( ! isset($this->DefinitionData['Footnote'][$name]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->DefinitionData['Footnote'][$name]['count'] ++;
|
||||
|
||||
if ( ! isset($this->DefinitionData['Footnote'][$name]['number']))
|
||||
{
|
||||
$this->DefinitionData['Footnote'][$name]['number'] = ++ $this->footnoteCount; # » &
|
||||
}
|
||||
|
||||
$Element = array(
|
||||
'name' => 'sup',
|
||||
'attributes' => array('id' => 'fnref'.$this->DefinitionData['Footnote'][$name]['count'].':'.$name),
|
||||
'handler' => 'element',
|
||||
'text' => array(
|
||||
'name' => 'a',
|
||||
'attributes' => array('href' => '#fn:'.$name, 'class' => 'footnote-ref'),
|
||||
'text' => $this->DefinitionData['Footnote'][$name]['number'],
|
||||
),
|
||||
);
|
||||
|
||||
return array(
|
||||
'extent' => strlen($matches[0]),
|
||||
'element' => $Element,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private $footnoteCount = 0;
|
||||
|
||||
#
|
||||
# Link
|
||||
|
||||
protected function inlineLink($Excerpt)
|
||||
{
|
||||
$Link = parent::inlineLink($Excerpt);
|
||||
|
||||
$remainder = $Link !== null ? substr($Excerpt['text'], $Link['extent']) : '';
|
||||
|
||||
if (preg_match('/^[ ]*{('.$this->regexAttribute.'+)}/', $remainder, $matches))
|
||||
{
|
||||
$Link['element']['attributes'] += $this->parseAttributeData($matches[1]);
|
||||
|
||||
$Link['extent'] += strlen($matches[0]);
|
||||
}
|
||||
|
||||
return $Link;
|
||||
}
|
||||
|
||||
#
|
||||
# ~
|
||||
#
|
||||
|
||||
protected function unmarkedText($text)
|
||||
{
|
||||
$text = parent::unmarkedText($text);
|
||||
|
||||
if (isset($this->DefinitionData['Abbreviation']))
|
||||
{
|
||||
foreach ($this->DefinitionData['Abbreviation'] as $abbreviation => $meaning)
|
||||
{
|
||||
$pattern = '/\b'.preg_quote($abbreviation, '/').'\b/';
|
||||
|
||||
$text = preg_replace($pattern, '<abbr title="'.$meaning.'">'.$abbreviation.'</abbr>', $text);
|
||||
}
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
#
|
||||
# Util Methods
|
||||
#
|
||||
|
||||
protected function addDdElement(array $Line, array $Block)
|
||||
{
|
||||
$text = substr($Line['text'], 1);
|
||||
$text = trim($text);
|
||||
|
||||
unset($Block['dd']);
|
||||
|
||||
$Block['dd'] = array(
|
||||
'name' => 'dd',
|
||||
'handler' => 'line',
|
||||
'text' => $text,
|
||||
);
|
||||
|
||||
if (isset($Block['interrupted']))
|
||||
{
|
||||
$Block['dd']['handler'] = 'text';
|
||||
|
||||
unset($Block['interrupted']);
|
||||
}
|
||||
|
||||
$Block['element']['text'] []= & $Block['dd'];
|
||||
|
||||
return $Block;
|
||||
}
|
||||
|
||||
protected function buildFootnoteElement()
|
||||
{
|
||||
$Element = array(
|
||||
'name' => 'div',
|
||||
'attributes' => array('class' => 'footnotes'),
|
||||
'handler' => 'elements',
|
||||
'text' => array(
|
||||
array(
|
||||
'name' => 'hr',
|
||||
),
|
||||
array(
|
||||
'name' => 'ol',
|
||||
'handler' => 'elements',
|
||||
'text' => array(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
uasort($this->DefinitionData['Footnote'], 'self::sortFootnotes');
|
||||
|
||||
foreach ($this->DefinitionData['Footnote'] as $definitionId => $DefinitionData)
|
||||
{
|
||||
if ( ! isset($DefinitionData['number']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$text = $DefinitionData['text'];
|
||||
|
||||
$text = parent::text($text);
|
||||
|
||||
$numbers = range(1, $DefinitionData['count']);
|
||||
|
||||
$backLinksMarkup = '';
|
||||
|
||||
foreach ($numbers as $number)
|
||||
{
|
||||
$backLinksMarkup .= ' <a href="#fnref'.$number.':'.$definitionId.'" rev="footnote" class="footnote-backref">↩</a>';
|
||||
}
|
||||
|
||||
$backLinksMarkup = substr($backLinksMarkup, 1);
|
||||
|
||||
if (substr($text, - 4) === '</p>')
|
||||
{
|
||||
$backLinksMarkup = ' '.$backLinksMarkup;
|
||||
|
||||
$text = substr_replace($text, $backLinksMarkup.'</p>', - 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
$text .= "\n".'<p>'.$backLinksMarkup.'</p>';
|
||||
}
|
||||
|
||||
$Element['text'][1]['text'] []= array(
|
||||
'name' => 'li',
|
||||
'attributes' => array('id' => 'fn:'.$definitionId),
|
||||
'text' => "\n".$text."\n",
|
||||
);
|
||||
}
|
||||
|
||||
return $Element;
|
||||
}
|
||||
|
||||
# ~
|
||||
|
||||
protected function parseAttributeData($attributeString)
|
||||
{
|
||||
$Data = array();
|
||||
|
||||
$attributes = preg_split('/[ ]+/', $attributeString, - 1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
foreach ($attributes as $attribute)
|
||||
{
|
||||
if ($attribute[0] === '#')
|
||||
{
|
||||
$Data['id'] = substr($attribute, 1);
|
||||
}
|
||||
else # "."
|
||||
{
|
||||
$classes []= substr($attribute, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($classes))
|
||||
{
|
||||
$Data['class'] = implode(' ', $classes);
|
||||
}
|
||||
|
||||
return $Data;
|
||||
}
|
||||
|
||||
# ~
|
||||
|
||||
protected function processTag($elementMarkup) # recursive
|
||||
{
|
||||
# http://stackoverflow.com/q/1148928/200145
|
||||
libxml_use_internal_errors(true);
|
||||
|
||||
$DOMDocument = new \DOMDocument;
|
||||
|
||||
# http://stackoverflow.com/q/11309194/200145
|
||||
$elementMarkup = mb_convert_encoding($elementMarkup, 'HTML-ENTITIES', 'UTF-8');
|
||||
|
||||
# http://stackoverflow.com/q/4879946/200145
|
||||
$DOMDocument->loadHTML($elementMarkup);
|
||||
$DOMDocument->removeChild($DOMDocument->doctype);
|
||||
$DOMDocument->replaceChild($DOMDocument->firstChild->firstChild->firstChild, $DOMDocument->firstChild);
|
||||
|
||||
$elementText = '';
|
||||
|
||||
if ($DOMDocument->documentElement->getAttribute('markdown') === '1')
|
||||
{
|
||||
foreach ($DOMDocument->documentElement->childNodes as $Node)
|
||||
{
|
||||
$elementText .= $DOMDocument->saveHTML($Node);
|
||||
}
|
||||
|
||||
$DOMDocument->documentElement->removeAttribute('markdown');
|
||||
|
||||
$elementText = "\n".$this->text($elementText)."\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ($DOMDocument->documentElement->childNodes as $Node)
|
||||
{
|
||||
$nodeMarkup = $DOMDocument->saveHTML($Node);
|
||||
|
||||
if ($Node instanceof \DOMElement and ! in_array($Node->nodeName, $this->textLevelElements))
|
||||
{
|
||||
$elementText .= $this->processTag($nodeMarkup);
|
||||
}
|
||||
else
|
||||
{
|
||||
$elementText .= $nodeMarkup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# because we don't want for markup to get encoded
|
||||
$DOMDocument->documentElement->nodeValue = 'placeholder\x1A';
|
||||
|
||||
$markup = $DOMDocument->saveHTML($DOMDocument->documentElement);
|
||||
$markup = str_replace('placeholder\x1A', $elementText, $markup);
|
||||
|
||||
return $markup;
|
||||
}
|
||||
|
||||
# ~
|
||||
|
||||
protected function sortFootnotes($A, $B) # callback
|
||||
{
|
||||
return $A['number'] - $B['number'];
|
||||
}
|
||||
|
||||
#
|
||||
# Fields
|
||||
#
|
||||
|
||||
protected $regexAttribute = '(?:[#.][-\w]+[ ]*)';
|
||||
}
|
||||
@@ -154,16 +154,11 @@ class Route
|
||||
* If the parameter exists in both, return Grav parameter.
|
||||
*
|
||||
* @param string $param
|
||||
* @return string|null
|
||||
* @return string|array|null
|
||||
*/
|
||||
public function getParam($param)
|
||||
{
|
||||
$value = $this->getGravParam($param);
|
||||
if ($value === null) {
|
||||
$value = $this->getQueryParam($param);
|
||||
}
|
||||
|
||||
return $value;
|
||||
return $this->getGravParam($param) ?? $this->getQueryParam($param);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,7 +172,7 @@ class Route
|
||||
|
||||
/**
|
||||
* @param string $param
|
||||
* @return string|null
|
||||
* @return string|array|null
|
||||
*/
|
||||
public function getQueryParam($param)
|
||||
{
|
||||
@@ -313,7 +308,8 @@ class Route
|
||||
*/
|
||||
protected function withParam($type, $param, $value)
|
||||
{
|
||||
$oldValue = $this->{$type}[$param] ?? null;
|
||||
$values = $this->{$type} ?? [];
|
||||
$oldValue = $values[$param] ?? null;
|
||||
|
||||
if ($oldValue === $value) {
|
||||
return $this;
|
||||
@@ -321,11 +317,13 @@ class Route
|
||||
|
||||
$new = $this->copy();
|
||||
if ($value === null) {
|
||||
unset($new->{$type}[$param]);
|
||||
unset($values[$param]);
|
||||
} else {
|
||||
$new->{$type}[$param] = $value;
|
||||
$values[$param] = $value;
|
||||
}
|
||||
|
||||
$new->{$type} = $values;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
@@ -376,8 +374,8 @@ class Route
|
||||
$this->language = $gravParts['language'];
|
||||
$this->route = $gravParts['route'];
|
||||
$this->extension = $gravParts['extension'] ?? '';
|
||||
$this->gravParams = $gravParts['params'];
|
||||
$this->queryParams = $parts['query_params'];
|
||||
$this->gravParams = $gravParts['params'] ?: [];
|
||||
$this->queryParams = $parts['query_params'] ?: [];
|
||||
|
||||
} else {
|
||||
$this->root = RouteFactory::getRoot();
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace Grav\Framework\Session;
|
||||
|
||||
use Grav\Common\User\Interfaces\UserInterface;
|
||||
use Grav\Framework\Session\Exceptions\SessionException;
|
||||
|
||||
/**
|
||||
@@ -17,16 +18,13 @@ use Grav\Framework\Session\Exceptions\SessionException;
|
||||
*/
|
||||
class Session implements SessionInterface
|
||||
{
|
||||
protected $options;
|
||||
/** @var array */
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
/** @var bool */
|
||||
protected $started = false;
|
||||
|
||||
/**
|
||||
* @var Session
|
||||
*/
|
||||
/** @var Session */
|
||||
protected static $instance;
|
||||
|
||||
/**
|
||||
@@ -178,9 +176,13 @@ class Session implements SessionInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
$sessionName = session_name();
|
||||
$sessionExists = isset($_COOKIE[$sessionName]);
|
||||
|
||||
// Protection against invalid session cookie names throwing exception: http://php.net/manual/en/function.session-id.php#116836
|
||||
if (isset($_COOKIE[session_name()]) && !preg_match('/^[-,a-zA-Z0-9]{1,128}$/', $_COOKIE[session_name()])) {
|
||||
unset($_COOKIE[session_name()]);
|
||||
if ($sessionExists && !preg_match('/^[-,a-zA-Z0-9]{1,128}$/', $_COOKIE[$sessionName])) {
|
||||
unset($_COOKIE[$sessionName]);
|
||||
$sessionExists = false;
|
||||
}
|
||||
|
||||
$options = $this->options;
|
||||
@@ -197,24 +199,28 @@ class Session implements SessionInterface
|
||||
throw new SessionException('Failed to start session: ' . $error, 500);
|
||||
}
|
||||
|
||||
if ($user && !$user->isValid()) {
|
||||
$this->clear();
|
||||
throw new SessionException('User Invalid', 500);
|
||||
$this->started = true;
|
||||
|
||||
if ($user && (!$user instanceof UserInterface || !$user->isValid())) {
|
||||
$this->invalidate();
|
||||
|
||||
throw new SessionException('Invalid User object, session destroyed.', 500);
|
||||
}
|
||||
|
||||
$params = session_get_cookie_params();
|
||||
// Extend the lifetime of the session.
|
||||
if ($sessionExists) {
|
||||
$params = session_get_cookie_params();
|
||||
|
||||
setcookie(
|
||||
session_name(),
|
||||
session_id(),
|
||||
time() + $params['lifetime'],
|
||||
$params['path'],
|
||||
$params['domain'],
|
||||
$params['secure'],
|
||||
$params['httponly']
|
||||
);
|
||||
|
||||
$this->started = true;
|
||||
setcookie(
|
||||
$sessionName,
|
||||
session_id(),
|
||||
time() + $params['lifetime'],
|
||||
$params['path'],
|
||||
$params['domain'],
|
||||
$params['secure'],
|
||||
$params['httponly']
|
||||
);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -39,3 +39,11 @@ foreach($iterator as $directory) {
|
||||
require $autoloader;
|
||||
}
|
||||
}
|
||||
|
||||
define('GANTRY_DEBUGGER', true);
|
||||
define('GANTRY5_DEBUG', true);
|
||||
define('GANTRY5_PLATFORM', 'grav');
|
||||
define('GANTRY5_ROOT', rtrim(ROOT_DIR, '/'));
|
||||
define('GANTRY5_VERSION', '@version@');
|
||||
define('GANTRY5_VERSION_DATE', '@versiondate@');
|
||||
define('GANTRYADMIN_PATH', '');
|
||||
|
||||
@@ -7,6 +7,8 @@ parameters:
|
||||
excludes_analyse:
|
||||
- %currentWorkingDirectory%/user/plugins/*/vendor/*
|
||||
- %currentWorkingDirectory%/user/plugins/*/tests/*
|
||||
- %currentWorkingDirectory%/user/plugins/gantry5/src/platforms
|
||||
- %currentWorkingDirectory%/user/plugins/gantry5/src/classes/Gantry/Framework/Services/ErrorServiceProvider.php
|
||||
bootstrap: tests/phpstan/plugins-bootstrap.php
|
||||
reportUnmatchedIgnoredErrors: true
|
||||
universalObjectCratesClasses:
|
||||
@@ -18,3 +20,4 @@ parameters:
|
||||
- Grav\Common\GPM\Local\Package
|
||||
- Grav\Common\GPM\Remote\Package
|
||||
- Grav\Common\Session
|
||||
- Gantry\Component\Config\Config
|
||||
|
||||
@@ -110,6 +110,7 @@ class ParsedownTest extends \Codeception\TestCase\Test
|
||||
|
||||
public function testImagesSubDir()
|
||||
{
|
||||
$this->config->set('system.images.cache_all', false);
|
||||
$this->uri->initializeWithUrlAndRootPath('http://testing.dev/subdir/item2/item2-2', '/subdir')->init();
|
||||
|
||||
$this->assertRegexp('|<p><img alt="" src="\/subdir\/images\/.*-home-cache-image.jpe?g" \/><\/p>|',
|
||||
|
||||
@@ -188,7 +188,7 @@ class UriTest extends \Codeception\TestCase\Test
|
||||
'environment' => 'localhost',
|
||||
'basename' => 'it',
|
||||
'base' => 'http://localhost:8080',
|
||||
'currentPage' => '',
|
||||
'currentPage' => 1,
|
||||
'rootUrl' => 'http://localhost:8080',
|
||||
'extension' => null,
|
||||
'addNonce' => 'http://localhost:8080/grav/it/ueper:xxx/page:/test:yyy/nonce:{{nonce}}',
|
||||
@@ -298,7 +298,7 @@ class UriTest extends \Codeception\TestCase\Test
|
||||
'environment' => 'api.getgrav.com',
|
||||
'basename' => '128',
|
||||
'base' => 'https://api.getgrav.com:4040',
|
||||
'currentPage' => 'x',
|
||||
'currentPage' => 1,
|
||||
'rootUrl' => 'https://api.getgrav.com:4040',
|
||||
'extension' => null,
|
||||
'addNonce' => 'https://username:password@api.getgrav.com:4040/v1/post/128/page:x/nonce:{{nonce}}?all=1',
|
||||
@@ -1073,7 +1073,7 @@ class UriTest extends \Codeception\TestCase\Test
|
||||
$this->runTestSet($this->tests, 'currentPage');
|
||||
|
||||
$this->uri->initializeWithURL('http://localhost:8080/a-page/page:2')->init();
|
||||
$this->assertSame('2', $this->uri->currentPage());
|
||||
$this->assertSame(2, $this->uri->currentPage());
|
||||
}
|
||||
|
||||
public function testReferrer()
|
||||
|
||||
@@ -382,30 +382,60 @@ class UtilsTest extends \Codeception\TestCase\Test
|
||||
// Fail hard
|
||||
$this->assertSame(false, Utils::url('', true));
|
||||
$this->assertSame(false, Utils::url(''));
|
||||
$this->assertSame(false, Utils::url('foo://bar/baz'));
|
||||
$this->assertSame(false, Utils::url(new stdClass()));
|
||||
$this->assertSame(false, Utils::url(['foo','bar','baz']));
|
||||
$this->assertSame(false, Utils::url('user://does/not/exist'));
|
||||
|
||||
// Fail Gracefully
|
||||
$this->assertSame('/', Utils::url('/', false, true));
|
||||
$this->assertSame('/', Utils::url('', false, true));
|
||||
$this->assertSame('foo://bar/baz', Utils::url('foo://bar/baz', false, true));
|
||||
$this->assertSame('/', Utils::url(new stdClass(), false, true));
|
||||
$this->assertSame('/', Utils::url(['foo','bar','baz'], false, true));
|
||||
$this->assertSame('/user/does/not/exist', Utils::url('user://does/not/exist', false, true));
|
||||
|
||||
// Simple paths
|
||||
$this->assertSame('/', Utils::url('/'));
|
||||
$this->assertSame('http://testing.dev/', Utils::url('/', true));
|
||||
$this->assertSame('http://testing.dev/path1', Utils::url('/path1', true));
|
||||
$this->assertSame('/path1', Utils::url('/path1'));
|
||||
$this->assertSame('/path1/path2', Utils::url('/path1/path2'));
|
||||
$this->assertSame('/random/path1/path2', Utils::url('/random/path1/path2'));
|
||||
$this->assertSame('/foobar.jpg', Utils::url('/foobar.jpg'));
|
||||
$this->assertSame('/path1/foobar.jpg', Utils::url('/path1/foobar.jpg'));
|
||||
$this->assertSame('/path1/path2/foobar.jpg', Utils::url('/path1/path2/foobar.jpg'));
|
||||
$this->assertSame('/random/path1/path2/foobar.jpg', Utils::url('/random/path1/path2/foobar.jpg'));
|
||||
|
||||
// Simple paths with domain
|
||||
$this->assertSame('http://testing.dev/', Utils::url('/', true));
|
||||
$this->assertSame('http://testing.dev/path1', Utils::url('/path1', true));
|
||||
$this->assertSame('http://testing.dev/path1/path2', Utils::url('/path1/path2', true));
|
||||
$this->assertSame('http://testing.dev/random/path1/path2', Utils::url('/random/path1/path2', true));
|
||||
$this->assertSame('http://testing.dev/foobar.jpg', Utils::url('/foobar.jpg', true));
|
||||
$this->assertSame('http://testing.dev/path1/foobar.jpg', Utils::url('/path1/foobar.jpg', true));
|
||||
$this->assertSame('http://testing.dev/path1/path2/foobar.jpg', Utils::url('/path1/path2/foobar.jpg', true));
|
||||
$this->assertSame('http://testing.dev/random/path1/path2/foobar.jpg', Utils::url('/random/path1/path2/foobar.jpg', true));
|
||||
|
||||
// Relative paths from Grav root.
|
||||
$this->assertSame('/subdir', Utils::url('subdir'));
|
||||
$this->assertSame('/subdir/path1', Utils::url('subdir/path1'));
|
||||
$this->assertSame('/subdir/path1/path2', Utils::url('subdir/path1/path2'));
|
||||
$this->assertSame('/path1', Utils::url('path1'));
|
||||
$this->assertSame('/path1/path2', Utils::url('path1/path2'));
|
||||
$this->assertSame('/foobar.jpg', Utils::url('foobar.jpg'));
|
||||
$this->assertSame('http://testing.dev/foobar.jpg', Utils::url('foobar.jpg', true));
|
||||
|
||||
// Relative paths from Grav root with domain.
|
||||
$this->assertSame('http://testing.dev/foobar.jpg', Utils::url('foobar.jpg', true));
|
||||
$this->assertSame('http://testing.dev/foobar.jpg', Utils::url('/foobar.jpg', true));
|
||||
$this->assertSame('http://testing.dev/path1/foobar.jpg', Utils::url('/path1/foobar.jpg', true));
|
||||
$this->assertSame('/foobar.jpg', Utils::url('/foobar.jpg'));
|
||||
$this->assertSame('/foobar.jpg', Utils::url('foobar.jpg'));
|
||||
$this->assertSame('/path1/foobar.jpg', Utils::url('/path1/foobar.jpg'));
|
||||
$this->assertSame('/path1/path2/foobar.jpg', Utils::url('/path1/path2/foobar.jpg'));
|
||||
|
||||
// All Non-existing streams should be treated as external URI / protocol.
|
||||
$this->assertSame('http://domain.com/path', Utils::url('http://domain.com/path'));
|
||||
$this->assertSame('ftp://domain.com/path', Utils::url('ftp://domain.com/path'));
|
||||
$this->assertSame('sftp://domain.com/path', Utils::url('sftp://domain.com/path'));
|
||||
$this->assertSame('ssh://domain.com', Utils::url('ssh://domain.com'));
|
||||
$this->assertSame('pop://domain.com', Utils::url('pop://domain.com'));
|
||||
$this->assertSame('foo://bar/baz', Utils::url('foo://bar/baz'));
|
||||
$this->assertSame('foo://bar/baz', Utils::url('foo://bar/baz', true));
|
||||
// $this->assertSame('mailto:joe@domain.com', Utils::url('mailto:joe@domain.com', true)); // FIXME <-
|
||||
}
|
||||
|
||||
public function testUrlWithRoot()
|
||||
@@ -415,31 +445,69 @@ class UtilsTest extends \Codeception\TestCase\Test
|
||||
// Fail hard
|
||||
$this->assertSame(false, Utils::url('', true));
|
||||
$this->assertSame(false, Utils::url(''));
|
||||
$this->assertSame(false, Utils::url('foo://bar/baz'));
|
||||
$this->assertSame(false, Utils::url(new stdClass()));
|
||||
$this->assertSame(false, Utils::url(['foo','bar','baz']));
|
||||
$this->assertSame(false, Utils::url('user://does/not/exist'));
|
||||
|
||||
// Fail Gracefully
|
||||
$this->assertSame('/subdir/', Utils::url('/', false, true));
|
||||
$this->assertSame('/subdir/', Utils::url('', false, true));
|
||||
$this->assertSame('foo://bar/baz', Utils::url('foo://bar/baz', false, true));
|
||||
$this->assertSame('/subdir/', Utils::url(new stdClass(), false, true));
|
||||
$this->assertSame('/subdir/', Utils::url(['foo','bar','baz'], false, true));
|
||||
$this->assertSame('/subdir/user/does/not/exist', Utils::url('user://does/not/exist', false, true));
|
||||
|
||||
$this->assertSame('http://testing.dev/subdir/', Utils::url('/', true));
|
||||
$this->assertSame('http://testing.dev/subdir/path1', Utils::url('/path1', true));
|
||||
$this->assertSame('http://testing.dev/subdir/path1', Utils::url('/subdir/path1', true));
|
||||
// Simple paths
|
||||
$this->assertSame('/subdir/', Utils::url('/'));
|
||||
$this->assertSame('/subdir/path1', Utils::url('/path1'));
|
||||
$this->assertSame('/subdir/path1/path2', Utils::url('/path1/path2'));
|
||||
$this->assertSame('/subdir/path1/path2', Utils::url('/subdir/path1/path2'));
|
||||
|
||||
$this->assertSame('http://testing.dev/subdir/foobar.jpg', Utils::url('foobar.jpg', true));
|
||||
$this->assertSame('http://testing.dev/subdir/foobar.jpg', Utils::url('/foobar.jpg', true));
|
||||
$this->assertSame('http://testing.dev/subdir/foobar.jpg', Utils::url('/subdir/foobar.jpg', true));
|
||||
$this->assertSame('http://testing.dev/subdir/path1/foobar.jpg', Utils::url('/path1/foobar.jpg', true));
|
||||
$this->assertSame('http://testing.dev/subdir/path1/foobar.jpg', Utils::url('/subdir/path1/foobar.jpg', true));
|
||||
$this->assertSame('/subdir/random/path1/path2', Utils::url('/random/path1/path2'));
|
||||
$this->assertSame('/subdir/foobar.jpg', Utils::url('/foobar.jpg'));
|
||||
$this->assertSame('/subdir/foobar.jpg', Utils::url('foobar.jpg'));
|
||||
$this->assertSame('/subdir/foobar.jpg', Utils::url('/subdir/foobar.jpg'));
|
||||
$this->assertSame('/subdir/path1/foobar.jpg', Utils::url('/path1/foobar.jpg'));
|
||||
$this->assertSame('/subdir/path1/path2/foobar.jpg', Utils::url('/path1/path2/foobar.jpg'));
|
||||
$this->assertSame('/subdir/random/path1/path2/foobar.jpg', Utils::url('/random/path1/path2/foobar.jpg'));
|
||||
|
||||
// Simple paths with domain
|
||||
$this->assertSame('http://testing.dev/subdir/', Utils::url('/', true));
|
||||
$this->assertSame('http://testing.dev/subdir/path1', Utils::url('/path1', true));
|
||||
$this->assertSame('http://testing.dev/subdir/path1/path2', Utils::url('/path1/path2', true));
|
||||
$this->assertSame('http://testing.dev/subdir/random/path1/path2', Utils::url('/random/path1/path2', true));
|
||||
$this->assertSame('http://testing.dev/subdir/foobar.jpg', Utils::url('/foobar.jpg', true));
|
||||
$this->assertSame('http://testing.dev/subdir/path1/foobar.jpg', Utils::url('/path1/foobar.jpg', true));
|
||||
$this->assertSame('http://testing.dev/subdir/path1/path2/foobar.jpg', Utils::url('/path1/path2/foobar.jpg', true));
|
||||
$this->assertSame('http://testing.dev/subdir/random/path1/path2/foobar.jpg', Utils::url('/random/path1/path2/foobar.jpg', true));
|
||||
|
||||
// Paths including the grav base.
|
||||
$this->assertSame('/subdir/', Utils::url('/subdir'));
|
||||
$this->assertSame('/subdir/path1', Utils::url('/subdir/path1'));
|
||||
$this->assertSame('/subdir/path1/path2', Utils::url('/subdir/path1/path2'));
|
||||
$this->assertSame('/subdir/foobar.jpg', Utils::url('/subdir/foobar.jpg'));
|
||||
$this->assertSame('/subdir/path1/foobar.jpg', Utils::url('/subdir/path1/foobar.jpg'));
|
||||
|
||||
// Relative paths from Grav root with domain.
|
||||
$this->assertSame('http://testing.dev/subdir/', Utils::url('/subdir', true));
|
||||
$this->assertSame('http://testing.dev/subdir/path1', Utils::url('/subdir/path1', true));
|
||||
$this->assertSame('http://testing.dev/subdir/path1/path2', Utils::url('/subdir/path1/path2', true));
|
||||
$this->assertSame('http://testing.dev/subdir/foobar.jpg', Utils::url('/subdir/foobar.jpg', true));
|
||||
$this->assertSame('http://testing.dev/subdir/path1/foobar.jpg', Utils::url('/subdir/path1/foobar.jpg', true));
|
||||
|
||||
// Relative paths from Grav root.
|
||||
$this->assertSame('/subdir/subdir', Utils::url('subdir'));
|
||||
$this->assertSame('/subdir/subdir/path1', Utils::url('subdir/path1'));
|
||||
$this->assertSame('/subdir/subdir/path1/path2', Utils::url('subdir/path1/path2'));
|
||||
$this->assertSame('/subdir/path1', Utils::url('path1'));
|
||||
$this->assertSame('/subdir/path1/path2', Utils::url('path1/path2'));
|
||||
$this->assertSame('/subdir/foobar.jpg', Utils::url('foobar.jpg'));
|
||||
$this->assertSame('http://testing.dev/subdir/foobar.jpg', Utils::url('foobar.jpg', true));
|
||||
|
||||
// All Non-existing streams should be treated as external URI / protocol.
|
||||
$this->assertSame('http://domain.com/path', Utils::url('http://domain.com/path'));
|
||||
$this->assertSame('ftp://domain.com/path', Utils::url('ftp://domain.com/path'));
|
||||
$this->assertSame('sftp://domain.com/path', Utils::url('sftp://domain.com/path'));
|
||||
$this->assertSame('ssh://domain.com', Utils::url('ssh://domain.com'));
|
||||
$this->assertSame('pop://domain.com', Utils::url('pop://domain.com'));
|
||||
$this->assertSame('foo://bar/baz', Utils::url('foo://bar/baz'));
|
||||
$this->assertSame('foo://bar/baz', Utils::url('foo://bar/baz', true));
|
||||
// $this->assertSame('mailto:joe@domain.com', Utils::url('mailto:joe@domain.com', true)); // FIXME <-
|
||||
}
|
||||
|
||||
public function testUrlWithStreams()
|
||||
|
||||
@@ -23,7 +23,7 @@ $HTTP["querystring"] =~ "_REQUEST(=|\[|\%[0-9A-Z])" {
|
||||
|
||||
#REROUTING TO THE INDEX PAGE
|
||||
url.rewrite-if-not-file = (
|
||||
"^/grav_path/(.*)$" => "/grav_path/index.php$1"
|
||||
"^/grav_path/(.*)$" => "/grav_path/index.php?$1"
|
||||
)
|
||||
|
||||
#IMPROVING SECURITY
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# You can override ddev's configuration by placing an edited copy
|
||||
# of this config (or one of the other ones) in .ddev/nginx-site.conf
|
||||
# See https://ddev.readthedocs.io/en/latest/users/extend/customization-extendibility/#providing-custom-nginx-configuration
|
||||
# See https://ddev.readthedocs.io/en/stable/users/extend/customization-extendibility/#providing-custom-nginx-configuration
|
||||
|
||||
# Set https to 'on' if x-forwarded-proto is https
|
||||
map $http_x_forwarded_proto $fcgi_https {
|
||||
@@ -11,11 +11,16 @@ map $http_x_forwarded_proto $fcgi_https {
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80; ## listen for ipv4; this line is default and implied
|
||||
listen [::]:80 default ipv6only=on; ## listen for ipv6
|
||||
# The NGINX_DOCROOT variable is substituted with
|
||||
listen 80;
|
||||
listen [::]:80 default ipv6only=on;
|
||||
|
||||
# The WEBSERVER_DOCROOT variable is substituted with
|
||||
# its value when the container is started.
|
||||
root $NGINX_DOCROOT;
|
||||
root $WEBSERVER_DOCROOT;
|
||||
|
||||
include /etc/nginx/monitoring.conf;
|
||||
|
||||
|
||||
index index.php index.htm index.html;
|
||||
|
||||
# Make site accessible from http://localhost/
|
||||
@@ -23,15 +28,20 @@ server {
|
||||
|
||||
# Disable sendfile as per https://docs.vagrantup.com/v2/synced-folders/virtualbox.html
|
||||
sendfile off;
|
||||
error_log /var/log/nginx/error.log info;
|
||||
error_log /dev/stdout info;
|
||||
access_log /var/log/nginx/access.log;
|
||||
|
||||
## Begin - Index
|
||||
# for subfolders, simply adjust:
|
||||
# `location /subfolder {`
|
||||
# and the rewrite to use `/subfolder/index.php`
|
||||
location / {
|
||||
absolute_redirect off;
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
## End - Index
|
||||
|
||||
# pass the PHP scripts to FastCGI server listening on socket
|
||||
|
||||
# pass the PHP scripts to FastCGI server listening on socket
|
||||
location ~ \.php$ {
|
||||
try_files $uri =404;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
@@ -42,38 +52,78 @@ server {
|
||||
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_intercept_errors on;
|
||||
fastcgi_intercept_errors off;
|
||||
# fastcgi_read_timeout should match max_execution_time in php.ini
|
||||
fastcgi_read_timeout 10m;
|
||||
fastcgi_param SERVER_NAME $host;
|
||||
fastcgi_param HTTPS $fcgi_https;
|
||||
}
|
||||
|
||||
# Expire rules for static content
|
||||
# Feed
|
||||
location ~* \.(?:rss|atom|cache)$ {
|
||||
expires 1h;
|
||||
}
|
||||
## Begin - Security
|
||||
# deny all direct access for these folders
|
||||
location ~* /(\.git|cache|bin|logs|backup|tests)/.*$ { return 403; }
|
||||
# deny running scripts inside core system folders
|
||||
location ~* /(system|vendor)/.*\.(txt|xml|md|html|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; }
|
||||
# deny running scripts inside user folder
|
||||
location ~* /user/.*\.(txt|md|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; }
|
||||
# deny access to specific files in the root folder
|
||||
location ~ /(LICENSE\.txt|composer\.lock|composer\.json|nginx\.conf|web\.config|htaccess\.txt|\.htaccess) { return 403; }
|
||||
## End - Security
|
||||
|
||||
# Media: images, icons, video, audio, HTC
|
||||
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
|
||||
expires 1M;
|
||||
access_log off;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
include /mnt/ddev_config/nginx/*.conf;
|
||||
}
|
||||
|
||||
# Prevent clients from accessing hidden files (starting with a dot)
|
||||
# This is particularly important if you store .htpasswd files in the site hierarchy
|
||||
# Access to `/.well-known/` is allowed.
|
||||
# https://www.mnot.net/blog/2010/04/07/well-known
|
||||
# https://tools.ietf.org/html/rfc5785
|
||||
location ~* /\.(?!well-known\/) {
|
||||
deny all;
|
||||
}
|
||||
|
||||
# Prevent clients from accessing to backup/config/source files
|
||||
location ~* (?:\.(?:bak|conf|dist|fla|in[ci]|log|psd|sh|sql|sw[op])|~)$ {
|
||||
deny all;
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 default ipv6only=on;
|
||||
|
||||
# The WEBSERVER_DOCROOT variable is substituted with
|
||||
# its value when the container is started.
|
||||
root $WEBSERVER_DOCROOT;
|
||||
|
||||
ssl_certificate /etc/ssl/certs/master.crt;
|
||||
ssl_certificate_key /etc/ssl/certs/master.key;
|
||||
|
||||
include /etc/nginx/monitoring.conf;
|
||||
|
||||
|
||||
index index.php index.htm index.html;
|
||||
|
||||
# Make site accessible from http://localhost/
|
||||
server_name _;
|
||||
|
||||
# Disable sendfile as per https://docs.vagrantup.com/v2/synced-folders/virtualbox.html
|
||||
sendfile off;
|
||||
error_log /dev/stdout info;
|
||||
access_log /var/log/nginx/access.log;
|
||||
|
||||
## Begin - Index
|
||||
# for subfolders, simply adjust:
|
||||
# `location /subfolder {`
|
||||
# and the rewrite to use `/subfolder/index.php`
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
## End - Index
|
||||
|
||||
|
||||
# pass the PHP scripts to FastCGI server listening on socket
|
||||
location ~ \.php$ {
|
||||
try_files $uri =404;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm.sock;
|
||||
fastcgi_buffers 16 16k;
|
||||
fastcgi_buffer_size 32k;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_intercept_errors off;
|
||||
# fastcgi_read_timeout should match max_execution_time in php.ini
|
||||
fastcgi_read_timeout 10m;
|
||||
fastcgi_param SERVER_NAME $host;
|
||||
fastcgi_param HTTPS $fcgi_https;
|
||||
}
|
||||
|
||||
## Begin - Security
|
||||
@@ -88,31 +138,5 @@ server {
|
||||
## End - Security
|
||||
|
||||
|
||||
## provide a health check endpoint
|
||||
location /healthcheck {
|
||||
access_log off;
|
||||
stub_status on;
|
||||
keepalive_timeout 0; # Disable HTTP keepalive
|
||||
return 200;
|
||||
}
|
||||
|
||||
error_page 400 401 /40x.html;
|
||||
location = /40x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
location ~ ^/(fpmstatus|ping)$ {
|
||||
access_log off;
|
||||
stub_status on;
|
||||
keepalive_timeout 0; # Disable HTTP keepalive
|
||||
allow 127.0.0.1;
|
||||
allow all;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
fastcgi_pass unix:/run/php-fpm.sock;
|
||||
}
|
||||
|
||||
|
||||
include /mnt/ddev_config/nginx/*.conf;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user