diff --git a/.editorconfig b/.editorconfig index 3b95e6dba..da1477309 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,5 +13,5 @@ indent_style = space indent_size = 4 # 2 space indentation -[*.{yaml,.yml}] +[*.{yaml,yml}] indent_size = 2 diff --git a/CHANGELOG.md b/CHANGELOG.md index c7e59d92a..bc889c147 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ +# v1.6.21 +## 02/11/2020 + +1. [](#new) + * Added `ConsoleCommand::setLanguage()` method to set language to be used from CLI + * Added `ConsoleCommand::initializeGrav()` method to properly set up Grav instance to be used from CLI + * Added `ConsoleCommand::initializePlugins()`method to properly set up all plugins to be used from CLI + * Added `ConsoleCommand::initializeThemes()`method to properly set up current theme to be used from CLI + * Added `ConsoleCommand::initializePages()` method to properly set up pages to be used from CLI +1. [](#improved) + * Vendor updates +1. [](#bugfix) + * Fixed `bin/plugin` CLI calling `$themes->init()` way too early (removed it, use above methods instead) + * Fixed call to `$grav['page']` crashing CLI + * Fixed encoding problems when PHP INI setting `default_charset` is not `utf-8` [#2154](https://github.com/getgrav/grav/issues/2154) + # v1.6.20 -## 03/02/2020 +## 02/03/2020 1. [](#bugfix) * Fixed incorrect routing caused by `str_replace()` in `Uri::init()` [#2754](https://github.com/getgrav/grav/issues/2754) diff --git a/bin/gpm b/bin/gpm index 7ad1e4ac5..592bb833e 100755 --- a/bin/gpm +++ b/bin/gpm @@ -28,6 +28,13 @@ if (!ini_get('date.timezone')) { date_default_timezone_set('UTC'); } +// Set internal encoding. +if (!\extension_loaded('mbstring')) { + die("'mbstring' extension is not loaded. This is required for Grav to run correctly"); +} +@ini_set('default_charset', 'UTF-8'); +mb_internal_encoding('UTF-8'); + if (!file_exists(GRAV_ROOT . '/index.php')) { exit('FATAL: Must be run from ROOT directory of Grav!'); } @@ -55,7 +62,7 @@ $grav->setup($environment); $grav['config']->init(); $grav['uri']->init(); -$grav['users']; +$grav['accounts']; $app = new Application('Grav Package Manager', GRAV_VERSION); $app->addCommands(array( diff --git a/bin/grav b/bin/grav index 7120218c0..b1e4c90e5 100755 --- a/bin/grav +++ b/bin/grav @@ -25,6 +25,17 @@ if (version_compare($ver = PHP_VERSION, $req = GRAV_PHP_MIN, '<')) { exit(sprintf("You are running PHP %s, but Grav needs at least PHP %s to run.\n", $ver, $req)); } +if (!ini_get('date.timezone')) { + date_default_timezone_set('UTC'); +} + +// Set internal encoding. +if (!\extension_loaded('mbstring')) { + die("'mbstring' extension is not loaded. This is required for Grav to run correctly"); +} +@ini_set('default_charset', 'UTF-8'); +mb_internal_encoding('UTF-8'); + $climate = new League\CLImate\CLImate; $climate->arguments->add([ 'environment' => [ @@ -42,10 +53,6 @@ $environment = $climate->arguments->get('environment'); $grav = Grav::instance(array('loader' => $autoload)); $grav->setup($environment); -if (!ini_get('date.timezone')) { - date_default_timezone_set('UTC'); -} - if (!file_exists(GRAV_ROOT . '/index.php')) { exit('FATAL: Must be run from ROOT directory of Grav!'); } diff --git a/bin/plugin b/bin/plugin index 3c431e379..737eb9105 100755 --- a/bin/plugin +++ b/bin/plugin @@ -32,6 +32,13 @@ if (!ini_get('date.timezone')) { date_default_timezone_set('UTC'); } +// Set internal encoding. +if (!\extension_loaded('mbstring')) { + die("'mbstring' extension is not loaded. This is required for Grav to run correctly"); +} +@ini_set('default_charset', 'UTF-8'); +mb_internal_encoding('UTF-8'); + if (!file_exists(GRAV_ROOT . '/index.php')) { exit('FATAL: Must be run from ROOT directory of Grav!'); } @@ -51,12 +58,7 @@ $environment = $climate->arguments->get('environment'); $grav = Grav::instance(array('loader' => $autoload)); $grav->setup($environment); - -$grav['config']->init(); -$grav['uri']->init(); -$grav['users']; -$grav['plugins']->init(); -$grav['themes']->init(); +$grav->initializeCli(); $app = new Application('Grav Plugins Commands', GRAV_VERSION); $pattern = '([A-Z]\w+Command\.php)'; diff --git a/composer.lock b/composer.lock index a500e73f1..1d740434c 100644 --- a/composer.lock +++ b/composer.lock @@ -2518,16 +2518,16 @@ }, { "name": "twig/twig", - "version": "v1.42.4", + "version": "v1.42.5", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "e587180584c3d2d6cb864a0454e777bb6dcb6152" + "reference": "87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/e587180584c3d2d6cb864a0454e777bb6dcb6152", - "reference": "e587180584c3d2d6cb864a0454e777bb6dcb6152", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e", + "reference": "87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e", "shasum": "" }, "require": { @@ -2536,8 +2536,7 @@ }, "require-dev": { "psr/container": "^1.0", - "symfony/debug": "^3.4|^4.2", - "symfony/phpunit-bridge": "^4.4@dev|^5.0" + "symfony/phpunit-bridge": "^4.4|^5.0" }, "type": "library", "extra": { @@ -2566,7 +2565,6 @@ }, { "name": "Twig Team", - "homepage": "https://twig.symfony.com/contributors", "role": "Contributors" }, { @@ -2580,7 +2578,7 @@ "keywords": [ "templating" ], - "time": "2019-11-11T16:49:32+00:00" + "time": "2020-02-11T05:59:23+00:00" }, { "name": "willdurand/negotiation", @@ -3562,16 +3560,16 @@ }, { "name": "nette/php-generator", - "version": "v3.3.3", + "version": "v3.3.4", "source": { "type": "git", "url": "https://github.com/nette/php-generator.git", - "reference": "a4ff22c91681fefaa774cf952a2b69c2ec9477c1" + "reference": "8fe7e699dca7db186f56d75800cb1ec32e39c856" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/php-generator/zipball/a4ff22c91681fefaa774cf952a2b69c2ec9477c1", - "reference": "a4ff22c91681fefaa774cf952a2b69c2ec9477c1", + "url": "https://api.github.com/repos/nette/php-generator/zipball/8fe7e699dca7db186f56d75800cb1ec32e39c856", + "reference": "8fe7e699dca7db186f56d75800cb1ec32e39c856", "shasum": "" }, "require": { @@ -3618,7 +3616,7 @@ "php", "scaffolding" ], - "time": "2020-01-20T11:40:42+00:00" + "time": "2020-02-09T14:39:09+00:00" }, { "name": "nette/robot-loader", @@ -3741,16 +3739,16 @@ }, { "name": "nette/utils", - "version": "v3.1.0", + "version": "v3.1.1", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "d6cd63d77dd9a85c3a2fae707e1255e44c2bc182" + "reference": "2c17d16d8887579ae1c0898ff94a3668997fd3eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/d6cd63d77dd9a85c3a2fae707e1255e44c2bc182", - "reference": "d6cd63d77dd9a85c3a2fae707e1255e44c2bc182", + "url": "https://api.github.com/repos/nette/utils/zipball/2c17d16d8887579ae1c0898ff94a3668997fd3eb", + "reference": "2c17d16d8887579ae1c0898ff94a3668997fd3eb", "shasum": "" }, "require": { @@ -3784,8 +3782,8 @@ "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause", - "GPL-2.0", - "GPL-3.0" + "GPL-2.0-only", + "GPL-3.0-only" ], "authors": [ { @@ -3815,7 +3813,7 @@ "utility", "validation" ], - "time": "2020-01-03T18:13:31+00:00" + "time": "2020-02-09T14:10:55+00:00" }, { "name": "nikic/php-parser", diff --git a/index.php b/index.php index 64154df89..7be4df1c6 100644 --- a/index.php +++ b/index.php @@ -20,6 +20,16 @@ if (PHP_SAPI === 'cli-server' && !isset($_SERVER['PHP_CLI_ROUTER'])) { die("PHP webserver requires a router to run Grav, please use:
php -S {$_SERVER['SERVER_NAME']}:{$_SERVER['SERVER_PORT']} system/router.php");
}
+// Set timezone to default, falls back to system if php.ini not set
+date_default_timezone_set(@date_default_timezone_get());
+
+// Set internal encoding.
+if (!\extension_loaded('mbstring')) {
+ die("'mbstring' extension is not loaded. This is required for Grav to run correctly");
+}
+@ini_set('default_charset', 'UTF-8');
+mb_internal_encoding('UTF-8');
+
// Ensure vendor libraries exist
$autoload = __DIR__ . '/vendor/autoload.php';
if (!is_file($autoload)) {
@@ -32,15 +42,6 @@ $loader = require $autoload;
use Grav\Common\Grav;
use RocketTheme\Toolbox\Event\Event;
-// Set timezone to default, falls back to system if php.ini not set
-date_default_timezone_set(@date_default_timezone_get());
-
-// Set internal encoding if mbstring loaded
-if (!\extension_loaded('mbstring')) {
- die("'mbstring' extension is not loaded. This is required for Grav to run correctly");
-}
-mb_internal_encoding('UTF-8');
-
// Get the Grav instance
$grav = Grav::instance(
array(
diff --git a/system/defines.php b/system/defines.php
index c472c958b..1dfa66890 100644
--- a/system/defines.php
+++ b/system/defines.php
@@ -8,7 +8,7 @@
// Some standard defines
define('GRAV', true);
-define('GRAV_VERSION', '1.6.20');
+define('GRAV_VERSION', '1.6.21');
define('GRAV_TESTING', false);
define('DS', '/');
diff --git a/system/src/Grav/Common/Grav.php b/system/src/Grav/Common/Grav.php
index 2ad278e6c..2f9b37329 100644
--- a/system/src/Grav/Common/Grav.php
+++ b/system/src/Grav/Common/Grav.php
@@ -170,6 +170,29 @@ class Grav extends Container
return $this;
}
+ /**
+ * Initialize CLI environment.
+ *
+ * Call after `$grav->setup($environment)`
+ *
+ * - Load configuration
+ * - Disable debugger
+ * - Set timezone, locale
+ * - Load plugins
+ * - Set Users type to be used in the site
+ *
+ * This method WILL NOT initialize assets, twig or pages.
+ *
+ * @param string|null $environment
+ * @return $this
+ */
+ public function initializeCli()
+ {
+ InitializeProcessor::initializeCli($this);
+
+ return $this;
+ }
+
/**
* Process a request
*/
diff --git a/system/src/Grav/Common/Page/Page.php b/system/src/Grav/Common/Page/Page.php
index 75bf45340..d7531df97 100644
--- a/system/src/Grav/Common/Page/Page.php
+++ b/system/src/Grav/Common/Page/Page.php
@@ -608,12 +608,12 @@ class Page implements PageInterface
return $content;
}
- return mb_strimwidth($content, 0, $size, '...', 'utf-8');
+ return mb_strimwidth($content, 0, $size, '...', 'UTF-8');
}
$summary = Utils::truncateHtml($content, $size);
- return html_entity_decode($summary);
+ return html_entity_decode($summary, ENT_COMPAT | ENT_HTML401, 'UTF-8');
}
/**
diff --git a/system/src/Grav/Common/Processors/InitializeProcessor.php b/system/src/Grav/Common/Processors/InitializeProcessor.php
index fe2869ce7..eca7c5442 100644
--- a/system/src/Grav/Common/Processors/InitializeProcessor.php
+++ b/system/src/Grav/Common/Processors/InitializeProcessor.php
@@ -10,6 +10,7 @@
namespace Grav\Common\Processors;
use Grav\Common\Config\Config;
+use Grav\Common\Grav;
use Grav\Common\Uri;
use Grav\Common\Utils;
use Grav\Framework\Session\Exceptions\SessionException;
@@ -22,6 +23,22 @@ class InitializeProcessor extends ProcessorBase
public $id = 'init';
public $title = 'Initialize';
+ /** @var bool */
+ private static $cli_initialized = false;
+
+ /**
+ * @param Grav $grav
+ */
+ public static function initializeCli(Grav $grav)
+ {
+ if (!static::$cli_initialized) {
+ static::$cli_initialized = true;
+
+ $instance = new static($grav);
+ $instance->processCli();
+ }
+ }
+
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
{
$this->startTimer();
@@ -77,4 +94,35 @@ class InitializeProcessor extends ProcessorBase
return $handler->handle($request);
}
+
+ public function processCli(): void
+ {
+ // Load configuration.
+ $this->container['config']->init();
+ $this->container['plugins']->setup();
+
+ // Disable debugger.
+ $this->container['debugger']->enabled(false);
+
+ // Set timezone, locale.
+ /** @var Config $config */
+ $config = $this->container['config'];
+ $timezone = $config->get('system.timezone');
+ if ($timezone) {
+ date_default_timezone_set($timezone);
+ }
+ $this->container->setLocale();
+
+ // Load plugins.
+ $this->container['plugins']->init();
+
+ // Initialize URI.
+ /** @var Uri $uri */
+ $uri = $this->container['uri'];
+ $uri->init();
+
+ // Load accounts.
+ // TODO: remove in 2.0.
+ $this->container['accounts'];
+ }
}
diff --git a/system/src/Grav/Common/Service/PagesServiceProvider.php b/system/src/Grav/Common/Service/PagesServiceProvider.php
index 0a37e831d..f425b6d9c 100644
--- a/system/src/Grav/Common/Service/PagesServiceProvider.php
+++ b/system/src/Grav/Common/Service/PagesServiceProvider.php
@@ -26,6 +26,19 @@ class PagesServiceProvider implements ServiceProviderInterface
return new Pages($c);
};
+ if (\defined('GRAV_CLI')) {
+ $container['page'] = static function ($c) {
+ $path = $c['locator']->findResource('system://pages/notfound.md');
+ $page = new Page();
+ $page->init(new \SplFileInfo($path));
+ $page->routable(false);
+
+ return $page;
+ };
+
+ return;
+ }
+
$container['page'] = function ($c) {
/** @var Grav $c */
diff --git a/system/src/Grav/Common/Twig/TwigExtension.php b/system/src/Grav/Common/Twig/TwigExtension.php
index f84923ebd..58b1d6d68 100644
--- a/system/src/Grav/Common/Twig/TwigExtension.php
+++ b/system/src/Grav/Common/Twig/TwigExtension.php
@@ -1055,7 +1055,7 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
*/
public function jsonDecodeFilter($str, $assoc = false, $depth = 512, $options = 0)
{
- return json_decode(html_entity_decode($str), $assoc, $depth, $options);
+ return json_decode(html_entity_decode($str, ENT_COMPAT | ENT_HTML401, 'UTF-8'), $assoc, $depth, $options);
}
/**
diff --git a/system/src/Grav/Common/Uri.php b/system/src/Grav/Common/Uri.php
index f875ab3de..10e85a5cc 100644
--- a/system/src/Grav/Common/Uri.php
+++ b/system/src/Grav/Common/Uri.php
@@ -306,7 +306,7 @@ class Uri
public function param($id)
{
if (isset($this->params[$id])) {
- return html_entity_decode(rawurldecode($this->params[$id]));
+ return html_entity_decode(rawurldecode($this->params[$id]), ENT_COMPAT | ENT_HTML401, 'UTF-8');
}
return false;
diff --git a/system/src/Grav/Console/ConsoleCommand.php b/system/src/Grav/Console/ConsoleCommand.php
index 2c63eaf54..c0fd9ad42 100644
--- a/system/src/Grav/Console/ConsoleCommand.php
+++ b/system/src/Grav/Console/ConsoleCommand.php
@@ -10,6 +10,10 @@
namespace Grav\Console;
use Grav\Common\Grav;
+use Grav\Common\Language\Language;
+use Grav\Common\Page\Page;
+use Grav\Common\Processors\InitializeProcessor;
+use RocketTheme\Toolbox\Event\Event;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@@ -18,6 +22,13 @@ class ConsoleCommand extends Command
{
use ConsoleTrait;
+ /** @var bool */
+ private $plugins_initialized = false;
+ /** @var bool */
+ private $themes_initialized = false;
+ /** @var bool */
+ private $pages_initialized = false;
+
/**
* @param InputInterface $input
* @param OutputInterface $output
@@ -31,12 +42,140 @@ class ConsoleCommand extends Command
}
/**
- *
+ * Override with your implementation.
*/
protected function serve()
{
}
+ /**
+ * Initialize Grav.
+ *
+ * - Load configuration
+ * - Disable debugger
+ * - Set timezone, locale
+ * - Load plugins
+ * - Set Users type to be used in the site
+ *
+ * Safe to be called multiple times.
+ *
+ * @return $this
+ */
+ final protected function initializeGrav()
+ {
+ InitializeProcessor::initializeCli(Grav::instance());
+
+ return $this;
+ }
+
+ /**
+ * Set language to be used in CLI.
+ *
+ * @param string|null $code
+ */
+ final protected function setLanguage(string $code = null)
+ {
+ $this->initializeGrav();
+
+ $grav = Grav::instance();
+ /** @var Language $language */
+ $language = $grav['language'];
+ if ($language->enabled()) {
+ if ($code && $language->validate($code)) {
+ $language->setActive($code);
+ } else {
+ $language->setActive($language->getDefault());
+ }
+ }
+ }
+
+ /**
+ * Properly initialize plugins.
+ *
+ * - call $this->initializeGrav()
+ * - call onPluginsInitialized event
+ *
+ * Safe to be called multiple times.
+ *
+ * @return $this
+ */
+ final protected function initializePlugins()
+ {
+ if (!$this->plugins_initialized) {
+ $this->plugins_initialized = true;
+
+ $this->initializeGrav();
+
+ // Initialize plugins.
+ $grav = Grav::instance();
+ $grav->fireEvent('onPluginsInitialized');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Properly initialize themes.
+ *
+ * - call $this->initializePlugins()
+ * - initialize theme (call onThemeInitialized event)
+ *
+ * Safe to be called multiple times.
+ *
+ * @return $this
+ */
+ final protected function initializeThemes()
+ {
+ if (!$this->themes_initialized) {
+ $this->themes_initialized = true;
+
+ $this->initializePlugins();
+
+ // Initialize themes.
+ $grav = Grav::instance();
+ $grav['themes']->init();
+ }
+
+ return $this;
+ }
+
+ /**
+ * Properly initialize pages.
+ *
+ * - call $this->initializeThemes()
+ * - initialize assets (call onAssetsInitialized event)
+ * - initialize twig (calls the twig events)
+ * - initialize pages (calls onPagesInitialized event)
+ *
+ * Safe to be called multiple times.
+ *
+ * @return $this
+ */
+ final protected function initializePages()
+ {
+ if (!$this->pages_initialized) {
+ $this->pages_initialized = true;
+
+ $this->initializeThemes();
+
+ $grav = Grav::instance();
+
+ // Initialize assets.
+ $grav['assets']->init();
+ $grav->fireEvent('onAssetsInitialized');
+
+ // Initialize twig.
+ $grav['twig']->init();
+
+ // Initialize pages.
+ $pages = $grav['pages'];
+ $pages->init();
+ $grav->fireEvent('onPagesInitialized', new Event(['pages' => $pages]));
+ }
+
+ return $this;
+ }
+
protected function displayGPMRelease()
{
$this->output->writeln('');