mirror of
https://github.com/getgrav/grav.git
synced 2025-10-26 07:56:07 +01:00
Feature/scheduler (#2170)
* scheduler first commit * moved jobs to config * got some args working * commands and static methods working.. events hooked up * No longer dependent on `exec()`.. uses Symfony\Process * More improvements * support getAt() * Make inflector available in DI * Fix for inflector * store job run states * more improvements including cron twig function * Add scheduler to event + cleanup * improvements to the CLI command * Added id field * use proper func * Added email * Fix quotes * Updated built-in composer * Better command for adding the scheduler cron via terminal * Fixed typo and added cron language * Added Cron class to make at -> human readable date possible * Added some checks when there are no jobs * Added method to get CronExpression * Revamped with Symfony 4.1 CLI updates
This commit is contained in:
Binary file not shown.
8
bin/grav
8
bin/grav
@@ -6,7 +6,9 @@ if (!file_exists(__DIR__ . '/../vendor')){
|
||||
require_once __DIR__ . '/../system/src/Grav/Common/Composer.php';
|
||||
}
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Composer;
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
if (!file_exists(__DIR__ . '/../vendor')){
|
||||
// Before we can even start, we need to run composer first
|
||||
@@ -16,9 +18,8 @@ if (!file_exists(__DIR__ . '/../vendor')){
|
||||
echo "\n\n";
|
||||
}
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
$autoload = require_once(__DIR__ . '/../vendor/autoload.php');
|
||||
Grav::instance(array('loader' => $autoload));
|
||||
|
||||
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));
|
||||
@@ -41,5 +42,6 @@ $app->addCommands(array(
|
||||
new \Grav\Console\Cli\ClearCacheCommand(),
|
||||
new \Grav\Console\Cli\BackupCommand(),
|
||||
new \Grav\Console\Cli\NewProjectCommand(),
|
||||
new \Grav\Console\Cli\SchedulerCommand(),
|
||||
));
|
||||
$app->run();
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"symfony/console": "~4.1",
|
||||
"symfony/event-dispatcher": "~4.1",
|
||||
"symfony/var-dumper": "~4.1",
|
||||
"symfony/process": "~4.1",
|
||||
"doctrine/cache": "^1.8",
|
||||
"doctrine/collections": "^1.5",
|
||||
"guzzlehttp/psr7": "^1.4",
|
||||
@@ -41,7 +42,8 @@
|
||||
"league/climate": "^3.4",
|
||||
"antoligy/dom-string-iterators": "^1.0",
|
||||
"miljar/php-exif": "^0.6.4",
|
||||
"composer/ca-bundle": "^1.0"
|
||||
"composer/ca-bundle": "^1.0",
|
||||
"dragonmantank/cron-expression": "^1.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "^2.4",
|
||||
|
||||
154
composer.lock
generated
154
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "cb0cecd3938f21e0f46cb68022ab2b88",
|
||||
"content-hash": "ee3b919ebe2a385d66074d22125c4317",
|
||||
"packages": [
|
||||
{
|
||||
"name": "antoligy/dom-string-iterators",
|
||||
@@ -299,6 +299,50 @@
|
||||
],
|
||||
"time": "2018-06-21T15:54:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "dragonmantank/cron-expression",
|
||||
"version": "v1.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dragonmantank/cron-expression.git",
|
||||
"reference": "9504fa9ea681b586028adaaa0877db4aecf32bad"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/9504fa9ea681b586028adaaa0877db4aecf32bad",
|
||||
"reference": "9504fa9ea681b586028adaaa0877db4aecf32bad",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0|~5.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Cron\\": "src/Cron/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
}
|
||||
],
|
||||
"description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due",
|
||||
"keywords": [
|
||||
"cron",
|
||||
"schedule"
|
||||
],
|
||||
"time": "2017-01-23T04:29:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "erusev/parsedown",
|
||||
"version": "1.6.4",
|
||||
@@ -1731,6 +1775,55 @@
|
||||
],
|
||||
"time": "2018-08-06T14:22:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v4.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "86cdb930a6a855b0ab35fb60c1504cb36184f843"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/86cdb930a6a855b0ab35fb60c1504cb36184f843",
|
||||
"reference": "86cdb930a6a855b0ab35fb60c1504cb36184f843",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Process\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Process Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2018-08-03T11:13:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
"version": "v4.1.4",
|
||||
@@ -3052,16 +3145,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "7.3.2",
|
||||
"version": "7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "34705f81bddc3f505b9599a2ef96e2b4315ba9b8"
|
||||
"reference": "1bd5629cccfb2c0a9ef5474b4ff772349e1ec898"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/34705f81bddc3f505b9599a2ef96e2b4315ba9b8",
|
||||
"reference": "34705f81bddc3f505b9599a2ef96e2b4315ba9b8",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1bd5629cccfb2c0a9ef5474b4ff772349e1ec898",
|
||||
"reference": "1bd5629cccfb2c0a9ef5474b4ff772349e1ec898",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3132,7 +3225,7 @@
|
||||
"testing",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2018-08-22T06:39:21+00:00"
|
||||
"time": "2018-09-01T15:49:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/code-unit-reverse-lookup",
|
||||
@@ -3913,55 +4006,6 @@
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2018-07-26T11:24:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v4.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "86cdb930a6a855b0ab35fb60c1504cb36184f843"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/86cdb930a6a855b0ab35fb60c1504cb36184f843",
|
||||
"reference": "86cdb930a6a855b0ab35fb60c1504cb36184f843",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Process\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Process Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2018-08-03T11:13:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
"version": "1.1.0",
|
||||
|
||||
89
composer_BACKUP_46658.json
Normal file
89
composer_BACKUP_46658.json
Normal file
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"name": "getgrav/grav",
|
||||
"type": "project",
|
||||
"description": "Modern, Crazy Fast, Ridiculously Easy and Amazingly Powerful Flat-File CMS",
|
||||
"keywords": ["cms","flat-file cms","flat cms","flatfile cms","php"],
|
||||
"homepage": "http://getgrav.org",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=7.1.3",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-curl": "*",
|
||||
"ext-zip": "*",
|
||||
"symfony/polyfill-iconv": "^1.9",
|
||||
"symfony/polyfill-php72": "^1.9",
|
||||
"symfony/polyfill-php73": "^1.9",
|
||||
|
||||
"psr/simple-cache": "^1.0",
|
||||
"psr/http-message": "^1.0",
|
||||
|
||||
"twig/twig": "~1.35",
|
||||
"erusev/parsedown": "1.6.4",
|
||||
"erusev/parsedown-extra": "~0.7",
|
||||
"symfony/yaml": "~4.1",
|
||||
"symfony/console": "~4.1",
|
||||
"symfony/event-dispatcher": "~4.1",
|
||||
"symfony/var-dumper": "~4.1",
|
||||
"doctrine/cache": "^1.7",
|
||||
"doctrine/collections": "^1.5",
|
||||
"guzzlehttp/psr7": "^1.4",
|
||||
"filp/whoops": "~2.2",
|
||||
|
||||
"matthiasmullie/minify": "^1.3",
|
||||
"monolog/monolog": "~1.0",
|
||||
"gregwar/image": "2.*",
|
||||
"donatj/phpuseragentparser": "~0.10",
|
||||
"pimple/pimple": "~3.2",
|
||||
"rockettheme/toolbox": "~1.4",
|
||||
"maximebf/debugbar": "~1.15",
|
||||
"league/climate": "^3.4",
|
||||
"antoligy/dom-string-iterators": "^1.0",
|
||||
<<<<<<< HEAD
|
||||
"miljar/php-exif": "^0.6.3",
|
||||
"composer/ca-bundle": "^1.0",
|
||||
"dragonmantank/cron-expression": "^1.2",
|
||||
"symfony/process": "^3.4"
|
||||
=======
|
||||
"miljar/php-exif": "^0.6.4",
|
||||
"composer/ca-bundle": "^1.0"
|
||||
>>>>>>> 1.6
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "^2.4",
|
||||
"phpunit/php-code-coverage": "~6.0",
|
||||
"fzaninotto/faker": "^1.8",
|
||||
"victorjonsson/markdowndocs": "dev-master"
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.1.3"
|
||||
}
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/trilbymedia/PHP-Markdown-Documentation-Generator"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Grav\\": "system/src/Grav"
|
||||
},
|
||||
"files": ["system/defines.php"]
|
||||
},
|
||||
"archive": {
|
||||
"exclude": ["VERSION"]
|
||||
},
|
||||
"scripts": {
|
||||
"post-create-project-cmd": "bin/grav install",
|
||||
"test": "vendor/bin/codecept run unit",
|
||||
"test-windows": "vendor\\bin\\codecept run unit"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-develop": "1.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
76
composer_BASE_46658.json
Normal file
76
composer_BASE_46658.json
Normal file
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"name": "getgrav/grav",
|
||||
"type": "project",
|
||||
"description": "Modern, Crazy Fast, Ridiculously Easy and Amazingly Powerful Flat-File CMS",
|
||||
"keywords": ["cms","flat-file cms","flat cms","flatfile cms","php"],
|
||||
"homepage": "http://getgrav.org",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=5.6.4",
|
||||
"twig/twig": "~1.24",
|
||||
"erusev/parsedown": "1.6.4",
|
||||
"erusev/parsedown-extra": "~0.7",
|
||||
"symfony/yaml": "~3.4",
|
||||
"symfony/console": "~3.4",
|
||||
"symfony/event-dispatcher": "~3.4",
|
||||
"symfony/var-dumper": "~3.4",
|
||||
"symfony/polyfill-iconv": "~1.0",
|
||||
"doctrine/cache": "^1.6",
|
||||
"doctrine/collections": "^1.4",
|
||||
"psr/simple-cache": "^1.0",
|
||||
"psr/http-message": "^1.0",
|
||||
"guzzlehttp/psr7": "^1.4",
|
||||
"filp/whoops": "~2.0",
|
||||
"matthiasmullie/minify": "^1.3",
|
||||
"monolog/monolog": "~1.0",
|
||||
"gregwar/image": "2.*",
|
||||
"donatj/phpuseragentparser": "~0.3",
|
||||
"pimple/pimple": "~3.2",
|
||||
"rockettheme/toolbox": "~1.4",
|
||||
"maximebf/debugbar": "~1.10",
|
||||
"ext-mbstring": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-curl": "*",
|
||||
"ext-zip": "*",
|
||||
"league/climate": "^3.2",
|
||||
"antoligy/dom-string-iterators": "^1.0",
|
||||
"miljar/php-exif": "^0.6.3",
|
||||
"composer/ca-bundle": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "^2.1",
|
||||
"phpunit/php-code-coverage": "~2.0",
|
||||
"fzaninotto/faker": "^1.5",
|
||||
"victorjonsson/markdowndocs": "dev-master"
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "5.6.4"
|
||||
}
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/trilbymedia/PHP-Markdown-Documentation-Generator"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Grav\\": "system/src/Grav"
|
||||
},
|
||||
"files": ["system/defines.php"]
|
||||
},
|
||||
"archive": {
|
||||
"exclude": ["VERSION"]
|
||||
},
|
||||
"scripts": {
|
||||
"post-create-project-cmd": "bin/grav install",
|
||||
"test": "vendor/bin/codecept run unit",
|
||||
"test-windows": "vendor\\bin\\codecept run unit"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-develop": "1.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
78
composer_LOCAL_46658.json
Normal file
78
composer_LOCAL_46658.json
Normal file
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"name": "getgrav/grav",
|
||||
"type": "project",
|
||||
"description": "Modern, Crazy Fast, Ridiculously Easy and Amazingly Powerful Flat-File CMS",
|
||||
"keywords": ["cms","flat-file cms","flat cms","flatfile cms","php"],
|
||||
"homepage": "http://getgrav.org",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=5.6.4",
|
||||
"twig/twig": "~1.24",
|
||||
"erusev/parsedown": "1.6.4",
|
||||
"erusev/parsedown-extra": "~0.7",
|
||||
"symfony/yaml": "~3.4",
|
||||
"symfony/console": "~3.4",
|
||||
"symfony/event-dispatcher": "~3.4",
|
||||
"symfony/var-dumper": "~3.4",
|
||||
"symfony/polyfill-iconv": "~1.0",
|
||||
"doctrine/cache": "^1.6",
|
||||
"doctrine/collections": "^1.4",
|
||||
"psr/simple-cache": "^1.0",
|
||||
"psr/http-message": "^1.0",
|
||||
"guzzlehttp/psr7": "^1.4",
|
||||
"filp/whoops": "~2.0",
|
||||
"matthiasmullie/minify": "^1.3",
|
||||
"monolog/monolog": "~1.0",
|
||||
"gregwar/image": "2.*",
|
||||
"donatj/phpuseragentparser": "~0.3",
|
||||
"pimple/pimple": "~3.2",
|
||||
"rockettheme/toolbox": "~1.4",
|
||||
"maximebf/debugbar": "~1.10",
|
||||
"ext-mbstring": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-curl": "*",
|
||||
"ext-zip": "*",
|
||||
"league/climate": "^3.2",
|
||||
"antoligy/dom-string-iterators": "^1.0",
|
||||
"miljar/php-exif": "^0.6.3",
|
||||
"composer/ca-bundle": "^1.0",
|
||||
"dragonmantank/cron-expression": "^1.2",
|
||||
"symfony/process": "^3.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "^2.1",
|
||||
"phpunit/php-code-coverage": "~2.0",
|
||||
"fzaninotto/faker": "^1.5",
|
||||
"victorjonsson/markdowndocs": "dev-master"
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "5.6.4"
|
||||
}
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/trilbymedia/PHP-Markdown-Documentation-Generator"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Grav\\": "system/src/Grav"
|
||||
},
|
||||
"files": ["system/defines.php"]
|
||||
},
|
||||
"archive": {
|
||||
"exclude": ["VERSION"]
|
||||
},
|
||||
"scripts": {
|
||||
"post-create-project-cmd": "bin/grav install",
|
||||
"test": "vendor/bin/codecept run unit",
|
||||
"test-windows": "vendor\\bin\\codecept run unit"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-develop": "1.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
82
composer_REMOTE_46658.json
Normal file
82
composer_REMOTE_46658.json
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"name": "getgrav/grav",
|
||||
"type": "project",
|
||||
"description": "Modern, Crazy Fast, Ridiculously Easy and Amazingly Powerful Flat-File CMS",
|
||||
"keywords": ["cms","flat-file cms","flat cms","flatfile cms","php"],
|
||||
"homepage": "http://getgrav.org",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=7.1.3",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-curl": "*",
|
||||
"ext-zip": "*",
|
||||
"symfony/polyfill-iconv": "^1.9",
|
||||
"symfony/polyfill-php72": "^1.9",
|
||||
"symfony/polyfill-php73": "^1.9",
|
||||
|
||||
"psr/simple-cache": "^1.0",
|
||||
"psr/http-message": "^1.0",
|
||||
|
||||
"twig/twig": "~1.35",
|
||||
"erusev/parsedown": "1.6.4",
|
||||
"erusev/parsedown-extra": "~0.7",
|
||||
"symfony/yaml": "~4.1",
|
||||
"symfony/console": "~4.1",
|
||||
"symfony/event-dispatcher": "~4.1",
|
||||
"symfony/var-dumper": "~4.1",
|
||||
"doctrine/cache": "^1.7",
|
||||
"doctrine/collections": "^1.5",
|
||||
"guzzlehttp/psr7": "^1.4",
|
||||
"filp/whoops": "~2.2",
|
||||
|
||||
"matthiasmullie/minify": "^1.3",
|
||||
"monolog/monolog": "~1.0",
|
||||
"gregwar/image": "2.*",
|
||||
"donatj/phpuseragentparser": "~0.10",
|
||||
"pimple/pimple": "~3.2",
|
||||
"rockettheme/toolbox": "~1.4",
|
||||
"maximebf/debugbar": "~1.15",
|
||||
"league/climate": "^3.4",
|
||||
"antoligy/dom-string-iterators": "^1.0",
|
||||
"miljar/php-exif": "^0.6.4",
|
||||
"composer/ca-bundle": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "^2.4",
|
||||
"phpunit/php-code-coverage": "~6.0",
|
||||
"fzaninotto/faker": "^1.8",
|
||||
"victorjonsson/markdowndocs": "dev-master"
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.1.3"
|
||||
}
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/trilbymedia/PHP-Markdown-Documentation-Generator"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Grav\\": "system/src/Grav"
|
||||
},
|
||||
"files": ["system/defines.php"]
|
||||
},
|
||||
"archive": {
|
||||
"exclude": ["VERSION"]
|
||||
},
|
||||
"scripts": {
|
||||
"post-create-project-cmd": "bin/grav install",
|
||||
"test": "vendor/bin/codecept run unit",
|
||||
"test-windows": "vendor\\bin\\codecept run unit"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-develop": "1.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
96
system/blueprints/config/scheduler.yaml
Normal file
96
system/blueprints/config/scheduler.yaml
Normal file
@@ -0,0 +1,96 @@
|
||||
title: PLUGIN_ADMIN.SCHEDULER
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
tabs:
|
||||
type: tabs
|
||||
class: subtle
|
||||
|
||||
fields:
|
||||
status_tab:
|
||||
type: tab
|
||||
title: Status
|
||||
|
||||
fields:
|
||||
setup_title:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.SCHEDULER_SETUP
|
||||
underline: true
|
||||
|
||||
setup:
|
||||
type: croninstall
|
||||
|
||||
status_title:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.SCHEDULER_STATUS
|
||||
underline: true
|
||||
|
||||
status:
|
||||
type: cronstatus
|
||||
validate:
|
||||
type: commalist
|
||||
|
||||
custom_tab:
|
||||
type: tab
|
||||
title: Custom
|
||||
|
||||
fields:
|
||||
jobs_title:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.SCHEDULER_JOBS
|
||||
underline: false
|
||||
custom_jobs:
|
||||
type: list
|
||||
style: vertical
|
||||
label:
|
||||
classes: cron-job-list
|
||||
key: id
|
||||
fields:
|
||||
.id:
|
||||
type: key
|
||||
label: ID
|
||||
placeholder: 'process-name'
|
||||
validate:
|
||||
required: true
|
||||
pattern: '[a-zа-я0-9_\-]+'
|
||||
max: 20
|
||||
message: 'ID must be lowercase with dashes/underscores only and less than 20 characters'
|
||||
.command:
|
||||
type: text
|
||||
label: Command
|
||||
placeholder: 'cd ~;ls -lah;'
|
||||
validate:
|
||||
required: true
|
||||
.args:
|
||||
type: text
|
||||
label: Extra Arguments
|
||||
.at:
|
||||
type: cron
|
||||
label: Run At
|
||||
help: 'Cron formatted "at" syntax'
|
||||
placeholder: '* * * * *'
|
||||
validate:
|
||||
required: true
|
||||
.output:
|
||||
type: text
|
||||
label: Output File
|
||||
help: 'The path/filename of the output file (from the root of the Grav installation)'
|
||||
placeholder: 'logs/ls-cron.out'
|
||||
.output_mode:
|
||||
type: select
|
||||
label: Output Type
|
||||
help: 'Either append to the same file each run, or overwrite the file with each run'
|
||||
default: append
|
||||
options:
|
||||
append: Append
|
||||
overwrite: Overwrite
|
||||
.email:
|
||||
type: text
|
||||
label: Email
|
||||
help: 'Email to send output to. NOTE: requires output file to be set'
|
||||
placeholder: 'notifications@yoursite.com'
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -93,6 +93,23 @@ NICETIME:
|
||||
MO_PLURAL: mos
|
||||
YR_PLURAL: yrs
|
||||
DEC_PLURAL: decs
|
||||
CRON:
|
||||
EVERY: every
|
||||
EVERY_HOUR: every hour
|
||||
EVERY_MINUTE: every minute
|
||||
EVERY_DAY_OF_WEEK: every day of the week
|
||||
EVERY_DAY_OF_MONTH: every day of the month
|
||||
EVERY_MONTH: every month
|
||||
TEXT_PERIOD: Every <b />
|
||||
TEXT_MINS: ' at <b /> minute(s) past the hour'
|
||||
TEXT_TIME: ' at <b />:<b />'
|
||||
TEXT_DOW: ' on <b />'
|
||||
TEXT_MONTH: ' of <b />'
|
||||
TEXT_DOM: ' on <b />'
|
||||
ERROR1: The tag %s is not supported!
|
||||
ERROR2: Bad number of elements
|
||||
ERROR3: The jquery_element should be set into jqCron settings
|
||||
ERROR4: Unrecognized expression
|
||||
FORM:
|
||||
VALIDATION_FAIL: <b>Validation failed:</b>
|
||||
INVALID_INPUT: Invalid input in
|
||||
|
||||
@@ -40,6 +40,7 @@ class Grav extends Container
|
||||
'cache' => 'Grav\Common\Cache',
|
||||
'Grav\Common\Service\SessionServiceProvider',
|
||||
'plugins' => 'Grav\Common\Plugins',
|
||||
'scheduler' => 'Grav\Common\Scheduler\Scheduler',
|
||||
'themes' => 'Grav\Common\Themes',
|
||||
'twig' => 'Grav\Common\Twig\Twig',
|
||||
'taxonomy' => 'Grav\Common\Taxonomy',
|
||||
@@ -53,6 +54,7 @@ class Grav extends Container
|
||||
'exif' => 'Grav\Common\Helpers\Exif',
|
||||
'Grav\Common\Service\StreamsServiceProvider',
|
||||
'Grav\Common\Service\ConfigServiceProvider',
|
||||
'Grav\Common\Service\InflectorServiceProvider',
|
||||
'inflector' => 'Grav\Common\Inflector',
|
||||
'siteSetupProcessor' => 'Grav\Common\Processors\SiteSetupProcessor',
|
||||
'configurationProcessor' => 'Grav\Common\Processors\ConfigurationProcessor',
|
||||
@@ -61,6 +63,7 @@ class Grav extends Container
|
||||
'initializeProcessor' => 'Grav\Common\Processors\InitializeProcessor',
|
||||
'pluginsProcessor' => 'Grav\Common\Processors\PluginsProcessor',
|
||||
'themesProcessor' => 'Grav\Common\Processors\ThemesProcessor',
|
||||
'schedulerProcessor' => 'Grav\Common\Processors\SchedulerProcessor',
|
||||
'tasksProcessor' => 'Grav\Common\Processors\TasksProcessor',
|
||||
'assetsProcessor' => 'Grav\Common\Processors\AssetsProcessor',
|
||||
'twigProcessor' => 'Grav\Common\Processors\TwigProcessor',
|
||||
@@ -80,6 +83,7 @@ class Grav extends Container
|
||||
'initializeProcessor',
|
||||
'pluginsProcessor',
|
||||
'themesProcessor',
|
||||
'schedulerProcessor',
|
||||
'tasksProcessor',
|
||||
'assetsProcessor',
|
||||
'twigProcessor',
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Grav\Common\Processors;
|
||||
|
||||
class AssetsProcessor extends ProcessorBase implements ProcessorInterface
|
||||
{
|
||||
public $id = 'assets';
|
||||
public $id = '_assets';
|
||||
public $title = 'Assets';
|
||||
|
||||
public function process()
|
||||
|
||||
24
system/src/Grav/Common/Processors/SchedulerProcessor.php
Normal file
24
system/src/Grav/Common/Processors/SchedulerProcessor.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Processors
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Processors;
|
||||
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
|
||||
class SchedulerProcessor extends ProcessorBase implements ProcessorInterface
|
||||
{
|
||||
public $id = '_scheduler';
|
||||
public $title = 'Scheduler';
|
||||
|
||||
public function process()
|
||||
{
|
||||
$scheduler = $this->container['scheduler'];
|
||||
$scheduler->loadSavedJobs();
|
||||
$this->container->fireEvent('onSchedulerInitialized', new Event(['scheduler' => $scheduler]));
|
||||
}
|
||||
}
|
||||
512
system/src/Grav/Common/Scheduler/Cron.php
Normal file
512
system/src/Grav/Common/Scheduler/Cron.php
Normal file
@@ -0,0 +1,512 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Scheduler
|
||||
* @author Originally based on jqCron by Arnaud Buathier <arnaud@arnapou.net> modified for Grav integration
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Scheduler;
|
||||
|
||||
/*
|
||||
* Usage examples :
|
||||
* ----------------
|
||||
*
|
||||
* $cron = new Cron('10-30/5 12 * * *');
|
||||
*
|
||||
* var_dump($cron->getMinutes());
|
||||
* // array(5) {
|
||||
* // [0]=> int(10)
|
||||
* // [1]=> int(15)
|
||||
* // [2]=> int(20)
|
||||
* // [3]=> int(25)
|
||||
* // [4]=> int(30)
|
||||
* // }
|
||||
*
|
||||
* var_dump($cron->getText('fr'));
|
||||
* // string(32) "Chaque jour à 12:10,15,20,25,30"
|
||||
*
|
||||
* var_dump($cron->getText('en'));
|
||||
* // string(30) "Every day at 12:10,15,20,25,30"
|
||||
*
|
||||
* var_dump($cron->getType());
|
||||
* // string(3) "day"
|
||||
*
|
||||
* var_dump($cron->getCronHours());
|
||||
* // string(2) "12"
|
||||
*
|
||||
* var_dump($cron->matchExact(new \DateTime('2012-07-01 13:25:10')));
|
||||
* // bool(false)
|
||||
*
|
||||
* var_dump($cron->matchExact(new \DateTime('2012-07-01 12:15:20')));
|
||||
* // bool(true)
|
||||
*
|
||||
* var_dump($cron->matchWithMargin(new \DateTime('2012-07-01 12:32:50'), -3, 5));
|
||||
* // bool(true)
|
||||
*/
|
||||
class Cron {
|
||||
const TYPE_UNDEFINED = '';
|
||||
const TYPE_MINUTE = 'minute';
|
||||
const TYPE_HOUR = 'hour';
|
||||
const TYPE_DAY = 'day';
|
||||
const TYPE_WEEK = 'week';
|
||||
const TYPE_MONTH = 'month';
|
||||
const TYPE_YEAR = 'year';
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $texts = array(
|
||||
'fr' => array(
|
||||
'empty' => '-tout-',
|
||||
'name_minute' => 'minute',
|
||||
'name_hour' => 'heure',
|
||||
'name_day' => 'jour',
|
||||
'name_week' => 'semaine',
|
||||
'name_month' => 'mois',
|
||||
'name_year' => 'année',
|
||||
'text_period' => 'Chaque %s',
|
||||
'text_mins' => 'à %s minutes',
|
||||
'text_time' => 'à %s:%s',
|
||||
'text_dow' => 'le %s',
|
||||
'text_month' => 'de %s',
|
||||
'text_dom' => 'le %s',
|
||||
'weekdays' => array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'),
|
||||
'months' => array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'),
|
||||
),
|
||||
'en' => array(
|
||||
'empty' => '-all-',
|
||||
'name_minute' => 'minute',
|
||||
'name_hour' => 'hour',
|
||||
'name_day' => 'day',
|
||||
'name_week' => 'week',
|
||||
'name_month' => 'month',
|
||||
'name_year' => 'year',
|
||||
'text_period' => 'Every %s',
|
||||
'text_mins' => 'at %s minutes past the hour',
|
||||
'text_time' => 'at %s:%s',
|
||||
'text_dow' => 'on %s',
|
||||
'text_month' => 'of %s',
|
||||
'text_dom' => 'on the %s',
|
||||
'weekdays' => array('monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'),
|
||||
'months' => array('january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'),
|
||||
),
|
||||
);
|
||||
/**
|
||||
* min hour dom month dow
|
||||
* @var string
|
||||
*/
|
||||
protected $cron = '';
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $minutes = array();
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $hours = array();
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $months = array();
|
||||
/**
|
||||
* 0-7 : sunday, monday, ... saturday, sunday
|
||||
* @var array
|
||||
*/
|
||||
protected $dow = array();
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dom = array();
|
||||
/**
|
||||
*
|
||||
* @param string $cron
|
||||
*/
|
||||
public function __construct($cron = null) {
|
||||
if (!empty($cron)) {
|
||||
$this->setCron($cron);
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCron() {
|
||||
return implode(' ', array(
|
||||
$this->getCronMinutes(),
|
||||
$this->getCronHours(),
|
||||
$this->getCronDaysOfMonth(),
|
||||
$this->getCronMonths(),
|
||||
$this->getCronDaysOfWeek(),
|
||||
));
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param string $lang 'fr' or 'en'
|
||||
* @return string
|
||||
*/
|
||||
public function getText($lang) {
|
||||
// check lang
|
||||
if (!isset($this->texts[$lang])) {
|
||||
return $this->getCron();
|
||||
}
|
||||
$texts = $this->texts[$lang];
|
||||
// check type
|
||||
$type = $this->getType();
|
||||
if ($type == self::TYPE_UNDEFINED) {
|
||||
return $this->getCron();
|
||||
}
|
||||
// init
|
||||
$elements = array();
|
||||
$elements[] = sprintf($texts['text_period'], $texts['name_' . $type]);
|
||||
// hour
|
||||
if (in_array($type, array(self::TYPE_HOUR))) {
|
||||
$elements[] = sprintf($texts['text_mins'], $this->getCronMinutes());
|
||||
}
|
||||
// week
|
||||
if (in_array($type, array(self::TYPE_WEEK))) {
|
||||
$dow = $this->getCronDaysOfWeek();
|
||||
foreach ($texts['weekdays'] as $i => $wd) {
|
||||
$dow = str_replace((string) ($i + 1), $wd, $dow);
|
||||
}
|
||||
$elements[] = sprintf($texts['text_dow'], $dow);
|
||||
}
|
||||
// month + year
|
||||
if (in_array($type, array(self::TYPE_MONTH, self::TYPE_YEAR))) {
|
||||
$elements[] = sprintf($texts['text_dom'], $this->getCronDaysOfMonth());
|
||||
}
|
||||
// year
|
||||
if (in_array($type, array(self::TYPE_YEAR))) {
|
||||
$months = $this->getCronMonths();
|
||||
for ($i = count($texts['months']) - 1; $i >= 0; $i--) {
|
||||
$months = str_replace((string) ($i + 1), $texts['months'][$i], $months);
|
||||
}
|
||||
$elements[] = sprintf($texts['text_month'], $months);
|
||||
}
|
||||
// day + week + month + year
|
||||
if (in_array($type, array(self::TYPE_DAY, self::TYPE_WEEK, self::TYPE_MONTH, self::TYPE_YEAR))) {
|
||||
$elements[] = sprintf($texts['text_time'], $this->getCronHours(), $this->getCronMinutes());
|
||||
}
|
||||
return str_replace('*', $texts['empty'], implode(' ', $elements));
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType() {
|
||||
$mask = preg_replace('/[^\* ]/si', '-', $this->getCron());
|
||||
$mask = preg_replace('/-+/si', '-', $mask);
|
||||
$mask = preg_replace('/[^-\*]/si', '', $mask);
|
||||
if ($mask == '*****') {
|
||||
return self::TYPE_MINUTE;
|
||||
}
|
||||
elseif ($mask == '-****') {
|
||||
return self::TYPE_HOUR;
|
||||
}
|
||||
elseif (substr($mask, -3) == '***') {
|
||||
return self::TYPE_DAY;
|
||||
}
|
||||
elseif (substr($mask, -3) == '-**') {
|
||||
return self::TYPE_MONTH;
|
||||
}
|
||||
elseif (substr($mask, -3) == '**-') {
|
||||
return self::TYPE_WEEK;
|
||||
}
|
||||
elseif (substr($mask, -2) == '-*') {
|
||||
return self::TYPE_YEAR;
|
||||
}
|
||||
return self::TYPE_UNDEFINED;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param string $cron
|
||||
* @return Cron
|
||||
*/
|
||||
public function setCron($cron) {
|
||||
// sanitize
|
||||
$cron = trim($cron);
|
||||
$cron = preg_replace('/\s+/', ' ', $cron);
|
||||
// explode
|
||||
$elements = explode(' ', $cron);
|
||||
if (count($elements) != 5) {
|
||||
throw new Exception('Bad number of elements');
|
||||
}
|
||||
$this->cron = $cron;
|
||||
$this->setMinutes($elements[0]);
|
||||
$this->setHours($elements[1]);
|
||||
$this->setDaysOfMonth($elements[2]);
|
||||
$this->setMonths($elements[3]);
|
||||
$this->setDaysOfWeek($elements[4]);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCronMinutes() {
|
||||
return $this->arrayToCron($this->minutes);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCronHours() {
|
||||
return $this->arrayToCron($this->hours);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCronDaysOfMonth() {
|
||||
return $this->arrayToCron($this->dom);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCronMonths() {
|
||||
return $this->arrayToCron($this->months);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCronDaysOfWeek() {
|
||||
return $this->arrayToCron($this->dow);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMinutes() {
|
||||
return $this->minutes;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHours() {
|
||||
return $this->hours;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDaysOfMonth() {
|
||||
return $this->dom;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMonths() {
|
||||
return $this->months;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDaysOfWeek() {
|
||||
return $this->dow;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param string|array $minutes
|
||||
* @return Cron
|
||||
*/
|
||||
public function setMinutes($minutes) {
|
||||
$this->minutes = $this->cronToArray($minutes, 0, 59);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param string|array $hours
|
||||
* @return Cron
|
||||
*/
|
||||
public function setHours($hours) {
|
||||
$this->hours = $this->cronToArray($hours, 0, 23);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param string|array $months
|
||||
* @return Cron
|
||||
*/
|
||||
public function setMonths($months) {
|
||||
$this->months = $this->cronToArray($months, 1, 12);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param string|array $dow
|
||||
* @return Cron
|
||||
*/
|
||||
public function setDaysOfWeek($dow) {
|
||||
$this->dow = $this->cronToArray($dow, 0, 7);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param string|array $dom
|
||||
* @return Cron
|
||||
*/
|
||||
public function setDaysOfMonth($dom) {
|
||||
$this->dom = $this->cronToArray($dom, 1, 31);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param mixed $date
|
||||
* @param int $min
|
||||
* @param int $hour
|
||||
* @param int $day
|
||||
* @param int $month
|
||||
* @param int $weekday
|
||||
* @return DateTime
|
||||
*/
|
||||
protected function parseDate($date, &$min, &$hour, &$day, &$month, &$weekday) {
|
||||
if (is_numeric($date) && intval($date) == $date) {
|
||||
$date = new \DateTime('@' . $date);
|
||||
}
|
||||
elseif (is_string($date)) {
|
||||
$date = new \DateTime('@' . strtotime($date));
|
||||
}
|
||||
if ($date instanceof \DateTime) {
|
||||
$min = intval($date->format('i'));
|
||||
$hour = intval($date->format('H'));
|
||||
$day = intval($date->format('d'));
|
||||
$month = intval($date->format('m'));
|
||||
$weekday = intval($date->format('w')); // 0-6
|
||||
}
|
||||
else {
|
||||
throw new Exception('Date format not supported');
|
||||
}
|
||||
return new \DateTime($date->format('Y-m-d H:i:sP'));
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param int|string|\Datetime $date
|
||||
*/
|
||||
public function matchExact($date) {
|
||||
$date = $this->parseDate($date, $min, $hour, $day, $month, $weekday);
|
||||
return
|
||||
(empty($this->minutes) || in_array($min, $this->minutes)) &&
|
||||
(empty($this->hours) || in_array($hour, $this->hours)) &&
|
||||
(empty($this->dom) || in_array($day, $this->dom)) &&
|
||||
(empty($this->months) || in_array($month, $this->months)) &&
|
||||
(empty($this->dow) || in_array($weekday, $this->dow) || ($weekday == 0 && in_array(7, $this->dow)) || ($weekday == 7 && in_array(0, $this->dow))
|
||||
);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param int|string|\Datetime $date
|
||||
* @param int $minuteBefore
|
||||
* @param int $minuteAfter
|
||||
*/
|
||||
public function matchWithMargin($date, $minuteBefore = 0, $minuteAfter = 0) {
|
||||
if ($minuteBefore > 0) {
|
||||
throw new Exception('MinuteBefore parameter cannot be positive !');
|
||||
}
|
||||
if ($minuteAfter < 0) {
|
||||
throw new Exception('MinuteAfter parameter cannot be negative !');
|
||||
}
|
||||
$date = $this->parseDate($date, $min, $hour, $day, $month, $weekday);
|
||||
$interval = new \DateInterval('PT1M'); // 1 min
|
||||
if ($minuteBefore != 0) {
|
||||
$date->sub(new \DateInterval('PT' . abs($minuteBefore) . 'M'));
|
||||
}
|
||||
$n = $minuteAfter - $minuteBefore + 1;
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
if ($this->matchExact($date)) {
|
||||
return true;
|
||||
}
|
||||
$date->add($interval);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param array $array
|
||||
* @return string
|
||||
*/
|
||||
protected function arrayToCron($array) {
|
||||
$n = count($array);
|
||||
if (!is_array($array) || $n == 0) {
|
||||
return '*';
|
||||
}
|
||||
$cron = array($array[0]);
|
||||
$s = $c = $array[0];
|
||||
for ($i = 1; $i < $n; $i++) {
|
||||
if ($array[$i] == $c + 1) {
|
||||
$c = $array[$i];
|
||||
$cron[count($cron) - 1] = $s . '-' . $c;
|
||||
}
|
||||
else {
|
||||
$s = $c = $array[$i];
|
||||
$cron[] = $c;
|
||||
}
|
||||
}
|
||||
return implode(',', $cron);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $min
|
||||
* @param int $max
|
||||
* @return array
|
||||
*/
|
||||
protected function cronToArray($string, $min, $max) {
|
||||
$array = array();
|
||||
if (is_array($string)) {
|
||||
foreach ($string as $val) {
|
||||
if (is_numeric($val) && intval($val) == $val && $val >= $min && $val <= $max) {
|
||||
$array[] = intval($val);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ($string !== '*') {
|
||||
while ($string != '') {
|
||||
// test "*/n" expression
|
||||
if (preg_match('/^\*\/([0-9]+),?/', $string, $m)) {
|
||||
for ($i = max(0, $min); $i <= min(59, $max); $i+=$m[1]) {
|
||||
$array[] = intval($i);
|
||||
}
|
||||
$string = substr($string, strlen($m[0]));
|
||||
continue;
|
||||
}
|
||||
// test "a-b/n" expression
|
||||
if (preg_match('/^([0-9]+)-([0-9]+)\/([0-9]+),?/', $string, $m)) {
|
||||
for ($i = max($m[1], $min); $i <= min($m[2], $max); $i+=$m[3]) {
|
||||
$array[] = intval($i);
|
||||
}
|
||||
$string = substr($string, strlen($m[0]));
|
||||
continue;
|
||||
}
|
||||
// test "a-b" expression
|
||||
if (preg_match('/^([0-9]+)-([0-9]+),?/', $string, $m)) {
|
||||
for ($i = max($m[1], $min); $i <= min($m[2], $max); $i++) {
|
||||
$array[] = intval($i);
|
||||
}
|
||||
$string = substr($string, strlen($m[0]));
|
||||
continue;
|
||||
}
|
||||
// test "c" expression
|
||||
if (preg_match('/^([0-9]+),?/', $string, $m)) {
|
||||
if ($m[1] >= $min && $m[1] <= $max) {
|
||||
$array[] = intval($m[1]);
|
||||
}
|
||||
$string = substr($string, strlen($m[0]));
|
||||
continue;
|
||||
}
|
||||
// something goes wrong in the expression
|
||||
return array();
|
||||
}
|
||||
}
|
||||
sort($array);
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
365
system/src/Grav/Common/Scheduler/IntervalTrait.php
Normal file
365
system/src/Grav/Common/Scheduler/IntervalTrait.php
Normal file
@@ -0,0 +1,365 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Scheduler
|
||||
* @author Originally based on peppeocchi/php-cron-scheduler modified for Grav integration
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Scheduler;
|
||||
|
||||
use Cron\CronExpression;
|
||||
|
||||
trait IntervalTrait
|
||||
{
|
||||
/**
|
||||
* Set the Job execution time.
|
||||
*compo
|
||||
* @param string $expression
|
||||
* @return self
|
||||
*/
|
||||
public function at($expression)
|
||||
{
|
||||
$this->at = $expression;
|
||||
$this->executionTime = CronExpression::factory($expression);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every minute.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function everyMinute()
|
||||
{
|
||||
return $this->at('* * * * *');
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every hour.
|
||||
*
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function hourly($minute = 0)
|
||||
{
|
||||
$c = $this->validateCronSequence($minute);
|
||||
return $this->at("{$c['minute']} * * * *");
|
||||
}
|
||||
/**
|
||||
* Set the execution time to once a day.
|
||||
*
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function daily($hour = 0, $minute = 0)
|
||||
{
|
||||
if (is_string($hour)) {
|
||||
$parts = explode(':', $hour);
|
||||
$hour = $parts[0];
|
||||
$minute = isset($parts[1]) ? $parts[1] : '0';
|
||||
}
|
||||
$c = $this->validateCronSequence($minute, $hour);
|
||||
return $this->at("{$c['minute']} {$c['hour']} * * *");
|
||||
}
|
||||
/**
|
||||
* Set the execution time to once a week.
|
||||
*
|
||||
* @param int|string $weekday
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function weekly($weekday = 0, $hour = 0, $minute = 0)
|
||||
{
|
||||
if (is_string($hour)) {
|
||||
$parts = explode(':', $hour);
|
||||
$hour = $parts[0];
|
||||
$minute = isset($parts[1]) ? $parts[1] : '0';
|
||||
}
|
||||
$c = $this->validateCronSequence($minute, $hour, null, null, $weekday);
|
||||
return $this->at("{$c['minute']} {$c['hour']} * * {$c['weekday']}");
|
||||
}
|
||||
/**
|
||||
* Set the execution time to once a month.
|
||||
*
|
||||
* @param int|string $month
|
||||
* @param int|string $day
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function monthly($month = '*', $day = 1, $hour = 0, $minute = 0)
|
||||
{
|
||||
if (is_string($hour)) {
|
||||
$parts = explode(':', $hour);
|
||||
$hour = $parts[0];
|
||||
$minute = isset($parts[1]) ? $parts[1] : '0';
|
||||
}
|
||||
$c = $this->validateCronSequence($minute, $hour, $day, $month);
|
||||
return $this->at("{$c['minute']} {$c['hour']} {$c['day']} {$c['month']} *");
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every Sunday.
|
||||
*
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function sunday($hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->weekly(0, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every Monday.
|
||||
*
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function monday($hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->weekly(1, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every Tuesday.
|
||||
*
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function tuesday($hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->weekly(2, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every Wednesday.
|
||||
*
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function wednesday($hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->weekly(3, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every Thursday.
|
||||
*
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function thursday($hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->weekly(4, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every Friday.
|
||||
*
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function friday($hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->weekly(5, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every Saturday.
|
||||
*
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function saturday($hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->weekly(6, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every January.
|
||||
*
|
||||
* @param int|string $day
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function january($day = 1, $hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->monthly(1, $day, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every February.
|
||||
*
|
||||
* @param int|string $day
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function february($day = 1, $hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->monthly(2, $day, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every March.
|
||||
*
|
||||
* @param int|string $day
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function march($day = 1, $hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->monthly(3, $day, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every April.
|
||||
*
|
||||
* @param int|string $day
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function april($day = 1, $hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->monthly(4, $day, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every May.
|
||||
*
|
||||
* @param int|string $day
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function may($day = 1, $hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->monthly(5, $day, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every June.
|
||||
*
|
||||
* @param int|string $day
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function june($day = 1, $hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->monthly(6, $day, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every July.
|
||||
*
|
||||
* @param int|string $day
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function july($day = 1, $hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->monthly(7, $day, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every August.
|
||||
*
|
||||
* @param int|string $day
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function august($day = 1, $hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->monthly(8, $day, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every September.
|
||||
*
|
||||
* @param int|string $day
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function september($day = 1, $hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->monthly(9, $day, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every October.
|
||||
*
|
||||
* @param int|string $day
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function october($day = 1, $hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->monthly(10, $day, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every November.
|
||||
*
|
||||
* @param int|string $day
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function november($day = 1, $hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->monthly(11, $day, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Set the execution time to every December.
|
||||
*
|
||||
* @param int|string $day
|
||||
* @param int|string $hour
|
||||
* @param int|string $minute
|
||||
* @return self
|
||||
*/
|
||||
public function december($day = 1, $hour = 0, $minute = 0)
|
||||
{
|
||||
return $this->monthly(12, $day, $hour, $minute);
|
||||
}
|
||||
/**
|
||||
* Validate sequence of cron expression.
|
||||
*
|
||||
* @param int|string $minute
|
||||
* @param int|string $hour
|
||||
* @param int|string $day
|
||||
* @param int|string $month
|
||||
* @param int|string $weekday
|
||||
* @return array
|
||||
*/
|
||||
private function validateCronSequence($minute = null, $hour = null, $day = null, $month = null, $weekday = null)
|
||||
{
|
||||
return [
|
||||
'minute' => $this->validateCronRange($minute, 0, 59),
|
||||
'hour' => $this->validateCronRange($hour, 0, 23),
|
||||
'day' => $this->validateCronRange($day, 1, 31),
|
||||
'month' => $this->validateCronRange($month, 1, 12),
|
||||
'weekday' => $this->validateCronRange($weekday, 0, 6),
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Validate sequence of cron expression.
|
||||
*
|
||||
* @param int|string $value
|
||||
* @param int $min
|
||||
* @param int $max
|
||||
* @return mixed
|
||||
*/
|
||||
private function validateCronRange($value, $min, $max)
|
||||
{
|
||||
if ($value === null || $value === '*') {
|
||||
return '*';
|
||||
}
|
||||
if (! is_numeric($value) ||
|
||||
! ($value >= $min && $value <= $max)
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
"Invalid value: it should be '*' or between {$min} and {$max}."
|
||||
);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
491
system/src/Grav/Common/Scheduler/Job.php
Normal file
491
system/src/Grav/Common/Scheduler/Job.php
Normal file
@@ -0,0 +1,491 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Scheduler
|
||||
* @author Originally based on peppeocchi/php-cron-scheduler modified for Grav integration
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Scheduler;
|
||||
|
||||
use Cron\CronExpression;
|
||||
use Grav\Common\Grav;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class Job
|
||||
{
|
||||
use IntervalTrait;
|
||||
|
||||
private $id;
|
||||
private $enabled = true;
|
||||
private $command;
|
||||
private $at;
|
||||
private $args = [];
|
||||
private $runInBackground = true;
|
||||
private $creationTime;
|
||||
private $executionTime;
|
||||
private $tempDir;
|
||||
private $lockFile;
|
||||
private $truthTest = true;
|
||||
private $output;
|
||||
private $returnCode = 0;
|
||||
private $outputTo = [];
|
||||
private $emailTo = [];
|
||||
private $emailConfig = [];
|
||||
private $before;
|
||||
private $after;
|
||||
private $whenOverlapping;
|
||||
private $outputMode;
|
||||
private $process;
|
||||
private $successful = false;
|
||||
|
||||
/**
|
||||
* Create a new Job instance.
|
||||
*
|
||||
* @param string|callable $command
|
||||
* @param array $args
|
||||
* @param string $id
|
||||
*/
|
||||
public function __construct($command, $args = [], $id = null)
|
||||
{
|
||||
if (is_string($id)) {
|
||||
$this->id = Grav::instance()['inflector']->hyphenize($id);
|
||||
} else {
|
||||
if (is_string($command)) {
|
||||
$this->id = md5($command);
|
||||
} else {
|
||||
/* @var object $command */
|
||||
$this->id = spl_object_hash($command);
|
||||
}
|
||||
}
|
||||
$this->creationTime = new \DateTime('now');
|
||||
// initialize the directory path for lock files
|
||||
$this->tempDir = sys_get_temp_dir();
|
||||
$this->command = $command;
|
||||
$this->args = $args;
|
||||
// Set enabled state
|
||||
$status = Grav::instance()['config']->get('scheduler.status');
|
||||
$this->enabled = isset($status[$id]) && $status[$id] === 'disabled' ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCommand()
|
||||
{
|
||||
return $this->command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cron 'at' syntax for this job
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAt()
|
||||
{
|
||||
return $this->at;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the status of this job
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getEnabled()
|
||||
{
|
||||
return $this->enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get optional arguments
|
||||
*
|
||||
* @return array|string|void
|
||||
*/
|
||||
public function getArguments()
|
||||
{
|
||||
if (is_string($this->args)) {
|
||||
return $this->args;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public function getCronExpression()
|
||||
{
|
||||
return CronExpression::factory($this->at);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the status of the last run for this job
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSuccessful()
|
||||
{
|
||||
return $this->successful;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Job id.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the Job is due to run.
|
||||
* It accepts as input a DateTime used to check if
|
||||
* the job is due. Defaults to job creation time.
|
||||
* It also default the execution time if not previously defined.
|
||||
*
|
||||
* @param DateTime $date
|
||||
* @return bool
|
||||
*/
|
||||
public function isDue(\DateTime $date = null)
|
||||
{
|
||||
// The execution time is being defaulted if not defined
|
||||
if (!$this->executionTime) {
|
||||
$this->at('* * * * *');
|
||||
}
|
||||
$date = $date !== null ? $date : $this->creationTime;
|
||||
return $this->executionTime->isDue($date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the Job is overlapping.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isOverlapping()
|
||||
{
|
||||
return $this->lockFile &&
|
||||
file_exists($this->lockFile) &&
|
||||
call_user_func($this->whenOverlapping, filemtime($this->lockFile)) === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the Job to run in foreground.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function inForeground()
|
||||
{
|
||||
$this->runInBackground = false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the Job can run in background.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function runInBackground()
|
||||
{
|
||||
if (is_callable($this->command) || $this->runInBackground === false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will prevent the Job from overlapping.
|
||||
* It prevents another instance of the same Job of
|
||||
* being executed if the previous is still running.
|
||||
* The job id is used as a filename for the lock file.
|
||||
*
|
||||
* @param string $tempDir The directory path for the lock files
|
||||
* @param callable $whenOverlapping A callback to ignore job overlapping
|
||||
* @return self
|
||||
*/
|
||||
public function onlyOne($tempDir = null, callable $whenOverlapping = null)
|
||||
{
|
||||
if ($tempDir === null || !is_dir($tempDir)) {
|
||||
$tempDir = $this->tempDir;
|
||||
}
|
||||
$this->lockFile = implode('/', [
|
||||
trim($tempDir),
|
||||
trim($this->id) . '.lock',
|
||||
]);
|
||||
if ($whenOverlapping) {
|
||||
$this->whenOverlapping = $whenOverlapping;
|
||||
} else {
|
||||
$this->whenOverlapping = function () {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the job.
|
||||
*
|
||||
* @param array $config
|
||||
* @return self
|
||||
*/
|
||||
public function configure(array $config = [])
|
||||
{
|
||||
// Check if config has defined a tempDir
|
||||
if (isset($config['tempDir']) && is_dir($config['tempDir'])) {
|
||||
$this->tempDir = $config['tempDir'];
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truth test to define if the job should run if due.
|
||||
*
|
||||
* @param callable $fn
|
||||
* @return self
|
||||
*/
|
||||
public function when(callable $fn)
|
||||
{
|
||||
$this->truthTest = $fn();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the job.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
// If the truthTest failed, don't run
|
||||
if ($this->truthTest !== true) {
|
||||
return false;
|
||||
}
|
||||
// If overlapping, don't run
|
||||
if ($this->isOverlapping()) {
|
||||
return false;
|
||||
}
|
||||
// Write lock file if necessary
|
||||
$this->createLockFile();
|
||||
|
||||
// Call before if required
|
||||
if (is_callable($this->before)) {
|
||||
call_user_func($this->before);
|
||||
}
|
||||
// If command is callable...
|
||||
if (is_callable($this->command)) {
|
||||
$this->output = $this->exec();
|
||||
} else {
|
||||
/** @var Process process */
|
||||
$args = is_string($this->args) ? $this->args : implode(' ', $this->args);
|
||||
$command = $this->command . ' ' . $args;
|
||||
$process = new Process($command);
|
||||
|
||||
$this->process = $process;
|
||||
|
||||
if ($this->runInBackground()) {
|
||||
$process->start();
|
||||
} else {
|
||||
$process->run();
|
||||
$this->finalize();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish up processing the job
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function finalize()
|
||||
{
|
||||
/** @var Process $process */
|
||||
$process = $this->process;
|
||||
|
||||
if ($process) {
|
||||
$process->wait();
|
||||
|
||||
if ($process->isSuccessful()) {
|
||||
$this->successful = true;
|
||||
$this->output = $process->getOutput();
|
||||
} else {
|
||||
$this->successful = false;
|
||||
$this->output = $process->getErrorOutput();
|
||||
}
|
||||
|
||||
$this->postRun();
|
||||
|
||||
unset($this->process);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Things to run after job has run
|
||||
*/
|
||||
private function postRun()
|
||||
{
|
||||
if (count($this->outputTo) > 0) {
|
||||
foreach ($this->outputTo as $file) {
|
||||
$output_mode = $this->outputMode === 'append' ? FILE_APPEND | LOCK_EX : LOCK_EX;
|
||||
file_put_contents($file, $this->output, $output_mode);
|
||||
}
|
||||
}
|
||||
|
||||
// Send output to email
|
||||
$this->emailOutput();
|
||||
|
||||
// Call any callback defined
|
||||
if (is_callable($this->after)) {
|
||||
call_user_func($this->after, $this->output, $this->returnCode);
|
||||
}
|
||||
|
||||
$this->removeLockFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the job lock file.
|
||||
*
|
||||
* @param mixed $content
|
||||
* @return void
|
||||
*/
|
||||
private function createLockFile($content = null)
|
||||
{
|
||||
if ($this->lockFile) {
|
||||
if ($content === null || !is_string($content)) {
|
||||
$content = $this->getId();
|
||||
}
|
||||
file_put_contents($this->lockFile, $content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the job lock file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function removeLockFile()
|
||||
{
|
||||
if ($this->lockFile && file_exists($this->lockFile)) {
|
||||
unlink($this->lockFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a callable job.
|
||||
*
|
||||
* @throws Exception
|
||||
* @return string
|
||||
*/
|
||||
private function exec()
|
||||
{
|
||||
$return_data = '';
|
||||
ob_start();
|
||||
try {
|
||||
$return_data = call_user_func_array($this->command, $this->args);
|
||||
$this->successful = true;
|
||||
} catch (Exception $e) {
|
||||
$this->successful = false;
|
||||
}
|
||||
$this->output = ob_get_clean() . (is_string($return_data) ? $return_data : '');
|
||||
|
||||
$this->postRun();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the file/s where to write the output of the job.
|
||||
*
|
||||
* @param string|array $filename
|
||||
* @param bool $append
|
||||
* @return self
|
||||
*/
|
||||
public function output($filename, $append = false)
|
||||
{
|
||||
$this->outputTo = is_array($filename) ? $filename : [$filename];
|
||||
$this->outputMode = $append === false ? 'overwrite' : 'append';
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the job output.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getOutput()
|
||||
{
|
||||
return $this->output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the emails where the output should be sent to.
|
||||
* The Job should be set to write output to a file
|
||||
* for this to work.
|
||||
*
|
||||
* @param string|array $email
|
||||
* @return self
|
||||
*/
|
||||
public function email($email)
|
||||
{
|
||||
if (!is_string($email) && !is_array($email)) {
|
||||
throw new InvalidArgumentException('The email can be only string or array');
|
||||
}
|
||||
$this->emailTo = is_array($email) ? $email : [$email];
|
||||
// Force the job to run in foreground
|
||||
$this->inForeground();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Email the output of the job, if any.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function emailOutput()
|
||||
{
|
||||
if (!count($this->outputTo) || !count($this->emailTo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_callable('Grav\Plugin\Email\Utils::sendEmail')) {
|
||||
$subject ='Grav Scheduled Job [' . $this->getId() . ']';
|
||||
$content = "<h1>Output from Job ID: {$this->getId()}</h1>\n<h4>Command: {$this->getCommand()}</h4><br /><pre style=\"font-size: 12px; font-family: Monaco, Consolas, monospace\">\n".$this->getOutput()."\n</pre>";
|
||||
$to = $this->emailTo;
|
||||
|
||||
\Grav\Plugin\Email\Utils::sendEmail($subject, $content, $to);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set function to be called before job execution
|
||||
* Job object is injected as a parameter to callable function.
|
||||
*
|
||||
* @param callable $fn
|
||||
* @return self
|
||||
*/
|
||||
public function before(callable $fn)
|
||||
{
|
||||
$this->before = $fn;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a function to be called after job execution.
|
||||
* By default this will force the job to run in foreground
|
||||
* because the output is injected as a parameter of this
|
||||
* function, but it could be avoided by passing true as a
|
||||
* second parameter. The job will run in background if it
|
||||
* meets all the other criteria.
|
||||
*
|
||||
* @param callable $fn
|
||||
* @param bool $runInBackground
|
||||
* @return self
|
||||
*/
|
||||
public function then(callable $fn, $runInBackground = false)
|
||||
{
|
||||
$this->after = $fn;
|
||||
// Force the job to run in foreground
|
||||
if ($runInBackground === false) {
|
||||
$this->inForeground();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
354
system/src/Grav/Common/Scheduler/Scheduler.php
Normal file
354
system/src/Grav/Common/Scheduler/Scheduler.php
Normal file
@@ -0,0 +1,354 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Scheduler
|
||||
* @author Originally based on peppeocchi/php-cron-scheduler modified for Grav integration
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Scheduler;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Grav;
|
||||
use Symfony\Component\Process\PhpExecutableFinder;
|
||||
use Symfony\Component\Process\Process;
|
||||
use RocketTheme\Toolbox\File\YamlFile;
|
||||
|
||||
class Scheduler
|
||||
{
|
||||
/**
|
||||
* The queued jobs.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $jobs = [];
|
||||
private $saved_jobs = [];
|
||||
private $jobs_run = [];
|
||||
private $outputSchedule = [];
|
||||
private $config;
|
||||
private $status_path;
|
||||
|
||||
/**
|
||||
* Create new instance.
|
||||
*
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = Grav::instance()['config']->get('scheduler.defaults', []);
|
||||
$this->config = $config;
|
||||
$this->loadSavedJobs();
|
||||
|
||||
$this->status_path = Grav::instance()['locator']->findResource('user://data/scheduler', true, true);
|
||||
if (!file_exists($this->status_path)) {
|
||||
Folder::create($this->status_path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Load saved jobs from config/scheduler.yaml file
|
||||
*/
|
||||
public function loadSavedJobs()
|
||||
{
|
||||
if (!$this->jobs) {
|
||||
$saved_jobs = (array) Grav::instance()['config']->get('scheduler.custom_jobs', []);
|
||||
|
||||
foreach ($saved_jobs as $id => $j) {
|
||||
$args = isset($j['args']) ? $j['args'] : [];
|
||||
$id = Grav::instance()['inflector']->hyphenize($id);
|
||||
$job = $this->addCommand($j['command'], $args, $id);
|
||||
|
||||
if (isset($j['at'])) {
|
||||
$job->at($j['at']);
|
||||
}
|
||||
|
||||
if (isset($j['output'])) {
|
||||
$mode = isset($j['output_mode']) && $j['output_mode'] === 'append' ? true : false;
|
||||
$job->output($j['output'], $mode);
|
||||
}
|
||||
|
||||
if (isset($j['email'])) {
|
||||
$job->email($j['email']);
|
||||
}
|
||||
|
||||
// store in saved_jobs
|
||||
$this->saved_jobs[] = $job;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the queued jobs as background/foreground
|
||||
*
|
||||
* @param bool $all
|
||||
* @return array
|
||||
*/
|
||||
public function getQueuedJobs($all = false)
|
||||
{
|
||||
$background = [];
|
||||
$foreground = [];
|
||||
foreach ($this->jobs as $job) {
|
||||
if ($all || $job->getEnabled()) {
|
||||
if ($job->runInBackground()) {
|
||||
$background[] = $job;
|
||||
} else {
|
||||
$foreground[] = $job;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return [$background, $foreground];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all jobs if they are disabled or not as one array
|
||||
*
|
||||
* @param bool $all
|
||||
* @return array
|
||||
*/
|
||||
public function getAllJobs()
|
||||
{
|
||||
list($background, $foreground) = $this->getQueuedJobs(true);
|
||||
return array_merge($background, $foreground);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues a PHP function execution.
|
||||
*
|
||||
* @param callable $fn The function to execute
|
||||
* @param array $args Optional arguments to pass to the php script
|
||||
* @param string $id Optional custom identifier
|
||||
* @return Job
|
||||
*/
|
||||
public function addFunction(callable $fn, $args = [], $id = null)
|
||||
{
|
||||
$job = new Job($fn, $args, $id);
|
||||
$this->queueJob($job->configure($this->config));
|
||||
return $job;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a raw shell command.
|
||||
*
|
||||
* @param string $command The command to execute
|
||||
* @param array $args Optional arguments to pass to the command
|
||||
* @param string $id Optional custom identifier
|
||||
* @return Job
|
||||
*/
|
||||
public function addCommand($command, $args = [], $id = null)
|
||||
{
|
||||
$job = new Job($command, $args, $id);
|
||||
$this->queueJob($job->configure($this->config));
|
||||
return $job;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the scheduler.
|
||||
*
|
||||
* @param \DateTime $runTime Optional, run at specific moment
|
||||
* @return array Executed jobs
|
||||
*/
|
||||
public function run(\Datetime $runTime = null)
|
||||
{
|
||||
list($background, $foreground) = $this->getQueuedJobs(false);
|
||||
$alljobs = array_merge($background, $foreground);
|
||||
|
||||
if (is_null($runTime)) {
|
||||
$runTime = new \DateTime('now');
|
||||
}
|
||||
|
||||
// Star processing jobs
|
||||
foreach ($alljobs as $job) {
|
||||
if ($job->isDue($runTime)) {
|
||||
$job->run();
|
||||
$this->jobs_run[] = $job;
|
||||
}
|
||||
}
|
||||
|
||||
// Finish handling any background jobs
|
||||
foreach($background as $job) {
|
||||
$job->finalize();
|
||||
}
|
||||
|
||||
// Store states
|
||||
$this->saveJobStates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all collected data of last run.
|
||||
*
|
||||
* Call before run() if you call run() multiple times.
|
||||
*/
|
||||
public function resetRun()
|
||||
{
|
||||
// Reset collected data of last run
|
||||
$this->executedJobs = [];
|
||||
$this->failedJobs = [];
|
||||
$this->outputSchedule = [];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the scheduler verbose output.
|
||||
*
|
||||
* @param string $type Allowed: text, html, array
|
||||
* @return mixed The return depends on the requested $type
|
||||
*/
|
||||
public function getVerboseOutput($type = 'text')
|
||||
{
|
||||
switch ($type) {
|
||||
case 'text':
|
||||
return implode("\n", $this->outputSchedule);
|
||||
case 'html':
|
||||
return implode('<br>', $this->outputSchedule);
|
||||
case 'array':
|
||||
return $this->outputSchedule;
|
||||
default:
|
||||
throw new \InvalidArgumentException('Invalid output type');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all queued Jobs.
|
||||
*/
|
||||
public function clearJobs()
|
||||
{
|
||||
$this->jobs = [];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get the full Cron command
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCronCommand()
|
||||
{
|
||||
$phpBinaryFinder = new PhpExecutableFinder();
|
||||
$php = $phpBinaryFinder->find();
|
||||
$command = 'cd ' . GRAV_ROOT . ';' . $php . ' bin/grav scheduler';
|
||||
|
||||
return "(crontab -l; echo \"* * * * * {$command} 1>> /dev/null 2>&1\") | crontab -";
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to determine if cron job is setup
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function isCrontabSetup()
|
||||
{
|
||||
$process = new Process('crontab -l');
|
||||
$process->run();
|
||||
|
||||
if ($process->isSuccessful()) {
|
||||
$output = $process->getOutput();
|
||||
|
||||
if (preg_match('$bin\/grav schedule$', $output)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Job states file
|
||||
*
|
||||
* @return \RocketTheme\Toolbox\File\FileInterface|YamlFile
|
||||
*/
|
||||
public function getJobStates()
|
||||
{
|
||||
$file = YamlFile::instance($this->status_path . '/status.yaml');
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save job states to statys file
|
||||
*/
|
||||
private function saveJobStates()
|
||||
{
|
||||
$now = time();
|
||||
$new_states = [];
|
||||
|
||||
foreach ($this->jobs_run as $job) {
|
||||
if ($job->isSuccessful()) {
|
||||
$new_states[$job->getId()] = ['state' => 'success', 'last-run' => $now];
|
||||
$this->pushExecutedJob($job);
|
||||
} else {
|
||||
$new_states[$job->getId()] = ['state' => 'failure', 'last-run' => $now, 'error' => $job->getOutput()];
|
||||
$this->pushFailedJob($job);
|
||||
}
|
||||
}
|
||||
$saved_states = $this->getJobStates();
|
||||
$saved_states->save(array_merge($saved_states->content(), $new_states));
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a job for execution in the correct queue.
|
||||
*
|
||||
* @param Job $job
|
||||
* @return void
|
||||
*/
|
||||
private function queueJob(Job $job)
|
||||
{
|
||||
$this->jobs[] = $job;
|
||||
|
||||
// Store jobs
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an entry to the scheduler verbose output array.
|
||||
*
|
||||
* @param string $string
|
||||
* @return void
|
||||
*/
|
||||
private function addSchedulerVerboseOutput($string)
|
||||
{
|
||||
$now = '[' . (new \DateTime('now'))->format('c') . '] ';
|
||||
$this->outputSchedule[] = $now . $string;
|
||||
// Print to stdoutput in light gray
|
||||
// echo "\033[37m{$string}\033[0m\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a succesfully executed job.
|
||||
*
|
||||
* @param Job $job
|
||||
* @return Job
|
||||
*/
|
||||
private function pushExecutedJob(Job $job)
|
||||
{
|
||||
$this->executedJobs[] = $job;
|
||||
$command = $job->getCommand();
|
||||
$args = $job->getArguments();
|
||||
// If callable, log the string Closure
|
||||
if (is_callable($command)) {
|
||||
$command = is_string($command) ? $command : 'Closure';
|
||||
}
|
||||
$this->addSchedulerVerboseOutput("<green>Success</green>: <white>{$command} {$args}</white>");
|
||||
return $job;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a failed job.
|
||||
*
|
||||
* @param Job $job
|
||||
* @return Job
|
||||
*/
|
||||
private function pushFailedJob(Job $job)
|
||||
{
|
||||
$this->failedJobs[] = $job;
|
||||
$command = $job->getCommand();
|
||||
// If callable, log the string Closure
|
||||
if (is_callable($command)) {
|
||||
$command = is_string($command) ? $command : 'Closure';
|
||||
}
|
||||
$output = trim($job->getOutput());
|
||||
$this->addSchedulerVerboseOutput("<red>Error</red>: <white>{$command}</white> → <normal>{$output}</normal>");
|
||||
return $job;
|
||||
}
|
||||
}
|
||||
25
system/src/Grav/Common/Service/InflectorServiceProvider.php
Normal file
25
system/src/Grav/Common/Service/InflectorServiceProvider.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Service
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Service;
|
||||
|
||||
use Grav\Common\Inflector;
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
|
||||
class InflectorServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container['inflector'] = function () {
|
||||
$inflector = new Inflector();
|
||||
|
||||
return $inflector;
|
||||
};
|
||||
}
|
||||
}
|
||||
21
system/src/Grav/Common/Service/SchedulerServiceProvider.php
Normal file
21
system/src/Grav/Common/Service/SchedulerServiceProvider.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Service
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Service;
|
||||
|
||||
use Grav\Common\Scheduler\Scheduler;
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
|
||||
class SchedulerServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container['scheduler'] = new Scheduler();
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,11 @@
|
||||
|
||||
namespace Grav\Common\Twig;
|
||||
|
||||
use Cron\CronExpression;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Page\Collection;
|
||||
use Grav\Common\Page\Media;
|
||||
use Grav\Common\Scheduler\Cron;
|
||||
use Grav\Common\Twig\TokenParser\TwigTokenParserScript;
|
||||
use Grav\Common\Twig\TokenParser\TwigTokenParserStyle;
|
||||
use Grav\Common\Twig\TokenParser\TwigTokenParserSwitch;
|
||||
@@ -110,6 +112,7 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
|
||||
new \Twig_SimpleFilter('bool', [$this, 'boolFilter']),
|
||||
new \Twig_SimpleFilter('float', [$this, 'floatFilter'], ['is_safe' => true]),
|
||||
new \Twig_SimpleFilter('array', [$this, 'arrayFilter']),
|
||||
new \Twig_SimpleFilter('nicecron', [$this, 'niceCronFilter']),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -157,6 +160,8 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
|
||||
new \Twig_SimpleFunction('nicenumber', [$this, 'niceNumberFunc']),
|
||||
new \Twig_SimpleFunction('nicefilesize', [$this, 'niceFilesizeFunc']),
|
||||
new \Twig_SimpleFunction('nicetime', [$this, 'nicetimeFilter']),
|
||||
new \Twig_SimpleFunction('cron', [$this, 'cronFunc']),
|
||||
|
||||
|
||||
// Translations
|
||||
new \Twig_simpleFunction('t', [$this, 'translate']),
|
||||
@@ -437,6 +442,24 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
|
||||
return (strpos($haystack, $needle) !== false);
|
||||
}
|
||||
|
||||
public function niceCronFilter($at)
|
||||
{
|
||||
$cron = new Cron($at);
|
||||
return $cron->getText('en');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Cron object for a crontab 'at' format
|
||||
*
|
||||
* @param $at
|
||||
* @return CronExpression
|
||||
*/
|
||||
public function cronFunc($at)
|
||||
{
|
||||
$cron = CronExpression::factory($at);
|
||||
return $cron;
|
||||
}
|
||||
|
||||
/**
|
||||
* displays a facebook style 'time ago' formatted date/time
|
||||
*
|
||||
|
||||
171
system/src/Grav/Console/Cli/SchedulerCommand.php
Normal file
171
system/src/Grav/Console/Cli/SchedulerCommand.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Console
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Cron\CronExpression;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Scheduler\Scheduler;
|
||||
use Grav\Console\ConsoleCommand;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
class SchedulerCommand extends ConsoleCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('scheduler')
|
||||
->addOption(
|
||||
'install',
|
||||
'i',
|
||||
InputOption::VALUE_NONE,
|
||||
'Show Install Command'
|
||||
)
|
||||
->addOption(
|
||||
'jobs',
|
||||
'j',
|
||||
InputOption::VALUE_NONE,
|
||||
'Show Jobs Summary'
|
||||
)
|
||||
->addOption(
|
||||
'details',
|
||||
'd',
|
||||
InputOption::VALUE_NONE,
|
||||
'Show Job Details'
|
||||
)
|
||||
->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");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null|void
|
||||
*/
|
||||
protected function serve()
|
||||
{
|
||||
// error_reporting(1);
|
||||
$grav = Grav::instance();
|
||||
|
||||
$grav['uri']->init();
|
||||
$grav['config']->init();
|
||||
$grav['streams'];
|
||||
$grav['plugins']->init();
|
||||
$grav['themes']->init();
|
||||
|
||||
// Initialize Plugins
|
||||
$grav->fireEvent('onPluginsInitialized');
|
||||
|
||||
/** @var Scheduler $scheduler */
|
||||
$scheduler = $grav['scheduler'];
|
||||
$grav->fireEvent('onSchedulerInitialized', new Event(['scheduler' => $scheduler]));
|
||||
|
||||
$this->setHelp('foo');
|
||||
|
||||
/** @var use new SymfonyStyle helper $io */
|
||||
$io = new SymfonyStyle($this->input, $this->output);
|
||||
|
||||
if ($this->input->getOption('jobs')) {
|
||||
// Show jobs list
|
||||
|
||||
$jobs = $scheduler->getAllJobs();
|
||||
$job_states = $scheduler->getJobStates()->content();
|
||||
$rows = [];
|
||||
|
||||
$table = new Table($this->output);
|
||||
$table->setStyle('box');
|
||||
$headers = ['Job ID', 'Command', 'Run At', 'Status', 'Last Run', 'State'];
|
||||
|
||||
$io->title('Scheduler Jobs Listing');
|
||||
|
||||
foreach ($jobs as $job) {
|
||||
$job_status = ucfirst($job_states[$job->getId()]['state'] ?? 'ready');
|
||||
$last_run = $job_states[$job->getId()]['last-run'] ?? 0;
|
||||
$status = $job_status === 'Failure' ? "<red>{$job_status}</red>" : "<green>{$job_status}</green>";
|
||||
$state = $job->getEnabled() ? "<cyan>Enabled</cyan>" : "<red>Disabled</red>";
|
||||
$row = [
|
||||
$job->getId(),
|
||||
"<white>{$job->getCommand()}</white>",
|
||||
"<magenta>{$job->getAt()}</magenta>",
|
||||
"{$status}",
|
||||
"<yellow>" . ($last_run === 0 ? 'Never' : date('Y-m-d H:i', $last_run)) . "</yellow>",
|
||||
$state,
|
||||
|
||||
];
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
if (!empty($rows)) {
|
||||
$table->setHeaders($headers);
|
||||
$table->setRows($rows);
|
||||
$table->render();
|
||||
} else {
|
||||
$io->text('no jobs found...');
|
||||
}
|
||||
|
||||
$io->newLine();
|
||||
$io->note('For error details run "bin/grav scheduler -d"');
|
||||
$io->newLine();
|
||||
} elseif ($this->input->getOption('details')) {
|
||||
$jobs = $scheduler->getAllJobs();
|
||||
$job_states = $scheduler->getJobStates()->content();
|
||||
|
||||
$io->title('Job Details');
|
||||
|
||||
$table = new Table($this->output);
|
||||
$table->setStyle('box');
|
||||
$table->setHeaders(['Job ID', 'Last Run', 'Next Run', 'Errors']);
|
||||
$rows = [];
|
||||
|
||||
foreach ($jobs as $job) {
|
||||
$job_state = $job_states[$job->getId()];
|
||||
$error = isset($job_state['error']) ? trim($job_state['error']) : false;
|
||||
|
||||
/** @var CronExpression $expression */
|
||||
$expression = $job->getCronExpression();
|
||||
$next_run = $expression->getNextRunDate();
|
||||
|
||||
$row = [];
|
||||
$row[] = $job->getId();
|
||||
$row[] = "<yellow>" . date('Y-m-d H:i', $job_state['last-run']) . "</yellow>";
|
||||
$row[] = "<yellow>" . $next_run->format('Y-m-d H:i') . "</yellow>";
|
||||
|
||||
if ($error) {
|
||||
$row[] = "<error>{$error}</error>";
|
||||
} else {
|
||||
$row[] = "<green>None</green>";
|
||||
}
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
$table->setRows($rows);
|
||||
$table->render();
|
||||
|
||||
} elseif ($this->input->getOption('install')) {
|
||||
$io->title('Install Scheduler');
|
||||
|
||||
if ($scheduler->isCrontabSetup()) {
|
||||
$io->success('All Ready! You have already set up Grav\'s Scheduler in your crontab');
|
||||
} else {
|
||||
$io->error('You still need to set up Grav\'s Scheduler in your crontab');
|
||||
}
|
||||
$io->note('To install, run the following command from your terminal:');
|
||||
$io->newLine();
|
||||
$io->text(trim($scheduler->getCronCommand()));
|
||||
} else {
|
||||
// Run scheduler
|
||||
$scheduler->run();
|
||||
|
||||
if ($this->input->getOption('verbose')) {
|
||||
$io->title('Running Scheduled Jobs');
|
||||
$io->text($scheduler->getVerboseOutput());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
trait ConsoleTrait
|
||||
{
|
||||
use GravTrait;
|
||||
// use GravTrait;
|
||||
|
||||
/**
|
||||
* @var
|
||||
|
||||
Reference in New Issue
Block a user