mirror of
https://github.com/getgrav/grav.git
synced 2025-12-05 15:29:57 +01:00
Compare commits
89 Commits
1.7.0-rc.2
...
1.7.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
916b7cfc44 | ||
|
|
317efc182a | ||
|
|
02b3b9a4d7 | ||
|
|
4bcfcfc4f8 | ||
|
|
57d0fc3774 | ||
|
|
2d1bca11df | ||
|
|
ac0de93139 | ||
|
|
9e39860868 | ||
|
|
b13a4d331c | ||
|
|
065cd5b7b7 | ||
|
|
1d34b5389e | ||
|
|
23df7cc90d | ||
|
|
c9a5d5a406 | ||
|
|
a5d2c86e59 | ||
|
|
f095cc760b | ||
|
|
c256b0675d | ||
|
|
a7af12d026 | ||
|
|
d7995e9be4 | ||
|
|
af2f4b66b9 | ||
|
|
15464aa10c | ||
|
|
7cfc02e7d9 | ||
|
|
c6b0a50e4e | ||
|
|
ac4f093590 | ||
|
|
a03746145b | ||
|
|
3a3b84af61 | ||
|
|
3ba68bf5c5 | ||
|
|
454e9c1d8e | ||
|
|
ee9fb4aeec | ||
|
|
ba2c224d5d | ||
|
|
515eab7dad | ||
|
|
18b0e01a3e | ||
|
|
57c6414d1c | ||
|
|
fdfcaaeb39 | ||
|
|
561b91894c | ||
|
|
aea01a3937 | ||
|
|
9b34178332 | ||
|
|
eced8facb8 | ||
|
|
074e13325e | ||
|
|
d7378f87c5 | ||
|
|
4091c16e97 | ||
|
|
56408b37ac | ||
|
|
fd18eeb62c | ||
|
|
cbd4ed311f | ||
|
|
8122703e3b | ||
|
|
2920b6143b | ||
|
|
8870463afc | ||
|
|
23fa2324a8 | ||
|
|
0df1082778 | ||
|
|
6647b8da3c | ||
|
|
f0b9411e19 | ||
|
|
5c1cc5cdd7 | ||
|
|
df9eb60ffa | ||
|
|
9893792768 | ||
|
|
379033aae4 | ||
|
|
193ab52a35 | ||
|
|
9eb6662db8 | ||
|
|
5e48146f59 | ||
|
|
627702a3a1 | ||
|
|
d9ee6de3d0 | ||
|
|
14df5a6d5f | ||
|
|
2738107b1e | ||
|
|
e53f26ca13 | ||
|
|
d3e80d62e5 | ||
|
|
b7aa20ed88 | ||
|
|
26da4a1aa3 | ||
|
|
4345a629f1 | ||
|
|
5bb4ca7822 | ||
|
|
79ee06f518 | ||
|
|
bc2435efe9 | ||
|
|
504a29f496 | ||
|
|
0948e9db9d | ||
|
|
c298464314 | ||
|
|
67a00a799c | ||
|
|
589c9e4445 | ||
|
|
d1925c8935 | ||
|
|
2923658bb9 | ||
|
|
c333da60d6 | ||
|
|
cf62db1329 | ||
|
|
625a39a892 | ||
|
|
ffc93a77a9 | ||
|
|
0cee2b6a97 | ||
|
|
a24ec2c433 | ||
|
|
e5727462e7 | ||
|
|
c1cb4e192f | ||
|
|
b0c12063a1 | ||
|
|
d4a20c71c2 | ||
|
|
ec68068b97 | ||
|
|
d9c1445542 | ||
|
|
6a4686d17b |
12
.github/workflows/build.yaml
vendored
12
.github/workflows/build.yaml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
if: "!github.event.release.prerelease"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -15,9 +15,11 @@ jobs:
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 7.1
|
||||
php-version: 7.3
|
||||
extensions: opcache, gd
|
||||
coverage: none
|
||||
env:
|
||||
COMPOSER_TOKEN: ${{ secrets.GLOBAL_TOKEN }}
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
@@ -38,9 +40,9 @@ jobs:
|
||||
|
||||
- name: Upload Grav Release Assets
|
||||
id: upload-release-asset
|
||||
uses: alexellis/upload-assets@0.2.2
|
||||
uses: alexellis/upload-assets@0.2.3
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
GITHUB_TOKEN: ${{ secrets.GLOBAL_TOKEN }}
|
||||
with:
|
||||
asset_paths: '["./grav-dist/*.zip"]'
|
||||
|
||||
@@ -59,6 +61,6 @@ jobs:
|
||||
author_name: 'Github Action Build'
|
||||
text: '🚚 Automated Build Failure'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
GITHUB_TOKEN: ${{ secrets.GLOBAL_TOKEN }}
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
|
||||
13
.github/workflows/tests.yaml
vendored
13
.github/workflows/tests.yaml
vendored
@@ -2,9 +2,9 @@ name: PHP Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ 1.7 ]
|
||||
branches: [ develop ]
|
||||
pull_request:
|
||||
branches: [ 1.7 ]
|
||||
branches: [ develop ]
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -26,6 +26,11 @@ jobs:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: opcache, gd
|
||||
coverage: none
|
||||
env:
|
||||
COMPOSER_TOKEN: ${{ secrets.GLOBAL_TOKEN }}
|
||||
|
||||
- name: Update composer
|
||||
run: composer update
|
||||
|
||||
- name: Validate composer.json and composer.lock
|
||||
run: composer validate
|
||||
@@ -59,9 +64,9 @@ jobs:
|
||||
status: failure
|
||||
fields: repo,message,author,action
|
||||
icon_emoji: ':octocat:'
|
||||
author_name: 'Github Action Tests (1.7)'
|
||||
author_name: 'Github Action Tests'
|
||||
text: '💥 Automated Test Failure'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
GITHUB_TOKEN: ${{ secrets.GLOBAL_TOKEN }}
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
|
||||
44
CHANGELOG.md
44
CHANGELOG.md
@@ -1,3 +1,43 @@
|
||||
# v1.7.0
|
||||
## 01/19/2021
|
||||
|
||||
1. [](#new)
|
||||
* Requires **PHP 7.3.6**
|
||||
* Read about this release in the [Grav 1.7 Released](https://getgrav.org/blog/grav-1.7-released) blog post
|
||||
* Read the full list of all changes in the [Changelog on GitHub](https://github.com/getgrav/grav/blob/1.7.0/CHANGELOG.md)
|
||||
* Please read [Grav 1.7 Upgrade Guide](https://learn.getgrav.org/17/advanced/grav-development/grav-17-upgrade-guide) before upgrading!
|
||||
* Added support for overriding configuration by using environment variables
|
||||
* Use PHP 7.4 serialization (the old `Serializable` methods are now final and cannot be overridden)
|
||||
* Enabled `ETag` setting by default for 304 responses
|
||||
* Added `FlexCollection::getDistinctValues()` to get all the assigned values from the field
|
||||
* `Flex Pages` method `$page->header()` returns `\Grav\Common\Page\Header` object, old `Page` class still returns `stdClass`
|
||||
1. [](#improved)
|
||||
* Make it possible to use an absolute path when loading a blueprint
|
||||
* Make serialize methods final in `ContentBlock`, `AbstractFile`, `FormTrait`, `ObjectCollectionTrait` and `ObjectTrait`
|
||||
* Added support for relative paths in `PageObject::getLevelListing()` [#3110](https://github.com/getgrav/grav/issues/3110)
|
||||
* Better `--env` and `--lang` support for `bin/grav`, `bin/gpm` and `bin/plugin` console commands
|
||||
* **BC BREAK** Shorthand for `--env`: `-e` will not work anymore as it conflicts with some plugins
|
||||
* Added support for locking the `start` and `limit` in a Page Collection
|
||||
1. [](#bugfix)
|
||||
* Fixed port issue with `system.custom_base_url`
|
||||
* Hide errors with `exif_read_data` in `ImageFile`
|
||||
* Fixed unserialize in `MarkdownFormatter` and `Framework\File` classes
|
||||
* Fixed pages with session messages should never be cached [#3108](https://github.com/getgrav/grav/issues/3108)
|
||||
* Fixed `Filesystem::normalize()` with dot-dot paths
|
||||
* Fixed Flex sorting issues [grav-plugin-flex-objects#92](https://github.com/trilbymedia/grav-plugin-flex-objects/issues/92)
|
||||
* Fixed Clockwork missing dumped arrays and objects
|
||||
* Fixed fatal error in PHP 8 when trying to access root page
|
||||
* Fixed Array->String conversion error when `languages:translations: false` [admin#1896](https://github.com/getgrav/grav-plugin-admin/issues/1896)
|
||||
* Fixed `Inflector` methods when translation is missing `GRAV.INFLECTOR_*` translations
|
||||
* Fixed exception when changing parent of new page [grav-plugin-admin#2018](https://github.com/getgrav/grav-plugin-admin/issues/2018)
|
||||
* Fixed ordering issue with moving pages [grav-plugin-admin#2015](https://github.com/getgrav/grav-plugin-admin/issues/2015)
|
||||
* Fixed Flex Pages cache not invalidating if saving an old `Page` object [#3152](https://github.com/getgrav/grav/issues/3152)
|
||||
* Fixed multiple issues with `system.language.translations: false`
|
||||
* Fixed page collections containing dummy items for untranslated default language [#2985](https://github.com/getgrav/grav/issues/2985)
|
||||
* Fixed streams in `setup.php` being overridden by `system/streams.yaml` [#2450](https://github.com/getgrav/grav/issues/2450)
|
||||
* Fixed `ERR_TOO_MANY_REDIRECTS` with HTTPS = 'On' [#3155](https://github.com/getgrav/grav/issues/3155)
|
||||
* Fixed page collection pagination not behaving as it did in Grav 1.6
|
||||
|
||||
# v1.7.0-rc.20
|
||||
## 12/15/2020
|
||||
|
||||
@@ -47,7 +87,7 @@
|
||||
* Allow `JsonFormatter` options to be passed as a string
|
||||
* Hide Flex Pages frontend configuration (not ready for production use)
|
||||
* Improve Flex configuration: gather views together in blueprint
|
||||
* Added XSS detection to all forms. See [documentation](http://learn.grav.local/17/forms/forms/form-options#xss-checks)
|
||||
* Added XSS detection to all forms. See [documentation](https://learn.getgrav.org/17/forms/forms/form-options#xss-checks)
|
||||
* Better handling of missing repository index [grav-plugin-admin#1916](https://github.com/getgrav/grav-plugin-admin/issues/1916)
|
||||
* Added support for having all sites / environments under `user/env` folder [#3072](https://github.com/getgrav/grav/issues/3072)
|
||||
* Added `FlexObject::refresh()` method to make sure object is up to date
|
||||
@@ -548,7 +588,7 @@
|
||||
* Optimization: Combine some early Grav processors into a single one
|
||||
|
||||
# v1.6.31
|
||||
## mm/dd/2020
|
||||
## 12/14/2020
|
||||
|
||||
1. [](#improved)
|
||||
* Allow all CSS and JS via `robots.txt` [#2006](https://github.com/getgrav/grav/issues/2006) [#3067](https://github.com/getgrav/grav/issues/3067)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Grav
|
||||
Copyright (c) 2021 Grav
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
18
README.md
18
README.md
@@ -24,6 +24,10 @@ The underlying architecture of Grav is designed to use well-established and _bes
|
||||
- PHP 7.3.6 or higher. Check the [required modules list](https://learn.getgrav.org/basics/requirements#php-requirements)
|
||||
- Check the [Apache](https://learn.getgrav.org/basics/requirements#apache-requirements) or [IIS](https://learn.getgrav.org/basics/requirements#iis-requirements) requirements
|
||||
|
||||
# Documentation
|
||||
|
||||
The full documentation can be found from [learn.getgrav.org](https://learn.getgrav.org).
|
||||
|
||||
# QuickStart
|
||||
|
||||
These are the options to get Grav:
|
||||
@@ -84,6 +88,11 @@ To update plugins and themes:
|
||||
$ bin/gpm update
|
||||
```
|
||||
|
||||
## Upgrading from older version
|
||||
|
||||
* [Upgrading to Grav 1.7](https://learn.getgrav.org/16/advanced/grav-development/grav-17-upgrade-guide)
|
||||
* [Upgrading to Grav 1.6](https://learn.getgrav.org/16/advanced/grav-development/grav-16-upgrade-guide)
|
||||
* [Upgrading from Grav <1.6](https://learn.getgrav.org/16/advanced/grav-development/grav-15-upgrade-guide)
|
||||
|
||||
# Contributing
|
||||
We appreciate any contribution to Grav, whether it is related to bugs, grammar, or simply a suggestion or improvement! Please refer to the [Contributing guide](CONTRIBUTING.md) for more guidance on this topic.
|
||||
@@ -128,7 +137,14 @@ See [LICENSE](LICENSE.txt)
|
||||
|
||||
# Running Tests
|
||||
|
||||
First install the dev dependencies by running `composer update` from the Grav root.
|
||||
First install the dev dependencies by running `composer install` from the Grav root.
|
||||
|
||||
Then `composer test` will run the Unit Tests, which should be always executed successfully on any site.
|
||||
Windows users should use the `composer test-windows` command.
|
||||
You can also run a single unit test file, e.g. `composer test tests/unit/Grav/Common/AssetsTest.php`
|
||||
|
||||
To run phpstan tests, you should run:
|
||||
|
||||
* `composer phpstan` for global tests
|
||||
* `composer phpstan-framework` for more strict tests
|
||||
* `composer phpstan-plugins` to test all installed plugins
|
||||
|
||||
356
UPGRADE-1.7.md
356
UPGRADE-1.7.md
@@ -1,356 +0,0 @@
|
||||
# UPGRADE FROM 1.6 TO 1.7.RC17
|
||||
|
||||
Grav 1.7 REQUIRES PHP 7.3.6
|
||||
|
||||
## ADMINISTRATORS
|
||||
|
||||
### YAML files
|
||||
|
||||
* Please run `bin/grav yamllinter` to find any YAML parsing errors in your site. You should run this command before and after upgrade. Grav falls back to older YAML parser if it detects an error, but it will slow down your site.
|
||||
|
||||
## Forms
|
||||
|
||||
* **BC BREAK** Fixed `validation: strict`. Please search through all your forms if you were using this feature. If you were, either remove the line or test if the form still works.
|
||||
* Added configuration option `system.strict_mode.blueprint_compat` to maintain old `validation: strict` behavior
|
||||
* If you disable compatibiity, form validation will be much more strict (recommended, but may break existing forms)
|
||||
|
||||
* DOCUMENT NEW FORM FLASH!
|
||||
|
||||
### Pages
|
||||
|
||||
* **BC BREAK** Fixed 404 error page when you go to non-routable page with routable child pages under it. Now you get redirected to the first routable child page instead. This is probably what you wanted in the first place. If you do not want this new behavior, you need to **TODO**
|
||||
|
||||
### Media
|
||||
|
||||
* Support for `webp` image format
|
||||
* Markdown: Added support for native `loading=lazy` attributes on images. Can be set in `system.images.defaults` or per md image with `?loading=lazy`
|
||||
* Added ability to `noprocess` specific items only in Link/Image Excerpts, e.g. `http://foo.com/page?id=foo&target=_blank&noprocess=id`
|
||||
|
||||
### Multi-language
|
||||
|
||||
* Improved language support
|
||||
* **BC BREAK** Please check that your fallback languages are correct. Old implementation had a fallback to any other language, now only default language is being used unless you use `system.languages.content_fallback` configuration option to override the default behavior.
|
||||
|
||||
### Admin
|
||||
|
||||
* If you upgrade from older 1.7 RC, you need to go to Flex Objects plugin settings and turn on `Pages`, `User Accounts` and `User Groups` directories (upgrading 1.6 automatically turns them on)
|
||||
* Disabling `User Accounts` and `User Groups` directories in Flex Objects plugin should be kept enabled; fine tuned **ACL** may not work without
|
||||
|
||||
### Sessions
|
||||
|
||||
* Session ID now changes on login to prevent session fixation issues
|
||||
|
||||
### CLI
|
||||
|
||||
* Added new `bin/grav server` CLI command to easily run Symfony or PHP built-in web servers
|
||||
* Added new `bin/grav page-system-validator [-r|--record] [-c|--check]` CLI command to test Flex Pages
|
||||
* Improved `Scheduler` cron command check and more useful CLI information
|
||||
* Added new `-r <job-id>` option for Scheduler CLI command to force-run a job
|
||||
* Improved `bin/grav yamllinter` CLI command by adding an option to find YAML Linting issues from the whole site or custom folder
|
||||
|
||||
### Configuration
|
||||
|
||||
* Added new configuration option `system.debugger.provider` to choose between debugbar and clockwork
|
||||
* Added new configuration option `system.debugger.censored` to hide potentially sensitive information
|
||||
* Added new configuration option `system.pages.type` to enable Flex Pages
|
||||
* Added new configuration option `system.languages.include_default_lang_file_extension` to keep default language in `.md` files if set to `false`
|
||||
* Added new configuration option `system.languages.content_fallback` to set fallback content languages individually for every language
|
||||
* Added new configuration option `security.sanitize_svg` to remove potentially dangerous code from SVG files
|
||||
* Added system configuration support for `HTTP_X_Forwarded` headers (host disabled by default)
|
||||
* Added new configuration option `system.strict_mode.blueprint_compat` to maintain old `validation: strict` behavior
|
||||
|
||||
### Debugging
|
||||
|
||||
* Added support for [Clockwork](https://underground.works/clockwork) developer tools (now default debugger)
|
||||
* Added support for [Tideways XHProf](https://github.com/tideways/php-xhprof-extension) PHP Extension for profiling method calls
|
||||
* Added Twig profiling for Clockwork debugger
|
||||
|
||||
## DEVELOPERS
|
||||
|
||||
### Use composer autoloader
|
||||
|
||||
* Please add `composer.json` file to your plugin and run `composer update --no-dev` (and remember to keep it updated):
|
||||
|
||||
composer.json
|
||||
```json
|
||||
{
|
||||
"name": "getgrav/grav-plugin-example",
|
||||
"type": "grav-plugin",
|
||||
"description": "Example plugin for Grav CMS",
|
||||
"keywords": ["example", "plugin"],
|
||||
"homepage": "https://github.com/getgrav/grav-plugin-example",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "...",
|
||||
"email": "...",
|
||||
"homepage": "...",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/getgrav/grav-plugin-example/issues",
|
||||
"docs": "https://github.com/getgrav/grav-plugin-example/blob/master/README.md"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Grav\\Plugin\\Example\\": "classes/",
|
||||
"Grav\\Plugin\\Console\\": "cli/"
|
||||
},
|
||||
"classmap": [
|
||||
"example.php"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See [Composer schema](https://getcomposer.org/doc/04-schema.md)
|
||||
|
||||
* Please use autoloader instead of `require` in the code:
|
||||
|
||||
example.php
|
||||
```php
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
'onPluginsInitialized' => [
|
||||
// This is only required in Grav 1.6. Grav 1.7 automatically calls $plugin->autolaod() method.
|
||||
['autoload', 100000],
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Composer autoload.
|
||||
*
|
||||
* @return \Composer\Autoload\ClassLoader
|
||||
*/
|
||||
public function autoload(): \Composer\Autoload\ClassLoader
|
||||
{
|
||||
return require __DIR__ . '/vendor/autoload.php';
|
||||
}
|
||||
```
|
||||
|
||||
* Plugins & Themes: Call `$plugin->autoload()` and `$theme->autoload()` automatically when object gets initialized
|
||||
* Make sure your code does not use `require` or `include` for loading classes
|
||||
|
||||
### Plugin/Theme Blueprints (`blueprints.yaml`)
|
||||
|
||||
* Please add:
|
||||
```yaml
|
||||
slug: folder-name
|
||||
type: plugin|theme
|
||||
```
|
||||
* Make sure you update your dependencies. I recommend setting Grav to either 1.6 or 1.7 and update your code/vendor to PHP 7.1
|
||||
```yaml
|
||||
dependencies:
|
||||
- { name: grav, version: '>=1.6.0' }
|
||||
```
|
||||
|
||||
### Sessions
|
||||
|
||||
* Added `Session::regenerateId()` method to properly prevent session fixation issues
|
||||
|
||||
### ACL
|
||||
|
||||
* `user.authorize()` now requires user to be authorized (passed 2FA check), unless the rule contains `login` in its name.
|
||||
* Added support for more advanced ACL (CRUD)
|
||||
|
||||
* **BC BREAK** `user.authorize()` and Flex `object.isAuthorized()` now have two deny states: `false` and `null`.
|
||||
|
||||
Make sure you do not have strict checks against false: `$user->authorize($action) === false` (PHP) or `user.authorize(action) is same as(false)` (Twig).
|
||||
|
||||
For the negative checks you should be using `!user->authorize($action)` (PHP) or `not user.authorize(action)` (Twig).
|
||||
|
||||
The change has been done to allow strong deny rules by chaining the actions if previous ones do not match: `user.authorize(action1) ?? user.authorize(action2) ?? user.authorize(action3)`.
|
||||
|
||||
Note that Twig function `authorize()` will still **keeps** the old behavior!
|
||||
|
||||
### Pages
|
||||
|
||||
* Added experimental support for `Flex Pages` in the frontend (not recommended to use yet)
|
||||
* Admin uses `Flex Pages` by default (can be disabled from `Flex-Objects` plugin)
|
||||
* Added page specific admin permissions support for `Flex Pages`
|
||||
* Added root page support for `Flex Pages`
|
||||
* Fixed wrong `Pages::dispatch()` calls (with redirect) when we really meant to call `Pages::find()`
|
||||
* Added `Pages::getCollection()` method
|
||||
* Moved `collection()` and `evaluate()` logic from `Page` class into `Pages` class
|
||||
* **DEPRECATED** `$page->modular()` in favor of `$page->isModule()`
|
||||
* **DEPRECATED** `PageCollectionInterface::nonModular()` in favor of `PageCollectionInterface::pages()`
|
||||
* **DEPRECATED** `PageCollectionInterface::modular()` in favor of `PageCollectionInterface::modules()`
|
||||
|
||||
* **BC BREAK** Fixed `Page::modular()` and `Page::modularTwig()` returning `null` for folders and other non-initialized pages. Should not affect your code unless you were checking against `false` or `null`.
|
||||
* **BC BREAK** Always use `\Grav\Common\Page\Interfaces\PageInterface` instead of `\Grav\Common\Page\Page` in method signatures
|
||||
* Admin now uses `Flex Pages` by default, collection will behave in slightly different way
|
||||
* **BC BREAK** `$page->topParent()` may return page itself instead of null
|
||||
|
||||
### Media
|
||||
|
||||
* Added `MediaTrait::freeMedia()` method to free media (and memory)
|
||||
* Added support for uploading and deleting images directly in `Media` by using PSR-7
|
||||
* Adjusted asset types to enable extension of assets in class
|
||||
* **BC BREAK** Media no longer extends `Getters`, accessing `$media->$filename` no longer works, use `$media[$filename]` instead!
|
||||
|
||||
### Markdown
|
||||
|
||||
* **BC BREAK** Upgraded Parsedown to 1.7 for Parsedown-Extra 0.8. Plugins that extend Parsedown may need a fix to render as HTML
|
||||
* Added new `Excerpts::processLinkHtml()` method
|
||||
|
||||
### Users
|
||||
|
||||
* Added experimental support for `Flex Users` in the frontend (not recommended to use yet)
|
||||
* Admin uses `Flex Users` by default (can be disabled from `Flex-Objects` plugin)
|
||||
* Improved `Flex Users`: obey blueprints and allow Flex to be used in admin only
|
||||
* Improved `Flex Users`: user and group ACL now supports deny permissions
|
||||
* Changed `UserInterface::authorize()` to return `null` having the same meaning as `false` if access is denied because of no matching rule
|
||||
* **DEPRECATED** `\Grav\Common\User\Group` in favor of `$grav['user_groups']`, which contains Flex UserGroup collection
|
||||
* **BC BREAK** Always use `\Grav\Common\User\Interfaces\UserInterface` instead of `\Grav\Common\User\User` in method signatures
|
||||
|
||||
### Flex
|
||||
|
||||
* Do not use `Framework` Flex classes directly, it's better to use or extend classes under `Grav\Common\Flex\Types\Generic` namespace
|
||||
* Added `$grav['flex']` to access all registered Flex Directories
|
||||
* Added `FlexRegisterEvent` which triggers when `$grav['flex']` is being accessed the first time
|
||||
* Added `hasFlexFeature()` method to test if `FlexObject` or `FlexCollection` implements a given feature
|
||||
* Added `getFlexFeatures()` method to return all features that `FlexObject` or `FlexCollection` implements
|
||||
* Added `FlexStorage::getMetaData()` to get updated object meta information for listed keys
|
||||
* `FlexDirectory::getObject()` can now be called without any parameters to create a new object
|
||||
* Flex Directory: Implemented customizable configuration per flex type
|
||||
* **DEPRECATED** `FlexDirectory::update()` and `FlexDirectory::remove()`
|
||||
* **BC BREAK** Moved all Flex type classes under `Grav\Common\Flex`
|
||||
* **BC BREAK** `FlexStorageInterface::getStoragePath()` and `getMediaPath()` can now return null
|
||||
* **BC BREAK** `FlexStorageIngerface::getMetaData()` now has second optional argument
|
||||
* **BC BREAK** Flex objects no longer return temporary key if they do not have one; empty key is returned instead
|
||||
* **BC BREAK** Added reload argument to `FlexStorageInterface::getMetaData()`
|
||||
* You can add `edit_list.html.twig` file to a form field in order to customize look in the listing view
|
||||
|
||||
### Templating
|
||||
|
||||
* Added support for Twig 2.12 (using Twig 1.43)
|
||||
* Added a new `{% cache %}` Twig tag eliminating need for `twigcache` extension.
|
||||
* Added `array_diff()` twig function
|
||||
* Added `template_from_string()` twig function
|
||||
* Added a new `svg_image()` twig function to make it easier to 'include' SVG source in Twig
|
||||
* Improved `url()` twig function to take third parameter (`true`) to return URL on non-existing file instead of returning false
|
||||
* Improved `|array` twig filter to work with iterators and objects with `toArray()` method
|
||||
* Improved `authorize()` twig function to work better with nested rule parameters
|
||||
* Improved `|yaml_serialize` twig filter: added support for `JsonSerializable` objects and other array-like objects
|
||||
* Added `themes` to cached blueprints and configuration
|
||||
* Added default templates for `external.html.twig`, `default.html.twig`, and `modular.html.twig`
|
||||
|
||||
### Multi-language
|
||||
|
||||
* Improved language support for `Route` class
|
||||
* Translations: rename MODULAR to MODULE everywhere
|
||||
* Added `Language::getPageExtensions()` to get full list of supported page language extensions
|
||||
* **BC BREAK** Fixed `Language::getFallbackPageExtensions()` to fall back only to default language instead of going through all languages
|
||||
|
||||
### Blueprints
|
||||
|
||||
* Added `flatten_array` filter to form field validation
|
||||
* Added support for `security@: or: [admin.super, admin.pages]` in blueprints (nested AND/OR mode support)
|
||||
* Blueprint validation: Added `validate: value_type: bool|int|float|string|trim` to `array` to filter all the values inside the array
|
||||
* If your plugins has blueprints folder, initializing it in the event will be too late. Do this instead:
|
||||
|
||||
```php
|
||||
class MyPlugin extends Plugin
|
||||
{
|
||||
/** @var array */
|
||||
public $features = [
|
||||
'blueprints' => 0, // Use priority 0
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
### Events
|
||||
|
||||
* Use `Symfony EventDispatcher` directly instead of `rockettheme/toolbox` wrapper.
|
||||
* Added `$grav->dispatchEvent()` method for PSR-14 events
|
||||
* Added `PluginsLoadedEvent` which triggers after plugins have been loaded but not yet initialized
|
||||
* Added `SessionStartEvent` which triggers when session is started
|
||||
* Added `FlexRegisterEvent` which triggers when `$grav['flex']` is being accessed the first time
|
||||
* Added `PermissionsRegisterEvent` which triggers when `$grav['permissions']` is being accessed the first time
|
||||
* Added `onAfterCacheClear` event
|
||||
* Check `onAdminTwigTemplatePaths` event, it should NOT be:
|
||||
|
||||
```php
|
||||
public function onAdminTwigTemplatePaths($event)
|
||||
{
|
||||
// This code breaks all the other plugins in admin, including Flex Objects
|
||||
$event['paths'] = [__DIR__ . '/admin/themes/grav/templates'];
|
||||
}
|
||||
```
|
||||
but:
|
||||
```php
|
||||
public function onAdminTwigTemplatePaths($event)
|
||||
{
|
||||
// Add plugin template path for admin.
|
||||
$paths = $event['paths'];
|
||||
$paths[] = __DIR__ . '/admin/themes/grav/templates';
|
||||
$event['paths'] = $paths;
|
||||
}
|
||||
```
|
||||
|
||||
### Misc
|
||||
|
||||
* Added `Utils::isAssoc()` and `Utils::isNegative()` helper methods
|
||||
* Added `Utils::simpleTemplate()` method for very simple variable templating
|
||||
* Added `Utils::fullPath()` to get the full path to a file be it stream, relative, etc.
|
||||
* Support customizable null character replacement in `CSVFormatter::decode()`
|
||||
* Added new `Security::sanitizeSVG()` function
|
||||
* Added `$grav->close()` method to properly terminate the request with a response
|
||||
* Added `Folder::countChildren()` method to determine if a folder has child folders
|
||||
* Support symlinks when saving `File`
|
||||
* Added `Route::getBase()` method
|
||||
* **BC BREAK** Make `Route` objects immutable. This means that you need to do: `{% set route = route.withExtension('.html') %}` (for all `withX` methods) to keep the updated version.
|
||||
* Better `Content-Encoding` handling in Apache when content compression is disabled
|
||||
* Added a `Uri::getAllHeaders()` compatibility function
|
||||
|
||||
### CLI
|
||||
|
||||
* **BC BREAK** Many plugins initialize Grav in a wrong way, it is not safe to initialize plugins and theme by yourself
|
||||
* Following calls require Grav 1.6.21 or later, so it is recommended to set Grav dependency to that version
|
||||
* Inside `serve()` method:
|
||||
* Call `$this->setLanguage($langCode);` before doing anything else if you want to set the language (or use default)
|
||||
* Call one of following:
|
||||
* `$this->initializeGrav();` Already called if you're in `bin/plugin`, otherwise you may need to call this one
|
||||
* `$this->initializePlugins();` This initializes grav, plugins (up to `onPluginsInitialized`)
|
||||
* `$this->initializeThemes();` This initializes grav, plugins and theme
|
||||
* `$this->initializePages();` This initializes grav, plugins, theme and everything needed by pages
|
||||
* It is a good idea to prefix your CLI command classes with your plugin name, otherwise there may be naming conflicts (we already have some!)
|
||||
|
||||
### Composer dependencies
|
||||
|
||||
* Updated Symfony Components to 4.4, please update any deprecated features in your code
|
||||
* **BC BREAK** Please run `bin/grav yamllinter -f user://` to find any YAML parsing errors in your site (including your plugins and themes).
|
||||
|
||||
## PLUGINS
|
||||
|
||||
### Admin
|
||||
|
||||
* Added `Content Editor` option to user account blueprint
|
||||
|
||||
* **BC BREAK** Admin will not initialize frontend pages anymore, this has been done to greatly speed up Admin plugin.
|
||||
|
||||
Please call `$grav['admin']->enablePages()` or `{% do admin.enablePages() %}` if you need to access frontend pages. This call can be safely made multiple times.
|
||||
|
||||
If you're using `Flex Pages`, please use Flex Directory instead, it will make your code so much faster.
|
||||
|
||||
* Admin now uses Flex for editing `Accounts` and `Pages`. If your plugin hooks into either of those, please make sure they still work.
|
||||
|
||||
* Admin cache is enabled by default, make sure your plugin clears cache when needed. Please avoid clearing all cache!
|
||||
|
||||
### Shortcode Core
|
||||
|
||||
* **DEPRECATED** Every shortcode needs to have `init()` method, classes without it will stop working in the future.
|
||||
34
bin/gpm
34
bin/gpm
@@ -2,8 +2,8 @@
|
||||
<?php
|
||||
|
||||
use Grav\Common\Composer;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Console\Application\GpmApplication;
|
||||
|
||||
\define('GRAV_CLI', true);
|
||||
\define('GRAV_REQUEST_TIME', microtime(true));
|
||||
@@ -43,37 +43,7 @@ if (!function_exists('curl_version')) {
|
||||
exit('FATAL: GPM requires PHP Curl module to be installed');
|
||||
}
|
||||
|
||||
$climate = new League\CLImate\CLImate;
|
||||
$climate->arguments->add([
|
||||
'environment' => [
|
||||
'prefix' => 'e',
|
||||
'longPrefix' => 'env',
|
||||
'description' => 'Configuration Environment',
|
||||
'defaultValue' => 'localhost'
|
||||
]
|
||||
]);
|
||||
$climate->arguments->parse();
|
||||
|
||||
// Set up environment based on params.
|
||||
$environment = $climate->arguments->get('environment');
|
||||
|
||||
$grav = Grav::instance(array('loader' => $autoload));
|
||||
$grav->setup($environment);
|
||||
|
||||
$grav['config']->init();
|
||||
$grav['uri']->init();
|
||||
$grav['accounts'];
|
||||
|
||||
$app = new Application('Grav Package Manager', GRAV_VERSION);
|
||||
$app->addCommands(array(
|
||||
new \Grav\Console\Gpm\IndexCommand(),
|
||||
new \Grav\Console\Gpm\VersionCommand(),
|
||||
new \Grav\Console\Gpm\InfoCommand(),
|
||||
new \Grav\Console\Gpm\InstallCommand(),
|
||||
new \Grav\Console\Gpm\UninstallCommand(),
|
||||
new \Grav\Console\Gpm\UpdateCommand(),
|
||||
new \Grav\Console\Gpm\SelfupgradeCommand(),
|
||||
new \Grav\Console\Gpm\DirectInstallCommand(),
|
||||
));
|
||||
|
||||
$app = new GpmApplication('Grav Package Manager', GRAV_VERSION);
|
||||
$app->run();
|
||||
|
||||
35
bin/grav
35
bin/grav
@@ -3,8 +3,7 @@
|
||||
|
||||
use Grav\Common\Composer;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Console\Cli;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Grav\Console\Application\GravApplication;
|
||||
|
||||
\define('GRAV_CLI', true);
|
||||
\define('GRAV_REQUEST_TIME', microtime(true));
|
||||
@@ -36,41 +35,11 @@ if (!\extension_loaded('mbstring')) {
|
||||
@ini_set('default_charset', 'UTF-8');
|
||||
mb_internal_encoding('UTF-8');
|
||||
|
||||
$climate = new League\CLImate\CLImate;
|
||||
$climate->arguments->add([
|
||||
'environment' => [
|
||||
'prefix' => 'e',
|
||||
'longPrefix' => 'env',
|
||||
'description' => 'Configuration Environment',
|
||||
'defaultValue' => 'localhost'
|
||||
]
|
||||
]);
|
||||
$climate->arguments->parse();
|
||||
|
||||
// Set up environment based on params.
|
||||
$environment = $climate->arguments->get('environment');
|
||||
|
||||
$grav = Grav::instance(array('loader' => $autoload));
|
||||
$grav->setup($environment);
|
||||
|
||||
if (!file_exists(GRAV_ROOT . '/index.php')) {
|
||||
exit('FATAL: Must be run from ROOT directory of Grav!');
|
||||
}
|
||||
|
||||
$app = new Application('Grav CLI Application', GRAV_VERSION);
|
||||
$app->addCommands(array(
|
||||
new Cli\InstallCommand(),
|
||||
new Cli\ComposerCommand(),
|
||||
new Cli\SandboxCommand(),
|
||||
new Cli\CleanCommand(),
|
||||
new Cli\ClearCacheCommand(),
|
||||
new Cli\BackupCommand(),
|
||||
new Cli\NewProjectCommand(),
|
||||
new Cli\SchedulerCommand(),
|
||||
new Cli\SecurityCommand(),
|
||||
new Cli\LogViewerCommand(),
|
||||
new Cli\YamlLinterCommand(),
|
||||
new Cli\ServerCommand(),
|
||||
new Cli\PageSystemValidatorCommand(),
|
||||
));
|
||||
$app = new GravApplication('Grav CLI Application', GRAV_VERSION);
|
||||
$app->run();
|
||||
|
||||
114
bin/plugin
114
bin/plugin
@@ -2,12 +2,8 @@
|
||||
<?php
|
||||
|
||||
use Grav\Common\Composer;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Console\Application\PluginApplication;
|
||||
|
||||
\define('GRAV_CLI', true);
|
||||
\define('GRAV_REQUEST_TIME', microtime(true));
|
||||
@@ -43,112 +39,8 @@ if (!file_exists(GRAV_ROOT . '/index.php')) {
|
||||
exit('FATAL: Must be run from ROOT directory of Grav!');
|
||||
}
|
||||
|
||||
$climate = new League\CLImate\CLImate;
|
||||
$climate->arguments->add([
|
||||
'environment' => [
|
||||
'prefix' => 'e',
|
||||
'longPrefix' => 'env',
|
||||
'description' => 'Configuration Environment',
|
||||
'defaultValue' => 'localhost'
|
||||
]
|
||||
]);
|
||||
$climate->arguments->parse();
|
||||
|
||||
$environment = $climate->arguments->get('environment');
|
||||
|
||||
// Bootstrap Grav container.
|
||||
$grav = Grav::instance(array('loader' => $autoload));
|
||||
$grav->setup($environment);
|
||||
$grav->initializeCli();
|
||||
|
||||
$app = new Application('Grav Plugins Commands', GRAV_VERSION);
|
||||
$pattern = '([A-Z]\w+Command\.php)';
|
||||
|
||||
// get arguments and strip the application name
|
||||
if (null === $argv) {
|
||||
$argv = $_SERVER['argv'];
|
||||
}
|
||||
|
||||
$bin = array_shift($argv);
|
||||
$name = array_shift($argv);
|
||||
$argv = array_merge([$bin], $argv);
|
||||
|
||||
$input = new ArgvInput($argv);
|
||||
|
||||
/** @var \Grav\Common\Data\Data $plugin */
|
||||
$plugin = $grav['plugins']->get($name);
|
||||
|
||||
$output = new ConsoleOutput();
|
||||
$output->getFormatter()->setStyle('red', new OutputFormatterStyle('red', null, array('bold')));
|
||||
$output->getFormatter()->setStyle('white', new OutputFormatterStyle('white', null, array('bold')));
|
||||
|
||||
if (!$name) {
|
||||
$output->writeln('');
|
||||
$output->writeln('<red>Usage:</red>');
|
||||
$output->writeln(" {$bin} [slug] [command] [arguments]");
|
||||
$output->writeln('');
|
||||
$output->writeln('<red>Example:</red>');
|
||||
$output->writeln(" {$bin} error log -l 1 --trace");
|
||||
$list = Folder::all('plugins://', ['compare' => 'Pathname', 'pattern' => '/\/cli\/' . $pattern . '$/usm', 'levels' => 2]);
|
||||
|
||||
$total = 0;
|
||||
if (count($list)) {
|
||||
$available = [];
|
||||
$output->writeln('');
|
||||
$output->writeln('<red>Plugins with CLI available:</red>');
|
||||
foreach ($list as $index => $entry) {
|
||||
$split = explode('/', $entry);
|
||||
$entry = array_shift($split);
|
||||
$index = str_pad($index++ + 1, 2, '0', STR_PAD_LEFT);
|
||||
$total = str_pad($total++ + 1, 2, '0', STR_PAD_LEFT);
|
||||
if (\in_array($entry, $available, true)) {
|
||||
$total--;
|
||||
continue;
|
||||
}
|
||||
|
||||
$available[] = $entry;
|
||||
$commands_count = $index - $total + 1;
|
||||
$output->writeln(' ' . $total . '. <red>' . str_pad($entry, 15) . "</red> <white>{$bin} {$entry} list</white>");
|
||||
}
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
if (null === $plugin) {
|
||||
$output->writeln('');
|
||||
$output->writeln("<red>$name plugin not found</red>");
|
||||
die;
|
||||
}
|
||||
|
||||
if (!$plugin->enabled) {
|
||||
$output->writeln('');
|
||||
$output->writeln("<red>$name not enabled</red>");
|
||||
die;
|
||||
}
|
||||
|
||||
|
||||
if ($plugin === null) {
|
||||
$output->writeln("<red>Grav Plugin <white>'{$name}'</white> is not installed</red>");
|
||||
exit;
|
||||
}
|
||||
|
||||
$path = 'plugins://' . $name . '/cli';
|
||||
|
||||
try {
|
||||
$commands = Folder::all($path, ['compare' => 'Filename', 'pattern' => '/' . $pattern . '$/usm', 'levels' => 1]);
|
||||
} catch (\RuntimeException $e) {
|
||||
$output->writeln("<red>No Console Commands for <white>'{$name}'</white> where found in <white>'{$path}'</white></red>");
|
||||
exit;
|
||||
}
|
||||
|
||||
foreach ($commands as $command_path) {
|
||||
$full_path = $grav['locator']->findResource("plugins://{$name}/cli/{$command_path}");
|
||||
require_once $full_path;
|
||||
|
||||
$command_class = 'Grav\Plugin\Console\\' . preg_replace('/.php$/', '', $command_path);
|
||||
$command = new $command_class();
|
||||
$app->add($command);
|
||||
}
|
||||
|
||||
$app->run($input);
|
||||
$app = new PluginApplication('Grav Plugins Commands', GRAV_VERSION);
|
||||
$app->run();
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"erusev/parsedown": "^1.7",
|
||||
"erusev/parsedown-extra": "~0.8",
|
||||
"symfony/contracts": "~1.1",
|
||||
"symfony/yaml": "4.4.16",
|
||||
"symfony/yaml": "~4.4",
|
||||
"symfony/console": "~4.4",
|
||||
"symfony/event-dispatcher": "~4.4",
|
||||
"symfony/var-dumper": "~4.4",
|
||||
@@ -70,6 +70,10 @@
|
||||
"codeception/module-asserts": "^1.3",
|
||||
"codeception/module-phpbrowser": "^1.0"
|
||||
},
|
||||
"provide": {
|
||||
"symfony/polyfill-php72": "*",
|
||||
"symfony/polyfill-php73": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "Recommended for better performance",
|
||||
"ext-iconv": "Recommended for better performance",
|
||||
@@ -115,8 +119,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 3 -c ./tests/phpstan/phpstan.neon --memory-limit=480M system/src",
|
||||
"phpstan-framework": "vendor/bin/phpstan analyse -l 8 -c ./tests/phpstan/phpstan.neon --memory-limit=480M system/src/Grav/Framework system/src/Grav/Events system/src/Grav/Installer system/src/Grav/Common/Assets system/src/Grav/Common/Backup system/src/Grav/Common/Config system/src/Grav/Common/Data system/src/Grav/Common/Errors system/src/Grav/Common/File system/src/Grav/Common/Helpers system/src/Grav/Common/Language system/src/Grav/Common/Markdown system/src/Grav/Common/Form system/src/Grav/Common/User",
|
||||
"phpstan": "vendor/bin/phpstan analyse -l 1 -c ./tests/phpstan/phpstan.neon --memory-limit=480M system/src",
|
||||
"phpstan-framework": "vendor/bin/phpstan analyse -l 1 -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"
|
||||
|
||||
552
composer.lock
generated
552
composer.lock
generated
File diff suppressed because it is too large
Load Diff
2
now.json
2
now.json
@@ -1,4 +1,4 @@
|
||||
{
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "*.php", "use": "@now/php" }]
|
||||
}
|
||||
|
||||
@@ -12,4 +12,5 @@ Allow: /user/themes/
|
||||
Allow: /user/images/
|
||||
Allow: /
|
||||
Allow: *.css$
|
||||
Allow: *.js$
|
||||
Allow: *.js$
|
||||
Allow: /system/*.js$
|
||||
@@ -1,16 +0,0 @@
|
||||
schemes:
|
||||
image:
|
||||
type: Stream
|
||||
paths:
|
||||
- user://images
|
||||
- system://images
|
||||
|
||||
page:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user://pages
|
||||
|
||||
account:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user://accounts
|
||||
@@ -73,7 +73,7 @@ pages:
|
||||
expires: 604800 # Page expires time in seconds (604800 seconds = 7 days)
|
||||
cache_control: # Can be blank for no setting, or a valid `cache-control` text value
|
||||
last_modified: false # Set the last modified date header based on file modification timestamp
|
||||
etag: false # Set the etag header tag
|
||||
etag: true # Set the etag header tag
|
||||
vary_accept_encoding: false # Add `Vary: Accept-Encoding` header
|
||||
redirect_default_route: false # Automatically redirect to a page's default route
|
||||
redirect_default_code: 302 # Default code to use for redirects
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||
// Some standard defines
|
||||
define('GRAV', true);
|
||||
define('GRAV_VERSION', '1.7.0-rc.20');
|
||||
define('GRAV_VERSION', '1.7.0');
|
||||
define('GRAV_SCHEMA', '1.7.0_2020-11-20_1');
|
||||
define('GRAV_TESTING', true);
|
||||
define('GRAV_TESTING', false);
|
||||
|
||||
// PHP minimum requirement
|
||||
if (!defined('GRAV_PHP_MIN')) {
|
||||
|
||||
@@ -125,6 +125,8 @@ GRAV:
|
||||
- 'Freitag'
|
||||
- 'Samstag'
|
||||
- 'Sonntag'
|
||||
YES: 'Ja'
|
||||
NO: 'Nein'
|
||||
CRON:
|
||||
EVERY: jede
|
||||
EVERY_HOUR: jede Stunde
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace Grav\Common;
|
||||
|
||||
use Closure;
|
||||
use Grav\Common\Assets\Pipeline;
|
||||
use Grav\Common\Assets\Traits\LegacyAssetsTrait;
|
||||
use Grav\Common\Assets\Traits\TestingAssetsTrait;
|
||||
@@ -81,7 +82,7 @@ class Assets extends PropertyObject
|
||||
/** @var array */
|
||||
protected $pipeline_options = [];
|
||||
|
||||
/** @var \Closure|string */
|
||||
/** @var Closure|string */
|
||||
protected $fetch_command;
|
||||
/** @var string */
|
||||
protected $autoload;
|
||||
|
||||
@@ -15,6 +15,8 @@ use Grav\Common\Grav;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Framework\Object\PropertyObject;
|
||||
use MatthiasMullie\Minify\CSS;
|
||||
use MatthiasMullie\Minify\JS;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use function array_key_exists;
|
||||
|
||||
@@ -131,7 +133,7 @@ class Pipeline extends PropertyObject
|
||||
|
||||
// Minify if required
|
||||
if ($this->shouldMinify('css')) {
|
||||
$minifier = new \MatthiasMullie\Minify\CSS();
|
||||
$minifier = new CSS();
|
||||
$minifier->add($buffer);
|
||||
$buffer = $minifier->minify();
|
||||
}
|
||||
@@ -194,7 +196,7 @@ class Pipeline extends PropertyObject
|
||||
|
||||
// Minify if required
|
||||
if ($this->shouldMinify('js')) {
|
||||
$minifier = new \MatthiasMullie\Minify\JS();
|
||||
$minifier = new JS();
|
||||
$minifier->add($buffer);
|
||||
$buffer = $minifier->minify();
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Grav\Common\Assets\Traits;
|
||||
use Closure;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Utils;
|
||||
use function dirname;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
|
||||
@@ -80,7 +81,7 @@ trait AssetUtilsTrait
|
||||
if (0 === strpos($link, '//')) {
|
||||
$link = 'http:' . $link;
|
||||
}
|
||||
$relative_dir = \dirname($relative_path);
|
||||
$relative_dir = dirname($relative_path);
|
||||
} else {
|
||||
// Fix to remove relative dir if grav is in one
|
||||
if (($this->base_url !== '/') && Utils::startsWith($relative_path, $this->base_url)) {
|
||||
@@ -88,7 +89,7 @@ trait AssetUtilsTrait
|
||||
$relative_path = ltrim(preg_replace($base_url, '/', $link, 1), '/');
|
||||
}
|
||||
|
||||
$relative_dir = \dirname($relative_path);
|
||||
$relative_dir = dirname($relative_path);
|
||||
$link = ROOT_DIR . $relative_path;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace Grav\Common;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function donatj\UserAgent\parse_user_agent;
|
||||
|
||||
/**
|
||||
@@ -26,7 +27,7 @@ class Browser
|
||||
{
|
||||
try {
|
||||
$this->useragent = parse_user_agent();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
} catch (InvalidArgumentException $e) {
|
||||
$this->useragent = parse_user_agent("Mozilla/5.0 (compatible; Unknown;)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use function is_callable;
|
||||
|
||||
/**
|
||||
* Class CompiledConfig
|
||||
@@ -68,7 +69,7 @@ class CompiledConfig extends CompiledBase
|
||||
*/
|
||||
protected function createObject(array $data = [])
|
||||
{
|
||||
if ($this->withDefaults && empty($data) && \is_callable($this->callable)) {
|
||||
if ($this->withDefaults && empty($data) && is_callable($this->callable)) {
|
||||
$blueprints = $this->callable;
|
||||
$data = $blueprints()->getDefaults();
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use Grav\Common\Grav;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Service\ConfigServiceProvider;
|
||||
use Grav\Common\Utils;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* Class Config
|
||||
@@ -130,7 +131,7 @@ class Config extends Data
|
||||
{
|
||||
$setup = Grav::instance()['setup']->toArray();
|
||||
foreach ($setup as $key => $value) {
|
||||
if ($key === 'streams' || !\is_array($value)) {
|
||||
if ($key === 'streams' || !is_array($value)) {
|
||||
// Optimized as streams and simple values are fully defined in setup.
|
||||
$this->items[$key] = $value;
|
||||
} else {
|
||||
|
||||
@@ -37,7 +37,7 @@ class Setup extends Data
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string Current environment normalized to lower case.
|
||||
* @var string|null Current environment normalized to lower case.
|
||||
*/
|
||||
public static $environment;
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ use function is_array;
|
||||
use function is_int;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
* Class Blueprint
|
||||
@@ -371,6 +372,10 @@ class Blueprint extends BlueprintForm
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
if (is_string($path) && !$locator->isStream($path)) {
|
||||
if (is_file($path)) {
|
||||
return [$path];
|
||||
}
|
||||
|
||||
// Find path overrides.
|
||||
if (null === $context) {
|
||||
$paths = (array) ($this->overrides[$path] ?? null);
|
||||
@@ -383,7 +388,7 @@ class Blueprint extends BlueprintForm
|
||||
$context = $this->context;
|
||||
}
|
||||
|
||||
if ($context && $context[\strlen($context)-1] !== '/') {
|
||||
if ($context && $context[strlen($context)-1] !== '/') {
|
||||
$context .= '/';
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
*/
|
||||
public function processForm(array $data, array $toggles = [])
|
||||
{
|
||||
return $this->processFormRecursive($data, $toggles, $this->nested);
|
||||
return $this->processFormRecursive($data, $toggles, $this->nested) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,7 +254,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
/**
|
||||
* @param array $nested
|
||||
* @param string $parent
|
||||
* @return void
|
||||
* @return bool
|
||||
*/
|
||||
protected function buildIgnoreNested(array $nested, $parent = '')
|
||||
{
|
||||
@@ -272,6 +272,8 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
$key = trim($parent, '.');
|
||||
$this->items[$key]['validate']['ignore'] = true;
|
||||
}
|
||||
|
||||
return $ignore;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use ArrayAccess;
|
||||
use Exception;
|
||||
use JsonSerializable;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Countable;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
|
||||
@@ -25,7 +27,7 @@ use function is_object;
|
||||
* Class Data
|
||||
* @package Grav\Common\Data
|
||||
*/
|
||||
class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable, ExportInterface
|
||||
class Data implements DataInterface, ArrayAccess, \Countable, JsonSerializable, ExportInterface
|
||||
{
|
||||
use NestedArrayAccessWithGetters, Countable, Export;
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ use function is_bool;
|
||||
use function is_float;
|
||||
use function is_int;
|
||||
use function is_string;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
* Class Validation
|
||||
@@ -63,7 +64,7 @@ class Validation
|
||||
$name = ucfirst($field['label'] ?? $field['name']);
|
||||
$message = (string) isset($field['validate']['message'])
|
||||
? $language->translate($field['validate']['message'])
|
||||
: $language->translate('GRAV.FORM.INVALID_INPUT', null, true) . ' "' . $language->translate($name) . '"';
|
||||
: $language->translate('GRAV.FORM.INVALID_INPUT') . ' "' . $language->translate($name) . '"';
|
||||
|
||||
|
||||
// Validate type with fallback type text.
|
||||
@@ -238,16 +239,16 @@ class Validation
|
||||
$value = trim($value);
|
||||
}
|
||||
|
||||
if (isset($params['min']) && \strlen($value) < $params['min']) {
|
||||
if (isset($params['min']) && strlen($value) < $params['min']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($params['max']) && \strlen($value) > $params['max']) {
|
||||
if (isset($params['max']) && strlen($value) > $params['max']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$min = $params['min'] ?? 0;
|
||||
if (isset($params['step']) && (\strlen($value) - $min) % $params['step'] === 0) {
|
||||
if (isset($params['step']) && (strlen($value) - $min) % $params['step'] === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,12 +10,13 @@
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Class ValidationException
|
||||
* @package Grav\Common\Data
|
||||
*/
|
||||
class ValidationException extends \RuntimeException
|
||||
class ValidationException extends RuntimeException
|
||||
{
|
||||
/** @var array */
|
||||
protected $messages = [];
|
||||
|
||||
@@ -36,12 +36,14 @@ use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use ReflectionObject;
|
||||
use SplFileInfo;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Throwable;
|
||||
use Twig\Environment;
|
||||
use Twig\Template;
|
||||
use Twig\TemplateWrapper;
|
||||
use function array_slice;
|
||||
use function call_user_func;
|
||||
use function count;
|
||||
use function define;
|
||||
use function defined;
|
||||
@@ -743,18 +745,19 @@ class Debugger
|
||||
}
|
||||
|
||||
if ($this->clockwork) {
|
||||
$context = $isString;
|
||||
if (!is_scalar($message)) {
|
||||
$isString = $message;
|
||||
$message = '';
|
||||
$context = $message;
|
||||
$message = gettype($context);
|
||||
}
|
||||
if (is_bool($isString)) {
|
||||
$isString = [];
|
||||
if (is_bool($context)) {
|
||||
$context = [];
|
||||
} elseif (!is_array($context)) {
|
||||
$type = gettype($context);
|
||||
$context = [$type => $context];
|
||||
}
|
||||
if (!is_array($isString)) {
|
||||
$type = gettype($isString);
|
||||
$isString = [$type => $isString];
|
||||
}
|
||||
$this->clockwork->log($label, $message, $isString);
|
||||
|
||||
$this->clockwork->log($label, $message, $context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -836,7 +839,7 @@ class Debugger
|
||||
{
|
||||
if ($errno !== E_USER_DEPRECATED && $errno !== E_DEPRECATED) {
|
||||
if ($this->errorHandler) {
|
||||
return \call_user_func($this->errorHandler, $errno, $errstr, $errfile, $errline);
|
||||
return call_user_func($this->errorHandler, $errno, $errstr, $errfile, $errline);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -869,7 +872,7 @@ class Debugger
|
||||
foreach ($backtrace as $current) {
|
||||
if (isset($current['args'])) {
|
||||
foreach ($current['args'] as $arg) {
|
||||
if ($arg instanceof \SplFileInfo) {
|
||||
if ($arg instanceof SplFileInfo) {
|
||||
$arg = $arg->getPathname();
|
||||
}
|
||||
if (is_string($arg) && preg_match('/.+\.(yaml|md)$/i', $arg)) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace Grav\Common\Errors;
|
||||
|
||||
use ErrorException;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use Whoops\Handler\Handler;
|
||||
@@ -49,7 +50,7 @@ class SimplePageHandler extends Handler
|
||||
}
|
||||
$message = $inspector->getException()->getMessage();
|
||||
|
||||
if ($inspector->getException() instanceof \ErrorException) {
|
||||
if ($inspector->getException() instanceof ErrorException) {
|
||||
$code = Misc::translateErrorCode($code);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ use Grav\Framework\Flex\FlexCollection;
|
||||
/**
|
||||
* Class GenericCollection
|
||||
* @package Grav\Common\Flex\Generic
|
||||
*
|
||||
* @extends FLexCollection<string,GenericObject>
|
||||
*/
|
||||
class GenericCollection extends FlexCollection
|
||||
{
|
||||
|
||||
@@ -18,6 +18,9 @@ use Grav\Framework\Flex\FlexIndex;
|
||||
/**
|
||||
* Class GenericIndex
|
||||
* @package Grav\Common\Flex\Generic
|
||||
*
|
||||
* @extends FLexIndex<string,GenericObject,GenericCollection>
|
||||
* @mixin GenericCollection
|
||||
*/
|
||||
class GenericIndex extends FlexIndex
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Grav\Common\Flex\Types\Pages;
|
||||
|
||||
use Exception;
|
||||
use Grav\Common\Flex\Traits\FlexCollectionTrait;
|
||||
use Grav\Common\Flex\Traits\FlexGravTrait;
|
||||
use Grav\Common\Grav;
|
||||
@@ -34,6 +35,8 @@ use function is_string;
|
||||
* Class GravPageCollection
|
||||
* @package Grav\Plugin\FlexObjects\Types\GravPages
|
||||
*
|
||||
* @extends FlexPageCollection<string,PageObject>
|
||||
*
|
||||
* Incompatibilities with Grav\Common\Page\Collection:
|
||||
* $page = $collection->key() will not work at all
|
||||
* $clone = clone $collection does not clone objects inside the collection, does it matter?
|
||||
@@ -43,6 +46,12 @@ use function is_string;
|
||||
* $collection->filter() incompatible method signature (takes closure instead of callable)
|
||||
* $collection->prev() does not rewind the internal pointer
|
||||
* AND most methods are immutable; they do not update the current collection, but return updated one
|
||||
*
|
||||
* @method static shuffle()
|
||||
* @method static select(array $keys)
|
||||
* @method static unselect(array $keys)
|
||||
* @method static createFrom(array $elements, string $keyField = null)
|
||||
* @method PageIndex getIndex()
|
||||
*/
|
||||
class PageCollection extends FlexPageCollection implements PageCollectionInterface
|
||||
{
|
||||
@@ -100,7 +109,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PageInterface|FlexObjectInterface
|
||||
* @return PageObject
|
||||
*/
|
||||
public function getRoot()
|
||||
{
|
||||
@@ -187,6 +196,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa
|
||||
* Return previous item.
|
||||
*
|
||||
* @return PageInterface|false
|
||||
* @phpstan-return PageObject|false
|
||||
*/
|
||||
public function prev()
|
||||
{
|
||||
@@ -201,6 +211,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa
|
||||
* Return nth item.
|
||||
* @param int $key
|
||||
* @return PageInterface|bool
|
||||
* @phpstan-return PageObject|false
|
||||
*/
|
||||
public function nth($key)
|
||||
{
|
||||
@@ -253,7 +264,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa
|
||||
* @param string $by
|
||||
* @param string $dir
|
||||
* @param array|null $manual
|
||||
* @param string|null $sort_flags
|
||||
* @param int|null $sort_flags
|
||||
* @return static
|
||||
*/
|
||||
public function order($by, $dir = 'asc', $manual = null, $sort_flags = null)
|
||||
@@ -422,7 +433,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa
|
||||
* @param string|false $endDate
|
||||
* @param string|null $field
|
||||
* @return static
|
||||
* @throws \Exception
|
||||
* @throws Exception
|
||||
*/
|
||||
public function dateRange($startDate, $endDate = false, $field = null)
|
||||
{
|
||||
@@ -761,7 +772,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa
|
||||
* Get the extended version of this Collection with each page keyed by route
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
* @throws Exception
|
||||
*/
|
||||
public function toExtendedArray(): array
|
||||
{
|
||||
@@ -771,6 +782,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa
|
||||
$entries[$object->route()] = $object->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ use Grav\Framework\Flex\FlexDirectory;
|
||||
use Grav\Framework\Flex\Interfaces\FlexCollectionInterface;
|
||||
use Grav\Framework\Flex\Interfaces\FlexStorageInterface;
|
||||
use Grav\Framework\Flex\Pages\FlexPageIndex;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
@@ -34,6 +35,9 @@ use function is_string;
|
||||
* Class GravPageObject
|
||||
* @package Grav\Plugin\FlexObjects\Types\GravPages
|
||||
*
|
||||
* @extends FlexPageIndex<string,PageObject,PageCollection>
|
||||
* @mixin PageCollection
|
||||
*
|
||||
* @method PageIndex withModules(bool $bool = true)
|
||||
* @method PageIndex withPages(bool $bool = true)
|
||||
* @method PageIndex withTranslation(bool $bool = true, string $languageCode = null, bool $fallback = null)
|
||||
@@ -205,7 +209,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
*
|
||||
* @param array $filters
|
||||
* @param bool $recursive
|
||||
* @return FlexCollectionInterface
|
||||
* @return static
|
||||
*/
|
||||
public function filterBy(array $filters, bool $recursive = false)
|
||||
{
|
||||
@@ -261,7 +265,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
|
||||
/**
|
||||
* @param array $filters
|
||||
* @return FlexCollectionInterface
|
||||
* @return static
|
||||
*/
|
||||
protected function filterByParent(array $filters)
|
||||
{
|
||||
@@ -590,7 +594,6 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
* Add a single page to a collection
|
||||
*
|
||||
* @param PageInterface $page
|
||||
*
|
||||
* @return PageCollection
|
||||
*/
|
||||
public function addPage(PageInterface $page)
|
||||
@@ -614,7 +617,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
* Merge another collection with the current collection
|
||||
*
|
||||
* @param PageCollectionInterface $collection
|
||||
* @return $this
|
||||
* @return PageCollection
|
||||
*/
|
||||
public function merge(PageCollectionInterface $collection)
|
||||
{
|
||||
@@ -626,7 +629,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
* Intersect another collection with the current collection
|
||||
*
|
||||
* @param PageCollectionInterface $collection
|
||||
* @return $this
|
||||
* @return PageCollection
|
||||
*/
|
||||
public function intersect(PageCollectionInterface $collection)
|
||||
{
|
||||
@@ -637,7 +640,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
* Split collection into array of smaller collections.
|
||||
*
|
||||
* @param int $size
|
||||
* @return PageCollectionInterface[]
|
||||
* @return PageCollection[]
|
||||
*/
|
||||
public function batch($size)
|
||||
{
|
||||
@@ -650,7 +653,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
* @param PageInterface|string|null $key
|
||||
*
|
||||
* @return $this
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function remove($key = null)
|
||||
{
|
||||
@@ -664,8 +667,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
* @param string $dir
|
||||
* @param array $manual
|
||||
* @param string $sort_flags
|
||||
*
|
||||
* @return PageCollectionInterface
|
||||
* @return static
|
||||
*/
|
||||
public function order($by, $dir = 'asc', $manual = null, $sort_flags = null)
|
||||
{
|
||||
@@ -679,7 +681,6 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
* Check to see if this item is the first in the collection.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool True if item is first.
|
||||
*/
|
||||
public function isFirst($path): bool
|
||||
@@ -695,7 +696,6 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
* Check to see if this item is the last in the collection.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool True if item is last.
|
||||
*/
|
||||
public function isLast($path): bool
|
||||
@@ -710,12 +710,11 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
* Gets the previous sibling based on current position.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return PageInterface|null The previous item.
|
||||
* @return PageObject|null The previous item.
|
||||
*/
|
||||
public function prevSibling($path)
|
||||
{
|
||||
/** @var PageInterface|null $result */
|
||||
/** @var PageObject|null $result */
|
||||
$result = $this->__call('prevSibling', [$path]);
|
||||
|
||||
return $result;
|
||||
@@ -725,12 +724,11 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
* Gets the next sibling based on current position.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return PageInterface|null The next item.
|
||||
* @return PageObject|null The next item.
|
||||
*/
|
||||
public function nextSibling($path)
|
||||
{
|
||||
/** @var PageInterface|null $result */
|
||||
/** @var PageObject|null $result */
|
||||
$result = $this->__call('nextSibling', [$path]);
|
||||
|
||||
return $result;
|
||||
@@ -741,12 +739,11 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
*
|
||||
* @param string $path
|
||||
* @param int $direction either -1 or +1
|
||||
*
|
||||
* @return PageInterface|false The sibling item.
|
||||
* @return PageObject|false The sibling item.
|
||||
*/
|
||||
public function adjacentSibling($path, $direction = 1)
|
||||
{
|
||||
/** @var PageInterface|false $result */
|
||||
/** @var PageObject|false $result */
|
||||
$result = $this->__call('adjacentSibling', [$path, $direction]);
|
||||
|
||||
return $result;
|
||||
@@ -756,7 +753,6 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
* Returns the item in the current position.
|
||||
*
|
||||
* @param string $path the path the item
|
||||
*
|
||||
* @return int|null The index of the current page, null if not found.
|
||||
*/
|
||||
public function currentPosition($path): ?int
|
||||
@@ -776,13 +772,11 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
* @param string $startDate
|
||||
* @param bool $endDate
|
||||
* @param string|null $field
|
||||
*
|
||||
* @return PageCollectionInterface
|
||||
* @throws \Exception
|
||||
* @return static
|
||||
* @throws Exception
|
||||
*/
|
||||
public function dateRange($startDate, $endDate = false, $field = null)
|
||||
{
|
||||
/** @var PageCollectionInterface $collection */
|
||||
$collection = $this->__call('dateRange', [$startDate, $endDate, $field]);
|
||||
|
||||
return $collection;
|
||||
@@ -802,11 +796,10 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
/**
|
||||
* Creates new collection with only visible pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only visible pages
|
||||
* @return static The collection with only visible pages
|
||||
*/
|
||||
public function visible()
|
||||
{
|
||||
/** @var PageCollectionInterface $collection */
|
||||
$collection = $this->__call('visible', []);
|
||||
|
||||
return $collection;
|
||||
@@ -815,11 +808,10 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
/**
|
||||
* Creates new collection with only non-visible pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only non-visible pages
|
||||
* @return static The collection with only non-visible pages
|
||||
*/
|
||||
public function nonVisible()
|
||||
{
|
||||
/** @var PageCollectionInterface $collection */
|
||||
$collection = $this->__call('nonVisible', []);
|
||||
|
||||
return $collection;
|
||||
@@ -828,11 +820,10 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
/**
|
||||
* Creates new collection with only non-modular pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only non-modular pages
|
||||
* @return static The collection with only non-modular pages
|
||||
*/
|
||||
public function pages()
|
||||
{
|
||||
/** @var PageCollectionInterface $collection */
|
||||
$collection = $this->__call('pages', []);
|
||||
|
||||
return $collection;
|
||||
@@ -841,11 +832,10 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
/**
|
||||
* Creates new collection with only modular pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only modular pages
|
||||
* @return static The collection with only modular pages
|
||||
*/
|
||||
public function modules()
|
||||
{
|
||||
/** @var PageCollectionInterface $collection */
|
||||
$collection = $this->__call('modules', []);
|
||||
|
||||
return $collection;
|
||||
@@ -854,7 +844,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
/**
|
||||
* Creates new collection with only modular pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only modular pages
|
||||
* @return static The collection with only modular pages
|
||||
*/
|
||||
public function modular()
|
||||
{
|
||||
@@ -864,7 +854,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
/**
|
||||
* Creates new collection with only non-modular pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only non-modular pages
|
||||
* @return static The collection with only non-modular pages
|
||||
*/
|
||||
public function nonModular()
|
||||
{
|
||||
@@ -874,11 +864,10 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
/**
|
||||
* Creates new collection with only published pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only published pages
|
||||
* @return static The collection with only published pages
|
||||
*/
|
||||
public function published()
|
||||
{
|
||||
/** @var PageCollectionInterface $collection */
|
||||
$collection = $this->__call('published', []);
|
||||
|
||||
return $collection;
|
||||
@@ -887,11 +876,10 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
/**
|
||||
* Creates new collection with only non-published pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only non-published pages
|
||||
* @return static The collection with only non-published pages
|
||||
*/
|
||||
public function nonPublished()
|
||||
{
|
||||
/** @var PageCollectionInterface $collection */
|
||||
$collection = $this->__call('nonPublished', []);
|
||||
|
||||
return $collection;
|
||||
@@ -900,11 +888,10 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
/**
|
||||
* Creates new collection with only routable pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only routable pages
|
||||
* @return static The collection with only routable pages
|
||||
*/
|
||||
public function routable()
|
||||
{
|
||||
/** @var PageCollectionInterface $collection */
|
||||
$collection = $this->__call('routable', []);
|
||||
|
||||
return $collection;
|
||||
@@ -913,11 +900,10 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
/**
|
||||
* Creates new collection with only non-routable pages
|
||||
*
|
||||
* @return PageCollectionInterface The collection with only non-routable pages
|
||||
* @return static The collection with only non-routable pages
|
||||
*/
|
||||
public function nonRoutable()
|
||||
{
|
||||
/** @var PageCollectionInterface $collection */
|
||||
$collection = $this->__call('nonRoutable', []);
|
||||
|
||||
return $collection;
|
||||
@@ -927,12 +913,10 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
* Creates new collection with only pages of the specified type
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return PageCollectionInterface The collection
|
||||
* @return static The collection
|
||||
*/
|
||||
public function ofType($type)
|
||||
{
|
||||
/** @var PageCollectionInterface $collection */
|
||||
$collection = $this->__call('ofType', []);
|
||||
|
||||
return $collection;
|
||||
@@ -942,12 +926,10 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
* Creates new collection with only pages of one of the specified types
|
||||
*
|
||||
* @param string[] $types
|
||||
*
|
||||
* @return PageCollectionInterface The collection
|
||||
* @return static The collection
|
||||
*/
|
||||
public function ofOneOfTheseTypes($types)
|
||||
{
|
||||
/** @var PageCollectionInterface $collection */
|
||||
$collection = $this->__call('ofOneOfTheseTypes', []);
|
||||
|
||||
return $collection;
|
||||
@@ -957,12 +939,10 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
* Creates new collection with only pages of one of the specified access levels
|
||||
*
|
||||
* @param array $accessLevels
|
||||
*
|
||||
* @return PageCollectionInterface The collection
|
||||
* @return static The collection
|
||||
*/
|
||||
public function ofOneOfTheseAccessLevels($accessLevels)
|
||||
{
|
||||
/** @var PageCollectionInterface $collection */
|
||||
$collection = $this->__call('ofOneOfTheseAccessLevels', []);
|
||||
|
||||
return $collection;
|
||||
|
||||
@@ -96,11 +96,16 @@ class PageObject extends FlexPageObject
|
||||
|
||||
/**
|
||||
* @param string|array $query
|
||||
* @return Route
|
||||
* @return Route|null
|
||||
*/
|
||||
public function getRoute($query = []): Route
|
||||
public function getRoute($query = []): ?Route
|
||||
{
|
||||
$route = RouteFactory::createFromString($this->route());
|
||||
$route = $this->route();
|
||||
if (null === $route) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$route = RouteFactory::createFromString($route);
|
||||
if ($lang = $route->getLanguage()) {
|
||||
$grav = Grav::instance();
|
||||
if (!$grav['config']->get('system.languages.include_default_lang')) {
|
||||
@@ -295,7 +300,6 @@ class PageObject extends FlexPageObject
|
||||
* You need to call $this->save() in order to perform the move.
|
||||
*
|
||||
* @param PageInterface $parent New parent page.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function move(PageInterface $parent)
|
||||
@@ -316,6 +320,13 @@ class PageObject extends FlexPageObject
|
||||
*/
|
||||
protected function reorderSiblings(array $ordering)
|
||||
{
|
||||
$storageKey = $this->getMasterKey();
|
||||
$filesystem = Filesystem::getInstance(false);
|
||||
$oldParentKey = ltrim($filesystem->dirname("/{$storageKey}"), '/');
|
||||
$newParentKey = $this->getProperty('parent_key');
|
||||
$isMoved = $oldParentKey !== $newParentKey;
|
||||
$order = !$isMoved ? $this->order() : false;
|
||||
|
||||
$parent = $this->parent();
|
||||
if (!$parent) {
|
||||
throw new RuntimeException('Cannot reorder a page which has no parent');
|
||||
@@ -327,13 +338,6 @@ class PageObject extends FlexPageObject
|
||||
/** @var PageCollection|null $siblings */
|
||||
$siblings = $siblings->getCollection()->withOrdered()->orderBy(['order' => 'ASC']);
|
||||
|
||||
$storageKey = $this->getMasterKey();
|
||||
$filesystem = Filesystem::getInstance(false);
|
||||
$oldParentKey = ltrim($filesystem->dirname("/{$storageKey}"), '/');
|
||||
$newParentKey = $this->getProperty('parent_key');
|
||||
$isMoved = $oldParentKey !== $newParentKey;
|
||||
$order = !$isMoved ? $this->order() : false;
|
||||
|
||||
if ($storageKey !== null) {
|
||||
if ($order !== false) {
|
||||
// Add current page back to the list if it's ordered.
|
||||
@@ -366,7 +370,7 @@ class PageObject extends FlexPageObject
|
||||
$siblings->removeElement($this);
|
||||
|
||||
// If menu item was moved, just make it to be the last in order.
|
||||
if ($isMoved && $order !== false) {
|
||||
if ($isMoved && $this->order() !== false) {
|
||||
$parentKey = $this->getProperty('parent_key');
|
||||
$newParent = $this->getFlexDirectory()->getObject($parentKey, 'storage_key');
|
||||
$newSiblings = $newParent->children()->getCollection()->withOrdered();
|
||||
@@ -439,8 +443,26 @@ class PageObject extends FlexPageObject
|
||||
public function getLevelListing(array $options): array
|
||||
{
|
||||
$index = $this->getFlexDirectory()->getIndex();
|
||||
if (!is_callable([$index, 'getLevelListing'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return method_exists($index, 'getLevelListing') ? $index->getLevelListing($options) : [];
|
||||
// Deal with relative paths.
|
||||
$initial = $options['initial'] ?? null;
|
||||
$var = $initial ? 'leaf_route' : 'route';
|
||||
$route = $options[$var] ?? '';
|
||||
if ($route !== '' && !str_starts_with($route, '/')) {
|
||||
$filesystem = Filesystem::getInstance();
|
||||
|
||||
$route = "/{$this->getKey()}/{$route}";
|
||||
$route = $filesystem->normalize($route);
|
||||
|
||||
$options[$var] = $route;
|
||||
}
|
||||
|
||||
[$status, $message, $response,] = $index->getLevelListing($options);
|
||||
|
||||
return [$status, $message, $response, $options[$var] ?? null];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -591,8 +613,8 @@ class PageObject extends FlexPageObject
|
||||
'storage_key' => $this->getStorageKey(),
|
||||
'parent_key' => $this->getProperty('parent_key'),
|
||||
'order' => $this->getProperty('order'),
|
||||
'folder' => preg_replace('|^\d+\.|', '', $this->getProperty('folder')),
|
||||
'template' => preg_replace('|modular/|', '', $this->getProperty('template')),
|
||||
'folder' => preg_replace('|^\d+\.|', '', $this->getProperty('folder') ?? ''),
|
||||
'template' => preg_replace('|modular/|', '', $this->getProperty('template') ?? ''),
|
||||
'lang' => $newLang
|
||||
] + parent::prepareStorage();
|
||||
|
||||
|
||||
@@ -192,9 +192,6 @@ trait PageLegacyTrait
|
||||
throw new InvalidArgumentException('Argument should be either header variable name or array of parameters');
|
||||
}
|
||||
|
||||
if (!$pagination) {
|
||||
$params['pagination'] = false;
|
||||
}
|
||||
$context = [
|
||||
'pagination' => $pagination,
|
||||
'self' => $this
|
||||
|
||||
@@ -16,6 +16,7 @@ use Grav\Common\Language\Language;
|
||||
use Grav\Common\Page\Page;
|
||||
use Grav\Common\Utils;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use SplFileInfo;
|
||||
|
||||
/**
|
||||
* Implements PageTranslateInterface
|
||||
@@ -75,7 +76,7 @@ trait PageTranslateTrait
|
||||
|
||||
// FIXME: use flex, also rawRoute() does not fully work?
|
||||
$aPage = new Page();
|
||||
$aPage->init(new \SplFileInfo($path), $languageExtension);
|
||||
$aPage->init(new SplFileInfo($path), $languageExtension);
|
||||
if ($onlyPublished && !$aPage->published()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ use Grav\Framework\Flex\FlexCollection;
|
||||
/**
|
||||
* Class UserGroupCollection
|
||||
* @package Grav\Common\Flex\Types\UserGroups
|
||||
*
|
||||
* @extends FlexCollection<string,UserGroupObject>
|
||||
*/
|
||||
class UserGroupCollection extends FlexCollection
|
||||
{
|
||||
|
||||
@@ -19,7 +19,8 @@ use Grav\Framework\Flex\FlexIndex;
|
||||
* Class GroupIndex
|
||||
* @package Grav\Common\User\FlexUser
|
||||
*
|
||||
* @method bool|null authorize(string $action, string $scope = null)
|
||||
* @extends FlexIndex<string,UserGroupObject,UserGroupCollection>
|
||||
* @mixin UserGroupCollection
|
||||
*/
|
||||
class UserGroupIndex extends FlexIndex
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Grav\Common\Flex\Types\Users\Traits;
|
||||
|
||||
use Grav\Common\Page\Medium\ImageMedium;
|
||||
use Grav\Common\Page\Medium\StaticImageMedium;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Trait UserObjectLegacyTrait
|
||||
@@ -87,6 +88,6 @@ trait UserObjectLegacyTrait
|
||||
{
|
||||
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.6', E_USER_DEPRECATED);
|
||||
|
||||
return \count($this->jsonSerialize());
|
||||
return count($this->jsonSerialize());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ use function is_string;
|
||||
/**
|
||||
* Class UserCollection
|
||||
* @package Grav\Common\Flex\Types\Users
|
||||
*
|
||||
* @extends FlexCollection<string,UserObject>
|
||||
*/
|
||||
class UserCollection extends FlexCollection implements UserCollectionInterface
|
||||
{
|
||||
|
||||
@@ -27,6 +27,9 @@ use function method_exists;
|
||||
/**
|
||||
* Class UserIndex
|
||||
* @package Grav\Common\Flex\Types\Users
|
||||
*
|
||||
* @extends FlexIndex<string,UserObject,UserCollection>
|
||||
* @mixin UserCollection
|
||||
*/
|
||||
class UserIndex extends FlexIndex
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Grav\Common\Flex\Types\Users;
|
||||
|
||||
use Countable;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Flex\Traits\FlexGravTrait;
|
||||
@@ -65,7 +66,7 @@ use function is_object;
|
||||
* @property bool $authenticated
|
||||
* @property bool $authorized
|
||||
*/
|
||||
class UserObject extends FlexObject implements UserInterface, \Countable
|
||||
class UserObject extends FlexObject implements UserInterface, Countable
|
||||
{
|
||||
use FlexGravTrait;
|
||||
use FlexObjectTrait;
|
||||
|
||||
@@ -17,6 +17,7 @@ use Grav\Common\Iterator;
|
||||
use Grav\Common\Utils;
|
||||
use RocketTheme\Toolbox\File\YamlFile;
|
||||
use RuntimeException;
|
||||
use stdClass;
|
||||
use function array_key_exists;
|
||||
use function count;
|
||||
use function in_array;
|
||||
@@ -485,7 +486,7 @@ class GPM extends Iterator
|
||||
*
|
||||
* @param string $search Can be either the slug or the name
|
||||
* @param bool $ignore_exception True if should not fire an exception (for use in Twig)
|
||||
* @return Remote\Package|bool Package if found, FALSE if not
|
||||
* @return Remote\Package|false Package if found, FALSE if not
|
||||
*/
|
||||
public function findPackage($search, $ignore_exception = false)
|
||||
{
|
||||
@@ -728,7 +729,7 @@ class GPM extends Iterator
|
||||
$type = 'plugins';
|
||||
}
|
||||
|
||||
$not_found = new \stdClass();
|
||||
$not_found = new stdClass();
|
||||
$not_found->name = $inflector::camelize($search);
|
||||
$not_found->slug = $search;
|
||||
$not_found->package_type = $type;
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Grav\Common\GPM;
|
||||
use DirectoryIterator;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Grav;
|
||||
use RuntimeException;
|
||||
use ZipArchive;
|
||||
use function count;
|
||||
use function in_array;
|
||||
@@ -274,7 +275,7 @@ class Installer
|
||||
public static function copyInstall($source_path, $install_path)
|
||||
{
|
||||
if (empty($source_path)) {
|
||||
throw new \RuntimeException("Directory $source_path is missing");
|
||||
throw new RuntimeException("Directory $source_path is missing");
|
||||
}
|
||||
|
||||
Folder::rcopy($source_path, $install_path);
|
||||
|
||||
@@ -17,7 +17,9 @@ use Symfony\Component\HttpClient\Exception\TransportException;
|
||||
use Symfony\Component\HttpClient\HttpClient;
|
||||
use Symfony\Component\HttpClient\HttpOptions;
|
||||
use Symfony\Component\HttpClient\NativeHttpClient;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use function call_user_func;
|
||||
use function defined;
|
||||
use function function_exists;
|
||||
|
||||
/**
|
||||
@@ -40,7 +42,7 @@ class Response
|
||||
* @param array $overrides An array of parameters for both `curl` and `fopen`
|
||||
* @param callable|null $callback Either a function or callback in array notation
|
||||
* @return string The response of the request
|
||||
* @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function get($uri = '', $overrides = [], $callback = null)
|
||||
{
|
||||
@@ -57,7 +59,7 @@ class Response
|
||||
}
|
||||
|
||||
$config = Grav::instance()['config'];
|
||||
$referer = \defined('GRAV_CLI') ? 'grav_cli' : Grav::instance()['uri']->rootUrl(true);
|
||||
$referer = defined('GRAV_CLI') ? 'grav_cli' : Grav::instance()['uri']->rootUrl(true);
|
||||
$options = new HttpOptions();
|
||||
|
||||
// Set default Headers
|
||||
|
||||
@@ -29,10 +29,26 @@ use Grav\Common\Processors\TasksProcessor;
|
||||
use Grav\Common\Processors\ThemesProcessor;
|
||||
use Grav\Common\Processors\TwigProcessor;
|
||||
use Grav\Common\Scheduler\Scheduler;
|
||||
use Grav\Common\Service\AccountsServiceProvider;
|
||||
use Grav\Common\Service\AssetsServiceProvider;
|
||||
use Grav\Common\Service\BackupsServiceProvider;
|
||||
use Grav\Common\Service\ConfigServiceProvider;
|
||||
use Grav\Common\Service\ErrorServiceProvider;
|
||||
use Grav\Common\Service\FilesystemServiceProvider;
|
||||
use Grav\Common\Service\FlexServiceProvider;
|
||||
use Grav\Common\Service\InflectorServiceProvider;
|
||||
use Grav\Common\Service\LoggerServiceProvider;
|
||||
use Grav\Common\Service\OutputServiceProvider;
|
||||
use Grav\Common\Service\PagesServiceProvider;
|
||||
use Grav\Common\Service\RequestServiceProvider;
|
||||
use Grav\Common\Service\SessionServiceProvider;
|
||||
use Grav\Common\Service\StreamsServiceProvider;
|
||||
use Grav\Common\Service\TaskServiceProvider;
|
||||
use Grav\Common\Twig\Twig;
|
||||
use Grav\Framework\DI\Container;
|
||||
use Grav\Framework\Psr7\Response;
|
||||
use Grav\Framework\RequestHandler\RequestHandler;
|
||||
use Grav\Framework\Session\Messages;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
@@ -40,6 +56,7 @@ use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use function array_key_exists;
|
||||
use function call_user_func_array;
|
||||
use function function_exists;
|
||||
use function get_class;
|
||||
use function in_array;
|
||||
use function is_callable;
|
||||
@@ -64,21 +81,21 @@ class Grav extends Container
|
||||
* to the dependency injection container.
|
||||
*/
|
||||
protected static $diMap = [
|
||||
'Grav\Common\Service\AccountsServiceProvider',
|
||||
'Grav\Common\Service\AssetsServiceProvider',
|
||||
'Grav\Common\Service\BackupsServiceProvider',
|
||||
'Grav\Common\Service\ConfigServiceProvider',
|
||||
'Grav\Common\Service\ErrorServiceProvider',
|
||||
'Grav\Common\Service\FilesystemServiceProvider',
|
||||
'Grav\Common\Service\FlexServiceProvider',
|
||||
'Grav\Common\Service\InflectorServiceProvider',
|
||||
'Grav\Common\Service\LoggerServiceProvider',
|
||||
'Grav\Common\Service\OutputServiceProvider',
|
||||
'Grav\Common\Service\PagesServiceProvider',
|
||||
'Grav\Common\Service\RequestServiceProvider',
|
||||
'Grav\Common\Service\SessionServiceProvider',
|
||||
'Grav\Common\Service\StreamsServiceProvider',
|
||||
'Grav\Common\Service\TaskServiceProvider',
|
||||
AccountsServiceProvider::class,
|
||||
AssetsServiceProvider::class,
|
||||
BackupsServiceProvider::class,
|
||||
ConfigServiceProvider::class,
|
||||
ErrorServiceProvider::class,
|
||||
FilesystemServiceProvider::class,
|
||||
FlexServiceProvider::class,
|
||||
InflectorServiceProvider::class,
|
||||
LoggerServiceProvider::class,
|
||||
OutputServiceProvider::class,
|
||||
PagesServiceProvider::class,
|
||||
RequestServiceProvider::class,
|
||||
SessionServiceProvider::class,
|
||||
StreamsServiceProvider::class,
|
||||
TaskServiceProvider::class,
|
||||
'browser' => Browser::class,
|
||||
'cache' => Cache::class,
|
||||
'events' => EventDispatcher::class,
|
||||
@@ -145,6 +162,11 @@ class Grav extends Container
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function isSetup(): bool
|
||||
{
|
||||
return isset($this->initialized['setup']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Grav instance using specific environment.
|
||||
*
|
||||
@@ -252,7 +274,7 @@ class Grav extends Container
|
||||
);
|
||||
|
||||
$default = static function () {
|
||||
return new Response(404, ['Expires' => 0, 'Cache-Control' => 'no-cache, no-store, must-revalidate'], 'Not Found');
|
||||
return new Response(404, ['Expires' => 0, 'Cache-Control' => 'no-store, max-age=0'], 'Not Found');
|
||||
};
|
||||
|
||||
$collection = new RequestHandler($this->middleware, $default, $container);
|
||||
@@ -260,17 +282,28 @@ class Grav extends Container
|
||||
$response = $collection->handle($this['request']);
|
||||
$body = $response->getBody();
|
||||
|
||||
/** @var Messages $messages */
|
||||
$messages = $this['messages'];
|
||||
|
||||
// Prevent caching if session messages were displayed in the page.
|
||||
$noCache = $messages->isCleared();
|
||||
if ($noCache) {
|
||||
$response = $response->withHeader('Cache-Control', 'no-store, max-age=0');
|
||||
}
|
||||
|
||||
// Handle ETag and If-None-Match headers.
|
||||
if ($response->getHeaderLine('ETag') === '1') {
|
||||
$etag = md5($body);
|
||||
$response = $response->withHeader('ETag', $etag);
|
||||
$response = $response->withHeader('ETag', '"' . $etag . '"');
|
||||
|
||||
if ($this['request']->getHeaderLine('If-None-Match') === $etag) {
|
||||
$search = trim($this['request']->getHeaderLine('If-None-Match'), '"');
|
||||
if ($noCache === false && $search === $etag) {
|
||||
$response = $response->withStatus(304);
|
||||
$body = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Echo page content.
|
||||
$this->header($response);
|
||||
echo $body;
|
||||
|
||||
@@ -312,17 +345,28 @@ class Grav extends Container
|
||||
|
||||
$body = $response->getBody();
|
||||
|
||||
/** @var Messages $messages */
|
||||
$messages = $this['messages'];
|
||||
|
||||
// Prevent caching if session messages were displayed in the page.
|
||||
$noCache = $messages->isCleared();
|
||||
if ($noCache) {
|
||||
$response = $response->withHeader('Cache-Control', 'no-store, max-age=0');
|
||||
}
|
||||
|
||||
// Handle ETag and If-None-Match headers.
|
||||
if ($response->getHeaderLine('ETag') === '1') {
|
||||
$etag = md5($body);
|
||||
$response = $response->withHeader('ETag', $etag);
|
||||
$response = $response->withHeader('ETag', '"' . $etag . '"');
|
||||
|
||||
if ($request->getHeaderLine('If-None-Match') === $etag) {
|
||||
$search = trim($this['request']->getHeaderLine('If-None-Match'), '"');
|
||||
if ($noCache === false && $search === $etag) {
|
||||
$response = $response->withStatus(304);
|
||||
$body = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Echo page content.
|
||||
$this->header($response);
|
||||
echo $body;
|
||||
exit();
|
||||
@@ -507,7 +551,7 @@ class Grav extends Container
|
||||
public function shutdown()
|
||||
{
|
||||
// Prevent user abort allowing onShutdown event to run without interruptions.
|
||||
if (\function_exists('ignore_user_abort')) {
|
||||
if (function_exists('ignore_user_abort')) {
|
||||
@ignore_user_abort(true);
|
||||
}
|
||||
|
||||
@@ -523,7 +567,7 @@ class Grav extends Container
|
||||
// the connection to the client open. This will make page loads to feel much faster.
|
||||
|
||||
// FastCGI allows us to flush all response data to the client and finish the request.
|
||||
$success = \function_exists('fastcgi_finish_request') ? @fastcgi_finish_request() : false;
|
||||
$success = function_exists('fastcgi_finish_request') ? @fastcgi_finish_request() : false;
|
||||
if (!$success) {
|
||||
// Unfortunately without FastCGI there is no way to force close the connection.
|
||||
// We need to ask browser to close the connection for us.
|
||||
|
||||
@@ -139,7 +139,7 @@ class LogViewer
|
||||
}
|
||||
|
||||
return array(
|
||||
'date' => \DateTime::createFromFormat('Y-m-d H:i:s', $data['date']),
|
||||
'date' => DateTime::createFromFormat('Y-m-d H:i:s', $data['date']),
|
||||
'logger' => $data['logger'],
|
||||
'level' => $data['level'],
|
||||
'message' => $data['message'],
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace Grav\Common\Helpers;
|
||||
|
||||
use Exception;
|
||||
use Grav\Common\Grav;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
@@ -93,7 +94,7 @@ class YamlLinter
|
||||
foreach ($iterator as $filepath => $file) {
|
||||
try {
|
||||
Yaml::parse(static::extractYaml($filepath));
|
||||
} catch (\Exception $e) {
|
||||
} catch (Exception $e) {
|
||||
$lint_errors[str_replace(GRAV_ROOT, '', $filepath)] = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use DateInterval;
|
||||
use DateTime;
|
||||
use Grav\Common\Language\Language;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
@@ -20,15 +21,17 @@ use function strlen;
|
||||
*/
|
||||
class Inflector
|
||||
{
|
||||
/** @var array */
|
||||
/** @var bool */
|
||||
protected static $initialized = false;
|
||||
/** @var array|null */
|
||||
protected static $plural;
|
||||
/** @var array */
|
||||
/** @var array|null */
|
||||
protected static $singular;
|
||||
/** @var array */
|
||||
/** @var array|null */
|
||||
protected static $uncountable;
|
||||
/** @var array */
|
||||
/** @var array|null */
|
||||
protected static $irregular;
|
||||
/** @var array */
|
||||
/** @var array|null */
|
||||
protected static $ordinals;
|
||||
|
||||
/**
|
||||
@@ -36,14 +39,17 @@ class Inflector
|
||||
*/
|
||||
public static function init()
|
||||
{
|
||||
if (empty(static::$plural)) {
|
||||
if (!static::$initialized) {
|
||||
static::$initialized = true;
|
||||
/** @var Language $language */
|
||||
$language = Grav::instance()['language'];
|
||||
static::$plural = $language->translate('GRAV.INFLECTOR_PLURALS', null, true);
|
||||
static::$singular = $language->translate('GRAV.INFLECTOR_SINGULAR', null, true);
|
||||
static::$uncountable = $language->translate('GRAV.INFLECTOR_UNCOUNTABLE', null, true);
|
||||
static::$irregular = $language->translate('GRAV.INFLECTOR_IRREGULAR', null, true);
|
||||
static::$ordinals = $language->translate('GRAV.INFLECTOR_ORDINALS', null, true);
|
||||
if (!$language->isDebug()) {
|
||||
static::$plural = $language->translate('GRAV.INFLECTOR_PLURALS', null, true);
|
||||
static::$singular = $language->translate('GRAV.INFLECTOR_SINGULAR', null, true);
|
||||
static::$uncountable = $language->translate('GRAV.INFLECTOR_UNCOUNTABLE', null, true);
|
||||
static::$irregular = $language->translate('GRAV.INFLECTOR_IRREGULAR', null, true);
|
||||
static::$ordinals = $language->translate('GRAV.INFLECTOR_ORDINALS', null, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,21 +70,27 @@ class Inflector
|
||||
|
||||
$lowercased_word = strtolower($word);
|
||||
|
||||
foreach (static::$uncountable as $_uncountable) {
|
||||
if (substr($lowercased_word, -1 * strlen($_uncountable)) === $_uncountable) {
|
||||
return $word;
|
||||
if (is_array(static::$uncountable)) {
|
||||
foreach (static::$uncountable as $_uncountable) {
|
||||
if (substr($lowercased_word, -1 * strlen($_uncountable)) === $_uncountable) {
|
||||
return $word;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (static::$irregular as $_plural => $_singular) {
|
||||
if (preg_match('/(' . $_plural . ')$/i', $word, $arr)) {
|
||||
return preg_replace('/(' . $_plural . ')$/i', substr($arr[0], 0, 1) . substr($_singular, 1), $word);
|
||||
if (is_array(static::$irregular)) {
|
||||
foreach (static::$irregular as $_plural => $_singular) {
|
||||
if (preg_match('/(' . $_plural . ')$/i', $word, $arr)) {
|
||||
return preg_replace('/(' . $_plural . ')$/i', substr($arr[0], 0, 1) . substr($_singular, 1), $word);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (static::$plural as $rule => $replacement) {
|
||||
if (preg_match($rule, $word)) {
|
||||
return preg_replace($rule, $replacement, $word);
|
||||
if (is_array(static::$plural)) {
|
||||
foreach (static::$plural as $rule => $replacement) {
|
||||
if (preg_match($rule, $word)) {
|
||||
return preg_replace($rule, $replacement, $word);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,21 +114,28 @@ class Inflector
|
||||
}
|
||||
|
||||
$lowercased_word = strtolower($word);
|
||||
foreach (static::$uncountable as $_uncountable) {
|
||||
if (substr($lowercased_word, -1 * strlen($_uncountable)) === $_uncountable) {
|
||||
return $word;
|
||||
|
||||
if (is_array(static::$uncountable)) {
|
||||
foreach (static::$uncountable as $_uncountable) {
|
||||
if (substr($lowercased_word, -1 * strlen($_uncountable)) === $_uncountable) {
|
||||
return $word;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (static::$irregular as $_plural => $_singular) {
|
||||
if (preg_match('/(' . $_singular . ')$/i', $word, $arr)) {
|
||||
return preg_replace('/(' . $_singular . ')$/i', substr($arr[0], 0, 1) . substr($_plural, 1), $word);
|
||||
if (is_array(static::$irregular)) {
|
||||
foreach (static::$irregular as $_plural => $_singular) {
|
||||
if (preg_match('/(' . $_singular . ')$/i', $word, $arr)) {
|
||||
return preg_replace('/(' . $_singular . ')$/i', substr($arr[0], 0, 1) . substr($_plural, 1), $word);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (static::$singular as $rule => $replacement) {
|
||||
if (preg_match($rule, $word)) {
|
||||
return preg_replace($rule, $replacement, $word);
|
||||
if (is_array(static::$singular)) {
|
||||
foreach (static::$singular as $rule => $replacement) {
|
||||
if (preg_match($rule, $word)) {
|
||||
return preg_replace($rule, $replacement, $word);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,6 +310,10 @@ class Inflector
|
||||
*/
|
||||
public static function ordinalize($number)
|
||||
{
|
||||
if (!is_array(static::$ordinals)) {
|
||||
return (string)$number;
|
||||
}
|
||||
|
||||
static::init();
|
||||
|
||||
if (in_array($number % 100, range(11, 13), true)) {
|
||||
|
||||
@@ -106,6 +106,16 @@ class Language
|
||||
return $this->enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if language debugging is turned on.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDebug(): bool
|
||||
{
|
||||
return !$this->config->get('system.languages.translations', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the array of supported languages
|
||||
*
|
||||
@@ -488,7 +498,7 @@ class Language
|
||||
$args = [];
|
||||
}
|
||||
|
||||
if ($this->config->get('system.languages.translations', true)) {
|
||||
if (!$this->isDebug()) {
|
||||
if ($lookup && $this->enabled() && empty($languages)) {
|
||||
$languages = $this->getTranslatedLanguages();
|
||||
}
|
||||
@@ -507,7 +517,7 @@ class Language
|
||||
}
|
||||
}
|
||||
} elseif ($array_support) {
|
||||
return [];
|
||||
return [$lookup];
|
||||
}
|
||||
|
||||
if ($html_out) {
|
||||
@@ -528,18 +538,20 @@ class Language
|
||||
*/
|
||||
public function translateArray($key, $index, $languages = null, $html_out = false)
|
||||
{
|
||||
if ($this->config->get('system.languages.translations', true)) {
|
||||
if ($this->enabled() && $key && empty($languages)) {
|
||||
$languages = $this->getTranslatedLanguages();
|
||||
}
|
||||
if ($this->isDebug()) {
|
||||
return $key . '[' . $index . ']';
|
||||
}
|
||||
|
||||
$languages = $languages ?: ['en'];
|
||||
if ($key && empty($languages) && $this->enabled()) {
|
||||
$languages = $this->getTranslatedLanguages();
|
||||
}
|
||||
|
||||
foreach ((array)$languages as $lang) {
|
||||
$translation_array = (array)Grav::instance()['languages']->get($lang . '.' . $key, null);
|
||||
if ($translation_array && array_key_exists($index, $translation_array)) {
|
||||
return $translation_array[$index];
|
||||
}
|
||||
$languages = $languages ?: ['en'];
|
||||
|
||||
foreach ((array)$languages as $lang) {
|
||||
$translation_array = (array)Grav::instance()['languages']->get($lang . '.' . $key, null);
|
||||
if ($translation_array && array_key_exists($index, $translation_array)) {
|
||||
return $translation_array[$index];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,6 +572,10 @@ class Language
|
||||
*/
|
||||
public function getTranslation($lang, $key, $array_support = false)
|
||||
{
|
||||
if ($this->isDebug()) {
|
||||
return $key;
|
||||
}
|
||||
|
||||
$translation = Grav::instance()['languages']->get($lang . '.' . $key, null);
|
||||
if (!$array_support && is_array($translation)) {
|
||||
return (string)array_shift($translation);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace Grav\Common\Media\Interfaces;
|
||||
|
||||
use ArrayAccess;
|
||||
use Grav\Common\Data\Data;
|
||||
|
||||
/**
|
||||
@@ -16,7 +17,7 @@ use Grav\Common\Data\Data;
|
||||
*
|
||||
* @property string $type
|
||||
*/
|
||||
interface MediaObjectInterface extends \Grav\Framework\Media\Interfaces\MediaObjectInterface, \ArrayAccess
|
||||
interface MediaObjectInterface extends \Grav\Framework\Media\Interfaces\MediaObjectInterface, ArrayAccess
|
||||
{
|
||||
/**
|
||||
* Create a copy of this media object
|
||||
|
||||
@@ -46,7 +46,7 @@ trait ImageMediaTrait
|
||||
'resize', 'forceResize', 'cropResize', 'crop', 'zoomCrop',
|
||||
'negate', 'brightness', 'contrast', 'grayscale', 'emboss',
|
||||
'smooth', 'sharp', 'edge', 'colorize', 'sepia', 'enableProgressive',
|
||||
'rotate', 'flip', 'fixOrientation', 'gaussianBlur'
|
||||
'rotate', 'flip', 'fixOrientation', 'gaussianBlur', 'format'
|
||||
];
|
||||
|
||||
/** @var array */
|
||||
|
||||
@@ -455,6 +455,48 @@ class Collection extends Iterator implements PageCollectionInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only translated pages
|
||||
*
|
||||
* @return Collection The collection with only published pages
|
||||
* @internal
|
||||
*/
|
||||
public function translated()
|
||||
{
|
||||
$published = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page !== null && $page->translated()) {
|
||||
$published[$path] = $slug;
|
||||
}
|
||||
}
|
||||
$this->items = $published;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only untranslated pages
|
||||
*
|
||||
* @return Collection The collection with only non-published pages
|
||||
* @internal
|
||||
*/
|
||||
public function nonTranslated()
|
||||
{
|
||||
$published = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page !== null && !$page->translated()) {
|
||||
$published[$path] = $slug;
|
||||
}
|
||||
}
|
||||
$this->items = $published;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only published pages
|
||||
*
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Grav\Common\Page\Interfaces;
|
||||
use ArrayAccess;
|
||||
use Countable;
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use Serializable;
|
||||
use Traversable;
|
||||
|
||||
@@ -91,7 +92,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S
|
||||
*
|
||||
* @param PageInterface|string|null $key
|
||||
* @return PageCollectionInterface
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
//public function remove($key = null);
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ class ImageFile extends Image
|
||||
}
|
||||
|
||||
try {
|
||||
$exif = exif_read_data($filepath);
|
||||
$exif = @exif_read_data($filepath);
|
||||
} catch (Exception $e) {
|
||||
Grav::instance()['log']->error($filepath . ' - ' . $e->getMessage());
|
||||
return $this;
|
||||
|
||||
@@ -27,9 +27,11 @@ use Grav\Common\Page\Traits\PageFormTrait;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Common\Yaml;
|
||||
use Grav\Framework\Flex\Flex;
|
||||
use InvalidArgumentException;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\File\MarkdownFile;
|
||||
use RuntimeException;
|
||||
use SplFileInfo;
|
||||
use function dirname;
|
||||
use function in_array;
|
||||
@@ -51,6 +53,8 @@ class Page implements PageInterface
|
||||
|
||||
/** @var string|null Filename. Leave as null if page is folder. */
|
||||
protected $name;
|
||||
/** @var bool */
|
||||
protected $initialized = false;
|
||||
/** @var string */
|
||||
protected $folder;
|
||||
/** @var string */
|
||||
@@ -181,6 +185,8 @@ class Page implements PageInterface
|
||||
{
|
||||
$config = Grav::instance()['config'];
|
||||
|
||||
$this->initialized = true;
|
||||
|
||||
// some extension logic
|
||||
if (empty($extension)) {
|
||||
$this->extension('.' . $file->getExtension());
|
||||
@@ -730,13 +736,13 @@ class Page implements PageInterface
|
||||
);
|
||||
$twig_first = $this->header->twig_first ?? $config->get(
|
||||
'system.pages.twig_first',
|
||||
true
|
||||
false
|
||||
);
|
||||
|
||||
// never cache twig means it's always run after content
|
||||
$never_cache_twig = $this->header->never_cache_twig ?? $config->get(
|
||||
'system.pages.never_cache_twig',
|
||||
false
|
||||
true
|
||||
);
|
||||
|
||||
// if no cached-content run everything
|
||||
@@ -1046,6 +1052,15 @@ class Page implements PageInterface
|
||||
return $this->raw_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @internal
|
||||
*/
|
||||
public function translated(): bool
|
||||
{
|
||||
return $this->initialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file object to the page.
|
||||
*
|
||||
@@ -1083,6 +1098,14 @@ class Page implements PageInterface
|
||||
$this->doReorder($reorder);
|
||||
}
|
||||
|
||||
// We need to signal Flex Pages about the change.
|
||||
/** @var Flex|null $flex */
|
||||
$flex = Grav::instance()['flex'] ?? null;
|
||||
$directory = $flex ? $flex->getDirectory('pages') : null;
|
||||
if (null !== $directory) {
|
||||
$directory->clearCache();
|
||||
}
|
||||
|
||||
$this->_original = null;
|
||||
}
|
||||
|
||||
@@ -1104,10 +1127,10 @@ class Page implements PageInterface
|
||||
$this->_action = 'move';
|
||||
|
||||
if ($this->route() === $parent->route()) {
|
||||
throw new \RuntimeException('Failed: Cannot set page parent to self');
|
||||
throw new RuntimeException('Failed: Cannot set page parent to self');
|
||||
}
|
||||
if (Utils::startsWith($parent->rawRoute(), $this->rawRoute())) {
|
||||
throw new \RuntimeException('Failed: Cannot set page parent to a child of current page');
|
||||
throw new RuntimeException('Failed: Cannot set page parent to a child of current page');
|
||||
}
|
||||
|
||||
$this->parent($parent);
|
||||
@@ -2587,9 +2610,7 @@ class Page implements PageInterface
|
||||
throw new InvalidArgumentException('Argument should be either header variable name or array of parameters');
|
||||
}
|
||||
|
||||
if (!$pagination) {
|
||||
$params['pagination'] = false;
|
||||
}
|
||||
$params['filter'] = ($params['filter'] ?? []) + ['translated' => true];
|
||||
$context = [
|
||||
'pagination' => $pagination,
|
||||
'self' => $this
|
||||
|
||||
@@ -34,6 +34,7 @@ use Grav\Plugin\Admin;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use RuntimeException;
|
||||
use SplFileInfo;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Whoops\Exception\ErrorException;
|
||||
use Collator;
|
||||
@@ -421,10 +422,10 @@ class Pages
|
||||
}
|
||||
|
||||
$pagination = $params['pagination'] ?? $context['pagination'];
|
||||
if ($pagination && !isset($params['page'])) {
|
||||
if ($pagination && !isset($params['page'], $params['start'])) {
|
||||
/** @var Uri $uri */
|
||||
$uri = $this->grav['uri'];
|
||||
$context['pagination_page'] = $uri->currentPage();
|
||||
$context['current_page'] = $uri->currentPage();
|
||||
}
|
||||
|
||||
$collection = $this->evaluate($params['items'], $context['self']);
|
||||
@@ -464,6 +465,13 @@ class Pages
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'translated':
|
||||
if ($filter) {
|
||||
$collection = $collection->translated();
|
||||
} else {
|
||||
$collection = $collection->nonTranslated();
|
||||
}
|
||||
break;
|
||||
case 'published':
|
||||
if ($filter) {
|
||||
$collection = $collection->published();
|
||||
@@ -537,18 +545,20 @@ class Pages
|
||||
|
||||
// New Custom event to handle things like pagination.
|
||||
if ($context['event']) {
|
||||
$this->grav->fireEvent('onCollectionProcessed', new Event(['collection' => $collection]));
|
||||
$this->grav->fireEvent('onCollectionProcessed', new Event(['collection' => $collection, 'context' => $context]));
|
||||
}
|
||||
|
||||
// Slice and dice the collection if pagination is required
|
||||
if ($pagination) {
|
||||
if ($context['pagination']) {
|
||||
// Slice and dice the collection if pagination is required
|
||||
$params = $collection->params();
|
||||
|
||||
$limit = (int)($params['limit'] ?? 0);
|
||||
$start = !empty($params['pagination']) ? (($params['page'] ?? $context['pagination_page']) - 1) * $limit : 0;
|
||||
$page = (int)($params['page'] ?? $context['current_page'] ?? 0);
|
||||
$start = (int)($params['start'] ?? 0);
|
||||
$start = $limit > 0 && $page > 0 ? ($page - 1) * $limit : max(0, $start);
|
||||
|
||||
if ($limit && $collection->count() > $limit) {
|
||||
$collection->slice($start, $limit);
|
||||
if ($start || ($limit && $collection->count() > $limit)) {
|
||||
$collection->slice($start, $limit ?: null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1344,7 +1354,7 @@ class Pages
|
||||
|
||||
/** @var PageInterface $page */
|
||||
$page = $event['page'];
|
||||
$page->init(new \SplFileInfo('plugin://admin/pages/admin/error.md'));
|
||||
$page->init(new SplFileInfo('plugin://admin/pages/admin/error.md'));
|
||||
$page->routable(true);
|
||||
$header = $page->header();
|
||||
$header->title = 'Please install missing plugin';
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace Grav\Common;
|
||||
|
||||
use ArrayAccess;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Page\Interfaces\PageInterface;
|
||||
@@ -25,7 +26,7 @@ use function is_string;
|
||||
* Class Plugin
|
||||
* @package Grav\Common
|
||||
*/
|
||||
class Plugin implements EventSubscriberInterface, \ArrayAccess
|
||||
class Plugin implements EventSubscriberInterface, ArrayAccess
|
||||
{
|
||||
/** @var string */
|
||||
public $name;
|
||||
@@ -288,6 +289,8 @@ class Plugin implements EventSubscriberInterface, \ArrayAccess
|
||||
/**
|
||||
* Merge global and page configurations.
|
||||
*
|
||||
* WARNING: This method modifies page header!
|
||||
*
|
||||
* @param PageInterface $page The page to merge the configurations with the
|
||||
* plugin settings.
|
||||
* @param mixed $deep false = shallow|true = recursive|merge = recursive+unique
|
||||
|
||||
@@ -30,6 +30,7 @@ class Plugins extends Iterator
|
||||
/** @var array */
|
||||
public $formFieldTypes;
|
||||
|
||||
/** @var bool */
|
||||
private $plugins_initialized = false;
|
||||
|
||||
/**
|
||||
@@ -172,7 +173,7 @@ class Plugins extends Iterator
|
||||
/**
|
||||
* Return list of all plugin data with their blueprints.
|
||||
*
|
||||
* @return array
|
||||
* @return array<string,Data>
|
||||
*/
|
||||
public static function all()
|
||||
{
|
||||
|
||||
@@ -193,6 +193,39 @@ class InitializeProcessor extends ProcessorBase
|
||||
}
|
||||
}
|
||||
|
||||
// Override configuration using the environment.
|
||||
$prefix = 'GRAV_CONFIG';
|
||||
$env = getenv($prefix);
|
||||
if ($env) {
|
||||
$cPrefix = $prefix . '__';
|
||||
$aPrefix = $prefix . '_ALIAS__';
|
||||
$cLen = strlen($cPrefix);
|
||||
$aLen = strlen($aPrefix);
|
||||
|
||||
$keys = $aliases = [];
|
||||
$env = $_ENV + $_SERVER;
|
||||
foreach ($env as $key => $value) {
|
||||
if (!str_starts_with($key, $prefix)) {
|
||||
continue;
|
||||
}
|
||||
if (str_starts_with($key, $cPrefix)) {
|
||||
$key = str_replace('__', '.', substr($key, $cLen));
|
||||
$keys[$key] = $value;
|
||||
} elseif (str_starts_with($key, $aPrefix)) {
|
||||
$key = substr($key, $aLen);
|
||||
$aliases[$key] = $value;
|
||||
}
|
||||
}
|
||||
$list = [];
|
||||
foreach ($keys as $key => $value) {
|
||||
foreach ($aliases as $alias => $real) {
|
||||
$key = str_replace($alias, $real, $key);
|
||||
}
|
||||
$list[$key] = $value;
|
||||
$config->set($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
$this->stopTimer('_init_config');
|
||||
|
||||
return $config;
|
||||
|
||||
@@ -14,6 +14,7 @@ use RocketTheme\Toolbox\Event\Event;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Class PagesProcessor
|
||||
@@ -55,7 +56,7 @@ class PagesProcessor extends ProcessorBase
|
||||
unset($this->container['page']);
|
||||
$this->container['page'] = $page = $event->page;
|
||||
} else {
|
||||
throw new \RuntimeException('Page Not Found', 404);
|
||||
throw new RuntimeException('Page Not Found', 404);
|
||||
}
|
||||
|
||||
$this->addMessage("Routed to page {$page->rawRoute()} (type: {$page->template()}) [Not Found fallback]");
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace Grav\Common\Scheduler;
|
||||
* // bool(true)
|
||||
*/
|
||||
|
||||
use DateInterval;
|
||||
use DateTime;
|
||||
use RuntimeException;
|
||||
use function count;
|
||||
@@ -473,9 +474,9 @@ class Cron
|
||||
}
|
||||
|
||||
$date = $this->parseDate($date, $min, $hour, $day, $month, $weekday);
|
||||
$interval = new \DateInterval('PT1M'); // 1 min
|
||||
$interval = new DateInterval('PT1M'); // 1 min
|
||||
if ($minuteBefore !== 0) {
|
||||
$date->sub(new \DateInterval('PT' . abs($minuteBefore) . 'M'));
|
||||
$date->sub(new DateInterval('PT' . abs($minuteBefore) . 'M'));
|
||||
}
|
||||
$n = $minuteAfter - $minuteBefore + 1;
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace Grav\Common\Scheduler;
|
||||
|
||||
use Closure;
|
||||
use Cron\CronExpression;
|
||||
use DateTime;
|
||||
use Grav\Common\Grav;
|
||||
@@ -109,7 +110,7 @@ class Job
|
||||
/**
|
||||
* Get the command
|
||||
*
|
||||
* @return \Closure|string
|
||||
* @return Closure|string
|
||||
*/
|
||||
public function getCommand()
|
||||
{
|
||||
@@ -336,7 +337,7 @@ class Job
|
||||
if (is_callable($this->command)) {
|
||||
$this->output = $this->exec();
|
||||
} else {
|
||||
$args = \is_string($this->args) ? explode(' ', $this->args) : $this->args;
|
||||
$args = is_string($this->args) ? explode(' ', $this->args) : $this->args;
|
||||
$command = array_merge([$this->command], $args);
|
||||
$process = new Process($command);
|
||||
|
||||
@@ -446,6 +447,7 @@ class Job
|
||||
$return_data = call_user_func_array($this->command, $this->args);
|
||||
$this->successful = true;
|
||||
} catch (RuntimeException $e) {
|
||||
$return_data = $e->getMessage();
|
||||
$this->successful = false;
|
||||
}
|
||||
$this->output = ob_get_clean() . (is_string($return_data) ? $return_data : '');
|
||||
|
||||
@@ -13,6 +13,7 @@ use DateTime;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Utils;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Component\Process\PhpExecutableFinder;
|
||||
use Symfony\Component\Process\Process;
|
||||
use RocketTheme\Toolbox\File\YamlFile;
|
||||
@@ -184,9 +185,10 @@ class Scheduler
|
||||
/**
|
||||
* Run the scheduler.
|
||||
*
|
||||
* @param DateTime|null $runTime Optional, run at specific moment
|
||||
* @param DateTime|null $runTime Optional, run at specific moment
|
||||
* @param bool $force force run even if not due
|
||||
*/
|
||||
public function run(DateTime $runTime = null)
|
||||
public function run(DateTime $runTime = null, $force = false)
|
||||
{
|
||||
$this->loadSavedJobs();
|
||||
|
||||
@@ -199,7 +201,7 @@ class Scheduler
|
||||
|
||||
// Star processing jobs
|
||||
foreach ($alljobs as $job) {
|
||||
if ($job->isDue($runTime)) {
|
||||
if ($job->isDue($runTime) || $force) {
|
||||
$job->run();
|
||||
$this->jobs_run[] = $job;
|
||||
}
|
||||
@@ -247,7 +249,7 @@ class Scheduler
|
||||
case 'array':
|
||||
return $this->output_schedule;
|
||||
default:
|
||||
throw new \InvalidArgumentException('Invalid output type');
|
||||
throw new InvalidArgumentException('Invalid output type');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace Grav\Common\Service;
|
||||
|
||||
use DirectoryIterator;
|
||||
use Grav\Common\Config\CompiledBlueprints;
|
||||
use Grav\Common\Config\CompiledConfig;
|
||||
use Grav\Common\Config\CompiledLanguages;
|
||||
@@ -169,9 +170,9 @@ class ConfigServiceProvider implements ServiceProviderInterface
|
||||
$paths = [];
|
||||
|
||||
foreach ($plugins as $path) {
|
||||
$iterator = new \DirectoryIterator($path);
|
||||
$iterator = new DirectoryIterator($path);
|
||||
|
||||
/** @var \DirectoryIterator $directory */
|
||||
/** @var DirectoryIterator $directory */
|
||||
foreach ($iterator as $directory) {
|
||||
if (!$directory->isDir() || $directory->isDot()) {
|
||||
continue;
|
||||
|
||||
@@ -70,7 +70,8 @@ class PagesServiceProvider implements ServiceProviderInterface
|
||||
}
|
||||
|
||||
if ($config->get('system.force_ssl')) {
|
||||
if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') {
|
||||
$scheme = $uri->scheme(true);
|
||||
if ($scheme !== 'https') {
|
||||
$url = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
$grav->redirect($url);
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ use Grav\Common\Debugger;
|
||||
use Grav\Common\Session;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Framework\Session\Messages;
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use RocketTheme\Toolbox\Session\Message;
|
||||
|
||||
/**
|
||||
* Class SessionServiceProvider
|
||||
@@ -113,14 +113,14 @@ class SessionServiceProvider implements ServiceProviderInterface
|
||||
$debugger = $c['debugger'];
|
||||
$debugger->addMessage('Inactive session: session messages may disappear', 'warming');
|
||||
|
||||
return new Message;
|
||||
return new Messages();
|
||||
}
|
||||
|
||||
/** @var Session $session */
|
||||
$session = $c['session'];
|
||||
|
||||
if (!isset($session->messages)) {
|
||||
$session->messages = new Message;
|
||||
if (!$session->messages instanceof Messages) {
|
||||
$session->messages = new Messages();
|
||||
}
|
||||
|
||||
return $session->messages;
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Form\FormFlash;
|
||||
use Grav\Events\SessionStartEvent;
|
||||
use Grav\Plugin\Form\Forms;
|
||||
use function is_string;
|
||||
|
||||
/**
|
||||
@@ -126,7 +127,7 @@ class Session extends \Grav\Framework\Session\Session
|
||||
|
||||
/** @var Uri $uri */
|
||||
$uri = $grav['uri'];
|
||||
/** @var Grav\Plugin\Form\Forms|null $form */
|
||||
/** @var Forms|null $form */
|
||||
$form = $grav['forms']->getActiveForm();
|
||||
|
||||
$sessionField = base64_encode($uri->url);
|
||||
|
||||
@@ -130,12 +130,12 @@ class TwigExtension extends AbstractExtension implements GlobalsInterface
|
||||
new TwigFilter('pad', [$this, 'padFilter']),
|
||||
new TwigFilter('regex_replace', [$this, 'regexReplace']),
|
||||
new TwigFilter('safe_email', [$this, 'safeEmailFilter'], ['is_safe' => ['html']]),
|
||||
new TwigFilter('safe_truncate', ['\Grav\Common\Utils', 'safeTruncate']),
|
||||
new TwigFilter('safe_truncate_html', ['\Grav\Common\Utils', 'safeTruncateHTML']),
|
||||
new TwigFilter('safe_truncate', [Utils::class, 'safeTruncate']),
|
||||
new TwigFilter('safe_truncate_html', [Utils::class, 'safeTruncateHTML']),
|
||||
new TwigFilter('sort_by_key', [$this, 'sortByKeyFilter']),
|
||||
new TwigFilter('starts_with', [$this, 'startsWithFilter']),
|
||||
new TwigFilter('truncate', ['\Grav\Common\Utils', 'truncate']),
|
||||
new TwigFilter('truncate_html', ['\Grav\Common\Utils', 'truncateHTML']),
|
||||
new TwigFilter('truncate', [Utils::class, 'truncate']),
|
||||
new TwigFilter('truncate_html', [Utils::class, 'truncateHTML']),
|
||||
new TwigFilter('json_decode', [$this, 'jsonDecodeFilter']),
|
||||
new TwigFilter('array_unique', 'array_unique'),
|
||||
new TwigFilter('basename', 'basename'),
|
||||
@@ -531,7 +531,7 @@ class TwigExtension extends AbstractExtension implements GlobalsInterface
|
||||
public function nicetimeFunc($date, $long_strings = true, $show_tense = true)
|
||||
{
|
||||
if (empty($date)) {
|
||||
return $this->grav['language']->translate('GRAV.NICETIME.NO_DATE_PROVIDED', null, true);
|
||||
return $this->grav['language']->translate('GRAV.NICETIME.NO_DATE_PROVIDED');
|
||||
}
|
||||
|
||||
if ($long_strings) {
|
||||
@@ -571,19 +571,19 @@ class TwigExtension extends AbstractExtension implements GlobalsInterface
|
||||
|
||||
// check validity of date
|
||||
if (empty($unix_date)) {
|
||||
return $this->grav['language']->translate('GRAV.NICETIME.BAD_DATE', null, true);
|
||||
return $this->grav['language']->translate('GRAV.NICETIME.BAD_DATE');
|
||||
}
|
||||
|
||||
// is it future date or past date
|
||||
if ($now > $unix_date) {
|
||||
$difference = $now - $unix_date;
|
||||
$tense = $this->grav['language']->translate('GRAV.NICETIME.AGO', null, true);
|
||||
$tense = $this->grav['language']->translate('GRAV.NICETIME.AGO');
|
||||
} elseif ($now == $unix_date) {
|
||||
$difference = $now - $unix_date;
|
||||
$tense = $this->grav['language']->translate('GRAV.NICETIME.JUST_NOW', null, false);
|
||||
$tense = $this->grav['language']->translate('GRAV.NICETIME.JUST_NOW');
|
||||
} else {
|
||||
$difference = $unix_date - $now;
|
||||
$tense = $this->grav['language']->translate('GRAV.NICETIME.FROM_NOW', null, true);
|
||||
$tense = $this->grav['language']->translate('GRAV.NICETIME.FROM_NOW');
|
||||
}
|
||||
|
||||
for ($j = 0; $difference >= $lengths[$j] && $j < count($lengths) - 1; $j++) {
|
||||
@@ -606,7 +606,7 @@ class TwigExtension extends AbstractExtension implements GlobalsInterface
|
||||
}
|
||||
}
|
||||
|
||||
$periods[$j] = $this->grav['language']->translate('GRAV.'.$periods[$j], null, true);
|
||||
$periods[$j] = $this->grav['language']->translate('GRAV.'.$periods[$j]);
|
||||
|
||||
if ($now == $unix_date) {
|
||||
return $tense;
|
||||
@@ -892,6 +892,7 @@ class TwigExtension extends AbstractExtension implements GlobalsInterface
|
||||
|
||||
$loader = new FilesystemLoader('.');
|
||||
$env = new Environment($loader);
|
||||
$env->addExtension($this);
|
||||
|
||||
$template = $env->createTemplate($twig);
|
||||
|
||||
|
||||
@@ -165,7 +165,6 @@ class Uri
|
||||
|
||||
// Handle custom base
|
||||
$custom_base = rtrim($grav['config']->get('system.custom_base_url'), '/');
|
||||
|
||||
if ($custom_base) {
|
||||
$custom_parts = parse_url($custom_base);
|
||||
if ($custom_parts === false) {
|
||||
@@ -175,6 +174,10 @@ class Uri
|
||||
$this->root_path = isset($custom_parts['path']) ? rtrim($custom_parts['path'], '/') : '';
|
||||
if (isset($custom_parts['scheme'])) {
|
||||
$this->base = $custom_parts['scheme'] . '://' . $custom_parts['host'];
|
||||
$this->port = $custom_parts['port'] ?? null;
|
||||
if ($this->port !== null && $config->get('system.reverse_proxy_setup') === false) {
|
||||
$this->base .= ':' . (string)$this->port;
|
||||
}
|
||||
$this->root = $custom_base;
|
||||
} else {
|
||||
$this->root = $this->base . $this->root_path;
|
||||
|
||||
@@ -22,6 +22,7 @@ use Grav\Common\User\Authentication;
|
||||
use Grav\Common\User\Interfaces\UserInterface;
|
||||
use Grav\Common\User\Traits\UserTrait;
|
||||
use Grav\Framework\Flex\Flex;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* Class User
|
||||
@@ -141,10 +142,11 @@ class User extends Data implements UserInterface
|
||||
|
||||
$file->save($data);
|
||||
|
||||
// We need to signal Flex Users about the change.
|
||||
/** @var Flex|null $flex */
|
||||
$flex = Grav::instance()['flex'] ?? null;
|
||||
$users = $flex ? $flex->getDirectory('user-accounts') : null;
|
||||
if ($users) {
|
||||
if (null !== $users) {
|
||||
$users->clearCache();
|
||||
}
|
||||
}
|
||||
@@ -303,7 +305,7 @@ class User extends Data implements UserInterface
|
||||
protected function getAvatarFile(): ?string
|
||||
{
|
||||
$avatars = $this->get('avatar');
|
||||
if (\is_array($avatars) && $avatars) {
|
||||
if (is_array($avatars) && $avatars) {
|
||||
$avatar = array_shift($avatars);
|
||||
return $avatar['path'] ?? null;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ use Grav\Common\User\Interfaces\UserInterface;
|
||||
use Grav\Common\Utils;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use RuntimeException;
|
||||
use function count;
|
||||
use function in_array;
|
||||
use function is_string;
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ use function extension_loaded;
|
||||
use function function_exists;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function is_callable;
|
||||
use function is_string;
|
||||
use function strlen;
|
||||
|
||||
|
||||
106
system/src/Grav/Console/Application/Application.php
Normal file
106
system/src/Grav/Console/Application/Application.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Grav\Console
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Console\Application;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Input\InputDefinition;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class GpmApplication
|
||||
* @package Grav\Console\Application
|
||||
*/
|
||||
class Application extends \Symfony\Component\Console\Application
|
||||
{
|
||||
/** @var string|null */
|
||||
protected $environment;
|
||||
/** @var string|null */
|
||||
protected $language;
|
||||
/** @var bool */
|
||||
protected $initialized = false;
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCommandName(InputInterface $input): ?string
|
||||
{
|
||||
$this->environment = $input->getOption('env');
|
||||
$this->language = $input->getOption('lang') ?? $this->language;
|
||||
$this->init();
|
||||
|
||||
return parent::getCommandName($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function init(): void
|
||||
{
|
||||
if ($this->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->initialized = true;
|
||||
|
||||
$grav = Grav::instance();
|
||||
$grav->setup($this->environment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add global a --env option.
|
||||
*
|
||||
* @return InputDefinition
|
||||
*/
|
||||
protected function getDefaultInputDefinition(): InputDefinition
|
||||
{
|
||||
$inputDefinition = parent::getDefaultInputDefinition();
|
||||
$inputDefinition->addOption(
|
||||
new InputOption(
|
||||
'env',
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Use environment configuration (defaults to localhost)'
|
||||
)
|
||||
);
|
||||
$inputDefinition->addOption(
|
||||
new InputOption(
|
||||
'lang',
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Language to be used (defaults to en)'
|
||||
)
|
||||
);
|
||||
|
||||
return $inputDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return void
|
||||
*/
|
||||
protected function configureIO(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$formatter = $output->getFormatter();
|
||||
$formatter->setStyle('normal', new OutputFormatterStyle('white'));
|
||||
$formatter->setStyle('yellow', new OutputFormatterStyle('yellow', null, ['bold']));
|
||||
$formatter->setStyle('red', new OutputFormatterStyle('red', null, ['bold']));
|
||||
$formatter->setStyle('cyan', new OutputFormatterStyle('cyan', null, ['bold']));
|
||||
$formatter->setStyle('green', new OutputFormatterStyle('green', null, ['bold']));
|
||||
$formatter->setStyle('magenta', new OutputFormatterStyle('magenta', null, ['bold']));
|
||||
$formatter->setStyle('white', new OutputFormatterStyle('white', null, ['bold']));
|
||||
|
||||
parent::configureIO($input, $output);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Grav\Console
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Console\Application\CommandLoader;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Grav;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\CommandLoader\CommandLoaderInterface;
|
||||
use Symfony\Component\Console\Exception\CommandNotFoundException;
|
||||
|
||||
/**
|
||||
* Class GpmApplication
|
||||
* @package Grav\Console\Application
|
||||
*/
|
||||
class PluginCommandLoader implements CommandLoaderInterface
|
||||
{
|
||||
/** @var array */
|
||||
private $commands;
|
||||
|
||||
/**
|
||||
* PluginCommandLoader constructor.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->commands = [];
|
||||
|
||||
try {
|
||||
$path = "plugins://{$name}/cli";
|
||||
$pattern = '([A-Z]\w+Command\.php)';
|
||||
|
||||
$commands = is_dir($path) ? Folder::all($path, ['compare' => 'Filename', 'pattern' => '/' . $pattern . '$/usm', 'levels' => 1]) : [];
|
||||
} catch (RuntimeException $e) {
|
||||
throw new RuntimeException("Failed to load console commands for plugin {$name}");
|
||||
}
|
||||
|
||||
$grav = Grav::instance();
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $grav['locator'];
|
||||
foreach ($commands as $command_path) {
|
||||
$full_path = $locator->findResource("plugins://{$name}/cli/{$command_path}");
|
||||
require_once $full_path;
|
||||
|
||||
$command_class = 'Grav\Plugin\Console\\' . preg_replace('/.php$/', '', $command_path);
|
||||
if (class_exists($command_class)) {
|
||||
$command = new $command_class();
|
||||
if ($command instanceof Command) {
|
||||
$this->commands[$command->getName()] = $command;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return Command
|
||||
*/
|
||||
public function get($name): Command
|
||||
{
|
||||
$command = $this->commands[$name] ?? null;
|
||||
if (null === $command) {
|
||||
throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
|
||||
}
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name): bool
|
||||
{
|
||||
return isset($this->commands[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNames(): array
|
||||
{
|
||||
return array_keys($this->commands);
|
||||
}
|
||||
}
|
||||
42
system/src/Grav/Console/Application/GpmApplication.php
Normal file
42
system/src/Grav/Console/Application/GpmApplication.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Grav\Console
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Console\Application;
|
||||
|
||||
use Grav\Console\Gpm\DirectInstallCommand;
|
||||
use Grav\Console\Gpm\IndexCommand;
|
||||
use Grav\Console\Gpm\InfoCommand;
|
||||
use Grav\Console\Gpm\InstallCommand;
|
||||
use Grav\Console\Gpm\SelfupgradeCommand;
|
||||
use Grav\Console\Gpm\UninstallCommand;
|
||||
use Grav\Console\Gpm\UpdateCommand;
|
||||
use Grav\Console\Gpm\VersionCommand;
|
||||
|
||||
/**
|
||||
* Class GpmApplication
|
||||
* @package Grav\Console\Application
|
||||
*/
|
||||
class GpmApplication extends Application
|
||||
{
|
||||
public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN')
|
||||
{
|
||||
parent::__construct($name, $version);
|
||||
|
||||
$this->addCommands([
|
||||
new IndexCommand(),
|
||||
new VersionCommand(),
|
||||
new InfoCommand(),
|
||||
new InstallCommand(),
|
||||
new UninstallCommand(),
|
||||
new UpdateCommand(),
|
||||
new SelfupgradeCommand(),
|
||||
new DirectInstallCommand(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
52
system/src/Grav/Console/Application/GravApplication.php
Normal file
52
system/src/Grav/Console/Application/GravApplication.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Grav\Console
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Console\Application;
|
||||
|
||||
use Grav\Console\Cli\BackupCommand;
|
||||
use Grav\Console\Cli\CleanCommand;
|
||||
use Grav\Console\Cli\ClearCacheCommand;
|
||||
use Grav\Console\Cli\ComposerCommand;
|
||||
use Grav\Console\Cli\InstallCommand;
|
||||
use Grav\Console\Cli\LogViewerCommand;
|
||||
use Grav\Console\Cli\NewProjectCommand;
|
||||
use Grav\Console\Cli\PageSystemValidatorCommand;
|
||||
use Grav\Console\Cli\SandboxCommand;
|
||||
use Grav\Console\Cli\SchedulerCommand;
|
||||
use Grav\Console\Cli\SecurityCommand;
|
||||
use Grav\Console\Cli\ServerCommand;
|
||||
use Grav\Console\Cli\YamlLinterCommand;
|
||||
|
||||
/**
|
||||
* Class GravApplication
|
||||
* @package Grav\Console\Application
|
||||
*/
|
||||
class GravApplication extends Application
|
||||
{
|
||||
public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN')
|
||||
{
|
||||
parent::__construct($name, $version);
|
||||
|
||||
$this->addCommands([
|
||||
new InstallCommand(),
|
||||
new ComposerCommand(),
|
||||
new SandboxCommand(),
|
||||
new CleanCommand(),
|
||||
new ClearCacheCommand(),
|
||||
new BackupCommand(),
|
||||
new NewProjectCommand(),
|
||||
new SchedulerCommand(),
|
||||
new SecurityCommand(),
|
||||
new LogViewerCommand(),
|
||||
new YamlLinterCommand(),
|
||||
new ServerCommand(),
|
||||
new PageSystemValidatorCommand(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
116
system/src/Grav/Console/Application/PluginApplication.php
Normal file
116
system/src/Grav/Console/Application/PluginApplication.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Grav\Console
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Console\Application;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Plugins;
|
||||
use Grav\Console\Application\CommandLoader\PluginCommandLoader;
|
||||
use Grav\Console\Plugin\PluginListCommand;
|
||||
use Symfony\Component\Console\Exception\NamespaceNotFoundException;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class PluginApplication
|
||||
* @package Grav\Console\Application
|
||||
*/
|
||||
class PluginApplication extends Application
|
||||
{
|
||||
/** @var string|null */
|
||||
protected $pluginName;
|
||||
|
||||
/**
|
||||
* PluginApplication constructor.
|
||||
* @param string $name
|
||||
* @param string $version
|
||||
*/
|
||||
public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN')
|
||||
{
|
||||
parent::__construct($name, $version);
|
||||
|
||||
$this->addCommands([
|
||||
new PluginListCommand(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $pluginName
|
||||
* @return void
|
||||
*/
|
||||
public function setPluginName(string $pluginName): void
|
||||
{
|
||||
$this->pluginName = $pluginName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginName(): string
|
||||
{
|
||||
return $this->pluginName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface|null $input
|
||||
* @param OutputInterface|null $output
|
||||
* @return int
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function run(InputInterface $input = null, OutputInterface $output = null): int
|
||||
{
|
||||
if (null === $input) {
|
||||
$argv = $_SERVER['argv'] ?? [];
|
||||
|
||||
$bin = array_shift($argv);
|
||||
$this->pluginName = array_shift($argv);
|
||||
$argv = array_merge([$bin], $argv);
|
||||
|
||||
$input = new ArgvInput($argv);
|
||||
}
|
||||
|
||||
return parent::run($input, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function init(): void
|
||||
{
|
||||
if ($this->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
parent::init();
|
||||
|
||||
if (null === $this->pluginName) {
|
||||
$this->setDefaultCommand('plugins:list');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$grav = Grav::instance();
|
||||
$grav->initializeCli();
|
||||
|
||||
/** @var Plugins $plugins */
|
||||
$plugins = $grav['plugins'];
|
||||
|
||||
$plugin = $this->pluginName ? $plugins::get($this->pluginName) : null;
|
||||
if (null === $plugin) {
|
||||
throw new NamespaceNotFoundException("Plugin \"{$this->pluginName}\" is not installed.");
|
||||
}
|
||||
if (!$plugin->enabled) {
|
||||
throw new NamespaceNotFoundException("Plugin \"{$this->pluginName}\" is not enabled.");
|
||||
}
|
||||
|
||||
$this->setCommandLoader(new PluginCommandLoader($this->pluginName));
|
||||
}
|
||||
}
|
||||
@@ -11,22 +11,21 @@ namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Common\Backup\Backups;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Console\GravCommand;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use ZipArchive;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Class BackupCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class BackupCommand extends ConsoleCommand
|
||||
class BackupCommand extends GravCommand
|
||||
{
|
||||
/** @var string $source */
|
||||
protected $source;
|
||||
|
||||
/** @var ProgressBar $progress */
|
||||
protected $progress;
|
||||
|
||||
@@ -55,40 +54,43 @@ class BackupCommand extends ConsoleCommand
|
||||
{
|
||||
$this->initializeGrav();
|
||||
|
||||
$this->progress = new ProgressBar($this->output);
|
||||
$this->progress->setFormat('Archiving <cyan>%current%</cyan> files [<green>%bar%</green>] <white>%percent:3s%%</white> %elapsed:6s% <yellow>%message%</yellow>');
|
||||
$this->progress->setBarWidth(100);
|
||||
$input = $this->getInput();
|
||||
$io = $this->getIO();
|
||||
|
||||
$io = new SymfonyStyle($this->input, $this->output);
|
||||
$io->title('Grav Backup');
|
||||
|
||||
if (!class_exists(\ZipArchive::class)) {
|
||||
if (!class_exists(ZipArchive::class)) {
|
||||
$io->error('php-zip extension needs to be enabled!');
|
||||
return 1;
|
||||
}
|
||||
|
||||
ProgressBar::setFormatDefinition('zip', 'Archiving <cyan>%current%</cyan> files [<green>%bar%</green>] <white>%percent:3s%%</white> %elapsed:6s% <yellow>%message%</yellow>');
|
||||
|
||||
$this->progress = $io->createProgressBar();
|
||||
$this->progress->setFormat('zip');
|
||||
$this->progress->setBarWidth(100);
|
||||
|
||||
/** @var Backups $backups */
|
||||
$backups = Grav::instance()['backups'];
|
||||
$backups_list = $backups->getBackupProfiles();
|
||||
$backups_list = $backups::getBackupProfiles();
|
||||
$backups_names = $backups->getBackupNames();
|
||||
|
||||
$id = null;
|
||||
|
||||
$inline_id = $this->input->getArgument('id');
|
||||
$inline_id = $input->getArgument('id');
|
||||
if (null !== $inline_id && is_numeric($inline_id)) {
|
||||
$id = $inline_id;
|
||||
}
|
||||
|
||||
if (null === $id) {
|
||||
if (count($backups_list) > 1) {
|
||||
$helper = $this->getHelper('question');
|
||||
$question = new ChoiceQuestion(
|
||||
'Choose a backup?',
|
||||
$backups_names,
|
||||
0
|
||||
);
|
||||
$question->setErrorMessage('Option %s is invalid.');
|
||||
$backup_name = $helper->ask($this->input, $this->output, $question);
|
||||
$backup_name = $io->askQuestion($question);
|
||||
$id = array_search($backup_name, $backups_names, true);
|
||||
|
||||
$io->newLine();
|
||||
@@ -98,7 +100,7 @@ class BackupCommand extends ConsoleCommand
|
||||
}
|
||||
}
|
||||
|
||||
$backup = $backups->backup($id, [$this, 'outputProgress']);
|
||||
$backup = $backups::backup($id, function($args) { $this->outputProgress($args); });
|
||||
|
||||
$io->newline(2);
|
||||
$io->success('Backup Successfully Created: ' . $backup);
|
||||
@@ -110,7 +112,7 @@ class BackupCommand extends ConsoleCommand
|
||||
* @param array $args
|
||||
* @return void
|
||||
*/
|
||||
public function outputProgress($args): void
|
||||
public function outputProgress(array $args): void
|
||||
{
|
||||
switch ($args['type']) {
|
||||
case 'count':
|
||||
|
||||
@@ -14,6 +14,7 @@ use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* Class CleanCommand
|
||||
@@ -21,11 +22,10 @@ use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
*/
|
||||
class CleanCommand extends Command
|
||||
{
|
||||
/* @var InputInterface $output */
|
||||
/** @var InputInterface */
|
||||
protected $input;
|
||||
|
||||
/* @var OutputInterface $output */
|
||||
protected $output;
|
||||
/** @var SymfonyStyle */
|
||||
protected $io;
|
||||
|
||||
/** @var array */
|
||||
protected $paths_to_remove = [
|
||||
@@ -284,33 +284,40 @@ class CleanCommand extends Command
|
||||
{
|
||||
$this->setupConsole($input, $output);
|
||||
|
||||
$this->cleanPaths();
|
||||
|
||||
return 0;
|
||||
return $this->cleanPaths() ? 0 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @return bool
|
||||
*/
|
||||
private function cleanPaths(): void
|
||||
private function cleanPaths(): bool
|
||||
{
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('<red>DELETING</red>');
|
||||
$success = true;
|
||||
|
||||
$this->io->writeln('');
|
||||
$this->io->writeln('<red>DELETING</red>');
|
||||
$anything = false;
|
||||
foreach ($this->paths_to_remove as $path) {
|
||||
$path = ROOT_DIR . $path;
|
||||
if (is_dir($path) && @Folder::delete($path)) {
|
||||
$anything = true;
|
||||
$this->output->writeln('<red>dir: </red>' . $path);
|
||||
} elseif (is_file($path) && @unlink($path)) {
|
||||
$anything = true;
|
||||
$this->output->writeln('<red>file: </red>' . $path);
|
||||
$path = GRAV_ROOT . $path;
|
||||
try {
|
||||
if (is_dir($path) && Folder::delete($path)) {
|
||||
$anything = true;
|
||||
$this->io->writeln('<red>dir: </red>' . $path);
|
||||
} elseif (is_file($path) && @unlink($path)) {
|
||||
$anything = true;
|
||||
$this->io->writeln('<red>file: </red>' . $path);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$success = false;
|
||||
$this->io->error(sprintf('Failed to delete %s: %s', $path, $e->getMessage()));
|
||||
}
|
||||
}
|
||||
if (!$anything) {
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('<green>Nothing to clean...</green>');
|
||||
$this->io->writeln('');
|
||||
$this->io->writeln('<green>Nothing to clean...</green>');
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,14 +330,14 @@ class CleanCommand extends Command
|
||||
public function setupConsole(InputInterface $input, OutputInterface $output): void
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
$this->io = new SymfonyStyle($input, $output);
|
||||
|
||||
$this->output->getFormatter()->setStyle('normal', new OutputFormatterStyle('white'));
|
||||
$this->output->getFormatter()->setStyle('yellow', new OutputFormatterStyle('yellow', null, ['bold']));
|
||||
$this->output->getFormatter()->setStyle('red', new OutputFormatterStyle('red', null, ['bold']));
|
||||
$this->output->getFormatter()->setStyle('cyan', new OutputFormatterStyle('cyan', null, ['bold']));
|
||||
$this->output->getFormatter()->setStyle('green', new OutputFormatterStyle('green', null, ['bold']));
|
||||
$this->output->getFormatter()->setStyle('magenta', new OutputFormatterStyle('magenta', null, ['bold']));
|
||||
$this->output->getFormatter()->setStyle('white', new OutputFormatterStyle('white', null, ['bold']));
|
||||
$this->io->getFormatter()->setStyle('normal', new OutputFormatterStyle('white'));
|
||||
$this->io->getFormatter()->setStyle('yellow', new OutputFormatterStyle('yellow', null, ['bold']));
|
||||
$this->io->getFormatter()->setStyle('red', new OutputFormatterStyle('red', null, ['bold']));
|
||||
$this->io->getFormatter()->setStyle('cyan', new OutputFormatterStyle('cyan', null, ['bold']));
|
||||
$this->io->getFormatter()->setStyle('green', new OutputFormatterStyle('green', null, ['bold']));
|
||||
$this->io->getFormatter()->setStyle('magenta', new OutputFormatterStyle('magenta', null, ['bold']));
|
||||
$this->io->getFormatter()->setStyle('white', new OutputFormatterStyle('white', null, ['bold']));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Common\Cache;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Console\GravCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Class ClearCacheCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class ClearCacheCommand extends ConsoleCommand
|
||||
class ClearCacheCommand extends GravCommand
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
@@ -57,37 +57,39 @@ class ClearCacheCommand extends ConsoleCommand
|
||||
*/
|
||||
private function cleanPaths(): void
|
||||
{
|
||||
$this->output->writeln('');
|
||||
$input = $this->getInput();
|
||||
$io = $this->getIO();
|
||||
|
||||
$io->newLine();
|
||||
|
||||
if ($this->input->getOption('purge')) {
|
||||
$this->output->writeln('<magenta>Purging old cache</magenta>');
|
||||
$this->output->writeln('');
|
||||
if ($input->getOption('purge')) {
|
||||
$io->writeln('<magenta>Purging old cache</magenta>');
|
||||
$io->newLine();
|
||||
|
||||
$msg = Cache::purgeJob();
|
||||
$this->output->writeln($msg);
|
||||
$io->writeln($msg);
|
||||
} else {
|
||||
$this->output->writeln('<magenta>Clearing cache</magenta>');
|
||||
$this->output->writeln('');
|
||||
$io->writeln('<magenta>Clearing cache</magenta>');
|
||||
$io->newLine();
|
||||
|
||||
if ($this->input->getOption('all')) {
|
||||
if ($input->getOption('all')) {
|
||||
$remove = 'all';
|
||||
} elseif ($this->input->getOption('assets-only')) {
|
||||
} elseif ($input->getOption('assets-only')) {
|
||||
$remove = 'assets-only';
|
||||
} elseif ($this->input->getOption('images-only')) {
|
||||
} elseif ($input->getOption('images-only')) {
|
||||
$remove = 'images-only';
|
||||
} elseif ($this->input->getOption('cache-only')) {
|
||||
} elseif ($input->getOption('cache-only')) {
|
||||
$remove = 'cache-only';
|
||||
} elseif ($this->input->getOption('tmp-only')) {
|
||||
} elseif ($input->getOption('tmp-only')) {
|
||||
$remove = 'tmp-only';
|
||||
} elseif ($this->input->getOption('invalidate')) {
|
||||
} elseif ($input->getOption('invalidate')) {
|
||||
$remove = 'invalidate';
|
||||
} else {
|
||||
$remove = 'standard';
|
||||
}
|
||||
|
||||
foreach (Cache::clearCache($remove) as $result) {
|
||||
$this->output->writeln($result);
|
||||
$io->writeln($result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Console\GravCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Class ComposerCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class ComposerCommand extends ConsoleCommand
|
||||
class ComposerCommand extends GravCommand
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
@@ -46,15 +46,18 @@ class ComposerCommand extends ConsoleCommand
|
||||
*/
|
||||
protected function serve(): int
|
||||
{
|
||||
$action = $this->input->getOption('install') ? 'install' : ($this->input->getOption('update') ? 'update' : 'install');
|
||||
$input = $this->getInput();
|
||||
$io = $this->getIO();
|
||||
|
||||
if ($this->input->getOption('install')) {
|
||||
$action = $input->getOption('install') ? 'install' : ($input->getOption('update') ? 'update' : 'install');
|
||||
|
||||
if ($input->getOption('install')) {
|
||||
$action = 'install';
|
||||
}
|
||||
|
||||
// Updates composer first
|
||||
$this->output->writeln("\nInstalling vendor dependencies");
|
||||
$this->output->writeln($this->composerUpdate(GRAV_ROOT, $action));
|
||||
$io->writeln("\nInstalling vendor dependencies");
|
||||
$io->writeln($this->composerUpdate(GRAV_ROOT, $action));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Console\GravCommand;
|
||||
use RocketTheme\Toolbox\File\YamlFile;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
@@ -18,14 +18,12 @@ use Symfony\Component\Console\Input\InputOption;
|
||||
* Class InstallCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class InstallCommand extends ConsoleCommand
|
||||
class InstallCommand extends GravCommand
|
||||
{
|
||||
/** @var array */
|
||||
protected $config;
|
||||
|
||||
/** @var string */
|
||||
protected $destination;
|
||||
|
||||
/** @var string */
|
||||
protected $user_path;
|
||||
|
||||
@@ -56,14 +54,17 @@ class InstallCommand extends ConsoleCommand
|
||||
*/
|
||||
protected function serve(): int
|
||||
{
|
||||
$input = $this->getInput();
|
||||
$io = $this->getIO();
|
||||
|
||||
$dependencies_file = '.dependencies';
|
||||
$this->destination = $this->input->getArgument('destination') ?: ROOT_DIR;
|
||||
$this->destination = $input->getArgument('destination') ?: ROOT_DIR;
|
||||
|
||||
// fix trailing slash
|
||||
$this->destination = rtrim($this->destination, DS) . DS;
|
||||
$this->user_path = $this->destination . USER_PATH;
|
||||
if ($local_config_file = $this->loadLocalConfig()) {
|
||||
$this->output->writeln('Read local config from <cyan>' . $local_config_file . '</cyan>');
|
||||
$io->writeln('Read local config from <cyan>' . $local_config_file . '</cyan>');
|
||||
}
|
||||
|
||||
// Look for dependencies file in ROOT and USER dir
|
||||
@@ -72,11 +73,11 @@ class InstallCommand extends ConsoleCommand
|
||||
} elseif (file_exists($this->destination . $dependencies_file)) {
|
||||
$file = YamlFile::instance($this->destination . $dependencies_file);
|
||||
} else {
|
||||
$this->output->writeln('<red>ERROR</red> Missing .dependencies file in <cyan>user/</cyan> folder');
|
||||
if ($this->input->getArgument('destination')) {
|
||||
$this->output->writeln('<yellow>HINT</yellow> <info>Are you trying to install a plugin or a theme? Make sure you use <cyan>bin/gpm install <something></cyan>, not <cyan>bin/grav install</cyan>. This command is only used to install Grav skeletons.');
|
||||
$io->writeln('<red>ERROR</red> Missing .dependencies file in <cyan>user/</cyan> folder');
|
||||
if ($input->getArgument('destination')) {
|
||||
$io->writeln('<yellow>HINT</yellow> <info>Are you trying to install a plugin or a theme? Make sure you use <cyan>bin/gpm install <something></cyan>, not <cyan>bin/grav install</cyan>. This command is only used to install Grav skeletons.');
|
||||
} else {
|
||||
$this->output->writeln('<yellow>HINT</yellow> <info>Are you trying to install Grav? Grav is already installed. You need to run this command only if you download a skeleton from GitHub directly.');
|
||||
$io->writeln('<yellow>HINT</yellow> <info>Are you trying to install Grav? Grav is already installed. You need to run this command only if you download a skeleton from GitHub directly.');
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -87,16 +88,15 @@ class InstallCommand extends ConsoleCommand
|
||||
|
||||
// If no config, fail.
|
||||
if (!$this->config) {
|
||||
$this->output->writeln('<red>ERROR</red> invalid YAML in ' . $dependencies_file);
|
||||
$io->writeln('<red>ERROR</red> invalid YAML in ' . $dependencies_file);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$error = 0;
|
||||
if (!$this->input->getOption('symlink')) {
|
||||
if (!$input->getOption('symlink')) {
|
||||
// Updates composer first
|
||||
$this->output->writeln("\nInstalling vendor dependencies");
|
||||
$this->output->writeln($this->composerUpdate(GRAV_ROOT, 'install'));
|
||||
$io->writeln("\nInstalling vendor dependencies");
|
||||
$io->writeln($this->composerUpdate(GRAV_ROOT, 'install'));
|
||||
|
||||
$error = $this->gitclone();
|
||||
} else {
|
||||
@@ -113,10 +113,12 @@ class InstallCommand extends ConsoleCommand
|
||||
*/
|
||||
private function gitclone(): int
|
||||
{
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('<green>Cloning Bits</green>');
|
||||
$this->output->writeln('============');
|
||||
$this->output->writeln('');
|
||||
$io = $this->getIO();
|
||||
|
||||
$io->newLine();
|
||||
$io->writeln('<green>Cloning Bits</green>');
|
||||
$io->writeln('============');
|
||||
$io->newLine();
|
||||
|
||||
$error = 0;
|
||||
$this->destination = rtrim($this->destination, DS);
|
||||
@@ -126,16 +128,16 @@ class InstallCommand extends ConsoleCommand
|
||||
exec('cd "' . $this->destination . '" && git clone -b ' . $data['branch'] . ' --depth 1 ' . $data['url'] . ' ' . $data['path'], $output, $return);
|
||||
|
||||
if (!$return) {
|
||||
$this->output->writeln('<green>SUCCESS</green> cloned <magenta>' . $data['url'] . '</magenta> -> <cyan>' . $path . '</cyan>');
|
||||
$io->writeln('<green>SUCCESS</green> cloned <magenta>' . $data['url'] . '</magenta> -> <cyan>' . $path . '</cyan>');
|
||||
} else {
|
||||
$this->output->writeln('<red>ERROR</red> cloning <magenta>' . $data['url']);
|
||||
$io->writeln('<red>ERROR</red> cloning <magenta>' . $data['url']);
|
||||
$error = 1;
|
||||
}
|
||||
|
||||
$this->output->writeln('');
|
||||
$io->newLine();
|
||||
} else {
|
||||
$this->output->writeln('<yellow>' . $path . ' already exists, skipping...</yellow>');
|
||||
$this->output->writeln('');
|
||||
$io->writeln('<yellow>' . $path . ' already exists, skipping...</yellow>');
|
||||
$io->newLine();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,14 +151,16 @@ class InstallCommand extends ConsoleCommand
|
||||
*/
|
||||
private function symlink(): int
|
||||
{
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('<green>Symlinking Bits</green>');
|
||||
$this->output->writeln('===============');
|
||||
$this->output->writeln('');
|
||||
$io = $this->getIO();
|
||||
|
||||
$io->newLine();
|
||||
$io->writeln('<green>Symlinking Bits</green>');
|
||||
$io->writeln('===============');
|
||||
$io->newLine();
|
||||
|
||||
if (!$this->local_config) {
|
||||
$this->output->writeln('<red>No local configuration available, aborting...</red>');
|
||||
$this->output->writeln('');
|
||||
$io->writeln('<red>No local configuration available, aborting...</red>');
|
||||
$io->newLine();
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -168,8 +172,8 @@ class InstallCommand extends ConsoleCommand
|
||||
$src = $data['src'] ?? null;
|
||||
$path = $data['path'] ?? null;
|
||||
if (!isset($scm, $src, $path)) {
|
||||
$this->output->writeln("<red>Dependency '$name' has broken configuration, skipping...</red>");
|
||||
$this->output->writeln('');
|
||||
$io->writeln("<red>Dependency '$name' has broken configuration, skipping...</red>");
|
||||
$io->newLine();
|
||||
$error = 1;
|
||||
|
||||
continue;
|
||||
@@ -188,20 +192,20 @@ class InstallCommand extends ConsoleCommand
|
||||
}
|
||||
|
||||
if (is_link($to) && !is_file("{$to}/{$name}.yaml")) {
|
||||
$this->output->writeln('<yellow>Removed broken symlink '. $path .'</yellow>');
|
||||
$io->writeln('<yellow>Removed broken symlink '. $path .'</yellow>');
|
||||
unlink($to);
|
||||
}
|
||||
if (null === $from) {
|
||||
$this->output->writeln('<red>source for ' . $src . ' does not exists, skipping...</red>');
|
||||
$this->output->writeln('');
|
||||
$io->writeln('<red>source for ' . $src . ' does not exists, skipping...</red>');
|
||||
$io->newLine();
|
||||
$error = 1;
|
||||
} elseif (!file_exists($to)) {
|
||||
symlink($from, $to);
|
||||
$this->output->writeln('<green>SUCCESS</green> symlinked <magenta>' . $src . '</magenta> -> <cyan>' . $path . '</cyan>');
|
||||
$this->output->writeln('');
|
||||
$io->writeln('<green>SUCCESS</green> symlinked <magenta>' . $src . '</magenta> -> <cyan>' . $path . '</cyan>');
|
||||
$io->newLine();
|
||||
} else {
|
||||
$this->output->writeln('<yellow>destination: ' . $path . ' already exists, skipping...</yellow>');
|
||||
$this->output->writeln('');
|
||||
$io->writeln('<yellow>destination: ' . $path . ' already exists, skipping...</yellow>');
|
||||
$io->newLine();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,15 +12,14 @@ namespace Grav\Console\Cli;
|
||||
use DateTime;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Helpers\LogViewer;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Console\GravCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* Class LogViewerCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class LogViewerCommand extends ConsoleCommand
|
||||
class LogViewerCommand extends GravCommand
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
@@ -50,11 +49,12 @@ class LogViewerCommand extends ConsoleCommand
|
||||
*/
|
||||
protected function serve(): int
|
||||
{
|
||||
$file = $this->input->getOption('file') ?? 'grav.log';
|
||||
$lines = $this->input->getOption('lines') ?? 20;
|
||||
$verbose = $this->input->getOption('verbose') ?? false;
|
||||
$input = $this->getInput();
|
||||
$io = $this->getIO();
|
||||
|
||||
$io = new SymfonyStyle($this->input, $this->output);
|
||||
$file = $input->getOption('file') ?? 'grav.log';
|
||||
$lines = $input->getOption('lines') ?? 20;
|
||||
$verbose = $input->getOption('verbose') ?? false;
|
||||
|
||||
$io->title('Log Viewer');
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Console\GravCommand;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
@@ -18,7 +18,7 @@ use Symfony\Component\Console\Input\InputOption;
|
||||
* Class NewProjectCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class NewProjectCommand extends ConsoleCommand
|
||||
class NewProjectCommand extends GravCommand
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
@@ -48,6 +48,8 @@ class NewProjectCommand extends ConsoleCommand
|
||||
*/
|
||||
protected function serve(): int
|
||||
{
|
||||
$io = $this->getIO();
|
||||
|
||||
$sandboxCommand = $this->getApplication()->find('sandbox');
|
||||
$installCommand = $this->getApplication()->find('install');
|
||||
|
||||
@@ -63,9 +65,9 @@ class NewProjectCommand extends ConsoleCommand
|
||||
'-s' => $this->input->getOption('symlink')
|
||||
]);
|
||||
|
||||
$error = $sandboxCommand->run($sandboxArguments, $this->output);
|
||||
$error = $sandboxCommand->run($sandboxArguments, $io);
|
||||
if ($error === 0) {
|
||||
$error = $installCommand->run($installArguments, $this->output);
|
||||
$error = $installCommand->run($installArguments, $io);
|
||||
}
|
||||
|
||||
return $error;
|
||||
|
||||
@@ -14,7 +14,7 @@ use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Page\Interfaces\PageInterface;
|
||||
use Grav\Common\Page\Pages;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Console\GravCommand;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use function in_array;
|
||||
@@ -24,7 +24,7 @@ use function is_object;
|
||||
* Class PageSystemValidatorCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class PageSystemValidatorCommand extends ConsoleCommand
|
||||
class PageSystemValidatorCommand extends GravCommand
|
||||
{
|
||||
/** @var array */
|
||||
protected $tests = [
|
||||
@@ -149,10 +149,13 @@ class PageSystemValidatorCommand extends ConsoleCommand
|
||||
*/
|
||||
protected function serve(): int
|
||||
{
|
||||
$input = $this->getInput();
|
||||
$io = $this->getIO();
|
||||
|
||||
$this->setLanguage('en');
|
||||
$this->initializePages();
|
||||
|
||||
$this->output->writeln('');
|
||||
$io->newLine();
|
||||
|
||||
$this->grav = $grav = Grav::instance();
|
||||
|
||||
@@ -161,27 +164,27 @@ class PageSystemValidatorCommand extends ConsoleCommand
|
||||
/** @var Config $config */
|
||||
$config = $grav['config'];
|
||||
|
||||
if ($this->input->getOption('record')) {
|
||||
$this->output->writeln('Pages: ' . $config->get('system.pages.type', 'page'));
|
||||
if ($input->getOption('record')) {
|
||||
$io->writeln('Pages: ' . $config->get('system.pages.type', 'page'));
|
||||
|
||||
$this->output->writeln('<magenta>Record tests</magenta>');
|
||||
$this->output->writeln('');
|
||||
$io->writeln('<magenta>Record tests</magenta>');
|
||||
$io->newLine();
|
||||
|
||||
$results = $this->record();
|
||||
$file = $this->getFile('pages-old');
|
||||
$file->save($results);
|
||||
|
||||
$this->output->writeln('Recorded tests to ' . $file->filename());
|
||||
} elseif ($this->input->getOption('check')) {
|
||||
$this->output->writeln('Pages: ' . $config->get('system.pages.type', 'page'));
|
||||
$io->writeln('Recorded tests to ' . $file->filename());
|
||||
} elseif ($input->getOption('check')) {
|
||||
$io->writeln('Pages: ' . $config->get('system.pages.type', 'page'));
|
||||
|
||||
$this->output->writeln('<magenta>Run tests</magenta>');
|
||||
$this->output->writeln('');
|
||||
$io->writeln('<magenta>Run tests</magenta>');
|
||||
$io->newLine();
|
||||
|
||||
$new = $this->record();
|
||||
$file = $this->getFile('pages-new');
|
||||
$file->save($new);
|
||||
$this->output->writeln('Recorded tests to ' . $file->filename());
|
||||
$io->writeln('Recorded tests to ' . $file->filename());
|
||||
|
||||
$file = $this->getFile('pages-old');
|
||||
$old = $file->content();
|
||||
@@ -189,11 +192,11 @@ class PageSystemValidatorCommand extends ConsoleCommand
|
||||
$results = $this->check($old, $new);
|
||||
$file = $this->getFile('diff');
|
||||
$file->save($results);
|
||||
$this->output->writeln('Recorded results to ' . $file->filename());
|
||||
$io->writeln('Recorded results to ' . $file->filename());
|
||||
} else {
|
||||
$this->output->writeln('<green>page-system-validator [-r|--record] [-c|--check]</green>');
|
||||
$io->writeln('<green>page-system-validator [-r|--record] [-c|--check]</green>');
|
||||
}
|
||||
$this->output->writeln('');
|
||||
$io->newLine();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -201,8 +204,10 @@ class PageSystemValidatorCommand extends ConsoleCommand
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function record()
|
||||
private function record(): array
|
||||
{
|
||||
$io = $this->getIO();
|
||||
|
||||
/** @var Pages $pages */
|
||||
$pages = $this->grav['pages'];
|
||||
$all = $pages->all();
|
||||
@@ -211,7 +216,7 @@ class PageSystemValidatorCommand extends ConsoleCommand
|
||||
$results[''] = $this->recordRow($pages->root());
|
||||
foreach ($all as $path => $page) {
|
||||
if (null === $page) {
|
||||
$this->output->writeln('<red>Error on page ' . $path . '</red>');
|
||||
$io->writeln('<red>Error on page ' . $path . '</red>');
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -225,7 +230,7 @@ class PageSystemValidatorCommand extends ConsoleCommand
|
||||
* @param PageInterface $page
|
||||
* @return array
|
||||
*/
|
||||
private function recordRow(PageInterface $page)
|
||||
private function recordRow(PageInterface $page): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
@@ -287,7 +292,7 @@ class PageSystemValidatorCommand extends ConsoleCommand
|
||||
* @param string $name
|
||||
* @return CompiledYamlFile
|
||||
*/
|
||||
private function getFile(string $name)
|
||||
private function getFile(string $name): CompiledYamlFile
|
||||
{
|
||||
return CompiledYamlFile::instance('cache://tests/' . $name . '.yaml');
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Console\GravCommand;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
@@ -20,7 +20,7 @@ use function count;
|
||||
* Class SandboxCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class SandboxCommand extends ConsoleCommand
|
||||
class SandboxCommand extends GravCommand
|
||||
{
|
||||
/** @var array */
|
||||
protected $directories = [
|
||||
@@ -100,7 +100,9 @@ class SandboxCommand extends ConsoleCommand
|
||||
*/
|
||||
protected function serve(): int
|
||||
{
|
||||
$this->destination = $this->input->getArgument('destination');
|
||||
$input = $this->getInput();
|
||||
|
||||
$this->destination = $input->getArgument('destination');
|
||||
|
||||
// Create Some core stuff if it doesn't exist
|
||||
$error = $this->createDirectories();
|
||||
@@ -109,7 +111,7 @@ class SandboxCommand extends ConsoleCommand
|
||||
}
|
||||
|
||||
// Copy files or create symlinks
|
||||
$error = $this->input->getOption('symlink') ? $this->symlink() : $this->copy();
|
||||
$error = $input->getOption('symlink') ? $this->symlink() : $this->copy();
|
||||
if ($error) {
|
||||
return $error;
|
||||
}
|
||||
@@ -137,8 +139,10 @@ class SandboxCommand extends ConsoleCommand
|
||||
*/
|
||||
private function createDirectories(): int
|
||||
{
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('<comment>Creating Directories</comment>');
|
||||
$io = $this->getIO();
|
||||
|
||||
$io->newLine();
|
||||
$io->writeln('<comment>Creating Directories</comment>');
|
||||
$dirs_created = false;
|
||||
|
||||
if (!file_exists($this->destination)) {
|
||||
@@ -148,13 +152,13 @@ class SandboxCommand extends ConsoleCommand
|
||||
foreach ($this->directories as $dir) {
|
||||
if (!file_exists($this->destination . $dir)) {
|
||||
$dirs_created = true;
|
||||
$this->output->writeln(' <cyan>' . $dir . '</cyan>');
|
||||
$io->writeln(' <cyan>' . $dir . '</cyan>');
|
||||
Folder::create($this->destination . $dir);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$dirs_created) {
|
||||
$this->output->writeln(' <red>Directories already exist</red>');
|
||||
$io->writeln(' <red>Directories already exist</red>');
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -165,8 +169,10 @@ class SandboxCommand extends ConsoleCommand
|
||||
*/
|
||||
private function copy(): int
|
||||
{
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('<comment>Copying Files</comment>');
|
||||
$io = $this->getIO();
|
||||
|
||||
$io->newLine();
|
||||
$io->writeln('<comment>Copying Files</comment>');
|
||||
|
||||
|
||||
foreach ($this->mappings as $source => $target) {
|
||||
@@ -177,7 +183,7 @@ class SandboxCommand extends ConsoleCommand
|
||||
$from = $this->source . $source;
|
||||
$to = $this->destination . $target;
|
||||
|
||||
$this->output->writeln(' <cyan>' . $source . '</cyan> <comment>-></comment> ' . $to);
|
||||
$io->writeln(' <cyan>' . $source . '</cyan> <comment>-></comment> ' . $to);
|
||||
@Folder::rcopy($from, $to);
|
||||
}
|
||||
|
||||
@@ -189,8 +195,10 @@ class SandboxCommand extends ConsoleCommand
|
||||
*/
|
||||
private function symlink(): int
|
||||
{
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('<comment>Resetting Symbolic Links</comment>');
|
||||
$io = $this->getIO();
|
||||
|
||||
$io->newLine();
|
||||
$io->writeln('<comment>Resetting Symbolic Links</comment>');
|
||||
|
||||
|
||||
foreach ($this->mappings as $source => $target) {
|
||||
@@ -201,7 +209,7 @@ class SandboxCommand extends ConsoleCommand
|
||||
$from = $this->source . $source;
|
||||
$to = $this->destination . $target;
|
||||
|
||||
$this->output->writeln(' <cyan>' . $source . '</cyan> <comment>-></comment> ' . $to);
|
||||
$io->writeln(' <cyan>' . $source . '</cyan> <comment>-></comment> ' . $to);
|
||||
|
||||
if (is_dir($to)) {
|
||||
@Folder::delete($to);
|
||||
@@ -219,8 +227,10 @@ class SandboxCommand extends ConsoleCommand
|
||||
*/
|
||||
private function pages(): int
|
||||
{
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('<comment>Pages Initializing</comment>');
|
||||
$io = $this->getIO();
|
||||
|
||||
$io->newLine();
|
||||
$io->writeln('<comment>Pages Initializing</comment>');
|
||||
|
||||
// get pages files and initialize if no pages exist
|
||||
$pages_dir = $this->destination . '/user/pages';
|
||||
@@ -229,7 +239,7 @@ class SandboxCommand extends ConsoleCommand
|
||||
if (count($pages_files) === 0) {
|
||||
$destination = $this->source . '/user/pages';
|
||||
Folder::rcopy($destination, $pages_dir);
|
||||
$this->output->writeln(' <cyan>' . $destination . '</cyan> <comment>-></comment> Created');
|
||||
$io->writeln(' <cyan>' . $destination . '</cyan> <comment>-></comment> Created');
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -244,8 +254,9 @@ class SandboxCommand extends ConsoleCommand
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('<comment>File Initializing</comment>');
|
||||
$io = $this->getIO();
|
||||
$io->newLine();
|
||||
$io->writeln('<comment>File Initializing</comment>');
|
||||
$files_init = false;
|
||||
|
||||
// Copy files if they do not exist
|
||||
@@ -260,12 +271,12 @@ class SandboxCommand extends ConsoleCommand
|
||||
if (!file_exists($to)) {
|
||||
$files_init = true;
|
||||
copy($from, $to);
|
||||
$this->output->writeln(' <cyan>' . $target . '</cyan> <comment>-></comment> Created');
|
||||
$io->writeln(' <cyan>' . $target . '</cyan> <comment>-></comment> Created');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$files_init) {
|
||||
$this->output->writeln(' <red>Files already exist</red>');
|
||||
$io->writeln(' <red>Files already exist</red>');
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -276,8 +287,9 @@ class SandboxCommand extends ConsoleCommand
|
||||
*/
|
||||
private function perms(): int
|
||||
{
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('<comment>Permissions Initializing</comment>');
|
||||
$io = $this->getIO();
|
||||
$io->newLine();
|
||||
$io->writeln('<comment>Permissions Initializing</comment>');
|
||||
|
||||
$dir_perms = 0755;
|
||||
|
||||
@@ -285,10 +297,10 @@ class SandboxCommand extends ConsoleCommand
|
||||
|
||||
foreach ($binaries as $bin) {
|
||||
chmod($bin, $dir_perms);
|
||||
$this->output->writeln(' <cyan>bin/' . basename($bin) . '</cyan> permissions reset to ' . decoct($dir_perms));
|
||||
$io->writeln(' <cyan>bin/' . basename($bin) . '</cyan> permissions reset to ' . decoct($dir_perms));
|
||||
}
|
||||
|
||||
$this->output->writeln("");
|
||||
$io->newLine();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -299,29 +311,30 @@ class SandboxCommand extends ConsoleCommand
|
||||
private function check(): bool
|
||||
{
|
||||
$success = true;
|
||||
$io = $this->getIO();
|
||||
|
||||
if (!file_exists($this->destination)) {
|
||||
$this->output->writeln(' file: <red>' . $this->destination . '</red> does not exist!');
|
||||
$io->writeln(' file: <red>' . $this->destination . '</red> does not exist!');
|
||||
$success = false;
|
||||
}
|
||||
|
||||
foreach ($this->directories as $dir) {
|
||||
if (!file_exists($this->destination . $dir)) {
|
||||
$this->output->writeln(' directory: <red>' . $dir . '</red> does not exist!');
|
||||
$io->writeln(' directory: <red>' . $dir . '</red> does not exist!');
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->mappings as $target => $link) {
|
||||
if (!file_exists($this->destination . $target)) {
|
||||
$this->output->writeln(' mappings: <red>' . $target . '</red> does not exist!');
|
||||
$io->writeln(' mappings: <red>' . $target . '</red> does not exist!');
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('<comment>install should be run with --symlink|--s to symlink first</comment>');
|
||||
$io->newLine();
|
||||
$io->writeln('<comment>install should be run with --symlink|--s to symlink first</comment>');
|
||||
}
|
||||
|
||||
return $success;
|
||||
|
||||
@@ -13,18 +13,17 @@ use Cron\CronExpression;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Common\Scheduler\Scheduler;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Console\GravCommand;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use function is_null;
|
||||
|
||||
/**
|
||||
* Class SchedulerCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class SchedulerCommand extends ConsoleCommand
|
||||
class SchedulerCommand extends GravCommand
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
@@ -54,8 +53,9 @@ class SchedulerCommand extends ConsoleCommand
|
||||
->addOption(
|
||||
'run',
|
||||
'r',
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Force run a job with a specific Job ID'
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Force run all jobs or a specific job if you specify a specific Job ID',
|
||||
false
|
||||
)
|
||||
->setDescription('Run the Grav Scheduler. Best when integrated with system cron')
|
||||
->setHelp("Running without any options will force the Scheduler to run through it's jobs and process them");
|
||||
@@ -70,6 +70,8 @@ class SchedulerCommand extends ConsoleCommand
|
||||
|
||||
$grav = Grav::instance();
|
||||
$grav['backups']->init();
|
||||
$this->initializePages();
|
||||
$this->initializeThemes();
|
||||
|
||||
/** @var Scheduler $scheduler */
|
||||
$scheduler = $grav['scheduler'];
|
||||
@@ -77,17 +79,20 @@ class SchedulerCommand extends ConsoleCommand
|
||||
|
||||
$this->setHelp('foo');
|
||||
|
||||
$io = new SymfonyStyle($this->input, $this->output);
|
||||
$input = $this->getInput();
|
||||
$io = $this->getIO();
|
||||
$error = 0;
|
||||
|
||||
if ($this->input->getOption('jobs')) {
|
||||
$run = $input->getOption('run');
|
||||
|
||||
if ($input->getOption('jobs')) {
|
||||
// Show jobs list
|
||||
|
||||
$jobs = $scheduler->getAllJobs();
|
||||
$job_states = (array)$scheduler->getJobStates()->content();
|
||||
$rows = [];
|
||||
|
||||
$table = new Table($this->output);
|
||||
$table = new Table($io);
|
||||
$table->setStyle('box');
|
||||
$headers = ['Job ID', 'Command', 'Run At', 'Status', 'Last Run', 'State'];
|
||||
|
||||
@@ -121,13 +126,13 @@ class SchedulerCommand extends ConsoleCommand
|
||||
$io->newLine();
|
||||
$io->note('For error details run "bin/grav scheduler -d"');
|
||||
$io->newLine();
|
||||
} elseif ($this->input->getOption('details')) {
|
||||
} elseif ($input->getOption('details')) {
|
||||
$jobs = $scheduler->getAllJobs();
|
||||
$job_states = (array)$scheduler->getJobStates()->content();
|
||||
|
||||
$io->title('Job Details');
|
||||
|
||||
$table = new Table($this->output);
|
||||
$table = new Table($io);
|
||||
$table->setStyle('box');
|
||||
$table->setHeaders(['Job ID', 'Last Run', 'Next Run', 'Errors']);
|
||||
$rows = [];
|
||||
@@ -159,10 +164,10 @@ class SchedulerCommand extends ConsoleCommand
|
||||
|
||||
$table->setRows($rows);
|
||||
$table->render();
|
||||
} elseif ($jobid = $this->input->getOption('run')) {
|
||||
$io->title('Force Run Job: ' . $jobid);
|
||||
} elseif ($run !== false && $run !== null) {
|
||||
$io->title('Force Run Job: ' . $run);
|
||||
|
||||
$job = $scheduler->getJob($jobid);
|
||||
$job = $scheduler->getJob($run);
|
||||
|
||||
if ($job) {
|
||||
$job->inForeground()->run();
|
||||
@@ -177,13 +182,13 @@ class SchedulerCommand extends ConsoleCommand
|
||||
$output = $job->getOutput();
|
||||
|
||||
if ($output) {
|
||||
$this->output->write($output);
|
||||
$io->write($output);
|
||||
}
|
||||
} else {
|
||||
$error = 1;
|
||||
$io->error('Could not find a job with id: ' . $jobid);
|
||||
$io->error('Could not find a job with id: ' . $run);
|
||||
}
|
||||
} elseif ($this->input->getOption('install')) {
|
||||
} elseif ($input->getOption('install')) {
|
||||
$io->title('Install Scheduler');
|
||||
|
||||
$verb = 'install';
|
||||
@@ -206,9 +211,10 @@ class SchedulerCommand extends ConsoleCommand
|
||||
}
|
||||
} else {
|
||||
// Run scheduler
|
||||
$scheduler->run();
|
||||
$force = $run === null;
|
||||
$scheduler->run(null, $force);
|
||||
|
||||
if ($this->input->getOption('verbose')) {
|
||||
if ($input->getOption('verbose')) {
|
||||
$io->title('Running Scheduled Jobs');
|
||||
$io->text($scheduler->getVerboseOutput());
|
||||
}
|
||||
|
||||
@@ -11,16 +11,15 @@ namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Security;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Console\GravCommand;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Class SecurityCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class SecurityCommand extends ConsoleCommand
|
||||
class SecurityCommand extends GravCommand
|
||||
{
|
||||
/** @var ProgressBar $progress */
|
||||
protected $progress;
|
||||
@@ -43,19 +42,19 @@ class SecurityCommand extends ConsoleCommand
|
||||
{
|
||||
$this->initializePages();
|
||||
|
||||
$io = $this->getIO();
|
||||
|
||||
/** @var Grav $grav */
|
||||
$grav = Grav::instance();
|
||||
$this->progress = new ProgressBar($this->output, count($grav['pages']->routes()) - 1);
|
||||
$this->progress = $io->createProgressBar(count($grav['pages']->routes()) - 1);
|
||||
$this->progress->setFormat('Scanning <cyan>%current%</cyan> pages [<green>%bar%</green>] <white>%percent:3s%%</white> %elapsed:6s%');
|
||||
$this->progress->setBarWidth(100);
|
||||
|
||||
$io = new SymfonyStyle($this->input, $this->output);
|
||||
$io->title('Grav Security Check');
|
||||
$io->newline(2);
|
||||
|
||||
$output = Security::detectXssFromPages($grav['pages'], false, [$this, 'outputProgress']);
|
||||
|
||||
$io->newline(2);
|
||||
|
||||
$error = 0;
|
||||
if (!empty($output)) {
|
||||
$counter = 1;
|
||||
@@ -82,7 +81,7 @@ class SecurityCommand extends ConsoleCommand
|
||||
* @param array $args
|
||||
* @return void
|
||||
*/
|
||||
public function outputProgress($args): void
|
||||
public function outputProgress(array $args): void
|
||||
{
|
||||
switch ($args['type']) {
|
||||
case 'count':
|
||||
|
||||
@@ -10,9 +10,8 @@
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Console\GravCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Process\PhpExecutableFinder;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
@@ -20,7 +19,7 @@ use Symfony\Component\Process\Process;
|
||||
* Class ServerCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class ServerCommand extends ConsoleCommand
|
||||
class ServerCommand extends GravCommand
|
||||
{
|
||||
const SYMFONY_SERVER = 'Symfony Server';
|
||||
const PHP_SERVER = 'Built-in PHP Server';
|
||||
@@ -29,8 +28,6 @@ class ServerCommand extends ConsoleCommand
|
||||
protected $ip;
|
||||
/** @var int */
|
||||
protected $port;
|
||||
/** @var SymfonyStyle */
|
||||
protected $io;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
@@ -51,7 +48,8 @@ class ServerCommand extends ConsoleCommand
|
||||
*/
|
||||
protected function serve(): int
|
||||
{
|
||||
$io = $this->io = new SymfonyStyle($this->input, $this->output);
|
||||
$input = $this->getInput();
|
||||
$io = $this->getIO();
|
||||
|
||||
$io->title('Grav Web Server');
|
||||
|
||||
@@ -59,15 +57,15 @@ class ServerCommand extends ConsoleCommand
|
||||
ini_set('cli_server.color', 'on');
|
||||
|
||||
// Options
|
||||
$force_symfony = $this->input->getOption('symfony');
|
||||
$force_php = $this->input->getOption('php');
|
||||
$force_symfony = $input->getOption('symfony');
|
||||
$force_php = $input->getOption('php');
|
||||
|
||||
// Find PHP
|
||||
$executableFinder = new PhpExecutableFinder();
|
||||
$php = $executableFinder->find(false);
|
||||
|
||||
$this->ip = '127.0.0.1';
|
||||
$this->port = (int)($this->input->getOption('port') ?? 8000);
|
||||
$this->port = (int)($input->getOption('port') ?? 8000);
|
||||
|
||||
// Get an open port
|
||||
while (!$this->portAvailable($this->ip, $this->port)) {
|
||||
@@ -92,16 +90,12 @@ class ServerCommand extends ConsoleCommand
|
||||
$error = 0;
|
||||
foreach ($commands as $name => $command) {
|
||||
$process = $this->runProcess($name, $command);
|
||||
|
||||
if (!$process) {
|
||||
$io->note('Starting ' . $name . '...');
|
||||
}
|
||||
|
||||
// Should only get here if there's an error running
|
||||
if (!$process->isRunning() && (
|
||||
($name === self::SYMFONY_SERVER && $force_symfony) ||
|
||||
($name === self::PHP_SERVER)
|
||||
)) {
|
||||
if (!$process->isRunning() && (($name === self::SYMFONY_SERVER && $force_symfony) || ($name === self::PHP_SERVER))) {
|
||||
$error = 1;
|
||||
$io->error('Could not start ' . $name);
|
||||
}
|
||||
@@ -115,23 +109,25 @@ class ServerCommand extends ConsoleCommand
|
||||
* @param array $cmd
|
||||
* @return Process
|
||||
*/
|
||||
protected function runProcess($name, $cmd)
|
||||
protected function runProcess(string $name, array $cmd): Process
|
||||
{
|
||||
$io = $this->getIO();
|
||||
|
||||
$process = new Process($cmd);
|
||||
$process->setTimeout(0);
|
||||
$process->start();
|
||||
|
||||
if ($name === self::SYMFONY_SERVER && Utils::contains($process->getErrorOutput(), 'symfony: not found')) {
|
||||
$this->io->error('The symfony binary could not be found, please install the CLI tools: https://symfony.com/download');
|
||||
$this->io->warning('Falling back to PHP web server...');
|
||||
$io->error('The symfony binary could not be found, please install the CLI tools: https://symfony.com/download');
|
||||
$io->warning('Falling back to PHP web server...');
|
||||
}
|
||||
|
||||
if ($name === self::PHP_SERVER) {
|
||||
$this->io->success('Built-in PHP web server listening on http://' . $this->ip . ':' . $this->port . ' (PHP v' . PHP_VERSION . ')');
|
||||
$io->success('Built-in PHP web server listening on http://' . $this->ip . ':' . $this->port . ' (PHP v' . PHP_VERSION . ')');
|
||||
}
|
||||
|
||||
$process->wait(function ($type, $buffer) {
|
||||
$this->output->write($buffer);
|
||||
$this->getIO()->write($buffer);
|
||||
});
|
||||
|
||||
return $process;
|
||||
@@ -144,7 +140,7 @@ class ServerCommand extends ConsoleCommand
|
||||
* @param int $port
|
||||
* @return bool
|
||||
*/
|
||||
protected function portAvailable($ip, $port): bool
|
||||
protected function portAvailable(string $ip, int $port): bool
|
||||
{
|
||||
$fp = @fsockopen($ip, $port, $errno, $errstr, 0.1);
|
||||
if (!$fp) {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Common\Helpers\YamlLinter;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Console\GravCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
@@ -18,7 +18,7 @@ use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
* Class YamlLinterCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class YamlLinterCommand extends ConsoleCommand
|
||||
class YamlLinterCommand extends GravCommand
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
@@ -27,12 +27,6 @@ class YamlLinterCommand extends ConsoleCommand
|
||||
{
|
||||
$this
|
||||
->setName('yamllinter')
|
||||
->addOption(
|
||||
'env',
|
||||
'e',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'The environment to trigger a specific configuration. For example: localhost, mysite.dev, www.mysite.com'
|
||||
)
|
||||
->addOption(
|
||||
'all',
|
||||
'a',
|
||||
@@ -54,12 +48,13 @@ class YamlLinterCommand extends ConsoleCommand
|
||||
*/
|
||||
protected function serve(): int
|
||||
{
|
||||
$io = new SymfonyStyle($this->input, $this->output);
|
||||
$input = $this->getInput();
|
||||
$io = $this->getIO();
|
||||
|
||||
$io->title('Yaml Linter');
|
||||
|
||||
$error = 0;
|
||||
if ($this->input->getOption('all')) {
|
||||
if ($input->getOption('all')) {
|
||||
$io->section('All');
|
||||
$errors = YamlLinter::lint('');
|
||||
|
||||
@@ -69,7 +64,7 @@ class YamlLinterCommand extends ConsoleCommand
|
||||
$error = 1;
|
||||
$this->displayErrors($errors, $io);
|
||||
}
|
||||
} elseif ($folder = $this->input->getOption('folder')) {
|
||||
} elseif ($folder = $input->getOption('folder')) {
|
||||
$io->section($folder);
|
||||
$errors = YamlLinter::lint($folder);
|
||||
|
||||
@@ -119,7 +114,7 @@ class YamlLinterCommand extends ConsoleCommand
|
||||
* @param SymfonyStyle $io
|
||||
* @return void
|
||||
*/
|
||||
protected function displayErrors($errors, SymfonyStyle $io): void
|
||||
protected function displayErrors(array $errors, SymfonyStyle $io): void
|
||||
{
|
||||
$io->error('YAML Linting issues found...');
|
||||
foreach ($errors as $path => $error) {
|
||||
|
||||
@@ -9,10 +9,6 @@
|
||||
|
||||
namespace Grav\Console;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Language\Language;
|
||||
use Grav\Common\Processors\InitializeProcessor;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
@@ -25,18 +21,10 @@ class ConsoleCommand extends Command
|
||||
{
|
||||
use ConsoleTrait;
|
||||
|
||||
/** @var bool */
|
||||
private $plugins_initialized = false;
|
||||
/** @var bool */
|
||||
private $themes_initialized = false;
|
||||
/** @var bool */
|
||||
private $pages_initialized = false;
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*
|
||||
* @return int|null|void
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
@@ -55,147 +43,4 @@ class ConsoleCommand extends Command
|
||||
// Return error.
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Grav.
|
||||
*
|
||||
* - Load configuration
|
||||
* - Initialize logger
|
||||
* - Disable debugger
|
||||
* - Set timezone, locale
|
||||
* - Load plugins (call PluginsLoadedEvent)
|
||||
* - Set Pages and Users type to be used in the site
|
||||
*
|
||||
* Safe to be called multiple times.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final protected function initializeGrav()
|
||||
{
|
||||
InitializeProcessor::initializeCli(Grav::instance());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set language to be used in CLI.
|
||||
*
|
||||
* @param string|null $code
|
||||
* @return $this
|
||||
*/
|
||||
final protected function setLanguage(string $code = null)
|
||||
{
|
||||
$this->initializeGrav();
|
||||
|
||||
$grav = Grav::instance();
|
||||
/** @var Language $language */
|
||||
$language = $grav['language'];
|
||||
if ($language->enabled()) {
|
||||
if ($code && $language->validate($code)) {
|
||||
$language->setActive($code);
|
||||
} else {
|
||||
$language->setActive($language->getDefault());
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properly initialize plugins.
|
||||
*
|
||||
* - call $this->initializeGrav()
|
||||
* - call onPluginsInitialized event
|
||||
*
|
||||
* Safe to be called multiple times.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final protected function initializePlugins()
|
||||
{
|
||||
if (!$this->plugins_initialized) {
|
||||
$this->plugins_initialized = true;
|
||||
|
||||
$this->initializeGrav();
|
||||
|
||||
// Initialize plugins.
|
||||
$grav = Grav::instance();
|
||||
$grav['plugins']->init();
|
||||
$grav->fireEvent('onPluginsInitialized');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properly initialize themes.
|
||||
*
|
||||
* - call $this->initializePlugins()
|
||||
* - initialize theme (call onThemeInitialized event)
|
||||
*
|
||||
* Safe to be called multiple times.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final protected function initializeThemes()
|
||||
{
|
||||
if (!$this->themes_initialized) {
|
||||
$this->themes_initialized = true;
|
||||
|
||||
$this->initializePlugins();
|
||||
|
||||
// Initialize themes.
|
||||
$grav = Grav::instance();
|
||||
$grav['themes']->init();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properly initialize pages.
|
||||
*
|
||||
* - call $this->initializeThemes()
|
||||
* - initialize assets (call onAssetsInitialized event)
|
||||
* - initialize twig (calls the twig events)
|
||||
* - initialize pages (calls onPagesInitialized event)
|
||||
*
|
||||
* Safe to be called multiple times.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final protected function initializePages()
|
||||
{
|
||||
if (!$this->pages_initialized) {
|
||||
$this->pages_initialized = true;
|
||||
|
||||
$this->initializeThemes();
|
||||
|
||||
$grav = Grav::instance();
|
||||
|
||||
// Initialize assets.
|
||||
$grav['assets']->init();
|
||||
$grav->fireEvent('onAssetsInitialized');
|
||||
|
||||
// Initialize twig.
|
||||
$grav['twig']->init();
|
||||
|
||||
// Initialize pages.
|
||||
$pages = $grav['pages'];
|
||||
$pages->init();
|
||||
$grav->fireEvent('onPagesInitialized', new Event(['pages' => $pages]));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function displayGPMRelease()
|
||||
{
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('GPM Releases Configuration: <yellow>' . ucfirst(Grav::instance()['config']->get('system.gpm.releases')) . '</yellow>');
|
||||
$this->output->writeln('');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,16 @@ use Exception;
|
||||
use Grav\Common\Cache;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Composer;
|
||||
use Grav\Common\Language\Language;
|
||||
use Grav\Common\Processors\InitializeProcessor;
|
||||
use Grav\Console\Cli\ClearCacheCommand;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\File\YamlFile;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* Trait ConsoleTrait
|
||||
@@ -28,16 +32,20 @@ trait ConsoleTrait
|
||||
{
|
||||
/** @var string */
|
||||
protected $argv;
|
||||
|
||||
/* @var InputInterface $output */
|
||||
/** @var InputInterface */
|
||||
protected $input;
|
||||
|
||||
/* @var OutputInterface $output */
|
||||
/** @var SymfonyStyle */
|
||||
protected $output;
|
||||
|
||||
/** @var array */
|
||||
protected $local_config;
|
||||
|
||||
/** @var bool */
|
||||
private $plugins_initialized = false;
|
||||
/** @var bool */
|
||||
private $themes_initialized = false;
|
||||
/** @var bool */
|
||||
private $pages_initialized = false;
|
||||
|
||||
/**
|
||||
* Set colors style definition for the formatter.
|
||||
*
|
||||
@@ -47,21 +55,195 @@ trait ConsoleTrait
|
||||
*/
|
||||
public function setupConsole(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// Initialize cache with CLI compatibility
|
||||
Grav::instance()['config']->set('system.cache.cli_compatibility', true);
|
||||
Grav::instance()['cache'];
|
||||
|
||||
$this->argv = $_SERVER['argv'][0];
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
$this->input = $input;
|
||||
$this->output = new SymfonyStyle($input, $output);
|
||||
|
||||
$this->output->getFormatter()->setStyle('normal', new OutputFormatterStyle('white'));
|
||||
$this->output->getFormatter()->setStyle('yellow', new OutputFormatterStyle('yellow', null, array('bold')));
|
||||
$this->output->getFormatter()->setStyle('red', new OutputFormatterStyle('red', null, array('bold')));
|
||||
$this->output->getFormatter()->setStyle('cyan', new OutputFormatterStyle('cyan', null, array('bold')));
|
||||
$this->output->getFormatter()->setStyle('green', new OutputFormatterStyle('green', null, array('bold')));
|
||||
$this->output->getFormatter()->setStyle('magenta', new OutputFormatterStyle('magenta', null, array('bold')));
|
||||
$this->output->getFormatter()->setStyle('white', new OutputFormatterStyle('white', null, array('bold')));
|
||||
$this->setupGrav();
|
||||
}
|
||||
|
||||
public function getInput(): InputInterface
|
||||
{
|
||||
return $this->input;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SymfonyStyle
|
||||
*/
|
||||
public function getIO(): SymfonyStyle
|
||||
{
|
||||
return $this->output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an option.
|
||||
*
|
||||
* @param string $name The option name
|
||||
* @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
|
||||
* @param int|null $mode The option mode: One of the InputOption::VALUE_* constants
|
||||
* @param string $description A description text
|
||||
* @param string|string[]|int|bool|null $default The default value (must be null for InputOption::VALUE_NONE)
|
||||
* @return $this
|
||||
* @throws InvalidArgumentException If option mode is invalid or incompatible
|
||||
*/
|
||||
public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null)
|
||||
{
|
||||
if ($name !== 'env' && $name !== 'lang') {
|
||||
parent::addOption($name, $shortcut, $mode, $description, $default);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
final protected function setupGrav(): void
|
||||
{
|
||||
try {
|
||||
$language = $this->input->getOption('lang');
|
||||
if ($language) {
|
||||
// Set used language.
|
||||
$this->setLanguage($language);
|
||||
}
|
||||
} catch (InvalidArgumentException $e) {}
|
||||
|
||||
// Initialize cache with CLI compatibility
|
||||
$grav = Grav::instance();
|
||||
$grav['config']->set('system.cache.cli_compatibility', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Grav.
|
||||
*
|
||||
* - Load configuration
|
||||
* - Initialize logger
|
||||
* - Disable debugger
|
||||
* - Set timezone, locale
|
||||
* - Load plugins (call PluginsLoadedEvent)
|
||||
* - Set Pages and Users type to be used in the site
|
||||
*
|
||||
* Safe to be called multiple times.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final protected function initializeGrav()
|
||||
{
|
||||
InitializeProcessor::initializeCli(Grav::instance());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set language to be used in CLI.
|
||||
*
|
||||
* @param string|null $code
|
||||
* @return $this
|
||||
*/
|
||||
final protected function setLanguage(string $code = null)
|
||||
{
|
||||
$this->initializeGrav();
|
||||
|
||||
$grav = Grav::instance();
|
||||
/** @var Language $language */
|
||||
$language = $grav['language'];
|
||||
if ($language->enabled()) {
|
||||
if ($code && $language->validate($code)) {
|
||||
$language->setActive($code);
|
||||
} else {
|
||||
$language->setActive($language->getDefault());
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properly initialize plugins.
|
||||
*
|
||||
* - call $this->initializeGrav()
|
||||
* - call onPluginsInitialized event
|
||||
*
|
||||
* Safe to be called multiple times.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final protected function initializePlugins()
|
||||
{
|
||||
if (!$this->plugins_initialized) {
|
||||
$this->plugins_initialized = true;
|
||||
|
||||
$this->initializeGrav();
|
||||
|
||||
// Initialize plugins.
|
||||
$grav = Grav::instance();
|
||||
$grav['plugins']->init();
|
||||
$grav->fireEvent('onPluginsInitialized');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properly initialize themes.
|
||||
*
|
||||
* - call $this->initializePlugins()
|
||||
* - initialize theme (call onThemeInitialized event)
|
||||
*
|
||||
* Safe to be called multiple times.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final protected function initializeThemes()
|
||||
{
|
||||
if (!$this->themes_initialized) {
|
||||
$this->themes_initialized = true;
|
||||
|
||||
$this->initializePlugins();
|
||||
|
||||
// Initialize themes.
|
||||
$grav = Grav::instance();
|
||||
$grav['themes']->init();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properly initialize pages.
|
||||
*
|
||||
* - call $this->initializeThemes()
|
||||
* - initialize assets (call onAssetsInitialized event)
|
||||
* - initialize twig (calls the twig events)
|
||||
* - initialize pages (calls onPagesInitialized event)
|
||||
*
|
||||
* Safe to be called multiple times.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final protected function initializePages()
|
||||
{
|
||||
if (!$this->pages_initialized) {
|
||||
$this->pages_initialized = true;
|
||||
|
||||
$this->initializeThemes();
|
||||
|
||||
$grav = Grav::instance();
|
||||
|
||||
// Initialize assets.
|
||||
$grav['assets']->init();
|
||||
$grav->fireEvent('onAssetsInitialized');
|
||||
|
||||
// Initialize twig.
|
||||
$grav['twig']->init();
|
||||
|
||||
// Initialize pages.
|
||||
$pages = $grav['pages'];
|
||||
$pages->init();
|
||||
$grav->fireEvent('onPagesInitialized', new Event(['pages' => $pages]));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,27 +252,29 @@ trait ConsoleTrait
|
||||
*/
|
||||
public function isGravInstance($path)
|
||||
{
|
||||
$io = $this->getIO();
|
||||
|
||||
if (!file_exists($path)) {
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln("<red>ERROR</red>: Destination doesn't exist:");
|
||||
$this->output->writeln(" <white>$path</white>");
|
||||
$this->output->writeln('');
|
||||
$io->writeln('');
|
||||
$io->writeln("<red>ERROR</red>: Destination doesn't exist:");
|
||||
$io->writeln(" <white>$path</white>");
|
||||
$io->writeln('');
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!is_dir($path)) {
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln("<red>ERROR</red>: Destination chosen to install is not a directory:");
|
||||
$this->output->writeln(" <white>$path</white>");
|
||||
$this->output->writeln('');
|
||||
$io->writeln('');
|
||||
$io->writeln("<red>ERROR</red>: Destination chosen to install is not a directory:");
|
||||
$io->writeln(" <white>$path</white>");
|
||||
$io->writeln('');
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!file_exists($path . DS . 'index.php') || !file_exists($path . DS . '.dependencies') || !file_exists($path . DS . 'system' . DS . 'config' . DS . 'system.yaml')) {
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('<red>ERROR</red>: Destination chosen to install does not appear to be a Grav instance:');
|
||||
$this->output->writeln(" <white>$path</white>");
|
||||
$this->output->writeln('');
|
||||
$io->writeln('');
|
||||
$io->writeln('<red>ERROR</red>: Destination chosen to install does not appear to be a Grav instance:');
|
||||
$io->writeln(" <white>$path</white>");
|
||||
$io->writeln('');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -109,7 +293,6 @@ trait ConsoleTrait
|
||||
|
||||
/**
|
||||
* @param array $all
|
||||
*
|
||||
* @return int
|
||||
* @throws Exception
|
||||
*/
|
||||
|
||||
@@ -16,12 +16,12 @@ use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\GPM\GPM;
|
||||
use Grav\Common\GPM\Installer;
|
||||
use Grav\Common\GPM\Response;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Console\GpmCommand;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use ZipArchive;
|
||||
use function is_array;
|
||||
use function is_callable;
|
||||
|
||||
@@ -29,11 +29,10 @@ use function is_callable;
|
||||
* Class DirectInstallCommand
|
||||
* @package Grav\Console\Gpm
|
||||
*/
|
||||
class DirectInstallCommand extends ConsoleCommand
|
||||
class DirectInstallCommand extends GpmCommand
|
||||
{
|
||||
/** @var string */
|
||||
protected $all_yes;
|
||||
|
||||
/** @var string */
|
||||
protected $destination;
|
||||
|
||||
@@ -72,8 +71,9 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
*/
|
||||
protected function serve(): int
|
||||
{
|
||||
if (!class_exists(\ZipArchive::class)) {
|
||||
$io = new SymfonyStyle($this->input, $this->output);
|
||||
$input = $this->getInput();
|
||||
$io = $this->getIO();
|
||||
if (!class_exists(ZipArchive::class)) {
|
||||
$io->title('Direct Install');
|
||||
$io->error('php-zip extension needs to be enabled!');
|
||||
|
||||
@@ -81,90 +81,89 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
}
|
||||
|
||||
// Making sure the destination is usable
|
||||
$this->destination = realpath($this->input->getOption('destination'));
|
||||
$this->destination = realpath($input->getOption('destination'));
|
||||
|
||||
if (!Installer::isGravInstance($this->destination) ||
|
||||
!Installer::isValidDestination($this->destination, [Installer::EXISTS, Installer::IS_LINK])
|
||||
) {
|
||||
$this->output->writeln('<red>ERROR</red>: ' . Installer::lastErrorMsg());
|
||||
$io->writeln('<red>ERROR</red>: ' . Installer::lastErrorMsg());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->all_yes = $this->input->getOption('all-yes');
|
||||
$this->all_yes = $input->getOption('all-yes');
|
||||
|
||||
$package_file = $this->input->getArgument('package-file');
|
||||
$package_file = $input->getArgument('package-file');
|
||||
|
||||
$helper = $this->getHelper('question');
|
||||
$question = new ConfirmationQuestion("Are you sure you want to direct-install <cyan>{$package_file}</cyan> [y|N] ", false);
|
||||
|
||||
$answer = $this->all_yes ? true : $helper->ask($this->input, $this->output, $question);
|
||||
$answer = $this->all_yes ? true : $io->askQuestion($question);
|
||||
|
||||
if (!$answer) {
|
||||
$this->output->writeln('exiting...');
|
||||
$this->output->writeln('');
|
||||
$io->writeln('exiting...');
|
||||
$io->newLine();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$tmp_dir = Grav::instance()['locator']->findResource('tmp://', true, true);
|
||||
$tmp_zip = $tmp_dir . '/Grav-' . uniqid();
|
||||
$tmp_zip = $tmp_dir . uniqid('/Grav-', false);
|
||||
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln("Preparing to install <cyan>{$package_file}</cyan>");
|
||||
$io->newLine();
|
||||
$io->writeln("Preparing to install <cyan>{$package_file}</cyan>");
|
||||
|
||||
|
||||
if (Response::isRemote($package_file)) {
|
||||
$this->output->write(' |- Downloading package... 0%');
|
||||
$io->write(' |- Downloading package... 0%');
|
||||
try {
|
||||
$zip = GPM::downloadPackage($package_file, $tmp_zip);
|
||||
} catch (RuntimeException $e) {
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln(" `- <red>ERROR: {$e->getMessage()}</red>");
|
||||
$this->output->writeln('');
|
||||
$io->newLine();
|
||||
$io->writeln(" `- <red>ERROR: {$e->getMessage()}</red>");
|
||||
$io->newLine();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($zip) {
|
||||
$this->output->write("\x0D");
|
||||
$this->output->write(' |- Downloading package... 100%');
|
||||
$this->output->writeln('');
|
||||
$io->write("\x0D");
|
||||
$io->write(' |- Downloading package... 100%');
|
||||
$io->newLine();
|
||||
}
|
||||
} else {
|
||||
$this->output->write(' |- Copying package... 0%');
|
||||
$io->write(' |- Copying package... 0%');
|
||||
$zip = GPM::copyPackage($package_file, $tmp_zip);
|
||||
if ($zip) {
|
||||
$this->output->write("\x0D");
|
||||
$this->output->write(' |- Copying package... 100%');
|
||||
$this->output->writeln('');
|
||||
$io->write("\x0D");
|
||||
$io->write(' |- Copying package... 100%');
|
||||
$io->newLine();
|
||||
}
|
||||
}
|
||||
|
||||
if (file_exists($zip)) {
|
||||
$tmp_source = $tmp_dir . '/Grav-' . uniqid();
|
||||
$tmp_source = $tmp_dir . uniqid('/Grav-', false);
|
||||
|
||||
$this->output->write(' |- Extracting package... ');
|
||||
$io->write(' |- Extracting package... ');
|
||||
$extracted = Installer::unZip($zip, $tmp_source);
|
||||
|
||||
if (!$extracted) {
|
||||
$this->output->write("\x0D");
|
||||
$this->output->writeln(' |- Extracting package... <red>failed</red>');
|
||||
$io->write("\x0D");
|
||||
$io->writeln(' |- Extracting package... <red>failed</red>');
|
||||
Folder::delete($tmp_source);
|
||||
Folder::delete($tmp_zip);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->output->write("\x0D");
|
||||
$this->output->writeln(' |- Extracting package... <green>ok</green>');
|
||||
$io->write("\x0D");
|
||||
$io->writeln(' |- Extracting package... <green>ok</green>');
|
||||
|
||||
|
||||
$type = GPM::getPackageType($extracted);
|
||||
|
||||
if (!$type) {
|
||||
$this->output->writeln(" '- <red>ERROR: Not a valid Grav package</red>");
|
||||
$this->output->writeln('');
|
||||
$io->writeln(" '- <red>ERROR: Not a valid Grav package</red>");
|
||||
$io->newLine();
|
||||
Folder::delete($tmp_source);
|
||||
Folder::delete($tmp_zip);
|
||||
|
||||
@@ -187,14 +186,14 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
$dependencies[] = $dependency;
|
||||
}
|
||||
}
|
||||
$this->output->writeln(' |- Dependencies found... <cyan>[' . implode(',', $dependencies) . ']</cyan>');
|
||||
$io->writeln(' |- Dependencies found... <cyan>[' . implode(',', $dependencies) . ']</cyan>');
|
||||
|
||||
$question = new ConfirmationQuestion(" | '- Dependencies will not be satisfied. Continue ? [y|N] ", false);
|
||||
$answer = $this->all_yes ? true : $helper->ask($this->input, $this->output, $question);
|
||||
$answer = $this->all_yes ? true : $io->askQuestion($question);
|
||||
|
||||
if (!$answer) {
|
||||
$this->output->writeln('exiting...');
|
||||
$this->output->writeln('');
|
||||
$io->writeln('exiting...');
|
||||
$io->newLine();
|
||||
Folder::delete($tmp_source);
|
||||
Folder::delete($tmp_zip);
|
||||
|
||||
@@ -204,31 +203,31 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
}
|
||||
|
||||
if ($type === 'grav') {
|
||||
$this->output->write(' |- Checking destination... ');
|
||||
$io->write(' |- Checking destination... ');
|
||||
Installer::isValidDestination(GRAV_ROOT . '/system');
|
||||
if (Installer::IS_LINK === Installer::lastErrorCode()) {
|
||||
$this->output->write("\x0D");
|
||||
$this->output->writeln(' |- Checking destination... <yellow>symbolic link</yellow>');
|
||||
$this->output->writeln(" '- <red>ERROR: symlinks found...</red> <yellow>" . GRAV_ROOT . '</yellow>');
|
||||
$this->output->writeln('');
|
||||
$io->write("\x0D");
|
||||
$io->writeln(' |- Checking destination... <yellow>symbolic link</yellow>');
|
||||
$io->writeln(" '- <red>ERROR: symlinks found...</red> <yellow>" . GRAV_ROOT . '</yellow>');
|
||||
$io->newLine();
|
||||
Folder::delete($tmp_source);
|
||||
Folder::delete($tmp_zip);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->output->write("\x0D");
|
||||
$this->output->writeln(' |- Checking destination... <green>ok</green>');
|
||||
$io->write("\x0D");
|
||||
$io->writeln(' |- Checking destination... <green>ok</green>');
|
||||
|
||||
$this->output->write(' |- Installing package... ');
|
||||
$io->write(' |- Installing package... ');
|
||||
|
||||
$this->upgradeGrav($zip, $extracted);
|
||||
} else {
|
||||
$name = GPM::getPackageName($extracted);
|
||||
|
||||
if (!$name) {
|
||||
$this->output->writeln('<red>ERROR: Name could not be determined.</red> Please specify with --name|-n');
|
||||
$this->output->writeln('');
|
||||
$io->writeln('<red>ERROR: Name could not be determined.</red> Please specify with --name|-n');
|
||||
$io->newLine();
|
||||
Folder::delete($tmp_source);
|
||||
Folder::delete($tmp_zip);
|
||||
|
||||
@@ -238,24 +237,24 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
$install_path = GPM::getInstallPath($type, $name);
|
||||
$is_update = file_exists($install_path);
|
||||
|
||||
$this->output->write(' |- Checking destination... ');
|
||||
$io->write(' |- Checking destination... ');
|
||||
|
||||
Installer::isValidDestination(GRAV_ROOT . DS . $install_path);
|
||||
if (Installer::lastErrorCode() === Installer::IS_LINK) {
|
||||
$this->output->write("\x0D");
|
||||
$this->output->writeln(' |- Checking destination... <yellow>symbolic link</yellow>');
|
||||
$this->output->writeln(" '- <red>ERROR: symlink found...</red> <yellow>" . GRAV_ROOT . DS . $install_path . '</yellow>');
|
||||
$this->output->writeln('');
|
||||
$io->write("\x0D");
|
||||
$io->writeln(' |- Checking destination... <yellow>symbolic link</yellow>');
|
||||
$io->writeln(" '- <red>ERROR: symlink found...</red> <yellow>" . GRAV_ROOT . DS . $install_path . '</yellow>');
|
||||
$io->newLine();
|
||||
Folder::delete($tmp_source);
|
||||
Folder::delete($tmp_zip);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->output->write("\x0D");
|
||||
$this->output->writeln(' |- Checking destination... <green>ok</green>');
|
||||
$io->write("\x0D");
|
||||
$io->writeln(' |- Checking destination... <green>ok</green>');
|
||||
|
||||
$this->output->write(' |- Installing package... ');
|
||||
$io->write(' |- Installing package... ');
|
||||
|
||||
Installer::install(
|
||||
$zip,
|
||||
@@ -271,18 +270,18 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
|
||||
Folder::delete($tmp_source);
|
||||
|
||||
$this->output->write("\x0D");
|
||||
$io->write("\x0D");
|
||||
|
||||
if (Installer::lastErrorCode()) {
|
||||
$this->output->writeln(" '- <red>" . Installer::lastErrorMsg() . '</red>');
|
||||
$this->output->writeln('');
|
||||
$io->writeln(" '- <red>" . Installer::lastErrorMsg() . '</red>');
|
||||
$io->newLine();
|
||||
} else {
|
||||
$this->output->writeln(' |- Installing package... <green>ok</green>');
|
||||
$this->output->writeln(" '- <green>Success!</green> ");
|
||||
$this->output->writeln('');
|
||||
$io->writeln(' |- Installing package... <green>ok</green>');
|
||||
$io->writeln(" '- <green>Success!</green> ");
|
||||
$io->newLine();
|
||||
}
|
||||
} else {
|
||||
$this->output->writeln(" '- <red>ERROR: ZIP package could not be found</red>");
|
||||
$io->writeln(" '- <red>ERROR: ZIP package could not be found</red>");
|
||||
Folder::delete($tmp_zip);
|
||||
|
||||
return 1;
|
||||
@@ -299,9 +298,10 @@ class DirectInstallCommand extends ConsoleCommand
|
||||
/**
|
||||
* @param string $zip
|
||||
* @param string $folder
|
||||
* @param false $keepFolder
|
||||
* @param bool $keepFolder
|
||||
* @return void
|
||||
*/
|
||||
private function upgradeGrav($zip, $folder, $keepFolder = false)
|
||||
private function upgradeGrav(string $zip, string $folder, bool $keepFolder = false): void
|
||||
{
|
||||
static $ignores = [
|
||||
'backup',
|
||||
|
||||
@@ -13,7 +13,7 @@ use Grav\Common\GPM\Remote\Package;
|
||||
use Grav\Common\GPM\GPM;
|
||||
use Grav\Common\GPM\Remote\Packages;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Console\GpmCommand;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
@@ -23,14 +23,12 @@ use function count;
|
||||
* Class IndexCommand
|
||||
* @package Grav\Console\Gpm
|
||||
*/
|
||||
class IndexCommand extends ConsoleCommand
|
||||
class IndexCommand extends GpmCommand
|
||||
{
|
||||
/** @var Packages */
|
||||
protected $data;
|
||||
|
||||
/** @var GPM */
|
||||
protected $gpm;
|
||||
|
||||
/** @var array */
|
||||
protected $options;
|
||||
|
||||
@@ -100,20 +98,21 @@ class IndexCommand extends ConsoleCommand
|
||||
*/
|
||||
protected function serve(): int
|
||||
{
|
||||
$this->options = $this->input->getOptions();
|
||||
$input = $this->getInput();
|
||||
$this->options = $input->getOptions();
|
||||
$this->gpm = new GPM($this->options['force']);
|
||||
$this->displayGPMRelease();
|
||||
$this->data = $this->gpm->getRepository();
|
||||
|
||||
$data = $this->filter($this->data);
|
||||
|
||||
$io = new SymfonyStyle($this->input, $this->output);
|
||||
$io = $this->getIO();
|
||||
|
||||
if (count($data) === 0) {
|
||||
$io->writeln('No data was found in the GPM repository stored locally.');
|
||||
$io->writeln('Please try clearing cache and running the <green>bin/gpm index -f</green> command again');
|
||||
$io->writeln('If this doesn\'t work try tweaking your GPM system settings.');
|
||||
$io->writeln('');
|
||||
$io->newLine();
|
||||
$io->writeln('For more help go to:');
|
||||
$io->writeln(' -> <yellow>https://learn.getgrav.org/troubleshooting/common-problems#cannot-connect-to-the-gpm</yellow>');
|
||||
|
||||
@@ -126,8 +125,8 @@ class IndexCommand extends ConsoleCommand
|
||||
$packages = $this->sort($packages);
|
||||
|
||||
if (!empty($packages)) {
|
||||
$section = $this->output->section('Packages table');
|
||||
$table = new Table($section);
|
||||
$io->section('Packages table');
|
||||
$table = new Table($io);
|
||||
$table->setHeaders(['Count', 'Name', 'Slug', 'Version', 'Installed']);
|
||||
|
||||
$index = 0;
|
||||
@@ -146,25 +145,24 @@ class IndexCommand extends ConsoleCommand
|
||||
$table->render();
|
||||
}
|
||||
|
||||
$io->writeln('');
|
||||
$io->newLine();
|
||||
}
|
||||
|
||||
$io->writeln('You can either get more informations about a package by typing:');
|
||||
$io->writeln(" <green>{$this->argv} info <cyan><package></cyan></green>");
|
||||
$io->writeln('');
|
||||
$io->newLine();
|
||||
$io->writeln('Or you can install a package by typing:');
|
||||
$io->writeln(" <green>{$this->argv} install <cyan><package></cyan></green>");
|
||||
$io->writeln('');
|
||||
$io->newLine();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Package $package
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function version($package)
|
||||
private function version(Package $package): string
|
||||
{
|
||||
$list = $this->gpm->{'getUpdatable' . ucfirst($package->package_type)}();
|
||||
$package = $list[$package->slug] ?? $package;
|
||||
@@ -178,19 +176,14 @@ class IndexCommand extends ConsoleCommand
|
||||
return "v<green>{$version}</green>";
|
||||
}
|
||||
|
||||
if ($updatable) {
|
||||
return "v<red>{$package->version}</red> <cyan>-></cyan> v<green>{$package->available}</green>";
|
||||
}
|
||||
|
||||
return '';
|
||||
return "v<red>{$package->version}</red> <cyan>-></cyan> v<green>{$package->available}</green>";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Package $package
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function installed($package)
|
||||
private function installed(Package $package): string
|
||||
{
|
||||
$package = $list[$package->slug] ?? $package;
|
||||
$type = ucfirst(preg_replace('/s$/', '', $package->package_type));
|
||||
@@ -202,10 +195,9 @@ class IndexCommand extends ConsoleCommand
|
||||
|
||||
/**
|
||||
* @param Packages $data
|
||||
*
|
||||
* @return Packages
|
||||
*/
|
||||
public function filter($data)
|
||||
public function filter(Packages $data): Packages
|
||||
{
|
||||
// filtering and sorting
|
||||
if ($this->options['plugins-only']) {
|
||||
@@ -260,14 +252,13 @@ class IndexCommand extends ConsoleCommand
|
||||
* @param Packages $packages
|
||||
* @return Packages
|
||||
*/
|
||||
public function sort($packages)
|
||||
public function sort(Packages $packages): Packages
|
||||
{
|
||||
foreach ($this->options['sort'] as $key) {
|
||||
$packages = $packages->sort(function ($a, $b) use ($key) {
|
||||
switch ($key) {
|
||||
case 'author':
|
||||
return strcmp($a->{$key}['name'], $b->{$key}['name']);
|
||||
break;
|
||||
default:
|
||||
return strcmp($a->$key, $b->$key);
|
||||
}
|
||||
|
||||
@@ -10,23 +10,22 @@
|
||||
namespace Grav\Console\Gpm;
|
||||
|
||||
use Grav\Common\GPM\GPM;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Console\GpmCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
* Class InfoCommand
|
||||
* @package Grav\Console\Gpm
|
||||
*/
|
||||
class InfoCommand extends ConsoleCommand
|
||||
class InfoCommand extends GpmCommand
|
||||
{
|
||||
/** @var array */
|
||||
protected $data;
|
||||
|
||||
/** @var GPM */
|
||||
protected $gpm;
|
||||
|
||||
/** @var string */
|
||||
protected $all_yes;
|
||||
|
||||
@@ -63,37 +62,40 @@ class InfoCommand extends ConsoleCommand
|
||||
*/
|
||||
protected function serve(): int
|
||||
{
|
||||
$this->gpm = new GPM($this->input->getOption('force'));
|
||||
$input = $this->getInput();
|
||||
$io = $this->getIO();
|
||||
|
||||
$this->all_yes = $this->input->getOption('all-yes');
|
||||
$this->gpm = new GPM($input->getOption('force'));
|
||||
|
||||
$this->all_yes = $input->getOption('all-yes');
|
||||
|
||||
$this->displayGPMRelease();
|
||||
|
||||
$foundPackage = $this->gpm->findPackage($this->input->getArgument('package'));
|
||||
$foundPackage = $this->gpm->findPackage($input->getArgument('package'));
|
||||
|
||||
if (!$foundPackage) {
|
||||
$this->output->writeln("The package <cyan>'{$this->input->getArgument('package')}'</cyan> was not found in the Grav repository.");
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('You can list all the available packages by typing:');
|
||||
$this->output->writeln(" <green>{$this->argv} index</green>");
|
||||
$this->output->writeln('');
|
||||
$io->writeln("The package <cyan>'{$input->getArgument('package')}'</cyan> was not found in the Grav repository.");
|
||||
$io->newLine();
|
||||
$io->writeln('You can list all the available packages by typing:');
|
||||
$io->writeln(" <green>{$this->argv} index</green>");
|
||||
$io->newLine();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->output->writeln("Found package <cyan>'{$this->input->getArgument('package')}'</cyan> under the '<green>" . ucfirst($foundPackage->package_type) . "</green>' section");
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln("<cyan>{$foundPackage->name}</cyan> [{$foundPackage->slug}]");
|
||||
$this->output->writeln(str_repeat('-', \strlen($foundPackage->name) + \strlen($foundPackage->slug) + 3));
|
||||
$this->output->writeln('<white>' . strip_tags($foundPackage->description_plain) . '</white>');
|
||||
$this->output->writeln('');
|
||||
$io->writeln("Found package <cyan>'{$input->getArgument('package')}'</cyan> under the '<green>" . ucfirst($foundPackage->package_type) . "</green>' section");
|
||||
$io->newLine();
|
||||
$io->writeln("<cyan>{$foundPackage->name}</cyan> [{$foundPackage->slug}]");
|
||||
$io->writeln(str_repeat('-', strlen($foundPackage->name) + strlen($foundPackage->slug) + 3));
|
||||
$io->writeln('<white>' . strip_tags($foundPackage->description_plain) . '</white>');
|
||||
$io->newLine();
|
||||
|
||||
$packageURL = '';
|
||||
if (isset($foundPackage->author['url'])) {
|
||||
$packageURL = '<' . $foundPackage->author['url'] . '>';
|
||||
}
|
||||
|
||||
$this->output->writeln('<green>' . str_pad(
|
||||
$io->writeln('<green>' . str_pad(
|
||||
'Author',
|
||||
12
|
||||
) . ':</green> ' . $foundPackage->author['name'] . ' <' . $foundPackage->author['email'] . '> ' . $packageURL);
|
||||
@@ -125,7 +127,7 @@ class InfoCommand extends ConsoleCommand
|
||||
}
|
||||
|
||||
$name = str_pad($name, 12);
|
||||
$this->output->writeln("<green>{$name}:</green> {$data}");
|
||||
$io->writeln("<green>{$name}:</green> {$data}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,54 +138,53 @@ class InfoCommand extends ConsoleCommand
|
||||
// display current version if installed and different
|
||||
if ($installed && $updatable) {
|
||||
$local = $this->gpm->{'getInstalled'. $type}($foundPackage->slug);
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln("Currently installed version: <magenta>{$local->version}</magenta>");
|
||||
$this->output->writeln('');
|
||||
$io->newLine();
|
||||
$io->writeln("Currently installed version: <magenta>{$local->version}</magenta>");
|
||||
$io->newLine();
|
||||
}
|
||||
|
||||
// display changelog information
|
||||
$questionHelper = $this->getHelper('question');
|
||||
$question = new ConfirmationQuestion(
|
||||
'Would you like to read the changelog? [y|N] ',
|
||||
false
|
||||
);
|
||||
$answer = $this->all_yes ? true : $questionHelper->ask($this->input, $this->output, $question);
|
||||
$answer = $this->all_yes ? true : $io->askQuestion($question);
|
||||
|
||||
if ($answer) {
|
||||
$changelog = $foundPackage->changelog;
|
||||
|
||||
$this->output->writeln('');
|
||||
$io->newLine();
|
||||
foreach ($changelog as $version => $log) {
|
||||
$title = $version . ' [' . $log['date'] . ']';
|
||||
$content = preg_replace_callback('/\d\.\s\[\]\(#(.*)\)/', static function ($match) {
|
||||
return "\n" . ucfirst($match[1]) . ':';
|
||||
}, $log['content']);
|
||||
|
||||
$this->output->writeln("<cyan>{$title}</cyan>");
|
||||
$this->output->writeln(str_repeat('-', \strlen($title)));
|
||||
$this->output->writeln($content);
|
||||
$this->output->writeln('');
|
||||
$io->writeln("<cyan>{$title}</cyan>");
|
||||
$io->writeln(str_repeat('-', strlen($title)));
|
||||
$io->writeln($content);
|
||||
$io->newLine();
|
||||
|
||||
$question = new ConfirmationQuestion('Press [ENTER] to continue or [q] to quit ', true);
|
||||
$answer = $this->all_yes ? false : $questionHelper->ask($this->input, $this->output, $question);
|
||||
$answer = $this->all_yes ? false : $io->askQuestion($question);
|
||||
if (!$answer) {
|
||||
break;
|
||||
}
|
||||
$this->output->writeln('');
|
||||
$io->newLine();
|
||||
}
|
||||
}
|
||||
|
||||
$this->output->writeln('');
|
||||
$io->newLine();
|
||||
|
||||
if ($installed && $updatable) {
|
||||
$this->output->writeln('You can update this package by typing:');
|
||||
$this->output->writeln(" <green>{$this->argv} update</green> <cyan>{$foundPackage->slug}</cyan>");
|
||||
$io->writeln('You can update this package by typing:');
|
||||
$io->writeln(" <green>{$this->argv} update</green> <cyan>{$foundPackage->slug}</cyan>");
|
||||
} else {
|
||||
$this->output->writeln("You can install this package by typing:");
|
||||
$this->output->writeln(" <green>{$this->argv} install</green> <cyan>{$foundPackage->slug}</cyan>");
|
||||
$io->writeln('You can install this package by typing:');
|
||||
$io->writeln(" <green>{$this->argv} install</green> <cyan>{$foundPackage->slug}</cyan>");
|
||||
}
|
||||
|
||||
$this->output->writeln('');
|
||||
$io->newLine();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -18,43 +18,37 @@ use Grav\Common\GPM\Response;
|
||||
use Grav\Common\GPM\Remote\Package;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Console\GpmCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use ZipArchive;
|
||||
use function array_key_exists;
|
||||
use function count;
|
||||
use function define;
|
||||
|
||||
\define('GIT_REGEX', '/http[s]?:\/\/(?:.*@)?(github|bitbucket)(?:.org|.com)\/.*\/(.*)/');
|
||||
define('GIT_REGEX', '/http[s]?:\/\/(?:.*@)?(github|bitbucket)(?:.org|.com)\/.*\/(.*)/');
|
||||
|
||||
/**
|
||||
* Class InstallCommand
|
||||
* @package Grav\Console\Gpm
|
||||
*/
|
||||
class InstallCommand extends ConsoleCommand
|
||||
class InstallCommand extends GpmCommand
|
||||
{
|
||||
/** @var array */
|
||||
protected $data;
|
||||
|
||||
/** @var GPM */
|
||||
protected $gpm;
|
||||
|
||||
/** @var string */
|
||||
protected $destination;
|
||||
|
||||
/** @var string */
|
||||
protected $file;
|
||||
|
||||
/** @var string */
|
||||
protected $tmp;
|
||||
|
||||
/** @var bool */
|
||||
protected $use_symlinks;
|
||||
|
||||
/** @var array */
|
||||
protected $demo_processing = [];
|
||||
|
||||
/** @var string */
|
||||
protected $all_yes;
|
||||
|
||||
@@ -98,7 +92,7 @@ class InstallCommand extends ConsoleCommand
|
||||
*
|
||||
* @param GPM $gpm
|
||||
*/
|
||||
public function setGpm(GPM $gpm)
|
||||
public function setGpm(GPM $gpm): void
|
||||
{
|
||||
$this->gpm = $gpm;
|
||||
}
|
||||
@@ -108,45 +102,47 @@ class InstallCommand extends ConsoleCommand
|
||||
*/
|
||||
protected function serve(): int
|
||||
{
|
||||
if (!class_exists(\ZipArchive::class)) {
|
||||
$io = new SymfonyStyle($this->input, $this->output);
|
||||
$input = $this->getInput();
|
||||
$io = $this->getIO();
|
||||
|
||||
if (!class_exists(ZipArchive::class)) {
|
||||
$io->title('GPM Install');
|
||||
$io->error('php-zip extension needs to be enabled!');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->gpm = new GPM($this->input->getOption('force'));
|
||||
$this->gpm = new GPM($input->getOption('force'));
|
||||
|
||||
$this->all_yes = $this->input->getOption('all-yes');
|
||||
$this->all_yes = $input->getOption('all-yes');
|
||||
|
||||
$this->displayGPMRelease();
|
||||
|
||||
$this->destination = realpath($this->input->getOption('destination'));
|
||||
$this->destination = realpath($input->getOption('destination'));
|
||||
|
||||
$packages = array_map('strtolower', $this->input->getArgument('package'));
|
||||
$packages = array_map('strtolower', $input->getArgument('package'));
|
||||
$this->data = $this->gpm->findPackages($packages);
|
||||
$this->loadLocalConfig();
|
||||
|
||||
if (!Installer::isGravInstance($this->destination) ||
|
||||
!Installer::isValidDestination($this->destination, [Installer::EXISTS, Installer::IS_LINK])
|
||||
) {
|
||||
$this->output->writeln('<red>ERROR</red>: ' . Installer::lastErrorMsg());
|
||||
$io->writeln('<red>ERROR</red>: ' . Installer::lastErrorMsg());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->output->writeln('');
|
||||
$io->newLine();
|
||||
|
||||
if (!$this->data['total']) {
|
||||
$this->output->writeln('Nothing to install.');
|
||||
$this->output->writeln('');
|
||||
$io->writeln('Nothing to install.');
|
||||
$io->newLine();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count($this->data['not_found'])) {
|
||||
$this->output->writeln('These packages were not found on Grav: <red>' . implode(
|
||||
$io->writeln('These packages were not found on Grav: <red>' . implode(
|
||||
'</red>, <red>',
|
||||
array_keys($this->data['not_found'])
|
||||
) . '</red>');
|
||||
@@ -157,24 +153,23 @@ class InstallCommand extends ConsoleCommand
|
||||
if (null !== $this->local_config) {
|
||||
// Symlinks available, ask if Grav should use them
|
||||
$this->use_symlinks = false;
|
||||
$helper = $this->getHelper('question');
|
||||
$question = new ConfirmationQuestion('Should Grav use the symlinks if available? [y|N] ', false);
|
||||
|
||||
$answer = $this->all_yes ? false : $helper->ask($this->input, $this->output, $question);
|
||||
$answer = $this->all_yes ? false : $io->askQuestion($question);
|
||||
|
||||
if ($answer) {
|
||||
$this->use_symlinks = true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->output->writeln('');
|
||||
$io->newLine();
|
||||
|
||||
try {
|
||||
$dependencies = $this->gpm->getDependencies($packages);
|
||||
} catch (Exception $e) {
|
||||
//Error out if there are incompatible packages requirements and tell which ones, and what to do
|
||||
//Error out if there is any error in parsing the dependencies and their versions, and tell which one is broken
|
||||
$this->output->writeln("<red>{$e->getMessage()}</red>");
|
||||
$io->writeln("<red>{$e->getMessage()}</red>");
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -185,13 +180,13 @@ class InstallCommand extends ConsoleCommand
|
||||
$this->installDependencies($dependencies, 'update', 'The following dependencies need to be updated...');
|
||||
$this->installDependencies($dependencies, 'ignore', "The following dependencies can be updated as there is a newer version, but it's not mandatory...", false);
|
||||
} catch (Exception $e) {
|
||||
$this->output->writeln('<red>Installation aborted</red>');
|
||||
$io->writeln('<red>Installation aborted</red>');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->output->writeln('<green>Dependencies are OK</green>');
|
||||
$this->output->writeln('');
|
||||
$io->writeln('<green>Dependencies are OK</green>');
|
||||
$io->newLine();
|
||||
}
|
||||
|
||||
|
||||
@@ -199,7 +194,7 @@ class InstallCommand extends ConsoleCommand
|
||||
foreach ($this->data as $data) {
|
||||
foreach ($data as $package_name => $package) {
|
||||
if (array_key_exists($package_name, $dependencies)) {
|
||||
$this->output->writeln("<green>Package {$package_name} already installed as dependency</green>");
|
||||
$io->writeln("<green>Package {$package_name} already installed as dependency</green>");
|
||||
} else {
|
||||
$is_valid_destination = Installer::isValidDestination($this->destination . DS . $package->install_path);
|
||||
if ($is_valid_destination || Installer::lastErrorCode() == Installer::NOT_FOUND) {
|
||||
@@ -210,25 +205,24 @@ class InstallCommand extends ConsoleCommand
|
||||
$this->askConfirmationIfMajorVersionUpdated($package);
|
||||
$this->gpm->checkNoOtherPackageNeedsThisDependencyInALowerVersion($package->slug, $package->available, array_keys($data));
|
||||
} catch (Exception $e) {
|
||||
$this->output->writeln("<red>{$e->getMessage()}</red>");
|
||||
$io->writeln("<red>{$e->getMessage()}</red>");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$helper = $this->getHelper('question');
|
||||
$question = new ConfirmationQuestion("The package <cyan>{$package_name}</cyan> is already installed, overwrite? [y|N] ", false);
|
||||
$answer = $this->all_yes ? true : $helper->ask($this->input, $this->output, $question);
|
||||
$answer = $this->all_yes ? true : $io->askQuestion($question);
|
||||
|
||||
if ($answer) {
|
||||
$is_update = true;
|
||||
$this->processPackage($package, $is_update);
|
||||
} else {
|
||||
$this->output->writeln("<yellow>Package {$package_name} not overwritten</yellow>");
|
||||
$io->writeln("<yellow>Package {$package_name} not overwritten</yellow>");
|
||||
}
|
||||
} else {
|
||||
if (Installer::lastErrorCode() == Installer::IS_LINK) {
|
||||
$this->output->writeln("<red>Cannot overwrite existing symlink for </red><cyan>{$package_name}</cyan>");
|
||||
$this->output->writeln('');
|
||||
$io->writeln("<red>Cannot overwrite existing symlink for </red><cyan>{$package_name}</cyan>");
|
||||
$io->newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,9 +248,9 @@ class InstallCommand extends ConsoleCommand
|
||||
* @param Package $package
|
||||
* @return void
|
||||
*/
|
||||
public function askConfirmationIfMajorVersionUpdated($package): void
|
||||
public function askConfirmationIfMajorVersionUpdated(Package $package): void
|
||||
{
|
||||
$helper = $this->getHelper('question');
|
||||
$io = $this->getIO();
|
||||
$package_name = $package->name;
|
||||
$new_version = $package->available ?: $this->gpm->getLatestVersionOfPackage($package->slug);
|
||||
$old_version = $package->version;
|
||||
@@ -265,14 +259,14 @@ class InstallCommand extends ConsoleCommand
|
||||
|
||||
if ($major_version_changed) {
|
||||
if ($this->all_yes) {
|
||||
$this->output->writeln("The package <cyan>{$package_name}</cyan> will be updated to a new major version <green>{$new_version}</green>, from <magenta>{$old_version}</magenta>");
|
||||
$io->writeln("The package <cyan>{$package_name}</cyan> will be updated to a new major version <green>{$new_version}</green>, from <magenta>{$old_version}</magenta>");
|
||||
return;
|
||||
}
|
||||
|
||||
$question = new ConfirmationQuestion("The package <cyan>{$package_name}</cyan> will be updated to a new major version <green>{$new_version}</green>, from <magenta>{$old_version}</magenta>. Be sure to read what changed with the new major release. Continue? [y|N] ", false);
|
||||
|
||||
if (!$helper->ask($this->input, $this->output, $question)) {
|
||||
$this->output->writeln("<yellow>Package {$package_name} not updated</yellow>");
|
||||
if (!$io->askQuestion($question)) {
|
||||
$io->writeln("<yellow>Package {$package_name} not updated</yellow>");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -290,21 +284,20 @@ class InstallCommand extends ConsoleCommand
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function installDependencies($dependencies, $type, $message, $required = true): void
|
||||
public function installDependencies(array $dependencies, string $type, string $message, bool $required = true): void
|
||||
{
|
||||
$io = $this->getIO();
|
||||
$packages = array_filter($dependencies, static function ($action) use ($type) {
|
||||
return $action === $type;
|
||||
});
|
||||
if (count($packages) > 0) {
|
||||
$this->output->writeln($message);
|
||||
$io->writeln($message);
|
||||
|
||||
foreach ($packages as $dependencyName => $dependencyVersion) {
|
||||
$this->output->writeln(" |- Package <cyan>{$dependencyName}</cyan>");
|
||||
$io->writeln(" |- Package <cyan>{$dependencyName}</cyan>");
|
||||
}
|
||||
|
||||
$this->output->writeln('');
|
||||
|
||||
$helper = $this->getHelper('question');
|
||||
$io->newLine();
|
||||
|
||||
if ($type === 'install') {
|
||||
$questionAction = 'Install';
|
||||
@@ -325,14 +318,14 @@ class InstallCommand extends ConsoleCommand
|
||||
}
|
||||
|
||||
$question = new ConfirmationQuestion("${questionAction} {$questionArticle} {$questionNoun}? [Y|n] ", true);
|
||||
$answer = $this->all_yes ? true : $helper->ask($this->input, $this->output, $question);
|
||||
$answer = $this->all_yes ? true : $io->askQuestion($question);
|
||||
|
||||
if ($answer) {
|
||||
foreach ($packages as $dependencyName => $dependencyVersion) {
|
||||
$package = $this->gpm->findPackage($dependencyName);
|
||||
$this->processPackage($package, $type === 'update');
|
||||
}
|
||||
$this->output->writeln('');
|
||||
$io->newLine();
|
||||
} elseif ($required) {
|
||||
throw new Exception();
|
||||
}
|
||||
@@ -344,11 +337,13 @@ class InstallCommand extends ConsoleCommand
|
||||
* @param bool $is_update True if the package is an update
|
||||
* @return void
|
||||
*/
|
||||
private function processPackage($package, $is_update = false): void
|
||||
private function processPackage(?Package $package, bool $is_update = false): void
|
||||
{
|
||||
$io = $this->getIO();
|
||||
|
||||
if (!$package) {
|
||||
$this->output->writeln('<red>Package not found on the GPM!</red>');
|
||||
$this->output->writeln('');
|
||||
$io->writeln('<red>Package not found on the GPM!</red>');
|
||||
$io->newLine();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -370,7 +365,7 @@ class InstallCommand extends ConsoleCommand
|
||||
* @param Package $package
|
||||
* @return void
|
||||
*/
|
||||
private function processDemo($package): void
|
||||
private function processDemo(Package $package): void
|
||||
{
|
||||
$demo_dir = $this->destination . DS . $package->install_path . DS . '_demo';
|
||||
if (file_exists($demo_dir)) {
|
||||
@@ -384,8 +379,9 @@ class InstallCommand extends ConsoleCommand
|
||||
* @param Package $package
|
||||
* @return void
|
||||
*/
|
||||
private function installDemoContent($package): void
|
||||
private function installDemoContent(Package $package): void
|
||||
{
|
||||
$io = $this->getIO();
|
||||
$demo_dir = $this->destination . DS . $package->install_path . DS . '_demo';
|
||||
|
||||
if (file_exists($demo_dir)) {
|
||||
@@ -393,15 +389,15 @@ class InstallCommand extends ConsoleCommand
|
||||
$pages_dir = $dest_dir . DS . 'pages';
|
||||
|
||||
// Demo content exists, prompt to install it.
|
||||
$this->output->writeln("<white>Attention: </white><cyan>{$package->name}</cyan> contains demo content");
|
||||
$helper = $this->getHelper('question');
|
||||
$io->writeln("<white>Attention: </white><cyan>{$package->name}</cyan> contains demo content");
|
||||
|
||||
$question = new ConfirmationQuestion('Do you wish to install this demo content? [y|N] ', false);
|
||||
|
||||
$answer = $helper->ask($this->input, $this->output, $question);
|
||||
$answer = $io->askQuestion($question);
|
||||
|
||||
if (!$answer) {
|
||||
$this->output->writeln(" '- <red>Skipped!</red> ");
|
||||
$this->output->writeln('');
|
||||
$io->writeln(" '- <red>Skipped!</red> ");
|
||||
$io->newLine();
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -410,11 +406,11 @@ class InstallCommand extends ConsoleCommand
|
||||
if (file_exists($demo_dir . DS . 'pages')) {
|
||||
$pages_backup = 'pages.' . date('m-d-Y-H-i-s');
|
||||
$question = new ConfirmationQuestion('This will backup your current `user/pages` folder to `user/' . $pages_backup . '`, continue? [y|N]', false);
|
||||
$answer = $this->all_yes ? true : $helper->ask($this->input, $this->output, $question);
|
||||
$answer = $this->all_yes ? true : $io->askQuestion($question);
|
||||
|
||||
if (!$answer) {
|
||||
$this->output->writeln(" '- <red>Skipped!</red> ");
|
||||
$this->output->writeln('');
|
||||
$io->writeln(" '- <red>Skipped!</red> ");
|
||||
$io->newLine();
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -422,18 +418,18 @@ class InstallCommand extends ConsoleCommand
|
||||
// backup current pages folder
|
||||
if (file_exists($dest_dir)) {
|
||||
if (rename($pages_dir, $dest_dir . DS . $pages_backup)) {
|
||||
$this->output->writeln(' |- Backing up pages... <green>ok</green>');
|
||||
$io->writeln(' |- Backing up pages... <green>ok</green>');
|
||||
} else {
|
||||
$this->output->writeln(' |- Backing up pages... <red>failed</red>');
|
||||
$io->writeln(' |- Backing up pages... <red>failed</red>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Confirmation received, copy over the data
|
||||
$this->output->writeln(' |- Installing demo content... <green>ok</green> ');
|
||||
$io->writeln(' |- Installing demo content... <green>ok</green> ');
|
||||
Folder::rcopy($demo_dir, $dest_dir);
|
||||
$this->output->writeln(" '- <green>Success!</green> ");
|
||||
$this->output->writeln('');
|
||||
$io->writeln(" '- <green>Success!</green> ");
|
||||
$io->newLine();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,7 +437,7 @@ class InstallCommand extends ConsoleCommand
|
||||
* @param Package $package
|
||||
* @return array|false
|
||||
*/
|
||||
private function getGitRegexMatches($package)
|
||||
private function getGitRegexMatches(Package $package)
|
||||
{
|
||||
if (isset($package->repository)) {
|
||||
$repository = $package->repository;
|
||||
@@ -458,7 +454,7 @@ class InstallCommand extends ConsoleCommand
|
||||
* @param Package $package
|
||||
* @return string|false
|
||||
*/
|
||||
private function getSymlinkSource($package)
|
||||
private function getSymlinkSource(Package $package)
|
||||
{
|
||||
$matches = $this->getGitRegexMatches($package);
|
||||
|
||||
@@ -482,84 +478,88 @@ class InstallCommand extends ConsoleCommand
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Package $package
|
||||
* @param Package $package
|
||||
* @return void
|
||||
*/
|
||||
private function processSymlink($package): void
|
||||
private function processSymlink(Package $package): void
|
||||
{
|
||||
$io = $this->getIO();
|
||||
|
||||
exec('cd ' . $this->destination);
|
||||
|
||||
$to = $this->destination . DS . $package->install_path;
|
||||
$from = $this->getSymlinkSource($package);
|
||||
|
||||
$this->output->writeln("Preparing to Symlink <cyan>{$package->name}</cyan>");
|
||||
$this->output->write(' |- Checking source... ');
|
||||
$io->writeln("Preparing to Symlink <cyan>{$package->name}</cyan>");
|
||||
$io->write(' |- Checking source... ');
|
||||
|
||||
if (file_exists($from)) {
|
||||
$this->output->writeln('<green>ok</green>');
|
||||
$io->writeln('<green>ok</green>');
|
||||
|
||||
$this->output->write(' |- Checking destination... ');
|
||||
$io->write(' |- Checking destination... ');
|
||||
$checks = $this->checkDestination($package);
|
||||
|
||||
if (!$checks) {
|
||||
$this->output->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
$this->output->writeln('');
|
||||
$io->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
$io->newLine();
|
||||
} elseif (file_exists($to)) {
|
||||
$this->output->writeln(" '- <red>Symlink cannot overwrite an existing package, please remove first</red>");
|
||||
$this->output->writeln('');
|
||||
$io->writeln(" '- <red>Symlink cannot overwrite an existing package, please remove first</red>");
|
||||
$io->newLine();
|
||||
} else {
|
||||
symlink($from, $to);
|
||||
|
||||
// extra white spaces to clear out the buffer properly
|
||||
$this->output->writeln(' |- Symlinking package... <green>ok</green> ');
|
||||
$this->output->writeln(" '- <green>Success!</green> ");
|
||||
$this->output->writeln('');
|
||||
$io->writeln(' |- Symlinking package... <green>ok</green> ');
|
||||
$io->writeln(" '- <green>Success!</green> ");
|
||||
$io->newLine();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->output->writeln('<red>not found!</red>');
|
||||
$this->output->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
$io->writeln('<red>not found!</red>');
|
||||
$io->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Package $package
|
||||
* @param bool $is_update
|
||||
* @param Package $package
|
||||
* @param bool $is_update
|
||||
* @return bool
|
||||
*/
|
||||
private function processGpm($package, $is_update = false)
|
||||
private function processGpm(Package $package, bool $is_update = false)
|
||||
{
|
||||
$io = $this->getIO();
|
||||
|
||||
$version = $package->available ?? $package->version;
|
||||
$license = Licenses::get($package->slug);
|
||||
|
||||
$this->output->writeln("Preparing to install <cyan>{$package->name}</cyan> [v{$version}]");
|
||||
$io->writeln("Preparing to install <cyan>{$package->name}</cyan> [v{$version}]");
|
||||
|
||||
$this->output->write(' |- Downloading package... 0%');
|
||||
$io->write(' |- Downloading package... 0%');
|
||||
$this->file = $this->downloadPackage($package, $license);
|
||||
|
||||
if (!$this->file) {
|
||||
$this->output->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
$this->output->writeln('');
|
||||
$io->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
$io->newLine();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->output->write(' |- Checking destination... ');
|
||||
$io->write(' |- Checking destination... ');
|
||||
$checks = $this->checkDestination($package);
|
||||
|
||||
if (!$checks) {
|
||||
$this->output->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
$this->output->writeln('');
|
||||
$io->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
$io->newLine();
|
||||
} else {
|
||||
$this->output->write(' |- Installing package... ');
|
||||
$io->write(' |- Installing package... ');
|
||||
$installation = $this->installPackage($package, $is_update);
|
||||
if (!$installation) {
|
||||
$this->output->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
$this->output->writeln('');
|
||||
$io->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
$io->newLine();
|
||||
} else {
|
||||
$this->output->writeln(" '- <green>Success!</green> ");
|
||||
$this->output->writeln('');
|
||||
$io->writeln(" '- <green>Success!</green> ");
|
||||
$io->newLine();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -570,11 +570,13 @@ class InstallCommand extends ConsoleCommand
|
||||
|
||||
/**
|
||||
* @param Package $package
|
||||
* @param string|null $license
|
||||
* @param string|null $license
|
||||
* @return string|null
|
||||
*/
|
||||
private function downloadPackage($package, $license = null)
|
||||
private function downloadPackage(Package $package, string $license = null)
|
||||
{
|
||||
$io = $this->getIO();
|
||||
|
||||
$tmp_dir = Grav::instance()['locator']->findResource('tmp://', true, true);
|
||||
$this->tmp = $tmp_dir . '/Grav-' . uniqid();
|
||||
$filename = $package->slug . basename($package->zipball_url);
|
||||
@@ -599,19 +601,19 @@ class InstallCommand extends ConsoleCommand
|
||||
$output = Response::get($package->zipball_url . $query, [], [$this, 'progress']);
|
||||
} catch (Exception $e) {
|
||||
$error = str_replace("\n", "\n | '- ", $e->getMessage());
|
||||
$this->output->write("\x0D");
|
||||
$io->write("\x0D");
|
||||
// extra white spaces to clear out the buffer properly
|
||||
$this->output->writeln(' |- Downloading package... <red>error</red> ');
|
||||
$this->output->writeln(" | '- " . $error);
|
||||
$io->writeln(' |- Downloading package... <red>error</red> ');
|
||||
$io->writeln(" | '- " . $error);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Folder::create($this->tmp);
|
||||
|
||||
$this->output->write("\x0D");
|
||||
$this->output->write(' |- Downloading package... 100%');
|
||||
$this->output->writeln('');
|
||||
$io->write("\x0D");
|
||||
$io->write(' |- Downloading package... 100%');
|
||||
$io->newLine();
|
||||
|
||||
file_put_contents($this->tmp . DS . $filename, $output);
|
||||
|
||||
@@ -619,21 +621,21 @@ class InstallCommand extends ConsoleCommand
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Package $package
|
||||
* @param Package $package
|
||||
* @return bool
|
||||
*/
|
||||
private function checkDestination($package): bool
|
||||
private function checkDestination(Package $package): bool
|
||||
{
|
||||
$question_helper = $this->getHelper('question');
|
||||
$io = $this->getIO();
|
||||
|
||||
Installer::isValidDestination($this->destination . DS . $package->install_path);
|
||||
|
||||
if (Installer::lastErrorCode() === Installer::IS_LINK) {
|
||||
$this->output->write("\x0D");
|
||||
$this->output->writeln(' |- Checking destination... <yellow>symbolic link</yellow>');
|
||||
$io->write("\x0D");
|
||||
$io->writeln(' |- Checking destination... <yellow>symbolic link</yellow>');
|
||||
|
||||
if ($this->all_yes) {
|
||||
$this->output->writeln(" | '- <yellow>Skipped automatically.</yellow>");
|
||||
$io->writeln(" | '- <yellow>Skipped automatically.</yellow>");
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -642,10 +644,10 @@ class InstallCommand extends ConsoleCommand
|
||||
" | '- Destination has been detected as symlink, delete symbolic link first? [y|N] ",
|
||||
false
|
||||
);
|
||||
$answer = $question_helper->ask($this->input, $this->output, $question);
|
||||
$answer = $io->askQuestion($question);
|
||||
|
||||
if (!$answer) {
|
||||
$this->output->writeln(" | '- <red>You decided to not delete the symlink automatically.</red>");
|
||||
$io->writeln(" | '- <red>You decided to not delete the symlink automatically.</red>");
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -653,8 +655,8 @@ class InstallCommand extends ConsoleCommand
|
||||
unlink($this->destination . DS . $package->install_path);
|
||||
}
|
||||
|
||||
$this->output->write("\x0D");
|
||||
$this->output->writeln(' |- Checking destination... <green>ok</green>');
|
||||
$io->write("\x0D");
|
||||
$io->writeln(' |- Checking destination... <green>ok</green>');
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -663,11 +665,13 @@ class InstallCommand extends ConsoleCommand
|
||||
* Install a package
|
||||
*
|
||||
* @param Package $package
|
||||
* @param bool $is_update True if it's an update. False if it's an install
|
||||
* @param bool $is_update True if it's an update. False if it's an install
|
||||
* @return bool
|
||||
*/
|
||||
private function installPackage($package, $is_update = false)
|
||||
private function installPackage(Package $package, bool $is_update = false): bool
|
||||
{
|
||||
$io = $this->getIO();
|
||||
|
||||
$type = $package->package_type;
|
||||
|
||||
Installer::install($this->file, $this->destination, ['install_path' => $package->install_path, 'theme' => $type === 'themes', 'is_update' => $is_update]);
|
||||
@@ -675,24 +679,24 @@ class InstallCommand extends ConsoleCommand
|
||||
Folder::delete($this->tmp);
|
||||
|
||||
if ($error_code) {
|
||||
$this->output->write("\x0D");
|
||||
$io->write("\x0D");
|
||||
// extra white spaces to clear out the buffer properly
|
||||
$this->output->writeln(' |- Installing package... <red>error</red> ');
|
||||
$this->output->writeln(" | '- " . Installer::lastErrorMsg());
|
||||
$io->writeln(' |- Installing package... <red>error</red> ');
|
||||
$io->writeln(" | '- " . Installer::lastErrorMsg());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$message = Installer::getMessage();
|
||||
if ($message) {
|
||||
$this->output->write("\x0D");
|
||||
$io->write("\x0D");
|
||||
// extra white spaces to clear out the buffer properly
|
||||
$this->output->writeln(" |- {$message}");
|
||||
$io->writeln(" |- {$message}");
|
||||
}
|
||||
|
||||
$this->output->write("\x0D");
|
||||
$io->write("\x0D");
|
||||
// extra white spaces to clear out the buffer properly
|
||||
$this->output->writeln(' |- Installing package... <green>ok</green> ');
|
||||
$io->writeln(' |- Installing package... <green>ok</green> ');
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -701,10 +705,12 @@ class InstallCommand extends ConsoleCommand
|
||||
* @param array $progress
|
||||
* @return void
|
||||
*/
|
||||
public function progress($progress): void
|
||||
public function progress(array $progress): void
|
||||
{
|
||||
$this->output->write("\x0D");
|
||||
$this->output->write(' |- Downloading package... ' . str_pad(
|
||||
$io = $this->getIO();
|
||||
|
||||
$io->write("\x0D");
|
||||
$io->write(' |- Downloading package... ' . str_pad(
|
||||
$progress['percent'],
|
||||
5,
|
||||
' ',
|
||||
|
||||
@@ -16,11 +16,11 @@ use Grav\Common\GPM\Installer;
|
||||
use Grav\Common\GPM\Response;
|
||||
use Grav\Common\GPM\Upgrader;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use Grav\Console\GpmCommand;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use ZipArchive;
|
||||
use function is_callable;
|
||||
use function strlen;
|
||||
|
||||
@@ -28,29 +28,23 @@ use function strlen;
|
||||
* Class SelfupgradeCommand
|
||||
* @package Grav\Console\Gpm
|
||||
*/
|
||||
class SelfupgradeCommand extends ConsoleCommand
|
||||
class SelfupgradeCommand extends GpmCommand
|
||||
{
|
||||
/** @var array */
|
||||
protected $data;
|
||||
|
||||
/** @var string */
|
||||
protected $file;
|
||||
|
||||
/** @var array */
|
||||
protected $types = ['plugins', 'themes'];
|
||||
|
||||
/** @var string */
|
||||
private $tmp;
|
||||
|
||||
/** @var Upgrader */
|
||||
private $upgrader;
|
||||
|
||||
/** @var string */
|
||||
protected $all_yes;
|
||||
|
||||
/** @var string */
|
||||
protected $overwrite;
|
||||
|
||||
/** @var int */
|
||||
protected $timeout;
|
||||
|
||||
@@ -96,18 +90,20 @@ class SelfupgradeCommand extends ConsoleCommand
|
||||
*/
|
||||
protected function serve(): int
|
||||
{
|
||||
if (!class_exists(\ZipArchive::class)) {
|
||||
$io = new SymfonyStyle($this->input, $this->output);
|
||||
$input = $this->getInput();
|
||||
$io = $this->getIO();
|
||||
|
||||
if (!class_exists(ZipArchive::class)) {
|
||||
$io->title('GPM Self Upgrade');
|
||||
$io->error('php-zip extension needs to be enabled!');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->upgrader = new Upgrader($this->input->getOption('force'));
|
||||
$this->all_yes = $this->input->getOption('all-yes');
|
||||
$this->overwrite = $this->input->getOption('overwrite');
|
||||
$this->timeout = (int) $this->input->getOption('timeout');
|
||||
$this->upgrader = new Upgrader($input->getOption('force'));
|
||||
$this->all_yes = $input->getOption('all-yes');
|
||||
$this->overwrite = $input->getOption('overwrite');
|
||||
$this->timeout = (int) $input->getOption('timeout');
|
||||
|
||||
$this->displayGPMRelease();
|
||||
|
||||
@@ -118,28 +114,28 @@ class SelfupgradeCommand extends ConsoleCommand
|
||||
$release = strftime('%c', strtotime($this->upgrader->getReleaseDate()));
|
||||
|
||||
if (!$this->upgrader->meetsRequirements()) {
|
||||
$this->output->writeln('<red>ATTENTION:</red>');
|
||||
$this->output->writeln(' Grav has increased the minimum PHP requirement.');
|
||||
$this->output->writeln(' You are currently running PHP <red>' . phpversion() . '</red>, but PHP <green>' . $this->upgrader->minPHPVersion() . '</green> is required.');
|
||||
$this->output->writeln(' Additional information: <white>http://getgrav.org/blog/changing-php-requirements</white>');
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('Selfupgrade aborted.');
|
||||
$this->output->writeln('');
|
||||
$io->writeln('<red>ATTENTION:</red>');
|
||||
$io->writeln(' Grav has increased the minimum PHP requirement.');
|
||||
$io->writeln(' You are currently running PHP <red>' . phpversion() . '</red>, but PHP <green>' . $this->upgrader->minPHPVersion() . '</green> is required.');
|
||||
$io->writeln(' Additional information: <white>http://getgrav.org/blog/changing-php-requirements</white>');
|
||||
$io->newLine();
|
||||
$io->writeln('Selfupgrade aborted.');
|
||||
$io->newLine();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!$this->overwrite && !$this->upgrader->isUpgradable()) {
|
||||
$this->output->writeln("You are already running the latest version of Grav (v{$local}) released on {$release}");
|
||||
$io->writeln("You are already running the latest version of Grav (v{$local}) released on {$release}");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Installer::isValidDestination(GRAV_ROOT . '/system');
|
||||
if (Installer::IS_LINK === Installer::lastErrorCode()) {
|
||||
$this->output->writeln('<red>ATTENTION:</red> Grav is symlinked, cannot upgrade, aborting...');
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln("You are currently running a symbolically linked Grav v{$local}. Latest available is v{$remote}.");
|
||||
$io->writeln('<red>ATTENTION:</red> Grav is symlinked, cannot upgrade, aborting...');
|
||||
$io->newLine();
|
||||
$io->writeln("You are currently running a symbolically linked Grav v{$local}. Latest available is v{$remote}.");
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -147,65 +143,63 @@ class SelfupgradeCommand extends ConsoleCommand
|
||||
// not used but preloaded just in case!
|
||||
new ArrayInput([]);
|
||||
|
||||
$questionHelper = $this->getHelper('question');
|
||||
|
||||
$this->output->writeln("Grav v<cyan>{$remote}</cyan> is now available [release date: {$release}].");
|
||||
$this->output->writeln('You are currently using v<cyan>' . GRAV_VERSION . '</cyan>.');
|
||||
$io->writeln("Grav v<cyan>{$remote}</cyan> is now available [release date: {$release}].");
|
||||
$io->writeln('You are currently using v<cyan>' . GRAV_VERSION . '</cyan>.');
|
||||
|
||||
if (!$this->all_yes) {
|
||||
$question = new ConfirmationQuestion(
|
||||
'Would you like to read the changelog before proceeding? [y|N] ',
|
||||
false
|
||||
);
|
||||
$answer = $questionHelper->ask($this->input, $this->output, $question);
|
||||
$answer = $io->askQuestion($question);
|
||||
|
||||
if ($answer) {
|
||||
$changelog = $this->upgrader->getChangelog(GRAV_VERSION);
|
||||
|
||||
$this->output->writeln('');
|
||||
$io->newLine();
|
||||
foreach ($changelog as $version => $log) {
|
||||
$title = $version . ' [' . $log['date'] . ']';
|
||||
$content = preg_replace_callback('/\d\.\s\[\]\(#(.*)\)/', static function ($match) {
|
||||
return "\n" . ucfirst($match[1]) . ':';
|
||||
}, $log['content']);
|
||||
|
||||
$this->output->writeln($title);
|
||||
$this->output->writeln(str_repeat('-', strlen($title)));
|
||||
$this->output->writeln($content);
|
||||
$this->output->writeln('');
|
||||
$io->writeln($title);
|
||||
$io->writeln(str_repeat('-', strlen($title)));
|
||||
$io->writeln($content);
|
||||
$io->newLine();
|
||||
}
|
||||
|
||||
$question = new ConfirmationQuestion('Press [ENTER] to continue.', true);
|
||||
$questionHelper->ask($this->input, $this->output, $question);
|
||||
$io->askQuestion($question);
|
||||
}
|
||||
|
||||
$question = new ConfirmationQuestion('Would you like to upgrade now? [y|N] ', false);
|
||||
$answer = $questionHelper->ask($this->input, $this->output, $question);
|
||||
$answer = $io->askQuestion($question);
|
||||
|
||||
if (!$answer) {
|
||||
$this->output->writeln('Aborting...');
|
||||
$io->writeln('Aborting...');
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln("Preparing to upgrade to v<cyan>{$remote}</cyan>..");
|
||||
$io->newLine();
|
||||
$io->writeln("Preparing to upgrade to v<cyan>{$remote}</cyan>..");
|
||||
|
||||
$this->output->write(" |- Downloading upgrade [{$this->formatBytes($update['size'])}]... 0%");
|
||||
$io->write(" |- Downloading upgrade [{$this->formatBytes($update['size'])}]... 0%");
|
||||
$this->file = $this->download($update);
|
||||
|
||||
$this->output->write(' |- Installing upgrade... ');
|
||||
$io->write(' |- Installing upgrade... ');
|
||||
$installation = $this->upgrade();
|
||||
|
||||
$error = 0;
|
||||
if (!$installation) {
|
||||
$this->output->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
$this->output->writeln('');
|
||||
$io->writeln(" '- <red>Installation failed or aborted.</red>");
|
||||
$io->newLine();
|
||||
$error = 1;
|
||||
} else {
|
||||
$this->output->writeln(" '- <green>Success!</green> ");
|
||||
$this->output->writeln('');
|
||||
$io->writeln(" '- <green>Success!</green> ");
|
||||
$io->newLine();
|
||||
}
|
||||
|
||||
// clear cache after successful upgrade
|
||||
@@ -218,8 +212,10 @@ class SelfupgradeCommand extends ConsoleCommand
|
||||
* @param array $package
|
||||
* @return string
|
||||
*/
|
||||
private function download($package)
|
||||
private function download(array $package): string
|
||||
{
|
||||
$io = $this->getIO();
|
||||
|
||||
$tmp_dir = Grav::instance()['locator']->findResource('tmp://', true, true);
|
||||
$this->tmp = $tmp_dir . '/Grav-' . uniqid('', false);
|
||||
$options = [
|
||||
@@ -235,9 +231,9 @@ class SelfupgradeCommand extends ConsoleCommand
|
||||
|
||||
Folder::create($this->tmp);
|
||||
|
||||
$this->output->write("\x0D");
|
||||
$this->output->write(" |- Downloading upgrade [{$this->formatBytes($package['size'])}]... 100%");
|
||||
$this->output->writeln('');
|
||||
$io->write("\x0D");
|
||||
$io->write(" |- Downloading upgrade [{$this->formatBytes($package['size'])}]... 100%");
|
||||
$io->newLine();
|
||||
|
||||
file_put_contents($this->tmp . DS . $package['name'], $output);
|
||||
|
||||
@@ -247,8 +243,10 @@ class SelfupgradeCommand extends ConsoleCommand
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function upgrade()
|
||||
private function upgrade(): bool
|
||||
{
|
||||
$io = $this->getIO();
|
||||
|
||||
if ($this->file) {
|
||||
$folder = Installer::unZip($this->file, $this->tmp . '/zip');
|
||||
} else {
|
||||
@@ -264,17 +262,17 @@ class SelfupgradeCommand extends ConsoleCommand
|
||||
}
|
||||
|
||||
if ($errorCode & (Installer::ZIP_OPEN_ERROR | Installer::ZIP_EXTRACT_ERROR)) {
|
||||
$this->output->write("\x0D");
|
||||
$io->write("\x0D");
|
||||
// extra white spaces to clear out the buffer properly
|
||||
$this->output->writeln(' |- Installing upgrade... <red>error</red> ');
|
||||
$this->output->writeln(" | '- " . Installer::lastErrorMsg());
|
||||
$io->writeln(' |- Installing upgrade... <red>error</red> ');
|
||||
$io->writeln(" | '- " . Installer::lastErrorMsg());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->output->write("\x0D");
|
||||
$io->write("\x0D");
|
||||
// extra white spaces to clear out the buffer properly
|
||||
$this->output->writeln(' |- Installing upgrade... <green>ok</green> ');
|
||||
$io->writeln(' |- Installing upgrade... <green>ok</green> ');
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -283,10 +281,12 @@ class SelfupgradeCommand extends ConsoleCommand
|
||||
* @param array $progress
|
||||
* @return void
|
||||
*/
|
||||
public function progress($progress): void
|
||||
public function progress(array $progress): void
|
||||
{
|
||||
$this->output->write("\x0D");
|
||||
$this->output->write(" |- Downloading upgrade [{$this->formatBytes($progress['filesize']) }]... " . str_pad(
|
||||
$io = $this->getIO();
|
||||
|
||||
$io->write("\x0D");
|
||||
$io->write(" |- Downloading upgrade [{$this->formatBytes($progress['filesize']) }]... " . str_pad(
|
||||
$progress['percent'],
|
||||
5,
|
||||
' ',
|
||||
@@ -299,7 +299,7 @@ class SelfupgradeCommand extends ConsoleCommand
|
||||
* @param int $precision
|
||||
* @return string
|
||||
*/
|
||||
public function formatBytes($size, $precision = 2)
|
||||
public function formatBytes($size, int $precision = 2): string
|
||||
{
|
||||
$base = log($size) / log(1024);
|
||||
$suffixes = array('', 'k', 'M', 'G', 'T');
|
||||
@@ -310,9 +310,10 @@ class SelfupgradeCommand extends ConsoleCommand
|
||||
/**
|
||||
* @param string $zip
|
||||
* @param string $folder
|
||||
* @param false $keepFolder
|
||||
* @param bool $keepFolder
|
||||
* @return void
|
||||
*/
|
||||
private function upgradeGrav($zip, $folder, $keepFolder = false)
|
||||
private function upgradeGrav(string $zip, string $folder, bool $keepFolder = false): void
|
||||
{
|
||||
static $ignores = [
|
||||
'backup',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user