diff --git a/README.md b/README.md index 046ffb19..0bbe1d0a 100644 --- a/README.md +++ b/README.md @@ -819,6 +819,17 @@ As a consequence, the work might end up being very fragmented and each chunk may Some projects do not mind this however, so this solution may still be quite suitable. +## Jobs admin UI + +In case your project has many jobs to search through and the basic UI is not enough, you can use the advanced jobs admin UI option. +This option requires the `silverstripe-terraformers/gridfield-rich-filter-header` module, though. +To enable the advanced UI simply add this config to your project. + +```yaml +Symbiote\QueuedJobs\Controllers\QueuedJobsAdmin: + advanced_admin_ui: true +``` + ## Contributing ### Translations diff --git a/composer.json b/composer.json index a88dadbd..c223329b 100644 --- a/composer.json +++ b/composer.json @@ -29,6 +29,9 @@ "replace": { "silverstripe/queuedjobs": "self.version" }, + "suggest": { + "silverstripe-terraformers/gridfield-rich-filter-header": "Required for advanced admin UI" + }, "autoload": { "psr-4": { "Symbiote\\QueuedJobs\\": "src/", diff --git a/src/Controllers/QueuedJobsAdmin.php b/src/Controllers/QueuedJobsAdmin.php index 10874e20..b17332b6 100644 --- a/src/Controllers/QueuedJobsAdmin.php +++ b/src/Controllers/QueuedJobsAdmin.php @@ -15,9 +15,12 @@ use SilverStripe\Forms\GridField\GridFieldAddNewButton; use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor; use SilverStripe\Forms\GridField\GridFieldDataColumns; +use SilverStripe\Forms\GridField\GridFieldFilterHeader; use SilverStripe\Forms\GridField\GridFieldPageCount; use SilverStripe\Forms\GridField\GridFieldToolbarHeader; use SilverStripe\Forms\TextareaField; +use SilverStripe\ORM\DataList; +use SilverStripe\ORM\FieldType\DBDatetime; use SilverStripe\Security\Permission; use SilverStripe\Security\Security; use Symbiote\QueuedJobs\DataObjects\QueuedJobDescriptor; @@ -25,6 +28,7 @@ use Symbiote\QueuedJobs\Services\AbstractQueuedJob; use Symbiote\QueuedJobs\Services\QueuedJob; use Symbiote\QueuedJobs\Services\QueuedJobService; +use Terraformers\RichFilterHeader\Form\GridField\RichFilterHeader; /** * @author Marcus Nyeholt @@ -32,6 +36,9 @@ */ class QueuedJobsAdmin extends ModelAdmin { + const SCHEDULED_FILTER_FUTURE = 'future'; + const SCHEDULED_FILTER_PAST = 'past'; + /** * @var string */ @@ -74,6 +81,16 @@ class QueuedJobsAdmin extends ModelAdmin */ private static $date_format_european = 'dd/MM/yyyy'; + /** + * Enables advanced admin UI + * Note that this requires optional module + * silverstripe-terraformers/gridfield-rich-filter-header + * + * @config + * @var bool + */ + private static $advanced_admin_ui = false; + /** * @var QueuedJobService */ @@ -167,6 +184,10 @@ public function getEditForm($id = null, $fields = null) ); } + if ($this->config()->get('advanced_admin_ui')) { + $this->enableAdvancedAdminUi($grid); + } + $this->extend('updateEditForm', $form); return $form; @@ -213,4 +234,161 @@ public function createjob($data, Form $form) } return $this->redirectBack(); } + + /** + * @param GridField $gridField + */ + private function enableAdvancedAdminUi(GridField $gridField) + { + if (!class_exists(RichFilterHeader::class)) { + return; + } + + $config = $gridField->getConfig(); + + /** @var GridFieldDataColumns $gridFieldColumns */ + $gridFieldColumns = $config->getComponentByType(GridFieldDataColumns::class); + + $gridFieldColumns->setDisplayFields([ + 'getImplementationSummary' => 'Type', + 'JobTypeString' => 'Queue', + 'JobStatus' => 'Status', + 'JobTitle' => 'Description', + 'Created' => 'Added', + 'StartAfter' => 'Scheduled', + 'JobFinished' => 'Finished', + ]); + + $config->removeComponentsByType(GridFieldFilterHeader::class); + + $filter = new RichFilterHeader(); + $filter + ->setFilterConfig([ + 'getImplementationSummary' => 'Implementation', + 'Description' => 'JobTitle', + 'Status' => [ + 'title' => 'JobStatus', + 'filter' => 'ExactMatchFilter', + ], + 'JobTypeString' => [ + 'title' => 'JobType', + 'filter' => 'ExactMatchFilter', + ], + 'Created' => 'Added', + 'StartAfter' => 'Scheduled', + ]) + ->setFilterFields([ + 'JobType' => $queueType = DropdownField::create( + '', + '', + $this->getQueueTypes() + ), + 'JobStatus' => $jobStatus = DropdownField::create( + '', + '', + $this->getJobStatuses() + ), + 'Added' => $added = DropdownField::create( + '', + '', + $this->getAddedDates() + ), + 'Scheduled' => $scheduled = DropdownField::create( + '', + '', + [ + self::SCHEDULED_FILTER_FUTURE => self::SCHEDULED_FILTER_FUTURE, + self::SCHEDULED_FILTER_PAST => self::SCHEDULED_FILTER_PAST, + ] + ), + ]) + ->setFilterMethods([ + 'Added' => static function (DataList $list, $name, $value) { + if ($value) { + $added = DBDatetime::now()->modify($value); + + return $list->filter(['Created:LessThanOrEqual' => $added->Rfc2822()]); + } + + return $list; + }, + 'Scheduled' => static function (DataList $list, $name, $value) { + if ($value === static::SCHEDULED_FILTER_FUTURE) { + return $list->filter([ + 'StartAfter:GreaterThan' => DBDatetime::now()->Rfc2822(), + ]); + } + + if ($value === static::SCHEDULED_FILTER_PAST) { + return $list->filter([ + 'StartAfter:LessThanOrEqual' => DBDatetime::now()->Rfc2822(), + ]); + } + + return $list; + }, + ]); + + foreach ([$jobStatus, $queueType, $added, $scheduled] as $dropDownField) { + /** @var DropdownField $dropDownField */ + $dropDownField->setEmptyString('-- select --'); + } + + $config->addComponent($filter, GridFieldPaginator::class); + } + + /** + * Queue types options for drop down field + * + * @return array + */ + private function getQueueTypes() + { + /** @var QueuedJobDescriptor $job */ + $job = QueuedJobDescriptor::singleton(); + $map = $job->getJobTypeValues(); + $values = array_values($map); + $keys = []; + + foreach (array_keys($map) as $key) { + $keys[] = (int) $key; + } + + return array_combine($keys, $values); + } + + /** + * All possible job statuses (this list is not exposed by the module) + * intended to be used in a drop down field + * + * @return array + */ + private function getJobStatuses() + { + /** @var QueuedJobDescriptor $job */ + $job = QueuedJobDescriptor::singleton(); + $statuses = $job->getJobStatusValues(); + + sort($statuses, SORT_STRING); + + $statuses = array_combine($statuses, $statuses); + + return $statuses; + } + + /** + * Date options for added dates drop down field + * + * @return array + */ + private function getAddedDates() + { + return [ + '-1 day' => '1 day or older', + '-3 day' => '3 days or older', + '-7 day' => '7 days or older', + '-14 day' => '14 days or older', + '-1 month' => '1 month or older', + ]; + } } diff --git a/src/DataObjects/QueuedJobDescriptor.php b/src/DataObjects/QueuedJobDescriptor.php index bcc60432..831fd45e 100644 --- a/src/DataObjects/QueuedJobDescriptor.php +++ b/src/DataObjects/QueuedJobDescriptor.php @@ -317,16 +317,11 @@ public function getJobTypeValues() } /** - * @return FieldList + * @return array */ - public function getCMSFields() + public function getJobStatusValues() { - $fields = parent::getCMSFields(); - $fields->replaceField( - 'JobType', - new DropdownField('JobType', $this->fieldLabel('JobType'), $this->getJobTypeValues()) - ); - $statuses = [ + return [ QueuedJob::STATUS_NEW, QueuedJob::STATUS_INIT, QueuedJob::STATUS_RUN, @@ -336,6 +331,19 @@ public function getCMSFields() QueuedJob::STATUS_CANCELLED, QueuedJob::STATUS_BROKEN, ]; + } + + /** + * @return FieldList + */ + public function getCMSFields() + { + $fields = parent::getCMSFields(); + $fields->replaceField( + 'JobType', + new DropdownField('JobType', $this->fieldLabel('JobType'), $this->getJobTypeValues()) + ); + $statuses = $this->getJobStatusValues(); $fields->replaceField( 'JobStatus', DropdownField::create('JobStatus', $this->fieldLabel('JobStatus'), array_combine($statuses, $statuses))