Compare commits

...

104 Commits
1.6.1 ... 1.6.9

Author SHA1 Message Date
Andy Miller
1df6b76e25 Merge branch 'release/1.6.9' 2019-05-09 10:49:10 -06:00
Andy Miller
2153e20bc5 prepare for release 2019-05-09 10:49:01 -06:00
Andy Miller
0cbc98ab53 Merge branch 'develop' of github.com:getgrav/grav into develop 2019-05-08 20:57:11 -06:00
Andy Miller
0915a0413d Added composer scripts api-16 and api-15 2019-05-08 20:57:06 -06:00
Andy Miller
f4d3d302f4 thowing api error 2019-05-08 20:54:42 -06:00
Riley Sommerville
5d5e2264c0 Update default.md (#2496)
Correct an error that ignored the "Typography" menu item in the default Grav installation and caused a discrepancy between the install page and the corresponding tutorial on getgrav/grav-learn. Related to PR https://github.com/getgrav/grav-learn/pull/681#issue-270418391 and solves issue https://github.com/getgrav/grav-learn/issues/650#issue-365078790.
2019-05-08 17:39:09 -06:00
Andy Miller
b39fc72bd2 Update languages 2019-05-08 14:22:47 -06:00
Matias Griese
d6d50c4b66 Fixed empty $grav['request']->getAttribute('route')->getExtension() 2019-05-07 19:07:09 +03:00
Matias Griese
791ef8ad88 Changelog update 2019-05-07 17:58:10 +03:00
Matias Griese
e0861e5505 Merge branch 'develop' of github.com:getgrav/grav into develop 2019-05-07 17:55:59 +03:00
Newb I the Newbd
48c9176d90 Make yaml_decode only return array to avoid errors (#2494) 2019-05-07 17:53:42 +03:00
Matias Griese
33cb20561e Fixed backwards compatibility to select field with selectize.create set to true [git-sync#141] 2019-05-07 14:05:55 +03:00
Andy Miller
0acb38f586 Checkbox getting interpreted as string 2019-05-06 16:22:31 -06:00
Andy Miller
1a41e00a4f Merge branch 'develop' of github.com:getgrav/grav into develop 2019-05-03 10:43:17 -06:00
Andy Miller
48ef93e495 more tidy 2019-05-03 10:43:13 -06:00
Matias Griese
3f7da86711 Missed lock file 2019-05-03 08:54:51 +03:00
Matias Griese
11aa2314d5 Fixed typo in composer.json 2019-05-03 08:52:53 +03:00
Andy Miller
179c5065ca minor rounding stuff 2019-05-02 16:06:08 -06:00
Andy Miller
7c60f73942 vendor libs 2019-05-02 15:05:01 -06:00
Andy Miller
7095c665b7 Output correct "Last Updated" in bin/gpm info command 2019-05-02 15:03:01 -06:00
Matias Griese
c6d94885e0 Fixed exception in Flex::getDirectories() if the first parameter is set 2019-05-02 16:37:35 +03:00
Matias Griese
d9ddab8239 Merge remote-tracking branch 'origin/develop' into develop 2019-05-02 14:11:35 +03:00
Matias Griese
a118d45177 Flex collection and object now fall back to the default template if template file doesn't exist 2019-05-02 14:11:22 +03:00
Matias Griese
416c400367 Made UserCollectionInderface to extend Countable to get the count of existing users 2019-05-02 14:10:09 +03:00
Raphaël Droz
e6839530d8 When using Grav behind an HTTPS-endpoint, we would probably have either one of (#2486)
* `HTTPS=on`
* `X-Forwarded-Proto https`
Still, the latest virtualhost would still provide `REQUEST_SCHEME` which,
contrary to HTTPS environment, would not be overriden (eg: Apache only
creates `REDIRECT_REQUEST_SCHEME` with the requested value).

This change gives priority to non-empty `HTTPS` over `REQUEST_SCHEME`.
2019-04-30 12:18:20 -06:00
Matias Griese
fea9e53be3 Flex admin: added default search options for flex objects 2019-04-30 10:49:59 +03:00
Matias Griese
c5b3792a60 Change cache touch parameter to invalidate, added CLI option for it 2019-04-30 10:48:35 +03:00
Andy Miller
4f83b5da5b non-standard lang code length fixes 2019-04-29 17:31:56 -06:00
Andy Miller
504c8faf4c Merge branch 'develop' of github.com:getgrav/grav into develop 2019-04-26 13:11:32 -06:00
Andy Miller
4d6db5b334 Fix for avatar_url provided by 3rd party providers 2019-04-26 13:11:28 -06:00
Matias Griese
ec1fc1f1e3 Change cache touch parameter to invalidate, added CLI option for it 2019-04-26 09:54:14 +03:00
Matias Griese
563cf8900c Merge remote-tracking branch 'origin/develop' into develop 2019-04-25 21:08:16 +03:00
Matias Griese
0850c2f362 Added Cache::clearCache('touch') parameter for just forcing simple cache clear 2019-04-25 21:08:07 +03:00
Andy Miller
f30334d80f Fixed Assets options array mixed with standalone priority #2477 2019-04-25 11:53:11 -06:00
Matias Griese
9057a804a2 Added Pages::setCheckMethod() method to override page configuration in Admin Plugin 2019-04-25 16:07:25 +03:00
Matias Griese
4c5c26033a Added support for configurable actions in Flex 2019-04-24 17:14:44 +03:00
Matias Griese
a4f679adcf Added Route::withoutParams() methods 2019-04-24 14:48:37 +03:00
Matias Griese
e9e12392ac Fixed $grav['route'] from being modified when the route instance gets modified 2019-04-24 14:03:55 +03:00
Andy Miller
6b4663c2ff Merge branch 'release/1.6.8' 2019-04-23 15:02:10 -06:00
Andy Miller
0411c3a98b Merge tag '1.6.8' into develop
Release v1.6.8
2019-04-23 15:02:10 -06:00
Andy Miller
972a758ac9 prepaer for release 2019-04-23 15:01:59 -06:00
Andy Miller
05d72306c6 updated changelog 2019-04-23 14:58:42 -06:00
Andy Miller
18b7c0955d updated clean command 2019-04-23 14:46:51 -06:00
Andy Miller
71cbbf4e1e remove hipchat 2019-04-23 14:46:35 -06:00
Matias Griese
0606e12872 Added FlexCollection::filterBy() method 2019-04-23 13:24:45 +03:00
Matias Griese
afc7cac5ab Improve null result handling in Flex 2019-04-23 11:13:29 +03:00
Matias Griese
471e3d8954 Revert "Use Null Coalesce Operator (#2466)"
This reverts commit be8eb639
2019-04-23 10:54:26 +03:00
Andy Miller
89a92cddc7 Merge branch 'release/1.6.7' 2019-04-22 15:20:18 -06:00
Andy Miller
74988f1254 Merge tag '1.6.7' into develop
Release v1.6.7
2019-04-22 15:20:18 -06:00
Andy Miller
f2f2bc1cf8 prepare for release 2019-04-22 15:20:08 -06:00
Andy Miller
31c5809e4a Better fix for #2470 2019-04-22 14:43:54 -06:00
Andy Miller
b4b8b63e24 Fix for manually set position on external URLs #2470 2019-04-22 14:29:31 -06:00
Andy Miller
9c0de8b0d3 New Yaml linter class 2019-04-20 17:41:17 -06:00
Andy Miller
f4cca777c2 Created a YAML Linter CLI command 2019-04-20 17:41:09 -06:00
Andy Miller
8d7d143d01 Merge branch 'develop' of github.com:getgrav/grav into develop 2019-04-20 12:33:11 -06:00
Andy Miller
d9109e9934 Fix issue when excluding inlineJs and inlineCss from Assets pipeline #2468 2019-04-20 12:33:07 -06:00
Gabriel Caruso
be8eb63944 Use Null Coalesce Operator (#2466) 2019-04-20 10:55:55 -06:00
Andy Miller
9342981d8c updated changelog 2019-04-19 10:43:01 -06:00
Tyler Cosgrove
6e9af3bb29 Add getSubnet util to Grav\Common\Utils (#2465) 2019-04-19 10:29:19 -06:00
Matias Griese
7e39755154 Improve null result handling in Flex 2019-04-18 19:16:01 +03:00
Matias Griese
97af8919fc Remove disabled fields from the form schema 2019-04-18 19:14:55 +03:00
Matias Griese
6cdfaeb8fb Improve FormTrait backwards compatibility with existing forms 2019-04-18 11:43:22 +03:00
Andy Miller
4ea00b0140 Merge branch 'release/1.6.6' 2019-04-17 16:02:05 -06:00
Andy Miller
864c5027c6 Merge tag '1.6.6' into develop
Release v1.6.6
2019-04-17 16:02:05 -06:00
Andy Miller
0bb55faa2d prepare for release 2019-04-17 16:01:54 -06:00
Andy Miller
f757863e1c updated changelog 2019-04-17 15:49:30 -06:00
Matias Griese
3ffd2f5f5e Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	CHANGELOG.md
2019-04-17 22:11:08 +03:00
Matias Griese
6aa135e80a Fixed FlexForm missing getter methods for defining form variables 2019-04-17 22:10:28 +03:00
Andy Miller
07f4bd0699 fixes #2461 2019-04-17 11:44:00 -06:00
Andy Miller
c200a55336 fixes #2460 2019-04-17 11:27:45 -06:00
Andy Miller
14fed2bb75 Merge branch 'develop' of github.com:getgrav/grav into develop
# Conflicts:
#	CHANGELOG.md
2019-04-17 09:00:35 -06:00
Andy Miller
44ecd61489 Roll back redirect_default_route fix as it has issues with multilang #2459 2019-04-17 09:00:03 -06:00
Matias Griese
882212520f Fixed bug in text field filtering: return empty string if value isn't a string or number 2019-04-17 16:25:36 +03:00
Matias Griese
9467939f53 Added new FormInterface::getTask() method which reads the task from form.task in the blueprint 2019-04-16 14:43:44 +03:00
Matias Griese
e3933ebdf6 Minor fix in Flex 2019-04-16 14:41:14 +03:00
Matias Griese
0d99a03c39 Fixed Flex simple storage not being properly initialized if used with caching 2019-04-16 10:58:20 +03:00
Andy Miller
e9117301d4 Merge branch 'release/1.6.5' 2019-04-15 19:22:14 -06:00
Andy Miller
bf199e9394 Merge tag '1.6.5' into develop
Release v1.6.5
2019-04-15 19:22:14 -06:00
Andy Miller
c070b0afbb prepare for release 2019-04-15 19:21:59 -06:00
Andy Miller
d54387b281 Backwards compatiblity with old Uri::__toString() output 2019-04-15 19:21:14 -06:00
Andy Miller
2bc6848464 Merge branch 'release/1.6.4' 2019-04-15 14:48:10 -06:00
Andy Miller
ab7c5d2fc5 Merge tag '1.6.4' into develop
Release v1.6.4
2019-04-15 14:48:10 -06:00
Andy Miller
abefbfc776 prepare for release 2019-04-15 14:48:01 -06:00
Andy Miller
ad173ca129 Improved redirect_default_route logic as well as Uri::toArray() to take into account root_path and extension 2019-04-15 12:44:48 -06:00
Andy Miller
d69ef0e39c Refactored rounded logic in Utils::parseSize() #2394 2019-04-15 11:00:11 -06:00
Matias Griese
436be17881 Fixed Flex simple storage not being properly initialized if used with caching 2019-04-15 14:26:55 +03:00
Andy Miller
9a5fa7e699 Extra forcing of first level arrays in taxonomy 2019-04-14 13:08:57 -06:00
Andy Miller
d502ff08c1 Attempt to resolve issue in form#332 2019-04-13 17:20:45 -06:00
Andy Miller
14eb1281f9 Fix to force all Page::taxonomy to be treated as strings #2446 2019-04-13 13:53:46 -06:00
Andy Miller
8fd7a5aebe Fixes #2445 pipeline excluded assets with cache on 2019-04-13 13:18:13 -06:00
Andy Miller
08423df547 code tidy 2019-04-13 12:27:19 -06:00
Andy Miller
40563ed2f8 Better Utils::normalizePath() logic #2216 2019-04-13 12:21:54 -06:00
Andy Miller
b639f09ca7 Merge branch 'release/1.6.3' 2019-04-12 19:48:13 -06:00
Andy Miller
dd134ad551 Merge tag '1.6.3' into develop
Release v1.6.3
2019-04-12 19:48:13 -06:00
Andy Miller
3d93d50cf0 prepare for release 2019-04-12 19:48:03 -06:00
Andy Miller
f1da7b6063 Fix for vUndefined errors when upgrading 2019-04-12 09:49:20 -06:00
Andy Miller
ef7b33f9b6 Fixed issue with Utils::normalizePath messing with external URLs #2216 2019-04-12 07:55:51 -06:00
Andy Miller
0f0e6ab1c8 Missed this one! #2442 2019-04-12 07:26:44 -06:00
Matias Griese
5362b312d1 Added Blueprint::addDynamicHandler() method to allow custom dynamic handlers, for example custom-options@: getCustomOptions 2019-04-12 12:23:31 +03:00
Andy Miller
ca4d6a398f Merge branch 'release/1.6.2' 2019-04-11 18:25:47 -06:00
Andy Miller
ed00d480f2 Merge tag '1.6.2' into develop
Release v1.6.2
2019-04-11 18:25:47 -06:00
Andy Miller
057bdd546b prepare for release 2019-04-11 18:25:38 -06:00
Andy Miller
7762f0c85e * Revert renaming of ClearCacheCommand to ensure CLI GPM upgrades go smoothly 2019-04-11 18:22:38 -06:00
Andy Miller
4b777f508b Merge tag '1.6.1' into develop
Release v1.6.1
2019-04-11 16:46:51 -06:00
62 changed files with 1411 additions and 347 deletions

View File

@@ -12,13 +12,6 @@ notifications:
email:
on_success: never
on_failure: always
hipchat:
# hipchat_api@grav
rooms:
- secure: "bqO0wM1B7bJnQw2fuhquSXEqI9gw6WmFytIh9sEWXzbYTzTUP5t0PcKOd3FT2BNMRaDxPJLVl+vG/oqmqDUBkEmOGcG504IQjeNzZqnMz0tXQMIcCc22Las9tFfc4Jf6RVi/qGomFtHGE9Wgii+TAN4zqZaufbNjwd8SyjO0+W8="
template:
- '%{repository}#%{build_number} (%{branch}): Travis Job Finished [%{duration}] (<a href="%{build_url}">Details</a>)'
format: html
slack:
secure: dowksPsxxCxGKT6nis5hUgkp6+ZDAhoqzQHF9rJnx4hx0iEygPhVBs7pKl9yL2jubYJoLs+EXwE7z1dYgDAEJh4BnfrCokCMLpFGcxVxQC/HeAUdSQ2/RtdBYR5PRT75ScaFpqM/SfXXZVtnwVXAw9Z+JC6BjQ9vmn23m51Jw4k=
env:

View File

@@ -1,3 +1,102 @@
# v1.6.9
## 05/09/2019
1. [](#new)
* Added `Route::withoutParams()` methods
* Added `Pages::setCheckMethod()` method to override page configuration in Admin Plugin
* Added `Cache::clearCache('invalidate')` parameter for just invalidating the cache without deleting any cached files
* Made `UserCollectionInderface` to extend `Countable` to get the count of existing users
1. [](#improved)
* Flex admin: added default search options for flex objects
* Flex collection and object now fall back to the default template if template file doesn't exist
* Updated Vendor libraries including Twig 1.40.1
* Updated language files from `https://crowdin.com/project/grav-core`
1. [](#bugfix)
* Fixed `$grav['route']` from being modified when the route instance gets modified
* Fixed Assets options array mixed with standalone priority [#2477](https://github.com/getgrav/grav/issues/2477)
* Fix for `avatar_url` provided by 3rd party providers
* Fixed non standard `lang` code lengths in `Utils` and `Session` detection
* Fixed saving a new object in Flex `SimpleStorage`
* Fixed exception in `Flex::getDirectories()` if the first parameter is set
* Output correct "Last Updated" in `bin/gpm info` command
* Checkbox getting interpreted as string, so created new `Validation::filterCheckbox()`
* Fixed backwards compatibility to `select` field with `selectize.create` set to true [git-sync#141](https://github.com/trilbymedia/grav-plugin-git-sync/issues/141)
* Fixed `YamlFormatter::decode()` to always return array [#2494](https://github.com/getgrav/grav/pull/2494)
* Fixed empty `$grav['request']->getAttribute('route')->getExtension()`
# v1.6.8
## 04/23/2019
1. [](#new)
* Added `FlexCollection::filterBy()` method
1. [](#bugfix)
* Revert `Use Null Coalesce Operator` [#2466](https://github.com/getgrav/grav/pull/2466)
* Fixed `FormTrait::render()` not providing config variable
* Updated `bin/grav clean` to clear `cache/compiled` and `user/config/security.yaml`
# v1.6.7
## 04/22/2019
1. [](#new)
* Added a new `bin/grav yamllinter` CLI command to find YAML Linting issues [#2468](https://github.com/getgrav/grav/issues/2468#issuecomment-485151681)
1. [](#improved)
* Improve `FormTrait` backwards compatibility with existing forms
* Added a new `Utils::getSubnet()` function for IPv4/IPv6 parsing [#2465](https://github.com/getgrav/grav/pull/2465)
1. [](#bugfix)
* Remove disabled fields from the form schema
* Fix issue when excluding `inlineJs` and `inlineCss` from Assets pipeline [#2468](https://github.com/getgrav/grav/issues/2468)
* Fix for manually set position on external URLs [#2470](https://github.com/getgrav/grav/issues/2470)
# v1.6.6
## 04/17/2019
1. [](#new)
* `FormInterface` now implements `RenderInterface`
* Added new `FormInterface::getTask()` method which reads the task from `form.task` in the blueprint
1. [](#improved)
* Updated vendor libraries to latest
1. [](#bugfix)
* Rollback `redirect_default_route` logic as it has issues with multi-lang [#2459](https://github.com/getgrav/grav/issues/2459)
* Fix potential issue with `|contains` Twig filter on PHP 7.3
* Fixed bug in text field filtering: return empty string if value isn't a string or number [#2460](https://github.com/getgrav/grav/issues/2460)
* Force Asset `priority` to be an integer and not throw error if invalid string passed [#2461](https://github.com/getgrav/grav/issues/2461)
* Fixed bug in text field filtering: return empty string if value isn't a string or number
* Fixed `FlexForm` missing getter methods for defining form variables
# v1.6.5
## 04/15/2019
1. [](#bugfix)
* Backwards compatiblity with old `Uri::__toString()` output
# v1.6.4
## 04/15/2019
1. [](#bugfix)
* Improved `redirect_default_route` logic as well as `Uri::toArray()` to take into account `root_path` and `extension`
* Rework logic to pull out excluded files from pipeline more reliably [#2445](https://github.com/getgrav/grav/issues/2445)
* Better logic in `Utils::normalizePath` to handle externals properly [#2216](https://github.com/getgrav/grav/issues/2216)
* Fixed to force all `Page::taxonomy` to be treated as strings [#2446](https://github.com/getgrav/grav/issues/2446)
* Fixed issue with `Grav['user']` not being available [form#332](https://github.com/getgrav/grav-plugin-form/issues/332)
* Updated rounding logic for `Utils::parseSize()` [#2394](https://github.com/getgrav/grav/issues/2394)
* Fixed Flex simple storage not being properly initialized if used with caching
# v1.6.3
## 04/12/2019
1. [](#new)
* Added `Blueprint::addDynamicHandler()` method to allow custom dynamic handlers, for example `custom-options@: getCustomOptions`
1. [](#bugfix)
* Missed a `CacheCommand` reference in `bin/grav` [#2442](https://github.com/getgrav/grav/issues/2442)
* Fixed issue with `Utils::normalizePath` messing with external URLs [#2216](https://github.com/getgrav/grav/issues/2216)
* Fix for `vUndefined` versions when upgrading
# v1.6.2
## 04/11/2019
1. [](#bugfix)
* Revert renaming of `ClearCacheCommand` to ensure CLI GPM upgrades go smoothly
# v1.6.1
## 04/11/2019
@@ -24,7 +123,7 @@
* Added `Grav\Framework\Object\ObjectIndex` class
* Added `Grav\Framework\Flex` classes
* Added support for hiding form fields in blueprints by using dynamic property like `security@: admin.foobar`, `scope@: object` or `scope-ignore@: object` to any field
* New experimental **FlexObjects** powered `Users` for increased performance and capability (**disabled** by default)
* New experimental **FlexObjects** powered `Users` for increased performance and capability (**disabled** by default)
* Added PSR-7 and PSR-15 classes
* Added `Grav\Framework\DI\Container` class
* Added `Grav\Framework\RequestHandler\RequestHandler` class
@@ -100,7 +199,7 @@
* Added ability to reset `Page::metadata` to allow rebuilding from automatically generated values
* Added back missing `page.types` field in system content configuration [admin#1612](https://github.com/getgrav/grav-plugin-admin/issues/1612)
* Console commands: add method for invalidating cache
* Updated languages
* Updated languages
* Improved `$page->forms()` call, added `$page->addForms()`
* Updated languages from crowdin
* Fixed `ImageMedium` constructor warning when file does not exist
@@ -121,7 +220,7 @@
* Added apcu autoloader optimization
* Additional helper methods in `Language`, `Languages`, and `LanguageCodes` classes
* Call `onFatalException` event also on internal PHP errors
* Built-in PHP Webserver: log requests before handling them
* Built-in PHP Webserver: log requests before handling them
* Added support for syslog and syslog facility logging (default: 'file')
* Improved usability of `System` configuration blueprint with side-tabs
1. [](#bugfix)
@@ -146,7 +245,7 @@
* Fixed failed login if user attempts to log in with upper case non-english letters
* Removed extra authenticated/authorized fields when saving existing user from a form
* Fixed `Grav\Framework\Route::__toString()` returning relative URL, not relative route
* Fixed handling of `append_url_extension` inside of `Page::templateFormat()` [#2264](https://github.com/getgrav/grav/issues/2264)
* Fixed handling of `append_url_extension` inside of `Page::templateFormat()` [#2264](https://github.com/getgrav/grav/issues/2264)
* Fixed a broken language string [#2261](https://github.com/getgrav/grav/issues/2261)
* Fixed clearing cache having no effect on Doctrine cache
* Fixed `Medium::relativePath()` for streams
@@ -199,7 +298,7 @@
* Updated vendor libraries
1. [](#bugfix)
* Support spaces with filenames in responsive images [#2300](https://github.com/getgrav/grav/pull/2300)
# v1.5.6
## 12/14/2018

View File

@@ -3,6 +3,7 @@
use Grav\Common\Composer;
use Grav\Common\Grav;
use League\CLImate\CLImate;
use Symfony\Component\Console\Application;
\define('GRAV_CLI', true);
@@ -24,7 +25,22 @@ 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));
}
Grav::instance(array('loader' => $autoload));
$climate = new League\CLImate\CLImate;
$climate->arguments->add([
'environment' => [
'prefix' => 'e',
'longPrefix' => 'env',
'description' => 'Configuration Environment',
'defaultValue' => 'localhost'
]
]);
$climate->arguments->parse();
// Set up environment based on params.
$environment = $climate->arguments->get('environment');
$grav = Grav::instance(array('loader' => $autoload));
$grav->setup($environment);
if (!ini_get('date.timezone')) {
date_default_timezone_set('UTC');
@@ -40,11 +56,12 @@ $app->addCommands(array(
new \Grav\Console\Cli\ComposerCommand(),
new \Grav\Console\Cli\SandboxCommand(),
new \Grav\Console\Cli\CleanCommand(),
new \Grav\Console\Cli\CacheCommand(),
new \Grav\Console\Cli\ClearCacheCommand(),
new \Grav\Console\Cli\BackupCommand(),
new \Grav\Console\Cli\NewProjectCommand(),
new \Grav\Console\Cli\SchedulerCommand(),
new \Grav\Console\Cli\SecurityCommand(),
new \Grav\Console\Cli\LogViewerCommand(),
new \Grav\Console\Cli\YamlLinterCommand(),
));
$app->run();

View File

@@ -24,7 +24,7 @@
"kodus/psr7-server": "*",
"nyholm/psr7": "^1.0",
"twig/twig": "~1.35",
"twig/twig": "~1.40",
"erusev/parsedown": "1.6.4",
"erusev/parsedown-extra": "~0.7",
"symfony/yaml": "~4.2",
@@ -89,6 +89,8 @@
"exclude": ["VERSION"]
},
"scripts": {
"api-16": "vendor/bin/phpdoc-md generate system/src > user/pages/14.api/default.16.md",
"api-15": "vendor/bin/phpdoc-md generate system/src > user/pages/14.api/default.md",
"post-create-project-cmd": "bin/grav install",
"phpstan": "vendor/bin/phpstan analyse -l 2 -c ./tests/phpstan/phpstan.neon system/src --memory-limit=256M",
"phpstan-framework": "vendor/bin/phpstan analyse -l 5 -c ./tests/phpstan/phpstan.neon system/src/Grav/Framework --memory-limit=256M",

170
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "dce6e4d72c9b5ac769cd3d93fecdf1c2",
"content-hash": "f9429e7cd2e75a232f968b01a1024983",
"packages": [
{
"name": "antoligy/dom-string-iterators",
@@ -541,17 +541,17 @@
},
{
"name": "gregwar/image",
"version": "v2.0.24",
"version": "v2.0.25",
"target-dir": "Gregwar/Image",
"source": {
"type": "git",
"url": "https://github.com/Gregwar/Image.git",
"reference": "52145816255dd20cb4bb115d0f9e1030c6287994"
"reference": "03534d5760cbea5c96e6292041ff81a3bb205c36"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Gregwar/Image/zipball/52145816255dd20cb4bb115d0f9e1030c6287994",
"reference": "52145816255dd20cb4bb115d0f9e1030c6287994",
"url": "https://api.github.com/repos/Gregwar/Image/zipball/03534d5760cbea5c96e6292041ff81a3bb205c36",
"reference": "03534d5760cbea5c96e6292041ff81a3bb205c36",
"shasum": ""
},
"require": {
@@ -589,7 +589,7 @@
"gd",
"image"
],
"time": "2019-01-27T15:10:06+00:00"
"time": "2019-03-01T15:55:29+00:00"
},
{
"name": "guzzlehttp/psr7",
@@ -1333,16 +1333,16 @@
},
{
"name": "psr/http-factory",
"version": "1.0.0",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-factory.git",
"reference": "378bfe27931ecc54ff824a20d6f6bfc303bbd04c"
"reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/378bfe27931ecc54ff824a20d6f6bfc303bbd04c",
"reference": "378bfe27931ecc54ff824a20d6f6bfc303bbd04c",
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
"reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
"shasum": ""
},
"require": {
@@ -1381,7 +1381,7 @@
"request",
"response"
],
"time": "2018-07-30T21:54:04+00:00"
"time": "2019-04-30T12:38:16+00:00"
},
{
"name": "psr/http-message",
@@ -1774,16 +1774,16 @@
},
{
"name": "symfony/console",
"version": "v4.2.5",
"version": "v4.2.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "24206aff3efe6962593297e57ef697ebb220e384"
"reference": "e2840bb38bddad7a0feaf85931e38fdcffdb2f81"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/24206aff3efe6962593297e57ef697ebb220e384",
"reference": "24206aff3efe6962593297e57ef697ebb220e384",
"url": "https://api.github.com/repos/symfony/console/zipball/e2840bb38bddad7a0feaf85931e38fdcffdb2f81",
"reference": "e2840bb38bddad7a0feaf85931e38fdcffdb2f81",
"shasum": ""
},
"require": {
@@ -1842,7 +1842,7 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"time": "2019-04-01T07:32:59+00:00"
"time": "2019-04-08T14:23:48+00:00"
},
{
"name": "symfony/contracts",
@@ -1914,16 +1914,16 @@
},
{
"name": "symfony/event-dispatcher",
"version": "v4.2.5",
"version": "v4.2.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "ca5af306fbc37f3cf597e91bc9cfa0c7d3f33544"
"reference": "fbce53cd74ac509cbe74b6f227622650ab759b02"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ca5af306fbc37f3cf597e91bc9cfa0c7d3f33544",
"reference": "ca5af306fbc37f3cf597e91bc9cfa0c7d3f33544",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/fbce53cd74ac509cbe74b6f227622650ab759b02",
"reference": "fbce53cd74ac509cbe74b6f227622650ab759b02",
"shasum": ""
},
"require": {
@@ -1974,7 +1974,7 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
"time": "2019-03-30T15:58:42+00:00"
"time": "2019-04-06T13:51:08+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -2267,16 +2267,16 @@
},
{
"name": "symfony/process",
"version": "v4.2.5",
"version": "v4.2.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "1e6cbb41dadcaf29e0db034d6ad0d039a9df06e6"
"reference": "8cf39fb4ccff793340c258ee7760fd40bfe745fe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/1e6cbb41dadcaf29e0db034d6ad0d039a9df06e6",
"reference": "1e6cbb41dadcaf29e0db034d6ad0d039a9df06e6",
"url": "https://api.github.com/repos/symfony/process/zipball/8cf39fb4ccff793340c258ee7760fd40bfe745fe",
"reference": "8cf39fb4ccff793340c258ee7760fd40bfe745fe",
"shasum": ""
},
"require": {
@@ -2312,20 +2312,20 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2019-03-10T20:07:02+00:00"
"time": "2019-04-10T16:20:36+00:00"
},
{
"name": "symfony/var-dumper",
"version": "v4.2.5",
"version": "v4.2.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "9f87189ac10b42edf7fb8edc846f1937c6d157cf"
"reference": "3c4084cb1537c0e2ad41aad622bbf55a44a5c9ce"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/9f87189ac10b42edf7fb8edc846f1937c6d157cf",
"reference": "9f87189ac10b42edf7fb8edc846f1937c6d157cf",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/3c4084cb1537c0e2ad41aad622bbf55a44a5c9ce",
"reference": "3c4084cb1537c0e2ad41aad622bbf55a44a5c9ce",
"shasum": ""
},
"require": {
@@ -2388,11 +2388,11 @@
"debug",
"dump"
],
"time": "2019-02-23T15:17:42+00:00"
"time": "2019-05-01T12:55:36+00:00"
},
{
"name": "symfony/yaml",
"version": "v4.2.5",
"version": "v4.2.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
@@ -2451,16 +2451,16 @@
},
{
"name": "twig/twig",
"version": "v1.38.4",
"version": "v1.40.1",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "7732e9e7017d751313811bd118de61302e9c8b35"
"reference": "35889516bbd6bbe46a600c2c33b03515df4a076e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/7732e9e7017d751313811bd118de61302e9c8b35",
"reference": "7732e9e7017d751313811bd118de61302e9c8b35",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/35889516bbd6bbe46a600c2c33b03515df4a076e",
"reference": "35889516bbd6bbe46a600c2c33b03515df4a076e",
"shasum": ""
},
"require": {
@@ -2475,7 +2475,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.38-dev"
"dev-master": "1.40-dev"
}
},
"autoload": {
@@ -2513,7 +2513,7 @@
"keywords": [
"templating"
],
"time": "2019-03-23T14:27:19+00:00"
"time": "2019-04-29T14:12:28+00:00"
},
{
"name": "willdurand/negotiation",
@@ -2630,16 +2630,16 @@
},
{
"name": "codeception/codeception",
"version": "2.5.5",
"version": "2.5.6",
"source": {
"type": "git",
"url": "https://github.com/Codeception/Codeception.git",
"reference": "547a64cb31edcf1902b296c511f5ca74101bcb4c"
"reference": "b83a9338296e706fab2ceb49de8a352fbca3dc98"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Codeception/Codeception/zipball/547a64cb31edcf1902b296c511f5ca74101bcb4c",
"reference": "547a64cb31edcf1902b296c511f5ca74101bcb4c",
"url": "https://api.github.com/repos/Codeception/Codeception/zipball/b83a9338296e706fab2ceb49de8a352fbca3dc98",
"reference": "b83a9338296e706fab2ceb49de8a352fbca3dc98",
"shasum": ""
},
"require": {
@@ -2718,7 +2718,7 @@
"functional testing",
"unit testing"
],
"time": "2019-03-23T17:57:45+00:00"
"time": "2019-04-24T11:28:19+00:00"
},
{
"name": "codeception/phpunit-wrapper",
@@ -3172,16 +3172,16 @@
},
{
"name": "myclabs/deep-copy",
"version": "1.8.1",
"version": "1.9.1",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8"
"reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8",
"reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72",
"reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72",
"shasum": ""
},
"require": {
@@ -3216,7 +3216,7 @@
"object",
"object graph"
],
"time": "2018-06-11T23:09:50+00:00"
"time": "2019-04-07T13:18:21+00:00"
},
{
"name": "nette/bootstrap",
@@ -4000,16 +4000,16 @@
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "4.3.0",
"version": "4.3.1",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "94fd0001232e47129dd3504189fa1c7225010d08"
"reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08",
"reference": "94fd0001232e47129dd3504189fa1c7225010d08",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c",
"reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c",
"shasum": ""
},
"require": {
@@ -4047,7 +4047,7 @@
}
],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"time": "2017-11-30T07:14:17+00:00"
"time": "2019-04-30T17:48:53+00:00"
},
{
"name": "phpdocumentor/type-resolver",
@@ -4161,28 +4161,29 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "0.3.1",
"version": "0.3.3",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "2cc49f47c69b023eaf05b48e6529389893b13d74"
"reference": "472d3161d289f652713a5e353532fa4592663a57"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/2cc49f47c69b023eaf05b48e6529389893b13d74",
"reference": "2cc49f47c69b023eaf05b48e6529389893b13d74",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/472d3161d289f652713a5e353532fa4592663a57",
"reference": "472d3161d289f652713a5e353532fa4592663a57",
"shasum": ""
},
"require": {
"php": "~7.1"
},
"require-dev": {
"consistence/coding-standard": "^2.0.0",
"consistence/coding-standard": "^3.5",
"jakub-onderka/php-parallel-lint": "^0.9.2",
"phing/phing": "^2.16.0",
"phpstan/phpstan": "^0.10",
"phpunit/phpunit": "^6.3",
"slevomat/coding-standard": "^3.3.0",
"slevomat/coding-standard": "^4.7.2",
"squizlabs/php_codesniffer": "^3.3.2",
"symfony/process": "^3.4 || ^4.0"
},
"type": "library",
@@ -4203,7 +4204,7 @@
"MIT"
],
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"time": "2019-01-14T12:26:23+00:00"
"time": "2019-04-23T20:26:19+00:00"
},
{
"name": "phpstan/phpstan",
@@ -4578,16 +4579,16 @@
},
{
"name": "phpunit/phpunit",
"version": "7.5.8",
"version": "7.5.9",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "c29c0525cf4572c11efe1db49a8b8aee9dfac58a"
"reference": "134669cf0eeac3f79bc7f0c793efbc158bffc160"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c29c0525cf4572c11efe1db49a8b8aee9dfac58a",
"reference": "c29c0525cf4572c11efe1db49a8b8aee9dfac58a",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/134669cf0eeac3f79bc7f0c793efbc158bffc160",
"reference": "134669cf0eeac3f79bc7f0c793efbc158bffc160",
"shasum": ""
},
"require": {
@@ -4658,7 +4659,7 @@
"testing",
"xunit"
],
"time": "2019-03-26T13:23:54+00:00"
"time": "2019-04-19T15:50:46+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
@@ -4827,16 +4828,16 @@
},
{
"name": "sebastian/environment",
"version": "4.1.0",
"version": "4.2.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
"reference": "6fda8ce1974b62b14935adc02a9ed38252eca656"
"reference": "3095910f0f0fb155ac4021fc51a4a7a39ac04e8a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6fda8ce1974b62b14935adc02a9ed38252eca656",
"reference": "6fda8ce1974b62b14935adc02a9ed38252eca656",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/3095910f0f0fb155ac4021fc51a4a7a39ac04e8a",
"reference": "3095910f0f0fb155ac4021fc51a4a7a39ac04e8a",
"shasum": ""
},
"require": {
@@ -4851,7 +4852,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.1-dev"
"dev-master": "4.2-dev"
}
},
"autoload": {
@@ -4876,7 +4877,7 @@
"environment",
"hhvm"
],
"time": "2019-02-01T05:27:49+00:00"
"time": "2019-04-25T07:55:20+00:00"
},
{
"name": "sebastian/exporter",
@@ -5228,16 +5229,16 @@
},
{
"name": "symfony/browser-kit",
"version": "v4.2.5",
"version": "v4.2.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/browser-kit.git",
"reference": "61d85c5af2fc058014c7c89504c3944e73a086f0"
"reference": "c09c18cca96d7067152f78956faf55346c338283"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/61d85c5af2fc058014c7c89504c3944e73a086f0",
"reference": "61d85c5af2fc058014c7c89504c3944e73a086f0",
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/c09c18cca96d7067152f78956faf55346c338283",
"reference": "c09c18cca96d7067152f78956faf55346c338283",
"shasum": ""
},
"require": {
@@ -5281,11 +5282,11 @@
],
"description": "Symfony BrowserKit Component",
"homepage": "https://symfony.com",
"time": "2019-02-23T15:17:42+00:00"
"time": "2019-04-07T09:56:43+00:00"
},
{
"name": "symfony/css-selector",
"version": "v4.2.5",
"version": "v4.2.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
@@ -5338,7 +5339,7 @@
},
{
"name": "symfony/dom-crawler",
"version": "v4.2.5",
"version": "v4.2.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/dom-crawler.git",
@@ -5395,16 +5396,16 @@
},
{
"name": "symfony/finder",
"version": "v4.2.5",
"version": "v4.2.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "267b7002c1b70ea80db0833c3afe05f0fbde580a"
"reference": "e45135658bd6c14b61850bf131c4f09a55133f69"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/267b7002c1b70ea80db0833c3afe05f0fbde580a",
"reference": "267b7002c1b70ea80db0833c3afe05f0fbde580a",
"url": "https://api.github.com/repos/symfony/finder/zipball/e45135658bd6c14b61850bf131c4f09a55133f69",
"reference": "e45135658bd6c14b61850bf131c4f09a55133f69",
"shasum": ""
},
"require": {
@@ -5440,7 +5441,7 @@
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
"time": "2019-02-23T15:42:05+00:00"
"time": "2019-04-06T13:51:08+00:00"
},
{
"name": "theseer/tokenizer",
@@ -5593,7 +5594,8 @@
"ext-mbstring": "*",
"ext-openssl": "*",
"ext-curl": "*",
"ext-zip": "*"
"ext-zip": "*",
"ext-dom": "*"
},
"platform-dev": [],
"platform-overrides": {

View File

@@ -8,7 +8,7 @@
// Some standard defines
define('GRAV', true);
define('GRAV_VERSION', '1.6.1');
define('GRAV_VERSION', '1.6.9');
define('GRAV_TESTING', false);
define('DS', '/');

View File

@@ -23,7 +23,7 @@ GRAV:
BAD_DATE: Fecha errónea
AGO: antes
FROM_NOW: desde ahora
JUST_NOW: justo ahora
JUST_NOW: hace un momento
SECOND: segundo
MINUTE: minuto
HOUR: hora

View File

@@ -1,11 +1,30 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---\npealkiri: %1$s\n---\n\n# Viga: vigane Frontmatter'i\n\nasukoht: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
INFLECTOR_UNCOUNTABLE:
- 'equipment'
- 'informatsioon'
- 'rice'
- 'money'
- 'species'
- 'series'
- 'kala'
- 'lammas'
INFLECTOR_IRREGULAR:
'person': 'inimesed'
'man': 'mees'
'child': 'lapsed'
INFLECTOR_ORDINALS:
'default': '.'
'first': '.'
'second': '.'
'third': '.'
NICETIME:
NO_DATE_PROVIDED: Kuupäev määramata
BAD_DATE: Vigane kuupäev
AGO: tagasi
FROM_NOW: praegusest
JUST_NOW: just nüüd
SECOND: sekund
MINUTE: minut
HOUR: tundi
@@ -60,3 +79,7 @@ GRAV:
- 'reede'
- 'laupäev'
- 'pühapäev'
CRON:
EVERY: iga
EVERY_MONTH: iga kuu
TEXT_PERIOD: Iga <b />

View File

@@ -14,6 +14,8 @@ GRAV:
'/sis$/i': 'ses'
'/([ti])um$/i': '\1a'
'/(buffal|tomat)o$/i': '\1es'
'/(bu)s$/i': 'Bus'
'/(alias|status)/i': 'alias|status'
'/(ax|test)is$/i': '\1s'
'/s$/i': 's'
'/$/': 's'

View File

@@ -11,6 +11,8 @@ GRAV:
- 'fish'
- 'sheep'
NICETIME:
NO_DATE_PROVIDED: Engin dagsetning gefin
BAD_DATE: Röng dagsetning
AGO: síðan
JUST_NOW: í þessu
SECOND: sekúndu
@@ -45,6 +47,7 @@ GRAV:
DEC_PLURAL: árat
FORM:
VALIDATION_FAIL: <b>Sannvottun mistókst:</b>
INVALID_INPUT: Ógilt inntak í
MISSING_REQUIRED_FIELD: 'Vantar nauðsynlegan reit:'
MONTHS_OF_THE_YEAR:
- 'janúar'
@@ -67,3 +70,11 @@ GRAV:
- 'Föstudagur'
- 'Laugardagur'
- 'Sunnudagur'
CRON:
TEXT_TIME: ' á <b />:<b />'
TEXT_DOW: ' á <b />'
TEXT_MONTH: ' af <b />'
TEXT_DOM: ' á <b />'
ERROR1: Merkið %s er ekki stutt!
ERROR3: Það ætti að setja jquery_element inn í stillingar jqCron
ERROR4: Óþekkt segð

View File

@@ -35,3 +35,12 @@ GRAV:
- 'Outubro'
- 'Novembro'
- 'Dezembro'
INFLECTOR_UNCOUNTABLE:
- 'equipment'
- 'information'
- 'arroz'
- 'money'
- 'species'
- 'series'
- 'fish'
- 'sheep'

View File

@@ -1,10 +1,75 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Chyba: Chybný frontmatter\n\nPath: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
INFLECTOR_PLURALS:
'/(quiz)$/i': '\1zes'
'/^(ox)$/i': '\1en'
'/([m|l])ouse$/i': '\1ice'
'/(matr|vert|ind)ix|ex$/i': '\1ices'
'/(x|ch|ss|sh)$/i': '\1es'
'/([^aeiouy]|qu)ies$/i': '\1y'
'/([^aeiouy]|qu)y$/i': '\1ies'
'/(hive)$/i': '\1s'
'/(?:([^f])fe|([lr])f)$/i': '\1\2ves'
'/sis$/i': 'ses'
'/([ti])um$/i': '\1a'
'/(buffal|tomat)o$/i': '\1oes'
'/(bu)s$/i': '\1ses'
'/(alias|status)/i': '\1es'
'/(octop|vir)us$/i': '\1i'
'/(ax|test)is$/i': '\1es'
'/s$/i': 's'
'/$/': 's'
INFLECTOR_SINGULAR:
'/(quiz)zes$/i': '\1'
'/(matr)ices$/i': '\1ix'
'/(vert|ind)ices$/i': '\1ex'
'/^(ox)en/i': '\1'
'/(alias|status)es$/i': '\1'
'/([octop|vir])i$/i': '\1us'
'/(cris|ax|test)es$/i': '\1is'
'/(shoe)s$/i': '\1'
'/(o)es$/i': '\1'
'/(bus)es$/i': '\1'
'/([m|l])ice$/i': '\1ouse'
'/(x|ch|ss|sh)es$/i': '\1'
'/(m)ovies$/i': '\1ovie'
'/(s)eries$/i': '\1eries'
'/([^aeiouy]|qu)ies$/i': '\1y'
'/([lr])ves$/i': '\1f'
'/(tive)s$/i': '\1'
'/(hive)s$/i': '\1'
'/([^f])ves$/i': '\1fe'
'/(^analy)ses$/i': '\1sis'
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2sis'
'/([ti])a$/i': '\1um'
'/(n)ews$/i': '\1ews'
INFLECTOR_UNCOUNTABLE:
- 'vybavenie'
- 'informácie'
- 'ryža'
- 'peniaze'
- 'druhy'
- 'séria'
- 'ryba'
- 'ovce'
INFLECTOR_IRREGULAR:
'person': 'ľudia'
'man': 'muži'
'child': 'deti'
'sex': 'pohlavia'
'move': 'pohyby'
INFLECTOR_ORDINALS:
'default': '.'
'first': '.'
'second': '.'
'third': '.'
NICETIME:
NO_DATE_PROVIDED: Neposkytnutý žiaden dátum
BAD_DATE: Nesprávny dátum
AGO: pred
FROM_NOW: odteraz
JUST_NOW: práve teraz
SECOND: sekunda
MINUTE: minúta
HOUR: hodina
@@ -14,10 +79,12 @@ GRAV:
YEAR: rok
DECADE: desaťročie
SEC: sek
MIN: min
HR: hod
WK: t
MO: m
YR: r
DEC: dec
SECOND_PLURAL: sekúnd
MINUTE_PLURAL: minút
HOUR_PLURAL: hodín
@@ -58,3 +125,20 @@ GRAV:
- 'Piatok'
- 'Sobota'
- 'Nedeľa'
CRON:
EVERY: každý
EVERY_HOUR: každú hodinu
EVERY_MINUTE: každú minútu
EVERY_DAY_OF_WEEK: každý deň v týždni
EVERY_DAY_OF_MONTH: každý deň v mesiaci
EVERY_MONTH: každý mesiac
TEXT_PERIOD: Každý <b />
TEXT_MINS: ' at <b /> minute(s) past the hour'
TEXT_TIME: ' at <b />:<b />'
TEXT_DOW: ' on <b />'
TEXT_MONTH: ' of <b />'
TEXT_DOM: ' on <b />'
ERROR1: Tag %s nieje podporovaný!
ERROR2: Chybný počet položiek
ERROR3: jquery_element musí byť nastavený v nastaveniach pre jqCron
ERROR4: Neznámy výraz

View File

@@ -1,6 +1,17 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "--- titel: %1$s --- # Fel: Ogiltig Frontmatter-sökväg: `%2$s` **%3$s** ``` %4$s ```"
INFLECTOR_UNCOUNTABLE:
- 'utrustning'
- 'information'
- 'ris'
- 'pengar'
- 'arter'
- 'serier'
- 'fisk'
- 'får'
INFLECTOR_IRREGULAR:
'person': 'personer'
NICETIME:
NO_DATE_PROVIDED: Inget datum har angivits
BAD_DATE: Ogiltigt datum

View File

@@ -1,24 +1,44 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# 錯誤: 不正確的 Frontmatter\n\n路徑 `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
NICETIME:
NO_DATE_PROVIDED: 沒有提供日期
BAD_DATE: 錯誤日期
AGO: 之前
FROM_NOW: 之後
JUST_NOW: 剛剛
SECOND:
MINUTE:
HOUR: 小時
DAY:
WEEK:
MONTH:
YEAR:
DECADE: 十年
SEC:
MIN:
HR: 小時
WK:
MO:
YR:
DEC: 十年
SECOND_PLURAL:
MINUTE_PLURAL:
HOUR_PLURAL:
DAY_PLURAL:
WEEK_PLURAL:
HOUR_PLURAL:
DAY_PLURAL:
WEEK_PLURAL:
MONTH_PLURAL:
YEAR_PLURAL:
DECADE_PLURAL: 十年
SEC_PLURAL:
MIN_PLURAL:
HR_PLURAL:
WK_PLURAL:
WK_PLURAL:
MO_PLURAL:
YR_PLURAL:
DEC_PLURAL: 十年
FORM:
MISSING_REQUIRED_FIELD: 遺漏必填欄位:
MONTHS_OF_THE_YEAR:
- '一月'
- '二月'

View File

@@ -1,57 +1,122 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# 錯誤: 不正確的 Frontmatter\n\n路徑 `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
FRONTMATTER_ERROR_PAGE: "---\n标题: %1$s\n---\n\n# 错误:无效参数\n\n位置 `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
INFLECTOR_PLURALS:
'/(quiz)$/i': '\1zes'
'/^(ox)$/i': '\1en'
'/([m|l])ouse$/i': '\1ice'
'/(matr|vert|ind)ix|ex$/i': '\1ices'
'/(x|ch|ss|sh)$/i': '\1es'
'/([^aeiouy]|qu)ies$/i': '\1y'
'/([^aeiouy]|qu)y$/i': '\1ies'
'/(hive)$/i': '\1s'
'/(?:([^f])fe|([lr])f)$/i': '\1\2ves'
'/sis$/i': 'ses'
'/([ti])um$/i': '\1a'
'/(buffal|tomat)o$/i': '\1oes'
'/(bu)s$/i': '\1ses'
'/(alias|status)/i': '\1es'
'/(octop|vir)us$/i': '\1i'
'/(ax|test)is$/i': '\1es'
'/s$/i': 's'
'/$/': 's'
INFLECTOR_SINGULAR:
'/(quiz)zes$/i': '\1'
'/(matr)ices$/i': '\1ix'
'/(vert|ind)ices$/i': '\1ex'
'/^(ox)en/i': '\1'
'/(alias|status)es$/i': '\1'
'/([octop|vir])i$/i': '\1us'
'/(cris|ax|test)es$/i': '\1is'
'/(shoe)s$/i': '\1'
'/(o)es$/i': '\1'
'/(bus)es$/i': '\1'
'/([m|l])ice$/i': '\1ouse'
'/(x|ch|ss|sh)es$/i': '\1'
'/(m)ovies$/i': '\1ovie'
'/(s)eries$/i': '\1eries'
'/([^aeiouy]|qu)ies$/i': '\1y'
'/([lr])ves$/i': '\1f'
'/(tive)s$/i': '\1'
'/(hive)s$/i': '\1'
'/([^f])ves$/i': '\1fe'
'/(^analy)ses$/i': '\1sis'
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2sis'
'/([ti])a$/i': '\1um'
'/(n)ews$/i': '\1ews'
INFLECTOR_UNCOUNTABLE:
- '装备'
- '信息'
- '大米'
- '钱'
- '物种'
- '系列'
- '鱼'
- '羊'
INFLECTOR_IRREGULAR:
'person': '人员'
'man': '男人'
'child': '儿童'
'sex': '性别'
'move': '移动'
INFLECTOR_ORDINALS:
'default': 'th'
'first': 'st'
'second': 'md'
'third': 'rd'
NICETIME:
NO_DATE_PROVIDED: 沒有提供日期
BAD_DATE: 錯誤日期
AGO:
FROM_NOW: 之後
JUST_NOW: 剛剛
NO_DATE_PROVIDED: 无日期信息
BAD_DATE: 无效日期
AGO:
FROM_NOW: 距今
JUST_NOW: 刚刚
SECOND:
MINUTE:
HOUR:
MINUTE:
HOUR:
DAY:
WEEK:
WEEK:
MONTH:
YEAR:
DECADE: 十年
SEC:
MIN:
HR:
WK:
MIN:
HR:
WK:
MO:
YR:
DEC:
DEC:
SECOND_PLURAL:
MINUTE_PLURAL:
HOUR_PLURAL:
HOUR_PLURAL:
DAY_PLURAL:
WEEK_PLURAL:
WEEK_PLURAL:
MONTH_PLURAL:
YEAR_PLURAL:
DECADE_PLURAL: 十年
SEC_PLURAL:
MIN_PLURAL:
HR_PLURAL:
WK_PLURAL:
HR_PLURAL:
WK_PLURAL:
MO_PLURAL:
YR_PLURAL:
DEC_PLURAL:
DEC_PLURAL:
FORM:
MISSING_REQUIRED_FIELD: 遺漏必填欄位:
VALIDATION_FAIL: <b>验证失败:</b>
INVALID_INPUT: 无效输入
MISSING_REQUIRED_FIELD: 必填字段缺失:
MONTHS_OF_THE_YEAR:
- '月'
- '月'
- '月'
- '月'
- '月'
- '月'
- '月'
- '月'
- '月'
- '月'
- '十一月'
- '十二月'
- '1月'
- '2月'
- '3月'
- '4月'
- '5月'
- '6月'
- '7月'
- '8月'
- '9月'
- '10月'
- '11月'
- '12月'
DAYS_OF_THE_WEEK:
- '星期一'
- '星期二'
@@ -60,4 +125,20 @@ GRAV:
- '星期五'
- '星期六'
- '星期日'
CRON:
EVERY: 每隔
EVERY_HOUR: 每小时
EVERY_MINUTE: 每分钟
EVERY_DAY_OF_WEEK: 一周中的每一天
EVERY_DAY_OF_MONTH: 月份中的每一天
EVERY_MONTH: 每月
TEXT_PERIOD: 所有 <b />
TEXT_MINS: ' 在 <b /> 小时过后的分钟'
TEXT_TIME: ' 在 <b />:<b />'
TEXT_DOW: ' on <b />'
TEXT_MONTH: ' of <b />'
TEXT_DOM: ' on <b />'
ERROR1: 不支持分享类型 %s
ERROR2: 无效数字
ERROR3: 请在 jqCron 设置中设定 jquery_element
ERROR4: 无法识别表达式

View File

@@ -45,8 +45,10 @@ class Assets extends PropertyObject
// Config Options
protected $css_pipeline;
protected $css_pipeline_include_externals;
protected $css_pipeline_before_excludes;
protected $js_pipeline;
protected $js_pipeline_include_externals;
protected $js_pipeline_before_excludes;
protected $pipeline_options = [];
@@ -170,7 +172,8 @@ class Assets extends PropertyObject
// If pipeline disabled, set to position if provided, else after
if (isset($options['pipeline'])) {
if ($options['pipeline'] === false) {
$excludes = strtolower($type . '_pipeline_before_excludes');
$exclude_type = ($type === $this::JS_TYPE || $type === $this::INLINE_JS_TYPE) ? $this::JS_TYPE : $this::CSS_TYPE;
$excludes = strtolower($exclude_type . '_pipeline_before_excludes');
if ($this->{$excludes}) {
$default = 'after';
} else {
@@ -264,6 +267,22 @@ class Assets extends PropertyObject
protected function filterAssets($assets, $key, $value, $sort = false)
{
$results = array_filter($assets, function($asset) use ($key, $value) {
if ($key === 'position' && $value === 'pipeline') {
$type = $asset->getType();
if ($asset->getRemote() && $this->{$type . '_pipeline_include_externals'} === false && $asset['position'] === 'pipeline' ) {
if ($this->{$type . '_pipeline_before_excludes'}) {
$asset->setPosition('after');
} else {
$asset->setPosition('before');
}
return false;
}
}
if ($asset[$key] === $value) return true;
return false;
});
@@ -292,11 +311,9 @@ class Assets extends PropertyObject
$before_output = '';
$pipeline_output = '';
$after_output = '';
$no_pipeline = [];
$assets = 'assets_' . $type;
$pipeline_enabled = $type . '_pipeline';
$before_excludes = $type . '_pipeline_before_excludes';
$render_pipeline = 'render' . ucfirst($type);
$group_assets = $this->filterAssets($this->$assets, 'group', $group);
@@ -309,22 +326,13 @@ class Assets extends PropertyObject
$options = array_merge($this->pipeline_options, ['timestamp' => $this->timestamp]);
$pipeline = new Pipeline($options);
$pipeline_output = $pipeline->$render_pipeline($pipeline_assets, $group, $attributes, $no_pipeline);
$pipeline_output = $pipeline->$render_pipeline($pipeline_assets, $group, $attributes);
} else {
foreach ($pipeline_assets as $asset) {
$pipeline_output .= $asset->render();
}
}
// Handle stuff that couldn't be pipelined
if (!empty($no_pipeline)) {
if ($this->{$before_excludes}) {
$after_assets = array_merge($after_assets, $no_pipeline);
} else {
$before_assets = array_merge($before_assets, $no_pipeline);
}
}
// Before Pipeline
foreach ($before_assets as $asset) {
$before_output .= $asset->render();

View File

@@ -78,6 +78,9 @@ abstract class BaseAsset extends PropertyObject
}
}
// Force priority to be an int
$this->priority = (int) $this->priority;
// Do some special stuff for CSS/JS (not inline)
if (!Utils::startsWith($this->getType(), 'inline')) {
$this->base_url = rtrim($uri->rootUrl($config->get('system.absolute_urls')), '/') . '/';
@@ -129,6 +132,12 @@ abstract class BaseAsset extends PropertyObject
return $this->remote;
}
public function setPosition($position)
{
$this->position = $position;
return $this;
}
/**
*

View File

@@ -50,9 +50,6 @@ class Pipeline extends PropertyObject
protected $query;
protected $asset;
protected $css_pipeline_include_externals;
protected $js_pipeline_include_externals;
/**
* Closure used by the pipeline to fetch assets.
*
@@ -91,11 +88,10 @@ class Pipeline extends PropertyObject
* @param array $assets
* @param string $group
* @param array $attributes
* @param array $no_pipeline
*
* @return bool|string URL or generated content if available, else false
*/
public function renderCss($assets, $group, $attributes = [], &$no_pipeline = [])
public function renderCss($assets, $group, $attributes = [])
{
// temporary list of assets to pipeline
$inline_group = false;
@@ -119,21 +115,13 @@ class Pipeline extends PropertyObject
if (file_exists($this->assets_dir . $file)) {
$buffer = file_get_contents($this->assets_dir . $file) . "\n";
} else {
foreach ($assets as $id => $asset) {
if ($this->css_pipeline_include_externals === false && $asset->getRemote()) {
$no_pipeline[$id] = $asset;
unset($assets[$id]);
}
}
//if nothing found get out of here!
if (empty($assets) && empty($no_pipeline)) {
if (empty($assets)) {
return false;
}
// Concatenate files
$buffer = $this->gatherLinks($assets, self::CSS_ASSET, $no_pipeline);
$buffer = $this->gatherLinks($assets, self::CSS_ASSET);
// Minify if required
if ($this->shouldMinify('css')) {
@@ -164,11 +152,10 @@ class Pipeline extends PropertyObject
* @param array $assets
* @param string $group
* @param array $attributes
* @param array $no_pipeline
*
* @return bool|string URL or generated content if available, else false
*/
public function renderJs($assets, $group, $attributes = [], &$no_pipeline = [])
public function renderJs($assets, $group, $attributes = [])
{
// temporary list of assets to pipeline
$inline_group = false;
@@ -192,21 +179,13 @@ class Pipeline extends PropertyObject
if (file_exists($this->assets_dir . $file)) {
$buffer = file_get_contents($this->assets_dir . $file) . "\n";
} else {
foreach ($assets as $id => $asset) {
if ($this->js_pipeline_include_externals === false && $asset->getRemote()) {
$no_pipeline[$id] = $asset;
unset($assets[$id]);
}
}
//if nothing found get out of here!
if (empty($assets) && empty($no_pipeline)) {
if (empty($assets)) {
return false;
}
// Concatenate files
$buffer = $this->gatherLinks($assets, self::JS_ASSET, $no_pipeline);
$buffer = $this->gatherLinks($assets, self::JS_ASSET);
// Minify if required
if ($this->shouldMinify('js')) {

View File

@@ -38,11 +38,10 @@ trait AssetUtilsTrait
*
* @param array $assets
* @param bool $css
* @param array $no_pipeline
*
* @return string
*/
protected function gatherLinks(array $assets, $css = true, &$no_pipeline = [])
protected function gatherLinks(array $assets, $css = true)
{
$buffer = '';
@@ -74,9 +73,6 @@ trait AssetUtilsTrait
// No file found, skip it...
if ($file === false) {
if (!$local) { // Assume we couldn't download this file for some reason assume it's not pipeline compatible
$no_pipeline[$id] = $asset;
}
continue;
}

View File

@@ -24,19 +24,21 @@ trait LegacyAssetsTrait
// First argument is always the asset
array_shift($args);
if (\count($args) === 0) {
if (count($args) === 0) {
return [];
}
if (\count($args) === 1 && \is_array($args[0])) {
// New options array format
if (count($args) === 1 && is_array($args[0])) {
return $args[0];
}
// Handle obscure case where options array is mixed with a priority
if (count($args) === 2 && is_array($args[0]) && is_int($args[1])) {
$arguments = $args[0];
$arguments['priority'] = $args[1];
return $arguments;
}
switch ($type) {
case(Assets::INLINE_CSS_TYPE):
$defaults = ['priority' => null, 'group' => null];
$arguments = $this->createArgumentsFromLegacy($args, $defaults);
break;
case(Assets::JS_TYPE):
$defaults = ['priority' => null, 'pipeline' => true, 'loading' => null, 'group' => null];
$arguments = $this->createArgumentsFromLegacy($args, $defaults);
@@ -55,6 +57,11 @@ trait LegacyAssetsTrait
break;
case(Assets::INLINE_CSS_TYPE):
$defaults = ['priority' => null, 'group' => null];
$arguments = $this->createArgumentsFromLegacy($args, $defaults);
break;
default:
case(Assets::CSS_TYPE):
$defaults = ['priority' => null, 'pipeline' => true, 'group' => null, 'loading' => null];

View File

@@ -437,6 +437,9 @@ class Cache extends Getters
case 'tmp-only':
$remove_paths = self::$tmp_remove;
break;
case 'invalidate':
$remove_paths = [];
break;
default:
if (Grav::instance()['config']->get('system.cache.clear_images_by_default')) {
$remove_paths = self::$standard_remove;

View File

@@ -28,6 +28,15 @@ class Blueprint extends BlueprintForm
/** @var array */
protected $defaults;
protected $handlers = [];
public function __clone()
{
if ($this->blueprintSchema) {
$this->blueprintSchema = clone $this->blueprintSchema;
}
}
public function setScope($scope)
{
$this->scope = $scope;
@@ -66,6 +75,55 @@ class Blueprint extends BlueprintForm
return $this->defaults;
}
/**
* Initialize blueprints with its dynamic fields.
*
* @return $this
*/
public function init()
{
foreach ($this->dynamic as $key => $data) {
// Locate field.
$path = explode('/', $key);
$current = &$this->items;
foreach ($path as $field) {
if (\is_object($current)) {
// Handle objects.
if (!isset($current->{$field})) {
$current->{$field} = [];
}
$current = &$current->{$field};
} else {
// Handle arrays and scalars.
if (!\is_array($current)) {
$current = [$field => []];
} elseif (!isset($current[$field])) {
$current[$field] = [];
}
$current = &$current[$field];
}
}
// Set dynamic property.
foreach ($data as $property => $call) {
$action = $call['action'];
$method = 'dynamic' . ucfirst($action);
if (isset($this->handlers[$action])) {
$callable = $this->handlers[$action];
$callable($current, $property, $call);
} elseif (method_exists($this, $method)) {
$this->{$method}($current, $property, $call);
}
}
}
return $this;
}
/**
* Merge two arrays by using blueprints.
*
@@ -165,6 +223,11 @@ class Blueprint extends BlueprintForm
return $this->blueprintSchema;
}
public function addDynamicHandler(string $name, callable $callable): void
{
$this->handlers[$name] = $callable;
}
/**
* Initialize validator.
*/

View File

@@ -142,7 +142,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
if ($rule) {
// Item has been defined in blueprints.
if (!empty($rule['validate']['ignore'])) {
if (!empty($rule['disabled']) || !empty($rule['validate']['ignore'])) {
// Skip validation in the ignored field.
continue;
}
@@ -178,7 +178,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
$val = $rules[$key] ?? $rules['*'] ?? null;
$rule = \is_string($val) ? $this->items[$val] : null;
if (empty($rule['validate']['ignore'])) {
if (empty($rule['disabled']) && empty($rule['validate']['ignore'])) {
continue;
}
}
@@ -191,7 +191,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
if ($rule) {
// Item has been defined in blueprints.
if (!empty($rule['validate']['ignore'])) {
if (!empty($rule['disabled']) || !empty($rule['validate']['ignore'])) {
// Skip any data in the ignored field.
unset($results[$key]);
continue;
@@ -240,6 +240,8 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
if (
// Not an input field
!$field
// Field has been disabled
|| !empty($field['disabled'])
// Field validation is set to be ignored
|| !empty($field['validate']['ignore'])
// Field is toggleable and the toggle is turned off
@@ -273,7 +275,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
$field = $this->items[$field];
// Skip ignored field, it will not be required.
if (!empty($field['validate']['ignore'])) {
if (!empty($field['disabled']) || !empty($field['validate']['ignore'])) {
continue;
}

View File

@@ -154,6 +154,10 @@ class Validation
protected static function filterText($value, array $params, array $field)
{
if (!\is_string($value) && !is_numeric($value)) {
return '';
}
if (!empty($params['trim'])) {
$value = trim($value);
}
@@ -161,6 +165,11 @@ class Validation
return (string) $value;
}
protected static function filterCheckbox($value, array $params, array $field)
{
return (bool) $value;
}
protected static function filterCommaList($value, array $params, array $field)
{
return \is_array($value) ? $value : preg_split('/\s*,\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
@@ -567,6 +576,11 @@ class Validation
}
}
// If creating new values is allowed, no further checks are needed.
if (!empty($field['selectize']['create'])) {
return true;
}
$options = $field['options'] ?? [];
$use = $field['use'] ?? 'values';

View File

@@ -12,11 +12,16 @@ namespace Grav\Common\GPM\Remote;
use Grav\Common\Data\Data;
use Grav\Common\GPM\Common\Package as BasePackage;
class Package extends BasePackage
class Package extends BasePackage implements \JsonSerializable
{
public function __construct($package, $package_type = null)
{
$data = new Data($package);
parent::__construct($data, $package_type);
}
public function jsonSerialize()
{
return $this->data;
}
}

View File

@@ -234,7 +234,7 @@ class Truncator {
}
/**
* @inheritDoc
*
*/
public function truncate(
$text,

View File

@@ -0,0 +1,76 @@
<?php
/**
* @package Grav\Common\Helpers
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Helpers;
use Grav\Common\Grav;
use RocketTheme\Toolbox\File\MarkdownFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Symfony\Component\Yaml\Yaml;
class YamlLinter
{
public static function lint()
{
$errors = static::lintConfig();
$errors = $errors + static::lintPages();
return $errors;
}
public static function lintPages()
{
return static::recurseFolder('page://');
}
public static function lintConfig()
{
return static::recurseFolder('config://');
}
public static function recurseFolder($path, $extensions = 'md|yaml')
{
$lint_errors = [];
/** @var UniformResourceLocator $locator */
$locator = Grav::instance()['locator'];
$flags = \RecursiveDirectoryIterator::SKIP_DOTS;
if ($locator->isStream($path)) {
$directory = $locator->getRecursiveIterator($path, $flags);
} else {
$directory = new \RecursiveDirectoryIterator($path, $flags);
}
$recursive = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST);
$iterator = new \RegexIterator($recursive, '/^.+\.'.$extensions.'$/i');
/** @var \RecursiveDirectoryIterator $file */
foreach ($iterator as $filepath => $file) {
try {
Yaml::parse(static::extractYaml($filepath));
} catch (\Exception $e) {
$lint_errors[str_replace(GRAV_ROOT, '', $filepath)] = $e->getMessage();
}
}
return $lint_errors;
}
protected static function extractYaml($path)
{
$extension = pathinfo($path, PATHINFO_EXTENSION);
if ($extension === 'md') {
$file = MarkdownFile::instance($path);
$contents = $file->frontmatter();
} else {
$contents = file_get_contents($path);
}
return $contents;
}
}

View File

@@ -104,6 +104,11 @@ class Language
public function getAvailable()
{
$languagesArray = $this->languages; //Make local copy
$languagesArray = array_map(function($value) {
return preg_quote($value);
}, $languagesArray);
sort($languagesArray);
return implode('|', array_reverse($languagesArray));

View File

@@ -414,9 +414,7 @@ class Page implements PageInterface
$this->markdown_extra = (bool)$this->header->markdown_extra;
}
if (isset($this->header->taxonomy)) {
foreach ((array)$this->header->taxonomy as $taxonomy => $taxitems) {
$this->taxonomy[$taxonomy] = (array)$taxitems;
}
$this->taxonomy($this->header->taxonomy);
}
if (isset($this->header->max_count)) {
$this->max_count = (int)$this->header->max_count;
@@ -2296,6 +2294,14 @@ class Page implements PageInterface
public function taxonomy($var = null)
{
if ($var !== null) {
// make sure first level are arrays
array_walk($var, function(&$value) {
$value = (array) $value;
});
// make sure all values are strings
array_walk_recursive($var, function(&$value) {
$value = (string) $value;
});
$this->taxonomy = $var;
}

View File

@@ -88,6 +88,9 @@ class Pages
*/
protected $ignore_hidden;
/** @var string */
protected $check_method;
/**
* @var Types
*/
@@ -226,6 +229,11 @@ class Pages
return $this->baseUrl($lang, $absolute) . Uri::filterPath($route);
}
public function setCheckMethod($method)
{
$this->check_method = strtolower($method);
}
/**
* Class initialization. Must be called before using this class.
*/
@@ -240,6 +248,10 @@ class Pages
$this->children = [];
$this->routes = [];
if (!$this->check_method) {
$this->setCheckMethod($config->get('system.cache.check.method', 'file'));
}
$this->buildPages();
}
@@ -947,7 +959,7 @@ class Pages
$taxonomy = $this->grav['taxonomy'];
// how should we check for last modified? Default is by file
switch (strtolower($config->get('system.cache.check.method', 'file'))) {
switch ($this->check_method) {
case 'none':
case 'off':
$hash = 0;

View File

@@ -30,10 +30,13 @@ class RequestProcessor extends ProcessorBase
$request = $request->withParsedBody(json_decode($request->getBody()->getContents(), true));
}
$uri = $request->getUri();
$ext = mb_strtolower(pathinfo($uri->getPath(), PATHINFO_EXTENSION));
$request = $request
->withAttribute('grav', $this->container)
->withAttribute('time', $_SERVER['REQUEST_TIME_FLOAT'] ?? GRAV_REQUEST_TIME)
->withAttribute('route', Uri::getCurrentRoute())
->withAttribute('route', Uri::getCurrentRoute()->withExtension($ext))
->withAttribute('referrer', $this->container['uri']->referrer());
$event = new RequestHandlerEvent(['request' => $request, 'handler' => $handler]);

View File

@@ -31,8 +31,8 @@ class RequestServiceProvider implements ServiceProviderInterface
return $creator->fromGlobals();
};
$container['route'] = function() {
return Uri::getCurrentRoute();
};
$container['route'] = $container->factory(function() {
return clone Uri::getCurrentRoute();
});
}
}

View File

@@ -13,6 +13,7 @@ use Grav\Common\Config\Config;
use Grav\Common\Debugger;
use Grav\Common\Session;
use Grav\Common\Uri;
use Grav\Common\Utils;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use RocketTheme\Toolbox\Session\Message;
@@ -55,8 +56,7 @@ class SessionServiceProvider implements ServiceProviderInterface
$current_route = str_replace(Uri::filterPath($uri->rootUrl(false)), '', parse_url($uri->url(true), PHP_URL_PATH));
// Check no language, simple language prefix (en) and region specific language prefix (en-US).
$pos = strpos($current_route, $base);
if ($pos === 0 || $pos === 3 || $pos === 6) {
if (Utils::startsWith($current_route, $base) || Utils::pathPrefixedByLangCode($current_route)) {
$cookie_lifetime = $config->get('plugins.admin.session.timeout', 1800);
$enabled = $is_admin = true;
}

View File

@@ -447,7 +447,11 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
*/
public function containsFilter($haystack, $needle)
{
return (strpos($haystack, $needle) !== false);
if (empty($needle)) {
return $haystack;
}
return (strpos($haystack, (string) $needle) !== false);
}
/**

View File

@@ -144,7 +144,7 @@ class Uri
} else {
$this->root = $this->base . $this->root_path;
}
$this->uri = Utils::replaceFirstOccurrence($orig_root_path, $this->root_path, $this->uri);
$this->uri = Utils::replaceFirstOccurrence($orig_root_path, $this->root_path, $this->uri);
} else {
$this->root = $this->base . $this->root_path;
}
@@ -187,11 +187,9 @@ class Uri
$this->extension = $parts['extension'];
}
$valid_page_types = implode('|', $config->get('system.pages.types'));
// Strip the file extension for valid page types
if (preg_match('/\.(' . $valid_page_types . ')$/', $parts['basename'])) {
$path = rtrim(str_replace(DIRECTORY_SEPARATOR, DS, $parts['dirname']), DS) . '/' . $parts['filename'];
if ($this->isValidExtension($this->extension)) {
$path = Utils::replaceLastOccurrence(".{$this->extension}", '', $path);
}
// set the new url
@@ -214,7 +212,7 @@ class Uri
/**
* Return URI path.
*
* @param string $id
* @param string $id
*
* @return string|string[]
*/
@@ -230,8 +228,8 @@ class Uri
/**
* Return route to the current URI. By default route doesn't include base path.
*
* @param bool $absolute True to include full path.
* @param bool $domain True to include domain. Works only if first parameter is also true.
* @param bool $absolute True to include full path.
* @param bool $domain True to include domain. Works only if first parameter is also true.
*
* @return string
*/
@@ -243,8 +241,8 @@ class Uri
/**
* Return full query string or a single query attribute.
*
* @param string $id Optional attribute. Get a single query attribute if set
* @param bool $raw If true and $id is not set, return the full query array. Otherwise return the query string
* @param string $id Optional attribute. Get a single query attribute if set
* @param bool $raw If true and $id is not set, return the full query array. Otherwise return the query string
*
* @return string|array Returns an array if $id = null and $raw = true
*/
@@ -268,8 +266,8 @@ class Uri
/**
* Return all or a single query parameter as a URI compatible string.
*
* @param string $id Optional parameter name.
* @param boolean $array return the array format or not
* @param string $id Optional parameter name.
* @param boolean $array return the array format or not
*
* @return null|string|array
*/
@@ -301,7 +299,7 @@ class Uri
/**
* Get URI parameter.
*
* @param string $id
* @param string $id
*
* @return bool|string
*/
@@ -332,7 +330,7 @@ class Uri
/**
* Return URL.
*
* @param bool $include_host Include hostname.
* @param bool $include_host Include hostname.
*
* @return string
*/
@@ -523,7 +521,7 @@ class Uri
/**
* Return root URL to the site.
*
* @param bool $include_host Include hostname.
* @param bool $include_host Include hostname.
*
* @return mixed
*/
@@ -584,15 +582,28 @@ class Uri
return static::buildUrl($this->toArray());
}
public function toArray()
public function toOriginalString()
{
return static::buildUrl($this->toArray(true));
}
public function toArray($full = false)
{
if ($full === true) {
$root_path = $this->root_path ?? '';
$extension = isset($this->extension) && $this->isValidExtension($this->extension) ? '.' . $this->extension : '';
$path = $root_path . $this->path . $extension;
} else {
$path = $this->path;
}
return [
'scheme' => $this->scheme,
'host' => $this->host,
'port' => $this->port,
'user' => $this->user,
'pass' => $this->password,
'path' => $this->path,
'path' => $path,
'params' => $this->params,
'query' => $this->query,
'fragment' => $this->fragment
@@ -1143,7 +1154,7 @@ class Uri
$this->scheme = $env['X-FORWARDED-PROTO'];
} elseif (isset($env['HTTP_CLOUDFRONT_FORWARDED_PROTO'])) {
$this->scheme = $env['HTTP_CLOUDFRONT_FORWARDED_PROTO'];
} elseif (isset($env['REQUEST_SCHEME'])) {
} elseif (isset($env['REQUEST_SCHEME']) && empty($env['HTTPS'])) {
$this->scheme = $env['REQUEST_SCHEME'];
} else {
$https = $env['HTTPS'] ?? '';
@@ -1326,6 +1337,38 @@ class Uri
return null;
}
/**
* Check if this is a valid Grav extension
*
* @param $extension
* @return bool
*/
public function isValidExtension($extension)
{
$valid_page_types = implode('|', Grav::instance()['config']->get('system.pages.types'));
// Strip the file extension for valid page types
if (preg_match('/(' . $valid_page_types . ')/', $extension)) {
return true;
}
return false;
}
/**
* Allow overriding of any element (be careful!)
*
* @param $data
* @return Uri
*/
public function setUriProperties($data)
{
foreach (get_object_vars($this) as $property => $default) {
if (!array_key_exists($property, $data)) continue;
$this->{$property} = $data[$property]; // assign value to object
}
return $this;
}
/**
* Get the base URI with port if needed
*

View File

@@ -118,4 +118,13 @@ class UserCollection implements UserCollectionInterface
return $file_path && unlink($file_path);
}
public function count(): int
{
// check for existence of a user account
$account_dir = $file_path = Grav::instance()['locator']->findResource('account://');
$accounts = glob($account_dir . '/*.yaml') ?: [];
return count($accounts);
}
}

View File

@@ -9,7 +9,7 @@
namespace Grav\Common\User\Interfaces;
interface UserCollectionInterface
interface UserCollectionInterface extends \Countable
{
/**
* Load user account.

View File

@@ -148,12 +148,13 @@ trait UserTrait
// Try looking for provider.
$provider = $this->get('provider');
if (\is_array($provider)) {
if (isset($provider['avatar_url']) && \is_string($provider['avatar_url'])) {
return $provider['avatar_url'];
$provider_options = $this->get($provider);
if (\is_array($provider_options)) {
if (isset($provider_options['avatar_url']) && \is_string($provider_options['avatar_url'])) {
return $provider_options['avatar_url'];
}
if (isset($provider['avatar']) && \is_string($provider['avatar'])) {
return $provider['avatar'];
if (isset($provider_options['avatar']) && \is_string($provider_options['avatar'])) {
return $provider_options['avatar'];
}
}

View File

@@ -20,7 +20,9 @@ abstract class Utils
{
protected static $nonces = [];
protected const ROOTURL_REGEX = '{^(\/*)}';
protected const ROOTURL_REGEX = '{^((?:http[s]?:\/\/[^\/]+)|(?:\/\/[^\/]+))(.*)}';
// ^((?:http[s]?:)?[\/]?(?:\/))
/**
* Simple helper method to make getting a Grav URL easier
@@ -784,26 +786,46 @@ abstract class Utils
*/
public static function normalizePath($path)
{
// Resolve any streams
/** @var UniformResourceLocator $locator */
$locator = Grav::instance()['locator'];
if ($locator->isStream($path)) {
$path = $locator->findResource($path);
}
// Set root properly for any URLs
$root = '';
preg_match(self::ROOTURL_REGEX, $path, $matches);
if ($matches) {
$root = $matches[0];
$root = $matches[1];
$path = $matches[2];
}
$clean_path = static::replaceFirstOccurrence($root, '', $path);
$segments = explode('/', trim($clean_path, '/'));
$ret = [];
foreach ($segments as $segment) {
if (($segment === '.') || $segment === '') {
continue;
}
if ($segment === '..') {
array_pop($ret);
} else {
$ret[] = $segment;
}
// Strip off leading / to ensure explode is accurate
if (Utils::startsWith($path,'/')) {
$root .= '/';
$path = ltrim($path, '/');
}
$normalized = $root . implode('/', $ret);
// If there are any relative paths (..) handle those
if (Utils::contains($path, '..')) {
$segments = explode('/', trim($path, '/'));
$ret = [];
foreach ($segments as $segment) {
if (($segment === '.') || $segment === '') {
continue;
}
if ($segment === '..') {
array_pop($ret);
} else {
$ret[] = $segment;
}
}
$path = implode('/', $ret);
}
// Stick everything back together
$normalized = $root . $path;
return $normalized;
}
@@ -953,6 +975,7 @@ abstract class Utils
* @param string $string The path
*
* @return bool
*
*/
public static function pathPrefixedByLangCode($string)
{
@@ -961,8 +984,13 @@ abstract class Utils
}
$languages_enabled = Grav::instance()['config']->get('system.languages.supported', []);
$parts = explode('/', trim($string, '/'));
return $string[0] === '/' && $string[3] === '/' && \in_array(substr($string, 1, 2), $languages_enabled, true);
if (count($parts) > 0 && in_array($parts[0], $languages_enabled)) {
return true;
}
return false;
}
/**
@@ -1319,6 +1347,8 @@ abstract class Utils
$post_max_size = static::parseSize(ini_get('post_max_size'));
if ($post_max_size > 0) {
$max_size = $post_max_size;
} else {
$max_size = 0;
}
$upload_max = static::parseSize(ini_get('upload_max_filesize'));
@@ -1382,11 +1412,12 @@ abstract class Utils
{
$unit = preg_replace('/[^bkmgtpezy]/i', '', $size);
$size = preg_replace('/[^0-9\.]/', '', $size);
if ($unit) {
return (int)((int)$size * (1024 ** stripos('bkmgtpezy', $unit[0])));
$size = $size * pow(1024, stripos('bkmgtpezy', $unit[0]));
}
return (int)$size;
return (int) abs(round($size));
}
/**
@@ -1447,4 +1478,44 @@ abstract class Utils
return $string;
}
/**
* Find the subnet of an ip with CIDR prefix size
*
* @param string $ip
* @param int $prefix
*
* @return string
* @throws \InvalidArgumentException if provided an invalid IP
*/
public static function getSubnet($ip, $prefix = 64)
{
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
throw new \InvalidArgumentException('Invalid IP: ' . $ip);
}
// Packed representation of IP
$ip = inet_pton($ip);
// Maximum netmask length = same as packed address
$len = 8*strlen($ip);
if ($prefix > $len) $prefix = $len;
$mask = str_repeat('f', $prefix>>2);
switch($prefix & 3)
{
case 3: $mask .= 'e'; break;
case 2: $mask .= 'c'; break;
case 1: $mask .= '8'; break;
}
$mask = str_pad($mask, $len>>2, '0');
// Packed representation of netmask
$mask = pack('H*', $mask);
// Bitwise - Take all bits that are both 1 to generate subnet
$subnet = inet_ntop($ip & $mask);
return $subnet;
}
}

View File

@@ -72,9 +72,6 @@ class CleanCommand extends Command
'vendor/doctrine/collections/composer.json',
'vendor/doctrine/collections/phpunit.xml.dist',
'vendor/doctrine/collections/tests',
'vendor/dragonmantank/cron-expression/.editorconfig',
'vendor/dragonmantank/cron-expression/composer.json',
'vendor/dragonmantank/cron-expression/tests',
'vendor/donatj/phpuseragentparser/.git',
'vendor/donatj/phpuseragentparser/.gitignore',
'vendor/donatj/phpuseragentparser/.editorconfig',
@@ -83,6 +80,9 @@ class CleanCommand extends Command
'vendor/donatj/phpuseragentparser/phpunit.xml.dist',
'vendor/donatj/phpuseragentparser/Tests',
'vendor/donatj/phpuseragentparser/Tools',
'vendor/dragonmantank/cron-expression/.editorconfig',
'vendor/dragonmantank/cron-expression/composer.json',
'vendor/dragonmantank/cron-expression/tests',
'vendor/erusev/parsedown/composer.json',
'vendor/erusev/parsedown/phpunit.xml.dist',
'vendor/erusev/parsedown/.travis.yml',
@@ -123,6 +123,8 @@ class CleanCommand extends Command
'vendor/gregwar/cache/Gregwar/Cache/demo',
'vendor/gregwar/cache/Gregwar/Cache/tests',
'vendor/guzzlehttp/psr7/composer.json',
'vendor/guzzlehttp/psr7/.editorconfig',
'vendor/kodus/psr7-server/composer.json',
'vendor/ircmaxell/password-compat/composer.json',
'vendor/ircmaxell/password-compat/phpunit.xml.dist',
'vendor/ircmaxell/password-compat/version-test.php',
@@ -158,7 +160,7 @@ class CleanCommand extends Command
'vendor/monolog/monolog/.php_cs',
'vendor/monolog/monolog/tests',
'vendor/nyholm/psr7/composer.json',
'vendor/nyholm/psr7-server/composer.json',
'vendor/nyholm/psr7/phpstan.neon.dist',
'vendor/phive/twig-extensions-deferred/.gitignore',
'vendor/phive/twig-extensions-deferred/.travis.yml',
'vendor/phive/twig-extensions-deferred/composer.json',
@@ -184,6 +186,11 @@ class CleanCommand extends Command
'vendor/psr/simple-cache/composer.json',
'vendor/psr/log/composer.json',
'vendor/psr/log/.gitignore',
'vendor/ralouphie/getallheaders/.gitignore',
'vendor/ralouphie/getallheaders/.travis.yml',
'vendor/ralouphie/getallheaders/composer.json',
'vendor/ralouphie/getallheaders/phpunit.xml',
'vendor/ralouphie/getallheaders/tests/',
'vendor/rockettheme/toolbox/.git',
'vendor/rockettheme/toolbox/.gitignore',
'vendor/rockettheme/toolbox/.scrutinizer.yml',
@@ -209,12 +216,15 @@ class CleanCommand extends Command
'vendor/symfony/event-dispatcher/composer.json',
'vendor/symfony/event-dispatcher/phpunit.xml.dist',
'vendor/symfony/event-dispatcher/Tests',
'vendor/symfony/polyfill-ctype/composer.json',
'vendor/symfony/polyfill-iconv/.git',
'vendor/symfony/polyfill-iconv/.gitignore',
'vendor/symfony/polyfill-iconv/composer.json',
'vendor/symfony/polyfill-mbstring/.git',
'vendor/symfony/polyfill-mbstring/.gitignore',
'vendor/symfony/polyfill-mbstring/composer.json',
'vendor/symfony/polyfill-php72/composer.json',
'vendor/symfony/polyfill-php73/composer.json',
'vendor/symfony/process/.gitignore',
'vendor/symfony/process/composer.json',
'vendor/symfony/process/phpunit.xml.dist',
@@ -246,6 +256,8 @@ class CleanCommand extends Command
'vendor/willdurand/negotiation/composer.json',
'vendor/willdurand/negotiation/phpunit.xml.dist',
'vendor/willdurand/negotiation/tests',
'user/config/security.yaml',
'cache/compiled/',
];
protected function configure()

View File

@@ -13,7 +13,7 @@ use Grav\Common\Cache;
use Grav\Console\ConsoleCommand;
use Symfony\Component\Console\Input\InputOption;
class CacheCommand extends ConsoleCommand
class ClearCacheCommand extends ConsoleCommand
{
protected function configure()
{
@@ -21,6 +21,7 @@ class CacheCommand extends ConsoleCommand
->setName('cache')
->setAliases(['clearcache', 'cache-clear'])
->setDescription('Clears Grav cache')
->addOption('invalidate', null, InputOption::VALUE_NONE, 'Invalidate cache, but do not remove any files')
->addOption('purge', null, InputOption::VALUE_NONE, 'If set purge old caches')
->addOption('all', null, InputOption::VALUE_NONE, 'If set will remove all including compiled, twig, doctrine caches')
->addOption('assets-only', null, InputOption::VALUE_NONE, 'If set will remove only assets/*')
@@ -64,6 +65,8 @@ class CacheCommand extends ConsoleCommand
$remove = 'cache-only';
} elseif ($this->input->getOption('tmp-only')) {
$remove = 'tmp-only';
} elseif ($this->input->getOption('invalidate')) {
$remove = 'invalidate';
} else {
$remove = 'standard';
}

View File

@@ -0,0 +1,73 @@
<?php
/**
* @package Grav\Console\Cli
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Console\Cli;
use Grav\Common\Grav;
use Grav\Common\Helpers\LogViewer;
use Grav\Common\Helpers\YamlLinter;
use Grav\Common\Utils;
use Grav\Console\ConsoleCommand;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Style\SymfonyStyle;
class YamlLinterCommand extends ConsoleCommand
{
protected function configure()
{
$this
->setName('yamllinter')
->addOption(
'env',
'e',
InputOption::VALUE_OPTIONAL,
'The environment to trigger a specific configuration. For example: localhost, mysite.dev, www.mysite.com'
)
->setDescription('Checks various files for YAML errors')
->setHelp("Checks various files for YAML errors");
}
protected function serve()
{
$grav = Grav::instance();
$grav->setup();
$io = new SymfonyStyle($this->input, $this->output);
$io->title('Yaml Linter');
$io->section('User Configuration');
$errors = YamlLinter::lintConfig();
if (empty($errors)) {
$io->success('No YAML Linting issues with configuration');
} else {
$this->displayErrors($errors, $io);
}
$io->section('Pages Frontmatter');
$errors = YamlLinter::lintPages();
if (empty($errors)) {
$io->success('No YAML Linting issues with pages');
} else {
$this->displayErrors($errors, $io);
}
}
protected function displayErrors($errors, $io)
{
$io->error("YAML Linting issues found...");
foreach ($errors as $path => $error) {
$io->writeln("<yellow>$path</yellow> - $error");
}
}
}

View File

@@ -13,7 +13,7 @@ use Grav\Common\Cache;
use Grav\Common\Grav;
use Grav\Common\Composer;
use Grav\Common\GravTrait;
use Grav\Console\Cli\CacheCommand;
use Grav\Console\Cli\ClearCacheCommand;
use RocketTheme\Toolbox\File\YamlFile;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\ArrayInput;
@@ -107,7 +107,7 @@ trait ConsoleTrait
$all = ['--all' => true];
}
$command = new CacheCommand();
$command = new ClearCacheCommand();
$input = new ArrayInput($all);
return $command->run($input, $this->output);
}

View File

@@ -114,7 +114,7 @@ class InfoCommand extends ConsoleCommand
if ($info === 'date') {
$name = 'Last Update';
$data = date('D, j M Y, H:i:s, P ', strtotime('2014-09-16T00:07:16Z'));
$data = date('D, j M Y, H:i:s, P ', strtotime($data));
}
$name = str_pad($name, 12);

View File

@@ -102,7 +102,7 @@ class YamlFormatter extends AbstractFormatter
}
try {
return YamlParser::parse($data);
return (array) YamlParser::parse($data);
} catch (ParseException $e) {
if ($this->useCompatibleDecoder()) {
return (array) FallbackYamlParser::parse($data);

View File

@@ -91,7 +91,7 @@ class Flex implements \Countable
// Return the directories in the given order.
$directories = [];
foreach ($types as $type) {
$directories = $this->types[$type] ?? null;
$directories[$type] = $this->types[$type] ?? null;
}
return $keepMissing ? $directories : array_filter($directories);
@@ -168,6 +168,11 @@ class Flex implements \Countable
$keyFieldFind = 'storage_key';
foreach ($keys as $flexKey) {
if (!$flexKey) {
continue;
}
$flexKey = (string)$flexKey;
// Normalize key and type using fallback to default type if it was set.
[$key, $type, $guess] = $this->resolveKeyAndType($flexKey, $defaultType);

View File

@@ -123,6 +123,22 @@ class FlexCollection extends ObjectCollection implements FlexCollectionInterface
return $matching;
}
/**
* @param array $filters
* @return FlexCollectionInterface
*/
public function filterBy(array $filters)
{
$expr = Criteria::expr();
$criteria = Criteria::create();
foreach ($filters as $key => $value) {
$criteria->andWhere($expr->eq($key, $value));
}
return $this->matching($criteria);
}
/**
* {@inheritdoc}
* @see FlexCollectionInterface::getFlexType()
@@ -301,7 +317,7 @@ class FlexCollection extends ObjectCollection implements FlexCollectionInterface
]));
$output = $this->getTemplate($layout)->render(
['grav' => $grav, 'block' => $block, 'collection' => $this, 'layout' => $layout] + $context
['grav' => $grav, 'config' => $grav['config'], 'block' => $block, 'collection' => $this, 'layout' => $layout] + $context
);
if ($debugger->enabled()) {
@@ -474,7 +490,12 @@ class FlexCollection extends ObjectCollection implements FlexCollectionInterface
$twig = $grav['twig'];
try {
return $twig->twig()->resolveTemplate(["flex-objects/layouts/{$this->getFlexType()}/collection/{$layout}.html.twig"]);
return $twig->twig()->resolveTemplate(
[
"flex-objects/layouts/{$this->getFlexType()}/collection/{$layout}.html.twig",
"flex-objects/layouts/_default/collection/{$layout}.html.twig"
]
);
} catch (LoaderError $e) {
/** @var Debugger $debugger */
$debugger = Grav::instance()['debugger'];

View File

@@ -12,11 +12,15 @@ namespace Grav\Framework\Flex;
use Grav\Common\Data\Blueprint;
use Grav\Common\Data\Data;
use Grav\Common\Grav;
use Grav\Common\Twig\Twig;
use Grav\Common\Utils;
use Grav\Framework\Flex\Interfaces\FlexFormInterface;
use Grav\Framework\Flex\Interfaces\FlexObjectInterface;
use Grav\Framework\Form\Traits\FormTrait;
use Grav\Framework\Route\Route;
use Twig\Error\LoaderError;
use Twig\Error\SyntaxError;
use Twig\TemplateWrapper;
/**
* Class FlexForm
@@ -45,11 +49,12 @@ class FlexForm implements FlexFormInterface
{
$this->name = $name;
$this->form = $form;
$uniqueId = $object->exists() ? $object->getStorageKey() : "{$object->getFlexType()}:new";
$this->setObject($object);
$this->setId($this->getName());
$this->setUniqueId(md5($uniqueId));
$this->errors = [];
$this->messages = [];
$this->submitted = false;
$flash = $this->getFlash();
@@ -221,6 +226,42 @@ class FlexForm implements FlexFormInterface
$this->doUnserialize($data);
}
public function __get($name)
{
$method = "get{$name}";
if (method_exists($this, $method)) {
return $this->{$method}();
}
$form = $this->getBlueprint()->form();
return $form[$name] ?? null;
}
public function __set($name, $value)
{
$method = "set{$name}";
if (method_exists($this, $method)) {
$this->{$method}($value);
}
}
public function __isset($name)
{
$method = "get{$name}";
if (method_exists($this, $method)) {
return true;
}
$form = $this->getBlueprint()->form();
return isset($form[$name]);
}
public function __unset($name)
{
}
/**
* Note: this method clones the object.
*
@@ -234,6 +275,29 @@ class FlexForm implements FlexFormInterface
return $this;
}
/**
* @param string $layout
* @return TemplateWrapper
* @throws LoaderError
* @throws SyntaxError
*/
protected function getTemplate($layout)
{
$grav = Grav::instance();
/** @var Twig $twig */
$twig = $grav['twig'];
return $twig->twig()->resolveTemplate(
[
"flex-objects/layouts/{$this->getFlexType()}/form/{$layout}.html.twig",
"flex-objects/layouts/_default/form/{$layout}.html.twig",
"forms/{$layout}/form.html.twig",
'forms/default/form.html.twig'
]
);
}
/**
* @param array $data
* @param array $files
@@ -243,6 +307,7 @@ class FlexForm implements FlexFormInterface
{
/** @var FlexObject $object */
$object = clone $this->getObject();
$object->update($data, $files);
$object->save();

View File

@@ -98,6 +98,16 @@ class FlexIndex extends ObjectIndex implements FlexCollectionInterface, FlexInde
return $this->orderBy($orderings);
}
/**
* {@inheritdoc}
* @see FlexCollectionInterface::filterBy()
*/
public function filterBy(array $filters)
{
return $this->__call('filterBy', [$filters]);
}
/**
* {@inheritdoc}
* @see FlexCollectionInterface::getFlexType()
@@ -386,10 +396,6 @@ class FlexIndex extends ObjectIndex implements FlexCollectionInterface, FlexInde
$cached = $result;
}
if ($cached === null) {
throw new \RuntimeException('Flex: Internal error');
}
$cache->set($key, $cached);
} catch (InvalidArgumentException $e) {
$debugger->addException($e);

View File

@@ -162,6 +162,13 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface
{
$options = $options ?? $this->getFlexDirectory()->getConfig('data.search.options', []);
$properties = $properties ?? $this->getFlexDirectory()->getConfig('data.search.fields', []);
if (!$properties) {
foreach ($this->getFlexDirectory()->getConfig('admin.list.fields', []) as $property => $value) {
if (!empty($value['link'])) {
$properties[] = $property;
}
}
}
$weight = 0;
foreach ((array)$properties as $property) {
@@ -273,7 +280,7 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface
return (float)$options['ends_with'];
}
if ((!$tested || !empty($options['contains'])) && Utils::contains($value, $search, $options['case_sensitive'] ?? false)) {
return (float)$options['contains'];
return (float)($options['contains'] ?? 1);
}
return 0;
@@ -416,7 +423,7 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface
]));
$output = $this->getTemplate($layout)->render(
['grav' => $grav, 'block' => $block, 'object' => $this, 'layout' => $layout] + $context
['grav' => $grav, 'config' => $grav['config'], 'block' => $block, 'object' => $this, 'layout' => $layout] + $context
);
if ($debugger->enabled()) {
@@ -730,8 +737,8 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface
$grav = Grav::instance();
/** @var Flex $flex */
$flex = $grav['flex_directory'];
$directory = $flex->getDirectory($type);
$flex = $grav['flex_objects'] ?? null;
$directory = $flex ? $flex->getDirectory($type) : null;
if (!$directory) {
throw new \InvalidArgumentException("Cannot unserialize '{$type}': Not found");
}
@@ -812,7 +819,12 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface
$twig = $grav['twig'];
try {
return $twig->twig()->resolveTemplate(["flex-objects/layouts/{$this->getFlexType()}/object/{$layout}.html.twig"]);
return $twig->twig()->resolveTemplate(
[
"flex-objects/layouts/{$this->getFlexType()}/object/{$layout}.html.twig",
"flex-objects/layouts/_default/object/{$layout}.html.twig"
]
);
} catch (LoaderError $e) {
/** @var Debugger $debugger */
$debugger = Grav::instance()['debugger'];

View File

@@ -70,6 +70,14 @@ interface FlexCollectionInterface extends FlexCommonInterface, ObjectCollectionI
*/
public function sort(array $orderings);
/**
* Filter collection by filter array with keys and values.
*
* @param array $filters
* @return FlexCollectionInterface
*/
public function filterBy(array $filters);
/**
* Get timestamps from all the objects in the collection.
*

View File

@@ -71,7 +71,7 @@ class FolderStorage extends AbstractFilesystemStorage
*/
public function hasKey(string $key): bool
{
return $key && !strpos($key, '@@') && file_exists($this->getPathFromKey($key));
return $key && strpos($key, '@@') === false && file_exists($this->getPathFromKey($key));
}
/**

View File

@@ -71,7 +71,11 @@ class SimpleStorage extends AbstractFilesystemStorage
*/
public function hasKey(string $key): bool
{
return $key && !strpos($key, '@@') && isset($this->data[$key]);
if (null === $this->data) {
$this->buildIndex();
}
return $key && strpos($key, '@@') === false && isset($this->data[$key]);
}
/**
@@ -80,6 +84,10 @@ class SimpleStorage extends AbstractFilesystemStorage
*/
public function createRows(array $rows): array
{
if (null === $this->data) {
$this->buildIndex();
}
$list = [];
foreach ($rows as $key => $row) {
$key = $this->getNewKey();
@@ -99,6 +107,10 @@ class SimpleStorage extends AbstractFilesystemStorage
*/
public function readRows(array $rows, array &$fetched = null): array
{
if (null === $this->data) {
$this->buildIndex();
}
$list = [];
foreach ($rows as $key => $row) {
if (null === $row || (!\is_object($row) && !\is_array($row))) {
@@ -123,6 +135,10 @@ class SimpleStorage extends AbstractFilesystemStorage
*/
public function updateRows(array $rows): array
{
if (null === $this->data) {
$this->buildIndex();
}
$list = [];
foreach ($rows as $key => $row) {
$key = (string)$key;
@@ -144,6 +160,10 @@ class SimpleStorage extends AbstractFilesystemStorage
*/
public function deleteRows(array $rows): array
{
if (null === $this->data) {
$this->buildIndex();
}
$list = [];
foreach ($rows as $key => $row) {
$key = (string)$key;
@@ -166,8 +186,15 @@ class SimpleStorage extends AbstractFilesystemStorage
*/
public function replaceRows(array $rows): array
{
if (null === $this->data) {
$this->buildIndex();
}
$list = [];
foreach ($rows as $key => $row) {
if (strpos($key, '@@')) {
$key = $this->getNewKey();
}
$this->data[$key] = $list[$key] = $row;
}
@@ -184,6 +211,10 @@ class SimpleStorage extends AbstractFilesystemStorage
*/
public function renameRow(string $src, string $dst): bool
{
if (null === $this->data) {
$this->buildIndex();
}
if ($this->hasKey($dst)) {
throw new \RuntimeException("Cannot rename object: key '{$dst}' is already taken");
}
@@ -221,6 +252,10 @@ class SimpleStorage extends AbstractFilesystemStorage
protected function save() : void
{
if (null === $this->data) {
$this->buildIndex();
}
try {
$file = $this->getFile($this->getStoragePath());
$file->save($this->data);

View File

@@ -13,6 +13,7 @@ namespace Grav\Framework\Flex\Traits;
use Grav\Common\Grav;
use Grav\Common\User\Interfaces\UserInterface;
use Grav\Framework\Flex\FlexDirectory;
use Grav\Framework\Flex\Interfaces\FlexObjectInterface;
/**
@@ -44,7 +45,11 @@ trait FlexAuthorizeTrait
$action = $this->exists() ? 'update' : 'create';
}
return $user->authorize(sprintf($this->_authorize, $scope, $action));
$directory = $this instanceof FlexDirectory ? $this : $this->getFlexDirectory();
$config = $directory->getConfig();
$allowed = $config->get("{$scope}.actions.{$action}") ?? $config->get("actions.{$action}") ?? true;
return $allowed && $user->authorize(sprintf($this->_authorize, $scope, $action));
}
protected function setAuthorizeRule(string $authorize) : void

View File

@@ -229,7 +229,6 @@ trait FlexMediaTrait
}
// Remove Extra Files
foreach (scandir($targetPath, SCANDIR_SORT_NONE) as $file) {
$preg_name = preg_quote($fileParts['filename'], '`');
$preg_ext =preg_quote($fileParts['extension'], '`');

View File

@@ -11,6 +11,7 @@ namespace Grav\Framework\Form\Interfaces;
use Grav\Common\Data\Blueprint;
use Grav\Common\Data\Data;
use Grav\Framework\Interfaces\RenderInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\UploadedFileInterface;
@@ -18,7 +19,7 @@ use Psr\Http\Message\UploadedFileInterface;
* Interface FormInterface
* @package Grav\Framework\Form
*/
interface FormInterface extends \Serializable
interface FormInterface extends RenderInterface, \Serializable
{
/**
* Get HTML id="..." attribute.
@@ -83,6 +84,13 @@ interface FormInterface extends \Serializable
*/
public function getNonce(): string;
/**
* Get task for the form if set in blueprints.
*
* @return string
*/
public function getTask(): string;
/**
* Get form action (URL). If action is empty, it points to the current page.
*
@@ -132,6 +140,11 @@ interface FormInterface extends \Serializable
*/
public function isValid(): bool;
/**
* @return string
*/
public function getError(): ?string;
/**
* @return array
*/

View File

@@ -14,11 +14,15 @@ use Grav\Common\Data\Data;
use Grav\Common\Data\ValidationException;
use Grav\Common\Form\FormFlash;
use Grav\Common\Grav;
use Grav\Common\Twig\Twig;
use Grav\Common\Utils;
use Grav\Framework\ContentBlock\HtmlBlock;
use Grav\Framework\Form\Interfaces\FormInterface;
use Grav\Framework\Session\Session;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\UploadedFileInterface;
use Twig\Error\LoaderError;
use Twig\Error\SyntaxError;
use Twig\TemplateWrapper;
/**
* Trait FormTrait
@@ -26,6 +30,13 @@ use Psr\Http\Message\UploadedFileInterface;
*/
trait FormTrait
{
/** @var string */
public $status = 'success';
/** @var string */
public $message;
/** @var string[] */
public $messages = [];
/** @var string */
private $name;
/** @var string */
@@ -34,8 +45,6 @@ trait FormTrait
private $uniqueid;
/** @var bool */
private $submitted;
/** @var string[] */
private $errors;
/** @var Data|object|null */
private $data;
/** @var array|UploadedFileInterface[] */
@@ -95,6 +104,11 @@ trait FormTrait
return '';
}
public function getTask(): string
{
return $this->getBlueprint()->get('form/task') ?? '';
}
public function getData(string $name = null)
{
return null !== $name ? $this->data[$name] : $this->data;
@@ -155,12 +169,24 @@ trait FormTrait
*/
public function handleRequest(ServerRequestInterface $request): FormInterface
{
// Set current form to be active.
$grav = Grav::instance();
$forms = $grav['forms'] ?? null;
if ($forms) {
$forms->setActiveForm($this);
/** @var Twig $twig */
$twig = $grav['twig'];
$twig->twig_vars['form'] = $this;
}
try {
[$data, $files] = $this->parseRequest($request);
$this->submit($data, $files);
} catch (\Exception $e) {
$this->errors[] = $e->getMessage();
$this->setError($e->getMessage());
}
return $this;
@@ -182,12 +208,17 @@ trait FormTrait
public function isValid(): bool
{
return !$this->errors;
return $this->status === 'success';
}
public function getError(): ?string
{
return !$this->isValid() ? $this->message : null;
}
public function getErrors(): array
{
return $this->errors;
return !$this->isValid() ? $this->messages : [];
}
public function isSubmitted(): bool
@@ -197,7 +228,7 @@ trait FormTrait
public function validate(): bool
{
if ($this->errors) {
if (!$this->isValid()) {
return false;
}
@@ -205,19 +236,14 @@ trait FormTrait
$this->validateData($this->data);
$this->validateUploads($this->getFiles());
} catch (ValidationException $e) {
$list = [];
foreach ($e->getMessages() as $field => $errors) {
$list[] = $errors;
}
$list = array_merge(...$list);
$this->errors = $list;
$this->setErrors($e->getMessages());
} catch (\Exception $e) {
$this->errors[] = $e->getMessage();
$this->setError($e->getMessage());
}
$this->filterData($this->data);
return empty($this->errors);
return $this->isValid();
}
/**
@@ -243,7 +269,7 @@ trait FormTrait
$this->submitted = true;
} catch (\Exception $e) {
$this->errors[] = $e->getMessage();
$this->setError($e->getMessage());
}
return $this;
@@ -256,7 +282,9 @@ trait FormTrait
$this->data = null;
$this->files = [];
$this->errors = [];
$this->status = 'success';
$this->message = null;
$this->messages = [];
$this->submitted = false;
$this->flash = null;
}
@@ -301,7 +329,6 @@ trait FormTrait
}
/**
* Get form flash object.
*
* @return FormFlash
@@ -309,44 +336,64 @@ trait FormTrait
public function getFlash(): FormFlash
{
if (null === $this->flash) {
/** @var Grav $grav */
$grav = Grav::instance();
$user = $grav['user'];
$id = null;
$rememberState = $this->getBlueprint()->get('form/remember_state');
if ($rememberState === 'user') {
$id = $user->username;
$user = $grav['user'] ?? null;
if (isset($user)) {
$rememberState = $this->getBlueprint()->get('form/remember_state');
if ($rememberState === 'user') {
$id = $user->username;
}
}
// By default store flash by the session id.
if (null === $id) {
/** @var Session $session */
$session = $grav['session'];
$id = $session->getId();
}
// 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();
}
$this->flash = new FormFlash($id, $this->getUniqueId(), $this->getName());
$this->flash->setUrl($grav['uri']->url)->setUser($user);
$this->flash = new FormFlash($id, $this->getUniqueId(), $this->getName());
$this->flash->setUrl($grav['uri']->url)->setUser($user);
}
}
return $this->flash;
}
/**
* {@inheritdoc}
* @see FormInterface::render()
*/
public function render(string $layout = null, array $context = [])
{
if (null === $layout) {
$layout = 'default';
}
$grav = Grav::instance();
$block = HtmlBlock::create();
$block->disableCache();
$output = $this->getTemplate($layout)->render(
['grav' => $grav, 'config' => $grav['config'], 'block' => $block, 'form' => $this, 'layout' => $layout] + $context
);
$block->setContent($output);
return $block;
}
protected function unsetFlash(): void
{
$this->flash = null;
}
/**
* Set all errors.
*
* @param array $errors
*/
protected function setErrors(array $errors): void
{
$this->errors = array_merge($this->errors, $errors);
}
/**
* Set a single error.
*
@@ -354,7 +401,40 @@ trait FormTrait
*/
protected function setError(string $error): void
{
$this->errors[] = $error;
$this->status = 'error';
$this->message = $error;
}
/**
* Set all errors.
*
* @param array $errors
*/
protected function setErrors(array $errors): void
{
$this->status = 'error';
$this->messages = $errors;
}
/**
* @param string $layout
* @return TemplateWrapper
* @throws LoaderError
* @throws SyntaxError
*/
protected function getTemplate($layout)
{
$grav = Grav::instance();
/** @var Twig $twig */
$twig = $grav['twig'];
return $twig->twig()->resolveTemplate(
[
"forms/{$layout}/form.html.twig",
'forms/default/form.html.twig'
]
);
}
/**
@@ -503,7 +583,7 @@ trait FormTrait
$value = json_decode($value, true);
if ($value === null && json_last_error() !== JSON_ERROR_NONE) {
unset($data[$key]);
$this->errors[] = "Badly encoded JSON data (for {$key}) was sent to the form";
$this->setError("Badly encoded JSON data (for {$key}) was sent to the form");
}
}
}
@@ -523,7 +603,9 @@ trait FormTrait
'id' => $this->id,
'uniqueid' => $this->uniqueid,
'submitted' => $this->submitted,
'errors' => $this->errors,
'status' => $this->status,
'message' => $this->message,
'messages' => $this->messages,
'data' => $data,
'files' => $this->files,
];
@@ -538,7 +620,9 @@ trait FormTrait
$this->id = $data['id'];
$this->uniqueid = $data['uniqueid'];
$this->submitted = $data['submitted'] ?? false;
$this->errors = $data['errors'] ?? [];
$this->status = $data['status'] ?? 'success';
$this->message = $data['message'] ?? null;
$this->messages = $data['messages'] ?? [];
$this->data = isset($data['data']) ? new Data($data['data'], $this->getBlueprint()) : null;
$this->files = $data['files'] ?? [];
}

View File

@@ -103,7 +103,7 @@ trait NestedPropertyTrait
$current[$offset] = [];
}
} else {
throw new \RuntimeException('Cannot set nested property on non-array value');
throw new \RuntimeException("Cannot set nested property {$property} on non-array value");
}
$current = &$current[$offset];
@@ -147,7 +147,7 @@ trait NestedPropertyTrait
return $this;
}
} else {
throw new \RuntimeException('Cannot set nested property on non-array value');
throw new \RuntimeException("Cannot unset nested property {$property} on non-array value");
}
$current = &$current[$offset];

View File

@@ -193,6 +193,7 @@ class Route
public function withRoute($route)
{
$this->route = $route;
return $this;
}
@@ -205,6 +206,7 @@ class Route
public function withRoot($root)
{
$this->root = $root;
return $this;
}
@@ -250,6 +252,25 @@ class Route
return $this->withParam('queryParams', $param, $value);
}
public function withoutParams()
{
return $this->withoutGravParams()->withoutQueryParams();
}
public function withoutGravParams()
{
$this->gravParams = [];
return $this;
}
public function withoutQueryParams()
{
$this->queryParams = [];
return $this;
}
/**
* @return \Grav\Framework\Uri\Uri
*/
@@ -298,7 +319,7 @@ class Route
return $this;
}
$new = clone $this;
$new = $this->copy();
if ($value === null) {
unset($new->{$type}[$param]);
} else {
@@ -308,6 +329,11 @@ class Route
return $new;
}
protected function copy()
{
return clone $this;
}
/**
* @param bool $includeRoot
* @return string

View File

@@ -302,7 +302,7 @@ class UriTest extends \Codeception\TestCase\Test
'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',
'__toString' => 'https://username:password@api.getgrav.com:4040/v1/post/128/page:x?all=1'
'toOriginalString' => 'https://username:password@api.getgrav.com:4040/v1/post/128/page:x?all=1'
],
'https://google.com:443/' => [
'scheme' => 'https://',
@@ -392,7 +392,7 @@ class UriTest extends \Codeception\TestCase\Test
'rootUrl' => 'http://localhost',
'extension' => null,
'addNonce' => 'http://localhost/this%20is%20the%20path/my%20page/nonce:{{nonce}}',
'__toString' => 'http://localhost/this%20is%20the%20path/my%20page'
'toOriginalString' => 'http://localhost/this%20is%20the%20path/my%20page'
],
'http://localhost/pölöpölö/päläpälä' => [
'scheme' => 'http://',
@@ -415,7 +415,7 @@ class UriTest extends \Codeception\TestCase\Test
'rootUrl' => 'http://localhost',
'extension' => null,
'addNonce' => 'http://localhost/p%C3%B6l%C3%B6p%C3%B6l%C3%B6/p%C3%A4l%C3%A4p%C3%A4l%C3%A4/nonce:{{nonce}}',
'__toString' => 'http://localhost/p%C3%B6l%C3%B6p%C3%B6l%C3%B6/p%C3%A4l%C3%A4p%C3%A4l%C3%A4'
'toOriginalString' => 'http://localhost/p%C3%B6l%C3%B6p%C3%B6l%C3%B6/p%C3%A4l%C3%A4p%C3%A4l%C3%A4'
],
// Query params tests.
'http://localhost:8080/grav/it/ueper?test=x&test2=y' => [
@@ -573,7 +573,7 @@ class UriTest extends \Codeception\TestCase\Test
'rootUrl' => 'http://localhost',
'extension' => 'html',
'addNonce' => 'http://localhost/a-page.html/nonce:{{nonce}}',
'__toString' => 'http://localhost/a-page', // FIXME <-
'toOriginalString' => 'http://localhost/a-page.html',
],
'http://localhost/a-page.json' => [
'scheme' => 'http://',
@@ -596,7 +596,7 @@ class UriTest extends \Codeception\TestCase\Test
'rootUrl' => 'http://localhost',
'extension' => 'json',
'addNonce' => 'http://localhost/a-page.json/nonce:{{nonce}}',
'__toString' => 'http://localhost/a-page', // FIX ME <-
'toOriginalString' => 'http://localhost/a-page.json',
],
'http://localhost/admin/ajax.json/task:getnewsfeed' => [
'scheme' => 'http://',
@@ -619,7 +619,7 @@ class UriTest extends \Codeception\TestCase\Test
'rootUrl' => 'http://localhost',
'extension' => 'json',
'addNonce' => 'http://localhost/admin/ajax.json/task:getnewsfeed/nonce:{{nonce}}',
'__toString' => 'http://localhost/admin/ajax/task:getnewsfeed',
'toOriginalString' => 'http://localhost/admin/ajax.json/task:getnewsfeed',
],
'http://localhost/grav/admin/media.json/route:L1VzZXJzL3JodWsvd29ya3NwYWNlL2dyYXYtZGVtby1zYW1wbGVyL3VzZXIvYXNzZXRzL3FRMXB4Vk1ERTNJZzh5Ni5qcGc=/task:removeFileFromBlueprint/proute:/blueprint:Y29uZmlnL2RldGFpbHM=/type:config/field:deep.nested.custom_file/path:dXNlci9hc3NldHMvcVExcHhWTURFM0lnOHk2LmpwZw==' => [
'scheme' => 'http://',
@@ -642,7 +642,7 @@ class UriTest extends \Codeception\TestCase\Test
'rootUrl' => 'http://localhost',
'extension' => 'json',
'addNonce' => 'http://localhost/grav/admin/media.json/route:L1VzZXJzL3JodWsvd29ya3NwYWNlL2dyYXYtZGVtby1zYW1wbGVyL3VzZXIvYXNzZXRzL3FRMXB4Vk1ERTNJZzh5Ni5qcGc=/task:removeFileFromBlueprint/proute:/blueprint:Y29uZmlnL2RldGFpbHM=/type:config/field:deep.nested.custom_file/path:dXNlci9hc3NldHMvcVExcHhWTURFM0lnOHk2LmpwZw==/nonce:{{nonce}}',
'__toString' => 'http://localhost/grav/admin/media/route:L1VzZXJzL3JodWsvd29ya3NwYWNlL2dyYXYtZGVtby1zYW1wbGVyL3VzZXIvYXNzZXRzL3FRMXB4Vk1ERTNJZzh5Ni5qcGc=/task:removeFileFromBlueprint/proute:/blueprint:Y29uZmlnL2RldGFpbHM=/type:config/field:deep.nested.custom_file/path:dXNlci9hc3NldHMvcVExcHhWTURFM0lnOHk2LmpwZw==', // FIXME <-
'toOriginalString' => 'http://localhost/grav/admin/media.json/route:L1VzZXJzL3JodWsvd29ya3NwYWNlL2dyYXYtZGVtby1zYW1wbGVyL3VzZXIvYXNzZXRzL3FRMXB4Vk1ERTNJZzh5Ni5qcGc=/task:removeFileFromBlueprint/proute:/blueprint:Y29uZmlnL2RldGFpbHM=/type:config/field:deep.nested.custom_file/path:dXNlci9hc3NldHMvcVExcHhWTURFM0lnOHk2LmpwZw==',
],
'http://localhost/a-page.foo' => [
'scheme' => 'http://',
@@ -665,7 +665,7 @@ class UriTest extends \Codeception\TestCase\Test
'rootUrl' => 'http://localhost',
'extension' => 'foo',
'addNonce' => 'http://localhost/a-page.foo/nonce:{{nonce}}',
'__toString' => 'http://localhost/a-page.foo'
'toOriginalString' => 'http://localhost/a-page.foo'
],
// Fragment tests.
'http://localhost:8080/a/b/c#my-fragment' => [
@@ -712,7 +712,7 @@ class UriTest extends \Codeception\TestCase\Test
'rootUrl' => '',
'extension' => null,
//'addNonce' => '%22%3E%3Cscript%3Ealert%3C/localhost/script%3E:/nonce:{{nonce}}', // FIXME <-
'__toString' => '%22%3E%3Cscript%3Ealert%3C/localhost/script%3E:' // FIXME <-
'toOriginalString' => '%22%3E%3Cscript%3Ealert%3C/localhost/script%3E:' // FIXME <-
],
'http://"><script>alert</script>' => [
'scheme' => 'http://',
@@ -735,7 +735,7 @@ class UriTest extends \Codeception\TestCase\Test
'rootUrl' => 'http://unknown',
'extension' => null,
'addNonce' => 'http://unknown/script%3E/nonce:{{nonce}}',
'__toString' => 'http://unknown/script%3E'
'toOriginalString' => 'http://unknown/script%3E'
],
'http://localhost/"><script>alert</script>' => [
'scheme' => 'http://',
@@ -758,7 +758,7 @@ class UriTest extends \Codeception\TestCase\Test
'rootUrl' => 'http://localhost',
'extension' => null,
'addNonce' => 'http://localhost/%22%3E%3Cscript%3Ealert%3C/script%3E/nonce:{{nonce}}',
'__toString' => 'http://localhost/%22%3E%3Cscript%3Ealert%3C/script%3E'
'toOriginalString' => 'http://localhost/%22%3E%3Cscript%3Ealert%3C/script%3E'
],
'http://localhost/something/p1:foo/p2:"><script>alert</script>' => [
'scheme' => 'http://',
@@ -781,7 +781,7 @@ class UriTest extends \Codeception\TestCase\Test
'rootUrl' => 'http://localhost',
'extension' => null,
//'addNonce' => 'http://localhost/something/script%3E/p1:foo/p2:%22%3E%3Cscript%3Ealert%3C/nonce:{{nonce}}', // FIXME <-
'__toString' => 'http://localhost/something/script%3E/p1:foo/p2:%22%3E%3Cscript%3Ealert%3C'
'toOriginalString' => 'http://localhost/something/script%3E/p1:foo/p2:%22%3E%3Cscript%3Ealert%3C'
],
'http://localhost/something?p="><script>alert</script>' => [
'scheme' => 'http://',
@@ -804,7 +804,7 @@ class UriTest extends \Codeception\TestCase\Test
'rootUrl' => 'http://localhost',
'extension' => null,
'addNonce' => 'http://localhost/something/nonce:{{nonce}}?p=%22%3E%3Cscript%3Ealert%3C/script%3E',
'__toString' => 'http://localhost/something?p=%22%3E%3Cscript%3Ealert%3C/script%3E'
'toOriginalString' => 'http://localhost/something?p=%22%3E%3Cscript%3Ealert%3C/script%3E'
],
'http://localhost/something#"><script>alert</script>' => [
'scheme' => 'http://',
@@ -827,7 +827,7 @@ class UriTest extends \Codeception\TestCase\Test
'rootUrl' => 'http://localhost',
'extension' => null,
'addNonce' => 'http://localhost/something/nonce:{{nonce}}#%22%3E%3Cscript%3Ealert%3C/script%3E',
'__toString' => 'http://localhost/something#%22%3E%3Cscript%3Ealert%3C/script%3E'
'toOriginalString' => 'http://localhost/something#%22%3E%3Cscript%3Ealert%3C/script%3E'
],
'https://www.getgrav.org/something/"><script>eval(atob("aGlzdG9yeS5wdXNoU3RhdGUoJycsJycsJy8nKTskKCdoZWFkLGJvZHknKS5odG1sKCcnKS5sb2FkKCcvJyk7JC5wb3N0KCcvYWRtaW4nLGZ1bmN0aW9uKGRhdGEpeyQucG9zdCgkKGRhdGEpLmZpbmQoJ1tpZD1hZG1pbi11c2VyLWRldGFpbHNdIGEnKS5hdHRyKCdocmVmJykseydhZG1pbi1ub25jZSc6JChkYXRhKS5maW5kKCdbZGF0YS1jbGVhci1jYWNoZV0nKS5hdHRyKCdkYXRhLWNsZWFyLWNhY2hlJykuc3BsaXQoJzonKS5wb3AoKS50cmltKCksJ2RhdGFbcGFzc3dvcmRdJzonSW0zdjFsaDR4eDByJywndGFzayc6J3NhdmUnfSl9KQ=="))</script><' => [
'scheme' => 'https://',
@@ -850,7 +850,7 @@ class UriTest extends \Codeception\TestCase\Test
'rootUrl' => 'https://www.getgrav.org',
'extension' => null,
'addNonce' => 'https://www.getgrav.org/something/%22%3E%3Cscript%3Eeval%28atob%28%22aGlzdG9yeS5wdXNoU3RhdGUoJycsJycsJy8nKTskKCdoZWFkLGJvZHknKS5odG1sKCcnKS5sb2FkKCcvJyk7JC5wb3N0KCcvYWRtaW4nLGZ1bmN0aW9uKGRhdGEpeyQucG9zdCgkKGRhdGEpLmZpbmQoJ1tpZD1hZG1pbi11c2VyLWRldGFpbHNdIGEnKS5hdHRyKCdocmVmJykseydhZG1pbi1ub25jZSc6JChkYXRhKS5maW5kKCdbZGF0YS1jbGVhci1jYWNoZV0nKS5hdHRyKCdkYXRhLWNsZWFyLWNhY2hlJykuc3BsaXQoJzonKS5wb3AoKS50cmltKCksJ2RhdGFbcGFzc3dvcmRdJzonSW0zdjFsaDR4eDByJywndGFzayc6J3NhdmUnfSl9KQ==%22%29%29%3C/script%3E%3C/nonce:{{nonce}}',
'__toString' => 'https://www.getgrav.org/something/%22%3E%3Cscript%3Eeval%28atob%28%22aGlzdG9yeS5wdXNoU3RhdGUoJycsJycsJy8nKTskKCdoZWFkLGJvZHknKS5odG1sKCcnKS5sb2FkKCcvJyk7JC5wb3N0KCcvYWRtaW4nLGZ1bmN0aW9uKGRhdGEpeyQucG9zdCgkKGRhdGEpLmZpbmQoJ1tpZD1hZG1pbi11c2VyLWRldGFpbHNdIGEnKS5hdHRyKCdocmVmJykseydhZG1pbi1ub25jZSc6JChkYXRhKS5maW5kKCdbZGF0YS1jbGVhci1jYWNoZV0nKS5hdHRyKCdkYXRhLWNsZWFyLWNhY2hlJykuc3BsaXQoJzonKS5wb3AoKS50cmltKCksJ2RhdGFbcGFzc3dvcmRdJzonSW0zdjFsaDR4eDByJywndGFzayc6J3NhdmUnfSl9KQ==%22%29%29%3C/script%3E%3C'
'toOriginalString' => 'https://www.getgrav.org/something/%22%3E%3Cscript%3Eeval%28atob%28%22aGlzdG9yeS5wdXNoU3RhdGUoJycsJycsJy8nKTskKCdoZWFkLGJvZHknKS5odG1sKCcnKS5sb2FkKCcvJyk7JC5wb3N0KCcvYWRtaW4nLGZ1bmN0aW9uKGRhdGEpeyQucG9zdCgkKGRhdGEpLmZpbmQoJ1tpZD1hZG1pbi11c2VyLWRldGFpbHNdIGEnKS5hdHRyKCdocmVmJykseydhZG1pbi1ub25jZSc6JChkYXRhKS5maW5kKCdbZGF0YS1jbGVhci1jYWNoZV0nKS5hdHRyKCdkYXRhLWNsZWFyLWNhY2hlJykuc3BsaXQoJzonKS5wb3AoKS50cmltKCksJ2RhdGFbcGFzc3dvcmRdJzonSW0zdjFsaDR4eDByJywndGFzayc6J3NhdmUnfSl9KQ==%22%29%29%3C/script%3E%3C'
],
];
@@ -868,7 +868,7 @@ class UriTest extends \Codeception\TestCase\Test
protected function runTestSet(array $tests, $method, $params = [])
{
foreach ($tests as $url => $candidates) {
if (!array_key_exists($method, $candidates) && $method !== '__toString') {
if (!array_key_exists($method, $candidates) && $method !== 'toOriginalString') {
continue;
}
if ($method === 'addNonce') {
@@ -881,7 +881,7 @@ class UriTest extends \Codeception\TestCase\Test
}
$this->uri->initializeWithURL($url)->init();
if ($method === '__toString' && !isset($candidates[$method])) {
if ($method === 'toOriginalString' && !isset($candidates[$method])) {
$expected = $url;
} else {
$expected = $candidates[$method];
@@ -921,7 +921,7 @@ class UriTest extends \Codeception\TestCase\Test
public function testToString()
{
$this->runTestSet($this->tests, '__toString');
$this->runTestSet($this->tests, 'toOriginalString');
}
public function testScheme()
@@ -1121,6 +1121,24 @@ class UriTest extends \Codeception\TestCase\Test
];
$this->assertSame('http://foo:bar@localhost:8080/test?x=2#xxx', Uri::buildUrl($parsed_url));
/** @var Uri $uri */
$uri = Grav::instance()['uri'];
$uri->initializeWithUrlAndRootPath('https://testing.dev/subdir/path1/path2/file.html', '/subdir')->init();
$this->assertSame('https://testing.dev/subdir/path1/path2/file.html', Uri::buildUrl($uri->toArray(true)));
$uri->initializeWithUrlAndRootPath('https://testing.dev/subdir/path1/path2/file.foo', '/subdir')->init();
$this->assertSame('https://testing.dev/subdir/path1/path2/file.foo', Uri::buildUrl($uri->toArray(true)));
$uri->initializeWithUrlAndRootPath('https://testing.dev/subdir/path1/path2/file.html', '/subdir/path1')->init();
$this->assertSame('https://testing.dev/subdir/path1/path2/file.html', Uri::buildUrl($uri->toArray(true)));
$uri->initializeWithUrlAndRootPath('https://testing.dev/subdir/path1/path2/file.html/foo:blah/bang:boom', '/subdir')->init();
$this->assertSame('https://testing.dev/subdir/path1/path2/file.html/foo:blah/bang:boom', Uri::buildUrl($uri->toArray(true)));
$uri->initializeWithUrlAndRootPath('https://testing.dev/subdir/path1/path2/file.html/foo:blah/bang:boom?fig=something', '/subdir')->init();
$this->assertSame('https://testing.dev/subdir/path1/path2/file.html/foo:blah/bang:boom?fig=something', Uri::buildUrl($uri->toArray(true)));
}
public function testConvertUrl()

View File

@@ -224,10 +224,19 @@ class UtilsTest extends \Codeception\TestCase\Test
$this->assertEquals('test', Utils::normalizePath('../test'));
$this->assertEquals('/test', Utils::normalizePath('/../test'));
$this->assertEquals('/test2', Utils::normalizePath('/test/../test2'));
$this->assertEquals('/test/test2', Utils::normalizePath('/test/./test2'));
$this->assertEquals('//something/test/test2', Utils::normalizePath('//../something/test/test2'));
$this->assertEquals('//something/test2', Utils::normalizePath('//something/test/../test2'));
$this->assertEquals('//test2', Utils::normalizePath('//something/../test/../test2'));
$this->assertEquals('/test3', Utils::normalizePath('/test/../test2/../test3'));
$this->assertEquals('//cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.css', Utils::normalizePath('//cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.css'));
$this->assertEquals('//use.fontawesome.com/releases/v5.8.1/css/all.css', Utils::normalizePath('//use.fontawesome.com/releases/v5.8.1/css/all.css'));
$this->assertEquals('//use.fontawesome.com/releases/v5.8.1/webfonts/fa-brands-400.eot', Utils::normalizePath('//use.fontawesome.com/releases/v5.8.1/css/../webfonts/fa-brands-400.eot'));
$this->assertEquals('http://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.css', Utils::normalizePath('http://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.css'));
$this->assertEquals('http://use.fontawesome.com/releases/v5.8.1/css/all.css', Utils::normalizePath('http://use.fontawesome.com/releases/v5.8.1/css/all.css'));
$this->assertEquals('http://use.fontawesome.com/releases/v5.8.1/webfonts/fa-brands-400.eot', Utils::normalizePath('http://use.fontawesome.com/releases/v5.8.1/css/../webfonts/fa-brands-400.eot'));
$this->assertEquals('https://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.css', Utils::normalizePath('https://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.css'));
$this->assertEquals('https://use.fontawesome.com/releases/v5.8.1/css/all.css', Utils::normalizePath('https://use.fontawesome.com/releases/v5.8.1/css/all.css'));
$this->assertEquals('https://use.fontawesome.com/releases/v5.8.1/webfonts/fa-brands-400.eot', Utils::normalizePath('https://use.fontawesome.com/releases/v5.8.1/css/../webfonts/fa-brands-400.eot'));
}
public function testIsFunctionDisabled()

View File

@@ -26,7 +26,7 @@ To edit this page, simply navigate to the folder you installed **Grav** into, an
Creating a new page is a simple affair in **Grav**. Simply follow these simple steps:
1. Navigate to your pages folder: `user/pages/` and create a new folder. In this example, we will use [explicit default ordering](http://learn.getgrav.org/content/content-pages) and call the folder `02.mypage`.
1. Navigate to your pages folder: `user/pages/` and create a new folder. In this example, we will use [explicit default ordering](http://learn.getgrav.org/content/content-pages) and call the folder `03.mypage`.
2. Launch your text editor and paste in the following sample code:
---
@@ -36,7 +36,7 @@ Creating a new page is a simple affair in **Grav**. Simply follow these simple
This is the body of **my new page** and I can easily use _Markdown_ syntax here.
3. Save this file in the `user/pages/02.mypage/` folder as `default.md`. This will tell **Grav** to render the page using the **default** template.
3. Save this file in the `user/pages/03.mypage/` folder as `default.md`. This will tell **Grav** to render the page using the **default** template.
4. That is it! Reload your browser to see your new page in the menu.
! NOTE: The page will automatically show up in the Menu after the "Home" menu item. If you wish to change the name that shows up in the Menu, simple add: `menu: My Page` between the dashes in the page content. This is called the YAML front matter, and it is where you configure page-specific options.
! NOTE: The page will automatically show up in the Menu after the "Typography" menu item. If you wish to change the name that shows up in the Menu, simple add: `menu: My Page` between the dashes in the page content. This is called the YAML front matter, and it is where you configure page-specific options.