5 Commits

Author SHA1 Message Date
jbcr
d631e6555e enclosure js code into anonymous function (#28)
* encosure js code into annonymous function
2019-12-05 15:25:50 +01:00
jbcr
ca37b2438d Add the ContentStructureFactory::transform() Mode. (#19)
* implement #18
* add an interface for factory
2019-11-13 10:36:37 +01:00
jbcr
d166f3aea8 Update to v2 (#15)
* refs #13

* update reload JS after form sent
* add eror management in job details view
* update Job and Schedule gateway
* update view
* remove unnecessary use
* add BC doc and fix error if no result in findForScheduled
* update dev dependency
* set final some class
* fix bug: form variable access on second-page loading
* add pager for scheduled dataflow list
2019-11-12 11:00:40 +01:00
jbcr
9ed5fd3c2b update docs #16 (#17)
* update docs #16

* add text style
2019-11-08 11:19:12 +01:00
jbcr
f98fecdca9 refs #12 (#14) 2019-11-08 11:18:54 +01:00
19 changed files with 493 additions and 342 deletions

14
CHANGELOG.md Normal file
View File

@@ -0,0 +1,14 @@
# version 2.0.0
* Update to use Dataflow v2.0+
* Add compiler pass to change the Dataflow DBAL connection factory
* Use the DBAL connection from siteaccess
* Add `mode` argument on `ContentStructureFactory::transform()` function
* Add `CodeRhapsodie\EzDataflowBundle\Factory\ContentStructureFactoryInterface`
# version 1.0.0
* Initial version to use Dataflow v1.0+ into eZ Platform
* Add Admin UI
* Add content writer
* Add content structure

View File

@@ -6,6 +6,8 @@ EzDataflow bundle is intended to manage content imports from external data sourc
> Note: before using this bundle, please read the [Code Rhapsodie Dataflow bundle documentation](https://github.com/code-rhapsodie/dataflow-bundle/blob/master/README.md).
> Command line notice: When you use Dataflow commands, **use `--siteaccess` instead of `--connection`** expect for `code-rhapsodie:dataflow:dump-schema` command.
## User Interface (UI)
The UI lets you create workflow processes from any defined `DataflowTypes`, and set options to each.
@@ -24,6 +26,8 @@ $ composer require code-rhapsodie/ezdataflow-bundle
### Step 2: Enable the bundle
> Note: The loading order between the Dataflow bundle and Ez Dataflow bundle is important. Dataflow must be loaded first.
#### Symfony 4 (new tree)
For Symfony 4, add those two lines in the `config/bundles.php` file:
@@ -198,7 +202,8 @@ class MyDataflowType extends AbstractDataflowType
$remoteId,
'eng-GB',
'article2',
54 //Parent location id
54, //Parent location id
ContentStructureFactoryInterface::MODE_INSERT_OR_UPDATE //Optional value. Other choice : ContentStructureFactoryInterface::MODE_INSERT_ONLY or ContentStructureFactoryInterface::MODE_UPDATE_ONLY
);
});
$builder->addWriter($this->contentWriter);

15
UPGRADE.md Normal file
View File

@@ -0,0 +1,15 @@
# From v1.0 to v2.0
When you use Dataflow commands, use `--siteaccess` instead of `--connection` except for `code-rhapsodie:dataflow:dump-schema`.
[BC] The return of `CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway::findForScheduled`
and `CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway::findAllOrderedByLabel` has been changed.
The iterable contains an associative array instead of an object.
[BC] In classes `CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway` and
`CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway`, all methods return `Doctrine\ORM\Query` object has
changed to return now a `Doctrine\DBAL\Query\QueryBuilder`
[BC] The return type of `CodeRhapsodie\EzDataflowBundle\Factory\ContentStructureFactory::transform` has been changed
from `CodeRhapsodie\EzDataflowBundle\Model\ContentStructure` to `mixed`. In fact only `false` or
`CodeRhapsodie\EzDataflowBundle\Model\ContentStructure` object will be returned.

View File

@@ -41,13 +41,13 @@
}
},
"require": {
"code-rhapsodie/dataflow-bundle": "^1.0 || dev-master",
"code-rhapsodie/dataflow-bundle": "^2.0 || dev-master",
"ezsystems/ezplatform-admin-ui": "^1.0",
"ezsystems/ezpublish-kernel": "^7.0"
},
"require-dev": {
"phpunit/phpunit": "^7||^8",
"doctrine/orm": "^2.4.5"
"doctrine/dbal": "^2.0"
},
"minimum-stability": "dev",
"prefer-stable": true,
@@ -56,7 +56,8 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
"dev-master": "2.x-dev",
"dev-v1.x": "1.x-dev"
}
}
}

View File

@@ -10,11 +10,11 @@ use CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway;
use CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway;
use CodeRhapsodie\DataflowBundle\Entity\Job;
use CodeRhapsodie\DataflowBundle\Entity\ScheduledDataflow;
use Doctrine\ORM\Query;
use Doctrine\DBAL\Query\QueryBuilder;
use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute;
use EzSystems\EzPlatformAdminUi\Notification\NotificationHandlerInterface;
use EzSystems\EzPlatformAdminUiBundle\Controller\Controller;
use Pagerfanta\Adapter\DoctrineORMAdapter;
use Pagerfanta\Adapter\DoctrineDbalAdapter;
use Pagerfanta\Pagerfanta;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -51,7 +51,7 @@ class DashboardController extends Controller
return $this->render('@ezdesign/ezdataflow/Dashboard/main.html.twig');
}
public function repeating(): Response
public function repeating(Request $request): Response
{
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
@@ -62,11 +62,27 @@ class DashboardController extends Controller
]);
return $this->render('@ezdesign/ezdataflow/Dashboard/repeating.html.twig', [
'items' => $this->scheduledDataflowGateway->findAllOrderedByLabel(),
'pager' => $this->getPager($this->scheduledDataflowGateway->getListQueryForAdmin(), $request),
'form' => $form->createView(),
]);
}
/**
* @Route("/repeating", name="coderhapsodie.ezdataflow.repeating")
*
* @param Request $request
*
* @return Response
*/
public function getRepeatingPage(Request $request): Response
{
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
return $this->render('@ezdesign/ezdataflow/Dashboard/repeating.html.twig', [
'pager' => $this->getPager($this->scheduledDataflowGateway->getListQueryForAdmin(), $request),
]);
}
public function oneshot(Request $request): Response
{
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
@@ -142,9 +158,12 @@ class DashboardController extends Controller
]);
}
private function getPager(Query $query, Request $request): Pagerfanta
private function getPager(QueryBuilder $query, Request $request): Pagerfanta
{
$pager = new Pagerfanta(new DoctrineORMAdapter($query));
$pager = new Pagerfanta(new DoctrineDbalAdapter($query, function ($queryBuilder) {
return $queryBuilder->select('COUNT(DISTINCT id) AS total_results')
->setMaxResults(1);
}));
$pager->setMaxPerPage(20);
$pager->setCurrentPage($request->query->get('page', 1));

View File

@@ -10,11 +10,12 @@ use CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway;
use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute;
use EzSystems\EzPlatformAdminUi\Notification\NotificationHandlerInterface;
use EzSystems\EzPlatformAdminUiBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
/**
* @Route("/ezdataflow/job")
@@ -82,7 +83,8 @@ class JobController extends Controller
}
return new JsonResponse([
'redirect' => $this->generateUrl('coderhapsodie.ezdataflow.main', ['_fragment' => 'oneshot']),
'redirect' => $this->generateUrl('coderhapsodie.ezdataflow.main', ['_fragment' => 'oneshot'],
UrlGeneratorInterface::ABSOLUTE_URL),
]);
}

View File

@@ -10,7 +10,7 @@ use CodeRhapsodie\EzDataflowBundle\Model\ContentUpdateStructure;
use eZ\Publish\API\Repository\ContentService;
use eZ\Publish\API\Repository\Exceptions\NotFoundException;
class ContentStructureFactory
final class ContentStructureFactory implements ContentStructureFactoryInterface
{
/**
* @var ContentService
@@ -28,27 +28,35 @@ class ContentStructureFactory
}
/**
* @param array $data
* @param string $remoteId
* @param string $language
* @param string $contentType
* @param mixed $parentLocations
* @param array $data
* @param string $remoteId
* @param string $language
* @param string $contentType
* @param mixed $parentLocations
* @param int $mode One of the constant ContentStructureFactoryInterface::MODE_*
*
* @return ContentStructure
* @return false|ContentStructure
*
* @throws \CodeRhapsodie\EzDataflowBundle\Exception\InvalidArgumentTypeException
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
*/
public function transform(array $data, string $remoteId, string $language, string $contentType, $parentLocations): ContentStructure
public function transform(array $data, string $remoteId, string $language, string $contentType, $parentLocations, int $mode = ContentStructureFactoryInterface::MODE_INSERT_OR_UPDATE)
{
try {
$content = $this->contentService->loadContentByRemoteId($remoteId);
if ($mode === static::MODE_INSERT_ONLY) {
return false;
}
return ContentUpdateStructure::createForContentId($content->id, $language, $data);
} catch (NotFoundException $e) {
// The content doesn't exist yet, so it will be created.
}
if ($mode === static::MODE_UPDATE_ONLY) {
return false;
}
return new ContentCreateStructure(
$contentType,
$language,

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Factory;
interface ContentStructureFactoryInterface
{
public const MODE_INSERT_OR_UPDATE = 1;
public const MODE_INSERT_ONLY = 2;
public const MODE_UPDATE_ONLY = 3;
/**
* @param array $data
* @param string $remoteId
* @param string $language
* @param string $contentType
* @param int|string $parentLocations Int for location id or string for remote location id
* @param int $mode ContentStructureFactoryInterface
*
* @return false|\CodeRhapsodie\EzDataflowBundle\Model\ContentStructure
*/
public function transform(array $data, string $remoteId, string $language, string $contentType, $parentLocations, int $mode = ContentStructureFactoryInterface::MODE_INSERT_OR_UPDATE);
}

View File

@@ -6,68 +6,49 @@ namespace CodeRhapsodie\EzDataflowBundle\Gateway;
use CodeRhapsodie\DataflowBundle\Entity\Job;
use CodeRhapsodie\DataflowBundle\Repository\JobRepository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Doctrine\DBAL\Query\QueryBuilder;
class JobGateway
final class JobGateway
{
/** @var EntityManagerInterface */
private $em;
/** @var JobRepository */
private $jobRepository;
public function __construct(EntityManagerInterface $em, JobRepository $jobRepository)
public function __construct(JobRepository $jobRepository)
{
$this->em = $em;
$this->jobRepository = $jobRepository;
}
public function find(int $id): Job
public function find(int $id): ?Job
{
return $this->jobRepository->find($id);
}
public function findForScheduled(int $id): iterable
public function getOneshotListQueryForAdmin(): QueryBuilder
{
return $this->jobRepository->findBy(['scheduledDataflow' => $id], ['requestedDate' => 'desc'], 20);
return $this->jobRepository->createQueryBuilder('i')
->andWhere('i.scheduled_dataflow_id IS NULL')
->addOrderBy('i.requested_date', 'DESC');
}
public function getOneshotListQueryForAdmin(): Query
public function getListQueryForAdmin(): QueryBuilder
{
$query = $this->jobRepository->createQueryBuilder('i')
->andWhere('i.scheduledDataflow IS NULL')
->addOrderBy('i.requestedDate', 'DESC');
return $query->getQuery();
return $this->jobRepository->createQueryBuilder('w')
->addOrderBy('w.requested_date', 'DESC');
}
public function getListQueryForAdmin(): Query
public function getListQueryForScheduleAdmin(int $id): QueryBuilder
{
$query = $this->jobRepository->createQueryBuilder('w')
->addOrderBy('w.requestedDate', 'DESC');
return $query->getQuery();
}
public function getListQueryForScheduleAdmin(int $id): Query
{
$query = $this->jobRepository->createQueryBuilder('w')
->where('w.scheduledDataflow = :schedule_id')
return $this->jobRepository->createQueryBuilder('w')
->where('w.scheduled_dataflow_id = :schedule_id')
->setParameter('schedule_id', $id)
->addOrderBy('w.requestedDate', 'DESC');
return $query->getQuery();
->addOrderBy('w.requested_date', 'DESC');
}
/**
* @param Job $job
*
* @throws \Doctrine\ORM\ORMException
* @throws \Doctrine\ORM\OptimisticLockException
*/
public function save(Job $job)
{
$this->em->persist($job);
$this->em->flush();
$this->jobRepository->save($job);
}
}

View File

@@ -6,54 +6,44 @@ namespace CodeRhapsodie\EzDataflowBundle\Gateway;
use CodeRhapsodie\DataflowBundle\Entity\ScheduledDataflow;
use CodeRhapsodie\DataflowBundle\Repository\ScheduledDataflowRepository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\DBAL\Query\QueryBuilder;
class ScheduledDataflowGateway
final class ScheduledDataflowGateway
{
/** @var EntityManagerInterface */
private $em;
/** @var ScheduledDataflowRepository */
private $scheduledDataflowRepository;
public function __construct(EntityManagerInterface $em, ScheduledDataflowRepository $scheduledDataflowRepository)
public function __construct(ScheduledDataflowRepository $scheduledDataflowRepository)
{
$this->em = $em;
$this->scheduledDataflowRepository = $scheduledDataflowRepository;
}
public function find(int $id): ScheduledDataflow
public function find(int $id): ?ScheduledDataflow
{
return $this->scheduledDataflowRepository->find($id);
}
public function findAllOrderedByLabel(): iterable
public function getListQueryForAdmin(): QueryBuilder
{
return $this->scheduledDataflowRepository->findBy([], ['label' => 'asc']);
return $this->scheduledDataflowRepository->createQueryBuilder('s')
->addOrderBy('s.label', 'ASC');
}
/**
* @param ScheduledDataflow $scheduledDataflow
*
* @throws \Doctrine\ORM\ORMException
* @throws \Doctrine\ORM\OptimisticLockException
*/
public function save(ScheduledDataflow $scheduledDataflow)
{
$this->em->persist($scheduledDataflow);
$this->em->flush();
$this->scheduledDataflowRepository->save($scheduledDataflow);
}
/**
* @param int $id
*
* @throws \Doctrine\ORM\ORMException
* @throws \Doctrine\ORM\OptimisticLockException
* @throws \Throwable
*/
public function delete(int $id): void
{
$workflow = $this->find($id);
$this->em->remove($workflow);
$this->em->flush();
$this->scheduledDataflowRepository->delete($id);
}
}

View File

@@ -2,6 +2,8 @@ services:
_defaults:
public: false
coderhapsodie.dataflow.connection: "@ezpublish.persistence.connection"
CodeRhapsodie\EzDataflowBundle\Controller\DashboardController:
public: true
arguments:
@@ -77,6 +79,8 @@ services:
CodeRhapsodie\EzDataflowBundle\EventSubscriber\MenuSubscriber:
tags: ['kernel.event_subscriber']
CodeRhapsodie\EzDataflowBundle\Factory\ContentStructureFactoryInterface: '@CodeRhapsodie\EzDataflowBundle\Factory\ContentStructureFactory'
CodeRhapsodie\EzDataflowBundle\Factory\ContentStructureFactory:
arguments:
$contentService: '@eZ\Publish\API\Repository\ContentService'
@@ -97,12 +101,10 @@ services:
CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway:
arguments:
$em: '@doctrine.orm.default_entity_manager'
$scheduledDataflowRepository: '@CodeRhapsodie\DataflowBundle\Repository\ScheduledDataflowRepository'
CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway:
arguments:
$em: '@doctrine.orm.default_entity_manager'
$jobRepository: '@CodeRhapsodie\DataflowBundle\Repository\JobRepository'
CodeRhapsodie\EzDataflowBundle\Tab\RepeatingTab:

View File

@@ -74,3 +74,4 @@ coderhapsodie.ezdataflow.workflow.edit.submit: Save
coderhapsodie.dataflow.update.next: 'Next execution'
coderhapsodie.ezdataflow.workflow.edit.success: 'Dataflow schedule successfully updated.'
coderhapsodie.ezdataflow.workflow.edit.error: 'An error occurred during the dataflow schedule update: "%message%".'
coderhapsodie.ezdataflow.notfound: 'Requested data is not found'

View File

@@ -72,3 +72,4 @@ coderhapsodie.ezdataflow.workflow.edit.submit: Sauvegarder
coderhapsodie.dataflow.update.next: 'Prochaine exécution'
coderhapsodie.ezdataflow.workflow.edit.success: 'La programmation du dataflow a été mis à jour avec succès.'
coderhapsodie.ezdataflow.workflow.edit.error: 'Une erreur est survenue lors de la modification de la programmation du dataflow : "%message%".'
coderhapsodie.ezdataflow.notfound: 'Les données demandées sont introuvables'

View File

@@ -24,7 +24,8 @@
<h3 class="modal-title">{{ 'coderhapsodie.ezdataflow.workflow.history.title'|trans }}</h3>
<button type="button" class="close" aria-label="Close">
<svg class="ez-icon ez-icon--medium" aria-hidden="true">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#discard"></use>
<use xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#discard"></use>
</svg>
</button>
</div>
@@ -34,35 +35,40 @@
</div>
<script>
$('#ez-modal--history-details').modal({keyboard: false, show: false});
$('.history-details-aware').delegate('.modal-history-details', 'click', function(e) {
e.preventDefault();
$('#modal_content-details').html('');
$('#ez-modal--history-details').modal('show');
$.ajax(this.href, {
success: function(result) {
$('#modal_content-details').html(result);
}
(function ($) {
$(document).ready(function ($) {
$('#ez-modal--history-details').modal({keyboard: false, show: false});
$('.history-details-aware').delegate('.modal-history-details', 'click', function (e) {
e.preventDefault();
$('#modal_content-details').html('');
$('#ez-modal--history-details').modal('show');
$.ajax(this.href, {
success: function (result) {
$('#modal_content-details').html(result);
}
});
});
$('#ez-modal--history-details .close').click(function () {
$('#ez-modal--history-details').modal('hide');
});
$(document).ready(function () {
if (window.location.hash && window.location.hash === '#oneshot') {
$('#ez-tab-list-coderhapsodie-ezdataflow li a').removeClass('active');
$('#ez-tab-list-content-coderhapsodie-ezdataflow .tab-pane').removeClass('active');
$('#ez-tab-list-coderhapsodie-ezdataflow li a:eq(1)').addClass('active');
$('#ez-tab-list-content-coderhapsodie-ezdataflow .tab-pane:eq(1)').addClass('active');
}
if (window.location.hash && window.location.hash === '#history') {
$('#ez-tab-list-coderhapsodie-ezdataflow li a').removeClass('active');
$('#ez-tab-list-content-coderhapsodie-ezdataflow .tab-pane').removeClass('active');
$('#ez-tab-list-coderhapsodie-ezdataflow li a:eq(2)').addClass('active');
$('#ez-tab-list-content-coderhapsodie-ezdataflow .tab-pane:eq(2)').addClass('active');
}
});
});
});
$('#ez-modal--history-details .close').click(function () {
$('#ez-modal--history-details').modal('hide');
});
$(document).ready(function () {
if (window.location.hash && window.location.hash === '#oneshot') {
$('#ez-tab-list-coderhapsodie-ezdataflow li a').removeClass('active');
$('#ez-tab-list-content-coderhapsodie-ezdataflow .tab-pane').removeClass('active');
$('#ez-tab-list-coderhapsodie-ezdataflow li a:eq(1)').addClass('active');
$('#ez-tab-list-content-coderhapsodie-ezdataflow .tab-pane:eq(1)').addClass('active');
}
if (window.location.hash && window.location.hash === '#history') {
$('#ez-tab-list-coderhapsodie-ezdataflow li a').removeClass('active');
$('#ez-tab-list-content-coderhapsodie-ezdataflow .tab-pane').removeClass('active');
$('#ez-tab-list-coderhapsodie-ezdataflow li a:eq(2)').addClass('active');
$('#ez-tab-list-content-coderhapsodie-ezdataflow .tab-pane:eq(2)').addClass('active');
}
});
})(jQuery);
</script>
{% endblock %}

View File

@@ -1,13 +1,13 @@
{% form_theme form 'bootstrap_3_layout.html.twig' %}
<h2>{{ 'coderhapsodie.ezdataflow.oneshot.title'|trans }}</h2>
<div class="ez-table-header">
<div class="ez-table-header__headline">{{ 'coderhapsodie.ezdataflow.oneshot.list.title'|trans }}</div>
<div>
<button type="button" class="btn btn-primary btn-modal-launcher" data-toggle="modal" data-target="#ez-modal--new-oneshot">
<button type="button" class="btn btn-primary btn-modal-launcher" data-toggle="modal"
data-target="#ez-modal--new-oneshot">
<svg class="ez-icon ez-icon--medium ez-icon--light ez-icon-create">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#create"></use>
<use xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#create"></use>
</svg>
</button>
</div>
@@ -16,6 +16,7 @@
{{ include('@ezdesign/ezdataflow/parts/tab/job_list.html.twig', {identifier: 'ezdataflow_oneshot_history', paginate_route: 'coderhapsodie.ezdataflow.oneshot'}) }}
{% if form is defined %}
{% form_theme form 'bootstrap_3_layout.html.twig' %}
<div class="modal fade ez-modal show" id="ez-modal--new-oneshot" tabindex="-1" role="dialog" aria-modal="true">
<div class="modal-dialog" role="document">
{{ include('@ezdesign/ezdataflow/parts/schedule_form.html.twig', {mode: 'oneshot'}) }}
@@ -23,27 +24,35 @@
</div>
<script>
$('#ez-modal--new-oneshot').on('submit', 'form', function (e) {
e.preventDefault();
url = $(this).attr('action');
data = new FormData(this);
$.ajax({
'type': 'POST',
'url': url,
'data': data,
processData: false,
contentType: false,
success: function (result) {
if (result.redirect) {
window.location = result.redirect;
return;
}
let obj = $(result.form).find('.modal-body');
$('#ez-modal--new-oneshot .modal-body').html($(obj).html());
}
(function ($) {
$(document).ready(function ($) {
$('#ez-modal--new-oneshot').on('submit', 'form', function (e) {
e.preventDefault();
url = $(this).attr('action');
data = new FormData(this);
$.ajax({
'type': 'POST',
'url': url,
'data': data,
processData: false,
contentType: false,
success: function (result) {
if (result.redirect) {
if (window.location.href === result.redirect) {
document.location.reload();
}
window.location = result.redirect;
return;
}
let obj = $(result.form).find('.modal-body');
$('#ez-modal--new-oneshot .modal-body').html($(obj).html());
}
});
});
});
});
})(jQuery);
</script>

View File

@@ -1,5 +1,3 @@
{% form_theme form 'bootstrap_3_layout.html.twig' %}
<h2>{{ 'coderhapsodie.ezdataflow.repeating.title'|trans }}</h2>
<div class="ez-table-header">
@@ -15,162 +13,172 @@
</div>
</div>
{{ include('@ezdesign/ezdataflow/parts/tab/schedule_list.html.twig') }}
{{ include('@ezdesign/ezdataflow/parts/tab/schedule_list.html.twig', {identifier: 'ezdataflow_schedule_results', paginate_route: 'coderhapsodie.ezdataflow.repeating'}) }}
<div class="modal fade ez-modal show" id="ez-modal--new-scheduled" tabindex="-1" role="dialog" aria-modal="true">
<div class="modal-dialog" role="document">
{{ include('@ezdesign/ezdataflow/parts/schedule_form.html.twig') }}
{% if form is defined %}
{% form_theme form 'bootstrap_3_layout.html.twig' %}
<div class="modal fade ez-modal show" id="ez-modal--new-scheduled" tabindex="-1" role="dialog" aria-modal="true">
<div class="modal-dialog" role="document">
{{ include('@ezdesign/ezdataflow/parts/schedule_form.html.twig') }}
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade ez-modal ez-modal--delete-workflow show" id="ez-modal--delete-workflow" tabindex="-1"
role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<svg class="ez-icon ez-icon--medium" aria-hidden="true">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#discard"></use>
</svg>
</button>
</div>
<div class="modal-body">
<p class="font-weight-bold" id="delete-modal--workflow-name"></p>
<p class="ez-modal-body__main">{{ 'coderhapsodie.ezdataflow.workflow.delete'|trans }}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-dark"
data-dismiss="modal">{{ 'coderhapsodie.ezdataflow.workflow.new.cancel'|trans }}</button>
<button type="button" class="btn btn-danger font-weight-bold"
id="ez-modal--delete-workflow-confirm">{{ 'coderhapsodie.ezdataflow.workflow.list.delete'|trans }}</button>
<!-- Modal -->
<div class="modal fade ez-modal ez-modal--delete-workflow show" id="ez-modal--delete-workflow" tabindex="-1"
role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<svg class="ez-icon ez-icon--medium" aria-hidden="true">
<use xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="../../ez-icons.svg#discard"></use>
</svg>
</button>
</div>
<div class="modal-body">
<p class="font-weight-bold" id="delete-modal--workflow-name"></p>
<p class="ez-modal-body__main">{{ 'coderhapsodie.ezdataflow.workflow.delete'|trans }}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-dark"
data-dismiss="modal">{{ 'coderhapsodie.ezdataflow.workflow.new.cancel'|trans }}</button>
<button type="button" class="btn btn-danger font-weight-bold"
id="ez-modal--delete-workflow-confirm">{{ 'coderhapsodie.ezdataflow.workflow.list.delete'|trans }}</button>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade ez-modal show" id="ez-modal--history" tabindex="-1" role="dialog" aria-modal="true">
<div class="modal-dialog" role="document" style="max-width: 80%">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">{{ 'coderhapsodie.ezdataflow.workflow.history.title'|trans }}</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<svg class="ez-icon ez-icon--medium" aria-hidden="true">
<use xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#discard"></use>
</svg>
</button>
<div class="modal fade ez-modal show" id="ez-modal--history" tabindex="-1" role="dialog" aria-modal="true">
<div class="modal-dialog" role="document" style="max-width: 80%">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">{{ 'coderhapsodie.ezdataflow.workflow.history.title'|trans }}</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<svg class="ez-icon ez-icon--medium" aria-hidden="true">
<use xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#discard"></use>
</svg>
</button>
</div>
<div class="modal-body history-details-aware" id="modal_content"></div>
</div>
<div class="modal-body history-details-aware" id="modal_content"></div>
</div>
</div>
</div>
<div class="modal fade ez-modal show" id="ez-modal--edit-scheduled" tabindex="-1" role="dialog" aria-modal="true">
<div class="modal-dialog" role="document">
<div id="schedule_edit"></div>
<div class="modal fade ez-modal show" id="ez-modal--edit-scheduled" tabindex="-1" role="dialog" aria-modal="true">
<div class="modal-dialog" role="document">
<div id="schedule_edit"></div>
</div>
</div>
</div>
<script>
$('#ez-modal--history').modal({keyboard: false, show: false});
$('.modal-history').each(function (index, elem) {
$(elem).click(function (e) {
e.preventDefault();
$('#modal_content').html('');
$('#ez-modal--history').modal('show');
$.ajax(elem.href, {
success: function (result) {
$('#modal_content').html(result);
}
});
});
});
$('#ez-modal--history .close').click(function () {
$('#ez-modal--history').modal('hide');
});
<script>
(function ($) {
$(document).ready(function ($) {
$('#ez-modal--history').modal({keyboard: false, show: false});
$('.modal-history').each(function (index, elem) {
$(elem).click(function (e) {
e.preventDefault();
$('#modal_content').html('');
$('#ez-modal--history').modal('show');
$.ajax(elem.href, {
success: function (result) {
$('#modal_content').html(result);
}
});
});
});
$('#ez-modal--history .close').click(function () {
$('#ez-modal--history').modal('hide');
});
$('#ez-modal--delete-workflow').modal({keyboard: true, show: false});
$('.modal-delete').each(function (index, elem) {
$(elem).click(function (e) {
e.preventDefault();
$('#delete-modal--workflow-name').html($(elem).data('name'));
$('#ez-modal--delete-workflow').modal('show');
$('#ez-modal--delete-workflow-confirm').data('target', $(elem).data('path'));
});
});
$('#ez-modal--delete-workflow-confirm').click(function () {
let target = $(this).data('target');
if (target && target !== '') {
$.ajax(target, {
method: 'POST',
complete: function () {
$('#ez-modal--delete-workflow').modal('hide');
window.location.reload();
}
});
}
});
$('#ez-modal--edit-scheduled').modal({keyboard: true, show: false});
$('.modal-edit').each(function (index, elem) {
$(elem).click(function (e) {
e.preventDefault();
$('#schedule_edit').html('');
$('#ez-modal--edit-scheduled').modal('show');
$.ajax(elem.href, {
success: function (result) {
if (result.redirect) {
window.location = result.redirect;
return;
$('#ez-modal--delete-workflow').modal({keyboard: true, show: false});
$('.modal-delete').each(function (index, elem) {
$(elem).click(function (e) {
e.preventDefault();
$('#delete-modal--workflow-name').html($(elem).data('name'));
$('#ez-modal--delete-workflow').modal('show');
$('#ez-modal--delete-workflow-confirm').data('target', $(elem).data('path'));
});
});
$('#ez-modal--delete-workflow-confirm').click(function () {
let target = $(this).data('target');
if (target && target !== '') {
$.ajax(target, {
method: 'POST',
complete: function () {
$('#ez-modal--delete-workflow').modal('hide');
window.location.reload();
}
});
}
});
$('#schedule_edit').html(result.form);
}
$('#ez-modal--edit-scheduled').modal({keyboard: true, show: false});
$('.modal-edit').each(function (index, elem) {
$(elem).click(function (e) {
e.preventDefault();
$('#schedule_edit').html('');
$('#ez-modal--edit-scheduled').modal('show');
$.ajax(elem.href, {
success: function (result) {
if (result.redirect) {
if (window.location.href === result.redirect) {
document.location.reload();
}
window.location = result.redirect;
return;
}
$('#schedule_edit').html(result.form);
}
});
});
});
$('#ez-modal--edit-scheduled').on('submit', 'form', function (e) {
e.preventDefault();
url = $(this).attr('action');
data = new FormData(this);
$.ajax({
'type': 'POST',
'url': url,
'data': data,
processData: false,
contentType: false,
success: function (result) {
if (result.redirect) {
window.location = result.redirect;
return;
}
$('#schedule_edit').html(result.form);
}
});
});
$('#ez-modal--new-scheduled form').on('submit', function (e) {
e.preventDefault();
url = $(this).attr('action');
data = new FormData(this);
$.ajax({
'type': 'POST',
'url': url,
'data': data,
processData: false,
contentType: false,
success: function (result) {
if (result.redirect) {
window.location = result.redirect;
return;
}
let obj = $(result.form).find('.modal-body');
$('#ez-modal--new-scheduled .modal-body').html($(obj).html());
}
});
});
});
});
});
$('#ez-modal--edit-scheduled').on('submit', 'form', function (e) {
e.preventDefault();
url = $(this).attr('action');
data = new FormData(this);
$.ajax({
'type': 'POST',
'url': url,
'data': data,
processData: false,
contentType: false,
success: function (result) {
if (result.redirect) {
window.location = result.redirect;
return;
}
$('#schedule_edit').html(result.form);
}
});
});
$('#ez-modal--new-scheduled form').on('submit', function (e) {
e.preventDefault();
url = $(this).attr('action');
data = new FormData(this);
$.ajax({
'type': 'POST',
'url': url,
'data': data,
processData: false,
contentType: false,
success: function (result) {
if (result.redirect) {
window.location = result.redirect;
return;
}
let obj = $(result.form).find('.modal-body');
$('#ez-modal--new-scheduled .modal-body').html($(obj).html());
}
});
});
</script>
})(jQuery);
</script>
{% endif %}

View File

@@ -1,7 +1,9 @@
{% import '@ezdesign/ezdataflow/macros.twig' as macros %}
{% block content %}
<div class="container ez-main-container">
{% if item is not null %}
<h2>{{ 'coderhapsodie.ezdataflow.history.job.title'|trans }}{{ item.id }}</h2>
<table class="table ez-table ez-table--list">
@@ -51,5 +53,8 @@
</tr>
</tbody>
</table>
{% else %}
<p class="ez-table-no-content">{{ 'coderhapsodie.ezdataflow.notfound'|trans }}</p>
{% endif %}
</div>
{% endblock %}

View File

@@ -4,7 +4,8 @@
<div id="loading_{{ id }}" class="text-center d-none">
<svg class="ez-icon ez-icon--extra-large">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#spinner"></use>
<use xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#spinner"></use>
</svg>
</div>
@@ -26,15 +27,18 @@
{% for job in pager.currentPageResults %}
<tr>
<td>{{ job.label }}</td>
<td>{{ job.requestedDate|date('d/m/Y H:i:s') }}</td>
<td>{{ job.requested_date|date('d/m/Y H:i:s') }}</td>
<td>{{ job.count|default('-') }}</td>
<td>{{ job.startTime ? job.startTime|date('d/m/Y H:i:s') : '-' }}</td>
<td>{{ job.endTime ? job.endTime|date('d/m/Y H:i:s') : '-' }}</td>
<td>{{ job.start_time ? job.start_time|date('d/m/Y H:i:s') : '-' }}</td>
<td>{{ job.end_time ? job.end_time|date('d/m/Y H:i:s') : '-' }}</td>
<td>{{ macros.translateStatus(job.status) }}</td>
<td class="ez-table__cell ez-table__cell--has-action-btns text-right">
<a href="{{ path('coderhapsodie.ezdataflow.job.details', {id: job.id}) }}" class="btn btn-icon mx-2 modal-history-details" title="{{ 'coderhapsodie.ezdataflow.history.list.view'|trans }}">
<a href="{{ path('coderhapsodie.ezdataflow.job.details', {id: job.id}) }}"
class="btn btn-icon mx-2 modal-history-details"
title="{{ 'coderhapsodie.ezdataflow.history.list.view'|trans }}">
<svg class="ez-icon ez-icon--small-medium">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#about-info"></use>
<use xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#about-info"></use>
</svg>
</a>
</td>
@@ -54,14 +58,19 @@
</div>
<script>
// Manage ajax pagination
$('#{{ id }}').delegate('.ez-pagination a', 'click', function (e) {
e.preventDefault();
$('#loading_{{ id }}').removeClass('d-none');
$('#{{ id }}')
.html('')
.load(this.href + ' #{{ id }}>*', null, function () {
$('#loading_{{ id }}').addClass('d-none');
(function ($) {
$(document).ready(function ($) {
// Manage ajax pagination
$('#{{ id }}').delegate('.ez-pagination a', 'click', function (e) {
e.preventDefault();
$('#loading_{{ id }}').removeClass('d-none');
$('#{{ id }}')
.html('')
.load(this.href + ' #{{ id }}>*', null, function () {
$('#loading_{{ id }}').addClass('d-none');
});
});
});
});
})(jQuery);
</script>

View File

@@ -1,55 +1,106 @@
{% if items|length %}
<table class="table">
<thead>
<tr>
<th>{{ 'coderhapsodie.ezdataflow.workflow.list.name'|trans }}</th>
<th>{{ 'coderhapsodie.ezdataflow.workflow.list.frequency'|trans }}</th>
<th>{{ 'coderhapsodie.ezdataflow.workflow.list.next_execution'|trans }}</th>
<th>{{ 'coderhapsodie.ezdataflow.workflow.list.enabled'|trans }}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for item in items %}
{% set id = identifier|default('ezdataflow_schedule_results') %}
<div id="loading_{{ id }}" class="text-center d-none">
<svg class="ez-icon ez-icon--extra-large">
<use xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#spinner"></use>
</svg>
</div>
<div id="{{ id }}" class="history-details-aware">
{% if pager.currentPageResults|length %}
<table class="table">
<thead>
<tr>
<td>{{ item.label }}</td>
<td>{{ item.frequency }}</td>
<td>{{ item.next|date('d/m/Y H:i:s') }}</td>
<td>{{ ('coderhapsodie.ezdataflow.' ~ (item.enabled ? 'yes' : 'no'))|trans }}</td>
<td class="ez-table__cell ez-table__cell--has-action-btns text-right">
<a href="{{ path('coderhapsodie.ezdataflow.history.workflow', {id: item.id}) }}" class="btn btn-icon mx-2 modal-history" title="{{ 'coderhapsodie.ezdataflow.workflow.list.history'|trans }}">
<svg class="ez-icon ez-icon--small-medium">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#history"></use>
</svg>
</a>
<a href="{{ path('coderhapsodie.ezdataflow.workflow.edit', {id: item.id}) }}" type="button" class="btn btn-icon mx-2 modal-edit" title="{{ 'coderhapsodie.ezdataflow.workflow.list.edit'|trans }}">
<svg class="ez-icon ez-icon--small-medium ez-icon-edit">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#edit"></use>
</svg>
</a>
{% if item.enabled %}
<a href="{{ path('coderhapsodie.ezdataflow.workflow.disable', {id: item.id}) }}" class="btn btn-icon mx-2" title="{{ 'coderhapsodie.ezdataflow.workflow.list.disable'|trans }}">
<svg class="ez-icon ez-icon--small-medium" style="fill: #f7d000;">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#logout"></use>
</svg>
</a>
{% else %}
<a href="{{ path('coderhapsodie.ezdataflow.workflow.enable', {id: item.id}) }}" class="btn btn-icon mx-2" title="{{ 'coderhapsodie.ezdataflow.workflow.list.enable'|trans }}">
<svg class="ez-icon ez-icon--small-medium" style="fill: #00825c;">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#approved"></use>
</svg>
</a>
{% endif %}
<button type="button" class="btn btn-icon mx-2 ez-btn--content-trash modal-delete" data-name="{{ item.label }}" data-path="{{ path('coderhapsodie.ezdataflow.workflow.delete', {id: item.id}) }}" title="{{ 'coderhapsodie.ezdataflow.workflow.list.delete'|trans }}">
<svg class="ez-icon ez-icon--small-medium ez-icon-trash">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#trash"></use>
</svg>
</button>
</td>
<th>{{ 'coderhapsodie.ezdataflow.workflow.list.name'|trans }}</th>
<th>{{ 'coderhapsodie.ezdataflow.workflow.list.frequency'|trans }}</th>
<th>{{ 'coderhapsodie.ezdataflow.workflow.list.next_execution'|trans }}</th>
<th>{{ 'coderhapsodie.ezdataflow.workflow.list.enabled'|trans }}</th>
<th></th>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="ez-table-no-content">{{ 'coderhapsodie.ezdataflow.workflow.list.empty'|trans }}</p>
{% endif %}
</thead>
<tbody>
{% for item in pager.currentPageResults %}
<tr>
<td>{{ item.label }}</td>
<td>{{ item.frequency }}</td>
<td>{{ item.next|date('d/m/Y H:i:s') }}</td>
<td>{{ ('coderhapsodie.ezdataflow.' ~ (item.enabled ? 'yes' : 'no'))|trans }}</td>
<td class="ez-table__cell ez-table__cell--has-action-btns text-right">
<a href="{{ path('coderhapsodie.ezdataflow.history.workflow', {id: item.id}) }}"
class="btn btn-icon mx-2 modal-history"
title="{{ 'coderhapsodie.ezdataflow.workflow.list.history'|trans }}">
<svg class="ez-icon ez-icon--small-medium">
<use xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#history"></use>
</svg>
</a>
<a href="{{ path('coderhapsodie.ezdataflow.workflow.edit', {id: item.id}) }}" type="button"
class="btn btn-icon mx-2 modal-edit"
title="{{ 'coderhapsodie.ezdataflow.workflow.list.edit'|trans }}">
<svg class="ez-icon ez-icon--small-medium ez-icon-edit">
<use xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#edit"></use>
</svg>
</a>
{% if item.enabled %}
<a href="{{ path('coderhapsodie.ezdataflow.workflow.disable', {id: item.id}) }}"
class="btn btn-icon mx-2"
title="{{ 'coderhapsodie.ezdataflow.workflow.list.disable'|trans }}">
<svg class="ez-icon ez-icon--small-medium" style="fill: #f7d000;">
<use xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#logout"></use>
</svg>
</a>
{% else %}
<a href="{{ path('coderhapsodie.ezdataflow.workflow.enable', {id: item.id}) }}"
class="btn btn-icon mx-2"
title="{{ 'coderhapsodie.ezdataflow.workflow.list.enable'|trans }}">
<svg class="ez-icon ez-icon--small-medium" style="fill: #00825c;">
<use xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#approved"></use>
</svg>
</a>
{% endif %}
<button type="button" class="btn btn-icon mx-2 ez-btn--content-trash modal-delete"
data-name="{{ item.label }}"
data-path="{{ path('coderhapsodie.ezdataflow.workflow.delete', {id: item.id}) }}"
title="{{ 'coderhapsodie.ezdataflow.workflow.list.delete'|trans }}">
<svg class="ez-icon ez-icon--small-medium ez-icon-trash">
<use xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#trash"></use>
</svg>
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="ez-table-no-content">{{ 'coderhapsodie.ezdataflow.workflow.list.empty'|trans }}</p>
{% endif %}
{% if pager.haveToPaginate %}
<div class="ez-pagination justify-content-center align-items-center ez-pagination__spacing">
{{ pagerfanta(pager, 'ez', {routeName: paginate_route, routeParams: paginate_params|default({})}) }}
</div>
{% endif %}
</div>
<script>
(function ($) {
$(document).ready(function ($) {
// Manage ajax pagination
$('#{{ id }}').delegate('.ez-pagination a', 'click', function (e) {
e.preventDefault();
$('#loading_{{ id }}').removeClass('d-none');
$('#{{ id }}')
.html('')
.load(this.href + ' #{{ id }}>*', null, function () {
$('#loading_{{ id }}').addClass('d-none');
});
});
});
})(jQuery);
</script>