Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NEW: Advanced jobs admin UI. #307

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,17 @@ Symbiote\QueuedJobs\DataObjects\QueuedJobDescriptor:

This will add Job data and Messages raw tabs to the job descriptor edit form. Displayed information is read only.

## Enabling extended 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
Expand Down
6 changes: 5 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3"
"squizlabs/php_codesniffer": "^3",
"silverstripe-terraformers/gridfield-rich-filter-header": "^2.1"
},
"minimum-stability": "dev",
"prefer-stable": true,
Expand All @@ -37,6 +38,9 @@
"replace": {
"silverstripe/queuedjobs": "self.version"
},
"suggest": {
"silverstripe-terraformers/gridfield-rich-filter-header": "Required for advanced admin UI"
},
"autoload": {
"psr-4": {
"Symbiote\\QueuedJobs\\": "src/",
Expand Down
178 changes: 178 additions & 0 deletions src/Controllers/QueuedJobsAdmin.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,30 @@
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;
use Symbiote\QueuedJobs\Forms\GridFieldQueuedJobExecute;
use Symbiote\QueuedJobs\Services\AbstractQueuedJob;
use Symbiote\QueuedJobs\Services\QueuedJob;
use Symbiote\QueuedJobs\Services\QueuedJobService;
use Terraformers\RichFilterHeader\Form\GridField\RichFilterHeader;

/**
* @author Marcus Nyeholt <[email protected]>
* @license BSD http://silverstripe.org/bsd-license/
*/
class QueuedJobsAdmin extends ModelAdmin
{
const SCHEDULED_FILTER_FUTURE = 'future';
const SCHEDULED_FILTER_PAST = 'past';

/**
* @var string
*/
Expand Down Expand Up @@ -74,6 +81,16 @@ class QueuedJobsAdmin extends ModelAdmin
*/
private static $date_format_european = 'dd/MM/yyyy';

/**
* Enables advanced admin UI
* This requires the optional module
* silverstripe-terraformers/gridfield-rich-filter-header
*
* @config
* @var bool
*/
private static $advanced_admin_ui = false;

/**
* @var QueuedJobService
*/
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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([
'getTrimmedImplementation' => _t(__CLASS__ . '.SUMMARY_TYPE', 'Type'),
'JobTypeString' => _t(__CLASS__ . '.SUMMARY_QUEUE', 'Queue'),
'JobStatus' => _t(__CLASS__ . '.SUMMARY_STATUS', 'Status'),
'JobTitle' => _t(__CLASS__ . '.SUMMARY_DESCRIPTION', 'Description'),
'Created' => _t(__CLASS__ . '.SUMMARY_ADDED', 'Added'),
'StartAfter' => _t(__CLASS__ . '.SUMMARY_SCHEDULED', 'Scheduled'),
'JobFinished' => _t(__CLASS__ . '.SUMMARY_FINISHED', 'Finished'),
]);

$config->removeComponentsByType(GridFieldFilterHeader::class);

$filter = new RichFilterHeader();
$filter
->setFilterConfig([
'getTrimmedImplementation' => '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',
];
}
}
14 changes: 14 additions & 0 deletions src/DataObjects/QueuedJobDescriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,20 @@ public function getMessagesRaw()
return $this->SavedJobMessages;
}

/**
* @return string
*/
public function getTrimmedImplementation()
{
$segments = explode('\\', $this->Implementation);

while (count($segments) > 2) {
array_shift($segments);
}

return implode('\\', $segments);
}

/**
* Return a map of numeric JobType values to localisable string representations.
* @return array
Expand Down
70 changes: 69 additions & 1 deletion tests/QueuedJobsAdminTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@

namespace Symbiote\QueuedJobs\Tests;

use SilverStripe\Core\Config\Config;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\FunctionalTest;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldFilterHeader;
use SilverStripe\Forms\TextareaField;
use SilverStripe\ORM\FieldType\DBDatetime;
use Symbiote\QueuedJobs\Controllers\QueuedJobsAdmin;
use Symbiote\QueuedJobs\Jobs\PublishItemsJob;
use Symbiote\QueuedJobs\Services\QueuedJobService;
use Symbiote\QueuedJobs\Tests\Jobs\TestDummyJob;
use Terraformers\RichFilterHeader\Form\GridField\RichFilterHeader;

/**
* Tests for the QueuedJobsAdmin ModelAdmin clas
Expand Down Expand Up @@ -120,4 +122,70 @@ public function testCreateJobWithStartAfterOption()

$this->admin->createjob($form->getData(), $form);
}

/**
* @param bool $enabled
* @dataProvider advancedUIProvider
*/
public function testAdvancedUI(bool $enabled): void
{
// enable / disable the advanced UI
QueuedJobsAdmin::config()->set('advanced_admin_ui', $enabled);

$fields = $this->admin->getEditForm('foo', new FieldList())->Fields();

/** @var GridField $field */
$field = $fields->fieldByName('QueuedJobDescriptor');
$this->assertInstanceOf(GridField::class, $field);
$this->assertCount(1, $field->getConfig()->getComponentsByType(GridFieldFilterHeader::class));

$expected = $enabled ? 1 : 0;
$this->assertCount($expected, $field->getConfig()->getComponentsByType(RichFilterHeader::class));

if (!$enabled) {
// If the advanced UI is not enabled we can bail out here
return;
}

/** @var RichFilterHeader $filterHeader */
$filterHeader = $field->getConfig()->getComponentByType(RichFilterHeader::class);
$html = $filterHeader->getHTMLFragments($field);

$this->assertArrayHasKey('header', $html);
$header = (string) $html['header'];

// Make sure that the advanced UI is properly rendered - we want to see dropdown fields and other inputs
$this->assertContains(
'<input type="text" name="filter[QueuedJobDescriptor][Implementation]" class="text',
$header
);
$this->assertContains(
'<select name="filter[QueuedJobDescriptor][JobType]" class="dropdown',
$header
);
$this->assertContains(
'<select name="filter[QueuedJobDescriptor][JobStatus]" class="dropdown',
$header
);
$this->assertContains(
'<input type="text" name="filter[QueuedJobDescriptor][JobTitle]" class="text',
$header
);
$this->assertContains(
'<select name="filter[QueuedJobDescriptor][Added]" class="dropdown',
$header
);
$this->assertContains(
'<select name="filter[QueuedJobDescriptor][Scheduled]" class="dropdown',
$header
);
}

public function advancedUIProvider(): array
{
return [
'Advanced UI disabled' => [false],
'Advanced UI enabled' => [true],
];
}
}