Added pages to view the list of potential jobs
TODO: add ability to run a job & view queued jobs
This commit is contained in:
@@ -3,7 +3,8 @@
|
|||||||
"type": "userfrosting-worker",
|
"type": "userfrosting-worker",
|
||||||
"description": "A sprinkle to add a worker thread.",
|
"description": "A sprinkle to add a worker thread.",
|
||||||
"require": {
|
"require": {
|
||||||
"symfony/lock": "*"
|
"symfony/lock": "*",
|
||||||
|
"twig/intl-extra": "^3.6"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"files" : [
|
"files" : [
|
||||||
|
|||||||
25
locale/en_US/messages.php
Normal file
25
locale/en_US/messages.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVSDev UF Worker (https://avsdev.uk)
|
||||||
|
*
|
||||||
|
* @link https://git.avsdev.uk/avsdev/sprinkle-worker
|
||||||
|
* @license https://git.avsdev.uk/avsdev/sprinkle-worker/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* US English message token translations for the 'worker' sprinkle.
|
||||||
|
*
|
||||||
|
* @author Craig Williams
|
||||||
|
*/
|
||||||
|
return [
|
||||||
|
'JOB' => [
|
||||||
|
1 => 'Job',
|
||||||
|
2 => 'Jobs',
|
||||||
|
|
||||||
|
'NAME' => 'Job name',
|
||||||
|
'PAGE_DESCRIPTION' => 'This page shows a list of programmed jobs that the UserFrosting Worker sprinkle knows about.',
|
||||||
|
],
|
||||||
|
|
||||||
|
'SPRINKLE' => 'Sprinkle',
|
||||||
|
];
|
||||||
22
routes/jobs.php
Normal file
22
routes/jobs.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVSDev UF Worker (https://avsdev.uk)
|
||||||
|
*
|
||||||
|
* @link https://git.avsdev.uk/avsdev/sprinkle-worker
|
||||||
|
* @license https://git.avsdev.uk/avsdev/sprinkle-worker/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
use UserFrosting\Sprinkle\Core\Util\NoCache;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Routes for available worker job listing.
|
||||||
|
*/
|
||||||
|
$app->group('/jobs', function () {
|
||||||
|
$this->get('', 'UserFrosting\Sprinkle\Worker\Controller\JobController:pageList')
|
||||||
|
->setName('uri_permissions');
|
||||||
|
})->add('authGuard')->add(new NoCache());
|
||||||
|
|
||||||
|
$app->group('/api/jobs', function () {
|
||||||
|
$this->get('', 'UserFrosting\Sprinkle\Worker\Controller\JobController:getList');
|
||||||
|
})->add('authGuard')->add(new NoCache());
|
||||||
102
src/Controller/JobController.php
Normal file
102
src/Controller/JobController.php
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVSDev UF Worker (https://avsdev.uk)
|
||||||
|
*
|
||||||
|
* @link https://git.avsdev.uk/avsdev/sprinkle-worker
|
||||||
|
* @license https://git.avsdev.uk/avsdev/sprinkle-worker/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace UserFrosting\Sprinkle\Worker\Controller;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
use UserFrosting\Sprinkle\Core\Controller\SimpleController;
|
||||||
|
use UserFrosting\Support\Exception\ForbiddenException;
|
||||||
|
use UserFrosting\Support\Exception\NotFoundException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller class for job-related requests, (listing jobs).
|
||||||
|
*
|
||||||
|
* @author Craig Williams (https://avsdev.uk)
|
||||||
|
*/
|
||||||
|
class JobController extends SimpleController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns a list of Jobs.
|
||||||
|
*
|
||||||
|
* Generates a list of jobs from the JobInspector. No sorting or pagination functionality.
|
||||||
|
* This page requires authentication.
|
||||||
|
*
|
||||||
|
* Request type: GET
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Response $response
|
||||||
|
* @param array $args
|
||||||
|
*
|
||||||
|
* @throws ForbiddenException If user is not authorized to access page
|
||||||
|
*/
|
||||||
|
public function getList(Request $request, Response $response, $args)
|
||||||
|
{
|
||||||
|
// GET parameters
|
||||||
|
$params = $request->getQueryParams();
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager */
|
||||||
|
$authorizer = $this->ci->authorizer;
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */
|
||||||
|
$currentUser = $this->ci->currentUser;
|
||||||
|
|
||||||
|
// Access-controlled page
|
||||||
|
if (!$authorizer->checkAccess($currentUser, 'uri_jobs')) {
|
||||||
|
throw new ForbiddenException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Sprinkle\Jobs\JobInspector $jobInspector */
|
||||||
|
$jobInspector = $this->ci->jobInspector;
|
||||||
|
|
||||||
|
$jobs = $jobInspector->getAvailableJobDefinitions();
|
||||||
|
|
||||||
|
// Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
|
||||||
|
// For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
|
||||||
|
return $response->withJson($jobs, 200, JSON_PRETTY_PRINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the job listing page.
|
||||||
|
*
|
||||||
|
* This page renders a table of jobs, with dropdown menus for admin actions for each job.
|
||||||
|
* Actions typically include: run job
|
||||||
|
* This page requires authentication.
|
||||||
|
*
|
||||||
|
* Request type: GET
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Response $response
|
||||||
|
* @param array $args
|
||||||
|
*
|
||||||
|
* @throws ForbiddenException If user is not authorized to access page
|
||||||
|
*/
|
||||||
|
public function pageList(Request $request, Response $response, $args)
|
||||||
|
{
|
||||||
|
/** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager */
|
||||||
|
$authorizer = $this->ci->authorizer;
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */
|
||||||
|
$currentUser = $this->ci->currentUser;
|
||||||
|
|
||||||
|
// Access-controlled page
|
||||||
|
if (!$authorizer->checkAccess($currentUser, 'uri_jobs')) {
|
||||||
|
throw new ForbiddenException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Sprinkle\Jobs\JobInspector $jobInspector */
|
||||||
|
$jobInspector = $this->ci->jobInspector;
|
||||||
|
|
||||||
|
$jobs = $jobInspector->getAvailableJobDefinitions();
|
||||||
|
|
||||||
|
return $this->ci->view->render($response, 'pages/jobs.html.twig', [
|
||||||
|
'jobs' => $jobs
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
95
src/Database/Seeds/WorkerPermissions.php
Normal file
95
src/Database/Seeds/WorkerPermissions.php
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVSDev UF Worker (https://avsdev.uk)
|
||||||
|
*
|
||||||
|
* @link https://git.avsdev.uk/avsdev/sprinkle-worker
|
||||||
|
* @license https://git.avsdev.uk/avsdev/sprinkle-worker/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace UserFrosting\Sprinkle\Worker\Database\Seeds;
|
||||||
|
|
||||||
|
use UserFrosting\Sprinkle\Account\Database\Models\Permission;
|
||||||
|
use UserFrosting\Sprinkle\Account\Database\Models\Role;
|
||||||
|
use UserFrosting\Sprinkle\Core\Database\Seeder\BaseSeed;
|
||||||
|
use UserFrosting\Sprinkle\Core\Facades\Seeder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seeder for the dashboard permissions.
|
||||||
|
*/
|
||||||
|
class WorkerPermissions extends BaseSeed
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
// We require the default roles
|
||||||
|
Seeder::execute('DefaultRoles');
|
||||||
|
|
||||||
|
// Get and save permissions
|
||||||
|
$permissions = $this->getPermissions();
|
||||||
|
$this->savePermissions($permissions);
|
||||||
|
|
||||||
|
// Add default mappings to permissions
|
||||||
|
$this->syncPermissionsRole($permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array Permissions to seed
|
||||||
|
*/
|
||||||
|
protected function getPermissions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'uri_job' => new Permission([
|
||||||
|
'slug' => 'uri_job',
|
||||||
|
'name' => 'View job',
|
||||||
|
'conditions' => 'always()',
|
||||||
|
'description' => 'View the job page of any job.',
|
||||||
|
]),
|
||||||
|
'uri_jobs' => new Permission([
|
||||||
|
'slug' => 'uri_jobs',
|
||||||
|
'name' => 'Jobs management page',
|
||||||
|
'conditions' => 'always()',
|
||||||
|
'description' => 'View a page containing a table of jobs.',
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save permissions.
|
||||||
|
*
|
||||||
|
* @param array $permissions
|
||||||
|
*/
|
||||||
|
protected function savePermissions(array &$permissions)
|
||||||
|
{
|
||||||
|
foreach ($permissions as $slug => $permission) {
|
||||||
|
// Trying to find if the permission already exist
|
||||||
|
$existingPermission = Permission::where(['slug' => $permission->slug, 'conditions' => $permission->conditions])->first();
|
||||||
|
|
||||||
|
// Don't save if already exist, use existing permission reference
|
||||||
|
// otherwise to re-sync permissions and roles
|
||||||
|
if ($existingPermission == null) {
|
||||||
|
$permission->save();
|
||||||
|
} else {
|
||||||
|
$permissions[$slug] = $existingPermission;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync permissions with default roles.
|
||||||
|
*
|
||||||
|
* @param array $permissions
|
||||||
|
*/
|
||||||
|
protected function syncPermissionsRole(array $permissions)
|
||||||
|
{
|
||||||
|
$roleSiteAdmin = Role::where('slug', 'site-admin')->first();
|
||||||
|
if ($roleSiteAdmin) {
|
||||||
|
$roleSiteAdmin->permissions()->syncWithoutDetaching([
|
||||||
|
$permissions['uri_jobs']->id,
|
||||||
|
$permissions['uri_job']->id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,8 +11,10 @@ namespace UserFrosting\Sprinkle\Worker\ServicesProvider;
|
|||||||
|
|
||||||
use Illuminate\Container\Container;
|
use Illuminate\Container\Container;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Twig\Extra\Intl\IntlExtension;
|
||||||
use UserFrosting\Sprinkle\Worker\Worker\Worker;
|
use UserFrosting\Sprinkle\Worker\Worker\Worker;
|
||||||
use UserFrosting\Sprinkle\Core\ServicesProvider\BaseServicesProvider;
|
use UserFrosting\Sprinkle\Core\ServicesProvider\BaseServicesProvider;
|
||||||
|
use UserFrosting\Sprinkle\Worker\Jobs\JobInspector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Worker services provider.
|
* Worker services provider.
|
||||||
@@ -49,5 +51,20 @@ class ServicesProvider
|
|||||||
$container['jobInspector'] = function ($c) {
|
$container['jobInspector'] = function ($c) {
|
||||||
return new JobInspector($c);
|
return new JobInspector($c);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extends the 'view' service with the HasRole extension for Twig.
|
||||||
|
*
|
||||||
|
* @return \Slim\Views\Twig
|
||||||
|
*/
|
||||||
|
$container->extend('view', function ($view, $c) {
|
||||||
|
$twig = $view->getEnvironment();
|
||||||
|
|
||||||
|
if (!$twig->hasExtension('Twig\Extra\Intl\IntlExtension')) {
|
||||||
|
$twig->addExtension(new IntlExtension());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $view;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
templates/navigation/sidebar-menu.html.twig
Normal file
10
templates/navigation/sidebar-menu.html.twig
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{% extends "@admin/navigation/sidebar-menu.html.twig" %}
|
||||||
|
|
||||||
|
{% block navigation %}
|
||||||
|
{{ parent() }}
|
||||||
|
{% if checkAccess('uri_jobs') %}
|
||||||
|
<li>
|
||||||
|
<a href="{{site.uri.public}}/jobs"><i class="fas fa-tasks fa-fw"></i> <span>{{ translate("JOB", 2) }}</span></a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
41
templates/pages/jobs.html.twig
Normal file
41
templates/pages/jobs.html.twig
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{% extends "pages/abstract/dashboard.html.twig" %}
|
||||||
|
|
||||||
|
{% block stylesheets_page %}
|
||||||
|
<!-- Page-specific CSS asset bundle -->
|
||||||
|
{{ assets.css('css/form-widgets') | raw }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{# Overrides blocks in head of base template #}
|
||||||
|
{% block page_title %}{{ translate("JOB", 2)}}{% endblock %}
|
||||||
|
|
||||||
|
{% block page_description %}{{ translate("JOB.PAGE_DESCRIPTION")}}{% endblock %}
|
||||||
|
|
||||||
|
{% block body_matter %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div id="widget-jobs" class="box box-primary">
|
||||||
|
<div class="box-header">
|
||||||
|
<h3 class="box-title pull-left"><i class="fas fa-jobs fa-fw"></i> {{translate('JOB', 2)}}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
{% include "tables/jobs.html.twig" with {
|
||||||
|
"table" : {
|
||||||
|
"id" : "table-jobs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
%}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block scripts_page %}
|
||||||
|
<!-- Include validation rules -->
|
||||||
|
<script>
|
||||||
|
{% include "pages/partials/page.js.twig" %}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Include form widgets JS -->
|
||||||
|
{{ assets.js('js/form-widgets') | raw }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
39
templates/tables/jobs.html.twig
Normal file
39
templates/tables/jobs.html.twig
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{# This partial template renders a table of permissions, to be populated with rows via an AJAX request.
|
||||||
|
# This extends a generic template for paginated tables.
|
||||||
|
#
|
||||||
|
# Note that this template contains a "skeleton" table with an empty table body, and then a block of Handlebars templates which are used
|
||||||
|
# to render the table cells with the data from the AJAX request.
|
||||||
|
#}
|
||||||
|
|
||||||
|
{% extends "tables/table-paginated.html.twig" %}
|
||||||
|
|
||||||
|
{% block table %}
|
||||||
|
<table id="{{table.id}}" class="tablesorter table table-bordered table-hover table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-column-name="name" data-priority="1">{{translate('NAME')}}</th>
|
||||||
|
<th data-column-name="sprinkle" data-priority="1">{{translate('SPRINKLE')}}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for job in jobs %}
|
||||||
|
<tr>
|
||||||
|
<td data-text="{{job.sprinkle}}/{{job.name}}">
|
||||||
|
<div>
|
||||||
|
<strong>
|
||||||
|
<!--<a href="{{site.uri.public}}/jobs/t/{{job.sprinkle}}/{{job.name}}">{{job.name}}</a>-->
|
||||||
|
{{job.name}}
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td data-text="{{job.sprinkle}}">
|
||||||
|
{{job.sprinkle}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block table_pager_controls %}{% endblock %}
|
||||||
Reference in New Issue
Block a user