Browse Source

front-end stuff and an update

master
freds 3 years ago
parent
commit
46125174cf
  1. 293
      CHANGELOG.md
  2. 17
      README.md
  3. 2
      assets/.gitkeep
  4. BIN
      backup/default_site_backup--20220203194155.zip
  5. BIN
      backup/default_site_backup--20220203195307.zip
  6. BIN
      backup/default_site_backup--20230324161017.zip
  7. BIN
      bin/composer.phar
  8. 14
      bin/gpm
  9. 14
      bin/grav
  10. 14
      bin/plugin
  11. 40
      composer.json
  12. 1704
      composer.lock
  13. 0
      default.md
  14. 34
      index.php
  15. 1
      node_modules/.bin/in-install
  16. 4
      node_modules/.bin/in-install
  17. 1
      node_modules/.bin/in-publish
  18. 4
      node_modules/.bin/in-publish
  19. 1
      node_modules/.bin/mkdirp
  20. 33
      node_modules/.bin/mkdirp
  21. 1
      node_modules/.bin/node-gyp
  22. 148
      node_modules/.bin/node-gyp
  23. 1
      node_modules/.bin/node-sass
  24. 412
      node_modules/.bin/node-sass
  25. 1
      node_modules/.bin/nopt
  26. 54
      node_modules/.bin/nopt
  27. 1
      node_modules/.bin/not-in-install
  28. 4
      node_modules/.bin/not-in-install
  29. 1
      node_modules/.bin/not-in-publish
  30. 4
      node_modules/.bin/not-in-publish
  31. 1
      node_modules/.bin/rimraf
  32. 50
      node_modules/.bin/rimraf
  33. 1
      node_modules/.bin/sassgraph
  34. 124
      node_modules/.bin/sassgraph
  35. 1
      node_modules/.bin/semver
  36. 160
      node_modules/.bin/semver
  37. 1
      node_modules/.bin/sshpk-conv
  38. 243
      node_modules/.bin/sshpk-conv
  39. 1
      node_modules/.bin/sshpk-sign
  40. 191
      node_modules/.bin/sshpk-sign
  41. 1
      node_modules/.bin/sshpk-verify
  42. 167
      node_modules/.bin/sshpk-verify
  43. 1
      node_modules/.bin/strip-indent
  44. 49
      node_modules/.bin/strip-indent
  45. 1
      node_modules/.bin/uuid
  46. 65
      node_modules/.bin/uuid
  47. 1
      node_modules/.bin/which
  48. 52
      node_modules/.bin/which
  49. 1
      node_modules/node-gyp/node_modules/.bin/semver
  50. 133
      node_modules/node-gyp/node_modules/.bin/semver
  51. 5
      system/assets/debugger/phpdebugbar.css
  52. 245
      system/blueprints/config/system.yaml
  53. 2
      system/blueprints/flex/pages.yaml
  54. 13
      system/blueprints/flex/user-accounts.yaml
  55. 1
      system/blueprints/flex/user-groups.yaml
  56. 12
      system/blueprints/pages/default.yaml
  57. 18
      system/blueprints/pages/external.yaml
  58. 10
      system/blueprints/pages/partials/security.yaml
  59. 19
      system/blueprints/user/account.yaml
  60. 2
      system/config/media.yaml
  61. 27
      system/config/system.yaml
  62. 4
      system/defines.php
  63. BIN
      system/images/watermark.png
  64. 2
      system/install.php
  65. 12
      system/languages/es.yaml
  66. 23
      system/router.php
  67. 165
      system/src/DOMLettersIterator.php
  68. 158
      system/src/DOMWordsIterator.php
  69. 175
      system/src/Grav/Common/Assets.php
  70. 23
      system/src/Grav/Common/Assets/BaseAsset.php
  71. 207
      system/src/Grav/Common/Assets/BlockAssets.php
  72. 4
      system/src/Grav/Common/Assets/Css.php
  73. 4
      system/src/Grav/Common/Assets/InlineCss.php
  74. 4
      system/src/Grav/Common/Assets/InlineJs.php
  75. 46
      system/src/Grav/Common/Assets/InlineJsModule.php
  76. 4
      system/src/Grav/Common/Assets/Js.php
  77. 49
      system/src/Grav/Common/Assets/JsModule.php
  78. 43
      system/src/Grav/Common/Assets/Link.php
  79. 65
      system/src/Grav/Common/Assets/Pipeline.php
  80. 19
      system/src/Grav/Common/Assets/Traits/AssetUtilsTrait.php
  81. 2
      system/src/Grav/Common/Assets/Traits/LegacyAssetsTrait.php
  82. 11
      system/src/Grav/Common/Assets/Traits/TestingAssetsTrait.php
  83. 9
      system/src/Grav/Common/Backup/Backups.php
  84. 2
      system/src/Grav/Common/Browser.php
  85. 4
      system/src/Grav/Common/Cache.php
  86. 2
      system/src/Grav/Common/Composer.php
  87. 2
      system/src/Grav/Common/Config/CompiledBase.php
  88. 2
      system/src/Grav/Common/Config/CompiledBlueprints.php
  89. 2
      system/src/Grav/Common/Config/CompiledConfig.php
  90. 2
      system/src/Grav/Common/Config/CompiledLanguages.php
  91. 2
      system/src/Grav/Common/Config/Config.php
  92. 2
      system/src/Grav/Common/Config/ConfigFileFinder.php
  93. 2
      system/src/Grav/Common/Config/Languages.php
  94. 11
      system/src/Grav/Common/Config/Setup.php
  95. 21
      system/src/Grav/Common/Data/Blueprint.php
  96. 27
      system/src/Grav/Common/Data/BlueprintSchema.php
  97. 2
      system/src/Grav/Common/Data/Blueprints.php
  98. 3
      system/src/Grav/Common/Data/Data.php
  99. 2
      system/src/Grav/Common/Data/DataInterface.php
  100. 40
      system/src/Grav/Common/Data/Validation.php
  101. Some files were not shown because too many files have changed in this diff Show More

293
CHANGELOG.md

@ -1,9 +1,302 @@
# v1.7.40
## 03/22/2023
1. [](#new)
* Added a new `timestamp: true|false` option for individual assets
1. [](#improved)
* Removed outdated `xcache` setting [#3615](https://github.com/getgrav/grav/pull/3615)
* Updated `robots.txt` [#3625](https://github.com/getgrav/grav/pull/3625)
1. [](#bugfix)
* Fixed `force_ssl` redirect in case of undefined hostname [#3702](https://github.com/getgrav/grav/pull/3702)
* Fixed an issue with duplicate identical page paths
* Fixed `BlueprintSchema:flattenData` to properly handle ignored fields
* Fixed LogViewer regex greediness [#3684](https://github.com/getgrav/grav/pull/3684)
* Fixed `whoami` command [#3695](https://github.com/getgrav/grav/pull/3695)
# v1.7.39.4
## 02/22/2023
1. [](#bugfix)
* Reverted a reorganization of `account.yaml` that caused username to be disabled [admin#2344](https://github.com/getgrav/grav-plugin-admin/issues/2344)
# v1.7.39.3
## 02/21/2023
1. [](#bugfix)
* Fix for overzealous modular page template rendering fix in 1.7.39 causing Feed plugin to break [#3689](https://github.com/getgrav/grav/issues/3689)
# v1.7.39.2
## 02/20/2023
1. [](#bugfix)
* Fix for invalid session breaking Flex Accounts (when switching from Regular to Flex)
# v1.7.39.1
## 02/20/2023
1. [](#bugfix)
* Fix for broken image CSS with the latest version of DebugBar
# v1.7.39
## 02/19/2023
1. [](#improved)
* Vendor library updates to latest versions
1. [](#bugfix)
* Various PHP 8.2 fixes
* Fixed an issue with modular pages rendering thew wrong template when dynamically changing the page
* Fixed an issue with `email` validation that was failing on UTF-8 characters. Following best practices and now only check for `@` and length.
* Fixed PHPUnit tests to remove deprecation warnings
# v1.7.38
## 01/02/2023
1. [](#new)
* New `onBeforeSessionStart()` event to be used to store data lost during session regeneration (e.g. login)
1. [](#improved)
* Vendor library updates to latest versions
* Updated `bin/composer.phar` to latest `2.4.4` version [#3627](https://github.com/getgrav/grav/issues/3627)
1. [](#bugfix)
* Don't fail hard if pages recurse with same path
* Github workflows security hardening [#3624](https://github.com/getgrav/grav/pull/3624)
# v1.7.37.1
## 10/05/2022
1. [](#bugfix)
* Fixed a bad return type [#3630](https://github.com/getgrav/grav/issues/3630)
# v1.7.37
## 10/05/2022
1. [](#new)
* Added new `onPageHeaders()` event to allow for header modification as needed
* Added a `system.pages.dirs` configuration option to allow for configurable paths, and multiple page paths
* Added new `Pages::getSimplePagesHash` which is useful for caching pages specific data
* Updated to latest vendor libraries
1. [](#bugfix)
* An attempt to workaround windows reading locked file issue [getgrav/grav-plugin-admin#2299](https://github.com/getgrav/grav-plugin-admin/issues/2299)
* Force user index file to be updated to fix email addresses [getgrav/grav-plugin-login#229](https://github.com/getgrav/grav-plugin-login/issues/229)
# v1.7.36
## 09/08/2022
1. [](#new)
* Added `authorize-*@:` support for Flex blueprints, e.g. `authorize-disabled@: not delete` disables the field if user does not have access to delete object
* Added support for `flex-ignore@` to hide all the nested fields in the blueprint
1. [](#bugfix)
* Fixed login with a capitalised email address when using old users [getgrav/grav-plugin-login#229](https://github.com/getgrav/grav-plugin-login/issues/229)
# v1.7.35
## 08/04/2022
1. [](#new)
* Added support for `multipart/form-data` content type in PUT and PATCH requests
* Added support for object relationships
* Added variables `$environment` (string), `$request` (PSR-7 ServerRequestInterface|null) and `$uri` (PSR-7 Uri|null) to be used in `setup.php`
1. [](#improved)
* Minor vendor updates
# v1.7.34
## 06/14/2022
1. [](#new)
* Added back Yiddish to Language Codes [#3336](https://github.com/getgrav/grav/pull/3336)
* Ignore upcoming `media.json` file in media
1. [](#bugfix)
* Regression: Fixed saving page with a new language causing cache corruption [getgrav/grav-plugin-admin#2282](https://github.com/getgrav/grav-plugin-admin/issues/2282)
* Fixed a potential fatal error when using watermark in images
* Fixed `bin/grav install` command with arbitrary destination folder name
* Fixed Twig `|filter()` allowing code execution
* Fixed login and user search by email not being case-insensitive when using Flex Users
# v1.7.33
## 04/25/2022
1. [](#improved)
* When saving yaml and markdown, create also a cached version of the file and recompile it in opcache
2. [](#bugfix)
* Fixed missing changes in **yaml** & **markdown** files if saved multiple times during the same second because of a caching issue
* Fixed XSS check not detecting onX events without quotes
* Fixed default collection ordering in pages admin
# v1.7.32
## 03/28/2022
1. [](#new)
* Added `|replace_last(search, replace)` filter
* Added `parseurl` Twig function to expose PHP's `parse_url` function
2. [](#improved)
* Added multi-language support for page routes in `Utils::url()`
* Set default maximum length for text fields
- `password`: 256
- `email`: 320
- `text`, `url`, `hidden`, `commalist`: 2048
- `text` (multiline), `textarea`: 65536
3. [](#bugfix)
* Fixed issue with `system.cache.gzip: true` resulted in "Fetch Failed" for PHP 8.0.17 and PHP 8.1.4 [PHP issue #8218](https://github.com/php/php-src/issues/8218)
* Fix for multi-lang issues with Security Report
* Fixed page search not working with selected language [#3316](https://github.com/getgrav/grav/issues/3316)
# v1.7.31
## 03/14/2022
1. [](#new)
* Added new local Multiavatar (local generation). **This will be default in Grav 1.8**
* Added support to get image size for SVG vector images [#3533](https://github.com/getgrav/grav/pull/3533)
* Added XSS check for uploaded SVG files before they get stored
* Fixed phpstan issues (All level 2, Framework level 5)
2. [](#improved)
* Moved Accounts out of Experimental section of System configuration to new "Accounts" tab
3. [](#bugfix)
* Fixed `'mbstring' extension is not loaded` error, use Polyfill instead [#3504](https://github.com/getgrav/grav/pull/3504)
* Fixed new `Utils::pathinfo()` and `Utils::basename()` being too strict for legacy use [#3542](https://github.com/getgrav/grav/issues/3542)
* Fixed non-standard video html atributes generated by `{{ media.html() }}` [#3540](https://github.com/getgrav/grav/issues/3540)
* Fixed entity sanitization for XSS detection
* Fixed avatar save location when `account://` stream points to custom directory
* Fixed bug in `Utils::url()` when path contains part of root
# v1.7.30
## 02/07/2022
1. [](#new)
* Added twig filter `|field_parent` to get parent field name
2. [](#bugfix)
* Fixed error while deleting retina image in admin
* Fixed "Page Authors" field in Security tab, wrongly loading and saving the value [#3525](https://github.com/getgrav/grav/issues/3525)
* Fixed accounts filter only matches against email address [getgrav/grav-plugin-admin#2224](https://github.com/getgrav/grav-plugin-admin/issues/2224)
# v1.7.29.1
## 01/31/2022
1. [](#bugfix)
* Fixed `Call to undefined method` error when upgrading from Grav 1.6 [#3523](https://github.com/getgrav/grav/issues/3523)
# v1.7.29
## 01/28/2022
1. [](#new)
* Added support for registering assets from `HtmlBlock`
* Added unicode-safe `Utils::basename()` and `Utils::pathinfo()` methods
2. [](#improved)
* Improved `Filesystem::basename()` and `Filesystem::pathinfo()` to be unicode-safe
* Made path handling unicode-safe, use new `Utils::basename()` and `Utils::pathinfo()` everywhere
3. [](#bugfix)
* Fixed error on thumbnail image creation
* Fixed MimeType for `gzip` (`application/x-gzip`)
# v1.7.28
## 01/24/2022
1. [](#new)
* Added links and modules support to `HtmlBlock` class
* Added module support for twig script tag: `{% script module 'theme://js/module.mjs' %}`
* Added twig tag for links: `{% link icon 'theme://images/favicon.png' priority: 20 with { type: 'image/png' } %}`
* Added `HtmlBlock` support for `{% style %}`, `{% script %}` and `{% link %}` tags
* Support for page-level `redirect_default_route` frontmatter header override
3. [](#bugfix)
* Fixed XSS check not detecting escaped `&#58`
# v1.7.27.1
## 01/12/2022
3. [](#bugfix)
* Fixed a typo in CSS Asset pipeline that was erroneously joining files with `;`
# v1.7.27
## 01/12/2022
1. [](#new)
* Support for `YubiKey OTP` 2-Factor authenticator
* Added support for generic `assets.link()` for external references. No pipeline support
* Added support for `assets.addJsModule()` with full pipeline support
* Added `Utils::getExtensionsByMime()` method to get all the registered extensions for the specific mime type
* Added `Media::getRoute()` and `Media::getRawRoute()` methods to get page route if available
* Added `Medium::getAlternatives()` to be able to list all the retina sizes
2. [](#improved)
* Improved `Utils::download()` method to allow overrides on download name, mime and expires header
* Improved `onPageFallBackUrl` event
* Reorganized the Asset system configuration blueprint for clarity
3. [](#bugfix)
* Fixed CLI `--env` and `--lang` options having no effect if they aren't added before all the other options
* Fixed scaled image medium filename when using non-existing retina file
* Fixed an issue with JS `imports` and pipelining Assets
# v1.7.26.1
## 01/04/2022
3. [](#bugfix)
* Fixed `UserObject::getAccess()` after cloning the object
# v1.7.26
## 01/03/2022
1. [](#new)
* Made `Grav::redirect()` to accept `Route` class
* Added `translated()` method to `PageTranslateInterface`
* Added second parameter to `UserObject::isMyself()` method
* Added `UserObject::$isAuthorizedCallable` to allow `$user->isAuthorized()` customization
* Use secure session cookies in HTTPS by default (`system.session.secure_https: true`)
* Added new `Plugin::inheritedConfigOption()` function to access plugin specific functions for page overrides
2. [](#improved)
* Upgraded vendor libs for PHP 8.1 compatibility
* Upgraded to **composer v2.1.14** for PHP 8.1 compatibility
* Added third `$name` parameter to `Blueprint::flattenData()` method, useful for flattening repeating data
* `ControllerResponseTrait`: Redirect response should be json if the extension is .json
* When symlinking Grav install, include also tests
* Updated copyright year to `2022`
3. [](#bugfix)
* Fixed bad key lookup in `FlexRelatedDirectoryTrait::getCollectionByProperty()`
* Fixed RequestHandlers `NotFoundException` having empty request
* Block `.json` files in web server configs
* Disabled pretty debug info for Flex as it slows down Twig rendering
* Fixed Twig being very slow when template overrides do not exist
* Fixed `UserObject::$authorizeCallable` binding to the user object
* Fixed `FlexIndex::call()` to return null instead of failing to call undefined method
* Fixed Flex directory configuration creating environment configuration when it should not
# v1.7.25
## 11/16/2021
1. [](#new)
* Updated phpstan to v1.0
* Added `FlexObject::getDiff()` to see difference to the saved object
2. [](#improved)
* Use Symfony `dump` instead of PHP's `vardump` in side the `{{ vardump(x) }}` Twig vardump function
* Added `route` and `request` to `onPagesInitialized` event
* Improved page cloning, added method `Page::initialize()`
* Improved `FlexObject::getChanges()`: return changed lists and arrays as whole instead of just changed keys/values
* Improved form validation JSON responses to contain list of failed fields with their error messages
* Improved redirects: send redirect response in JSON if the request was in JSON
3. [](#bugfix)
* Fixed path traversal vulnerability when using `bin/grav server`
* Fixed unescaped error messages in JSON error responses
* Fixed `|t(variable)` twig filter in admin
* Fixed `FlexObject::getChanges()` always returning empty array
* Fixed form validation exceptions to use `400 Bad Request` instead of `500 Internal Server Error`
# v1.7.24
## 10/26/2021
1. [](#new)
* Added support for image watermarks
* Added support to disable a form, making it readonly
2. [](#improved)
* Flex `$user->authorize()` now checks user groups before `admin.super`, allowing deny rules to work properly
3. [](#bugfix)
* Fixed a bug in `PermissionsReader` in PHP 7.3
* Fixed `session_store_active` language option (#3464)
* Fixed deprecated warnings on `ArrayAccess` in PHP 8.1
* Fixed XSS detection with `:`
# v1.7.23
## 09/29/2021
1. [](#new)
* Added method `Pages::referrerRoute()` to get the referrer route and language
* Added true unique `Utils::uniqueId()` / `{{ unique_id() }}` utilities with length, prefix, and suffix support
* Added `UserObject::isMyself()` method to check if flex user is currently logged in
* Added support for custom form field options validation with `validate: options: key|ignore`
2. [](#improved)
* Replaced GPL `SVG-Sanitizer` with MIT licensed `DOM-Sanitizer`
* `Uri::referrer()` now accepts third parameter, if set to `true`, it returns route without base or language code [#3411](https://github.com/getgrav/grav/issues/3411)

17
README.md

@ -2,18 +2,18 @@
[![PHPStan](https://img.shields.io/badge/PHPStan-enabled-brightgreen.svg?style=flat)](https://github.com/phpstan/phpstan)
[![Discord](https://img.shields.io/discord/501836936584101899.svg?logo=discord&colorB=728ADA&label=Discord%20Chat)](https://chat.getgrav.org)
[![PHP Tests](https://github.com/getgrav/grav/workflows/PHP%20Tests/badge.svg?branch=develop)](https://github.com/getgrav/grav/actions?query=workflow%3A%22PHP+Tests%22) [![OpenCollective](https://opencollective.com/grav/backers/badge.svg)](#backers) [![OpenCollective](https://opencollective.com/grav/sponsors/badge.svg)](#sponsors)
[![PHP Tests](https://github.com/getgrav/grav/workflows/PHP%20Tests/badge.svg?branch=develop)](https://github.com/getgrav/grav/actions?query=workflow%3A%22PHP+Tests%22) [![OpenCollective](https://opencollective.com/grav/tiers/backers/badge.svg?label=Backers&color=brightgreen)](#backers) [![OpenCollective](https://opencollective.com/grav/tiers/supporters/badge.svg?label=Supporters&color=brightgreen)](#supporters) [![OpenCollective](https://opencollective.com/grav/tiers/sponsors/badge.svg?label=Sponsors&color=brightgreen)](#sponsors)
Grav is a **Fast**, **Simple**, and **Flexible**, file-based Web-platform. There is **Zero** installation required. Just extract the ZIP archive, and you are already up and running. It follows similar principles to other flat-file CMS platforms, but has a different design philosophy than most. Grav comes with a powerful **Package Management System** to allow for simple installation and upgrading of plugins and themes, as well as simple updating of Grav itself.
The underlying architecture of Grav is designed to use well-established and _best-in-class_ technologies to ensure that Grav is simple to use and easy to extend. Some of these key technologies include:
* [Twig Templating](https://twig.sensiolabs.org/): for powerful control of the user interface
* [Twig Templating](https://twig.symfony.com/): for powerful control of the user interface
* [Markdown](https://en.wikipedia.org/wiki/Markdown): for easy content creation
* [YAML](https://yaml.org): for simple configuration
* [Parsedown](https://parsedown.org/): for fast Markdown and Markdown Extra support
* [Doctrine Cache](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/caching.html): layer for performance
* [Pimple Dependency Injection Container](https://pimple.sensiolabs.org/): for extensibility and maintainability
* [Pimple Dependency Injection Container](https://github.com/silexphp/Pimple): for extensibility and maintainability
* [Symfony Event Dispatcher](https://symfony.com/doc/current/components/event_dispatcher/introduction.html): for plugin event handling
* [Symfony Console](https://symfony.com/doc/current/components/console/introduction.html): for CLI interface
* [Gregwar Image Library](https://github.com/Gregwar/Image): for dynamic image manipulation
@ -117,12 +117,19 @@ If you discover a possible security issue related to Grav or one of its plugins,
* More [Awesome Grav Stuff](https://github.com/getgrav/awesome-grav)
# Backers
Support Grav with a monthly donation to help us continue development. [[Become a backer](https://opencollective.com/grav#backer)]
Support Grav with a monthly donation to help us continue development. [[Become a backer](https://opencollective.com/grav/contribute)]
<img src="https://opencollective.com/grav/tiers/backers.svg?avatarHeight=36&width=600" />
# Supporters
Support Grav with a monthly donation to help us continue development. [[Become a supporter](https://opencollective.com/grav/contribute)]
<img src="https://opencollective.com/grav/tiers/supporters.svg?avatarHeight=36&width=600" />
# Sponsors
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/grav#sponsor)]
Support Grav with a yearly donation to help us continue development. [[Become a sponsor](https://opencollective.com/grav/contribute)]
<img src="https://opencollective.com/grav/tiers/sponsors.svg?avatarHeight=36&width=600" />

2
assets/.gitkeep

@ -1 +1 @@
/* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. */
/* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved. */

BIN
backup/default_site_backup--20220203194155.zip

Binary file not shown.

BIN
backup/default_site_backup--20220203195307.zip

Binary file not shown.

BIN
backup/default_site_backup--20230324161017.zip

Binary file not shown.

BIN
bin/composer.phar

Binary file not shown.

14
bin/gpm

@ -2,7 +2,7 @@
<?php
/**
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -25,18 +25,10 @@ if (!file_exists(__DIR__ . '/../vendor/autoload.php')){
$autoload = require __DIR__ . '/../vendor/autoload.php';
if (version_compare($ver = PHP_VERSION, $req = GRAV_PHP_MIN, '<')) {
exit(sprintf("You are running PHP %s, but Grav needs at least PHP %s to run.\n", $ver, $req));
}
if (!ini_get('date.timezone')) {
date_default_timezone_set('UTC');
}
// Set timezone to default, falls back to system if php.ini not set
date_default_timezone_set(@date_default_timezone_get());
// Set internal encoding.
if (!\extension_loaded('mbstring')) {
die("'mbstring' extension is not loaded. This is required for Grav to run correctly");
}
@ini_set('default_charset', 'UTF-8');
mb_internal_encoding('UTF-8');

14
bin/grav

@ -2,7 +2,7 @@
<?php
/**
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -25,18 +25,10 @@ if (!file_exists(__DIR__ . '/../vendor/autoload.php')){
$autoload = require __DIR__ . '/../vendor/autoload.php';
if (version_compare($ver = PHP_VERSION, $req = GRAV_PHP_MIN, '<')) {
exit(sprintf("You are running PHP %s, but Grav needs at least PHP %s to run.\n", $ver, $req));
}
if (!ini_get('date.timezone')) {
date_default_timezone_set('UTC');
}
// Set timezone to default, falls back to system if php.ini not set
date_default_timezone_set(@date_default_timezone_get());
// Set internal encoding.
if (!\extension_loaded('mbstring')) {
die("'mbstring' extension is not loaded. This is required for Grav to run correctly");
}
@ini_set('default_charset', 'UTF-8');
mb_internal_encoding('UTF-8');

14
bin/plugin

@ -2,7 +2,7 @@
<?php
/**
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -25,18 +25,10 @@ if (!file_exists(__DIR__ . '/../vendor/autoload.php')){
$autoload = require __DIR__ . '/../vendor/autoload.php';
if (version_compare($ver = PHP_VERSION, $req = GRAV_PHP_MIN, '<')) {
exit(sprintf("You are running PHP %s, but Grav needs at least PHP %s to run.\n", $ver, $req));
}
if (!ini_get('date.timezone')) {
date_default_timezone_set('UTC');
}
// Set timezone to default, falls back to system if php.ini not set
date_default_timezone_set(@date_default_timezone_get());
// Set internal encoding.
if (!\extension_loaded('mbstring')) {
die("'mbstring' extension is not loaded. This is required for Grav to run correctly");
}
@ini_set('default_charset', 'UTF-8');
mb_internal_encoding('UTF-8');

40
composer.json

@ -19,17 +19,19 @@
"ext-zip": "*",
"ext-dom": "*",
"ext-libxml": "*",
"symfony/polyfill-mbstring": "~1.20",
"symfony/polyfill-iconv": "^1.20",
"symfony/polyfill-php74": "^1.20",
"symfony/polyfill-php80": "^1.20",
"ext-gd": "*",
"symfony/polyfill-mbstring": "~1.23",
"symfony/polyfill-iconv": "^1.23",
"symfony/polyfill-php74": "^1.23",
"symfony/polyfill-php80": "^1.23",
"symfony/polyfill-php81": "^1.23",
"psr/simple-cache": "^1.0",
"psr/http-message": "^1.0",
"psr/http-server-middleware": "^1.0",
"psr/container": "~1.0.0",
"psr/container": "~1.1.0",
"nyholm/psr7-server": "^1.0",
"nyholm/psr7": "^1.3",
"twig/twig": "~1.44",
"twig/twig": "~v1.44",
"erusev/parsedown": "^1.7",
"erusev/parsedown-extra": "~0.8",
"symfony/contracts": "~1.1",
@ -47,11 +49,10 @@
"getgrav/image": "^3.0",
"getgrav/cache": "^2.0",
"donatj/phpuseragentparser": "~1.1",
"pimple/pimple": "~3.3.0",
"pimple/pimple": "~3.5.0",
"rockettheme/toolbox": "~1.5",
"maximebf/debugbar": "~1.16",
"league/climate": "^3.6",
"antoligy/dom-string-iterators": "^1.0",
"miljar/php-exif": "^0.6",
"composer/ca-bundle": "^1.2",
"dragonmantank/cron-expression": "^1.2",
@ -59,12 +60,13 @@
"itsgoingd/clockwork": "^5.0",
"symfony/http-client": "^4.4",
"composer/semver": "^1.4",
"rhukster/dom-sanitizer": "^1.0"
"rhukster/dom-sanitizer": "^1.0",
"multiavatar/multiavatar-php": "^1.0"
},
"require-dev": {
"codeception/codeception": "^4.1",
"phpstan/phpstan": "^0.12",
"phpstan/phpstan-deprecation-rules": "^0.12",
"phpstan/phpstan": "^1.8",
"phpstan/phpstan-deprecation-rules": "^1.0",
"phpunit/php-code-coverage": "~9.2",
"getgrav/markdowndocs": "^2.0",
"codeception/module-asserts": "^1.3",
@ -82,7 +84,8 @@
"ext-intl": "Recommended for multi-language sites",
"ext-memcache": "Needed to support Memcache servers",
"ext-memcached": "Needed to support Memcached servers",
"ext-redis": "Needed to support Redis servers"
"ext-redis": "Needed to support Redis servers",
"ext-exif": "Needed to use exif data from images."
},
"config": {
"apcu-autoloader": true,
@ -96,9 +99,16 @@
"Twig\\": "system/src/Twig"
},
"files": [
"system/defines.php"
"system/defines.php",
"system/src/DOMLettersIterator.php",
"system/src/DOMWordsIterator.php"
]
},
"autoload-dev": {
"psr-4": {
"PHPStan\\": "tests/phpstan/classes"
}
},
"archive": {
"exclude": [
"VERSION"
@ -107,8 +117,8 @@
"scripts": {
"api-17": "vendor/bin/phpdoc-md generate system/src > user/pages/14.api/default.17.md",
"post-create-project-cmd": "bin/grav install",
"phpstan": "vendor/bin/phpstan analyse -l 1 -c ./tests/phpstan/phpstan.neon --memory-limit=520M system/src",
"phpstan-framework": "vendor/bin/phpstan analyse -l 3 -c ./tests/phpstan/phpstan.neon --memory-limit=480M system/src/Grav/Framework system/src/Grav/Events system/src/Grav/Installer",
"phpstan": "vendor/bin/phpstan analyse -l 2 -c ./tests/phpstan/phpstan.neon --memory-limit=720M system/src",
"phpstan-framework": "vendor/bin/phpstan analyse -l 5 -c ./tests/phpstan/phpstan.neon --memory-limit=480M system/src/Grav/Framework system/src/Grav/Events system/src/Grav/Installer",
"phpstan-plugins": "vendor/bin/phpstan analyse -l 1 -c ./tests/phpstan/plugins.neon --memory-limit=400M user/plugins",
"test": "vendor/bin/codecept run unit",
"test-windows": "vendor\\bin\\codecept run unit"

1704
composer.lock generated

File diff suppressed because it is too large Load Diff

0
user/pages/05.contact/default.md → default.md

34
index.php

@ -3,7 +3,7 @@
/**
* @package Grav.Core
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -12,10 +12,6 @@ namespace Grav;
\define('GRAV_REQUEST_TIME', microtime(true));
\define('GRAV_PHP_MIN', '7.3.6');
if (version_compare($ver = PHP_VERSION, $req = GRAV_PHP_MIN, '<')) {
die(sprintf('You are running PHP %s, but Grav needs at least <strong>PHP %s</strong> to run.', $ver, $req));
}
if (PHP_SAPI === 'cli-server') {
$symfony_server = stripos(getenv('_'), 'symfony') !== false || stripos($_SERVER['SERVER_SOFTWARE'] ?? '', 'symfony') !== false || stripos($_ENV['SERVER_SOFTWARE'] ?? '', 'symfony') !== false;
@ -24,16 +20,6 @@ if (PHP_SAPI === 'cli-server') {
}
}
// Set timezone to default, falls back to system if php.ini not set
date_default_timezone_set(@date_default_timezone_get());
// Set internal encoding.
if (!\extension_loaded('mbstring')) {
die("'mbstring' extension is not loaded. This is required for Grav to run correctly");
}
@ini_set('default_charset', 'UTF-8');
mb_internal_encoding('UTF-8');
// Ensure vendor libraries exist
$autoload = __DIR__ . '/vendor/autoload.php';
if (!is_file($autoload)) {
@ -43,23 +29,23 @@ if (!is_file($autoload)) {
// Register the auto-loader.
$loader = require $autoload;
// Set timezone to default, falls back to system if php.ini not set
date_default_timezone_set(@date_default_timezone_get());
// Set internal encoding.
@ini_set('default_charset', 'UTF-8');
mb_internal_encoding('UTF-8');
use Grav\Common\Grav;
use RocketTheme\Toolbox\Event\Event;
// Get the Grav instance
$grav = Grav::instance(
array(
'loader' => $loader
)
);
$grav = Grav::instance(array('loader' => $loader));
// Process the page
try {
$grav->process();
} catch (\Error $e) {
$grav->fireEvent('onFatalException', new Event(array('exception' => $e)));
throw $e;
} catch (\Exception $e) {
} catch (\Error|\Exception $e) {
$grav->fireEvent('onFatalException', new Event(array('exception' => $e)));
throw $e;
}

1
node_modules/.bin/in-install generated vendored

@ -1 +0,0 @@
../in-publish/in-install.js

4
node_modules/.bin/in-install generated vendored

@ -0,0 +1,4 @@
#!/usr/bin/env node
'use strict'
var inInstall = require('./index.js').inInstall
process.exit(inInstall() ? 0 : 1)

1
node_modules/.bin/in-publish generated vendored

@ -1 +0,0 @@
../in-publish/in-publish.js

4
node_modules/.bin/in-publish generated vendored

@ -0,0 +1,4 @@
#!/usr/bin/env node
'use strict'
var inPublish = require('./index.js').inPublish
process.exit(inPublish() ? 0 : 1)

1
node_modules/.bin/mkdirp generated vendored

@ -1 +0,0 @@
../mkdirp/bin/cmd.js

33
node_modules/.bin/mkdirp generated vendored

@ -0,0 +1,33 @@
#!/usr/bin/env node
var mkdirp = require('../');
var minimist = require('minimist');
var fs = require('fs');
var argv = minimist(process.argv.slice(2), {
alias: { m: 'mode', h: 'help' },
string: [ 'mode' ]
});
if (argv.help) {
fs.createReadStream(__dirname + '/usage.txt').pipe(process.stdout);
return;
}
var paths = argv._.slice();
var mode = argv.mode ? parseInt(argv.mode, 8) : undefined;
(function next () {
if (paths.length === 0) return;
var p = paths.shift();
if (mode === undefined) mkdirp(p, cb)
else mkdirp(p, mode, cb)
function cb (err) {
if (err) {
console.error(err.message);
process.exit(1);
}
else next();
}
})();

1
node_modules/.bin/node-gyp generated vendored

@ -1 +0,0 @@
../node-gyp/bin/node-gyp.js

148
node_modules/.bin/node-gyp generated vendored

@ -0,0 +1,148 @@
#!/usr/bin/env node
/**
* Set the title.
*/
process.title = 'node-gyp'
/**
* Module dependencies.
*/
var gyp = require('../')
var log = require('npmlog')
var osenv = require('osenv')
var path = require('path')
/**
* Process and execute the selected commands.
*/
var prog = gyp()
var completed = false
prog.parseArgv(process.argv)
prog.devDir = prog.opts.devdir
var homeDir = osenv.home()
if (prog.devDir) {
prog.devDir = prog.devDir.replace(/^~/, homeDir)
} else if (homeDir) {
prog.devDir = path.resolve(homeDir, '.node-gyp')
} else {
throw new Error(
"node-gyp requires that the user's home directory is specified " +
"in either of the environmental variables HOME or USERPROFILE. " +
"Overide with: --devdir /path/to/.node-gyp")
}
if (prog.todo.length === 0) {
if (~process.argv.indexOf('-v') || ~process.argv.indexOf('--version')) {
console.log('v%s', prog.version)
} else {
console.log('%s', prog.usage())
}
return process.exit(0)
}
log.info('it worked if it ends with', 'ok')
log.verbose('cli', process.argv)
log.info('using', 'node-gyp@%s', prog.version)
log.info('using', 'node@%s | %s | %s', process.versions.node, process.platform, process.arch)
/**
* Change dir if -C/--directory was passed.
*/
var dir = prog.opts.directory
if (dir) {
var fs = require('fs')
try {
var stat = fs.statSync(dir)
if (stat.isDirectory()) {
log.info('chdir', dir)
process.chdir(dir)
} else {
log.warn('chdir', dir + ' is not a directory')
}
} catch (e) {
if (e.code === 'ENOENT') {
log.warn('chdir', dir + ' is not a directory')
} else {
log.warn('chdir', 'error during chdir() "%s"', e.message)
}
}
}
function run () {
var command = prog.todo.shift()
if (!command) {
// done!
completed = true
log.info('ok')
return
}
prog.commands[command.name](command.args, function (err) {
if (err) {
log.error(command.name + ' error')
log.error('stack', err.stack)
errorMessage()
log.error('not ok')
return process.exit(1)
}
if (command.name == 'list') {
var versions = arguments[1]
if (versions.length > 0) {
versions.forEach(function (version) {
console.log(version)
})
} else {
console.log('No node development files installed. Use `node-gyp install` to install a version.')
}
} else if (arguments.length >= 2) {
console.log.apply(console, [].slice.call(arguments, 1))
}
// now run the next command in the queue
process.nextTick(run)
})
}
process.on('exit', function (code) {
if (!completed && !code) {
log.error('Completion callback never invoked!')
issueMessage()
process.exit(6)
}
})
process.on('uncaughtException', function (err) {
log.error('UNCAUGHT EXCEPTION')
log.error('stack', err.stack)
issueMessage()
process.exit(7)
})
function errorMessage () {
// copied from npm's lib/util/error-handler.js
var os = require('os')
log.error('System', os.type() + ' ' + os.release())
log.error('command', process.argv
.map(JSON.stringify).join(' '))
log.error('cwd', process.cwd())
log.error('node -v', process.version)
log.error('node-gyp -v', 'v' + prog.package.version)
}
function issueMessage () {
errorMessage()
log.error('', [ 'This is a bug in `node-gyp`.'
, 'Try to update node-gyp and file an Issue if it does not help:'
, ' <https://github.com/nodejs/node-gyp/issues>'
].join('\n'))
}
// start running the given commands!
run()

1
node_modules/.bin/node-sass generated vendored

@ -1 +0,0 @@
../node-sass/bin/node-sass

412
node_modules/.bin/node-sass generated vendored

@ -0,0 +1,412 @@
#!/usr/bin/env node
var Emitter = require('events').EventEmitter,
forEach = require('async-foreach').forEach,
Gaze = require('gaze'),
meow = require('meow'),
util = require('util'),
path = require('path'),
glob = require('glob'),
sass = require('../lib'),
render = require('../lib/render'),
watcher = require('../lib/watcher'),
stdout = require('stdout-stream'),
stdin = require('get-stdin'),
fs = require('fs');
/**
* Initialize CLI
*/
var cli = meow({
pkg: '../package.json',
version: sass.info,
help: [
'Usage:',
' node-sass [options] <input.scss>',
' cat <input.scss> | node-sass [options] > output.css',
'',
'Example: Compile foobar.scss to foobar.css',
' node-sass --output-style compressed foobar.scss > foobar.css',
' cat foobar.scss | node-sass --output-style compressed > foobar.css',
'',
'Example: Watch the sass directory for changes, compile with sourcemaps to the css directory',
' node-sass --watch --recursive --output css',
' --source-map true --source-map-contents sass',
'',
'Options',
' -w, --watch Watch a directory or file',
' -r, --recursive Recursively watch directories or files',
' -o, --output Output directory',
' -x, --omit-source-map-url Omit source map URL comment from output',
' -i, --indented-syntax Treat data from stdin as sass code (versus scss)',
' -q, --quiet Suppress log output except on error',
' -v, --version Prints version info',
' --output-style CSS output style (nested | expanded | compact | compressed)',
' --indent-type Indent type for output CSS (space | tab)',
' --indent-width Indent width; number of spaces or tabs (maximum value: 10)',
' --linefeed Linefeed style (cr | crlf | lf | lfcr)',
' --source-comments Include debug info in output',
' --source-map Emit source map (boolean, or path to output .map file)',
' --source-map-contents Embed include contents in map',
' --source-map-embed Embed sourceMappingUrl as data URI',
' --source-map-root Base path, will be emitted in source-map as is',
' --include-path Path to look for imported files',
' --follow Follow symlinked directories',
' --precision The amount of precision allowed in decimal numbers',
' --error-bell Output a bell character on errors',
' --importer Path to .js file containing custom importer',
' --functions Path to .js file containing custom functions',
' --help Print usage info'
].join('\n')
}, {
boolean: [
'error-bell',
'follow',
'indented-syntax',
'omit-source-map-url',
'quiet',
'recursive',
'source-map-embed',
'source-map-contents',
'source-comments',
'watch'
],
string: [
'functions',
'importer',
'include-path',
'indent-type',
'linefeed',
'output',
'output-style',
'precision',
'source-map-root'
],
alias: {
c: 'source-comments',
i: 'indented-syntax',
q: 'quiet',
o: 'output',
r: 'recursive',
x: 'omit-source-map-url',
v: 'version',
w: 'watch'
},
default: {
'include-path': process.cwd(),
'indent-type': 'space',
'indent-width': 2,
linefeed: 'lf',
'output-style': 'nested',
precision: 5,
quiet: false,
recursive: true
}
});
/**
* Is a Directory
*
* @param {String} filePath
* @returns {Boolean}
* @api private
*/
function isDirectory(filePath) {
var isDir = false;
try {
var absolutePath = path.resolve(filePath);
isDir = fs.statSync(absolutePath).isDirectory();
} catch (e) {
isDir = e.code === 'ENOENT';
}
return isDir;
}
/**
* Get correct glob pattern
*
* @param {Object} options
* @returns {String}
* @api private
*/
function globPattern(options) {
return options.recursive ? '**/*.{sass,scss}' : '*.{sass,scss}';
}
/**
* Create emitter
*
* @api private
*/
function getEmitter() {
var emitter = new Emitter();
emitter.on('error', function(err) {
if (options.errorBell) {
err += '\x07';
}
console.error(err);
if (!options.watch) {
process.exit(1);
}
});
emitter.on('warn', function(data) {
if (!options.quiet) {
console.warn(data);
}
});
emitter.on('info', function(data) {
if (!options.quiet) {
console.info(data);
}
});
emitter.on('log', stdout.write.bind(stdout));
return emitter;
}
/**
* Construct options
*
* @param {Array} arguments
* @param {Object} options
* @api private
*/
function getOptions(args, options) {
var cssDir, sassDir, file, mapDir;
options.src = args[0];
if (args[1]) {
options.dest = path.resolve(args[1]);
} else if (options.output) {
options.dest = path.join(
path.resolve(options.output),
[path.basename(options.src, path.extname(options.src)), '.css'].join('')); // replace ext.
}
if (options.directory) {
sassDir = path.resolve(options.directory);
file = path.relative(sassDir, args[0]);
cssDir = path.resolve(options.output);
options.dest = path.join(cssDir, file).replace(path.extname(file), '.css');
}
if (options.sourceMap) {
if(!options.sourceMapOriginal) {
options.sourceMapOriginal = options.sourceMap;
}
// check if sourceMap path ends with .map to avoid isDirectory false-positive
var sourceMapIsDirectory = options.sourceMapOriginal.indexOf('.map', options.sourceMapOriginal.length - 4) === -1 && isDirectory(options.sourceMapOriginal);
if (options.sourceMapOriginal === 'true') {
options.sourceMap = options.dest + '.map';
} else if (!sourceMapIsDirectory) {
options.sourceMap = path.resolve(options.sourceMapOriginal);
} else if (sourceMapIsDirectory) {
if (!options.directory) {
options.sourceMap = path.resolve(options.sourceMapOriginal, path.basename(options.dest) + '.map');
} else {
sassDir = path.resolve(options.directory);
file = path.relative(sassDir, args[0]);
mapDir = path.resolve(options.sourceMapOriginal);
options.sourceMap = path.join(mapDir, file).replace(path.extname(file), '.css.map');
}
}
}
return options;
}
/**
* Watch
*
* @param {Object} options
* @param {Object} emitter
* @api private
*/
function watch(options, emitter) {
var handler = function(files) {
files.added.forEach(function(file) {
var watch = gaze.watched();
Object.keys(watch).forEach(function (dir) {
if (watch[dir].indexOf(file) !== -1) {
gaze.add(file);
}
});
});
files.changed.forEach(function(file) {
if (path.basename(file)[0] !== '_') {
renderFile(file, options, emitter);
}
});
files.removed.forEach(function(file) {
gaze.remove(file);
});
};
var gaze = new Gaze();
gaze.add(watcher.reset(options));
gaze.on('error', emitter.emit.bind(emitter, 'error'));
gaze.on('changed', function(file) {
handler(watcher.changed(file));
});
gaze.on('added', function(file) {
handler(watcher.added(file));
});
gaze.on('deleted', function(file) {
handler(watcher.removed(file));
});
}
/**
* Run
*
* @param {Object} options
* @param {Object} emitter
* @api private
*/
function run(options, emitter) {
if (!Array.isArray(options.includePath)) {
options.includePath = [options.includePath];
}
if (options.directory) {
if (!options.output) {
emitter.emit('error', 'An output directory must be specified when compiling a directory');
}
if (!isDirectory(options.output)) {
emitter.emit('error', 'An output directory must be specified when compiling a directory');
}
}
if (options.sourceMapOriginal && options.directory && !isDirectory(options.sourceMapOriginal) && options.sourceMapOriginal !== 'true') {
emitter.emit('error', 'The --source-map option must be either a boolean or directory when compiling a directory');
}
if (options.importer) {
if ((path.resolve(options.importer) === path.normalize(options.importer).replace(/(.+)([\/|\\])$/, '$1'))) {
options.importer = require(options.importer);
} else {
options.importer = require(path.resolve(options.importer));
}
}
if (options.functions) {
if ((path.resolve(options.functions) === path.normalize(options.functions).replace(/(.+)([\/|\\])$/, '$1'))) {
options.functions = require(options.functions);
} else {
options.functions = require(path.resolve(options.functions));
}
}
if (options.watch) {
watch(options, emitter);
} else if (options.directory) {
renderDir(options, emitter);
} else {
render(options, emitter);
}
}
/**
* Render a file
*
* @param {String} file
* @param {Object} options
* @param {Object} emitter
* @api private
*/
function renderFile(file, options, emitter) {
options = getOptions([path.resolve(file)], options);
if (options.watch && !options.quiet) {
emitter.emit('info', util.format('=> changed: %s', file));
}
render(options, emitter);
}
/**
* Render all sass files in a directory
*
* @param {Object} options
* @param {Object} emitter
* @api private
*/
function renderDir(options, emitter) {
var globPath = path.resolve(options.directory, globPattern(options));
glob(globPath, { ignore: '**/_*', follow: options.follow }, function(err, files) {
if (err) {
return emitter.emit('error', util.format('You do not have permission to access this path: %s.', err.path));
} else if (!files.length) {
return emitter.emit('error', 'No input file was found.');
}
forEach(files, function(subject) {
emitter.once('done', this.async());
renderFile(subject, options, emitter);
}, function(successful, arr) {
var outputDir = path.join(process.cwd(), options.output);
if (!options.quiet) {
emitter.emit('info', util.format('Wrote %s CSS files to %s', arr.length, outputDir));
}
process.exit();
});
});
}
/**
* Arguments and options
*/
var options = getOptions(cli.input, cli.flags);
var emitter = getEmitter();
/**
* Show usage if no arguments are supplied
*/
if (!options.src && process.stdin.isTTY) {
emitter.emit('error', [
'Provide a Sass file to render',
'',
'Example: Compile foobar.scss to foobar.css',
' node-sass --output-style compressed foobar.scss > foobar.css',
' cat foobar.scss | node-sass --output-style compressed > foobar.css',
'',
'Example: Watch the sass directory for changes, compile with sourcemaps to the css directory',
' node-sass --watch --recursive --output css',
' --source-map true --source-map-contents sass',
].join('\n'));
}
/**
* Apply arguments
*/
if (options.src) {
if (isDirectory(options.src)) {
options.directory = options.src;
}
run(options, emitter);
} else if (!process.stdin.isTTY) {
stdin(function(data) {
options.data = data;
options.stdin = true;
run(options, emitter);
});
}

1
node_modules/.bin/nopt generated vendored

@ -1 +0,0 @@
../nopt/bin/nopt.js

54
node_modules/.bin/nopt generated vendored

@ -0,0 +1,54 @@
#!/usr/bin/env node
var nopt = require("../lib/nopt")
, path = require("path")
, types = { num: Number
, bool: Boolean
, help: Boolean
, list: Array
, "num-list": [Number, Array]
, "str-list": [String, Array]
, "bool-list": [Boolean, Array]
, str: String
, clear: Boolean
, config: Boolean
, length: Number
, file: path
}
, shorthands = { s: [ "--str", "astring" ]
, b: [ "--bool" ]
, nb: [ "--no-bool" ]
, tft: [ "--bool-list", "--no-bool-list", "--bool-list", "true" ]
, "?": ["--help"]
, h: ["--help"]
, H: ["--help"]
, n: [ "--num", "125" ]
, c: ["--config"]
, l: ["--length"]
, f: ["--file"]
}
, parsed = nopt( types
, shorthands
, process.argv
, 2 )
console.log("parsed", parsed)
if (parsed.help) {
console.log("")
console.log("nopt cli tester")
console.log("")
console.log("types")
console.log(Object.keys(types).map(function M (t) {
var type = types[t]
if (Array.isArray(type)) {
return [t, type.map(function (type) { return type.name })]
}
return [t, type && type.name]
}).reduce(function (s, i) {
s[i[0]] = i[1]
return s
}, {}))
console.log("")
console.log("shorthands")
console.log(shorthands)
}

1
node_modules/.bin/not-in-install generated vendored

@ -1 +0,0 @@
../in-publish/not-in-install.js

4
node_modules/.bin/not-in-install generated vendored

@ -0,0 +1,4 @@
#!/usr/bin/env node
'use strict'
var inInstall = require('./index.js').inInstall
process.exit(inInstall() ? 1 : 0)

1
node_modules/.bin/not-in-publish generated vendored

@ -1 +0,0 @@
../in-publish/not-in-publish.js

4
node_modules/.bin/not-in-publish generated vendored

@ -0,0 +1,4 @@
#!/usr/bin/env node
'use strict'
var inPublish = require('./index.js').inPublish
process.exit(inPublish() ? 1 : 0)

1
node_modules/.bin/rimraf generated vendored

@ -1 +0,0 @@
../rimraf/bin.js

50
node_modules/.bin/rimraf generated vendored

@ -0,0 +1,50 @@
#!/usr/bin/env node
var rimraf = require('./')
var help = false
var dashdash = false
var noglob = false
var args = process.argv.slice(2).filter(function(arg) {
if (dashdash)
return !!arg
else if (arg === '--')
dashdash = true
else if (arg === '--no-glob' || arg === '-G')
noglob = true
else if (arg === '--glob' || arg === '-g')
noglob = false
else if (arg.match(/^(-+|\/)(h(elp)?|\?)$/))
help = true
else
return !!arg
})
if (help || args.length === 0) {
// If they didn't ask for help, then this is not a "success"
var log = help ? console.log : console.error
log('Usage: rimraf <path> [<path> ...]')
log('')
log(' Deletes all files and folders at "path" recursively.')
log('')
log('Options:')
log('')
log(' -h, --help Display this usage info')
log(' -G, --no-glob Do not expand glob patterns in arguments')
log(' -g, --glob Expand glob patterns in arguments (default)')
process.exit(help ? 0 : 1)
} else
go(0)
function go (n) {
if (n >= args.length)
return
var options = {}
if (noglob)
options = { glob: false }
rimraf(args[n], options, function (er) {
if (er)
throw er
go(n+1)
})
}

1
node_modules/.bin/sassgraph generated vendored

@ -1 +0,0 @@
../sass-graph/bin/sassgraph

124
node_modules/.bin/sassgraph generated vendored

@ -0,0 +1,124 @@
#!/usr/bin/env node
var fs = require('fs');
var path = require('path');
var command, directory, file;
var yargs = require('yargs')
.usage('Usage: $0 <command> [options] <dir> [file]')
// .demand(1)
.command('ancestors', 'Output the ancestors')
.command('descendents', 'Output the descendents')
.example('$0 ancestors -I src src/ src/_footer.scss', 'outputs the ancestors of src/_footer.scss')
.option('I', {
alias: 'load-path',
default: [process.cwd()],
describe: 'Add directories to the sass load path',
type: 'array',
})
.option('e', {
alias: 'extensions',
default: ['scss', 'css', 'sass'],
describe: 'File extensions to include in the graph',
type: 'array',
})
.option('f', {
alias: 'follow',
default: false,
describe: 'Follow symbolic links',
type: 'bool',
})
.option('j', {
alias: 'json',
default: false,
describe: 'Output the index in json',
type: 'bool',
})
.version(function() {
return require('../package').version;
})
.alias('v', 'version')
.help('h')
.alias('h', 'help');
var argv = yargs.argv;
if (argv._.length === 0) {
yargs.showHelp();
process.exit(1);
}
if (['ancestors', 'descendents'].indexOf(argv._[0]) !== -1) {
command = argv._.shift();
}
if (argv._ && path.extname(argv._[0]) === '') {
directory = argv._.shift();
}
if (argv._ && path.extname(argv._[0])) {
file = argv._.shift();
}
try {
if (!directory) {
throw new Error('Missing directory');
}
if (!command && !argv.json) {
throw new Error('Missing command');
}
if (!file && (command === 'ancestors' || command === 'descendents')) {
throw new Error(command + ' command requires a file');
}
var loadPaths = argv.loadPath;
if(process.env.SASS_PATH) {
loadPaths = loadPaths.concat(process.env.SASS_PATH.split(/:/).map(function(f) {
return path.resolve(f);
}));
}
var graph = require('../').parseDir(directory, {
extensions: argv.extensions,
loadPaths: loadPaths,
follow: argv.follow,
});
if(argv.json) {
console.log(JSON.stringify(graph.index, null, 4));
process.exit(0);
}
if (command === 'ancestors') {
graph.visitAncestors(path.resolve(file), function(f) {
console.log(f);
});
}
if (command === 'descendents') {
graph.visitDescendents(path.resolve(file), function(f) {
console.log(f);
});
}
} catch(e) {
if (e.code === 'ENOENT') {
console.error('Error: no such file or directory "' + e.path + '"');
}
else {
console.log('Error: ' + e.message);
}
// console.log(e.stack);
process.exit(1);
}

1
node_modules/.bin/semver generated vendored

@ -1 +0,0 @@
../semver/bin/semver

160
node_modules/.bin/semver generated vendored

@ -0,0 +1,160 @@
#!/usr/bin/env node
// Standalone semver comparison program.
// Exits successfully and prints matching version(s) if
// any supplied version is valid and passes all tests.
var argv = process.argv.slice(2)
var versions = []
var range = []
var inc = null
var version = require('../package.json').version
var loose = false
var includePrerelease = false
var coerce = false
var identifier
var semver = require('../semver')
var reverse = false
var options = {}
main()
function main () {
if (!argv.length) return help()
while (argv.length) {
var a = argv.shift()
var indexOfEqualSign = a.indexOf('=')
if (indexOfEqualSign !== -1) {
a = a.slice(0, indexOfEqualSign)
argv.unshift(a.slice(indexOfEqualSign + 1))
}
switch (a) {
case '-rv': case '-rev': case '--rev': case '--reverse':
reverse = true
break
case '-l': case '--loose':
loose = true
break
case '-p': case '--include-prerelease':
includePrerelease = true
break
case '-v': case '--version':
versions.push(argv.shift())
break
case '-i': case '--inc': case '--increment':
switch (argv[0]) {
case 'major': case 'minor': case 'patch': case 'prerelease':
case 'premajor': case 'preminor': case 'prepatch':
inc = argv.shift()
break
default:
inc = 'patch'
break
}
break
case '--preid':
identifier = argv.shift()
break
case '-r': case '--range':
range.push(argv.shift())
break
case '-c': case '--coerce':
coerce = true
break
case '-h': case '--help': case '-?':
return help()
default:
versions.push(a)
break
}
}
var options = { loose: loose, includePrerelease: includePrerelease }
versions = versions.map(function (v) {
return coerce ? (semver.coerce(v) || { version: v }).version : v
}).filter(function (v) {
return semver.valid(v)
})
if (!versions.length) return fail()
if (inc && (versions.length !== 1 || range.length)) { return failInc() }
for (var i = 0, l = range.length; i < l; i++) {
versions = versions.filter(function (v) {
return semver.satisfies(v, range[i], options)
})
if (!versions.length) return fail()
}
return success(versions)
}
function failInc () {
console.error('--inc can only be used on a single version with no range')
fail()
}
function fail () { process.exit(1) }
function success () {
var compare = reverse ? 'rcompare' : 'compare'
versions.sort(function (a, b) {
return semver[compare](a, b, options)
}).map(function (v) {
return semver.clean(v, options)
}).map(function (v) {
return inc ? semver.inc(v, inc, options, identifier) : v
}).forEach(function (v, i, _) { console.log(v) })
}
function help () {
console.log(['SemVer ' + version,
'',
'A JavaScript implementation of the https://semver.org/ specification',
'Copyright Isaac Z. Schlueter',
'',
'Usage: semver [options] <version> [<version> [...]]',
'Prints valid versions sorted by SemVer precedence',
'',
'Options:',
'-r --range <range>',
' Print versions that match the specified range.',
'',
'-i --increment [<level>]',
' Increment a version by the specified level. Level can',
' be one of: major, minor, patch, premajor, preminor,',
" prepatch, or prerelease. Default level is 'patch'.",
' Only one version may be specified.',
'',
'--preid <identifier>',
' Identifier to be used to prefix premajor, preminor,',
' prepatch or prerelease version increments.',
'',
'-l --loose',
' Interpret versions and ranges loosely',
'',
'-p --include-prerelease',
' Always include prerelease versions in range matching',
'',
'-c --coerce',
' Coerce a string into SemVer if possible',
' (does not imply --loose)',
'',
'Program exits successfully if any valid version satisfies',
'all supplied ranges, and prints all satisfying versions.',
'',
'If no satisfying versions are found, then exits failure.',
'',
'Versions are printed in ascending order, so supplying',
'multiple versions to the utility will just sort them.'
].join('\n'))
}

1
node_modules/.bin/sshpk-conv generated vendored

@ -1 +0,0 @@
../sshpk/bin/sshpk-conv

243
node_modules/.bin/sshpk-conv generated vendored

@ -0,0 +1,243 @@
#!/usr/bin/env node
// -*- mode: js -*-
// vim: set filetype=javascript :
// Copyright 2018 Joyent, Inc. All rights reserved.
var dashdash = require('dashdash');
var sshpk = require('../lib/index');
var fs = require('fs');
var path = require('path');
var tty = require('tty');
var readline = require('readline');
var getPassword = require('getpass').getPass;
var options = [
{
names: ['outformat', 't'],
type: 'string',
help: 'Output format'
},
{
names: ['informat', 'T'],
type: 'string',
help: 'Input format'
},
{
names: ['file', 'f'],
type: 'string',
help: 'Input file name (default stdin)'
},
{
names: ['out', 'o'],
type: 'string',
help: 'Output file name (default stdout)'
},
{
names: ['private', 'p'],
type: 'bool',
help: 'Produce a private key as output'
},
{
names: ['derive', 'd'],
type: 'string',
help: 'Output a new key derived from this one, with given algo'
},
{
names: ['identify', 'i'],
type: 'bool',
help: 'Print key metadata instead of converting'
},
{
names: ['fingerprint', 'F'],
type: 'bool',
help: 'Output key fingerprint'
},
{
names: ['hash', 'H'],
type: 'string',
help: 'Hash function to use for key fingeprint with -F'
},
{
names: ['spki', 's'],
type: 'bool',
help: 'With -F, generates an SPKI fingerprint instead of SSH'
},
{
names: ['comment', 'c'],
type: 'string',
help: 'Set key comment, if output format supports'
},
{
names: ['help', 'h'],
type: 'bool',
help: 'Shows this help text'
}
];
if (require.main === module) {
var parser = dashdash.createParser({
options: options
});
try {
var opts = parser.parse(process.argv);
} catch (e) {
console.error('sshpk-conv: error: %s', e.message);
process.exit(1);
}
if (opts.help || opts._args.length > 1) {
var help = parser.help({}).trimRight();
console.error('sshpk-conv: converts between SSH key formats\n');
console.error(help);
console.error('\navailable key formats:');
console.error(' - pem, pkcs1 eg id_rsa');
console.error(' - ssh eg id_rsa.pub');
console.error(' - pkcs8 format you want for openssl');
console.error(' - openssh like output of ssh-keygen -o');
console.error(' - rfc4253 raw OpenSSH wire format');
console.error(' - dnssec dnssec-keygen format');
console.error(' - putty PuTTY ppk format');
console.error('\navailable fingerprint formats:');
console.error(' - hex colon-separated hex for SSH');
console.error(' straight hex for SPKI');
console.error(' - base64 SHA256:* format from OpenSSH');
process.exit(1);
}
/*
* Key derivation can only be done on private keys, so use of the -d
* option necessarily implies -p.
*/
if (opts.derive)
opts.private = true;
var inFile = process.stdin;
var inFileName = 'stdin';
var inFilePath;
if (opts.file) {
inFilePath = opts.file;
} else if (opts._args.length === 1) {
inFilePath = opts._args[0];
}
if (inFilePath)
inFileName = path.basename(inFilePath);
try {
if (inFilePath) {
fs.accessSync(inFilePath, fs.R_OK);
inFile = fs.createReadStream(inFilePath);
}
} catch (e) {
ifError(e, 'error opening input file');
}
var outFile = process.stdout;
try {
if (opts.out && !opts.identify) {
fs.accessSync(path.dirname(opts.out), fs.W_OK);
outFile = fs.createWriteStream(opts.out);
}
} catch (e) {
ifError(e, 'error opening output file');
}
var bufs = [];
inFile.on('readable', function () {
var data;
while ((data = inFile.read()))
bufs.push(data);
});
var parseOpts = {};
parseOpts.filename = inFileName;
inFile.on('end', function processKey() {
var buf = Buffer.concat(bufs);
var fmt = 'auto';
if (opts.informat)
fmt = opts.informat;
var f = sshpk.parseKey;
if (opts.private)
f = sshpk.parsePrivateKey;
try {
var key = f(buf, fmt, parseOpts);
} catch (e) {
if (e.name === 'KeyEncryptedError') {
getPassword(function (err, pw) {
if (err)
ifError(err);
parseOpts.passphrase = pw;
processKey();
});
return;
}
ifError(e);
}
if (opts.derive)
key = key.derive(opts.derive);
if (opts.comment)
key.comment = opts.comment;
if (opts.identify) {
var kind = 'public';
if (sshpk.PrivateKey.isPrivateKey(key))
kind = 'private';
console.log('%s: a %d bit %s %s key', inFileName,
key.size, key.type.toUpperCase(), kind);
if (key.type === 'ecdsa')
console.log('ECDSA curve: %s', key.curve);
if (key.comment)
console.log('Comment: %s', key.comment);
console.log('SHA256 fingerprint: ' +
key.fingerprint('sha256').toString());
console.log('MD5 fingerprint: ' +
key.fingerprint('md5').toString());
console.log('SPKI-SHA256 fingerprint: ' +
key.fingerprint('sha256', 'spki').toString());
process.exit(0);
return;
}
if (opts.fingerprint) {
var hash = opts.hash;
var type = opts.spki ? 'spki' : 'ssh';
var format = opts.outformat;
var fp = key.fingerprint(hash, type).toString(format);
outFile.write(fp);
outFile.write('\n');
outFile.once('drain', function () {
process.exit(0);
});
return;
}
fmt = undefined;
if (opts.outformat)
fmt = opts.outformat;
outFile.write(key.toBuffer(fmt));
if (fmt === 'ssh' ||
(!opts.private && fmt === undefined))
outFile.write('\n');
outFile.once('drain', function () {
process.exit(0);
});
});
}
function ifError(e, txt) {
if (txt)
txt = txt + ': ';
else
txt = '';
console.error('sshpk-conv: ' + txt + e.name + ': ' + e.message);
if (process.env['DEBUG'] || process.env['V']) {
console.error(e.stack);
if (e.innerErr)
console.error(e.innerErr.stack);
}
process.exit(1);
}

1
node_modules/.bin/sshpk-sign generated vendored

@ -1 +0,0 @@
../sshpk/bin/sshpk-sign

191
node_modules/.bin/sshpk-sign generated vendored

@ -0,0 +1,191 @@
#!/usr/bin/env node
// -*- mode: js -*-
// vim: set filetype=javascript :
// Copyright 2015 Joyent, Inc. All rights reserved.
var dashdash = require('dashdash');
var sshpk = require('../lib/index');
var fs = require('fs');
var path = require('path');
var getPassword = require('getpass').getPass;
var options = [
{
names: ['hash', 'H'],
type: 'string',
help: 'Hash algorithm (sha1, sha256, sha384, sha512)'
},
{
names: ['verbose', 'v'],
type: 'bool',
help: 'Display verbose info about key and hash used'
},
{
names: ['identity', 'i'],
type: 'string',
help: 'Path to key to use'
},
{
names: ['file', 'f'],
type: 'string',
help: 'Input filename'
},
{
names: ['out', 'o'],
type: 'string',
help: 'Output filename'
},
{
names: ['format', 't'],
type: 'string',
help: 'Signature format (asn1, ssh, raw)'
},
{
names: ['binary', 'b'],
type: 'bool',
help: 'Output raw binary instead of base64'
},
{
names: ['help', 'h'],
type: 'bool',
help: 'Shows this help text'
}
];
var parseOpts = {};
if (require.main === module) {
var parser = dashdash.createParser({
options: options
});
try {
var opts = parser.parse(process.argv);
} catch (e) {
console.error('sshpk-sign: error: %s', e.message);
process.exit(1);
}
if (opts.help || opts._args.length > 1) {
var help = parser.help({}).trimRight();
console.error('sshpk-sign: sign data using an SSH key\n');
console.error(help);
process.exit(1);
}
if (!opts.identity) {
var help = parser.help({}).trimRight();
console.error('sshpk-sign: the -i or --identity option ' +
'is required\n');
console.error(help);
process.exit(1);
}
var keyData = fs.readFileSync(opts.identity);
parseOpts.filename = opts.identity;
run();
}
function run() {
var key;
try {
key = sshpk.parsePrivateKey(keyData, 'auto', parseOpts);
} catch (e) {
if (e.name === 'KeyEncryptedError') {
getPassword(function (err, pw) {
parseOpts.passphrase = pw;
run();
});
return;
}
console.error('sshpk-sign: error loading private key "' +
opts.identity + '": ' + e.name + ': ' + e.message);
process.exit(1);
}
var hash = opts.hash || key.defaultHashAlgorithm();
var signer;
try {
signer = key.createSign(hash);
} catch (e) {
console.error('sshpk-sign: error creating signer: ' +
e.name + ': ' + e.message);
process.exit(1);
}
if (opts.verbose) {
console.error('sshpk-sign: using %s-%s with a %d bit key',
key.type, hash, key.size);
}
var inFile = process.stdin;
var inFileName = 'stdin';
var inFilePath;
if (opts.file) {
inFilePath = opts.file;
} else if (opts._args.length === 1) {
inFilePath = opts._args[0];
}
if (inFilePath)
inFileName = path.basename(inFilePath);
try {
if (inFilePath) {
fs.accessSync(inFilePath, fs.R_OK);
inFile = fs.createReadStream(inFilePath);
}
} catch (e) {
console.error('sshpk-sign: error opening input file' +
': ' + e.name + ': ' + e.message);
process.exit(1);
}
var outFile = process.stdout;
try {
if (opts.out && !opts.identify) {
fs.accessSync(path.dirname(opts.out), fs.W_OK);
outFile = fs.createWriteStream(opts.out);
}
} catch (e) {
console.error('sshpk-sign: error opening output file' +
': ' + e.name + ': ' + e.message);
process.exit(1);
}
inFile.pipe(signer);
inFile.on('end', function () {
var sig;
try {
sig = signer.sign();
} catch (e) {
console.error('sshpk-sign: error signing data: ' +
e.name + ': ' + e.message);
process.exit(1);
}
var fmt = opts.format || 'asn1';
var output;
try {
output = sig.toBuffer(fmt);
if (!opts.binary)
output = output.toString('base64');
} catch (e) {
console.error('sshpk-sign: error converting signature' +
' to ' + fmt + ' format: ' + e.name + ': ' +
e.message);
process.exit(1);
}
outFile.write(output);
if (!opts.binary)
outFile.write('\n');
outFile.once('drain', function () {
process.exit(0);
});
});
}

1
node_modules/.bin/sshpk-verify generated vendored

@ -1 +0,0 @@
../sshpk/bin/sshpk-verify

167
node_modules/.bin/sshpk-verify generated vendored

@ -0,0 +1,167 @@
#!/usr/bin/env node
// -*- mode: js -*-
// vim: set filetype=javascript :
// Copyright 2015 Joyent, Inc. All rights reserved.
var dashdash = require('dashdash');
var sshpk = require('../lib/index');
var fs = require('fs');
var path = require('path');
var Buffer = require('safer-buffer').Buffer;
var options = [
{
names: ['hash', 'H'],
type: 'string',
help: 'Hash algorithm (sha1, sha256, sha384, sha512)'
},
{
names: ['verbose', 'v'],
type: 'bool',
help: 'Display verbose info about key and hash used'
},
{
names: ['identity', 'i'],
type: 'string',
help: 'Path to (public) key to use'
},
{
names: ['file', 'f'],
type: 'string',
help: 'Input filename'
},
{
names: ['format', 't'],
type: 'string',
help: 'Signature format (asn1, ssh, raw)'
},
{
names: ['signature', 's'],
type: 'string',
help: 'base64-encoded signature data'
},
{
names: ['help', 'h'],
type: 'bool',
help: 'Shows this help text'
}
];
if (require.main === module) {
var parser = dashdash.createParser({
options: options
});
try {
var opts = parser.parse(process.argv);
} catch (e) {
console.error('sshpk-verify: error: %s', e.message);
process.exit(3);
}
if (opts.help || opts._args.length > 1) {
var help = parser.help({}).trimRight();
console.error('sshpk-verify: sign data using an SSH key\n');
console.error(help);
process.exit(3);
}
if (!opts.identity) {
var help = parser.help({}).trimRight();
console.error('sshpk-verify: the -i or --identity option ' +
'is required\n');
console.error(help);
process.exit(3);
}
if (!opts.signature) {
var help = parser.help({}).trimRight();
console.error('sshpk-verify: the -s or --signature option ' +
'is required\n');
console.error(help);
process.exit(3);
}
var keyData = fs.readFileSync(opts.identity);
var key;
try {
key = sshpk.parseKey(keyData);
} catch (e) {
console.error('sshpk-verify: error loading key "' +
opts.identity + '": ' + e.name + ': ' + e.message);
process.exit(2);
}
var fmt = opts.format || 'asn1';
var sigData = Buffer.from(opts.signature, 'base64');
var sig;
try {
sig = sshpk.parseSignature(sigData, key.type, fmt);
} catch (e) {
console.error('sshpk-verify: error parsing signature: ' +
e.name + ': ' + e.message);
process.exit(2);
}
var hash = opts.hash || key.defaultHashAlgorithm();
var verifier;
try {
verifier = key.createVerify(hash);
} catch (e) {
console.error('sshpk-verify: error creating verifier: ' +
e.name + ': ' + e.message);
process.exit(2);
}
if (opts.verbose) {
console.error('sshpk-verify: using %s-%s with a %d bit key',
key.type, hash, key.size);
}
var inFile = process.stdin;
var inFileName = 'stdin';
var inFilePath;
if (opts.file) {
inFilePath = opts.file;
} else if (opts._args.length === 1) {
inFilePath = opts._args[0];
}
if (inFilePath)
inFileName = path.basename(inFilePath);
try {
if (inFilePath) {
fs.accessSync(inFilePath, fs.R_OK);
inFile = fs.createReadStream(inFilePath);
}
} catch (e) {
console.error('sshpk-verify: error opening input file' +
': ' + e.name + ': ' + e.message);
process.exit(2);
}
inFile.pipe(verifier);
inFile.on('end', function () {
var ret;
try {
ret = verifier.verify(sig);
} catch (e) {
console.error('sshpk-verify: error verifying data: ' +
e.name + ': ' + e.message);
process.exit(1);
}
if (ret) {
console.error('OK');
process.exit(0);
}
console.error('NOT OK');
process.exit(1);
});
}

1
node_modules/.bin/strip-indent generated vendored

@ -1 +0,0 @@
../strip-indent/cli.js

49
node_modules/.bin/strip-indent generated vendored

@ -0,0 +1,49 @@
#!/usr/bin/env node
'use strict';
var fs = require('fs');
var stdin = require('get-stdin');
var pkg = require('./package.json');
var stripIndent = require('./');
var argv = process.argv.slice(2);
var input = argv[0];
function help() {
console.log([
'',
' ' + pkg.description,
'',
' Usage',
' strip-indent <file>',
' echo <string> | strip-indent',
'',
' Example',
' echo \'\\tunicorn\\n\\t\\tcake\' | strip-indent',
' unicorn',
' \tcake'
].join('\n'));
}
function init(data) {
console.log(stripIndent(data));
}
if (argv.indexOf('--help') !== -1) {
help();
return;
}
if (argv.indexOf('--version') !== -1) {
console.log(pkg.version);
return;
}
if (process.stdin.isTTY) {
if (!input) {
help();
return;
}
init(fs.readFileSync(input, 'utf8'));
} else {
stdin(init);
}

1
node_modules/.bin/uuid generated vendored

@ -1 +0,0 @@
../uuid/bin/uuid

65
node_modules/.bin/uuid generated vendored

@ -0,0 +1,65 @@
#!/usr/bin/env node
var assert = require('assert');
function usage() {
console.log('Usage:');
console.log(' uuid');
console.log(' uuid v1');
console.log(' uuid v3 <name> <namespace uuid>');
console.log(' uuid v4');
console.log(' uuid v5 <name> <namespace uuid>');
console.log(' uuid --help');
console.log('\nNote: <namespace uuid> may be "URL" or "DNS" to use the corresponding UUIDs defined by RFC4122');
}
var args = process.argv.slice(2);
if (args.indexOf('--help') >= 0) {
usage();
process.exit(0);
}
var version = args.shift() || 'v4';
switch (version) {
case 'v1':
var uuidV1 = require('../v1');
console.log(uuidV1());
break;
case 'v3':
var uuidV3 = require('../v3');
var name = args.shift();
var namespace = args.shift();
assert(name != null, 'v3 name not specified');
assert(namespace != null, 'v3 namespace not specified');
if (namespace == 'URL') namespace = uuidV3.URL;
if (namespace == 'DNS') namespace = uuidV3.DNS;
console.log(uuidV3(name, namespace));
break;
case 'v4':
var uuidV4 = require('../v4');
console.log(uuidV4());
break;
case 'v5':
var uuidV5 = require('../v5');
var name = args.shift();
var namespace = args.shift();
assert(name != null, 'v5 name not specified');
assert(namespace != null, 'v5 namespace not specified');
if (namespace == 'URL') namespace = uuidV5.URL;
if (namespace == 'DNS') namespace = uuidV5.DNS;
console.log(uuidV5(name, namespace));
break;
default:
usage();
process.exit(1);
}

1
node_modules/.bin/which generated vendored

@ -1 +0,0 @@
../which/bin/which

52
node_modules/.bin/which generated vendored

@ -0,0 +1,52 @@
#!/usr/bin/env node
var which = require("../")
if (process.argv.length < 3)
usage()
function usage () {
console.error('usage: which [-as] program ...')
process.exit(1)
}
var all = false
var silent = false
var dashdash = false
var args = process.argv.slice(2).filter(function (arg) {
if (dashdash || !/^-/.test(arg))
return true
if (arg === '--') {
dashdash = true
return false
}
var flags = arg.substr(1).split('')
for (var f = 0; f < flags.length; f++) {
var flag = flags[f]
switch (flag) {
case 's':
silent = true
break
case 'a':
all = true
break
default:
console.error('which: illegal option -- ' + flag)
usage()
}
}
return false
})
process.exit(args.reduce(function (pv, current) {
try {
var f = which.sync(current, { all: all })
if (all)
f = f.join('\n')
if (!silent)
console.log(f)
return pv;
} catch (e) {
return 1;
}
}, 0))

1
node_modules/node-gyp/node_modules/.bin/semver generated vendored

@ -1 +0,0 @@
../semver/bin/semver

133
node_modules/node-gyp/node_modules/.bin/semver generated vendored

@ -0,0 +1,133 @@
#!/usr/bin/env node
// Standalone semver comparison program.
// Exits successfully and prints matching version(s) if
// any supplied version is valid and passes all tests.
var argv = process.argv.slice(2)
, versions = []
, range = []
, gt = []
, lt = []
, eq = []
, inc = null
, version = require("../package.json").version
, loose = false
, identifier = undefined
, semver = require("../semver")
, reverse = false
main()
function main () {
if (!argv.length) return help()
while (argv.length) {
var a = argv.shift()
var i = a.indexOf('=')
if (i !== -1) {
a = a.slice(0, i)
argv.unshift(a.slice(i + 1))
}
switch (a) {
case "-rv": case "-rev": case "--rev": case "--reverse":
reverse = true
break
case "-l": case "--loose":
loose = true
break
case "-v": case "--version":
versions.push(argv.shift())
break
case "-i": case "--inc": case "--increment":
switch (argv[0]) {
case "major": case "minor": case "patch": case "prerelease":
case "premajor": case "preminor": case "prepatch":
inc = argv.shift()
break
default:
inc = "patch"
break
}
break
case "--preid":
identifier = argv.shift()
break
case "-r": case "--range":
range.push(argv.shift())
break
case "-h": case "--help": case "-?":
return help()
default:
versions.push(a)
break
}
}
versions = versions.filter(function (v) {
return semver.valid(v, loose)
})
if (!versions.length) return fail()
if (inc && (versions.length !== 1 || range.length))
return failInc()
for (var i = 0, l = range.length; i < l ; i ++) {
versions = versions.filter(function (v) {
return semver.satisfies(v, range[i], loose)
})
if (!versions.length) return fail()
}
return success(versions)
}
function failInc () {
console.error("--inc can only be used on a single version with no range")
fail()
}
function fail () { process.exit(1) }
function success () {
var compare = reverse ? "rcompare" : "compare"
versions.sort(function (a, b) {
return semver[compare](a, b, loose)
}).map(function (v) {
return semver.clean(v, loose)
}).map(function (v) {
return inc ? semver.inc(v, inc, loose, identifier) : v
}).forEach(function (v,i,_) { console.log(v) })
}
function help () {
console.log(["SemVer " + version
,""
,"A JavaScript implementation of the http://semver.org/ specification"
,"Copyright Isaac Z. Schlueter"
,""
,"Usage: semver [options] <version> [<version> [...]]"
,"Prints valid versions sorted by SemVer precedence"
,""
,"Options:"
,"-r --range <range>"
," Print versions that match the specified range."
,""
,"-i --increment [<level>]"
," Increment a version by the specified level. Level can"
," be one of: major, minor, patch, premajor, preminor,"
," prepatch, or prerelease. Default level is 'patch'."
," Only one version may be specified."
,""
,"--preid <identifier>"
," Identifier to be used to prefix premajor, preminor,"
," prepatch or prerelease version increments."
,""
,"-l --loose"
," Interpret versions and ranges loosely"
,""
,"Program exits successfully if any valid version satisfies"
,"all supplied ranges, and prints all satisfying versions."
,""
,"If no satisfying versions are found, then exits failure."
,""
,"Versions are printed in ascending order, so supplying"
,"multiple versions to the utility will just sort them."
].join("\n"))
}

5
system/assets/debugger/phpdebugbar.css

@ -14,11 +14,8 @@ div.phpdebugbar {
padding: 5px 8px;
}
.phpdebugbar div.phpdebugbar-header, .phpdebugbar a.phpdebugbar-restore-btn {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAA/1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHh4AAAD///8EBAT7+/sLCwv29vYVFRUvLy/t7e3m5ubCwsKxsbE/Pz+mpqZMTEwcHBzy8vLp6emfn5+AgIA2Njbi4uLf39+rq6tzc3NWVlYhISHa2trW1tbS0tLMzMy7u7uZmZmUlJSMjIxvb29kZGRHR0c7Ozt5eXkqKiq1tbWQkJBqampbW1tSUlLHx8eHh4ckJCRDQ0M3wD42AAAAI3RSTlMA/PibTbQ0x76TVAlw4LhZLOuEYCAN9Hjx0a2ppGZEGYw97djhXHwAAATZSURBVFjDlVcHW+MwDO1eFCjj2McNOzvdpXTTXVbL/P+/5SQ7QSSX5Di1X1onfi/Sk+Q4sTDbKqWK+YuznZ2zi3wxVdqK/Zf92M1nT9gnO8rmd398GX6Z3xaoOFoiAQcx3E5efgmeSuN8F6Xg1x3G06l/wjNpMR1B0uif4EhnIuFb+0diIoFXk3IVfokisR+h52GO4JKgyjmfaMhAFNlSaPR7DpwI+lzn/E4QKIqmKIJirxCMP4izBPPZPXhgXwMBYgULw0nfg/BF5scDbslb7QeJ08yqqTEmGYoB95d4H8ETL8+n9wBqrLu6ao3bBsMwAnxISf/9BHcqxNB8Y7cWl3Zz7TAUfPrvAT6AoNEFFXvsjutL01yOuMrtBxnFXsmT/1wQHmdWAFNnI3uI48Yj0FUcHbKf62GfUfr8eeQt7Uk3mQZpZNoVRPEui5vtEz5zFEpgWnyqVBZMc6oaGNriH2hGVZ0OxEvInPeMaZWJBA7vmPbCr5jjws5HBnAUxvDMH40aCIf4G5BjRQSs8E8HFFYf8bGxgDvD55bzGhwWkoBcuIyHR/AMdaCagxXDhtL6tSqoWpd4BMnlIR+Or+rYTK/a3EAGcc6e4AWHISnWv20iCCojsHoVlQdjrMexFF2C7UMg2A2WEGWbQhXN6l3eXC6XGp4b9qxbuEB2EBGBwtocrK90cVG5mbRXm6vmx/0phq1sIAGKDgLOBiN1MrO5a9aDl+D0W6x0Ar9BCTRuIIANa90Y7LrLVRXzwVtDInCqMRWcf2bUOEAsa4wJqFowQALL9EiAtVRk8QC4OW+1pOM9jIaVASwYagyNXDj+W0NcfuZNzjtXOiL0Zzg30Llj+ptfxQs4+vBPNiL5PawFCBkgXpUaVtqGl+A8dgZHL34BcBUQrwPptToW+o37Ku+UH9eYByJIx3YkAeFnMFuGO7S5gEp7YhXxa5OOAM39RXDPXb0qmpROsswZe+twXdU55oUIZAiEv3bD1UFwIYKkmGqytPCDCwKFQCKK0yL7qtSAPX54UAbtsLuBHkb9zyLmPQSNjsSgmQwKUOIfEY8F8t4B34DvndJY9BA8tNBJq1Nev9axmaStFcQLhgYoCTo0salkIaW8OUDdWjMTR2sHPhrAFZqx6cqcKE4pl2BJJ4K6hfwvqNgAnXfKX/HU6X3Zrhnu0k7tLNZtTBRv1hkwTDBY1NzFU6doDYjJbWdQkQhWwuU7/LvhTh3SDoco4ECL4i5dwURbc8NdDZz2IwKicE8d0KIqWetLE3+lL4hvUuGSeRfVWNLfj/gpOw4smBJBkKQHCzlHGwvAj4woB1gq5NGGLSXtORBPnUQPV5/MPVkDMxbpwG7w4x0xL6Ltxka0A/4NBvV09UVk4DoSn/jl2+JQS9q9KYawisAD4CfhsZ4TH3htylsdEHARIQBusqCKyUpymycgbbkkXEXjT3z7/oKQFTFVuZD2FMJHZIDsO5x2d4aAr2jR+GLwZhtAb028/0yJ9J8dE87jQyKObcjtTXT8dH+fDuKF4/eiPwzH44wTf/yUi6wrpRIOZ9lM1EtXAifFI+CJn9+iX/t2xMQwOMth/UZbASi8btAwR9FHWSpJr75g9Oqbin3VDg+SpwlP6k6TB4ex/7JvmcJx8jydy6XPk8eFTKhyfwCgX71MSvaBHgAAAABJRU5ErkJggg==);
}
.phpdebugbar a.phpdebugbar-restore-btn {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAA/1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHh4AAAD///8EBAT7+/sLCwv29vYVFRUvLy/t7e3m5ubCwsKxsbE/Pz+mpqZMTEwcHBzy8vLp6emfn5+AgIA2Njbi4uLf39+rq6tzc3NWVlYhISHa2trW1tbS0tLMzMy7u7uZmZmUlJSMjIxvb29kZGRHR0c7Ozt5eXkqKiq1tbWQkJBqampbW1tSUlLHx8eHh4ckJCRDQ0M3wD42AAAAI3RSTlMA/PibTbQ0x76TVAlw4LhZLOuEYCAN9Hjx0a2ppGZEGYw97djhXHwAAATZSURBVFjDlVcHW+MwDO1eFCjj2McNOzvdpXTTXVbL/P+/5SQ7QSSX5Di1X1onfi/Sk+Q4sTDbKqWK+YuznZ2zi3wxVdqK/Zf92M1nT9gnO8rmd398GX6Z3xaoOFoiAQcx3E5efgmeSuN8F6Xg1x3G06l/wjNpMR1B0uif4EhnIuFb+0diIoFXk3IVfokisR+h52GO4JKgyjmfaMhAFNlSaPR7DpwI+lzn/E4QKIqmKIJirxCMP4izBPPZPXhgXwMBYgULw0nfg/BF5scDbslb7QeJ08yqqTEmGYoB95d4H8ETL8+n9wBqrLu6ao3bBsMwAnxISf/9BHcqxNB8Y7cWl3Zz7TAUfPrvAT6AoNEFFXvsjutL01yOuMrtBxnFXsmT/1wQHmdWAFNnI3uI48Yj0FUcHbKf62GfUfr8eeQt7Uk3mQZpZNoVRPEui5vtEz5zFEpgWnyqVBZMc6oaGNriH2hGVZ0OxEvInPeMaZWJBA7vmPbCr5jjws5HBnAUxvDMH40aCIf4G5BjRQSs8E8HFFYf8bGxgDvD55bzGhwWkoBcuIyHR/AMdaCagxXDhtL6tSqoWpd4BMnlIR+Or+rYTK/a3EAGcc6e4AWHISnWv20iCCojsHoVlQdjrMexFF2C7UMg2A2WEGWbQhXN6l3eXC6XGp4b9qxbuEB2EBGBwtocrK90cVG5mbRXm6vmx/0phq1sIAGKDgLOBiN1MrO5a9aDl+D0W6x0Ar9BCTRuIIANa90Y7LrLVRXzwVtDInCqMRWcf2bUOEAsa4wJqFowQALL9EiAtVRk8QC4OW+1pOM9jIaVASwYagyNXDj+W0NcfuZNzjtXOiL0Zzg30Llj+ptfxQs4+vBPNiL5PawFCBkgXpUaVtqGl+A8dgZHL34BcBUQrwPptToW+o37Ku+UH9eYByJIx3YkAeFnMFuGO7S5gEp7YhXxa5OOAM39RXDPXb0qmpROsswZe+twXdU55oUIZAiEv3bD1UFwIYKkmGqytPCDCwKFQCKK0yL7qtSAPX54UAbtsLuBHkb9zyLmPQSNjsSgmQwKUOIfEY8F8t4B34DvndJY9BA8tNBJq1Nev9axmaStFcQLhgYoCTo0salkIaW8OUDdWjMTR2sHPhrAFZqx6cqcKE4pl2BJJ4K6hfwvqNgAnXfKX/HU6X3Zrhnu0k7tLNZtTBRv1hkwTDBY1NzFU6doDYjJbWdQkQhWwuU7/LvhTh3SDoco4ECL4i5dwURbc8NdDZz2IwKicE8d0KIqWetLE3+lL4hvUuGSeRfVWNLfj/gpOw4smBJBkKQHCzlHGwvAj4woB1gq5NGGLSXtORBPnUQPV5/MPVkDMxbpwG7w4x0xL6Ltxka0A/4NBvV09UVk4DoSn/jl2+JQS9q9KYawisAD4CfhsZ4TH3htylsdEHARIQBusqCKyUpymycgbbkkXEXjT3z7/oKQFTFVuZD2FMJHZIDsO5x2d4aAr2jR+GLwZhtAb028/0yJ9J8dE87jQyKObcjtTXT8dH+fDuKF4/eiPwzH44wTf/yUi6wrpRIOZ9lM1EtXAifFI+CJn9+iX/t2xMQwOMth/UZbASi8btAwR9FHWSpJr75g9Oqbin3VDg+SpwlP6k6TB4ex/7JvmcJx8jydy6XPk8eFTKhyfwCgX71MSvaBHgAAAABJRU5ErkJggg==);
width: 13px;
}

245
system/blueprints/config/system.yaml

@ -608,7 +608,6 @@ form:
file: File
apc: APC
apcu: APCu
xcache: Xcache
memcache: Memcache
memcached: Memcached
wincache: WinCache
@ -888,9 +887,45 @@ form:
title: PLUGIN_ADMIN.ASSETS
fields:
assets_section:
general_config_section:
type: section
title: PLUGIN_ADMIN.ASSETS
title: PLUGIN_ADMIN.GENERAL_CONFIG
underline: true
assets.enable_asset_timestamp:
type: toggle
label: PLUGIN_ADMIN.ENABLED_TIMESTAMPS_ON_ASSETS
help: PLUGIN_ADMIN.ENABLED_TIMESTAMPS_ON_ASSETS_HELP
highlight: 0
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: bool
assets.enable_asset_sri:
type: toggle
label: PLUGIN_ADMIN.ENABLED_SRI_ON_ASSETS
help: PLUGIN_ADMIN.ENABLED_SRI_ON_ASSETS_HELP
highlight: 0
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: bool
assets.collections:
type: multilevel
label: PLUGIN_ADMIN.COLLECTIONS
placeholder_key: collection_name
placeholder_value: collection_path
validate:
type: array
css_assets_section:
type: section
title: PLUGIN_ADMIN.CSS_ASSETS
underline: true
assets.css_pipeline:
@ -959,6 +994,11 @@ form:
validate:
type: bool
js_assets_section:
type: section
title: PLUGIN_ADMIN.JS_ASSETS
underline: true
assets.js_pipeline:
type: toggle
label: PLUGIN_ADMIN.JAVASCRIPT_PIPELINE
@ -1003,10 +1043,15 @@ form:
validate:
type: bool
assets.enable_asset_timestamp:
js_module_assets_section:
type: section
title: PLUGIN_ADMIN.JS_MODULE_ASSETS
underline: true
assets.js_module_pipeline:
type: toggle
label: PLUGIN_ADMIN.ENABLED_TIMESTAMPS_ON_ASSETS
help: PLUGIN_ADMIN.ENABLED_TIMESTAMPS_ON_ASSETS_HELP
label: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE
help: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE_HELP
highlight: 0
options:
1: PLUGIN_ADMIN.YES
@ -1014,24 +1059,29 @@ form:
validate:
type: bool
assets.enable_asset_sri:
assets.js_module_pipeline_include_externals:
type: toggle
label: PLUGIN_ADMIN.ENABLED_SRI_ON_ASSETS
help: PLUGIN_ADMIN.ENABLED_SRI_ON_ASSETS_HELP
highlight: 0
label: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE_INCLUDE_EXTERNALS
help: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE_INCLUDE_EXTERNALS_HELP
highlight: 1
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: bool
assets.collections:
type: multilevel
label: PLUGIN_ADMIN.COLLECTIONS
placeholder_key: collection_name
placeholder_value: collection_path
assets.js_module_pipeline_before_excludes:
type: toggle
label: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE_BEFORE_EXCLUDES
help: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE_BEFORE_EXCLUDES_HELP
highlight: 1
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: array
type: bool
errors:
type: tab
@ -1105,6 +1155,13 @@ form:
local6: local6
local7: local7
log.syslog.tag:
type: text
size: small
label: PLUGIN_ADMIN.SYSLOG_TAG
help: PLUGIN_ADMIN.SYSLOG_TAG_HELP
placeholder: "grav"
debugger:
type: tab
title: PLUGIN_ADMIN.DEBUGGER
@ -1394,6 +1451,18 @@ form:
validate:
type: bool
session.secure_https:
type: toggle
label: PLUGIN_ADMIN.SESSION_SECURE_HTTPS
help: PLUGIN_ADMIN.SESSION_SECURE_HTTPS_HELP
highlight: 1
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
default: true
validate:
type: bool
session.httponly:
type: toggle
label: PLUGIN_ADMIN.SESSION_HTTPONLY
@ -1446,6 +1515,10 @@ form:
title: PLUGIN_ADMIN.ADVANCED
underline: true
gpm_section:
type: section
title: PLUGIN_ADMIN.GPM_SECTION
gpm.releases:
type: toggle
label: PLUGIN_ADMIN.GPM_RELEASES
@ -1455,14 +1528,23 @@ form:
stable: PLUGIN_ADMIN.STABLE
testing: PLUGIN_ADMIN.TESTING
gpm.proxy_url:
type: text
size: medium
placeholder: "e.g. 127.0.0.1:3128"
label: PLUGIN_ADMIN.PROXY_URL
help: PLUGIN_ADMIN.PROXY_URL_HELP
gpm.official_gpm_only:
type: toggle
label: PLUGIN_ADMIN.GPM_OFFICIAL_ONLY
highlight: 1
help: PLUGIN_ADMIN.GPM_OFFICIAL_ONLY_HELP
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
default: true
validate:
type: bool
http_section:
type: section
title: PLUGIN_ADMIN.HTTP_SECTION
gpm.method:
http.method:
type: toggle
label: PLUGIN_ADMIN.GPM_METHOD
highlight: auto
@ -1472,29 +1554,66 @@ form:
fopen: PLUGIN_ADMIN.FOPEN
curl: PLUGIN_ADMIN.CURL
gpm.official_gpm_only:
http.enable_proxy:
type: toggle
label: PLUGIN_ADMIN.GPM_OFFICIAL_ONLY
label: PLUGIN_ADMIN.SSL_ENABLE_PROXY
highlight: 1
help: PLUGIN_ADMIN.GPM_OFFICIAL_ONLY_HELP
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
default: true
default: false
validate:
type: bool
gpm.verify_peer:
http.proxy_url:
type: text
size: medium
placeholder: "e.g. 127.0.0.1:3128"
label: PLUGIN_ADMIN.PROXY_URL
help: PLUGIN_ADMIN.PROXY_URL_HELP
http.proxy_cert_path:
type: text
size: medium
placeholder: "e.g. /Users/bob/certs/"
label: PLUGIN_ADMIN.PROXY_CERT
help: PLUGIN_ADMIN.PROXY_CERT_HELP
http.verify_peer:
type: toggle
label: PLUGIN_ADMIN.SSL_VERIFY_PEER
highlight: 1
help: PLUGIN_ADMIN.SSL_VERIFY_PEER_HELP
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: bool
http.verify_host:
type: toggle
label: PLUGIN_ADMIN.GPM_VERIFY_PEER
label: PLUGIN_ADMIN.SSL_VERIFY_HOST
highlight: 1
help: PLUGIN_ADMIN.GPM_VERIFY_PEER_HELP
help: PLUGIN_ADMIN.SSL_VERIFY_HOST_HELP
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: bool
http.concurrent_connections:
type: number
size: x-small
label: PLUGIN_ADMIN.HTTP_CONNECTIONS
help: PLUGIN_ADMIN.HTTP_CONNECTIONS_HELP
validate:
min: 1
max: 20
misc_section:
type: section
title: PLUGIN_ADMIN.MISC_SECTION
reverse_proxy_setup:
type: toggle
label: PLUGIN_ADMIN.REVERSE_PROXY
@ -1673,35 +1792,15 @@ form:
validate:
type: bool
experimental:
accounts:
type: tab
title: PLUGIN_ADMIN.EXPERIMENTAL
title: PLUGIN_ADMIN.ACCOUNTS
fields:
experimental_section:
type: section
title: PLUGIN_ADMIN.EXPERIMENTAL
underline: true
# flex_pages:
# type: section
# title: Flex Pages
#
# pages.type:
# type: select
# label: PLUGIN_ADMIN.PAGES_TYPE
# highlight: regular
# help: PLUGIN_ADMIN.PAGES_TYPE_HELP
# options:
# regular: PLUGIN_ADMIN.REGULAR
# flex: PLUGIN_ADMIN.FLEX
pages.type:
type: hidden
flex_accounts:
type: section
title: Flex Accounts
title: User Accounts
accounts.type:
type: select
@ -1720,3 +1819,41 @@ form:
options:
file: PLUGIN_ADMIN.FILE
folder: PLUGIN_ADMIN.FOLDER
accounts.avatar:
type: select
label: PLUGIN_ADMIN.AVATAR
default: gravatar
help: PLUGIN_ADMIN.AVATAR_HELP
options:
multiavatar: Multiavatar [local]
gravatar: Gravatar [external]
# experimental:
# type: tab
# title: PLUGIN_ADMIN.EXPERIMENTAL
#
# fields:
# experimental_section:
# type: section
# title: PLUGIN_ADMIN.EXPERIMENTAL
# underline: true
#
# flex_pages:
# type: section
# title: Flex Pages
#
# pages.type:
# type: select
# label: PLUGIN_ADMIN.PAGES_TYPE
# highlight: regular
# help: PLUGIN_ADMIN.PAGES_TYPE_HELP
# options:
# regular: PLUGIN_ADMIN.REGULAR
# flex: PLUGIN_ADMIN.FLEX
#
# pages.type:
# type: hidden

2
system/blueprints/flex/pages.yaml

@ -104,7 +104,7 @@ config:
edit:
title:
template: "{% if object.root %}Root <small>( &lt;root&gt; ){% else %}{{ (form.value('header.title') ?? form.value('folder'))|e }} <small>( {{ (object.getRoute().toString(false) ?: '/')|e }} )</small>{% endif %}"
template: "{% if object.root %}Root <small>( &lt;root&gt; )</small>{% else %}{{ (form.value('header.title') ?? form.value('folder'))|e }} <small>( {{ (object.getRoute().toString(false) ?: '/')|e }} )</small>{% endif %}"
# TODO: not used yet
buttons:

13
system/blueprints/flex/user-accounts.yaml

@ -122,6 +122,19 @@ config:
fields:
- key
- email
- username
- fullname
relationships:
media:
type: media
cardinality: to-many
avatar:
type: media
cardinality: to-one
# roles:
# type: user-groups
# cardinality: to-many
blueprints:
configure:

1
system/blueprints/flex/user-groups.yaml

@ -113,6 +113,7 @@ config:
fields:
- key
- groupname
- readableName
- description
blueprints:

12
system/blueprints/pages/default.yaml

@ -320,6 +320,18 @@ form:
fields:
header.redirect_default_route:
type: toggle
toggleable: true
label: PLUGIN_ADMIN.REDIRECT_DEFAULT_ROUTE
help: PLUGIN_ADMIN.REDIRECT_DEFAULT_ROUTE_HELP
config-highlight@: system.pages.redirect_default_route
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: bool
header.routes.default:
type: text
toggleable: true

18
system/blueprints/pages/external.yaml

@ -1,7 +1,7 @@
title: PLUGIN_ADMIN:EXTERNAL
title: PLUGIN_ADMIN.EXTERNAL
extends@:
type: default
context: blueprints://pages
type: default
context: blueprints://pages
form:
validation: loose
@ -29,16 +29,16 @@ form:
unset@: true
header.external_url:
type: text
label: PLUGIN_ADMIN.EXTERNAL_URL
placeholder: https://getgrav.org
validate:
required: true
type: text
label: PLUGIN_ADMIN.EXTERNAL_URL
placeholder: https://getgrav.org
validate:
required: true
options:
fields:
publishing:
fields:
header.date:

10
system/blueprints/pages/partials/security.yaml

@ -51,17 +51,13 @@ form:
type: bool
header.permissions.authors:
type: list
type: array
toggleable: true
value_only: true
placeholder_value: PLUGIN_ADMIN.USERNAME
label: PLUGIN_ADMIN.PAGE_AUTHORS
help: PLUGIN_ADMIN.PAGE_AUTHORS_HELP
fields:
value:
type: text
placeholder: PLUGIN_ADMIN.USERNAME
style: vertical
header.permissions.groups:
ignore@: true
type: acl_picker

19
system/blueprints/user/account.yaml

@ -11,10 +11,21 @@ form:
avatar:
type: file
size: large
destination: 'user://accounts/avatars'
destination: 'account://avatars'
multiple: false
random_name: true
multiavatar_only:
type: conditional
condition: config.system.accounts.avatar == 'multiavatar'
fields:
avatar_hash:
type: text
label: ''
placeholder: 'e.g. dceaadcfda491f4e45'
description: PLUGIN_ADMIN.AVATAR_HASH
size: large
content:
type: section
title: PLUGIN_ADMIN.ACCOUNT
@ -107,6 +118,12 @@ form:
label: PLUGIN_ADMIN.2FA_SECRET
sublabel: PLUGIN_ADMIN.2FA_SECRET_HELP
yubikey_id:
type: text
label: PLUGIN_ADMIN.YUBIKEY_ID
description: PLUGIN_ADMIN.YUBIKEY_HELP
size: small
maxlength: 12

2
system/config/media.yaml

@ -199,7 +199,7 @@ types:
gz:
type: file
thumb: media/thumb-gz.png
mime: application/gzip
mime: application/x-gzip
tar:
type: file
thumb: media/thumb-tar.png

27
system/config/system.yaml

@ -35,6 +35,7 @@ home:
pages:
type: regular # EXPERIMENTAL: Page type: regular or flex
dirs: ['page://'] # Advanced functionality, allows for multiple page paths
theme: quark # Default theme (defaults to "quark" theme)
order:
by: default # Order pages by "default", "alpha" or "date"
@ -96,7 +97,7 @@ cache:
purge_at: '0 4 * * *' # How often to purge old file cache (using new scheduler)
clear_at: '0 3 * * *' # How often to clear cache (using new scheduler)
clear_job_type: 'standard' # Type to clear when processing the scheduled clear job `standard`|`all`
clear_images_by_default: false # By default grav does not include processed images in cache clear, this can be enabled
clear_images_by_default: false # By default grav does not include processed images in cache clear, this can be enabled
cli_compatibility: false # Ensures only non-volatile drivers are used (file, redis, memcache, etc.)
lifetime: 604800 # Lifetime of cached data in seconds (0 = infinite)
gzip: false # GZip compress the page output
@ -127,6 +128,9 @@ assets: # Configuration for Assets Mana
js_pipeline: false # The JS pipeline is the unification of multiple JS resources into one file
js_pipeline_include_externals: true # Include external URLs in the pipeline by default
js_pipeline_before_excludes: true # Render the pipeline before any excluded files
js_module_pipeline: false # The JS Module pipeline is the unification of multiple JS Module resources into one file
js_module_pipeline_include_externals: true # Include external URLs in the pipeline by default
js_module_pipeline_before_excludes: true # Render the pipeline before any excluded files
js_minify: true # Minify the JS during pipelining
enable_asset_timestamp: false # Enable asset timestamps
enable_asset_sri: false # Enable asset SRI
@ -141,6 +145,7 @@ log:
handler: file # Log handler. Currently supported: file | syslog
syslog:
facility: local6 # Syslog facilities output
tag: grav # Syslog tag. Default: "grav".
debugger:
enabled: false # Enable Grav debugger and following settings
@ -162,6 +167,12 @@ images:
retina_scale: 1 # scale to adjust auto-sizes for better handling of HiDPI resolutions
defaults:
loading: auto # Let browser pick [auto|lazy|eager]
watermark:
image: 'system://images/watermark.png' # Path to a watermark image
position_y: 'center' # top|center|bottom
position_x: 'center' # left|center|right
scale: 33 # percentage of watermark scale
watermark_all: false # automatically watermark all images
media:
enable_media_timestamp: false # Enable media timestamps
@ -176,6 +187,7 @@ session:
name: grav-site # Name prefix of the session cookie. Use alphanumeric, dashes or underscores only. Do not use dots in the session name
uniqueness: path # Should sessions be `path` based or `security.salt` based
secure: false # Set session secure. If true, indicates that communication for this cookie must be over an encrypted transmission. Enable this only on sites that run exclusively on HTTPS
secure_https: true # Set session secure on HTTPS but not on HTTP. Has no effect if you have `session.secure: true`. Set to false if your site jumps between HTTP and HTTPS.
httponly: true # Set session HTTP only. If true, indicates that cookies should be used only over HTTP, and JavaScript modification is not allowed.
samesite: Lax # Set session SameSite. Possible values are Lax, Strict and None. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
split: true # Sessions should be independent between site and plugins (such as admin)
@ -184,14 +196,21 @@ session:
gpm:
releases: stable # Set to either 'stable' or 'testing'
proxy_url: # Configure a manual proxy URL for GPM (eg 127.0.0.1:3128)
method: 'auto' # Either 'curl', 'fopen' or 'auto'. 'auto' will try fopen first and if not available cURL
verify_peer: true # Sometimes on some systems (Windows most commonly) GPM is unable to connect because the SSL certificate cannot be verified. Disabling this setting might help.
official_gpm_only: true # By default GPM direct-install will only allow URLs via the official GPM proxy to ensure security
http:
method: auto # Either 'curl', 'fopen' or 'auto'. 'auto' will try fopen first and if not available cURL
enable_proxy: true # Enable proxy server configuration
proxy_url: # Configure a manual proxy URL for GPM (eg 127.0.0.1:3128)
proxy_cert_path: # Local path to proxy certificate folder containing pem files
concurrent_connections: 5 # Concurrent HTTP connections when multiplexing
verify_peer: true # Enable/Disable SSL verification of peer certificates
verify_host: true # Enable/Disable SSL verification of host certificates
accounts:
type: regular # EXPERIMENTAL: Account type: regular or flex
storage: file # EXPERIMENTAL: Flex storage type: file or folder
avatar: gravatar # Avatar generator [multiavatar|gravatar]
flex:
cache:

4
system/defines.php

@ -3,13 +3,13 @@
/**
* @package Grav\Core
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
// Some standard defines
define('GRAV', true);
define('GRAV_VERSION', '1.7.23');
define('GRAV_VERSION', '1.7.40');
define('GRAV_SCHEMA', '1.7.0_2020-11-20_1');
define('GRAV_TESTING', false);

BIN
system/images/watermark.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

2
system/install.php

@ -2,7 +2,7 @@
/**
* @package Grav\Core
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

12
system/languages/es.yaml

@ -44,7 +44,7 @@ GRAV:
WK: sem
MO: mes
YR: año
DEC: dic
DEC: déc
SECOND_PLURAL: segundos
MINUTE_PLURAL: minutos
HOUR_PLURAL: horas
@ -64,7 +64,7 @@ GRAV:
VALIDATION_FAIL: '<b>Falló la validación: </b>'
INVALID_INPUT: 'Dato inválido en: '
MISSING_REQUIRED_FIELD: 'Falta el campo requerido: '
XSS_ISSUES: "Se detectaron problemas XSS potenciales en el campo '%s'"
XSS_ISSUES: "Se detectaron potenciales problemas XSS en el campo '%s'"
MONTHS_OF_THE_YEAR:
- 'Enero'
- 'Febrero'
@ -86,7 +86,7 @@ GRAV:
- 'Viernes'
- 'Sábado'
- 'Domingo'
YES: "Si"
YES: "Sí"
NO: "No"
CRON:
EVERY: cada
@ -96,12 +96,12 @@ GRAV:
EVERY_DAY_OF_MONTH: cada día del mes
EVERY_MONTH: cada mes
TEXT_PERIOD: Cada <b />
TEXT_MINS: ' a <b /> minuto(s) despues de la hora'
TEXT_MINS: ' a <b /> minuto(s) después de la hora'
TEXT_TIME: ' a <b />:<b />'
TEXT_DOW: ' en <b />'
TEXT_MONTH: ' de<b />'
TEXT_DOM: ' en<b />'
ERROR1: La etiqueta %s no está soportada!
ERROR2: El número de elementos es erroneo
ERROR1: '¡La etiqueta %s no está soportada!'
ERROR2: El número de elementos es erróneo
ERROR3: El jquery_element debería establecerse en la configuración del jqCron
ERROR4: Expresión no reconocida

23
system/router.php

@ -3,7 +3,7 @@
/**
* @package Grav\Core
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -13,8 +13,25 @@ if (PHP_SAPI !== 'cli-server') {
$_SERVER['PHP_CLI_ROUTER'] = true;
if (is_file($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . $_SERVER['SCRIPT_NAME'])) {
return false;
$root = $_SERVER['DOCUMENT_ROOT'];
$path = $_SERVER['SCRIPT_NAME'];
if ($path !== '/index.php' && is_file($root . $path)) {
if (!(
// Block all direct access to files and folders beginning with a dot
strpos($path, '/.') !== false
// Block all direct access for these folders
|| preg_match('`^/(\.git|cache|bin|logs|backup|webserver-configs|tests)/`ui', $path)
// Block access to specific file types for these system folders
|| preg_match('`^/(system|vendor)/(.*)\.(txt|xml|md|html|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$`ui', $path)
// Block access to specific file types for these user folders
|| preg_match('`^/(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$`ui', $path)
// Block all direct access to .md files
|| preg_match('`\.md$`ui', $path)
// Block access to specific files in the root folder
|| preg_match('`^/(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess)$`ui', $path)
)) {
return false;
}
}
$grav_index = 'index.php';

165
system/src/DOMLettersIterator.php

@ -0,0 +1,165 @@
<?php
/**
* Iterates individual characters (Unicode codepoints) of DOM text and CDATA nodes
* while keeping track of their position in the document.
*
* Example:
*
* $doc = new DOMDocument();
* $doc->load('example.xml');
* foreach(new DOMLettersIterator($doc) as $letter) echo $letter;
*
* NB: If you only need characters without their position
* in the document, use DOMNode->textContent instead.
*
* @author porneL http://pornel.net
* @license Public Domain
* @url https://github.com/antoligy/dom-string-iterators
*
* @implements Iterator<int,string>
*/
final class DOMLettersIterator implements Iterator
{
/** @var DOMElement */
private $start;
/** @var DOMElement|null */
private $current;
/** @var int */
private $offset = -1;
/** @var int|null */
private $key;
/** @var array<int,string>|null */
private $letters;
/**
* expects DOMElement or DOMDocument (see DOMDocument::load and DOMDocument::loadHTML)
*
* @param DOMNode $el
*/
public function __construct(DOMNode $el)
{
if ($el instanceof DOMDocument) {
$el = $el->documentElement;
}
if (!$el instanceof DOMElement) {
throw new InvalidArgumentException('Invalid arguments, expected DOMElement or DOMDocument');
}
$this->start = $el;
}
/**
* Returns position in text as DOMText node and character offset.
* (it's NOT a byte offset, you must use mb_substr() or similar to use this offset properly).
* node may be NULL if iterator has finished.
*
* @return array
*/
public function currentTextPosition(): array
{
return [$this->current, $this->offset];
}
/**
* Returns DOMElement that is currently being iterated or NULL if iterator has finished.
*
* @return DOMElement|null
*/
public function currentElement(): ?DOMElement
{
return $this->current ? $this->current->parentNode : null;
}
// Implementation of Iterator interface
/**
* @return int|null
*/
public function key(): ?int
{
return $this->key;
}
/**
* @return void
*/
public function next(): void
{
if (null === $this->current) {
return;
}
if ($this->current->nodeType === XML_TEXT_NODE || $this->current->nodeType === XML_CDATA_SECTION_NODE) {
if ($this->offset === -1) {
preg_match_all('/./us', $this->current->textContent, $m);
$this->letters = $m[0];
}
$this->offset++;
$this->key++;
if ($this->letters && $this->offset < count($this->letters)) {
return;
}
$this->offset = -1;
}
while ($this->current->nodeType === XML_ELEMENT_NODE && $this->current->firstChild) {
$this->current = $this->current->firstChild;
if ($this->current->nodeType === XML_TEXT_NODE || $this->current->nodeType === XML_CDATA_SECTION_NODE) {
$this->next();
return;
}
}
while (!$this->current->nextSibling && $this->current->parentNode) {
$this->current = $this->current->parentNode;
if ($this->current === $this->start) {
$this->current = null;
return;
}
}
$this->current = $this->current->nextSibling;
$this->next();
}
/**
* Return the current element
* @link https://php.net/manual/en/iterator.current.php
*
* @return string|null
*/
public function current(): ?string
{
return $this->letters ? $this->letters[$this->offset] : null;
}
/**
* Checks if current position is valid
* @link https://php.net/manual/en/iterator.valid.php
*
* @return bool
*/
public function valid(): bool
{
return (bool)$this->current;
}
/**
* @return void
*/
public function rewind(): void
{
$this->current = $this->start;
$this->offset = -1;
$this->key = 0;
$this->letters = [];
$this->next();
}
}

158
system/src/DOMWordsIterator.php

@ -0,0 +1,158 @@
<?php
/**
* Iterates individual words of DOM text and CDATA nodes
* while keeping track of their position in the document.
*
* Example:
*
* $doc = new DOMDocument();
* $doc->load('example.xml');
* foreach(new DOMWordsIterator($doc) as $word) echo $word;
*
* @author pjgalbraith http://www.pjgalbraith.com
* @author porneL http://pornel.net (based on DOMLettersIterator available at http://pornel.net/source/domlettersiterator.php)
* @license Public Domain
* @url https://github.com/antoligy/dom-string-iterators
*
* @implements Iterator<int,string>
*/
final class DOMWordsIterator implements Iterator
{
/** @var DOMElement */
private $start;
/** @var DOMElement|null */
private $current;
/** @var int */
private $offset = -1;
/** @var int|null */
private $key;
/** @var array<int,array<int,int|string>>|null */
private $words;
/**
* expects DOMElement or DOMDocument (see DOMDocument::load and DOMDocument::loadHTML)
*
* @param DOMNode $el
*/
public function __construct(DOMNode $el)
{
if ($el instanceof DOMDocument) {
$el = $el->documentElement;
}
if (!$el instanceof DOMElement) {
throw new InvalidArgumentException('Invalid arguments, expected DOMElement or DOMDocument');
}
$this->start = $el;
}
/**
* Returns position in text as DOMText node and character offset.
* (it's NOT a byte offset, you must use mb_substr() or similar to use this offset properly).
* node may be NULL if iterator has finished.
*
* @return array
*/
public function currentWordPosition(): array
{
return [$this->current, $this->offset, $this->words];
}
/**
* Returns DOMElement that is currently being iterated or NULL if iterator has finished.
*
* @return DOMElement|null
*/
public function currentElement(): ?DOMElement
{
return $this->current ? $this->current->parentNode : null;
}
// Implementation of Iterator interface
/**
* Return the key of the current element
* @link https://php.net/manual/en/iterator.key.php
* @return int|null
*/
public function key(): ?int
{
return $this->key;
}
/**
* @return void
*/
public function next(): void
{
if (null === $this->current) {
return;
}
if ($this->current->nodeType === XML_TEXT_NODE || $this->current->nodeType === XML_CDATA_SECTION_NODE) {
if ($this->offset === -1) {
$this->words = preg_split("/[\n\r\t ]+/", $this->current->textContent, -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_OFFSET_CAPTURE) ?: [];
}
$this->offset++;
if ($this->words && $this->offset < count($this->words)) {
$this->key++;
return;
}
$this->offset = -1;
}
while ($this->current->nodeType === XML_ELEMENT_NODE && $this->current->firstChild) {
$this->current = $this->current->firstChild;
if ($this->current->nodeType === XML_TEXT_NODE || $this->current->nodeType === XML_CDATA_SECTION_NODE) {
$this->next();
return;
}
}
while (!$this->current->nextSibling && $this->current->parentNode) {
$this->current = $this->current->parentNode;
if ($this->current === $this->start) {
$this->current = null;
return;
}
}
$this->current = $this->current->nextSibling;
$this->next();
}
/**
* Return the current element
* @link https://php.net/manual/en/iterator.current.php
* @return string|null
*/
public function current(): ?string
{
return $this->words ? (string)$this->words[$this->offset][0] : null;
}
/**
* Checks if current position is valid
* @link https://php.net/manual/en/iterator.valid.php
* @return bool
*/
public function valid(): bool
{
return (bool)$this->current;
}
public function rewind(): void
{
$this->current = $this->start;
$this->offset = -1;
$this->key = 0;
$this->words = [];
$this->next();
}
}

175
system/src/Grav/Common/Assets.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -16,8 +16,8 @@ use Grav\Common\Assets\Traits\TestingAssetsTrait;
use Grav\Common\Config\Config;
use Grav\Framework\Object\PropertyObject;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use function array_slice;
use function call_user_func_array;
use function count;
use function func_get_args;
use function is_array;
@ -30,14 +30,21 @@ class Assets extends PropertyObject
use TestingAssetsTrait;
use LegacyAssetsTrait;
const LINK = 'link';
const CSS = 'css';
const JS = 'js';
const JS_MODULE = 'js_module';
const LINK_COLLECTION = 'assets_link';
const CSS_COLLECTION = 'assets_css';
const JS_COLLECTION = 'assets_js';
const JS_MODULE_COLLECTION = 'assets_js_module';
const LINK_TYPE = Assets\Link::class;
const CSS_TYPE = Assets\Css::class;
const JS_TYPE = Assets\Js::class;
const JS_MODULE_TYPE = Assets\JsModule::class;
const INLINE_CSS_TYPE = Assets\InlineCss::class;
const INLINE_JS_TYPE = Assets\InlineJs::class;
const INLINE_JS_MODULE_TYPE = Assets\InlineJsModule::class;
/** @const Regex to match CSS and JavaScript files */
const DEFAULT_REGEX = '/.\.(css|js)$/i';
@ -48,15 +55,24 @@ class Assets extends PropertyObject
/** @const Regex to match JavaScript files */
const JS_REGEX = '/.\.js$/i';
/** @const Regex to match JavaScriptModyle files */
const JS_MODULE_REGEX = '/.\.mjs$/i';
/** @var string */
protected $assets_dir;
/** @var string */
protected $assets_url;
/** @var array */
protected $assets_link = [];
/** @var array */
protected $assets_css = [];
/** @var array */
protected $assets_js = [];
/** @var array */
protected $assets_js_module = [];
// Following variables come from the configuration:
/** @var bool */
@ -66,19 +82,17 @@ class Assets extends PropertyObject
/** @var bool */
protected $css_pipeline_before_excludes;
/** @var bool */
protected $inlinecss_pipeline_include_externals;
/** @var bool */
protected $inlinecss_pipeline_before_excludes;
/** @var bool */
protected $js_pipeline;
/** @var bool */
protected $js_pipeline_include_externals;
/** @var bool */
protected $js_pipeline_before_excludes;
/** @var bool */
protected $inlinejs_pipeline_include_externals;
protected $js_module_pipeline;
/** @var bool */
protected $inlinejs_pipeline_before_excludes;
protected $js_module_pipeline_include_externals;
/** @var bool */
protected $js_module_pipeline_before_excludes;
/** @var array */
protected $pipeline_options = [];
@ -160,6 +174,10 @@ class Assets extends PropertyObject
*/
public function add($asset)
{
if (!$asset) {
return $this;
}
$args = func_get_args();
// More than one asset
@ -184,7 +202,8 @@ class Assets extends PropertyObject
call_user_func_array([$this, 'add'], $args);
} else {
// Get extension
$extension = pathinfo(parse_url($asset, PHP_URL_PATH), PATHINFO_EXTENSION);
$path = parse_url($asset, PHP_URL_PATH);
$extension = $path ? Utils::pathinfo($path, PATHINFO_EXTENSION) : '';
// JavaScript or CSS
if ($extension !== '') {
@ -193,6 +212,8 @@ class Assets extends PropertyObject
call_user_func_array([$this, 'addCss'], $args);
} elseif ($extension === 'js') {
call_user_func_array([$this, 'addJs'], $args);
} elseif ($extension === 'mjs') {
call_user_func_array([$this, 'addJsModule'], $args);
}
}
}
@ -222,7 +243,7 @@ class Assets extends PropertyObject
return $this;
}
if (($type === $this::CSS_TYPE || $type === $this::JS_TYPE) && isset($this->collections[$asset])) {
if ($this->isValidType($type) && isset($this->collections[$asset])) {
$this->addType($collection, $type, $this->collections[$asset], $options);
return $this;
}
@ -230,7 +251,9 @@ class Assets extends PropertyObject
// If pipeline disabled, set to position if provided, else after
if (isset($options['pipeline'])) {
if ($options['pipeline'] === false) {
$exclude_type = ($type === $this::JS_TYPE || $type === $this::INLINE_JS_TYPE) ? $this::JS : $this::CSS;
$exclude_type = $this->getBaseType($type);
$excludes = strtolower($exclude_type . '_pipeline_before_excludes');
if ($this->{$excludes}) {
$default = 'after';
@ -245,7 +268,13 @@ class Assets extends PropertyObject
}
// Add timestamp
$options['timestamp'] = $this->timestamp;
$timestamp_override = $options['timestamp'] ?? true;
if (filter_var($timestamp_override, FILTER_VALIDATE_BOOLEAN)) {
$options['timestamp'] = $this->timestamp;
} else {
$options['timestamp'] = null;
}
// Set order
$group = $options['group'] ?? 'head';
@ -269,6 +298,16 @@ class Assets extends PropertyObject
return $this;
}
/**
* Add a CSS asset or a collection of assets.
*
* @return $this
*/
public function addLink($asset)
{
return $this->addType($this::LINK_COLLECTION, $this::LINK_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::LINK_TYPE));
}
/**
* Add a CSS asset or a collection of assets.
*
@ -309,6 +348,25 @@ class Assets extends PropertyObject
return $this->addType($this::JS_COLLECTION, $this::INLINE_JS_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::INLINE_JS_TYPE));
}
/**
* Add a JS asset or a collection of assets.
*
* @return $this
*/
public function addJsModule($asset)
{
return $this->addType($this::JS_MODULE_COLLECTION, $this::JS_MODULE_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::JS_MODULE_TYPE));
}
/**
* Add an Inline JS asset or a collection of assets.
*
* @return $this
*/
public function addInlineJsModule($asset)
{
return $this->addType($this::JS_MODULE_COLLECTION, $this::INLINE_JS_MODULE_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::INLINE_JS_MODULE_TYPE));
}
/**
* Add/replace collection.
@ -400,7 +458,7 @@ class Assets extends PropertyObject
$after_assets = $this->filterAssets($group_assets, 'position', 'after', true);
// Pipeline
if ($this->{$pipeline_enabled}) {
if ($this->{$pipeline_enabled} ?? false) {
$options = array_merge($this->pipeline_options, ['timestamp' => $this->timestamp]);
$pipeline = new Pipeline($options);
@ -432,9 +490,29 @@ class Assets extends PropertyObject
* @param array $attributes
* @return string
*/
public function css($group = 'head', $attributes = [])
public function css($group = 'head', $attributes = [], $include_link = true)
{
$output = '';
if ($include_link) {
$output = $this->link($group, $attributes);
}
$output .= $this->render(self::CSS, $group, $attributes);
return $output;
}
/**
* Build the CSS link tags.
*
* @param string $group name of the group
* @param array $attributes
* @return string
*/
public function link($group = 'head', $attributes = [])
{
return $this->render('css', $group, $attributes);
return $this->render(self::LINK, $group, $attributes);
}
/**
@ -444,8 +522,71 @@ class Assets extends PropertyObject
* @param array $attributes
* @return string
*/
public function js($group = 'head', $attributes = [])
public function js($group = 'head', $attributes = [], $include_js_module = true)
{
$output = $this->render(self::JS, $group, $attributes);
if ($include_js_module) {
$output .= $this->jsModule($group, $attributes);
}
return $output;
}
/**
* Build the Javascript Modules tags
*
* @param string $group
* @param array $attributes
* @return string
*/
public function jsModule($group = 'head', $attributes = [])
{
return $this->render('js', $group, $attributes);
return $this->render(self::JS_MODULE, $group, $attributes);
}
/**
* @param string $group
* @param array $attributes
* @return string
*/
public function all($group = 'head', $attributes = [])
{
$output = $this->css($group, $attributes, false);
$output .= $this->link($group, $attributes);
$output .= $this->js($group, $attributes, false);
$output .= $this->jsModule($group, $attributes);
return $output;
}
/**
* @param class-string $type
* @return bool
*/
protected function isValidType($type)
{
return in_array($type, [self::CSS_TYPE, self::JS_TYPE, self::JS_MODULE_TYPE]);
}
/**
* @param class-string $type
* @return string
*/
protected function getBaseType($type)
{
switch ($type) {
case $this::JS_TYPE:
case $this::INLINE_JS_TYPE:
$base_type = $this::JS;
break;
case $this::JS_MODULE_TYPE:
case $this::INLINE_JS_MODULE_TYPE:
$base_type = $this::JS_MODULE;
break;
default:
$base_type = $this::CSS;
}
return $base_type;
}
}

23
system/src/Grav/Common/Assets/BaseAsset.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -26,8 +26,9 @@ abstract class BaseAsset extends PropertyObject
{
use AssetUtilsTrait;
protected const CSS_ASSET = true;
protected const JS_ASSET = false;
protected const CSS_ASSET = 1;
protected const JS_ASSET = 2;
protected const JS_MODULE_ASSET = 3;
/** @var string|false */
protected $asset;
@ -69,7 +70,7 @@ abstract class BaseAsset extends PropertyObject
* @param array $elements
* @param string|null $key
*/
public function __construct(array $elements = [], $key = null)
public function __construct(array $elements = [], ?string $key = null)
{
$base_config = [
'group' => 'head',
@ -248,6 +249,7 @@ abstract class BaseAsset extends PropertyObject
*
* @return array
*/
#[\ReturnTypeWillChange]
public function jsonSerialize()
{
return ['type' => $this->getType(), 'elements' => $this->getElements()];
@ -265,4 +267,17 @@ abstract class BaseAsset extends PropertyObject
{
return '';
}
/**
* Finds relative JS urls() and rewrites the URL with an absolute one
*
* @param string $file the css source file
* @param string $dir local relative path to the css file
* @param bool $local is this a local or remote asset
* @return string
*/
protected function jsRewrite($file, $dir, $local)
{
return '';
}
}

207
system/src/Grav/Common/Assets/BlockAssets.php

@ -0,0 +1,207 @@
<?php
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Assets;
use Grav\Common\Assets;
use Grav\Common\Config\Config;
use Grav\Common\Grav;
use Grav\Framework\ContentBlock\HtmlBlock;
use function strlen;
/**
* Register block assets into Grav.
*/
class BlockAssets
{
/**
* @param HtmlBlock $block
* @return void
*/
public static function registerAssets(HtmlBlock $block): void
{
$grav = Grav::instance();
/** @var Assets $assets */
$assets = $grav['assets'];
$types = $block->getAssets();
foreach ($types as $type => $groups) {
switch ($type) {
case 'frameworks':
static::registerFrameworks($assets, $groups);
break;
case 'styles':
static::registerStyles($assets, $groups);
break;
case 'scripts':
static::registerScripts($assets, $groups);
break;
case 'links':
static::registerLinks($assets, $groups);
break;
case 'html':
static::registerHtml($assets, $groups);
break;
}
}
}
/**
* @param Assets $assets
* @param array $list
* @return void
*/
protected static function registerFrameworks(Assets $assets, array $list): void
{
if ($list) {
throw new \RuntimeException('Not Implemented');
}
}
/**
* @param Assets $assets
* @param array $groups
* @return void
*/
protected static function registerStyles(Assets $assets, array $groups): void
{
$grav = Grav::instance();
/** @var Config $config */
$config = $grav['config'];
foreach ($groups as $group => $styles) {
foreach ($styles as $style) {
switch ($style[':type']) {
case 'file':
$options = [
'priority' => $style[':priority'],
'group' => $group,
'type' => $style['type'],
'media' => $style['media']
] + $style['element'];
$assets->addCss(static::getRelativeUrl($style['href'], $config->get('system.assets.css_pipeline')), $options);
break;
case 'inline':
$options = [
'priority' => $style[':priority'],
'group' => $group,
'type' => $style['type'],
] + $style['element'];
$assets->addInlineCss($style['content'], $options);
break;
}
}
}
}
/**
* @param Assets $assets
* @param array $groups
* @return void
*/
protected static function registerScripts(Assets $assets, array $groups): void
{
$grav = Grav::instance();
/** @var Config $config */
$config = $grav['config'];
foreach ($groups as $group => $scripts) {
$group = $group === 'footer' ? 'bottom' : $group;
foreach ($scripts as $script) {
switch ($script[':type']) {
case 'file':
$options = [
'group' => $group,
'priority' => $script[':priority'],
'src' => $script['src'],
'type' => $script['type'],
'loading' => $script['loading'],
'defer' => $script['defer'],
'async' => $script['async'],
'handle' => $script['handle']
] + $script['element'];
$assets->addJs(static::getRelativeUrl($script['src'], $config->get('system.assets.js_pipeline')), $options);
break;
case 'inline':
$options = [
'priority' => $script[':priority'],
'group' => $group,
'type' => $script['type'],
'loading' => $script['loading']
] + $script['element'];
$assets->addInlineJs($script['content'], $options);
break;
}
}
}
}
/**
* @param Assets $assets
* @param array $groups
* @return void
*/
protected static function registerLinks(Assets $assets, array $groups): void
{
foreach ($groups as $group => $links) {
foreach ($links as $link) {
$href = $link['href'];
$options = [
'group' => $group,
'priority' => $link[':priority'],
'rel' => $link['rel'],
] + $link['element'];
$assets->addLink($href, $options);
}
}
}
/**
* @param Assets $assets
* @param array $groups
* @return void
*/
protected static function registerHtml(Assets $assets, array $groups): void
{
if ($groups) {
throw new \RuntimeException('Not Implemented');
}
}
/**
* @param string $url
* @param bool $pipeline
* @return string
*/
protected static function getRelativeUrl($url, $pipeline)
{
$grav = Grav::instance();
$base = rtrim($grav['base_url'], '/') ?: '/';
if (strpos($url, $base) === 0) {
if ($pipeline) {
// Remove file timestamp if CSS pipeline has been enabled.
$url = preg_replace('|[?#].*|', '', $url);
}
return substr($url, strlen($base) - 1);
}
return $url;
}
}

4
system/src/Grav/Common/Assets/Css.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -22,7 +22,7 @@ class Css extends BaseAsset
* @param array $elements
* @param string|null $key
*/
public function __construct(array $elements = [], $key = null)
public function __construct(array $elements = [], ?string $key = null)
{
$base_options = [
'asset_type' => 'css',

4
system/src/Grav/Common/Assets/InlineCss.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -22,7 +22,7 @@ class InlineCss extends BaseAsset
* @param array $elements
* @param string|null $key
*/
public function __construct(array $elements = [], $key = null)
public function __construct(array $elements = [], ?string $key = null)
{
$base_options = [
'asset_type' => 'css',

4
system/src/Grav/Common/Assets/InlineJs.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -22,7 +22,7 @@ class InlineJs extends BaseAsset
* @param array $elements
* @param string|null $key
*/
public function __construct(array $elements = [], $key = null)
public function __construct(array $elements = [], ?string $key = null)
{
$base_options = [
'asset_type' => 'js',

46
system/src/Grav/Common/Assets/InlineJsModule.php

@ -0,0 +1,46 @@
<?php
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Assets;
use Grav\Common\Utils;
/**
* Class InlineJs
* @package Grav\Common\Assets
*/
class InlineJsModule extends BaseAsset
{
/**
* InlineJs constructor.
* @param array $elements
* @param string|null $key
*/
public function __construct(array $elements = [], ?string $key = null)
{
$base_options = [
'asset_type' => 'js_module',
'attributes' => ['type' => 'module'],
'position' => 'after'
];
$merged_attributes = Utils::arrayMergeRecursiveUnique($base_options, $elements);
parent::__construct($merged_attributes, $key);
}
/**
* @return string
*/
public function render()
{
return '<script' . $this->renderAttributes(). ">\n" . trim($this->asset) . "\n</script>\n";
}
}

4
system/src/Grav/Common/Assets/Js.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -22,7 +22,7 @@ class Js extends BaseAsset
* @param array $elements
* @param string|null $key
*/
public function __construct(array $elements = [], $key = null)
public function __construct(array $elements = [], ?string $key = null)
{
$base_options = [
'asset_type' => 'js',

49
system/src/Grav/Common/Assets/JsModule.php

@ -0,0 +1,49 @@
<?php
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Assets;
use Grav\Common\Utils;
/**
* Class Js
* @package Grav\Common\Assets
*/
class JsModule extends BaseAsset
{
/**
* Js constructor.
* @param array $elements
* @param string|null $key
*/
public function __construct(array $elements = [], ?string $key = null)
{
$base_options = [
'asset_type' => 'js_module',
'attributes' => ['type' => 'module']
];
$merged_attributes = Utils::arrayMergeRecursiveUnique($base_options, $elements);
parent::__construct($merged_attributes, $key);
}
/**
* @return string
*/
public function render()
{
if (isset($this->attributes['loading']) && $this->attributes['loading'] === 'inline') {
$buffer = $this->gatherLinks([$this], self::JS_MODULE_ASSET);
return '<script' . $this->renderAttributes() . ">\n" . trim($buffer) . "\n</script>\n";
}
return '<script src="' . trim($this->asset) . $this->renderQueryString() . '"' . $this->renderAttributes() . $this->integrityHash($this->asset) . "></script>\n";
}
}

43
system/src/Grav/Common/Assets/Link.php

@ -0,0 +1,43 @@
<?php
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Assets;
use Grav\Common\Utils;
/**
* Class Link
* @package Grav\Common\Assets
*/
class Link extends BaseAsset
{
/**
* Css constructor.
* @param array $elements
* @param string|null $key
*/
public function __construct(array $elements = [], ?string $key = null)
{
$base_options = [
'asset_type' => 'link',
];
$merged_attributes = Utils::arrayMergeRecursiveUnique($base_options, $elements);
parent::__construct($merged_attributes, $key);
}
/**
* @return string
*/
public function render()
{
return '<link href="' . trim($this->asset) . $this->renderQueryString() . '"' . $this->renderAttributes() . $this->integrityHash($this->asset) . ">\n";
}
}

65
system/src/Grav/Common/Assets/Pipeline.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -29,12 +29,16 @@ class Pipeline extends PropertyObject
{
use AssetUtilsTrait;
protected const CSS_ASSET = true;
protected const JS_ASSET = false;
protected const CSS_ASSET = 1;
protected const JS_ASSET = 2;
protected const JS_MODULE_ASSET = 3;
/** @const Regex to match CSS urls */
protected const CSS_URL_REGEX = '{url\(([\'\"]?)(.*?)\1\)}';
/** @const Regex to match JS imports */
protected const JS_IMPORT_REGEX = '{import.+from\s?[\'|\"](.+?)[\'|\"]}';
/** @const Regex to match CSS sourcemap comments */
protected const CSS_SOURCEMAP_REGEX = '{\/\*# (.*?) \*\/}';
@ -122,7 +126,7 @@ class Pipeline extends PropertyObject
// Compute uid based on assets and timestamp
$json_assets = json_encode($assets);
$uid = md5($json_assets . $this->css_minify . $this->css_rewrite . $group);
$uid = md5($json_assets . (int)$this->css_minify . (int)$this->css_rewrite . $group);
$file = $uid . '.css';
$relative_path = "{$this->base_url}{$this->assets_url}/{$file}";
@ -169,7 +173,7 @@ class Pipeline extends PropertyObject
* @param array $attributes
* @return bool|string URL or generated content if available, else false
*/
public function renderJs($assets, $group, $attributes = [])
public function renderJs($assets, $group, $attributes = [], $type = self::JS_ASSET)
{
// temporary list of assets to pipeline
$inline_group = false;
@ -198,7 +202,7 @@ class Pipeline extends PropertyObject
}
// Concatenate files
$buffer = $this->gatherLinks($assets, self::JS_ASSET);
$buffer = $this->gatherLinks($assets, $type);
// Minify if required
if ($this->shouldMinify('js')) {
@ -223,6 +227,19 @@ class Pipeline extends PropertyObject
return $output;
}
/**
* Minify and concatenate JS files.
*
* @param array $assets
* @param string $group
* @param array $attributes
* @return bool|string URL or generated content if available, else false
*/
public function renderJs_Module($assets, $group, $attributes = [])
{
$attributes['type'] = 'module';
return $this->renderJs($assets, $group, $attributes, self::JS_MODULE_ASSET);
}
/**
* Finds relative CSS urls() and rewrites the URL with an absolute one
@ -262,6 +279,42 @@ class Pipeline extends PropertyObject
return $file;
}
/**
* Finds relative JS urls() and rewrites the URL with an absolute one
*
* @param string $file the css source file
* @param string $dir local relative path to the css file
* @param bool $local is this a local or remote asset
* @return string
*/
protected function jsRewrite($file, $dir, $local)
{
// Find any js import elements, grab the URLs and calculate an absolute path
// Then replace the old url with the new one
$file = (string)preg_replace_callback(self::JS_IMPORT_REGEX, function ($matches) use ($dir, $local) {
$old_url = $matches[1];
// Ensure link is not rooted to web server, a data URL, or to a remote host
if (preg_match(self::FIRST_FORWARDSLASH_REGEX, $old_url) || $this->isRemoteLink($old_url)) {
return $matches[0];
}
// clean leading /
$old_url = Utils::normalizePath($dir . '/' . $old_url);
$old_url = str_replace('/./', '/', $old_url);
if (preg_match(self::FIRST_FORWARDSLASH_REGEX, $old_url)) {
$old_url = ltrim($old_url, '/');
}
$new_url = ($local ? $this->base_url : '') . $old_url;
return str_replace($matches[1], $new_url, $matches[0]);
}, $file);
return $file;
}
/**
* @param string $type
* @return bool

19
system/src/Grav/Common/Assets/Traits/AssetUtilsTrait.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets\Traits
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -62,13 +62,13 @@ trait AssetUtilsTrait
* Download and concatenate the content of several links.
*
* @param array $assets
* @param bool $css
* @param int $type
* @return string
*/
protected function gatherLinks(array $assets, $css = true)
protected function gatherLinks(array $assets, int $type = self::CSS_ASSET): string
{
$buffer = '';
foreach ($assets as $id => $asset) {
foreach ($assets as $asset) {
$local = true;
$link = $asset->getAsset();
@ -100,21 +100,25 @@ trait AssetUtilsTrait
}
// Double check last character being
if (!$css) {
if ($type === self::JS_ASSET || $type === self::JS_MODULE_ASSET) {
$file = rtrim($file, ' ;') . ';';
}
// If this is CSS + the file is local + rewrite enabled
if ($css && $this->css_rewrite) {
if ($type === self::CSS_ASSET && $this->css_rewrite) {
$file = $this->cssRewrite($file, $relative_dir, $local);
}
if ($type === self::JS_MODULE_ASSET) {
$file = $this->jsRewrite($file, $relative_dir, $local);
}
$file = rtrim($file) . PHP_EOL;
$buffer .= $file;
}
// Pull out @imports and move to top
if ($css) {
if ($type === self::CSS_ASSET) {
$buffer = $this->moveImports($buffer);
}
@ -188,6 +192,7 @@ trait AssetUtilsTrait
$querystring = '';
$asset = $asset ?? $this->asset;
$attributes = $this->attributes;
if (!empty($this->query)) {
if (Utils::contains($asset, '?')) {

2
system/src/Grav/Common/Assets/Traits/LegacyAssetsTrait.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets\Traits
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

11
system/src/Grav/Common/Assets/Traits/TestingAssetsTrait.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets\Traits
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -285,6 +285,15 @@ trait TestingAssetsTrait
return $this;
}
// Add JavaScript Module files
if ($pattern === self::JS_MODULE_REGEX) {
foreach ($files as $file) {
$this->addJsModule($file);
}
return $this;
}
// Unknown pattern.
foreach ($files as $asset) {
$this->add($asset);

9
system/src/Grav/Common/Backup/Backups.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Backup
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -104,8 +104,8 @@ class Backups
*/
public function getBackupDownloadUrl($backup, $base_url)
{
$param_sep = $param_sep = Grav::instance()['config']->get('system.param_sep', ':');
$download = urlencode(base64_encode(basename($backup)));
$param_sep = Grav::instance()['config']->get('system.param_sep', ':');
$download = urlencode(base64_encode(Utils::basename($backup)));
$url = rtrim(Grav::instance()['uri']->rootUrl(true), '/') . '/' . trim(
$base_url,
'/'
@ -144,9 +144,8 @@ class Backups
public static function getTotalBackupsSize()
{
$backups = static::getAvailableBackups();
$size = array_sum(array_column($backups, 'size'));
return $size ?? 0;
return $backups ? array_sum(array_column($backups, 'size')) : 0;
}
/**

2
system/src/Grav/Common/Browser.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

4
system/src/Grav/Common/Cache.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -177,7 +177,7 @@ class Cache extends Getters
public function purgeOldCache()
{
$cache_dir = dirname($this->cache_dir);
$current = basename($this->cache_dir);
$current = Utils::basename($this->cache_dir);
$count = 0;
foreach (new DirectoryIterator($cache_dir) as $file) {

2
system/src/Grav/Common/Composer.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

2
system/src/Grav/Common/Config/CompiledBase.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

2
system/src/Grav/Common/Config/CompiledBlueprints.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

2
system/src/Grav/Common/Config/CompiledConfig.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

2
system/src/Grav/Common/Config/CompiledLanguages.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

2
system/src/Grav/Common/Config/Config.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

2
system/src/Grav/Common/Config/ConfigFileFinder.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

2
system/src/Grav/Common/Config/Languages.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

11
system/src/Grav/Common/Config/Setup.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -182,13 +182,14 @@ class Setup extends Data
// If no environment is set, make sure we get one (CLI or hostname).
if (null === $environment) {
if (defined('GRAV_CLI')) {
$request = null;
$uri = null;
$environment = 'cli';
} else {
/** @var ServerRequestInterface $request */
$request = $container['request'];
$host = $request->getUri()->getHost();
$environment = Utils::substrToString($host, ':');
$uri = $request->getUri();
$environment = $uri->getHost();
}
}
@ -401,7 +402,7 @@ class Setup extends Data
}
// Create security.yaml salt if it doesn't exist into existing configuration environment if possible.
$securityFile = basename(static::$securityFile);
$securityFile = Utils::basename(static::$securityFile);
$securityFolder = substr(static::$securityFile, 0, -\strlen($securityFile));
$securityFolder = $locator->findResource($securityFolder, true) ?: $locator->findResource($securityFolder, true, true);
$filename = "{$securityFolder}/{$securityFile}";

21
system/src/Grav/Common/Data/Blueprint.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Data
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -99,7 +99,7 @@ class Blueprint extends BlueprintForm
*/
public function getDefaultValue(string $name)
{
$path = explode('.', $name) ?: [];
$path = explode('.', $name);
$current = $this->getDefaults();
foreach ($path as $field) {
@ -293,15 +293,16 @@ class Blueprint extends BlueprintForm
/**
* Flatten data by using blueprints.
*
* @param array $data
* @param bool $includeAll
* @param array $data Data to be flattened.
* @param bool $includeAll True if undefined properties should also be included.
* @param string $name Property which will be flattened, useful for flattening repeating data.
* @return array
*/
public function flattenData(array $data, bool $includeAll = false)
public function flattenData(array $data, bool $includeAll = false, string $name = '')
{
$this->initInternals();
return $this->blueprintSchema->flattenData($data, $includeAll);
return $this->blueprintSchema->flattenData($data, $includeAll, $name);
}
@ -514,7 +515,7 @@ class Blueprint extends BlueprintForm
$success = $this->resolveActions($user, $actions);
}
if (!$success) {
$this->addPropertyRecursive($field, 'validate', ['ignore' => true]);
static::addPropertyRecursive($field, 'validate', ['ignore' => true]);
}
}
@ -565,7 +566,7 @@ class Blueprint extends BlueprintForm
}
if ($matches) {
$this->addPropertyRecursive($field, 'validate', ['ignore' => true]);
static::addPropertyRecursive($field, 'validate', ['ignore' => true]);
return;
}
}
@ -576,7 +577,7 @@ class Blueprint extends BlueprintForm
* @param mixed $value
* @return void
*/
protected function addPropertyRecursive(array &$field, $property, $value)
public static function addPropertyRecursive(array &$field, $property, $value)
{
if (is_array($value) && isset($field[$property]) && is_array($field[$property])) {
$field[$property] = array_merge_recursive($field[$property], $value);
@ -586,7 +587,7 @@ class Blueprint extends BlueprintForm
if (!empty($field['fields'])) {
foreach ($field['fields'] as $key => &$child) {
$this->addPropertyRecursive($child, $property, $value);
static::addPropertyRecursive($child, $property, $value);
}
}
}

27
system/src/Grav/Common/Data/BlueprintSchema.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Data
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -83,7 +83,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
}
if (!empty($messages)) {
throw (new ValidationException())->setMessages($messages);
throw (new ValidationException('', 400))->setMessages($messages);
}
}
@ -115,23 +115,30 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
/**
* Flatten data by using blueprints.
*
* @param array $data Data to be flattened.
* @param bool $includeAll
* @param array $data Data to be flattened.
* @param bool $includeAll True if undefined properties should also be included.
* @param string $name Property which will be flattened, useful for flattening repeating data.
* @return array
*/
public function flattenData(array $data, bool $includeAll = false)
public function flattenData(array $data, bool $includeAll = false, string $name = '')
{
$prefix = $name !== '' ? $name . '.' : '';
$list = [];
if ($includeAll) {
foreach ($this->items as $key => $rules) {
$items = $name !== '' ? $this->getProperty($name)['fields'] ?? [] : $this->items;
foreach ($items as $key => $rules) {
$type = $rules['type'] ?? '';
if (!str_starts_with($type, '_') && !str_contains($key, '*')) {
$list[$key] = null;
$ignore = (bool) array_filter((array)($rules['validate']['ignore'] ?? [])) ?? false;
if (!str_starts_with($type, '_') && !str_contains($key, '*') && $ignore !== true) {
$list[$prefix . $key] = null;
}
}
}
return array_replace($list, $this->flattenArray($data, $this->nested, ''));
$nested = $this->getNestedRules($name);
return array_replace($list, $this->flattenArray($data, $nested, $prefix));
}
/**
@ -199,7 +206,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
/** @var Config $config */
$config = Grav::instance()['config'];
if (!$config->get('system.strict_mode.blueprint_strict_compat', true)) {
throw new RuntimeException(sprintf('%s is not defined in blueprints', $key));
throw new RuntimeException(sprintf('%s is not defined in blueprints', $key), 400);
}
user_error(sprintf('Having extra key %s in your data is deprecated with blueprint having \'validation: strict\'', $key), E_USER_DEPRECATED);

2
system/src/Grav/Common/Data/Blueprints.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Data
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

3
system/src/Grav/Common/Data/Data.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Data
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -335,6 +335,7 @@ class Data implements DataInterface, ArrayAccess, \Countable, JsonSerializable,
/**
* @return array
*/
#[\ReturnTypeWillChange]
public function jsonSerialize()
{
return $this->items;

2
system/src/Grav/Common/Data/DataInterface.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Data
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

40
system/src/Grav/Common/Data/Validation.php

@ -3,7 +3,7 @@
/**
* @package Grav\Common\Data
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@ -246,7 +246,9 @@ class Validation
return false;
}
$max = (int)($params['max'] ?? 0);
$multiline = isset($params['multiline']) && $params['multiline'];
$max = (int)($params['max'] ?? ($multiline ? 65536 : 2048));
if ($max && $len > $max) {
return false;
}
@ -256,7 +258,7 @@ class Validation
return false;
}
if ((!isset($params['multiline']) || !$params['multiline']) && preg_match('/\R/um', $value)) {
if (!$multiline && preg_match('/\R/um', $value)) {
return false;
}
@ -317,6 +319,10 @@ class Validation
*/
public static function typeCommaList($value, array $params, array $field)
{
if (!isset($params['max'])) {
$params['max'] = 2048;
}
return is_array($value) ? true : self::typeText($value, $params, $field);
}
@ -379,6 +385,10 @@ class Validation
*/
public static function typePassword($value, array $params, array $field)
{
if (!isset($params['max'])) {
$params['max'] = 256;
}
return self::typeText($value, $params, $field);
}
@ -621,10 +631,14 @@ class Validation
*/
public static function typeEmail($value, array $params, array $field)
{
if (!isset($params['max'])) {
$params['max'] = 320;
}
$values = !is_array($value) ? explode(',', preg_replace('/\s+/', '', $value)) : $value;
foreach ($values as $val) {
if (!(self::typeText($val, $params, $field) && filter_var($val, FILTER_VALIDATE_EMAIL))) {
if (!(self::typeText($val, $params, $field) && strpos($val, '@', 1))) {
return false;
}
}
@ -642,6 +656,10 @@ class Validation
*/
public static function typeUrl($value, array $params, array $field)
{
if (!isset($params['max'])) {
$params['max'] = 2048;
}
return self::typeText($value, $params, $field) && filter_var($value, FILTER_VALIDATE_URL);
}
@ -781,14 +799,22 @@ class Validation
}
// If creating new values is allowed, no further checks are needed.
if (!empty($field['selectize']['create'])) {
$validateOptions = $field['validate']['options'] ?? null;
if (!empty($field['selectize']['create']) || $validateOptions === 'ignore') {
return true;
}
$options = $field['options'] ?? [];
$use = $field['use'] ?? 'values';
if (empty($field['selectize']) || empty($field['multiple'])) {
if ($validateOptions) {
// Use custom options structure.
foreach ($options as &$option) {
$option = $option[$validateOptions] ?? null;
}
unset($option);
$options = array_values($options);
} elseif (empty($field['selectize']) || empty($field['multiple'])) {
$options = array_keys($options);
}
if ($use === 'keys') {
@ -808,7 +834,7 @@ class Validation
{
$value = static::filterArray($value, $params, $field);
return Utils::arrayUnflattenDotNotation($value);
return is_array($value) ? Utils::arrayUnflattenDotNotation($value) : null;
}
/**

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save