├── Block └── Adminhtml │ ├── Job │ └── Listing │ │ └── Actions.php │ ├── Task │ ├── Listing │ │ └── Actions.php │ ├── Timeline.php │ └── Timeline │ │ └── Actions.php │ └── UpgradeToPro.php ├── Console └── Command │ ├── Job │ └── Listing.php │ └── Task │ ├── Listing.php │ └── Show.php ├── Controller └── Adminhtml │ ├── Job │ ├── GenerateSchedule.php │ └── Listing.php │ ├── Task.php │ └── Task │ ├── Listing.php │ ├── MassDelete.php │ ├── Timeline.php │ └── View.php ├── Cron └── HeartBeat.php ├── Helper ├── HeartBeat.php ├── Job.php ├── Task.php └── Url.php ├── INSTALL.txt ├── Model └── ResourceModel │ └── Task │ └── Collection.php ├── Plugin └── Cron │ └── Observer │ ├── ProcessCronQueueObserver.php │ ├── ProcessCronQueueObserver_2.0.php │ ├── ProcessCronQueueObserver_2.1.php │ └── ProcessCronQueueObserver_2.2.php ├── README.md ├── Setup ├── InstallSchema.php └── UpgradeData.php ├── Ui ├── Component │ ├── JobListing │ │ └── Column │ │ │ ├── Code │ │ │ └── Options.php │ │ │ └── Group │ │ │ └── Options.php │ └── TaskListing │ │ └── Column │ │ ├── Actions.php │ │ ├── Code │ │ └── Options.php │ │ ├── Messages.php │ │ └── Status │ │ └── Options.php └── DataProvider │ ├── JobProvider.php │ └── TaskProvider.php ├── composer.json ├── etc ├── acl.xml ├── adminhtml │ ├── menu.xml │ ├── routes.xml │ └── system.xml ├── config.xml ├── cron_groups.xml ├── crontab.xml ├── di.xml └── module.xml ├── i18n ├── en_US.csv └── fr_FR.csv ├── registration.php └── view └── adminhtml ├── layout ├── cronscheduler_job_listing.xml ├── cronscheduler_task_listing.xml └── cronscheduler_task_timeline.xml ├── requirejs-config.js ├── templates ├── job │ └── listing │ │ └── actions.phtml ├── task │ ├── listing │ │ └── actions.phtml │ ├── timeline.phtml │ ├── timeline │ │ └── actions.phtml │ └── view.phtml └── upgradeToPro.phtml ├── ui_component ├── cronscheduler_job_listing.xml ├── cronscheduler_job_listing_2.0.xml ├── cronscheduler_job_listing_Mage20.xml ├── cronscheduler_task_listing.xml ├── cronscheduler_task_listing_2.0.xml └── cronscheduler_task_listing_Mage20.xml └── web ├── css ├── common.css └── timeline.css ├── js ├── task.js └── timeline.js └── template └── task └── grid └── status.html /Block/Adminhtml/Job/Listing/Actions.php: -------------------------------------------------------------------------------- 1 | Cron Scheduler > Jobs List (button : Generate Schedule) 12 | * @version 1.0.0 13 | */ 14 | class Actions extends \Magento\Backend\Block\Template 15 | { 16 | 17 | /** 18 | * @var \Magento\Framework\Authorization 19 | */ 20 | protected $_authorization = null; 21 | 22 | /** 23 | * @var string 24 | */ 25 | protected $_aclResource = "generate_schedule"; 26 | 27 | /** 28 | * Class constructor 29 | * @param \Magento\Backend\Block\Template\Context $context 30 | * @param array $data 31 | */ 32 | public function __construct( 33 | \Magento\Backend\Block\Template\Context $context, 34 | array $data = [] 35 | ) 36 | { 37 | $this->_authorization = $context->getAuthorization(); 38 | $this->setTemplate('job/listing/actions.phtml'); 39 | parent::__construct($context, $data); 40 | } 41 | 42 | public function isAllowed() 43 | { 44 | return $this->_authorization->isAllowed('Wyomind_CronScheduler::' . $this->_aclResource); 45 | } 46 | 47 | /** 48 | * Get the url to generate schedule 49 | * @return string the url 50 | */ 51 | public function getGenerateScheduleUrl() 52 | { 53 | return $this->getUrl("*/job/generateSchedule", ["redirect" => "cronscheduler_job_listing"]); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /Block/Adminhtml/Task/Listing/Actions.php: -------------------------------------------------------------------------------- 1 | Cron Scheduler > Tasks List (button : Generate Schedule) 12 | * @version 1.0.0 13 | */ 14 | class Actions extends \Magento\Backend\Block\Template 15 | { 16 | 17 | /** 18 | * @var \Magento\Framework\Authorization 19 | */ 20 | protected $_authorization = null; 21 | 22 | /** 23 | * @var string 24 | */ 25 | protected $_aclResource = "generate_schedule"; 26 | 27 | /** 28 | * Class constructor 29 | * @param \Magento\Backend\Block\Template\Context $context 30 | * @param array $data 31 | */ 32 | public function __construct( 33 | \Magento\Backend\Block\Template\Context $context, 34 | array $data = [] 35 | ) 36 | { 37 | parent::__construct($context, $data); 38 | $this->_authorization = $context->getAuthorization(); 39 | $this->setTemplate('task/listing/actions.phtml'); 40 | } 41 | 42 | public function isAllowed() 43 | { 44 | return $this->_authorization->isAllowed('Wyomind_CronScheduler::' . $this->_aclResource); 45 | } 46 | 47 | /** 48 | * Get the url to generate schedule 49 | * @return string the url 50 | */ 51 | public function getGenerateScheduleUrl() 52 | { 53 | return $this->getUrl("*/job/generateSchedule", ["redirect" => "cronscheduler_task_listing"]); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /Block/Adminhtml/Task/Timeline.php: -------------------------------------------------------------------------------- 1 | _datetime = $datetime; 54 | $this->_taskHelper = $taskHelper; 55 | $this->_collectionFactory = $collectionFactory; 56 | $explodedVersion = explode("-", $productMetaData->getVersion()); // in case of 2.2.0-dev 57 | $this->_magentoVersion = $explodedVersion[0]; 58 | parent::__construct($context, $data); 59 | $this->setTemplate('task/timeline.phtml'); 60 | } 61 | 62 | /** 63 | * Get the task view modal popup url 64 | * @return string 65 | */ 66 | public function getViewUrl() 67 | { 68 | return $this->getUrl(\Wyomind\CronScheduler\Helper\Url::TASK_VIEW); 69 | } 70 | 71 | /** 72 | * Add the timezone offset to a datetime 73 | * @param string $datetime 74 | * @return string 75 | */ 76 | private function addOffset($datetime) 77 | { 78 | if ($datetime == null) { 79 | return null; 80 | } 81 | if (version_compare($this->_magentoVersion, "2.2.0") >= 0) { 82 | return $this->_datetime->date("Y-m-d H:i:s", strtotime($datetime) + $this->_datetime->getGmtOffSet('seconds')); 83 | } else { 84 | return $datetime; 85 | } 86 | } 87 | 88 | /** 89 | * Get the system current date for javascript use 90 | * @return string 91 | */ 92 | public function getCurrentJsDate() 93 | { 94 | $current = $this->_datetime->date('U') + $this->_datetime->getGmtOffSet('seconds'); 95 | return "new Date(" . $this->_datetime->date("Y,", $current) . ($this->_datetime->date("m", $current) - 1) . $this->_datetime->date(",d,H,i,s", $current) . ")"; 96 | } 97 | 98 | /** 99 | * Get the data to construct the timeline 100 | * @return array 101 | */ 102 | public function getTimelineData() 103 | { 104 | 105 | $data = []; 106 | $tasks = $this->_collectionFactory->create(); 107 | $tasks->getSelect()->order('job_code'); 108 | 109 | foreach ($tasks as $task) { 110 | $start = $this->addOffset($task->getExecutedAt()); 111 | $end = $this->addOffset($task->getFinishedAt()); 112 | 113 | 114 | list ($type, $inner) = $this->_taskHelper->getStatusRenderer($task->getStatus()); 115 | 116 | $messages = $task->getMessages(); 117 | if (strlen($messages) > 200) { 118 | $messages = substr($messages, 0, 200) . "..."; 119 | } 120 | $messages = nl2br($messages); 121 | 122 | 123 | $tooltip = "" 124 | . "" 127 | . "" 131 | . "" 136 | . "" 141 | . "" 146 | . "" 151 | . ""; 156 | if ($messages != "") { 157 | $tooltip .= ""; 162 | } 163 | $tooltip .= "
" 125 | . $task->getJobCode() 126 | . "
" 128 | . __('Id') 129 | . "" 130 | . $task->getId() . "
" 132 | . __('Status') 133 | . "" 134 | . "" . $inner . "" 135 | . "
" 137 | . __('Created at') 138 | . "" 139 | . $this->addOffset($task->getCreatedAt()) 140 | . "
" 142 | . __('Scheduled at') 143 | . "" 144 | . $this->addOffset($task->getScheduledAt()) 145 | . "
" 147 | . __('Executed at') 148 | . "" 149 | . ($start != null ? $start : "") 150 | . "
" 152 | . __('Finished at') 153 | . "" 154 | . ($end != null ? $end : "") 155 | . "
" 158 | . __('Messages') 159 | . "" 160 | . $messages 161 | . "
"; 164 | 165 | if ($start == null) { 166 | $start = $this->addOffset($task->getScheduledAt()); 167 | $end = $this->addOffset($task->getScheduledAt()); 168 | } 169 | 170 | if ($task->getStatus() == \Magento\Cron\Model\Schedule::STATUS_RUNNING) { 171 | $end = $this->addOffset($this->_datetime->date('Y-m-d H:i:s')); 172 | } 173 | 174 | if ($task->getStatus() == \Magento\Cron\Model\Schedule::STATUS_ERROR && $end == null) { 175 | $end = $start; 176 | } 177 | 178 | $data[] = [ 179 | $task->getJobCode(), 180 | $task->getStatus(), 181 | $tooltip, 182 | "new Date(" . $this->_datetime->date('Y,', $start) . ($this->_datetime->date('m', $start) - 1) . $this->_datetime->date(',d,H,i,s,0', $start) . ")", 183 | "new Date(" . $this->_datetime->date('Y,', $end) . ($this->_datetime->date('m', $end) - 1) . $this->_datetime->date(',d,H,i,s,0', $end) . ")", 184 | $task->getScheduleId() 185 | ]; 186 | } 187 | 188 | return $data; 189 | } 190 | 191 | } 192 | -------------------------------------------------------------------------------- /Block/Adminhtml/Task/Timeline/Actions.php: -------------------------------------------------------------------------------- 1 | Cron Scheduler > Tasks Timeline (button : Generate Schedule) 12 | * @version 1.0.0 13 | */ 14 | class Actions extends \Magento\Backend\Block\Template 15 | { 16 | 17 | /** 18 | * @var \Magento\Framework\Authorization 19 | */ 20 | protected $_authorization = null; 21 | 22 | /** 23 | * @var string 24 | */ 25 | protected $_aclResource = "generate_schedule"; 26 | 27 | /** 28 | * Class constructor 29 | * @param \Magento\Backend\Block\Template\Context $context 30 | * @param array $data 31 | */ 32 | public function __construct( 33 | \Magento\Backend\Block\Template\Context $context, 34 | array $data = [] 35 | ) 36 | { 37 | parent::__construct($context, $data); 38 | $this->_authorization = $context->getAuthorization(); 39 | $this->setTemplate('task/timeline/actions.phtml'); 40 | } 41 | 42 | /** 43 | * Is allowed to generate schedule 44 | * @return boolean 45 | */ 46 | public function isAllowed() 47 | { 48 | return $this->_authorization->isAllowed('Wyomind_CronScheduler::' . $this->_aclResource); 49 | } 50 | 51 | /** 52 | * Get the url to generate schedule 53 | * @return string the url 54 | */ 55 | public function getGenerateScheduleUrl() 56 | { 57 | return $this->getUrl("*/job/generateSchedule", ["redirect" => "cronscheduler_task_timeline"]); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Block/Adminhtml/UpgradeToPro.php: -------------------------------------------------------------------------------- 1 | setTemplate('upgradeToPro.phtml'); 32 | $this->_moduleList = $moduleList; 33 | } 34 | 35 | /** 36 | * Using the pro version ? 37 | * @return type 38 | */ 39 | public function isPro() { 40 | return $this->_moduleList->has('Wyomind_CronSchedulerPro'); 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Console/Command/Job/Listing.php: -------------------------------------------------------------------------------- 1 | 14 | * $ bin/magento help wyomind:cronscheduler:job:list 15 | * Usage: 16 | * wyomind:cronscheduler:job:list 17 | * 18 | * Options: 19 | * --help (-h) Display this help message 20 | * --quiet (-q) Do not output any message 21 | * --verbose (-v|vv|vvv) Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug 22 | * --version (-V) Display this application version 23 | * --ansi Force ANSI output 24 | * --no-ansi Disable ANSI output 25 | * --no-interaction (-n) Do not ask any interactive question 26 | * 27 | */ 28 | class Listing extends \Symfony\Component\Console\Command\Command 29 | { 30 | 31 | /** 32 | * @var \Magento\Cron\Model\ConfigInterface 33 | */ 34 | protected $_cronConfig = null; 35 | 36 | /** 37 | * @var \Magento\Framework\App\State 38 | */ 39 | protected $_state = null; 40 | 41 | /** 42 | * Class constructor 43 | * @param \Magento\Cron\Model\ConfigInterface $cronConfig 44 | * @param \Magento\Framework\App\State $state 45 | */ 46 | public function __construct( 47 | \Magento\Cron\Model\ConfigInterface $cronConfig, 48 | \Magento\Framework\App\State $state 49 | ) 50 | { 51 | $this->_state = $state; 52 | $this->_cronConfig = $cronConfig; 53 | parent::__construct(); 54 | } 55 | 56 | /** 57 | * Configure the command line 58 | */ 59 | protected function configure() 60 | { 61 | $this->setName('wyomind:cronscheduler:job:list') 62 | ->setDescription(__('Cron Scheduler : get list of all jobs')) 63 | ->setDefinition([]); 64 | parent::configure(); 65 | } 66 | 67 | /** 68 | * Execute the command line 69 | * @param \Symfony\Component\Console\Input\InputInterface $input 70 | * @param \Symfony\Component\Console\Output\OutputInterface $output 71 | * @return int \Magento\Framework\Console\Cli::RETURN_FAILURE or \Magento\Framework\Console\Cli::RETURN_SUCCESS 72 | */ 73 | protected function execute( 74 | \Symfony\Component\Console\Input\InputInterface $input, 75 | \Symfony\Component\Console\Output\OutputInterface $output 76 | ) 77 | { 78 | 79 | 80 | try { 81 | $this->_state->setAreaCode('adminhtml'); 82 | 83 | $configJobs = $this->_cronConfig->getJobs(); 84 | 85 | $table = $this->getHelperSet()->get('table'); 86 | $table->setHeaders(['Code', 'Instance', 'Schedule', 'Group']); 87 | 88 | foreach ($configJobs as $group => $jobs) { 89 | foreach ($jobs as $code => $job) { 90 | $instance = $job['instance']; 91 | $method = $job['method']; 92 | $schedule = (isset($job['schedule']) ? $job['schedule'] : ""); 93 | $itemData = [ 94 | $code, 95 | $instance . "::" . $method, 96 | $schedule, 97 | $group, 98 | ]; 99 | $table->addRow($itemData); 100 | } 101 | } 102 | 103 | $table->render($output); 104 | 105 | $returnValue = \Magento\Framework\Console\Cli::RETURN_SUCCESS; 106 | } catch (\Magento\Framework\Exception\LocalizedException $e) { 107 | $output->writeln($e->getMessage()); 108 | $returnValue = \Magento\Framework\Console\Cli::RETURN_FAILURE; 109 | } 110 | 111 | 112 | return $returnValue; 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /Console/Command/Task/Listing.php: -------------------------------------------------------------------------------- 1 | 14 | * $ bin/magento help wyomind:cronscheduler:task:list 15 | * Usage: 16 | * wyomind:cronscheduler:task:list 17 | * 18 | * Options: 19 | * --help (-h) Display this help message 20 | * --quiet (-q) Do not output any message 21 | * --verbose (-v|vv|vvv) Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug 22 | * --version (-V) Display this application version 23 | * --ansi Force ANSI output 24 | * --no-ansi Disable ANSI output 25 | * --no-interaction (-n) Do not ask any interactive question 26 | * 27 | */ 28 | class Listing extends \Symfony\Component\Console\Command\Command 29 | { 30 | 31 | /** 32 | * @var \Wyomind\CronScheduler\Model\ResourceModel\Task\Collection 33 | */ 34 | protected $_taskCollection = null; 35 | 36 | /** 37 | * @var \Magento\Framework\App\State 38 | */ 39 | protected $_state = null; 40 | 41 | /** 42 | * Class constructor 43 | * @param \Wyomind\CronScheduler\Model\ResourceModel\Task\Collection $taskCollection 44 | * @param \Magento\Framework\App\State $state 45 | */ 46 | public function __construct( 47 | \Wyomind\CronScheduler\Model\ResourceModel\Task\Collection $taskCollection, 48 | \Magento\Framework\App\State $state 49 | ) 50 | { 51 | $this->_state = $state; 52 | $this->_taskCollection = $taskCollection->sortByScheduledAtDesc(); 53 | parent::__construct(); 54 | } 55 | 56 | /** 57 | * Configure the command line 58 | */ 59 | protected function configure() 60 | { 61 | $this->setName('wyomind:cronscheduler:task:list') 62 | ->setDescription(__('Cron Scheduler : get list of all tasks')) 63 | ->setDefinition([]); 64 | parent::configure(); 65 | } 66 | 67 | /** 68 | * Execute the command line 69 | * @param \Symfony\Component\Console\Input\InputInterface $input 70 | * @param \Symfony\Component\Console\Output\OutputInterface $output 71 | * @return int \Magento\Framework\Console\Cli::RETURN_FAILURE or \Magento\Framework\Console\Cli::RETURN_SUCCESS 72 | */ 73 | protected function execute( 74 | \Symfony\Component\Console\Input\InputInterface $input, 75 | \Symfony\Component\Console\Output\OutputInterface $output 76 | ) 77 | { 78 | 79 | 80 | try { 81 | $this->_state->setAreaCode('adminhtml'); 82 | 83 | $table = $this->getHelperSet()->get('table'); 84 | $table->setHeaders(['Id', 'Code', 'Status', 'Created at', 'Schedule at', 'Executed at', 'Finished at']); 85 | foreach ($this->_taskCollection as $task) { 86 | 87 | $itemData = [ 88 | $task->getScheduleId(), 89 | $task->getJobCode(), 90 | $task->getStatus(), 91 | $task->getCreatedAt(), 92 | $task->getScheduledAt(), 93 | $task->getExecutedAt(), 94 | $task->getFinishedAt() 95 | ]; 96 | $table->addRow($itemData); 97 | } 98 | 99 | $table->render($output); 100 | 101 | $returnValue = \Magento\Framework\Console\Cli::RETURN_SUCCESS; 102 | } catch (\Magento\Framework\Exception\LocalizedException $e) { 103 | $output->writeln($e->getMessage()); 104 | $returnValue = \Magento\Framework\Console\Cli::RETURN_FAILURE; 105 | } 106 | 107 | 108 | return $returnValue; 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /Console/Command/Task/Show.php: -------------------------------------------------------------------------------- 1 | 14 | * $ bin/magento help wyomind:cronscheduler:show 15 | * Usage: 16 | * wyomind:cronscheduler:task:show id 17 | * 18 | * Arguments: 19 | * task_id The id of the task 20 | * 21 | * Options: 22 | * --help (-h) Display this help message 23 | * --quiet (-q) Do not output any message 24 | * --verbose (-v|vv|vvv) Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug 25 | * --version (-V) Display this application version 26 | * --ansi Force ANSI output 27 | * --no-ansi Disable ANSI output 28 | * --no-interaction (-n) Do not ask any interactive question 29 | * 30 | */ 31 | class Show extends \Symfony\Component\Console\Command\Command 32 | { 33 | 34 | /** 35 | * Command line argument name 36 | */ 37 | const TASK_ID_ARG = "task_id"; 38 | 39 | /** 40 | * @var \Magento\Cron\Model\ScheduleFactory 41 | */ 42 | protected $_taskModelFactory = null; 43 | 44 | /** 45 | * @var \Magento\Framework\App\State 46 | */ 47 | protected $_state = null; 48 | 49 | /** 50 | * Class constructor 51 | * @param \Magento\Cron\Model\ScheduleFactory $taskModelFactory 52 | * @param \Magento\Framework\App\State $state 53 | */ 54 | public function __construct( 55 | \Magento\Cron\Model\ScheduleFactory $taskModelFactory, 56 | \Magento\Framework\App\State $state 57 | ) 58 | { 59 | $this->_state = $state; 60 | $this->_taskModelFactory = $taskModelFactory; 61 | parent::__construct(); 62 | } 63 | 64 | /** 65 | * Configure the command line 66 | */ 67 | protected function configure() 68 | { 69 | $this->setName('wyomind:cronscheduler:task:show') 70 | ->setDescription(__('Cron Scheduler : get details of a task')) 71 | ->setDefinition([ 72 | new \Symfony\Component\Console\Input\InputArgument( 73 | self::TASK_ID_ARG, \Symfony\Component\Console\Input\InputArgument::REQUIRED, __('The id of the task') 74 | ) 75 | ]); 76 | parent::configure(); 77 | } 78 | 79 | /** 80 | * Execute the command line 81 | * @param \Symfony\Component\Console\Input\InputInterface $input 82 | * @param \Symfony\Component\Console\Output\OutputInterface $output 83 | * @return int \Magento\Framework\Console\Cli::RETURN_FAILURE or \Magento\Framework\Console\Cli::RETURN_SUCCESS 84 | */ 85 | protected function execute( 86 | \Symfony\Component\Console\Input\InputInterface $input, 87 | \Symfony\Component\Console\Output\OutputInterface $output 88 | ) 89 | { 90 | 91 | 92 | try { 93 | $this->_state->setAreaCode('adminhtml'); 94 | $taskId = $input->getArgument(self::TASK_ID_ARG); 95 | $task = $this->_taskModelFactory->create()->load($taskId); 96 | 97 | 98 | 99 | $table = $this->getHelperSet()->get('table'); 100 | $table->setHeaders(['Id', 'Code', 'Status', 'Created at', 'Schedule at', 'Executed at', 'Finished at', 'Messages']); 101 | 102 | $itemData = [ 103 | $task->getScheduleId(), 104 | $task->getJobCode(), 105 | $task->getStatus(), 106 | $task->getCreatedAt(), 107 | $task->getScheduledAt(), 108 | $task->getExecutedAt(), 109 | $task->getFinishedAt(), 110 | $task->getMessages() 111 | ]; 112 | $table->addRow($itemData); 113 | 114 | $table->render($output); 115 | 116 | $returnValue = \Magento\Framework\Console\Cli::RETURN_SUCCESS; 117 | } catch (\Magento\Framework\Exception\LocalizedException $e) { 118 | $output->writeln($e->getMessage()); 119 | $returnValue = \Magento\Framework\Console\Cli::RETURN_FAILURE; 120 | } 121 | 122 | 123 | return $returnValue; 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Job/GenerateSchedule.php: -------------------------------------------------------------------------------- 1 | cron = $cron; 53 | $this->observer = $observer; 54 | $this->resultForwardFactory = $resultForwardFactory; 55 | parent::__construct($context); 56 | } 57 | 58 | /** 59 | * Execute action 60 | */ 61 | public function execute() 62 | { 63 | $this->cron->execute($this->observer); 64 | $params = $this->getRequest()->getParams(); 65 | if (isset($params['redirect'])) { 66 | return $this->resultRedirectFactory->create()->setPath(str_replace("_","/",$params['redirect']), []); 67 | } 68 | return $this->resultRedirectFactory->create()->setPath(\Wyomind\CronScheduler\Helper\Url::JOB_CONFIG, []); 69 | } 70 | 71 | /** 72 | * Is the action allowed? 73 | * @return boolean 74 | */ 75 | protected function _isAllowed() 76 | { 77 | return $this->_authorization->isAllowed('Wyomind_CronScheduler::'.$this->_aclResource); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Job/Listing.php: -------------------------------------------------------------------------------- 1 | _resultPageFactory = $resultPageFactory; 44 | $this->heartBeatHelper = $heartBeatHelper; 45 | parent::__construct($context); 46 | } 47 | 48 | /** 49 | * Execute action 50 | */ 51 | public function execute() 52 | { 53 | $this->heartBeatHelper->getLastHearBeatMessage(); 54 | $resultPage = $this->_resultPageFactory->create(); 55 | $resultPage->setActiveMenu("Magento_Backend::system"); 56 | $resultPage->getConfig()->getTitle()->prepend(__('Cron Scheduler > Jobs Configuration')); 57 | $resultPage->addBreadcrumb(__('Cron Scheduler'), __('Cron Scheduler')); 58 | return $resultPage; 59 | } 60 | 61 | 62 | /** 63 | * Is the action allowed? 64 | * @return boolean 65 | */ 66 | protected function _isAllowed() 67 | { 68 | return $this->_authorization->isAllowed('Wyomind_CronScheduler::'.$this->_aclResource); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Task.php: -------------------------------------------------------------------------------- 1 | _resultPageFactory = $resultPageFactory; 44 | $this->heartBeatHelper = $heartBeatHelper; 45 | parent::__construct($context); 46 | $this->heartBeatHelper->getLastHearBeatMessage(); 47 | } 48 | 49 | /** 50 | * Is the action allowed? 51 | * @return boolean 52 | */ 53 | protected function _isAllowed() 54 | { 55 | return $this->_authorization->isAllowed('Wyomind_CronScheduler::'.$this->_aclResource); 56 | } 57 | 58 | /** 59 | * Execute the action 60 | */ 61 | abstract public function execute(); 62 | } 63 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Task/Listing.php: -------------------------------------------------------------------------------- 1 | _resultPageFactory->create(); 29 | $resultPage->setActiveMenu("Magento_Backend::system"); 30 | $resultPage->getConfig()->getTitle()->prepend(__('Cron Scheduler > Tasks List')); 31 | $resultPage->addBreadcrumb(__('Cron Scheduler'), __('Cron Scheduler')); 32 | return $resultPage; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Task/MassDelete.php: -------------------------------------------------------------------------------- 1 | filter = $filter; 49 | $this->collectionFactory = $taskCollectionFactory; 50 | } 51 | 52 | /** 53 | * Execute the action 54 | * @return \Magento\Backend\Model\View\Result\Redirect 55 | * @throws \Magento\Framework\Exception\LocalizedException|\Exception 56 | */ 57 | public function execute() 58 | { 59 | try { 60 | $collection = $this->filter->getCollection($this->collectionFactory->create()); 61 | return $this->massAction($collection); 62 | } catch (\Exception $e) { 63 | $this->messageManager->addError($e->getMessage()); 64 | $resultRedirect = $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT); 65 | return $resultRedirect->setPath($this->redirectUrl); 66 | } 67 | } 68 | 69 | /** 70 | * Execute the mass delete action 71 | * @param type $collection 72 | * @return type 73 | */ 74 | public function massAction($collection) 75 | { 76 | 77 | $resultRedirect = $this->resultRedirectFactory->create(); 78 | $resultRedirect->setPath($this->redirectUrl); 79 | 80 | if (!$this->_authorization->isAllowed('Wyomind_CronScheduler::' . $this->_aclResource)) { 81 | $this->messageManager->addError(__("You are not allowed to delete tasks")); 82 | } else { 83 | 84 | $counter = 0; 85 | foreach ($collection as $item) { 86 | $item->delete(); 87 | $counter ++; 88 | } 89 | if ($counter >= 2) { 90 | $this->messageManager->addSuccess(__("%1 tasks have been deleted", $counter)); 91 | } else { 92 | $this->messageManager->addSuccess(__("%1 task has been deleted", $counter)); 93 | } 94 | } 95 | 96 | return $resultRedirect; 97 | } 98 | 99 | /** 100 | * Is the action allowed? 101 | * @return boolean 102 | */ 103 | protected function _isAllowed() 104 | { 105 | return true; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Task/Timeline.php: -------------------------------------------------------------------------------- 1 | _resultPageFactory->create(); 28 | $resultPage->setActiveMenu("Magento_Backend::system"); 29 | $resultPage->getConfig()->getTitle()->prepend(__('Cron Scheduler > Timeline')); 30 | $resultPage->addBreadcrumb(__('CronScheduler'), __('CronScheduler')); 31 | return $resultPage; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Task/View.php: -------------------------------------------------------------------------------- 1 | _jsonHelper = $jsonHelper; 51 | $this->_scheduleFactory = $scheduleFactory; 52 | $this->_taskHelper = $taskHelper; 53 | parent::__construct($context); 54 | } 55 | 56 | /** 57 | * Action to view the details of a tesk 58 | * @return \Magento\Framework\View\Result\Page 59 | */ 60 | public function execute() 61 | { 62 | $scheduleId = $this->getRequest()->getParam("schedule_id"); 63 | $schedule = $this->_scheduleFactory->create()->load($scheduleId); 64 | $data = $schedule->getData(); 65 | if (!empty($data)) { 66 | $data['messages'] = nl2br($data['messages']); 67 | $data['status'] = $this->_taskHelper->getStatusRenderer($data['status']); 68 | $data['origin'] = $this->_taskHelper->getOriginToString($data['origin']); 69 | } else { 70 | $data['error'] = __("This task doesn't exist anymore"); 71 | } 72 | $this->getResponse()->representJson($this->_jsonHelper->jsonEncode($data)); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /Cron/HeartBeat.php: -------------------------------------------------------------------------------- 1 | setMessages(__("Cron is alive")); 25 | $schedule->save(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /Helper/HeartBeat.php: -------------------------------------------------------------------------------- 1 | scheduleCollectionFactory = $scheduleCollectionFactory; 53 | $this->datetime = $datetime; 54 | $this->messageManager = $messageManager; 55 | $explodedVersion = explode("-", $productMetaData->getVersion()); // in case of 2.2.0-dev 56 | $this->_magentoVersion = $explodedVersion[0]; 57 | parent::__construct($context); 58 | } 59 | 60 | /** 61 | * Check the existence of the last heartbeat, and when it has been ticked for the last time 62 | * Adds a message in the message manager with the result 63 | */ 64 | public function getLastHearBeatMessage() 65 | { 66 | if (version_compare($this->_magentoVersion, "2.2.0") >= 0) { 67 | $currentTime = $this->datetime->date('U'); 68 | } else { 69 | $currentTime = $this->datetime->date('U') + $this->datetime->getGmtOffset('hours') * 60 * 60; 70 | } 71 | $lastHeartBeat = strtotime($this->scheduleCollectionFactory->create()->getLastHeartBeat()); 72 | if ($lastHeartBeat != null) { 73 | $diff = floor(($currentTime - $lastHeartBeat) / 60); // in minutes 74 | if ($diff > 5) { 75 | if ($diff >= 60) { 76 | $diff = floor($diff / 60); 77 | $this->messageManager->addError(__("Last heartbeat is older than %1 hour%2", $diff, ($diff > 1) ? "s" : "")); 78 | } else { 79 | $this->messageManager->addError(__("Last heartbeat is older than %1 minute%2", $diff, ($diff > 1) ? "s" : "")); 80 | } 81 | } else { 82 | $this->messageManager->addSuccess(__("Last heartbeat was %1 minute%2 ago", $diff, ($diff > 1) ? "s" : "")); 83 | } 84 | } else { 85 | $this->messageManager->addError(__("No heartbeat found")); 86 | } 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /Helper/Job.php: -------------------------------------------------------------------------------- 1 | _cronConfig = $cronConfig; 32 | parent::__construct($context); 33 | } 34 | 35 | /** 36 | * Get the job data 37 | * (independant method in order to be able to plugin it in the Pro version) 38 | * @return array 39 | */ 40 | public function getJobData() 41 | { 42 | $data = []; 43 | $configJobs = $this->_cronConfig->getJobs(); 44 | foreach ($configJobs as $group => $jobs) { 45 | foreach ($jobs as $code => $job) { 46 | $job['code'] = $code; 47 | $job['group'] = $group; 48 | if (!isset($job['config_schedule'])) { 49 | if (isset($job['schedule'])) { 50 | $job['config_schedule'] = $job['schedule']; 51 | } else { 52 | $job['config_schedule'] = ""; 53 | } 54 | } 55 | $data[$code] = $job; 56 | } 57 | } 58 | return $data; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /Helper/Task.php: -------------------------------------------------------------------------------- 1 | get("\Magento\Framework\App\RequestInterface"); 30 | $state = $objectManager->get("\Magento\Framework\App\State"); 31 | if ((isset($request->getParams()['redirect']))) { // from the backend 32 | $schedule->setOrigin(self::ORIGIN_BACKEND); 33 | $schedule->setIp($request->getClientIp()); 34 | $auth = $objectManager->get("\Magento\Backend\Model\Auth"); 35 | if ($auth->getUser() != null) { 36 | $schedule->setUser($auth->getUser()->getUsername()); 37 | } else { 38 | $schedule->setUser("Unknown user"); 39 | } 40 | } else { 41 | if (php_sapi_name() == "cli") { // bin/magento OR crontab OR separate process for the group 42 | $schedule->setOrigin(self::ORIGIN_CLI); 43 | $schedule->setUser(utf8_encode(get_current_user())); 44 | } else { 45 | if ($state->getAreaCode() == "webapi_rest") { // Web API? 46 | $schedule->setOrigin(self::ORIGIN_WEBAPI); 47 | $schedule->setIp($request->getClientIp()); 48 | } else { // should never go into this case 49 | $schedule->setOrigin(self::ORIGIN_CRON); 50 | $schedule->setUser(utf8_encode(get_current_user())); 51 | } 52 | } 53 | } 54 | } 55 | 56 | /** 57 | * Get the cron task status renderer information 58 | * @param string $status 59 | * @return array first item : css class / second item : label 60 | */ 61 | public function getStatusRenderer($status) 62 | { 63 | $type = ""; 64 | $inner = ""; 65 | switch ($status) { 66 | case \Magento\Cron\Model\Schedule::STATUS_ERROR; 67 | $type = 'major'; 68 | $inner = __("ERROR"); 69 | break; 70 | case \Magento\Cron\Model\Schedule::STATUS_MISSED; 71 | $type = 'major'; 72 | $inner = __("MISSED"); 73 | break; 74 | case \Magento\Cron\Model\Schedule::STATUS_RUNNING; 75 | $type = 'running'; 76 | $inner = __("RUNNING"); 77 | break; 78 | case \Magento\Cron\Model\Schedule::STATUS_PENDING; 79 | $type = 'minor'; 80 | $inner = __("PENDING"); 81 | break; 82 | case \Magento\Cron\Model\Schedule::STATUS_SUCCESS; 83 | $type = 'notice'; 84 | $inner = __("SUCCESS"); 85 | break; 86 | } 87 | return [$type, $inner]; 88 | } 89 | 90 | public function getOriginToString($origin) 91 | { 92 | switch ($origin) { 93 | case self::ORIGIN_CRON: 94 | return __("Cron"); 95 | case self::ORIGIN_BACKEND: 96 | return __("Backend"); 97 | case self::ORIGIN_CLI: 98 | return __("CLI"); 99 | case self::ORIGIN_WEBAPI: 100 | return __("WebAPI"); 101 | } 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /Helper/Url.php: -------------------------------------------------------------------------------- 1 | The extension is already compatible with Magento 2.2 6 | 7 | ##### Magento 2.1 8 | 9 | $ cp app/code/Wyomind/CronScheduler/Plugin/Cron/Observer/ProcessCronQueueObserverM21.php app/code/Wyomind/CronScheduler/Plugin/Cron/Observer/ProcessCronQueueObserver.php 10 | 11 | ##### Magento 2.0 12 | 13 | $ cp app/code/Wyomind/CronScheduler/Plugin/Cron/Observer/ProcessCronQueueObserverM20.php app/code/Wyomind/CronScheduler/Plugin/Cron/Observer/ProcessCronQueueObserver.php 14 | $ cp app/code/Wyomind/CronScheduler/view/adminhtml/ui_component/cronscheduler_job_listing_Mage20.xml app/code/Wyomind/CronScheduler/view/adminhtml/ui_component/cronscheduler_job_listing.xml 15 | $ cp app/code/Wyomind/CronScheduler/view/adminhtml/ui_component/cronscheduler_task_listing_Mage20.xml app/code/Wyomind/CronScheduler/view/adminhtml/ui_component/cronscheduler_task_listing.xml 16 | 17 | -------------------------------------------------------------------------------- /Model/ResourceModel/Task/Collection.php: -------------------------------------------------------------------------------- 1 | getSelect()->order('scheduled_at DESC'); 24 | return $this; 25 | } 26 | 27 | /** 28 | * Get distinct job codes based on the tasks scheduled 29 | * @return \Wyomind\CronScheduler\Model\ResourceModel\Task\Collection 30 | */ 31 | public function getJobCodes() 32 | { 33 | $this->getSelect()->reset('columns') 34 | ->columns('DISTINCT(job_code) as job_code') 35 | ->order('job_code ASC'); 36 | 37 | return $this; 38 | } 39 | 40 | /** 41 | * Get distinct status based on the tasks scheduled 42 | * @link http://www.wyomind.com wyomind.com 43 | * @see \Wyomind\CronScheduler\Block\Adminhtml\Task\Timeline 44 | * @return \Wyomind\CronScheduler\Model\ResourceModel\Task\Collection 45 | */ 46 | public function getTaskStatuses() 47 | { 48 | $this->getSelect()->reset('columns') 49 | ->columns('DISTINCT(status) as status') 50 | ->order('status ASC'); 51 | 52 | return $this; 53 | } 54 | 55 | /** 56 | * Get the last heart beat found execution date time 57 | * @return string | null 58 | */ 59 | public function getLastHeartbeat() 60 | { 61 | $this->getSelect()->reset('columns') 62 | ->columns(['executed_at']) 63 | ->where('executed_at is not null and job_code ="cronscheduler_heartbeat"') 64 | ->order('finished_at desc'); 65 | 66 | $last = $this->getFirstItem(); 67 | if ($last) { 68 | return $last->getExecutedAt(); 69 | } else { 70 | return null; 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /Plugin/Cron/Observer/ProcessCronQueueObserver.php: -------------------------------------------------------------------------------- 1 | logger = $logger; // because private 73 | $this->state = $state; // because private 74 | $this->_eventManager = $eventManager; 75 | $this->_taskHelper = $taskHelper; 76 | $jobGroupsRoot = $this->_config->getJobs(); 77 | $groups = array_values($jobGroupsRoot); 78 | foreach (array_values($groups) as $jobs) { 79 | foreach ($jobs as $job) { 80 | if (isset($job['code'])) { 81 | $this->_jobStatus[$job['code']] = isset($job['status']) ? $job['status'] : 1; 82 | } elseif (isset($job['name'])) { 83 | $this->_jobStatus[$job['name']] = isset($job['status']) ? $job['status'] : 1; 84 | } 85 | } 86 | } 87 | } 88 | 89 | /** 90 | * Override the observer on cron:run 91 | * @param \Magento\Cron\Observer\ProcessCronQueueObserver $subject 92 | * @param \Closure $proceed 93 | * @param \Magento\Framework\Event\Observer $observer 94 | * @event cronscheduler_task_failed(\Magento\Cron\Model\Scheduler $task, array $error) when a task fails 95 | * @event cronscheduler_task_succes(\Magento\Cron\Model\Scheduler $task) when a task is successful 96 | * @event cronscheduler_task_run_before(\Magento\Cron\Model\Scheduler $task) before running a task 97 | * @event cronscheduler_task_run when(\Magento\Cron\Model\Scheduler $task) running a task 98 | * @event cronscheduler_task_run_after(\Magento\Cron\Model\Scheduler $task) after running a task 99 | */ 100 | public function aroundExecute( 101 | \Magento\Cron\Observer\ProcessCronQueueObserver $subject, 102 | \Closure $proceed, 103 | \Magento\Framework\Event\Observer $observer) 104 | { 105 | 106 | // 107 | // current task ran 108 | $currentShedule = null; 109 | 110 | // set the shutdown/error_hnalder functions to catch a task that throws a fatal error (like parsing error) 111 | register_shutdown_function(function() use (&$currentSchedule) { 112 | $lastError = error_get_last(); 113 | if ($lastError) { 114 | if ($currentSchedule != null) { 115 | $s = $currentSchedule; 116 | $s->setMessages($lastError['message']); 117 | $s->setStatus(\Magento\Cron\Model\Schedule::STATUS_ERROR); 118 | $s->setErrorFile($lastError['file']); 119 | $s->setErrorLine($lastError['line']); 120 | $this->_taskHelper->setTrace($s); 121 | $s->save(); 122 | $this->_eventManager->dispatch('cronscheduler_task_failed', ['task' => $s]); 123 | } 124 | } 125 | }); 126 | set_error_handler(function($errorLevel, 127 | $errorMessage, 128 | $errorFile, 129 | $errorLine, 130 | $errorContext) use (&$currentSchedule) { 131 | 132 | if ($errorLevel != "" && $currentSchedule != null) { 133 | $s = $currentSchedule; 134 | $s->setMessages($errorMessage); 135 | $s->setStatus(\Magento\Cron\Model\Schedule::STATUS_ERROR); 136 | $s->setErrorFile($errorFile); 137 | $s->setErrorLine($errorLine); 138 | $this->_taskHelper->setTrace($s); 139 | $s->save(); 140 | $this->_eventManager->dispatch('cronscheduler_task_failed', ['task' => $s]); 141 | } 142 | }); 143 | // 144 | 145 | 146 | $pendingJobs = $this->_getPendingSchedules(); 147 | $currentTime = $this->dateTime->gmtTimestamp(); 148 | $jobGroupsRoot = $this->_config->getJobs(); 149 | 150 | 151 | $phpPath = $this->phpExecutableFinder->find() ? : 'php'; 152 | 153 | foreach ($jobGroupsRoot as $groupId => $jobsRoot) { 154 | 155 | $this->_cleanup($groupId); 156 | $this->_generate($groupId); 157 | 158 | if ($this->_request->getParam('group') !== null && $this->_request->getParam('group') !== '\'' . ($groupId) . '\'' && $this->_request->getParam('group') !== $groupId) { 159 | continue; 160 | } 161 | if (($this->_request->getParam(self::STANDALONE_PROCESS_STARTED) !== '1') && ( 162 | $this->_scopeConfig->getValue( 163 | 'system/cron/' . $groupId . '/use_separate_process', \Magento\Store\Model\ScopeInterface::SCOPE_STORE 164 | ) == 1 165 | )) { 166 | 167 | $this->_shell->execute( 168 | $phpPath . ' %s cron:run --group=' . $groupId . ' --' . \Magento\Framework\Console\Cli::INPUT_KEY_BOOTSTRAP . '=' 169 | . self::STANDALONE_PROCESS_STARTED . '=1', [ 170 | BP . '/bin/magento' 171 | ] 172 | ); 173 | continue; 174 | } 175 | 176 | 177 | foreach ($pendingJobs as $schedule) { 178 | 179 | // 180 | // set the current task running 181 | $currentSchedule = $schedule; 182 | $this->_eventManager->dispatch('cronscheduler_task_run_before', ['task' => $schedule]); 183 | // 184 | 185 | $jobConfig = isset($jobsRoot[$schedule->getJobCode()]) ? $jobsRoot[$schedule->getJobCode()] : null; 186 | if (!$jobConfig /* */ || isset($jobConfig['status']) && $jobConfig['status'] == 0 /* */) { 187 | continue; 188 | } 189 | 190 | $scheduledTime = strtotime($schedule->getScheduledAt()); 191 | if ($scheduledTime > $currentTime) { 192 | continue; 193 | } 194 | 195 | try { 196 | if ($schedule->tryLockJob()) { 197 | 198 | // 199 | $this->_eventManager->dispatch('cronscheduler_task_run', ['task' => $schedule]); 200 | // 201 | 202 | $this->_runJob($scheduledTime, $currentTime, $jobConfig, $schedule, $groupId); 203 | } 204 | // 205 | $this->_taskHelper->setTrace($schedule); 206 | $schedule->save(); 207 | $this->_eventManager->dispatch('cronscheduler_task_success', ['task' => $schedule]); 208 | // 209 | } catch (\Exception $e) { 210 | $schedule->setMessages($e->getMessage()); 211 | 212 | 213 | // 214 | $schedule->setErrorFile($e->getFile()); 215 | $schedule->setErrorLine($e->getLine()); 216 | $this->_taskHelper->setTrace($schedule); 217 | $schedule->setStatus(\Magento\Cron\Model\Schedule::STATUS_ERROR); 218 | $schedule->save(); 219 | // 220 | 221 | if ($schedule->getStatus() === \Magento\Cron\Model\Schedule::STATUS_ERROR) { 222 | $this->logger->critical($e); 223 | } 224 | if ($schedule->getStatus() === \Magento\Cron\Model\Schedule::STATUS_MISSED && $this->state->getMode() === \Magento\Cron\Model\Schedule::MODE_DEVELOPER 225 | ) { 226 | $this->logger->info( 227 | sprintf( 228 | "%s Schedule Id: %s Job Code: %s", $schedule->getMessages(), $schedule->getScheduleId(), $schedule->getJobCode() 229 | ) 230 | ); 231 | } 232 | 233 | // 234 | $this->_eventManager->dispatch('cronscheduler_task_failed', ['task' => $schedule]); 235 | // 236 | } 237 | 238 | // 239 | $this->_eventManager->dispatch('cronscheduler_task_run_after', ['task' => $schedule]); 240 | // 241 | } 242 | 243 | $this->_generate($groupId); 244 | $this->_cleanup($groupId); 245 | } 246 | } 247 | 248 | /** 249 | * Save a schedule only if the job is enable (pro version => cannot plugin it because of protected modifier) 250 | * @param string $jobCode 251 | * @param string $cronExpression 252 | * @param int $timeInterval 253 | * @param array $exists 254 | * @return void 255 | */ 256 | protected function saveSchedule($jobCode, 257 | $cronExpression, 258 | $timeInterval, 259 | $exists) 260 | { 261 | if (isset($this->_jobStatus[$jobCode]) && $this->_jobStatus[$jobCode]) { 262 | parent::saveSchedule($jobCode, $cronExpression, $timeInterval, $exists); 263 | } 264 | } 265 | 266 | } 267 | -------------------------------------------------------------------------------- /Plugin/Cron/Observer/ProcessCronQueueObserver_2.0.php: -------------------------------------------------------------------------------- 1 | _eventManager = $eventManager; 61 | $this->_taskHelper = $taskHelper; 62 | $jobGroupsRoot = $this->_config->getJobs(); 63 | $groups = array_values($jobGroupsRoot); 64 | foreach (array_values($groups) as $jobs) { 65 | foreach ($jobs as $job) { 66 | if (isset($job['code'])) { 67 | $this->_jobStatus[$job['code']] = isset($job['status']) ? $job['status'] : 1; 68 | } elseif (isset($job['name'])) { 69 | $this->_jobStatus[$job['name']] = isset($job['status']) ? $job['status'] : 1; 70 | } 71 | } 72 | } 73 | } 74 | 75 | /** 76 | * Override the observer on cron:run 77 | * @param \Magento\Cron\Observer\ProcessCronQueueObserver $subject 78 | * @param \Closure $proceed 79 | * @param \Magento\Framework\Event\Observer $observer 80 | * @event cronscheduler_task_failed(\Magento\Cron\Model\Scheduler $task, array $error) when a task fails 81 | * @event cronscheduler_task_succes(\Magento\Cron\Model\Scheduler $task) when a task is successful 82 | * @event cronscheduler_task_run_before(\Magento\Cron\Model\Scheduler $task) before running a task 83 | * @event cronscheduler_task_run when(\Magento\Cron\Model\Scheduler $task) running a task 84 | * @event cronscheduler_task_run_after(\Magento\Cron\Model\Scheduler $task) after running a task 85 | */ 86 | public function aroundExecute( 87 | \Magento\Cron\Observer\ProcessCronQueueObserver $subject, 88 | \Closure $proceed, 89 | \Magento\Framework\Event\Observer $observer) 90 | { 91 | 92 | // 93 | // current task ran 94 | $currentShedule = null; 95 | 96 | // set the shutdown/error_hnalder functions to catch a task that throws a fatal error (like parsing error) 97 | register_shutdown_function(function() use (&$currentSchedule) { 98 | $lastError = error_get_last(); 99 | if ($lastError) { 100 | if ($currentSchedule != null) { 101 | $s = $currentSchedule; 102 | $s->setMessages($lastError['message']); 103 | $s->setStatus(\Magento\Cron\Model\Schedule::STATUS_ERROR); 104 | $s->setErrorFile($lastError['file']); 105 | $s->setErrorLine($lastError['line']); 106 | $this->_taskHelper->setTrace($s); 107 | $s->save(); 108 | $this->_eventManager->dispatch('cronscheduler_task_failed', ['task' => $s]); 109 | } 110 | } 111 | }); 112 | set_error_handler(function($errorLevel, 113 | $errorMessage, 114 | $errorFile, 115 | $errorLine, 116 | $errorContext) use (&$currentSchedule) { 117 | 118 | if ($errorLevel != "" && $currentSchedule != null) { 119 | $s = $currentSchedule; 120 | $s->setMessages($errorMessage); 121 | $s->setStatus(\Magento\Cron\Model\Schedule::STATUS_ERROR); 122 | $s->setErrorFile($errorFile); 123 | $s->setErrorLine($errorLine); 124 | $this->_taskHelper->setTrace($s); 125 | $s->save(); 126 | $this->_eventManager->dispatch('cronscheduler_task_failed', ['task' => $s]); 127 | } 128 | }); 129 | // 130 | 131 | 132 | $pendingJobs = $this->_getPendingSchedules(); 133 | $currentTime = $this->timezone->scopeTimeStamp(); 134 | $jobGroupsRoot = $this->_config->getJobs(); 135 | 136 | $phpExecutable = (new \Symfony\Component\Process\PhpExecutableFinder())->find() ? : 'php'; 137 | 138 | 139 | foreach ($jobGroupsRoot as $groupId => $jobsRoot) { 140 | if ($this->_request->getParam('group') !== null && $this->_request->getParam('group') !== '\'' . ($groupId) . '\'' && $this->_request->getParam('group') !== $groupId) { 141 | continue; 142 | } 143 | if (($this->_request->getParam(self::STANDALONE_PROCESS_STARTED) !== '1') && ( 144 | $this->_scopeConfig->getValue( 145 | 'system/cron/' . $groupId . '/use_separate_process', \Magento\Store\Model\ScopeInterface::SCOPE_STORE 146 | ) == 1 147 | )) { 148 | 149 | $this->_shell->execute( 150 | $phpExecutable.' %s cron:run --group=' . $groupId . ' --' . \Magento\Framework\Console\Cli::INPUT_KEY_BOOTSTRAP . '=' 151 | . self::STANDALONE_PROCESS_STARTED . '=1', [ 152 | BP . '/bin/magento' 153 | ] 154 | ); 155 | continue; 156 | } 157 | 158 | 159 | foreach ($pendingJobs as $schedule) { 160 | 161 | // 162 | // set the current task running 163 | $currentSchedule = $schedule; 164 | $this->_eventManager->dispatch('cronscheduler_task_run_before', ['task' => $schedule]); 165 | // 166 | 167 | $jobConfig = isset($jobsRoot[$schedule->getJobCode()]) ? $jobsRoot[$schedule->getJobCode()] : null; 168 | if (!$jobConfig /* */ || isset($jobConfig['status']) && $jobConfig['status'] == 0 /* */) { 169 | continue; 170 | } 171 | 172 | $scheduledTime = strtotime($schedule->getScheduledAt()); 173 | if ($scheduledTime > $currentTime) { 174 | continue; 175 | } 176 | 177 | try { 178 | if ($schedule->tryLockJob()) { 179 | 180 | // 181 | $this->_eventManager->dispatch('cronscheduler_task_run', ['task' => $schedule]); 182 | // 183 | 184 | $this->_runJob($scheduledTime, $currentTime, $jobConfig, $schedule, $groupId); 185 | } 186 | // 187 | $this->_taskHelper->setTrace($schedule); 188 | // 189 | $schedule->save(); 190 | // 191 | $this->_eventManager->dispatch('cronscheduler_task_success', ['task' => $schedule]); 192 | // 193 | } catch (\Exception $e) { 194 | $schedule->setMessages($e->getMessage()); 195 | // 196 | $schedule->setErrorFile($e->getFile()); 197 | $schedule->setErrorLine($e->getLine()); 198 | $this->_taskHelper->setTrace($schedule); 199 | $schedule->setStatus(\Magento\Cron\Model\Schedule::STATUS_ERROR); 200 | $schedule->save(); 201 | $this->_eventManager->dispatch('cronscheduler_task_failed', ['task' => $schedule]); 202 | // 203 | } 204 | 205 | // 206 | $this->_eventManager->dispatch('cronscheduler_task_run_after', ['task' => $schedule]); 207 | // 208 | } 209 | 210 | $this->_generate($groupId); 211 | $this->_cleanup($groupId); 212 | } 213 | } 214 | 215 | /** 216 | * Save a schedule only if the job is enable (pro version => cannot plugin it because of protected modifier) 217 | * @param string $jobCode 218 | * @param string $cronExpression 219 | * @param int $timeInterval 220 | * @param array $exists 221 | * @return void 222 | */ 223 | protected function saveSchedule($jobCode, 224 | $cronExpression, 225 | $timeInterval, 226 | $exists) 227 | { 228 | if (isset($this->_jobStatus[$jobCode]) && $this->_jobStatus[$jobCode]) { 229 | parent::saveSchedule($jobCode, $cronExpression, $timeInterval, $exists); 230 | } 231 | } 232 | 233 | } { 234 | 235 | } 236 | -------------------------------------------------------------------------------- /Plugin/Cron/Observer/ProcessCronQueueObserver_2.1.php: -------------------------------------------------------------------------------- 1 | _eventManager = $eventManager; 59 | $this->_taskHelper = $taskHelper; 60 | $jobGroupsRoot = $this->_config->getJobs(); 61 | $groups = array_values($jobGroupsRoot); 62 | foreach (array_values($groups) as $jobs) { 63 | foreach ($jobs as $job) { 64 | if (isset($job['code'])) { 65 | $this->_jobStatus[$job['code']] = isset($job['status']) ? $job['status'] : 1; 66 | } elseif (isset($job['name'])) { 67 | $this->_jobStatus[$job['name']] = isset($job['status']) ? $job['status'] : 1; 68 | } 69 | } 70 | } 71 | } 72 | 73 | /** 74 | * Override the observer on cron:run 75 | * @param \Magento\Cron\Observer\ProcessCronQueueObserver $subject 76 | * @param \Closure $proceed 77 | * @param \Magento\Framework\Event\Observer $observer 78 | * @event cronscheduler_task_failed(\Magento\Cron\Model\Scheduler $task, array $error) when a task fails 79 | * @event cronscheduler_task_succes(\Magento\Cron\Model\Scheduler $task) when a task is successful 80 | * @event cronscheduler_task_run_before(\Magento\Cron\Model\Scheduler $task) before running a task 81 | * @event cronscheduler_task_run when(\Magento\Cron\Model\Scheduler $task) running a task 82 | * @event cronscheduler_task_run_after(\Magento\Cron\Model\Scheduler $task) after running a task 83 | */ 84 | public function aroundExecute( 85 | \Magento\Cron\Observer\ProcessCronQueueObserver $subject, 86 | \Closure $proceed, 87 | \Magento\Framework\Event\Observer $observer) 88 | { 89 | 90 | // 91 | // current task ran 92 | $currentShedule = null; 93 | 94 | // set the shutdown/error_hnalder functions to catch a task that throws a fatal error (like parsing error) 95 | register_shutdown_function(function() use (&$currentSchedule) { 96 | $lastError = error_get_last(); 97 | if ($lastError) { 98 | if ($currentSchedule != null) { 99 | $s = $currentSchedule; 100 | $s->setMessages($lastError['message']); 101 | $s->setStatus(\Magento\Cron\Model\Schedule::STATUS_ERROR); 102 | $s->setErrorFile($lastError['file']); 103 | $s->setErrorLine($lastError['line']); 104 | $this->_taskHelper->setTrace($s); 105 | $s->save(); 106 | $this->_eventManager->dispatch('cronscheduler_task_failed', ['task' => $s]); 107 | } 108 | } 109 | }); 110 | set_error_handler(function($errorLevel, 111 | $errorMessage, 112 | $errorFile, 113 | $errorLine, 114 | $errorContext) use (&$currentSchedule) { 115 | 116 | if ($errorLevel != "" && $currentSchedule != null) { 117 | $s = $currentSchedule; 118 | $s->setMessages($errorMessage); 119 | $s->setStatus(\Magento\Cron\Model\Schedule::STATUS_ERROR); 120 | $s->setErrorFile($errorFile); 121 | $s->setErrorLine($errorLine); 122 | $this->_taskHelper->setTrace($s); 123 | $s->save(); 124 | $this->_eventManager->dispatch('cronscheduler_task_failed', ['task' => $s]); 125 | } 126 | }); 127 | // 128 | 129 | 130 | $pendingJobs = $this->_getPendingSchedules(); 131 | $currentTime = $this->timezone->scopeTimeStamp(); 132 | $jobGroupsRoot = $this->_config->getJobs(); 133 | 134 | 135 | $phpPath = $this->phpExecutableFinder->find() ? : 'php'; 136 | 137 | foreach ($jobGroupsRoot as $groupId => $jobsRoot) { 138 | if ($this->_request->getParam('group') !== null && $this->_request->getParam('group') !== '\'' . ($groupId) . '\'' && $this->_request->getParam('group') !== $groupId) { 139 | continue; 140 | } 141 | if (($this->_request->getParam(self::STANDALONE_PROCESS_STARTED) !== '1') && ( 142 | $this->_scopeConfig->getValue( 143 | 'system/cron/' . $groupId . '/use_separate_process', \Magento\Store\Model\ScopeInterface::SCOPE_STORE 144 | ) == 1 145 | )) { 146 | 147 | $this->_shell->execute( 148 | $phpPath . ' %s cron:run --group=' . $groupId . ' --' . \Magento\Framework\Console\Cli::INPUT_KEY_BOOTSTRAP . '=' 149 | . self::STANDALONE_PROCESS_STARTED . '=1', [ 150 | BP . '/bin/magento' 151 | ] 152 | ); 153 | continue; 154 | } 155 | 156 | 157 | foreach ($pendingJobs as $schedule) { 158 | 159 | // 160 | // set the current task running 161 | $currentSchedule = $schedule; 162 | $this->_eventManager->dispatch('cronscheduler_task_run_before', ['task' => $schedule]); 163 | // 164 | 165 | $jobConfig = isset($jobsRoot[$schedule->getJobCode()]) ? $jobsRoot[$schedule->getJobCode()] : null; 166 | if (!$jobConfig /* */ || isset($jobConfig['status']) && $jobConfig['status'] == 0 /* */) { 167 | continue; 168 | } 169 | 170 | $scheduledTime = strtotime($schedule->getScheduledAt()); 171 | if ($scheduledTime > $currentTime) { 172 | continue; 173 | } 174 | 175 | try { 176 | if ($schedule->tryLockJob()) { 177 | 178 | // 179 | $this->_eventManager->dispatch('cronscheduler_task_run', ['task' => $schedule]); 180 | // 181 | 182 | $this->_runJob($scheduledTime, $currentTime, $jobConfig, $schedule, $groupId); 183 | } 184 | // 185 | $this->_taskHelper->setTrace($schedule); 186 | // 187 | $schedule->save(); 188 | // 189 | $this->_eventManager->dispatch('cronscheduler_task_success', ['task' => $schedule]); 190 | // 191 | } catch (\Exception $e) { 192 | $schedule->setMessages($e->getMessage()); 193 | // 194 | $schedule->setErrorFile($e->getFile()); 195 | $schedule->setErrorLine($e->getLine()); 196 | $this->_taskHelper->setTrace($schedule); 197 | $schedule->setStatus(\Magento\Cron\Model\Schedule::STATUS_ERROR); 198 | $schedule->save(); 199 | $this->_eventManager->dispatch('cronscheduler_task_failed', ['task' => $schedule]); 200 | // 201 | } 202 | 203 | // 204 | $this->_eventManager->dispatch('cronscheduler_task_run_after', ['task' => $schedule]); 205 | // 206 | } 207 | 208 | $this->_generate($groupId); 209 | $this->_cleanup($groupId); 210 | } 211 | } 212 | 213 | /** 214 | * Save a schedule only if the job is enable (pro version => cannot plugin it because of protected modifier) 215 | * @param string $jobCode 216 | * @param string $cronExpression 217 | * @param int $timeInterval 218 | * @param array $exists 219 | * @return void 220 | */ 221 | protected function saveSchedule($jobCode, 222 | $cronExpression, 223 | $timeInterval, 224 | $exists) 225 | { 226 | if (isset($this->_jobStatus[$jobCode]) && $this->_jobStatus[$jobCode]) { 227 | parent::saveSchedule($jobCode, $cronExpression, $timeInterval, $exists); 228 | } 229 | } 230 | 231 | } 232 | -------------------------------------------------------------------------------- /Plugin/Cron/Observer/ProcessCronQueueObserver_2.2.php: -------------------------------------------------------------------------------- 1 | logger = $logger; // because private 73 | $this->state = $state; // because private 74 | $this->_eventManager = $eventManager; 75 | $this->_taskHelper = $taskHelper; 76 | $jobGroupsRoot = $this->_config->getJobs(); 77 | $groups = array_values($jobGroupsRoot); 78 | foreach (array_values($groups) as $jobs) { 79 | foreach ($jobs as $job) { 80 | if (isset($job['code'])) { 81 | $this->_jobStatus[$job['code']] = isset($job['status']) ? $job['status'] : 1; 82 | } elseif (isset($job['name'])) { 83 | $this->_jobStatus[$job['name']] = isset($job['status']) ? $job['status'] : 1; 84 | } 85 | } 86 | } 87 | } 88 | 89 | /** 90 | * Override the observer on cron:run 91 | * @param \Magento\Cron\Observer\ProcessCronQueueObserver $subject 92 | * @param \Closure $proceed 93 | * @param \Magento\Framework\Event\Observer $observer 94 | * @event cronscheduler_task_failed(\Magento\Cron\Model\Scheduler $task, array $error) when a task fails 95 | * @event cronscheduler_task_succes(\Magento\Cron\Model\Scheduler $task) when a task is successful 96 | * @event cronscheduler_task_run_before(\Magento\Cron\Model\Scheduler $task) before running a task 97 | * @event cronscheduler_task_run when(\Magento\Cron\Model\Scheduler $task) running a task 98 | * @event cronscheduler_task_run_after(\Magento\Cron\Model\Scheduler $task) after running a task 99 | */ 100 | public function aroundExecute( 101 | \Magento\Cron\Observer\ProcessCronQueueObserver $subject, 102 | \Closure $proceed, 103 | \Magento\Framework\Event\Observer $observer) 104 | { 105 | 106 | // 107 | // current task ran 108 | $currentShedule = null; 109 | 110 | // set the shutdown/error_hnalder functions to catch a task that throws a fatal error (like parsing error) 111 | register_shutdown_function(function() use (&$currentSchedule) { 112 | $lastError = error_get_last(); 113 | if ($lastError) { 114 | if ($currentSchedule != null) { 115 | $s = $currentSchedule; 116 | $s->setMessages($lastError['message']); 117 | $s->setStatus(\Magento\Cron\Model\Schedule::STATUS_ERROR); 118 | $s->setErrorFile($lastError['file']); 119 | $s->setErrorLine($lastError['line']); 120 | $this->_taskHelper->setTrace($s); 121 | $s->save(); 122 | $this->_eventManager->dispatch('cronscheduler_task_failed', ['task' => $s]); 123 | } 124 | } 125 | }); 126 | set_error_handler(function($errorLevel, 127 | $errorMessage, 128 | $errorFile, 129 | $errorLine, 130 | $errorContext) use (&$currentSchedule) { 131 | 132 | if ($errorLevel != "" && $currentSchedule != null) { 133 | $s = $currentSchedule; 134 | $s->setMessages($errorMessage); 135 | $s->setStatus(\Magento\Cron\Model\Schedule::STATUS_ERROR); 136 | $s->setErrorFile($errorFile); 137 | $s->setErrorLine($errorLine); 138 | $this->_taskHelper->setTrace($s); 139 | $s->save(); 140 | $this->_eventManager->dispatch('cronscheduler_task_failed', ['task' => $s]); 141 | } 142 | }); 143 | // 144 | 145 | 146 | $pendingJobs = $this->_getPendingSchedules(); 147 | $currentTime = $this->dateTime->gmtTimestamp(); 148 | $jobGroupsRoot = $this->_config->getJobs(); 149 | 150 | 151 | $phpPath = $this->phpExecutableFinder->find() ? : 'php'; 152 | 153 | foreach ($jobGroupsRoot as $groupId => $jobsRoot) { 154 | 155 | $this->_cleanup($groupId); 156 | $this->_generate($groupId); 157 | 158 | if ($this->_request->getParam('group') !== null && $this->_request->getParam('group') !== '\'' . ($groupId) . '\'' && $this->_request->getParam('group') !== $groupId) { 159 | continue; 160 | } 161 | if (($this->_request->getParam(self::STANDALONE_PROCESS_STARTED) !== '1') && ( 162 | $this->_scopeConfig->getValue( 163 | 'system/cron/' . $groupId . '/use_separate_process', \Magento\Store\Model\ScopeInterface::SCOPE_STORE 164 | ) == 1 165 | )) { 166 | 167 | $this->_shell->execute( 168 | $phpPath . ' %s cron:run --group=' . $groupId . ' --' . \Magento\Framework\Console\Cli::INPUT_KEY_BOOTSTRAP . '=' 169 | . self::STANDALONE_PROCESS_STARTED . '=1', [ 170 | BP . '/bin/magento' 171 | ] 172 | ); 173 | continue; 174 | } 175 | 176 | 177 | foreach ($pendingJobs as $schedule) { 178 | 179 | // 180 | // set the current task running 181 | $currentSchedule = $schedule; 182 | $this->_eventManager->dispatch('cronscheduler_task_run_before', ['task' => $schedule]); 183 | // 184 | 185 | $jobConfig = isset($jobsRoot[$schedule->getJobCode()]) ? $jobsRoot[$schedule->getJobCode()] : null; 186 | if (!$jobConfig /* */ || isset($jobConfig['status']) && $jobConfig['status'] == 0 /* */) { 187 | continue; 188 | } 189 | 190 | $scheduledTime = strtotime($schedule->getScheduledAt()); 191 | if ($scheduledTime > $currentTime) { 192 | continue; 193 | } 194 | 195 | try { 196 | if ($schedule->tryLockJob()) { 197 | 198 | // 199 | $this->_eventManager->dispatch('cronscheduler_task_run', ['task' => $schedule]); 200 | // 201 | 202 | $this->_runJob($scheduledTime, $currentTime, $jobConfig, $schedule, $groupId); 203 | } 204 | // 205 | $this->_taskHelper->setTrace($schedule); 206 | $schedule->save(); 207 | $this->_eventManager->dispatch('cronscheduler_task_success', ['task' => $schedule]); 208 | // 209 | } catch (\Exception $e) { 210 | $schedule->setMessages($e->getMessage()); 211 | 212 | 213 | // 214 | $schedule->setErrorFile($e->getFile()); 215 | $schedule->setErrorLine($e->getLine()); 216 | $this->_taskHelper->setTrace($schedule); 217 | $schedule->setStatus(\Magento\Cron\Model\Schedule::STATUS_ERROR); 218 | $schedule->save(); 219 | // 220 | 221 | if ($schedule->getStatus() === \Magento\Cron\Model\Schedule::STATUS_ERROR) { 222 | $this->logger->critical($e); 223 | } 224 | if ($schedule->getStatus() === \Magento\Cron\Model\Schedule::STATUS_MISSED && $this->state->getMode() === \Magento\Cron\Model\Schedule::MODE_DEVELOPER 225 | ) { 226 | $this->logger->info( 227 | sprintf( 228 | "%s Schedule Id: %s Job Code: %s", $schedule->getMessages(), $schedule->getScheduleId(), $schedule->getJobCode() 229 | ) 230 | ); 231 | } 232 | 233 | // 234 | $this->_eventManager->dispatch('cronscheduler_task_failed', ['task' => $schedule]); 235 | // 236 | } 237 | 238 | // 239 | $this->_eventManager->dispatch('cronscheduler_task_run_after', ['task' => $schedule]); 240 | // 241 | } 242 | 243 | $this->_generate($groupId); 244 | $this->_cleanup($groupId); 245 | } 246 | } 247 | 248 | /** 249 | * Save a schedule only if the job is enable (pro version => cannot plugin it because of protected modifier) 250 | * @param string $jobCode 251 | * @param string $cronExpression 252 | * @param int $timeInterval 253 | * @param array $exists 254 | * @return void 255 | */ 256 | protected function saveSchedule($jobCode, 257 | $cronExpression, 258 | $timeInterval, 259 | $exists) 260 | { 261 | if (isset($this->_jobStatus[$jobCode]) && $this->_jobStatus[$jobCode]) { 262 | parent::saveSchedule($jobCode, $cronExpression, $timeInterval, $exists); 263 | } 264 | } 265 | 266 | } 267 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Cron Scheduler for Magento 2 (get more details on wyomind.com) 2 | 3 | #### Monitor the cron tasks that run in the background of your Magento® 2 website 4 | Visualize the cron tasks in a timeline that offers a clear 5 | and understandable view of what is going on 6 | in the background of your Magento® website. 7 | 8 | 9 | 10 | #### List all cron jobs and check the configurations 11 | Get a detailed list to all cron jobs that run under your Magento® 2 website and check the configuration for each task individually 12 | 13 | 14 | 15 | #### Run and schedule cron jobs manually 16 | 17 | No need to access the CLI to run your cron job and/or schedule new jobs. Manually trigger the process from your Magento® back-office. 18 | 19 | 20 | 21 | #### Enjoy a new CLI to manage your cron tasks 22 | 23 | Want to get more of the cron tasks from your CLI? Cron scheduler extends the CLI to give you a better control over the cron jobs 24 | 25 | 26 | 27 | ## Need more ? 28 | * Be notified about cron errors in real time by email and/or in your back-office 29 | * Manage each cron job individually from your Magento® 2 back-office 30 | * Take control over cron jobs through the CLI or the Magento® API 31 | 32 | Checkout Cron Scheduler Pro on wyomind.com 33 | -------------------------------------------------------------------------------- /Setup/InstallSchema.php: -------------------------------------------------------------------------------- 1 | startSetup(); 32 | 33 | /* 34 | * add column `origin` to `cron_schedule` 35 | */ 36 | $installer->getConnection()->addColumn($installer->getTable('cron_schedule'), 'origin', [ 37 | 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, 38 | 'length' => 1, 39 | 'nullable' => true, 40 | 'comment' => 'Where does the schedule has been triggered? 0:Cron, 1:Backend, 2:CLI, 3:WebAPI' 41 | ]); 42 | 43 | /* 44 | * add column `user` to `cron_schedule` 45 | */ 46 | $installer->getConnection()->addColumn($installer->getTable('cron_schedule'), 'user', [ 47 | 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 48 | 'length' => 100, 49 | 'nullable' => true, 50 | 'comment' => 'Who triggered the schedule' 51 | ]); 52 | 53 | /* 54 | * add column `ip` to `cron_schedule` 55 | */ 56 | $installer->getConnection()->addColumn($installer->getTable('cron_schedule'), 'ip', [ 57 | 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 58 | 'length' => 40, 59 | 'nullable' => true, 60 | 'comment' => 'From which IP?' 61 | ]); 62 | 63 | /* 64 | * add column `error_file` to `cron_schedule` 65 | */ 66 | $installer->getConnection()->addColumn($installer->getTable('cron_schedule'), 'error_file', [ 67 | 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 68 | 'length' => 500, 69 | 'nullable' => true, 70 | 'comment' => 'Where (file) the error has been triggered?' 71 | ]); 72 | 73 | /* 74 | * add column `error_line` to `cron_schedule` 75 | */ 76 | $installer->getConnection()->addColumn($installer->getTable('cron_schedule'), 'error_line', [ 77 | 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 78 | 'length' => 6, 79 | 'nullable' => true, 80 | 'comment' => 'Where (line) the error has been triggered?' 81 | ]); 82 | 83 | $installer->endSetup(); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /Setup/UpgradeData.php: -------------------------------------------------------------------------------- 1 | output = $output; 26 | $explodedVersion = explode("-", $productMetaData->getVersion()); 27 | $this->magentoVersion = $explodedVersion[0]; 28 | } 29 | 30 | public function copyFilesByMagentoVersion($files) 31 | { 32 | $this->output->writeln(""); 33 | $version = $this->magentoVersion; 34 | $this->output->writeln("Copying files for Magento " . $version . ""); 35 | 36 | $explodedVersion = explode(".", $version); 37 | $possibleVersion = [ 38 | $version, 39 | $explodedVersion[0] . "." . $explodedVersion[1], 40 | $explodedVersion[0] 41 | ]; 42 | 43 | $path = str_replace("Setup" . DIRECTORY_SEPARATOR . "UpgradeData.php", "", __FILE__); 44 | 45 | 46 | foreach ($files as $file) { 47 | $fullFile = $path . str_replace("/", DIRECTORY_SEPARATOR, $file); 48 | $ext = pathinfo($fullFile, PATHINFO_EXTENSION); 49 | 50 | foreach ($possibleVersion as $v) { 51 | $newFile = str_replace("." . $ext, "_" . $v . "." . $ext, $fullFile); 52 | if (file_exists($newFile)) { 53 | copy($newFile, $fullFile); 54 | break; 55 | } 56 | } 57 | } 58 | } 59 | 60 | public function upgrade(ModuleDataSetupInterface $setup, 61 | ModuleContextInterface $context) 62 | { 63 | 64 | $setup->startSetup(); 65 | 66 | $files = [ 67 | "Plugin/Cron/Observer/ProcessCronQueueObserver.php", 68 | "view/adminhtml/ui_component/cronscheduler_task_listing.xml", 69 | "view/adminhtml/ui_component/cronscheduler_job_listing.xml" 70 | ]; 71 | $this->copyFilesByMagentoVersion($files); 72 | 73 | $setup->endSetup(); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /Ui/Component/JobListing/Column/Code/Options.php: -------------------------------------------------------------------------------- 1 | cronConfig = $cronConfig; 36 | } 37 | 38 | /** 39 | * Get all options available 40 | * @return array 41 | */ 42 | public function toOptionArray() 43 | { 44 | $options = []; 45 | 46 | if ($this->options === null) { 47 | $configJobs = $this->cronConfig->getJobs(); 48 | foreach (array_values($configJobs) as $jobs) { 49 | foreach (array_keys($jobs) as $code) { 50 | $options[] = $code; 51 | } 52 | } 53 | } 54 | 55 | sort($options); 56 | foreach ($options as $option) { 57 | $this->options[] = [ 58 | "label" => $option, "value" => $option 59 | ]; 60 | } 61 | return $this->options; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /Ui/Component/JobListing/Column/Group/Options.php: -------------------------------------------------------------------------------- 1 | cronConfig = $cronConfig; 36 | } 37 | 38 | /** 39 | * Get all options available 40 | * @return array 41 | */ 42 | public function toOptionArray() 43 | { 44 | if ($this->options === null) { 45 | $configJobs = $this->cronConfig->getJobs(); 46 | foreach (array_keys($configJobs) as $group) { 47 | $this->options[] = [ 48 | "label" => $group, "value" => $group 49 | ]; 50 | } 51 | } 52 | return $this->options; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /Ui/Component/TaskListing/Column/Actions.php: -------------------------------------------------------------------------------- 1 | urlBuilder = $urlBuilder; 45 | parent::__construct($context, $uiComponentFactory, $components, $data); 46 | } 47 | 48 | /** 49 | * Prepare Data Source 50 | * @param array $dataSource 51 | * @return array 52 | */ 53 | public function prepareDataSource(array $dataSource) 54 | { 55 | if (isset($dataSource['data']['items'])) { 56 | foreach ($dataSource['data']['items'] as & $item) { 57 | $name = $this->getData('name'); 58 | if (isset($item['schedule_id']) && $item['status'] != \Magento\Cron\Model\Schedule::STATUS_PENDING) { 59 | $url = $this->urlBuilder->getUrl($this->_viewUrl); 60 | $item[$name]['view_more'] = [ 61 | 'href' => "javascript:void(require(['cs_task'], function (task) { task.view('" . $url . "','" . $item['schedule_id'] . "'); }))", 62 | 'label' => __('View More'), 63 | ]; 64 | } 65 | } 66 | } 67 | 68 | return $dataSource; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /Ui/Component/TaskListing/Column/Code/Options.php: -------------------------------------------------------------------------------- 1 | taskCollection = $taskCollectionFactory->create(); 31 | } 32 | 33 | /** 34 | * Get all options available 35 | * @return array 36 | */ 37 | public function toOptionArray() 38 | { 39 | $options = []; 40 | 41 | if ($this->options === null) { 42 | $this->options = []; 43 | $jobCodes = $this->taskCollection->getJobCodes(); 44 | foreach ($jobCodes as $jobCode) { 45 | $options[] = $jobCode->getJobCode(); 46 | } 47 | sort($options); 48 | foreach ($options as $option) { 49 | $this->options[] = [ 50 | "label" => $option, "value" => $option 51 | ]; 52 | } 53 | } 54 | return $this->options; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /Ui/Component/TaskListing/Column/Messages.php: -------------------------------------------------------------------------------- 1 | urlBuilder = $urlBuilder; 44 | parent::__construct($context, $uiComponentFactory, $components, $data); 45 | } 46 | 47 | /** 48 | * Prepare Data Source 49 | * @param array $dataSource 50 | * @return array 51 | */ 52 | public function prepareDataSource(array $dataSource) 53 | { 54 | if (isset($dataSource['data']['items'])) { 55 | $url = $this->urlBuilder->getUrl($this->_viewUrl); 56 | foreach ($dataSource['data']['items'] as &$item) { 57 | $messages = nl2br($item[$this->getData('name')]); 58 | if (strlen($messages) > 200) { 59 | $messages = substr($messages, 0, 200) . "..."; 60 | } 61 | $item[$this->getData('name')] = $messages; 62 | } 63 | } 64 | 65 | return $dataSource; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /Ui/Component/TaskListing/Column/Status/Options.php: -------------------------------------------------------------------------------- 1 | taskCollection = $taskCollectionFactory->create(); 35 | } 36 | 37 | /** 38 | * Get all options available 39 | * @return array 40 | */ 41 | public function toOptionArray() 42 | { 43 | if ($this->options === null) { 44 | $this->options = []; 45 | $taskStatuses = $this->taskCollection->getTaskStatuses(); 46 | foreach ($taskStatuses as $taskStatus) { 47 | $this->options[] = [ 48 | "label" => $taskStatus->getStatus(), "value" => $taskStatus->getStatus() 49 | ]; 50 | } 51 | } 52 | return $this->options; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /Ui/DataProvider/JobProvider.php: -------------------------------------------------------------------------------- 1 | _directoryRead = $directoryRead; 85 | $this->_directoryList = $directoryList; 86 | $this->jobHelper = $jobHelper; 87 | parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); 88 | } 89 | 90 | /** 91 | * Set the limit of the collection 92 | * @param type $offset 93 | * @param type $size 94 | */ 95 | public function setLimit( 96 | $offset, 97 | $size 98 | ) 99 | { 100 | $this->_size = $size; 101 | $this->_offset = $offset; 102 | } 103 | 104 | /** 105 | * Get the collection 106 | * @return type 107 | */ 108 | public function getData() 109 | { 110 | 111 | $data = array_values($this->jobHelper->getJobData()); 112 | 113 | 114 | $totalRecords = count($data); 115 | 116 | // sorting 117 | $sortField = $this->_sortField; 118 | $sortDir = $this->_sortDir; 119 | usort($data, function($a, $b) use ($sortField, $sortDir) { 120 | if ($sortDir == "asc") { 121 | return $a[$sortField] > $b[$sortField]; 122 | } else { 123 | return $a[$sortField] < $b[$sortField]; 124 | } 125 | }); 126 | 127 | // filters 128 | foreach ($this->_likeFilters as $column => $value) { 129 | $data = array_filter($data, function($item) use ($column, $value) { 130 | return stripos($item[$column], $value) !== false; 131 | }); 132 | } 133 | 134 | // pagination 135 | $data = array_slice($data, ($this->_offset - 1) * $this->_size, $this->_size); 136 | 137 | return [ 138 | 'totalRecords' => $totalRecords, 139 | 'items' => $data, 140 | ]; 141 | } 142 | 143 | /** 144 | * Add filters to the collection 145 | * @param \Magento\Framework\Api\Filter $filter 146 | */ 147 | public function addFilter(\Magento\Framework\Api\Filter $filter) 148 | { 149 | if ($filter->getConditionType() == "like") { 150 | $this->_likeFilters[$filter->getField()] = substr($filter->getValue(), 1, -1); 151 | } elseif ($filter->getConditionType() == "eq") { 152 | $this->_likeFilters[$filter->getField()] = $filter->getValue(); 153 | } elseif ($filter->getConditionType() == "gteq") { 154 | $this->_rangeFilters[$filter->getField()]['from'] = $filter->getValue(); 155 | } elseif ($filter->getConditionType() == "lteq") { 156 | $this->_rangeFilters[$filter->getField()]['to'] = $filter->getValue(); 157 | } 158 | } 159 | 160 | /** 161 | * Set the order of the collection 162 | * @param type $field 163 | * @param type $direction 164 | */ 165 | public function addOrder( 166 | $field, 167 | $direction 168 | ) 169 | { 170 | $this->_sortField = $field; 171 | $this->_sortDir = strtolower($direction); 172 | } 173 | 174 | } 175 | -------------------------------------------------------------------------------- /Ui/DataProvider/TaskProvider.php: -------------------------------------------------------------------------------- 1 | collection = $collectionFactory->create(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wyomind/cronscheduler", 3 | "description": "Cron Scheduler is a toolbox that makes visible and comprehensible the cron tasks that run in the background of your Magento 2 website. This extension helps to monitor cron jobs and provides many features that help to prevent errors.", 4 | "type": "magento2-module", 5 | "version": "1.3.0", 6 | "license": [ 7 | "Commercial License - Copyright 2017 - WYOMIND Sarl - Purchasing any product (Software) from the Wyomind.com web-site means buyer agree to the following statements (see LICENSE.TXT)" 8 | ], 9 | "require": { 10 | "php": "~5.5.0|~5.6.0|>=7.0.0", 11 | "magento/framework": ">=100.0.0" 12 | }, 13 | "autoload": { 14 | "files": [ 15 | "registration.php" 16 | ], 17 | "psr-4": { 18 | "Wyomind\\CronScheduler\\": "" 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /etc/acl.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /etc/adminhtml/menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 15 | 23 | 31 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /etc/adminhtml/routes.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /etc/adminhtml/system.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | separator-top 19 | 20 | wyomind 21 | Wyomind_CronScheduler::config 22 | 23 |
24 |
25 |
-------------------------------------------------------------------------------- /etc/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | Cron Scheduler 11 | 1.3.0.1 12 | cs 13 | 1 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /etc/cron_groups.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 1 10 | 20 11 | 15 12 | 10 13 | 60 14 | 600 15 | 0 16 | 17 | 18 | -------------------------------------------------------------------------------- /etc/crontab.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | * * * * * 11 | 12 | 13 | -------------------------------------------------------------------------------- /etc/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Wyomind\CronScheduler\Console\Command\Task\Listing 18 | Wyomind\CronScheduler\Console\Command\Task\Show 19 | Wyomind\CronScheduler\Console\Command\Job\Listing 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /i18n/en_US.csv: -------------------------------------------------------------------------------- 1 | Id,Id 2 | Status,Status 3 | "Created at","Created at" 4 | "Scheduled at","Scheduled at" 5 | "Executed at","Executed at" 6 | "Finished at","Finished at" 7 | Messages,Messages 8 | "Cron Scheduler : get list of all jobs","Cron Scheduler : get list of all jobs" 9 | Code,Code 10 | Group,Group 11 | Method,Method 12 | Schedule,Schedule 13 | "Cron Scheduler : get list of all tasks","Cron Scheduler : get list of all tasks" 14 | "Cron Scheduler : get details of a task","Cron Scheduler : get details of a task" 15 | "The id of the task","The id of the task" 16 | "Cron Scheduler > Jobs Configuration","Cron Scheduler > Jobs Configuration" 17 | "Cron Scheduler","Cron Scheduler" 18 | "Cron Scheduler > Tasks List","Cron Scheduler > Tasks List" 19 | CronScheduler,CronScheduler 20 | "You are not allowed to delete tasks","You are not allowed to delete tasks" 21 | "%1 tasks have been deleted","%1 tasks have been deleted" 22 | "%1 task has been deleted","%1 task has been deleted" 23 | "Cron Scheduler > Timeline","Cron Scheduler > Timeline" 24 | "Cron is alive","Cron is alive" 25 | "Last heartbeat is older than %1 hour%2","Last heartbeat is older than %1 hour%2" 26 | "Last heartbeat is older than %1 minute%2","Last heartbeat is older than %1 minute%2" 27 | "Last heartbeat was %1 minute%2 ago","Last heartbeat was %1 minute%2 ago" 28 | "No heartbeat found","No heartbeat found" 29 | ERROR,ERROR 30 | MISSED,MISSED 31 | RUNNING,RUNNING 32 | PENDING,PENDING 33 | SUCCESS,SUCCESS 34 | "Message: ","Message: " 35 | "File: ","File: " 36 | "Line: ","Line: " 37 | "View more","View more" 38 | "Run tasks & Generate Schedule","Run tasks & Generate Schedule" 39 | "Task #","Task #" 40 | "Job code","Job code" 41 | "Upgrade to Cron Sheduler Pro","Upgrade to Cron Sheduler Pro" 42 | "License activation","License activation" 43 | "Extension version","Extension version" 44 | "Activation key","Activation key" 45 | "Enter your activation key and click on `save config`.","Enter your activation key and click on `save config`." 46 | "License code","License code" 47 | "Enter your license code only if prompted.","Enter your license code only if prompted." 48 | Instance,Instance 49 | Delete,Delete 50 | "Delete tasks","Delete tasks" 51 | "Are you sure you wan't to delete selected tasks?","Are you sure you wan't to delete selected tasks?" 52 | ID,ID 53 | "Generate Schedule","Generate Schedule" 54 | Call,Call 55 | "Original Schedule","Original Schedule" 56 | "Delete Selected","Delete Selected" 57 | "Delete all selected tasks","Delete all selected tasks" 58 | "Do you want to delete all the selected tasks?","Do you want to delete all the selected tasks?" 59 | -------------------------------------------------------------------------------- /i18n/fr_FR.csv: -------------------------------------------------------------------------------- 1 | "Message: ","Message: " 2 | "File: ","Fichier: " 3 | "Line: ","Ligne: " 4 | "Upgrade to Cron Sheduler Pro","Mettre à jour vers Cron Sheduler Pro" 5 | "License activation","Activation de la licence" 6 | "Extension version","Version de l'extension" 7 | "Activation key","Clef d'activation" 8 | "Enter your activation key and click on `save config`.","Renseignez votre clef d'activation et cliquez sur `Enregistrer la configuration`." 9 | "License code","Code de licence" 10 | "Enter your license code only if prompted.","Renseignez votre code de licenceseulement si demandé." 11 | Instance,Instance 12 | Delete,Supprimer 13 | "Delete tasks","Supprimer des tâches" 14 | "Are you sure you wan't to delete selected tasks?","Êtes-vous sûr de vouloir supprimer les tâches sélectionnés ?" 15 | ID,ID 16 | Call,Appel 17 | "Original Schedule","Progammation originale" 18 | "Delete Selected","Supprimer les tâches sélectionnés" 19 | "Delete all selected tasks","Supprimer toutes les tâches séléctioonées" 20 | "Do you want to delete all the selected tasks?","Voulez vous supprimer touts les tâches séléctionnées ?" 21 | "Created at","Créée le" 22 | "Executed at","Exécutée le" 23 | "Finished at","Finie le" 24 | "Id","Id" 25 | "Messages","Messages" 26 | "Scheduled at","Programmé le" 27 | "Status","Statut" 28 | "Method","Methode" 29 | "Code","Code" 30 | "Cron Scheduler : get list of all jobs","Cron Scheduler : affiche une liste de toutes les tâches cron" 31 | "Group","Groupe" 32 | "Schedule","Programmation" 33 | "Cron Scheduler : get list of all tasks","Cron Scheduler : affiche une liste de toutes les tâches cron" 34 | "Cron Scheduler : get details of a task","Cron Scheduler : affiche le details d'un tâche programmée" 35 | "The id of the task","L'identifiant de la tâche programmée" 36 | "Cron Scheduler","Cron Scheduler" 37 | "Cron Scheduler > Jobs Configuration","Cron Scheduler > Configuration des tâches cron" 38 | "Cron Scheduler > Tasks List","Cron Scheduler > Liste des tâches programmées" 39 | "%1 tasks have been deleted","%1 tâches ont été supprimées" 40 | "%1 task has been deleted","%1 tâche a été supprimée" 41 | "Cron Scheduler > Timeline","Cron Scheduler > Timeline" 42 | "Cron is alive","Cron is alive" 43 | "Last heartbeat is older than %1 hour%2","Le dernier signe de vie de cron date de plus de %1 heure%2" 44 | "Last heartbeat is older than %1 minute%2","Le dernier signe de vie de cron date de plus de %1 minute%2" 45 | "Last heartbeat was %1 minute%2 ago","Le dernier signe de vie de cron était il y a %1 minute%2" 46 | "No heartbeat found","Aucun signe de vie de cron" 47 | "ERROR","ERREUR" 48 | "MISSED","MANQUEE" 49 | "PENDING","EN ATTENTE" 50 | "RUNNING","EN COURS" 51 | "SUCCESS","SUCCES" 52 | "View more","Voir plus" 53 | "Run tasks & Generate Schedule","Exécuter les tâches & Programmer les suivantes" 54 | "Job code","Code de la tâche" 55 | "Task #","Tâche #" 56 | "Jobs Configuration","Configuration des tâches cron" 57 | "Tasks List","Liste des tâches programmées" 58 | "Tasks Timeline","Historique des tâches" 59 | "You are not allowed to delete tasks","Vous n'êtes pas autorisé à supprimer des tâches" 60 | -------------------------------------------------------------------------------- /registration.php: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /view/adminhtml/layout/cronscheduler_task_listing.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /view/adminhtml/layout/cronscheduler_task_timeline.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 55 | 56 | 57 | 58 |
59 | 60 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /view/adminhtml/templates/task/timeline/actions.phtml: -------------------------------------------------------------------------------- 1 | 7 | isAllowed()) : /* only if allowed */ ?> 8 |
9 |
10 |
11 |
12 | 15 |
16 |
17 |
18 | 7 | 25 | -------------------------------------------------------------------------------- /view/adminhtml/templates/upgradeToPro.phtml: -------------------------------------------------------------------------------- 1 | isPro()) : /* only if Cron Scheduler Pro is not installed */ ?> 2 |
3 | 6 |
7 | 2 | 6 | 7 | 8 | 9 | cronscheduler_job_listing.job_listing_data_source 10 | cronscheduler_job_listing.job_listing_data_source 11 | 12 | job_columns 13 | 14 | 15 | 16 | Wyomind\CronScheduler\Ui\DataProvider\JobProvider 17 | job_listing_data_source 18 | code 19 | code 20 | 21 | 22 | 23 | 24 | code 25 | 26 | 27 | 28 | 29 | 30 | 31 | Magento_Ui/js/grid/provider 32 | 33 | 34 | 35 | 36 | 37 | 38 | false 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | cronscheduler_job_listing.cronscheduler_job_listing.listing_top.bookmarks 48 | current.filters 49 | 50 | 51 | cronscheduler_job_listing.cronscheduler_job_listing.listing_top.listing_filters 52 | 53 | cronscheduler_job_listing.cronscheduler_job_listing.listing_top.bookmarks:current.columns.${ $.index }.visible 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | cronscheduler_job_listing.cronscheduler_job_listing.listing_top.bookmarks 64 | current.paging 65 | 66 | cronscheduler_job_listing.cronscheduler_job_listing.job_columns.code 67 | bottom 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Wyomind\CronScheduler\Ui\Component\JobListing\Column\Code\Options 76 | 77 | select 78 | Magento_Ui/js/grid/columns/select 79 | select 80 | Code 81 | 10 82 | true 83 | 84 | 85 | 86 | 87 | 88 | Wyomind\CronScheduler\Ui\Component\JobListing\Column\Group\Options 89 | 90 | select 91 | Magento_Ui/js/grid/columns/select 92 | select 93 | Group 94 | 20 95 | true 96 | 97 | 98 | 99 | 100 | 101 | 102 | text 103 | Instance 104 | 30 105 | true 106 | 107 | 108 | 109 | 110 | 111 | 112 | text 113 | Method 114 | 35 115 | true 116 | 117 | 118 | 119 | 120 | 121 | 122 | text 123 | Schedule 124 | 40 125 | true 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /view/adminhtml/ui_component/cronscheduler_job_listing_2.0.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | cronscheduler_job_listing.job_listing_data_source 10 | cronscheduler_job_listing.job_listing_data_source 11 | 12 | job_columns 13 | 14 | 15 | 16 | Wyomind\CronScheduler\Ui\DataProvider\JobProvider 17 | job_listing_data_source 18 | code 19 | code 20 | 21 | 22 | 23 | 24 | code 25 | 26 | 27 | 28 | 29 | 30 | 31 | Magento_Ui/js/grid/provider 32 | 33 | 34 | 35 | 36 | 37 | 38 | ui/grid/toolbar 39 | 40 | 41 | 42 | 43 | 44 | 45 | cronscheduler_job_listing.cronscheduler_job_listing.job_columns 46 | 47 | 48 | cronscheduler_job_listing.cronscheduler_job_listing.listing_top.listing_filters 49 | 50 | cronscheduler_job_listing.cronscheduler_job_listing.job_columns.${ $.index }.visible 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | cronscheduler_job_listing.cronscheduler_job_listing.job_columns.code 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | Wyomind\CronScheduler\Ui\Component\JobListing\Column\Code\Options 68 | 69 | select 70 | Magento_Ui/js/grid/columns/select 71 | select 72 | Code 73 | 10 74 | true 75 | 76 | 77 | 78 | 79 | 80 | Wyomind\CronScheduler\Ui\Component\JobListing\Column\Group\Options 81 | 82 | select 83 | Magento_Ui/js/grid/columns/select 84 | select 85 | Group 86 | 20 87 | true 88 | 89 | 90 | 91 | 92 | 93 | 94 | text 95 | Instance 96 | 30 97 | true 98 | 99 | 100 | 101 | 102 | 103 | 104 | text 105 | Method 106 | 35 107 | true 108 | 109 | 110 | 111 | 112 | 113 | 114 | text 115 | Schedule 116 | 40 117 | true 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /view/adminhtml/ui_component/cronscheduler_job_listing_Mage20.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | cronscheduler_job_listing.job_listing_data_source 10 | cronscheduler_job_listing.job_listing_data_source 11 | 12 | job_columns 13 | 14 | 15 | 16 | Wyomind\CronScheduler\Ui\DataProvider\JobProvider 17 | job_listing_data_source 18 | code 19 | code 20 | 21 | 22 | 23 | 24 | code 25 | 26 | 27 | 28 | 29 | 30 | 31 | Magento_Ui/js/grid/provider 32 | 33 | 34 | 35 | 36 | 37 | 38 | ui/grid/toolbar 39 | 40 | 41 | 42 | 43 | 44 | 45 | cronscheduler_job_listing.cronscheduler_job_listing.job_columns 46 | 47 | 48 | cronscheduler_job_listing.cronscheduler_job_listing.listing_top.listing_filters 49 | 50 | cronscheduler_job_listing.cronscheduler_job_listing.job_columns.${ $.index }.visible 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | cronscheduler_job_listing.cronscheduler_job_listing.job_columns.code 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | Wyomind\CronScheduler\Ui\Component\JobListing\Column\Code\Options 68 | 69 | select 70 | Magento_Ui/js/grid/columns/select 71 | select 72 | Code 73 | 10 74 | true 75 | 76 | 77 | 78 | 79 | 80 | Wyomind\CronScheduler\Ui\Component\JobListing\Column\Group\Options 81 | 82 | select 83 | Magento_Ui/js/grid/columns/select 84 | select 85 | Group 86 | 20 87 | true 88 | 89 | 90 | 91 | 92 | 93 | 94 | text 95 | Instance 96 | 30 97 | true 98 | 99 | 100 | 101 | 102 | 103 | 104 | text 105 | Method 106 | 35 107 | true 108 | 109 | 110 | 111 | 112 | 113 | 114 | text 115 | Schedule 116 | 40 117 | true 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /view/adminhtml/ui_component/cronscheduler_task_listing.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | cronscheduler_task_listing.task_listing_data_source 10 | cronscheduler_task_listing.task_listing_data_source 11 | 12 | task_columns 13 | 14 | 15 | 16 | Wyomind\CronScheduler\Ui\DataProvider\TaskProvider 17 | task_listing_data_source 18 | schedule_id 19 | schedule_id 20 | 21 | 22 | 23 | 24 | schedule_id 25 | 26 | 27 | 28 | 29 | 30 | 31 | Magento_Ui/js/grid/provider 32 | 33 | 34 | 35 | 36 | 37 | 38 | false 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | cronscheduler_task_listing.cronscheduler_task_listing.listing_top.bookmarks 48 | current.filters 49 | 50 | 51 | cronscheduler_task_listing.cronscheduler_task_listing.listing_top.listing_filters 52 | 53 | cronscheduler_task_listing.cronscheduler_task_listing.listing_top.bookmarks:current.columns.${ $.index }.visible 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | cronscheduler_task_listing.cronscheduler_task_listing.listing_top.bookmarks 64 | current.paging 65 | 66 | cronscheduler_task_listing.cronscheduler_task_listing.task_columns.schedule_id 67 | bottom 68 | 69 | 70 | 71 | 72 | 73 | 74 | cronscheduler_task_listing.cronscheduler_task_listing.task_columns.schedule_ids 75 | schedule_id 76 | 77 | 78 | 79 | 80 | 81 | delete 82 | Delete 83 | 84 | 85 | Delete tasks 86 | Are you sure you wan't to delete selected tasks? 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | cronscheduler_task_listing.cronscheduler_task_listing.listing_top.bookmarks 98 | current 99 | 100 | 101 | true 102 | 103 | cronscheduler_task_listing.cronscheduler_task_listing.listing_top.bookmarks 104 | columns.${ $.index } 105 | current.${ $.storageConfig.root} 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | schedule_id 114 | 1 115 | 116 | 117 | 118 | 119 | 120 | 121 | textRange 122 | desc 123 | ID 124 | 10 125 | true 126 | 127 | 128 | 129 | 130 | 131 | Wyomind\CronScheduler\Ui\Component\TaskListing\Column\Code\Options 132 | 133 | select 134 | Magento_Ui/js/grid/columns/select 135 | select 136 | Code 137 | 20 138 | true 139 | 140 | 141 | 142 | 143 | 144 | Wyomind\CronScheduler\Ui\Component\TaskListing\Column\Status\Options 145 | 146 | select 147 | Magento_Ui/js/grid/columns/select 148 | select 149 | Status 150 | Wyomind_CronScheduler/task/grid/status 151 | 30 152 | true 153 | 154 | 155 | 156 | 157 | 158 | 159 | text 160 | ui/grid/cells/html 161 | Messages 162 | 40 163 | true 164 | 165 | 166 | 167 | 168 | 169 | 170 | dateRange 171 | Magento_Ui/js/grid/columns/date 172 | date 173 | Created at 174 | 50 175 | true 176 | 177 | 178 | 179 | 180 | 181 | 182 | dateRange 183 | Magento_Ui/js/grid/columns/date 184 | date 185 | Scheduled at 186 | 60 187 | true 188 | 189 | 190 | 191 | 192 | 193 | 194 | dateRange 195 | Magento_Ui/js/grid/columns/date 196 | date 197 | Executed at 198 | 70 199 | true 200 | 201 | 202 | 203 | 204 | 205 | 206 | dateRange 207 | Magento_Ui/js/grid/columns/date 208 | date 209 | Finished at 210 | 80 211 | true 212 | 213 | 214 | 215 | 216 | 217 | 218 | false 219 | schedule_id 220 | 999 221 | 222 | 223 | 224 | 225 | -------------------------------------------------------------------------------- /view/adminhtml/ui_component/cronscheduler_task_listing_2.0.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | cronscheduler_task_listing.task_listing_data_source 10 | cronscheduler_task_listing.task_listing_data_source 11 | 12 | task_columns 13 | 14 | 15 | 16 | Wyomind\CronScheduler\Ui\DataProvider\TaskProvider 17 | task_listing_data_source 18 | schedule_id 19 | schedule_id 20 | 21 | 22 | 23 | 24 | schedule_id 25 | 26 | 27 | 28 | 29 | 30 | 31 | Magento_Ui/js/grid/provider 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | ui/grid/toolbar 40 | 41 | 42 | 43 | 44 | 45 | cronscheduler_task_listing.cronscheduler_task_listing.task_columns 46 | 47 | cronscheduler_task_listing.cronscheduler_task_listing.listing_top.listing_filters 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | cronscheduler_task_listing.cronscheduler_task_listing.task_columns.schedule_ids 56 | schedule_id 57 | 58 | 59 | 60 | 61 | 62 | delete 63 | Delete 64 | 65 | 66 | Delete tasks 67 | Are you sure you wan't to delete selected tasks? 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | cronscheduler_task_listing.cronscheduler_task_listing.task_columns.schedule_id 77 | bottom 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | schedule_id 87 | 1 88 | 89 | 90 | 91 | 92 | 93 | 94 | textRange 95 | desc 96 | ID 97 | 10 98 | true 99 | 100 | 101 | 102 | 103 | 104 | Wyomind\CronScheduler\Ui\Component\TaskListing\Column\Code\Options 105 | 106 | select 107 | Magento_Ui/js/grid/columns/select 108 | select 109 | Code 110 | 20 111 | true 112 | 113 | 114 | 115 | 116 | 117 | Wyomind\CronScheduler\Ui\Component\TaskListing\Column\Status\Options 118 | 119 | select 120 | Magento_Ui/js/grid/columns/select 121 | select 122 | Status 123 | Wyomind_CronScheduler/task/grid/status 124 | 30 125 | true 126 | 127 | 128 | 129 | 130 | 131 | 132 | text 133 | ui/grid/cells/html 134 | Messages 135 | 40 136 | true 137 | 138 | 139 | 140 | 141 | 142 | 143 | dateRange 144 | Magento_Ui/js/grid/columns/date 145 | date 146 | Created at 147 | 50 148 | true 149 | 150 | 151 | 152 | 153 | 154 | 155 | dateRange 156 | Magento_Ui/js/grid/columns/date 157 | date 158 | Scheduled at 159 | 60 160 | true 161 | 162 | 163 | 164 | 165 | 166 | 167 | dateRange 168 | Magento_Ui/js/grid/columns/date 169 | date 170 | Executed at 171 | 70 172 | true 173 | 174 | 175 | 176 | 177 | 178 | 179 | dateRange 180 | Magento_Ui/js/grid/columns/date 181 | date 182 | Finished at 183 | 80 184 | true 185 | 186 | 187 | 188 | 189 | 190 | 191 | false 192 | schedule_id 193 | 999 194 | 195 | 196 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /view/adminhtml/ui_component/cronscheduler_task_listing_Mage20.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | cronscheduler_task_listing.task_listing_data_source 10 | cronscheduler_task_listing.task_listing_data_source 11 | 12 | task_columns 13 | 14 | 15 | 16 | Wyomind\CronScheduler\Ui\DataProvider\TaskProvider 17 | task_listing_data_source 18 | schedule_id 19 | schedule_id 20 | 21 | 22 | 23 | 24 | schedule_id 25 | 26 | 27 | 28 | 29 | 30 | 31 | Magento_Ui/js/grid/provider 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | ui/grid/toolbar 40 | 41 | 42 | 43 | 44 | 45 | cronscheduler_task_listing.cronscheduler_task_listing.task_columns 46 | 47 | cronscheduler_task_listing.cronscheduler_task_listing.listing_top.listing_filters 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | cronscheduler_task_listing.cronscheduler_task_listing.task_columns.schedule_ids 56 | schedule_id 57 | 58 | 59 | 60 | 61 | 62 | delete 63 | Delete 64 | 65 | 66 | Delete tasks 67 | Are you sure you wan't to delete selected tasks? 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | cronscheduler_task_listing.cronscheduler_task_listing.task_columns.schedule_id 77 | bottom 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | schedule_id 87 | 1 88 | 89 | 90 | 91 | 92 | 93 | 94 | textRange 95 | desc 96 | ID 97 | 10 98 | true 99 | 100 | 101 | 102 | 103 | 104 | Wyomind\CronScheduler\Ui\Component\TaskListing\Column\Code\Options 105 | 106 | select 107 | Magento_Ui/js/grid/columns/select 108 | select 109 | Code 110 | 20 111 | true 112 | 113 | 114 | 115 | 116 | 117 | Wyomind\CronScheduler\Ui\Component\TaskListing\Column\Status\Options 118 | 119 | select 120 | Magento_Ui/js/grid/columns/select 121 | select 122 | Status 123 | Wyomind_CronScheduler/task/grid/status 124 | 30 125 | true 126 | 127 | 128 | 129 | 130 | 131 | 132 | text 133 | ui/grid/cells/html 134 | Messages 135 | 40 136 | true 137 | 138 | 139 | 140 | 141 | 142 | 143 | dateRange 144 | Magento_Ui/js/grid/columns/date 145 | date 146 | Created at 147 | 50 148 | true 149 | 150 | 151 | 152 | 153 | 154 | 155 | dateRange 156 | Magento_Ui/js/grid/columns/date 157 | date 158 | Scheduled at 159 | 60 160 | true 161 | 162 | 163 | 164 | 165 | 166 | 167 | dateRange 168 | Magento_Ui/js/grid/columns/date 169 | date 170 | Executed at 171 | 70 172 | true 173 | 174 | 175 | 176 | 177 | 178 | 179 | dateRange 180 | Magento_Ui/js/grid/columns/date 181 | date 182 | Finished at 183 | 80 184 | true 185 | 186 | 187 | 188 | 189 | 190 | 191 | false 192 | schedule_id 193 | 999 194 | 195 | 196 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /view/adminhtml/web/css/common.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2017 Wyomind. All rights reserved. 3 | See LICENSE.txt for license details. 4 | */ 5 | span.grid-severity-running{ 6 | background: #fff3da; 7 | border: 1px solid #ffd400; 8 | color: #ffd400; 9 | display: block; 10 | font-weight: bold; 11 | line-height: 17px; 12 | padding: 0 3px; 13 | text-align: center; 14 | text-transform: uppercase; 15 | } 16 | 17 | #task-view table { 18 | width:100%; 19 | } 20 | 21 | #task-view td { 22 | vertical-align:top; 23 | 24 | } 25 | #task-view td:first-child { 26 | min-width:200px; 27 | max-width:200px; 28 | width:200px; 29 | } 30 | 31 | #task-view span#task-messages { 32 | display: block; 33 | height: 430px; 34 | overflow-y: scroll; 35 | font-family: monospace; 36 | font-size: 1.3em; 37 | } 38 | #task-view span#task-status { 39 | width: 150px; 40 | } 41 | 42 | #task-view span { 43 | display: block; 44 | margin-bottom: 20px; 45 | vertical-align: bottom; 46 | } 47 | 48 | #task-view label { 49 | display: block; 50 | margin-bottom: 20px; 51 | float: left; 52 | font-weight: bold; 53 | width: 150px; 54 | } -------------------------------------------------------------------------------- /view/adminhtml/web/css/timeline.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2017 Wyomind. All rights reserved. 3 | See LICENSE.txt for license details. 4 | */ 5 | div.timeline-frame { 6 | -moz-box-sizing: border-box; 7 | border: 1px solid #bebebe; 8 | box-sizing: border-box; 9 | overflow: hidden; 10 | position: relative; 11 | } 12 | 13 | div.timeline-content { 14 | overflow: hidden; 15 | position: relative; 16 | } 17 | 18 | div.timeline-axis { 19 | -moz-box-sizing: border-box; 20 | border-color: #bebebe; 21 | border-top-style: solid; 22 | border-width: 1px; 23 | box-sizing: border-box; 24 | } 25 | 26 | div.timeline-axis-grid { 27 | -moz-box-sizing: border-box; 28 | border-left-style: solid; 29 | border-width: 1px; 30 | box-sizing: border-box; 31 | } 32 | 33 | div.timeline-axis-grid-minor { 34 | border-color: #e5e5e5; 35 | } 36 | 37 | div.timeline-axis-grid-major { 38 | border-color: #bfbfbf; 39 | } 40 | 41 | div.timeline-axis-text { 42 | color: #4d4d4d; 43 | padding: 3px; 44 | white-space: nowrap; 45 | } 46 | 47 | div.timeline-axis-text-minor { 48 | } 49 | 50 | div.timeline-axis-text-major { 51 | } 52 | 53 | div.timeline-event { 54 | -moz-box-sizing: border-box; 55 | background-color: #d5ddf6; 56 | border-color: #97b0f8; 57 | box-sizing: border-box; 58 | color: #1a1a1a; 59 | display: inline-block; 60 | } 61 | 62 | div.timeline-event-selected { 63 | background-color: #fff785; 64 | border-color: #ffc200; 65 | z-index: 999; 66 | } 67 | 68 | /* TODO: use another color or pattern? */ 69 | div.timeline-event-cluster { 70 | /*background: url('img/cluster_bg.png') #97b0f8;*/ 71 | color: #ffffff; 72 | } 73 | 74 | div.timeline-event-cluster div.timeline-event-dot { 75 | border-color: #d5ddf6; 76 | } 77 | 78 | div.timeline-event-box { 79 | -moz-border-radius: 5px; /* For Firefox 3.6 and older */ 80 | border-radius: 5px; 81 | border-style: solid; 82 | border-width: 1px; 83 | text-align: center; 84 | } 85 | 86 | div.timeline-event-dot { 87 | -moz-border-radius: 5px; /* For Firefox 3.6 and older */ 88 | border-radius: 5px; 89 | border-style: solid; 90 | border-width: 5px; 91 | } 92 | 93 | div.timeline-event-range { 94 | -moz-border-radius: 2px; /* For Firefox 3.6 and older */ 95 | border-radius: 2px; 96 | border-style: solid; 97 | border-width: 1px; 98 | } 99 | 100 | div.timeline-event-range-drag-left { 101 | cursor: w-resize; 102 | z-index: 1000; 103 | } 104 | 105 | div.timeline-event-range-drag-right { 106 | cursor: e-resize; 107 | z-index: 1000; 108 | } 109 | 110 | div.timeline-event-line { 111 | -moz-box-sizing: border-box; 112 | border-left-style: solid; 113 | border-left-width: 1px; 114 | box-sizing: border-box; 115 | } 116 | 117 | div.timeline-event-content { 118 | margin: 5px; 119 | overflow: hidden; 120 | white-space: nowrap; 121 | } 122 | 123 | div.timeline-groups-axis { 124 | -moz-box-sizing: border-box; 125 | border-color: #bebebe; 126 | border-width: 1px; 127 | box-sizing: border-box; 128 | } 129 | 130 | div.timeline-groups-axis-onleft { 131 | border-style: none solid none none; 132 | } 133 | 134 | div.timeline-groups-axis-onright { 135 | border-style: none none none solid; 136 | } 137 | 138 | div.timeline-groups-text { 139 | color: #4d4d4d; 140 | padding-left: 10px; 141 | padding-right: 10px; 142 | } 143 | 144 | div.timeline-currenttime { 145 | -moz-box-sizing: border-box; 146 | background-color: #ff7f6e; 147 | box-sizing: border-box; 148 | width: 2px; 149 | } 150 | 151 | div.timeline-customtime { 152 | -moz-box-sizing: border-box; 153 | background-color: #6e94ff; 154 | box-sizing: border-box; 155 | cursor: move; 156 | width: 2px; 157 | } 158 | 159 | div.timeline-navigation { 160 | -moz-border-radius: 2px; /* For Firefox 3.6 and older */ 161 | -moz-box-sizing: border-box; 162 | background-color: #f5f5f5; 163 | border: 1px solid #bebebe; 164 | border-radius: 2px; 165 | box-sizing: border-box; 166 | color: #808080; 167 | font-family: arial; 168 | font-size: 20px; 169 | font-weight: bold; 170 | } 171 | 172 | div.timeline-navigation-new, 173 | div.timeline-navigation-delete, 174 | div.timeline-navigation-zoom-in, 175 | div.timeline-navigation-zoom-out, 176 | div.timeline-navigation-move-left, 177 | div.timeline-navigation-move-right { 178 | -moz-box-sizing: border-box; 179 | box-sizing: border-box; 180 | cursor: pointer; 181 | float: left; 182 | height: 36px; 183 | padding: 10px; 184 | text-decoration: none; 185 | width: 36px; 186 | } 187 | 188 | div.timeline-navigation-new { 189 | /*background: url('img/16/new.png') no-repeat center;*/ 190 | } 191 | 192 | /* separator between new and navigation buttons */ 193 | div.timeline-navigation-new-line { 194 | border-right: 1px solid #bebebe; 195 | } 196 | 197 | div.timeline-navigation-delete { 198 | /*background: url('img/16/delete.png') no-repeat center;*/ 199 | } 200 | 201 | div.timeline-navigation-zoom-in { 202 | /*background: url('img/16/zoomin.png') no-repeat center;*/ 203 | } 204 | 205 | div.timeline-navigation-zoom-out { 206 | /*background: url('img/16/zoomout.png') no-repeat center;*/ 207 | } 208 | 209 | div.timeline-navigation-move-left { 210 | /*background: url('img/16/moveleft.png') no-repeat center;*/ 211 | } 212 | 213 | div.timeline-navigation-move-right { 214 | /*background: url('img/16/moveright.png') no-repeat center;*/ 215 | } 216 | 217 | div.timeline-event { 218 | min-width: 1px; 219 | padding: 0px; 220 | height: 36px; 221 | } 222 | 223 | div.timeline-event.success { 224 | background:green; 225 | border-color:green; 226 | } 227 | div.timeline-event.missed, 228 | div.timeline-event.error { 229 | background:red; 230 | border-color:red; 231 | } 232 | div.timeline-event.running { 233 | background: #ffd400; 234 | border-color: #ffd400; 235 | } 236 | 237 | div.timeline-event.pending { 238 | background:#ea992e; 239 | border-color:#ea992e; 240 | } 241 | .timeline-content { 242 | border: 0px solid transparent !important; 243 | } 244 | 245 | #timeline-tooltip { 246 | background:white; 247 | border: 1px solid black; 248 | padding:5px; 249 | } 250 | #timeline-tooltip table td { 251 | padding:3px; 252 | } 253 | #timeline-tooltip table tr td:first-child { 254 | font-weight: bold; 255 | font-variant: small-caps; 256 | } -------------------------------------------------------------------------------- /view/adminhtml/web/js/task.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright © 2017 Wyomind. All rights reserved. 3 | * See LICENSE.txt for license details. 4 | */ 5 | 6 | /** 7 | * Open a task in a modal window 8 | * @param {type} $ 9 | * @returns {task_L10.taskAnonym$1} 10 | */ 11 | define(["jquery", "Magento_Ui/js/modal/modal"], function ($) { 12 | 'use strict'; 13 | return { 14 | /** 15 | * OPen the modal window with the task information 16 | * @param string url 17 | * @param int schedule_id 18 | * @returns void 19 | */ 20 | view: function (url, schedule_id) { 21 | 22 | $('#task-view').modal({ 23 | 'type': 'slide', 24 | 'title': '', 25 | 'modalClass': 'mage-new-category-dialog form-inline', 26 | buttons: [] 27 | }); 28 | 29 | var indices = new Array("schedule_id", "job_code", "status", "created_at", "scheduled_at", "executed_at", "finished_at", "messages", "origin", "user", "ip", "error_file", "error_line"); 30 | for (var indice in indices) { 31 | $('#task-view #tr-task-' + indices[indice]).css({display: "none"}); 32 | } 33 | $('#task-view #task-status').attr("class", ""); 34 | $('#task-view').modal('openModal'); 35 | $.ajax({ 36 | url: url, 37 | data: {schedule_id: schedule_id}, 38 | type: 'POST', 39 | showLoader: true, 40 | success: function (data) { 41 | if (typeof data.error !== "undefined") { 42 | $("#task-view table").css({display: "none"}); 43 | $("#task-view #error").html(data.error); 44 | } else { 45 | $("#task-view table").css({display: "table"}); 46 | $("#task-view #error").html(""); 47 | 48 | if (typeof data.schedule_id !== "undefined") { 49 | for (var indice in indices) { 50 | if (indices[indice] !== "status") { 51 | $('#task-view #task-' + indices[indice]).html(data[indices[indice]]); 52 | } 53 | if (data[indices[indice]] === "" || data[indices[indice]] === null) { 54 | $('#task-view #tr-task-' + indices[indice]).css({display: "none"}); 55 | } else { 56 | $('#task-view #tr-task-' + indices[indice]).css({display: "table-row"}); 57 | } 58 | } 59 | } 60 | if (data["status"][0] === "major") { 61 | $('#task-view #tr-task-error_file').css({display: "table-row"}); 62 | $('#task-view #tr-task-error_line').css({display: "table-row"}); 63 | } else { 64 | $('#task-view #tr-task-error_file').css({display: "none"}); 65 | $('#task-view #tr-task-error_line').css({display: "none"}); 66 | } 67 | 68 | 69 | 70 | $('#task-view #task-status').html(data['status'][1]); 71 | $('#task-view #task-status').attr("class", "grid-severity-" + data['status'][0]); 72 | } 73 | }, 74 | error: function (data) { 75 | $('#task-view').html(data.responseText); 76 | } 77 | }); 78 | } 79 | }; 80 | }); -------------------------------------------------------------------------------- /view/adminhtml/web/template/task/grid/status.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | --------------------------------------------------------------------------------