4 Commits

Author SHA1 Message Date
Jérémy J
176517f9c6 Updated CHANGELOG 2024-12-17 11:50:52 +01:00
Jérémy J
9507a39720 Added error count columns to job tables 2024-12-17 11:48:27 +01:00
Jérémy J
131045afe8 Fix NotModifiedContentFilter when creating new translation 2024-02-05 15:55:55 +01:00
jeremycr
0a47f788dc Allow Dataflow 4 (#43) 2023-07-27 09:51:45 +02:00
62 changed files with 830 additions and 818 deletions

View File

@@ -12,7 +12,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-version: [7.4, 8.0, 8.1, 8.2]
php-version: [7.3, 7.4, 8.0, 8.1]
runs-on: ubuntu-latest

View File

@@ -1,6 +1,14 @@
# Version 4.0.0
# Version 3.4.0
* Add compatibility with Ibexa 4.0+ and drop compatibility for eZPlatform 2 and Ibexa 3
* Added error count columns to job tables
# Version 3.3.2
* Fix NotModifiedContentFilter when creating new translation
# Version 3.3.1
* Allow Dataflow 4
# Version 3.2.0

View File

@@ -1,7 +1,7 @@
# Code Rhapsodie eZ Dataflow Bundle
EzDataflowBundle is a bundle integrating [Code Rhapsodie Dataflow bundle](https://github.com/code-rhapsodie/dataflow-bundle) into Ibexa 4.0+.
Dataflows can be piloted from an interface integrated into the Ibexa backoffice.
EzDataflowBundle is a bundle integrating [Code Rhapsodie Dataflow bundle](https://github.com/code-rhapsodie/dataflow-bundle) into eZ Platfom 2.0+.
Dataflows can be piloted from an interface integrated in eZ Platform backoffice.
EzDataflow bundle is intended to manage content imports from external data sources.
> Note: before using this bundle, please read the [Code Rhapsodie Dataflow bundle documentation](https://github.com/code-rhapsodie/dataflow-bundle/blob/master/README.md).
@@ -28,7 +28,9 @@ $ composer require code-rhapsodie/ezdataflow-bundle
> Note: The loading order between the Dataflow bundle and Ez Dataflow bundle is important. Dataflow must be loaded first.
Add those two lines in the `config/bundles.php` file:
#### Symfony 4 (new tree)
For Symfony 4, add those two lines in the `config/bundles.php` file:
```php
<?php
@@ -41,10 +43,29 @@ return [
];
```
#### Symfony 3.4 (old tree)
For Symfony 3.4, add those two lines in the `app/AppKernel.php` file:
```php
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = [
// ...
new CodeRhapsodie\DataflowBundle\CodeRhapsodieDataflowBundle(),
new CodeRhapsodie\EzDataflowBundle\CodeRhapsodieEzDataflowBundle(),
// ...
];
}
```
### Step 3: Import bundle routing file
```yaml
# config/routing/ezdataflow.yaml
# app/config/routing.yml or config/routing.yaml
_cr.dataflow:
resource: '@CodeRhapsodieEzDataflowBundle/Resources/config/routing.yaml'
@@ -63,7 +84,7 @@ Please refer to the [Code-Rhapsodie Dataflow Bundle Queue section](https://githu
By default, the `ContentWriter` will publish contents using the `admin` user. If you want to use another user (with sufficient permissions), you can configure it like this:
```yaml
# config/packages/code_rhapsodie_ez_dataflow.yaml
# app/config/config.yml or config/packages/code_rhapsodie_ez_dataflow.yaml
code_rhapsodie_ez_dataflow:
# Integer values are assumed to be user ids, non-integer values are assumed to be user logins
@@ -76,7 +97,7 @@ Before using the admin UI to manage your dataflows, you need to define them. Ple
## Use the ContentWriter
To add or update Ibexa contents, you can use the `CodeRhapsodie\EzDataflowBundle\Writer\ContentWriter` writer.
To add or update eZ Platform contents, you can use the `CodeRhapsodie\EzDataflowBundle\Writer\ContentWriter` writer.
### Step 1: Inject the dependencies and add the writer
@@ -112,7 +133,7 @@ class MyDataflowType extends AbstractDataflowType
### Step 2: Add a step for prepare the content
To process Ibexa contents into your Dataflow, you need to transform the data into `ContentCreateStructure` or `ContentUpdateStructure` objects.
To process eZ Platform content into your Dataflow, you need to transform the data into `ContentCreateStructure` or `ContentUpdateStructure` objects.
in order to respectively create or update contents.
But, in order to determine if the content already exists or not, you first need to look up for it.
@@ -120,19 +141,19 @@ But, in order to determine if the content already exists or not, you first need
One way is to use the remote id to search for the content.
In the following example, the remote id pattern is `article-<id>` with the `<id>` replaced by the data id provided by the reader.
To check if the content exists or not, I use the service `ContentService` provided by Ibexa.
To check if the content exists or not, I use the service `ContentService` provided by eZ Platform.
The step is added as an anonymous function and has 3 types of return values:
* When the step returns `false`, the data is dropped.
* When the step returns a `ContentCreateStructure`, the data will be saved into a new Ibexa content.
* When the step returns a `ContentUpdateStructure`, the existing Ibexa content will be updated by overwriting all defined fields in the data.
* When the step returns a `ContentCreateStructure`, the data will be saved into a new eZ Platform content.
* When the step returns a `ContentUpdateStructure`, the existing eZ Platform content will be updated by overwriting all defined fields in the data.
For the new content, you must provide one or more "parent location id" as the 3rd argument of the `ContentCreateStructure` constructor.
In this example, I have added a new folder to store all articles.
To get the location id of the parent Ibexa content, go to the admin UI and select the future parent content, click on the details tabs, and read the "Location id" like this:
To get the location id of the parent eZ Platform content, go to the admin UI and select the future parent content, click on the details tabs, and read the "Location id" like this:
![parent folder](src/Resources/doc/dest_folder.jpg)
@@ -251,7 +272,7 @@ If you want to add support for a field type, simply create your own comparator.
<?php
use CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\AbstractFieldComparator;
use Ibexa\Core\FieldType\Value;
use eZ\Publish\Core\FieldType\Value;
//[...]
class MyFieldComparator extends AbstractFieldComparator
@@ -277,7 +298,7 @@ class MyFieldComparator extends AbstractFieldComparator
## Access to the eZ Dataflow UI
You can access the eZ Dataflow administration UI from your Ibexa admin back-office.
You can access the eZ Dataflow administration UI from your eZ Platform admin back-office.
![Admin menu](src/Resources/doc/ez_dataflow_admin_menu.jpg)

View File

@@ -1,8 +1,8 @@
{
"name": "code-rhapsodie/ezdataflow-bundle",
"description": "Import/export bundle for Ibexa based on Code-Rhapsodie Dataflow",
"description": "Import/export bundle for eZ Platform based on Code-Rhapsodie Dataflow",
"type": "symfony-bundle",
"keywords": ["dataflow", "import", "export", "data processing", "ibexa"],
"keywords": ["dataflow", "import", "export", "data processing", "ez publish", "ez platform"],
"license": "MIT",
"authors": [
{
@@ -41,15 +41,16 @@
}
},
"require": {
"php": "^7.4||^8.0",
"code-rhapsodie/dataflow-bundle": "^3.0",
"http-interop/http-factory-guzzle": "^1.2",
"ibexa/admin-ui": "^4.0",
"ibexa/core": "^4.0"
"php": "^7.3||^8.0",
"ext-json": "*",
"code-rhapsodie/dataflow-bundle": "^3.0||^4.0",
"ezsystems/ezplatform-admin-ui": "^2.3",
"ezsystems/ezplatform-kernel": "^1.3",
"http-interop/http-factory-guzzle": "^1.2"
},
"require-dev": {
"doctrine/dbal": "^2.0|^3.0",
"phpunit/phpunit": "^7||^8||^9"
"phpunit/phpunit": "^7||^8||^9",
"doctrine/dbal": "^2.0"
},
"minimum-stability": "dev",
"prefer-stable": true,
@@ -61,9 +62,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "4.x-dev",
"dev-v3.x": "3.x-dev",
"dev-v2.x": "2.x-dev",
"dev-master": "2.x-dev",
"dev-v1.x": "1.x-dev"
}
}

View File

@@ -7,7 +7,7 @@ namespace CodeRhapsodie\EzDataflowBundle;
use CodeRhapsodie\EzDataflowBundle\DependencyInjection\CodeRhapsodieEzDataflowExtension;
use CodeRhapsodie\EzDataflowBundle\DependencyInjection\Compiler\FieldComparatorCompilerPass;
use CodeRhapsodie\EzDataflowBundle\Security\PolicyProvider;
use Ibexa\Bundle\Core\DependencyInjection\IbexaCoreExtension;
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\EzPublishCoreExtension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
@@ -26,8 +26,8 @@ class CodeRhapsodieEzDataflowBundle extends Bundle
$container->addCompilerPass(new FieldComparatorCompilerPass());
/** @var IbexaCoreExtension $ibexaExtension */
$ibexaExtension = $container->getExtension('ibexa');
$ibexaExtension->addPolicyProvider(new PolicyProvider());
/** @var EzPublishCoreExtension $eZExtension */
$eZExtension = $container->getExtension('ezpublish');
$eZExtension->addPolicyProvider(new PolicyProvider());
}
}

View File

@@ -8,12 +8,13 @@ use CodeRhapsodie\DataflowBundle\Entity\Job;
use CodeRhapsodie\DataflowBundle\Entity\ScheduledDataflow;
use CodeRhapsodie\EzDataflowBundle\Form\CreateOneshotType;
use CodeRhapsodie\EzDataflowBundle\Form\CreateScheduledType;
use CodeRhapsodie\EzDataflowBundle\Gateway\ExceptionJSONDecoderAdapter;
use CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway;
use CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway;
use Doctrine\DBAL\Query\QueryBuilder;
use Ibexa\Contracts\AdminUi\Controller\Controller;
use Ibexa\Core\MVC\Symfony\Security\Authorization\Attribute;
use Pagerfanta\Doctrine\DBAL\QueryAdapter;
use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute;
use EzSystems\EzPlatformAdminUiBundle\Controller\Controller;
use Pagerfanta\Adapter\DoctrineDbalAdapter;
use Pagerfanta\Pagerfanta;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -24,9 +25,9 @@ use Symfony\Component\Routing\Annotation\Route;
*/
class DashboardController extends Controller
{
/** @var \CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway */
/** @var JobGateway */
private $jobGateway;
/** @var \CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway */
/** @var ScheduledDataflowGateway */
private $scheduledDataflowGateway;
public function __construct(JobGateway $jobGateway, ScheduledDataflowGateway $scheduledDataflowGateway)
@@ -42,7 +43,7 @@ class DashboardController extends Controller
{
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
return $this->render('@ibexadesign/ezdataflow/Dashboard/main.html.twig');
return $this->render('@ezdesign/ezdataflow/Dashboard/main.html.twig');
}
public function repeating(Request $request): Response
@@ -55,7 +56,7 @@ class DashboardController extends Controller
'action' => $this->generateUrl('coderhapsodie.ezdataflow.workflow.create'),
]);
return $this->render('@ibexadesign/ezdataflow/Dashboard/repeating.html.twig', [
return $this->render('@ezdesign/ezdataflow/Dashboard/repeating.html.twig', [
'pager' => $this->getPager($this->scheduledDataflowGateway->getListQueryForAdmin(), $request),
'form' => $form->createView(),
]);
@@ -68,7 +69,7 @@ class DashboardController extends Controller
{
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
return $this->render('@ibexadesign/ezdataflow/Dashboard/repeating.html.twig', [
return $this->render('@ezdesign/ezdataflow/Dashboard/repeating.html.twig', [
'pager' => $this->getPager($this->scheduledDataflowGateway->getListQueryForAdmin(), $request),
]);
}
@@ -83,7 +84,7 @@ class DashboardController extends Controller
'action' => $this->generateUrl('coderhapsodie.ezdataflow.job.create'),
]);
return $this->render('@ibexadesign/ezdataflow/Dashboard/oneshot.html.twig', [
return $this->render('@ezdesign/ezdataflow/Dashboard/oneshot.html.twig', [
'pager' => $this->getPager($this->jobGateway->getOneshotListQueryForAdmin(), $request),
'form' => $form->createView(),
]);
@@ -96,7 +97,7 @@ class DashboardController extends Controller
{
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
return $this->render('@ibexadesign/ezdataflow/Dashboard/oneshot.html.twig', [
return $this->render('@ezdesign/ezdataflow/Dashboard/oneshot.html.twig', [
'pager' => $this->getPager($this->jobGateway->getOneshotListQueryForAdmin(), $request),
]);
}
@@ -106,7 +107,7 @@ class DashboardController extends Controller
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
$filter = (int) $request->query->get('filter', JobGateway::FILTER_NONE);
return $this->render('@ibexadesign/ezdataflow/Dashboard/history.html.twig', [
return $this->render('@ezdesign/ezdataflow/Dashboard/history.html.twig', [
'pager' => $this->getPager($this->jobGateway->getListQueryForAdmin($filter), $request),
'filter' => $filter,
]);
@@ -120,7 +121,7 @@ class DashboardController extends Controller
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
$filter = (int) $request->query->get('filter', JobGateway::FILTER_NONE);
return $this->render('@ibexadesign/ezdataflow/Dashboard/history.html.twig', [
return $this->render('@ezdesign/ezdataflow/Dashboard/history.html.twig', [
'pager' => $this->getPager($this->jobGateway->getListQueryForAdmin($filter), $request),
'filter' => $filter,
]);
@@ -133,7 +134,7 @@ class DashboardController extends Controller
{
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
return $this->render('@ibexadesign/ezdataflow/Dashboard/schedule_history.html.twig', [
return $this->render('@ezdesign/ezdataflow/Dashboard/schedule_history.html.twig', [
'id' => $id,
'pager' => $this->getPager($this->jobGateway->getListQueryForScheduleAdmin($id), $request),
]);
@@ -141,11 +142,15 @@ class DashboardController extends Controller
private function getPager(QueryBuilder $query, Request $request): Pagerfanta
{
$pager = new Pagerfanta(new QueryAdapter($query, function ($queryBuilder) {
return $queryBuilder->select('COUNT(DISTINCT id) AS total_results')
->resetQueryPart('orderBy')
->setMaxResults(1);
}));
$pager = new Pagerfanta(
new ExceptionJSONDecoderAdapter(
new DoctrineDbalAdapter($query, function ($queryBuilder) {
return $queryBuilder->select('COUNT(DISTINCT id) AS total_results')
->resetQueryPart('orderBy')
->setMaxResults(1);
})
)
);
$pager->setMaxPerPage(20);
$pager->setCurrentPage($request->query->get('page', 1));

View File

@@ -7,9 +7,9 @@ namespace CodeRhapsodie\EzDataflowBundle\Controller;
use CodeRhapsodie\DataflowBundle\Entity\Job;
use CodeRhapsodie\EzDataflowBundle\Form\CreateOneshotType;
use CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway;
use Ibexa\Contracts\AdminUi\Controller\Controller;
use Ibexa\Contracts\AdminUi\Notification\NotificationHandlerInterface;
use Ibexa\Core\MVC\Symfony\Security\Authorization\Attribute;
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;
@@ -22,11 +22,11 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
class JobController extends Controller
{
/** @var \CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway */
/** @var JobGateway */
private $jobGateway;
/** @var \Ibexa\Contracts\AdminUi\Notification\NotificationHandlerInterface */
/** @var NotificationHandlerInterface */
private $notificationHandler;
/** @var \Symfony\Contracts\Translation\TranslatorInterface */
/** @var Symfony\Component\Translation\TranslatorInterface|Symfony\Contracts\Translation\TranslatorInterface */
private $translator;
public function __construct(
@@ -46,7 +46,7 @@ class JobController extends Controller
{
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
return $this->render('@ibexadesign/ezdataflow/Item/details.html.twig', [
return $this->render('@ezdesign/ezdataflow/Item/details.html.twig', [
'item' => $this->jobGateway->find($id),
]);
}
@@ -62,7 +62,7 @@ class JobController extends Controller
return preg_replace('~#\d+~', "\n$0", $line);
}, $item->getExceptions());
return $this->render('@ibexadesign/ezdataflow/Item/log.html.twig', [
return $this->render('@ezdesign/ezdataflow/Item/log.html.twig', [
'log' => $log,
]);
}
@@ -78,7 +78,7 @@ class JobController extends Controller
$form = $this->createForm(CreateOneshotType::class, $newOneshot);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** @var \CodeRhapsodie\DataflowBundle\Entity\Job $newOneshot */
/** @var Job $newOneshot */
$newOneshot = $form->getData();
$newOneshot->setStatus(Job::STATUS_PENDING);
@@ -97,7 +97,7 @@ class JobController extends Controller
}
return new JsonResponse([
'form' => $this->renderView('@ibexadesign/ezdataflow/parts/form_modal.html.twig', [
'form' => $this->renderView('@ezdesign/ezdataflow/parts/schedule_form.html.twig', [
'form' => $form->createView(),
'type_action' => 'new',
'mode' => 'oneshot',

View File

@@ -7,10 +7,11 @@ namespace CodeRhapsodie\EzDataflowBundle\Controller;
use CodeRhapsodie\DataflowBundle\Entity\ScheduledDataflow;
use CodeRhapsodie\EzDataflowBundle\Form\CreateScheduledType;
use CodeRhapsodie\EzDataflowBundle\Form\UpdateScheduledType;
use CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway;
use CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway;
use Ibexa\Contracts\AdminUi\Controller\Controller;
use Ibexa\Contracts\AdminUi\Notification\NotificationHandlerInterface;
use Ibexa\Core\MVC\Symfony\Security\Authorization\Attribute;
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;
@@ -22,18 +23,22 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
class ScheduledDataflowController extends Controller
{
/** @var \Ibexa\Contracts\AdminUi\Notification\NotificationHandlerInterface */
/** @var JobGateway */
private $jobGateway;
/** @var NotificationHandlerInterface */
private $notificationHandler;
/** @var \CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway */
/** @var ScheduledDataflowGateway */
private $scheduledDataflowGateway;
/** @var \Symfony\Contracts\Translation\TranslatorInterface */
/** @var TranslatorInterface */
private $translator;
public function __construct(
JobGateway $jobGateway,
NotificationHandlerInterface $notificationHandler,
ScheduledDataflowGateway $scheduledDataflowGateway,
TranslatorInterface $translator
) {
$this->jobGateway = $jobGateway;
$this->notificationHandler = $notificationHandler;
$this->scheduledDataflowGateway = $scheduledDataflowGateway;
$this->translator = $translator;
@@ -51,7 +56,7 @@ class ScheduledDataflowController extends Controller
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** @var \CodeRhapsodie\DataflowBundle\Entity\ScheduledDataflow $newWorkflow */
/** @var ScheduledDataflow $newWorkflow */
$newWorkflow = $form->getData();
try {
$this->scheduledDataflowGateway->save($newWorkflow);
@@ -65,7 +70,7 @@ class ScheduledDataflowController extends Controller
}
return new JsonResponse([
'form' => $this->renderView('@ibexadesign/ezdataflow/parts/form_modal.html.twig', [
'form' => $this->renderView('@ezdesign/ezdataflow/parts/schedule_form.html.twig', [
'form' => $form->createView(),
'type_action' => 'new',
]),
@@ -82,12 +87,14 @@ class ScheduledDataflowController extends Controller
try {
$this->scheduledDataflowGateway->delete($id);
$this->notificationHandler->success($this->translator->trans('coderhapsodie.ezdataflow.workflow.delete.success'));
return new JsonResponse(['code' => 200]);
} catch (\Exception $e) {
$this->notificationHandler->error($this->translator->trans('coderhapsodie.ezdataflow.workflow.delete.error',
['message' => $e->getMessage()]));
}
return $this->redirectToRoute('coderhapsodie.ezdataflow.main');
return new JsonResponse(['code' => $e->getCode()]);
}
}
/**
@@ -101,7 +108,7 @@ class ScheduledDataflowController extends Controller
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** @var \CodeRhapsodie\DataflowBundle\Entity\ScheduledDataflow $editDataflow */
/** @var ScheduledDataflow $editDataflow */
$editDataflow = $form->getData();
try {
@@ -116,7 +123,7 @@ class ScheduledDataflowController extends Controller
}
return new JsonResponse([
'form' => $this->renderView('@ibexadesign/ezdataflow/parts/form_modal.html.twig', [
'form' => $this->renderView('@ezdesign/ezdataflow/parts/schedule_form.html.twig', [
'form' => $form->createView(),
'type_action' => 'edit',
]),
@@ -138,7 +145,7 @@ class ScheduledDataflowController extends Controller
private function changeDataflowStatus(int $id, bool $status)
{
try {
/** @var \CodeRhapsodie\DataflowBundle\Entity\ScheduledDataflow $workflow */
/** @var ScheduledDataflow $workflow */
$workflow = $this->scheduledDataflowGateway->find($id);
$workflow->setEnabled($status);
$this->scheduledDataflowGateway->save($workflow);

View File

@@ -7,23 +7,23 @@ namespace CodeRhapsodie\EzDataflowBundle\Core\Content;
use CodeRhapsodie\EzDataflowBundle\Core\Field\ContentStructFieldFillerInterface;
use CodeRhapsodie\EzDataflowBundle\Matcher\LocationMatcherInterface;
use CodeRhapsodie\EzDataflowBundle\Model\ContentCreateStructure;
use Ibexa\Contracts\Core\Repository\ContentService;
use Ibexa\Contracts\Core\Repository\ContentTypeService;
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct;
use eZ\Publish\API\Repository\ContentService;
use eZ\Publish\API\Repository\ContentTypeService;
use eZ\Publish\API\Repository\Values\Content\Content;
use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct;
class ContentCreator implements ContentCreatorInterface
{
/** @var \Ibexa\Contracts\Core\Repository\ContentService */
/** @var ContentService */
private $contentService;
/** @var \Ibexa\Contracts\Core\Repository\ContentTypeService */
/** @var ContentTypeService */
private $contentTypeService;
/** @var \CodeRhapsodie\EzDataflowBundle\Core\Field\ContentStructFieldFillerInterface */
/** @var ContentStructFieldFillerInterface */
private $filler;
/** @var \CodeRhapsodie\EzDataflowBundle\Matcher\LocationMatcherInterface */
/** @var LocationMatcherInterface */
private $matcher;
public function __construct(ContentService $contentService, ContentTypeService $contentTypeService, ContentStructFieldFillerInterface $filler, LocationMatcherInterface $matcher)
@@ -35,12 +35,12 @@ class ContentCreator implements ContentCreatorInterface
}
/**
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException
* @throws \eZ\Publish\API\Repository\Exceptions\BadStateException
* @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException
* @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
*/
public function createFromStructure(ContentCreateStructure $structure): Content
{
@@ -54,7 +54,7 @@ class ContentCreator implements ContentCreatorInterface
}
/**
* @return \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct[]
* @return LocationCreateStruct[]
*/
private function getLocationCreateStructs(array $locations): array
{

View File

@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Core\Content;
use CodeRhapsodie\EzDataflowBundle\Model\ContentCreateStructure;
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
use eZ\Publish\API\Repository\Values\Content\Content;
interface ContentCreatorInterface
{

View File

@@ -7,19 +7,19 @@ namespace CodeRhapsodie\EzDataflowBundle\Core\Content;
use CodeRhapsodie\EzDataflowBundle\Core\Field\ContentStructFieldFillerInterface;
use CodeRhapsodie\EzDataflowBundle\Exception\NoMatchFoundException;
use CodeRhapsodie\EzDataflowBundle\Model\ContentUpdateStructure;
use Ibexa\Contracts\Core\Repository\ContentService;
use Ibexa\Contracts\Core\Repository\ContentTypeService;
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
use eZ\Publish\API\Repository\ContentService;
use eZ\Publish\API\Repository\ContentTypeService;
use eZ\Publish\API\Repository\Values\Content\Content;
class ContentUpdater implements ContentUpdaterInterface
{
/** @var \Ibexa\Contracts\Core\Repository\ContentService */
/** @var ContentService */
private $contentService;
/** @var \Ibexa\Contracts\Core\Repository\ContentTypeService */
/** @var ContentTypeService */
private $contentTypeService;
/** @var \CodeRhapsodie\EzDataflowBundle\Core\Field\ContentStructFieldFillerInterface */
/** @var ContentStructFieldFillerInterface */
private $filler;
public function __construct(ContentService $contentService, ContentTypeService $contentTypeService, ContentStructFieldFillerInterface $filler)
@@ -30,13 +30,13 @@ class ContentUpdater implements ContentUpdaterInterface
}
/**
* @throws \CodeRhapsodie\EzDataflowBundle\Exception\NoMatchFoundException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException
* @throws NoMatchFoundException
* @throws \eZ\Publish\API\Repository\Exceptions\BadStateException
* @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException
* @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
*/
public function updateFromStructure(ContentUpdateStructure $structure): Content
{

View File

@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Core\Content;
use CodeRhapsodie\EzDataflowBundle\Model\ContentUpdateStructure;
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
use eZ\Publish\API\Repository\Values\Content\Content;
interface ContentUpdaterInterface
{

View File

@@ -6,9 +6,9 @@ namespace CodeRhapsodie\EzDataflowBundle\Core\Field;
use CodeRhapsodie\EzDataflowBundle\Exception\UnknownFieldException;
use CodeRhapsodie\EzDataflowBundle\Exception\UnsupportedFieldTypeException;
use Ibexa\Contracts\Core\FieldType\Value;
use Ibexa\Contracts\Core\Repository\Values\Content\ContentStruct;
use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType;
use eZ\Publish\API\Repository\Values\Content\ContentStruct;
use eZ\Publish\API\Repository\Values\ContentType\ContentType;
use eZ\Publish\Core\FieldType\Value;
class ContentStructFieldFiller implements ContentStructFieldFillerInterface
{
@@ -26,8 +26,8 @@ class ContentStructFieldFiller implements ContentStructFieldFillerInterface
/**
* {@inheritdoc}
*
* @throws \CodeRhapsodie\EzDataflowBundle\Exception\UnknownFieldException
* @throws \CodeRhapsodie\EzDataflowBundle\Exception\UnsupportedFieldTypeException
* @throws UnknownFieldException
* @throws UnsupportedFieldTypeException
*/
public function fillFields(ContentType $contentType, ContentStruct $contentStruct, array $fieldHashes): void
{
@@ -47,7 +47,7 @@ class ContentStructFieldFiller implements ContentStructFieldFillerInterface
/**
* @param mixed $hash
*
* @throws \CodeRhapsodie\EzDataflowBundle\Exception\UnsupportedFieldTypeException
* @throws UnsupportedFieldTypeException
*/
private function createFieldValue(string $fieldTypeIdentifier, $hash): Value
{

View File

@@ -4,8 +4,8 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Core\Field;
use Ibexa\Contracts\Core\Repository\Values\Content\ContentStruct;
use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType;
use eZ\Publish\API\Repository\Values\Content\ContentStruct;
use eZ\Publish\API\Repository\Values\ContentType\ContentType;
interface ContentStructFieldFillerInterface
{

View File

@@ -4,12 +4,12 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Core\Field;
use Ibexa\Contracts\Core\FieldType\Value;
use Ibexa\Contracts\Core\Repository\FieldTypeService;
use eZ\Publish\API\Repository\FieldTypeService;
use eZ\Publish\Core\FieldType\Value;
class DefaultFieldValueCreator implements FieldValueCreatorInterface
{
/** @var \Ibexa\Contracts\Core\Repository\FieldTypeService */
/** @var FieldTypeService */
private $fieldTypeService;
public function __construct(FieldTypeService $fieldTypeService)

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Core\Field;
use Ibexa\Contracts\Core\FieldType\Value;
use eZ\Publish\Core\FieldType\Value;
interface FieldValueCreatorInterface
{

View File

@@ -4,13 +4,13 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Core\FieldComparator;
use Ibexa\Contracts\Core\FieldType\Value;
use Ibexa\Contracts\Core\Repository\FieldTypeService;
use Ibexa\Contracts\Core\Repository\Values\Content\Field;
use eZ\Publish\API\Repository\FieldTypeService;
use eZ\Publish\API\Repository\Values\Content\Field;
use eZ\Publish\Core\FieldType\Value;
abstract class AbstractFieldComparator implements FieldComparatorInterface
{
/** @var \Ibexa\Contracts\Core\Repository\FieldTypeService */
/** @var FieldTypeService */
private $fieldTypeService;
public function __construct(FieldTypeService $fieldTypeService)

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Core\FieldComparator;
use Ibexa\Contracts\Core\Repository\Values\Content\Field;
use eZ\Publish\API\Repository\Values\Content\Field;
class DelegatorFieldComparator implements FieldComparatorInterface
{

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Core\FieldComparator;
use Ibexa\Contracts\Core\Repository\Values\Content\Field;
use eZ\Publish\API\Repository\Values\Content\Field;
interface FieldComparatorInterface
{

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Core\FieldComparator;
use Ibexa\Contracts\Core\FieldType\Value;
use eZ\Publish\Core\FieldType\Value;
class MapLocationFieldComparator extends AbstractFieldComparator
{

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Core\FieldComparator;
use Ibexa\Contracts\Core\FieldType\Value;
use eZ\Publish\Core\FieldType\Value;
class MatrixFieldComparator extends AbstractFieldComparator
{

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Core\FieldComparator;
use Ibexa\Contracts\Core\FieldType\Value;
use eZ\Publish\Core\FieldType\Value;
class NovaSEOMetasFieldComparator extends AbstractFieldComparator
{

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Core\FieldComparator;
use Ibexa\Contracts\Core\FieldType\Value;
use eZ\Publish\Core\FieldType\Value;
class SimpleFieldComparator extends AbstractFieldComparator
{

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Core\FieldComparator;
use Ibexa\Contracts\Core\FieldType\Value;
use eZ\Publish\Core\FieldType\Value;
class UrlFieldComparator extends AbstractFieldComparator
{

View File

@@ -16,7 +16,13 @@ class Configuration implements ConfigurationInterface
{
$treeBuilder = new TreeBuilder('code_rhapsodie_ez_dataflow');
$rootNode = $treeBuilder->getRootNode();
if (method_exists($treeBuilder, 'getRootNode')) {
$rootNode = $treeBuilder->getRootNode();
} else {
// BC for symfony/config < 4.2
$rootNode = $treeBuilder->root('code_rhapsodie_ez_dataflow');
}
$rootNode
->children()
->scalarNode('admin_login_or_id')

View File

@@ -4,8 +4,9 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\EventSubscriber;
use Ibexa\AdminUi\Menu\Event\ConfigureMenuEvent;
use Ibexa\AdminUi\Menu\MainMenuBuilder;
use EzSystems\EzPlatformAdminUi\Menu\Event\ConfigureMenuEvent;
use EzSystems\EzPlatformAdminUi\Menu\MainMenuBuilder;
use Knp\Menu\ItemInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class MenuSubscriber implements EventSubscriberInterface
@@ -20,7 +21,7 @@ class MenuSubscriber implements EventSubscriberInterface
public function onConfigureMenu(ConfigureMenuEvent $event)
{
/** @var \Knp\Menu\ItemInterface $menu */
/** @var ItemInterface $menu */
$menu = $event->getMenu();
if (!isset($menu[MainMenuBuilder::ITEM_ADMIN])) {
return;

View File

@@ -5,14 +5,15 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Factory;
use CodeRhapsodie\EzDataflowBundle\Model\ContentCreateStructure;
use CodeRhapsodie\EzDataflowBundle\Model\ContentStructure;
use CodeRhapsodie\EzDataflowBundle\Model\ContentUpdateStructure;
use Ibexa\Contracts\Core\Repository\ContentService;
use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException;
use eZ\Publish\API\Repository\ContentService;
use eZ\Publish\API\Repository\Exceptions\NotFoundException;
final class ContentStructureFactory implements ContentStructureFactoryInterface
{
/**
* @var \Ibexa\Contracts\Core\Repository\ContentService
* @var ContentService
*/
private $contentService;
@@ -28,10 +29,10 @@ final class ContentStructureFactory implements ContentStructureFactoryInterface
* @param mixed $parentLocations
* @param int $mode One of the constant ContentStructureFactoryInterface::MODE_*
*
* @return false|\CodeRhapsodie\EzDataflowBundle\Model\ContentStructure
* @return false|ContentStructure
*
* @throws \CodeRhapsodie\EzDataflowBundle\Exception\InvalidArgumentTypeException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
*/
public function transform(array $data, string $remoteId, string $language, string $contentType, $parentLocations, int $mode = ContentStructureFactoryInterface::MODE_INSERT_OR_UPDATE)
{
@@ -46,7 +47,7 @@ final class ContentStructureFactory implements ContentStructureFactoryInterface
// The content doesn't exist yet, so it will be created.
}
if (self::MODE_UPDATE_ONLY === $mode) {
if ($mode === static::MODE_UPDATE_ONLY) {
return false;
}

View File

@@ -6,7 +6,8 @@ namespace CodeRhapsodie\EzDataflowBundle\Filter;
use CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\FieldComparatorInterface;
use CodeRhapsodie\EzDataflowBundle\Model\ContentUpdateStructure;
use Ibexa\Contracts\Core\Repository\ContentService;
use eZ\Publish\API\Repository\ContentService;
use eZ\Publish\API\Repository\Exceptions\NotFoundException;
use Psr\Log\LoggerAwareTrait;
/**
@@ -16,10 +17,10 @@ class NotModifiedContentFilter
{
use LoggerAwareTrait;
/** @var \Ibexa\Contracts\Core\Repository\ContentService */
/** @var ContentService */
private $contentService;
/** @var \CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\FieldComparatorInterface */
/** @var FieldComparatorInterface */
private $comparator;
public function __construct(ContentService $contentService, FieldComparatorInterface $comparator)
@@ -37,7 +38,12 @@ class NotModifiedContentFilter
if ($data->getId()) {
$content = $this->contentService->loadContent($data->getId(), [$data->getLanguageCode()]);
} else {
$content = $this->contentService->loadContentByRemoteId($data->getRemoteId(), [$data->getLanguageCode()]);
try {
$content = $this->contentService->loadContentByRemoteId($data->getRemoteId(), [$data->getLanguageCode()]);
} catch (NotFoundException $e) {
// New translation
return $data;
}
}
foreach ($data->getFields() as $identifier => $hash) {

View File

@@ -11,7 +11,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class DataflowTypeChoiceType extends AbstractType
{
/** @var \CodeRhapsodie\DataflowBundle\Registry\DataflowTypeRegistryInterface */
/** @var DataflowTypeRegistryInterface */
private $registry;
public function __construct(DataflowTypeRegistryInterface $registry)

View File

@@ -4,13 +4,13 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Form;
use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException;
use Ibexa\Contracts\Core\Repository\UserPreferenceService;
use eZ\Publish\API\Repository\Exceptions\NotFoundException;
use eZ\Publish\API\Repository\UserPreferenceService;
use Symfony\Component\Form\DataTransformerInterface;
class UserTimezoneAwareDateTimeTransformer implements DataTransformerInterface
{
/** @var \Ibexa\Contracts\Core\Repository\UserPreferenceService */
/** @var UserPreferenceService */
private $userPreferenceService;
public function __construct(UserPreferenceService $userPreferenceService)

View File

@@ -4,14 +4,14 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Form;
use Ibexa\Contracts\Core\Repository\UserPreferenceService;
use eZ\Publish\API\Repository\UserPreferenceService;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\FormBuilderInterface;
class UserTimezoneAwareDateTimeType extends AbstractType
{
/** @var \Ibexa\Contracts\Core\Repository\UserPreferenceService */
/** @var UserPreferenceService */
private $userPreferenceService;
public function __construct(UserPreferenceService $userPreferenceService)

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Gateway;
use Pagerfanta\Adapter\AdapterInterface;
class ExceptionJSONDecoderAdapter implements AdapterInterface
{
/** @var AdapterInterface */
private $adapter;
public function __construct(AdapterInterface $adapter)
{
$this->adapter = $adapter;
}
public function getNbResults()
{
return $this->adapter->getNbResults();
}
public function getSlice($offset, $length)
{
$slice = $this->adapter->getSlice($offset, $length);
array_walk($slice, static function (&$value) {
if (isset($value['exceptions'])) {
$value['exceptions'] = json_decode($value['exceptions'], true, 512, JSON_THROW_ON_ERROR);
}
});
return $slice;
}
}

View File

@@ -13,7 +13,7 @@ final class JobGateway
public const FILTER_NONE = 0;
public const FILTER_NON_EMPTY = 1;
/** @var \CodeRhapsodie\DataflowBundle\Repository\JobRepository */
/** @var JobRepository */
private $jobRepository;
public function __construct(JobRepository $jobRepository)

View File

@@ -10,7 +10,7 @@ use Doctrine\DBAL\Query\QueryBuilder;
final class ScheduledDataflowGateway
{
/** @var \CodeRhapsodie\DataflowBundle\Repository\ScheduledDataflowRepository */
/** @var ScheduledDataflowRepository */
private $scheduledDataflowRepository;
public function __construct(ScheduledDataflowRepository $scheduledDataflowRepository)

View File

@@ -5,13 +5,13 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Matcher;
use CodeRhapsodie\EzDataflowBundle\Exception\NoMatchFoundException;
use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException;
use Ibexa\Contracts\Core\Repository\LocationService;
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
use eZ\Publish\API\Repository\Exceptions\NotFoundException;
use eZ\Publish\API\Repository\LocationService;
use eZ\Publish\API\Repository\Values\Content\Location;
class LocationMatcher implements LocationMatcherInterface
{
/** @var \Ibexa\Contracts\Core\Repository\LocationService */
/** @var LocationService */
private $locationService;
public function __construct(LocationService $locationService)
@@ -22,8 +22,8 @@ class LocationMatcher implements LocationMatcherInterface
/**
* @param mixed $valueToMatch
*
* @throws \CodeRhapsodie\EzDataflowBundle\Exception\NoMatchFoundException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException
* @throws NoMatchFoundException
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
*/
public function matchLocation($valueToMatch): Location
{

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Matcher;
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
use eZ\Publish\API\Repository\Values\Content\Location;
interface LocationMatcherInterface
{

View File

@@ -5,8 +5,8 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Model;
use CodeRhapsodie\EzDataflowBundle\Exception\InvalidArgumentTypeException;
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct;
use eZ\Publish\API\Repository\Values\Content\Location;
use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct;
class ContentCreateStructure extends ContentStructure
{
@@ -28,7 +28,7 @@ class ContentCreateStructure extends ContentStructure
* <li>a LocationCreateStruct object</li>
* </ul>
*
* @throws \CodeRhapsodie\EzDataflowBundle\Exception\InvalidArgumentTypeException
* @throws InvalidArgumentTypeException
*/
public function __construct(string $contentTypeIdentifier, string $languageCode, array $locations, array $fields, ?string $remoteId = null)
{
@@ -50,7 +50,7 @@ class ContentCreateStructure extends ContentStructure
}
/**
* @throws \CodeRhapsodie\EzDataflowBundle\Exception\InvalidArgumentTypeException
* @throws InvalidArgumentTypeException
*/
private function setLocations(array $locations)
{

View File

@@ -5,7 +5,7 @@ services:
_defaults:
public: false
coderhapsodie.dataflow.connection: "@ibexa.persistence.connection"
coderhapsodie.dataflow.connection: "@ezpublish.persistence.connection"
CodeRhapsodie\EzDataflowBundle\Controller\DashboardController:
public: true
@@ -23,7 +23,8 @@ services:
tags:
- { name: controller.service_arguments }
arguments:
$notificationHandler: '@Ibexa\Contracts\AdminUi\Notification\NotificationHandlerInterface'
$jobGateway: '@CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway'
$notificationHandler: '@EzSystems\EzPlatformAdminUi\Notification\NotificationHandlerInterface'
$scheduledDataflowGateway: '@CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway'
$translator: '@translator'
calls:
@@ -36,7 +37,7 @@ services:
- { name: controller.service_arguments }
arguments:
$jobGateway: '@CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway'
$notificationHandler: '@Ibexa\Contracts\AdminUi\Notification\NotificationHandlerInterface'
$notificationHandler: '@EzSystems\EzPlatformAdminUi\Notification\NotificationHandlerInterface'
$translator: '@translator'
calls:
- [ 'setContainer', [ '@service_container' ] ]
@@ -57,23 +58,23 @@ services:
CodeRhapsodie\EzDataflowBundle\UserSwitcher\UserSwitcherInterface: '@CodeRhapsodie\EzDataflowBundle\UserSwitcher\UserSwitcher'
CodeRhapsodie\EzDataflowBundle\UserSwitcher\UserSwitcher:
arguments:
$permissionResolver: '@Ibexa\Contracts\Core\Repository\PermissionResolver'
$userService: '@Ibexa\Contracts\Core\Repository\UserService'
$permissionResolver: '@eZ\Publish\API\Repository\PermissionResolver'
$userService: '@eZ\Publish\API\Repository\UserService'
$adminLoginOrId: '%coderhapsodie.ezdataflow.admin_login_or_id%'
CodeRhapsodie\EzDataflowBundle\Core\Content\ContentCreatorInterface: '@CodeRhapsodie\EzDataflowBundle\Core\Content\ContentCreator'
CodeRhapsodie\EzDataflowBundle\Core\Content\ContentCreator:
arguments:
$contentService: '@Ibexa\Contracts\Core\Repository\ContentService'
$contentTypeService: '@Ibexa\Contracts\Core\Repository\ContentTypeService'
$contentService: '@eZ\Publish\API\Repository\ContentService'
$contentTypeService: '@eZ\Publish\API\Repository\ContentTypeService'
$filler: '@CodeRhapsodie\EzDataflowBundle\Core\Field\ContentStructFieldFillerInterface'
$matcher: '@CodeRhapsodie\EzDataflowBundle\Matcher\LocationMatcherInterface'
CodeRhapsodie\EzDataflowBundle\Core\Content\ContentUpdaterInterface: '@CodeRhapsodie\EzDataflowBundle\Core\Content\ContentUpdater'
CodeRhapsodie\EzDataflowBundle\Core\Content\ContentUpdater:
arguments:
$contentService: '@Ibexa\Contracts\Core\Repository\ContentService'
$contentTypeService: '@Ibexa\Contracts\Core\Repository\ContentTypeService'
$contentService: '@eZ\Publish\API\Repository\ContentService'
$contentTypeService: '@eZ\Publish\API\Repository\ContentTypeService'
$filler: '@CodeRhapsodie\EzDataflowBundle\Core\Field\ContentStructFieldFillerInterface'
CodeRhapsodie\EzDataflowBundle\Core\Field\ContentStructFieldFillerInterface: '@CodeRhapsodie\EzDataflowBundle\Core\Field\ContentStructFieldFiller'
@@ -84,11 +85,11 @@ services:
CodeRhapsodie\EzDataflowBundle\Matcher\LocationMatcherInterface: '@CodeRhapsodie\EzDataflowBundle\Matcher\LocationMatcher'
CodeRhapsodie\EzDataflowBundle\Matcher\LocationMatcher:
arguments:
$locationService: '@Ibexa\Contracts\Core\Repository\LocationService'
$locationService: '@eZ\Publish\API\Repository\LocationService'
CodeRhapsodie\EzDataflowBundle\Core\Field\DefaultFieldValueCreator:
arguments:
$fieldTypeService: '@Ibexa\Contracts\Core\Repository\FieldTypeService'
$fieldTypeService: '@eZ\Publish\API\Repository\FieldTypeService'
tags:
- { name: 'coderhapsodie.ezdataflow.field_value_creator', priority: -1000 }
@@ -99,7 +100,7 @@ services:
CodeRhapsodie\EzDataflowBundle\Factory\ContentStructureFactory:
arguments:
$contentService: '@Ibexa\Contracts\Core\Repository\ContentService'
$contentService: '@eZ\Publish\API\Repository\ContentService'
CodeRhapsodie\EzDataflowBundle\Form\DataflowTypeChoiceType:
arguments:
@@ -117,7 +118,7 @@ services:
CodeRhapsodie\EzDataflowBundle\Form\UserTimezoneAwareDateTimeType:
arguments:
$userPreferenceService: '@Ibexa\Contracts\Core\Repository\UserPreferenceService'
$userPreferenceService: '@eZ\Publish\API\Repository\UserPreferenceService'
tags: ['form.type']
CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway:
@@ -129,33 +130,33 @@ services:
$jobRepository: '@CodeRhapsodie\DataflowBundle\Repository\JobRepository'
CodeRhapsodie\EzDataflowBundle\Tab\RepeatingTab:
parent: Ibexa\Contracts\AdminUi\Tab\AbstractTab
parent: EzSystems\EzPlatformAdminUi\Tab\AbstractTab
public: false
arguments:
$httpKernelRuntime: '@twig.runtime.httpkernel'
tags:
- {name: ibexa.admin_ui.tab, group: coderhapsodie-ezdataflow}
- {name: ezplatform.tab, group: coderhapsodie-ezdataflow}
CodeRhapsodie\EzDataflowBundle\Tab\OneshotTab:
parent: Ibexa\Contracts\AdminUi\Tab\AbstractTab
parent: EzSystems\EzPlatformAdminUi\Tab\AbstractTab
public: false
arguments:
$httpKernelRuntime: '@twig.runtime.httpkernel'
tags:
- {name: ibexa.admin_ui.tab, group: coderhapsodie-ezdataflow}
- {name: ezplatform.tab, group: coderhapsodie-ezdataflow}
CodeRhapsodie\EzDataflowBundle\Tab\HistoryTab:
parent: Ibexa\Contracts\AdminUi\Tab\AbstractTab
parent: EzSystems\EzPlatformAdminUi\Tab\AbstractTab
public: false
arguments:
$httpKernelRuntime: '@twig.runtime.httpkernel'
tags:
- {name: ibexa.admin_ui.tab, group: coderhapsodie-ezdataflow}
- {name: ezplatform.tab, group: coderhapsodie-ezdataflow}
CodeRhapsodie\EzDataflowBundle\Filter\NotModifiedContentFilter:
arguments:
$contentService: '@Ibexa\Contracts\Core\Repository\ContentService'
$contentService: '@eZ\Publish\API\Repository\ContentService'
$comparator: '@CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\FieldComparatorInterface'
CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\FieldComparatorInterface: '@CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\DelegatorFieldComparator'
@@ -163,15 +164,16 @@ services:
CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\AbstractFieldComparator:
arguments:
$fieldTypeService: '@Ibexa\Contracts\Core\Repository\FieldTypeService'
$fieldTypeService: '@eZ\Publish\API\Repository\FieldTypeService'
abstract: true
_cr.admin_tabs.ezdataflow_group:
parent: Ibexa\AdminUi\Component\TabsComponent
parent: EzSystems\EzPlatformAdminUi\Component\TabsComponent
autowire: true
autoconfigure: false
public: false
arguments:
$template: '@@ezdesign/ezdataflow/parts/tab/ezdataflow.html.twig'
$groupIdentifier: 'coderhapsodie-ezdataflow'
tags:
- { name: ibexa.admin_ui.component, group: 'coderhapsodie-ezdataflow' }
- { name: ezplatform.admin_ui.component, group: 'coderhapsodie-ezdataflow' }

View File

@@ -1 +0,0 @@
coderhapsodie.ezdataflow: eZ Dataflow

View File

@@ -1 +0,0 @@
coderhapsodie.ezdataflow: eZ Dataflow

View File

@@ -20,8 +20,8 @@ coderhapsodie.ezdataflow.history.title: History
coderhapsodie.ezdataflow.history.list.title: 'Executions list'
coderhapsodie.ezdataflow.history.list.name: Name
coderhapsodie.ezdataflow.history.list.request: 'Requested on'
coderhapsodie.ezdataflow.history.list.count: 'Items count'
coderhapsodie.ezdataflow.history.list.error_count: 'Errors count'
coderhapsodie.ezdataflow.history.list.count: 'Number of successes'
coderhapsodie.ezdataflow.history.list.errors: 'Number of errors'
coderhapsodie.ezdataflow.history.list.start: 'Started on'
coderhapsodie.ezdataflow.history.list.end: 'Finished on'
coderhapsodie.ezdataflow.history.list.view: 'View details'

View File

@@ -20,16 +20,12 @@ coderhapsodie.ezdataflow.history.title: Historique
coderhapsodie.ezdataflow.history.list.title: 'Liste des exécutions'
coderhapsodie.ezdataflow.history.list.name: Nom
coderhapsodie.ezdataflow.history.list.request: 'Demandé le'
coderhapsodie.ezdataflow.history.list.count: 'Nombre d''objets'
coderhapsodie.ezdataflow.history.list.error_count: 'Nombre d''erreurs'
coderhapsodie.ezdataflow.history.list.count: 'Nombre de succès'
coderhapsodie.ezdataflow.history.list.errors: 'Nombre d''erreurs'
coderhapsodie.ezdataflow.history.list.start: 'Commencé le'
coderhapsodie.ezdataflow.history.list.end: 'Terminé le'
coderhapsodie.ezdataflow.history.list.view: 'Voir le détail'
coderhapsodie.ezdataflow.history.list.status: État
coderhapsodie.ezdataflow.history.filter.label: Filtrer les résultats
coderhapsodie.ezdataflow.history.filter.none: Tous les résultats
coderhapsodie.ezdataflow.history.filter.non_empty_only: Seulement non vides
coderhapsodie.ezdataflow.history.filter.with_error_only: Seulement avec erreurs
coderhapsodie.ezdataflow.job.status.pending: 'En attente'
coderhapsodie.ezdataflow.job.status.running: 'En cours'
coderhapsodie.ezdataflow.job.status.complete: Terminé

View File

@@ -1,60 +1,35 @@
{%- block content -%}
{% set choices = [
{
'value': 0,
'label': 'coderhapsodie.ezdataflow.history.filter.none'|trans,
},
{
'value': 1,
'label': 'coderhapsodie.ezdataflow.history.filter.non_empty_only'|trans,
},
] %}
<h2>{{ 'coderhapsodie.ezdataflow.history.title'|trans }}</h2>
{% set source %}
<select id="ezdataflow_history_filter" class="form-control ibexa-input">
{% for choice in choices %}
<option value="{{ choice.value }}">
{{ choice.label }}
</option>
{% endfor %}
<div class="ez-table-header">
<div class="ez-table-header__headline">{{ 'coderhapsodie.ezdataflow.history.list.title'|trans }}</div>
<div class="ez-table-header__tools">
<label for="ezdataflow_history_filter">{{ 'coderhapsodie.ezdataflow.history.filter.label'|trans }}</label>
<select id="ezdataflow_history_filter" name="filter">
<option value="0" {{ filter == 0 ? 'selected="selected"' }}>{{ 'coderhapsodie.ezdataflow.history.filter.none'|trans }}</option>
<option value="1" {{ filter == 1 ? 'selected="selected"' }}>{{ 'coderhapsodie.ezdataflow.history.filter.non_empty_only'|trans }}</option>
</select>
{% endset %}
</div>
</div>
{% set actions %}
{{ include('@ibexadesign/ui/component/dropdown/dropdown.html.twig', {
source: source,
choices: choices,
value: filter,
}) }}
{% endset %}
{% include '@ezdesign/ezdataflow/parts/tab/job_list.html.twig' with {
identifier: 'ezdataflow_history_results',
paginate_route: 'coderhapsodie.ezdataflow.history',
paginate_params: {'filter': filter}
} %}
{{ include('@ibexadesign/ezdataflow/parts/tab/job_list.html.twig', {
identifier: 'ezdataflow_history_results',
paginate_route: 'coderhapsodie.ezdataflow.history',
paginate_params: {'filter': filter},
headline: 'coderhapsodie.ezdataflow.history.title'|trans,
actions: actions
}) }}
<script>
document.addEventListener('DOMContentLoaded', function () {
<script>
(function ($) {
$(document).ready(function ($) {
// Manage ajax pagination
document.getElementById('ezdataflow_history_filter').addEventListener('change', function (e) {
$('#ezdataflow_history_filter').change(function (e) {
e.preventDefault();
const loading = document.getElementById('loading_ezdataflow_history_results');
const results = document.getElementById('ezdataflow_history_results').querySelector('.ibexa-table');
loading.hidden = false;
results.innerHTML = '';
fetch('{{ path('coderhapsodie.ezdataflow.history') }}?filter=' + this.value)
.then((r) => r.text())
.then((content) => {
const node = document.createElement('div');
node.innerHTML = content;
results.innerHTML = node.querySelector('#ezdataflow_history_results .ibexa-table').innerHTML;
loading.hidden = true;
})
;
$('#loading_ezdataflow_history_results').removeClass('d-none');
$('#ezdataflow_history_results')
.html('')
.load('{{ path('coderhapsodie.ezdataflow.history') }}?filter=' + this.value + ' #ezdataflow_history_results>*', null, function () {
$('#loading_ezdataflow_history_results').addClass('d-none');
});
});
})
</script>
{%- endblock -%}
});
})(jQuery);
</script>

View File

@@ -1,101 +1,80 @@
{% extends "@ibexadesign/ui/layout.html.twig" %}
{% extends ["@ezdesign/layout.html.twig", "@ezdesign/ui/layout.html.twig"] %}
{% block body_class %}ibexa-ezdataflow{% endblock %}
{% block body_class %}ez-has-full-width-footer{% endblock %}
{% block breadcrumbs %}
{% include '@ibexadesign/ui/breadcrumbs.html.twig' with { items: [
{% include ['@ezdesign/parts/breadcrumbs.html.twig', '@ezdesign/ui/breadcrumbs.html.twig'] with { items: [
{ value: 'breadcrumb.admin'|trans(domain='messages')|desc('Admin') },
{ value: 'coderhapsodie.ezdataflow'|trans|desc('EzDataflow') }
]} %}
{% endblock %}
{% block header %}
{% include '@ibexadesign/ui/page_title.html.twig' with {
{% block page_title %}
{% include ['@ezdesign/parts/page_title.html.twig', '@ezdesign/ui/page_title.html.twig'] with {
title: 'coderhapsodie.ezdataflow'|trans|desc('EzDataflow'),
iconName: 'workflow'
} %}
{% endblock %}
{% block content %}
{{ ibexa_render_component_group('coderhapsodie-ezdataflow', {'filter': app.request.query.get('filter', 0)}) }}
{{ ez_render_component_group('coderhapsodie-ezdataflow', {'filter': app.request.query.get('filter', 0)}, '@ezdesign/ezdataflow/parts/tab/ezdataflow.html.twig') }}
{% embed '@ibexadesign/ui/component/modal/modal.html.twig' with {
id: 'modal-history-details',
attr_dialog: {'style': 'max-width: 75vw;'},
title: 'coderhapsodie.ezdataflow.history.job.title'|trans,
} %}
{% block body_content '' %}
{% endembed %}
{% embed '@ibexadesign/ui/component/modal/modal.html.twig' with {
id: 'modal-history-log',
attr_dialog: {'style': 'max-width: 75vw;'},
title: 'coderhapsodie.ezdataflow.workflow.log.title'|trans,
} %}
{% block body_content '' %}
{% endembed %}
{% embed '@ibexadesign/ui/component/modal/modal.html.twig' with {
id: 'modal-history',
attr_dialog: {'style': 'max-width: 75vw;'},
title: 'coderhapsodie.ezdataflow.workflow.history.title'|trans,
} %}
{% block body_content '' %}
{% endembed %}
<div class="modal fade ez-modal show" id="ez-modal--history-details" tabindex="-1" role="dialog" aria-modal="true">
<div class="modal-dialog" role="document" style="max-width: 90%">
<div class="modal-content">
<div class="modal-header">
<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>
</svg>
</button>
</div>
<div class="modal-body" id="modal_content-details"></div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const bindModalLinks = (modalId, linkSelector) => {
const modal = document.getElementById(modalId);
const modalBody = modal.querySelector('.modal-body');
document.addEventListener('click', (e) => {
const link = e.target.closest(linkSelector);
if (link) {
e.preventDefault();
modalBody.innerHTML = '';
fetch(link.href)
.then((r) => r.text())
.then((content) => {
modalBody.innerHTML = content;
})
;
return;
(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 h3').html("{{ 'coderhapsodie.ezdataflow.workflow.history.title'|trans }}");
if ($(this).hasClass('modal-history-log')) {
$('#ez-modal--history-details h3').html("{{ 'coderhapsodie.ezdataflow.workflow.log.title'|trans }}");
}
})
}
bindModalLinks('modal-history-details', '.modal-history-details');
bindModalLinks('modal-history-log', '.modal-history-log');
bindModalLinks('modal-history', '.modal-history');
$('#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');
const labels = document.getElementById('ibexa-tab-label-coderhapsodie-ezdataflow-code-rhapsodie-ezdataflow-repeating')
.closest('ul').querySelectorAll('li.ibexa-tabs__tab');
const tabs = document.getElementById('ibexa-tab-coderhapsodie-ezdataflow-code-rhapsodie-ezdataflow-repeating')
.closest('.tab-content').querySelectorAll('.tab-pane');
if (window.location.hash && window.location.hash === '#oneshot') {
labels.forEach((link) => link.classList.remove('active'));
tabs.forEach((tab) => tab.classList.remove('active'));
document.getElementById('ibexa-tab-label-coderhapsodie-ezdataflow-code-rhapsodie-ezdataflow-oneshot')
.closest('li.ibexa-tabs__tab').classList.add('active');
document.getElementById('ibexa-tab-coderhapsodie-ezdataflow-code-rhapsodie-ezdataflow-oneshot')
.closest('.tab-pane').classList.add('active');
}
if (window.location.hash && window.location.hash === '#history') {
labels.forEach((link) => link.classList.remove('active'));
tabs.forEach((tab) => tab.classList.remove('active'));
document.getElementById('ibexa-tab-label-coderhapsodie-ezdataflow-code-rhapsodie-ezdataflow-history')
.closest('li.ibexa-tabs__tab').classList.add('active');
document.getElementById('ibexa-tab-coderhapsodie-ezdataflow-code-rhapsodie-ezdataflow-history')
.closest('.tab-pane').classList.add('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 %}
{% block stylesheets %}
{{ parent() }}
<style>
.ezdataflow-date .ibexa-dropdown {
min-width: 0;
}
</style>
{% endblock %}

View File

@@ -1,57 +1,60 @@
{%- block content -%}
{% set actions %}
<button
type="button"
class="btn ibexa-btn ibexa-btn--tertiary ibexa-btn--small"
data-bs-toggle="modal"
data-bs-target="#modal-new-oneshot"
>
<svg class="ibexa-icon ibexa-icon--small ibexa-icon--create">
<use xlink:href="{{ ibexa_icon_path('create') }}"></use>
<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">
<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>
</svg>
<span class="ibexa-btn__label">
{{ 'coderhapsodie.ezdataflow.workflow.new.submit'|trans|desc('Create') }}
</span>
</button>
{% endset %}
</div>
</div>
{{ include('@ibexadesign/ezdataflow/parts/tab/job_list.html.twig', {
identifier: 'ezdataflow_oneshot_history',
paginate_route: 'coderhapsodie.ezdataflow.oneshot',
headline: 'coderhapsodie.ezdataflow.oneshot.title'|trans,
actions: actions,
}) }}
{{ include('@ezdesign/ezdataflow/parts/tab/job_list.html.twig', {identifier: 'ezdataflow_oneshot_history', paginate_route: 'coderhapsodie.ezdataflow.oneshot'}) }}
{% if form is defined %}
{{ include('@ibexadesign/ezdataflow/parts/form_modal.html.twig', {
'id': 'modal-new-oneshot',
'form': form,
'mode': 'oneshot'
}) }}
{% endif %}
{% 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'}) }}
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
document.querySelector('#modal-new-oneshot form').addEventListener('submit', function (e) {
e.preventDefault();
url = this.getAttribute('action');
data = new FormData(this);
fetch(url, {
'method': 'post',
'body': data,
})
.then((r) => r.json())
.then((result) => {
if (result.redirect) {
window.location = result.redirect;
return;
(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) {
window.location.reload();
}
window.location = result.redirect;
window.location.reload();
return;
}
let obj = $(result.form).find('.modal-body');
$('#ez-modal--new-oneshot .modal-body').html($(obj).html());
}
const node = document.createElement('div');
node.innerHTML = result.form;
document.querySelector('#modal-new-oneshot .form-fields').innerHTML = node.querySelector('.form-fields').innerHTML;
})
;
});
});
});
});
})(jQuery);
</script>
{%- endblock -%}
{% endif %}

View File

@@ -1,158 +1,185 @@
{%- block content -%}
<h2>{{ 'coderhapsodie.ezdataflow.repeating.title'|trans }}</h2>
{{ include('@ibexadesign/ezdataflow/parts/tab/schedule_list.html.twig', {
identifier: 'ezdataflow_schedule_results',
paginate_route: 'coderhapsodie.ezdataflow.repeating'
}) }}
<div class="ez-table-header">
<div class="ez-table-header__headline">{{ 'coderhapsodie.ezdataflow.workflow.list.title'|trans }}</div>
<div>
<button type="button" class="btn btn-primary btn-modal-launcher" data-toggle="modal"
data-target="#ez-modal--new-scheduled">
<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>
</svg>
</button>
</div>
</div>
{{ include('@ibexadesign/ezdataflow/parts/form_modal.html.twig', {
'id': 'modal-new-scheduled',
'form': form
}) }}
{{ include('@ezdesign/ezdataflow/parts/tab/schedule_list.html.twig', {identifier: 'ezdataflow_schedule_results', paginate_route: 'coderhapsodie.ezdataflow.repeating'}) }}
{% embed '@ibexadesign/ui/component/modal/modal.html.twig' with {
id: 'modal-edit-scheduled',
title: 'coderhapsodie.ezdataflow.workflow.repeating.edit.title'|trans,
} %}
{% block body_content %}
<form action="" method="post">
<div class="form-fields"></div>
<button id="modal-edit-submit" type="submit" hidden />
</form>
{% endblock %}
{% block footer_content %}
<button type="button" class="btn ibexa-btn ibexa-btn--primary ibexa-btn--trigger" data-click="#modal-edit-submit">
{{ 'coderhapsodie.ezdataflow.workflow.edit.submit'|trans }}
</button>
<button class="btn ibexa-btn ibexa-btn--secondary" data-bs-dismiss="modal">
{{ 'coderhapsodie.ezdataflow.workflow.new.cancel'|trans }}
</button>
{% endblock %}
{% endembed %}
{% 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>
{% embed '@ibexadesign/ui/component/modal/modal.html.twig' with {
id: 'modal-delete-confirm',
title: 'coderhapsodie.ezdataflow.workflow.delete'|trans,
} %}
{% block body_content %}
<form action="" method="post">
<div class="item-name"></div>
<button id="modal-delete-submit" type="submit" hidden />
</form>
{% endblock %}
{% block footer_content %}
<button type="button" class="btn ibexa-btn ibexa-btn--primary ibexa-btn--trigger" data-click="#modal-delete-submit">
{{ 'coderhapsodie.ezdataflow.workflow.list.delete'|trans }}
</button>
<button class="btn ibexa-btn ibexa-btn--secondary" data-bs-dismiss="modal">
{{ 'coderhapsodie.ezdataflow.workflow.new.cancel'|trans }}
</button>
{% endblock %}
{% endembed %}
<!-- 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 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>
</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>
<script>
document.addEventListener('DOMContentLoaded', () => {
// $('#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) {
// window.location.reload();
// }
// window.location = result.redirect;
// window.location.reload();
// 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);
// }
// });
// });
(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');
});
const bindFormSubmit = (modalId) => {
document.querySelector('#'+modalId+' form').addEventListener('submit', function (e) {
$('#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) {
if (window.location.href === result.redirect) {
window.location.reload();
}
window.location = result.redirect;
window.location.reload();
return;
}
$('#schedule_edit').html(result.form);
}
});
});
});
$('#ez-modal--edit-scheduled').on('submit', 'form', function (e) {
e.preventDefault();
url = this.getAttribute('action');
url = $(this).attr('action');
data = new FormData(this);
fetch(url, {
'method': 'post',
'body': data,
})
.then((r) => r.json())
.then((result) => {
$.ajax({
'type': 'POST',
'url': url,
'data': data,
processData: false,
contentType: false,
success: function (result) {
if (result.redirect) {
window.location = result.redirect;
return;
}
const node = document.createElement('div');
node.innerHTML = result.form;
document.querySelector('#'+modalId+' .form-fields').innerHTML = node.querySelector('.form-fields').innerHTML;
})
;
$('#schedule_edit').html(result.form);
}
});
});
};
bindFormSubmit('modal-new-scheduled');
bindFormSubmit('modal-edit-scheduled');
const editModal = document.getElementById('modal-edit-scheduled')
document.addEventListener('click', (e) => {
const link = e.target.closest('.modal-edit');
if (link) {
$('#ez-modal--new-scheduled form').on('submit', function (e) {
e.preventDefault();
fetch(link.href, {
'method': 'post'
})
.then((r) => r.json())
.then((result) => {
const node = document.createElement('div');
node.innerHTML = result.form;
editModal.querySelector('form').action = link.href;
editModal.querySelector('.form-fields').innerHTML = node.querySelector('.form-fields').innerHTML;
})
;
return;
}
})
const deleteModal = document.getElementById('modal-delete-confirm')
document.addEventListener('click', (e) => {
const link = e.target.closest('.modal-delete');
if (link) {
e.preventDefault();
deleteModal.querySelector('.item-name').textContent = link.dataset.name;
deleteModal.querySelector('form').action = link.href;
return;
}
})
});
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());
}
});
});
});
})(jQuery);
</script>
{%- endblock -%}
{% endif %}

View File

@@ -1,5 +1,7 @@
{% include '@ibexadesign/ezdataflow/parts/tab/job_list.html.twig' with {
identifier: 'ezdataflow_schedule_history_results',
paginate_route: 'coderhapsodie.ezdataflow.history.workflow',
paginate_params: {id: id}
} %}
<h2>{{ 'coderhapsodie.ezdataflow.history.title'|trans }}</h2>
<div class="ez-table-header">
<div class="ez-table-header__headline">{{ 'coderhapsodie.ezdataflow.history.list.title'|trans }}</div>
</div>
{% include '@ezdesign/ezdataflow/parts/tab/job_list.html.twig' with {identifier: 'ezdataflow_schedule_history_results', paginate_route: 'coderhapsodie.ezdataflow.history.workflow', paginate_params: {id: id}} %}

View File

@@ -1,4 +1,4 @@
{% import '@ibexadesign/ezdataflow/macros.twig' as macros %}
{% import '@ezdesign/ezdataflow/macros.twig' as macros %}
{% block content %}
<div class="container ez-main-container history-details-aware">
@@ -20,7 +20,7 @@
</tr>
<tr>
<td>{{ 'coderhapsodie.ezdataflow.history.details.request'|trans }}</td>
<td>{{ date(item.requestedDate)|ibexa_short_datetime }}</td>
<td>{{ date(item.requestedDate)|ez_short_datetime }}</td>
</tr>
<tr>
<td>{{ 'coderhapsodie.ezdataflow.history.details.status'|trans }}</td>
@@ -28,11 +28,11 @@
</tr>
<tr>
<td>{{ 'coderhapsodie.ezdataflow.history.details.start'|trans }}</td>
<td>{{ item.startTime ? date(item.startTime)|ibexa_short_datetime : '—' }}</td>
<td>{{ item.startTime ? date(item.startTime)|ez_short_datetime : '—' }}</td>
</tr>
<tr>
<td>{{ 'coderhapsodie.ezdataflow.history.details.end'|trans }}</td>
<td>{{ item.endTime ? date(item.endTime)|ibexa_short_datetime : '—' }}</td>
<td>{{ item.endTime ? date(item.endTime)|ez_short_datetime : '—' }}</td>
</tr>
<tr>
<td>{{ 'coderhapsodie.ezdataflow.history.details.count'|trans }}</td>

View File

@@ -1,10 +0,0 @@
{% extends '@ibexadesign/ui/form_fields.html.twig' %}
{%- block checkbox_widget -%}
{{ block('toggle_widget') }}
{%- endblock -%}
{% block datetime_widget -%}
{% set attr = attr|merge({class: (attr.class|default('') ~ ' ezdataflow-date')|trim}) %}
{{ parent() }}
{%- endblock datetime_widget %}

View File

@@ -1,23 +0,0 @@
{% form_theme form '@ibexadesign/ezdataflow/form_theme.html.twig' %}
{% embed '@ibexadesign/ui/component/modal/modal.html.twig' with {
id: id ?? '',
title: ('coderhapsodie.ezdataflow.workflow.'~(mode|default('repeating'))~'.'~(type_action|default('new'))~'.title')|trans,
} %}
{% block body_content %}
{{ form_start(form) }}
<div class="form-fields">
{{ form_widget(form) }}
</div>
<button type="submit" hidden id="{{ id ~ '-submit' }}"></button>
{{ form_end(form) }}
{% endblock %}
{% block footer_content %}
<button class="btn ibexa-btn ibexa-btn--primary ibexa-btn--trigger" data-click="#{{ id ~ '-submit' }}">
{{ ('coderhapsodie.ezdataflow.workflow.'~(type_action|default('new'))~'.submit')|trans }}
</button>
<button type="button" class="btn ibexa-btn ibexa-btn--secondary" data-bs-dismiss="modal">
{{ 'coderhapsodie.ezdataflow.workflow.new.cancel'|trans }}
</button>
{% endblock %}
{% endembed %}

View File

@@ -0,0 +1,24 @@
{% form_theme form 'bootstrap_3_layout.html.twig' %}
{{ form_start(form) }}
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">{{ ('coderhapsodie.ezdataflow.workflow.'~(mode|default('repeating'))~'.'~(type_action|default('new'))~'.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">
{{ form_widget(form) }}
</div>
<div class="modal-footer justify-content-center">
<button type="button" class="btn btn-dark"
data-dismiss="modal">{{ 'coderhapsodie.ezdataflow.workflow.new.cancel'|trans }}</button>
<button type="submit"
class="btn btn-primary font-weight-bold">{{ ('coderhapsodie.ezdataflow.workflow.'~(type_action|default('new'))~'.submit')|trans }}</button>
</div>
</div>
{{ form_end(form) }}

View File

@@ -0,0 +1,27 @@
<div class="row ez-header pt-3">
<div class="container">
<ul class="nav nav-tabs ez-tabs nav-tabs-{{ group }} px-4" role="tablist" id="ez-tab-list-{{ group }}">
{% for tab in tabs %}
{% set id = group ~ '-' ~ tab.identifier %}
{% set active = loop.first %}
<li class="nav-item">
<a class="nav-link{% if active %} active{% endif %}" id="ez-tab-label-{{ group }}"
data-toggle="tab" href="#ez-tab-{{ id }}" role="tab" aria-controls="ez-tab-{{ id }}"
aria-expanded="{{ active }}">{{ tab.name }}</a>
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="tab-content container mt-4 px-5" id="ez-tab-list-content-{{ group }}">
{% for tab in tabs %}
{% set id = group ~ '-' ~ tab.identifier %}
{% set active = loop.first %}
<div class="tab-pane{% if active %} active{% endif %}" id="ez-tab-{{ id }}" role="tabpanel"
aria-labelledby="ez-tab-label-{{ group }}">
{{ tab.view|raw }}
</div>
{% endfor %}
</div>

View File

@@ -1,117 +1,85 @@
{% set id = identifier|default('ezdataflow_history_results') %}
{% import '@ibexadesign/ezdataflow/macros.twig' as macros %}
{% import '@ezdesign/ezdataflow/macros.twig' as macros %}
<div id="loading_{{ id }}" class="text-center" hidden>
<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="{{ ibexa_icon_path('spinner') }}"></use>
xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#spinner"></use>
</svg>
</div>
<div id="{{ id }}" class="history-details-aware">
{% set body_rows = [] %}
{% for job in pager.currentPageResults %}
{% set body_row_cols = [] %}
{% set body_row_cols = body_row_cols|merge([
{content: job.label},
{content: date(job.requested_date)|ibexa_short_datetime},
{content: job.count|default('—')},
{content: job.start_time ? date(job.start_time)|ibexa_short_datetime : '—'},
{content: job.end_time ? date(job.end_time)|ibexa_short_datetime : '—'},
{content: macros.translateStatus(job.status)},
]) %}
{% set col_raw %}
<a href="{{ path('coderhapsodie.ezdataflow.job.details', {id: job.id}) }}"
class="btn ibexa-btn ibexa-btn--ghost ibexa-btn--no-text modal-history-details"
title="{{ 'coderhapsodie.ezdataflow.history.list.view'|trans }}"
data-bs-toggle="modal"
data-bs-target="#modal-history-details"
>
<svg class="ibexa-icon ibexa-icon--small ibexa-icon--about-info">
<use xlink:href="{{ ibexa_icon_path('about-info') }}"></use>
</svg>
</a>
<a href="{{ path('coderhapsodie.ezdataflow.job.log', {id: job.id}) }}"
class="btn ibexa-btn ibexa-btn--ghost ibexa-btn--no-text modal-history-log"
data-bs-toggle="modal"
data-bs-target="#modal-history-log"
title="{{ 'coderhapsodie.ezdataflow.history.details.log'|trans }}"
>
<svg class="ibexa-icon ibexa-icon--small ibexa-icon--article">
<use xlink:href="{{ ibexa_icon_path('article') }}"></use>
</svg>
</a>
{% endset %}
{% set body_row_cols = body_row_cols|merge([{
has_action_btns: true,
content: col_raw,
raw: true,
}]) %}
{% set body_rows = body_rows|merge([{ cols: body_row_cols }]) %}
{% endfor %}
{% embed '@ibexadesign/ui/component/table/table.html.twig' with {
headline: headline ?? null,
head_cols: [
{ content: 'coderhapsodie.ezdataflow.history.list.name'|trans },
{ content: 'coderhapsodie.ezdataflow.history.list.request'|trans },
{ content: 'coderhapsodie.ezdataflow.history.list.count'|trans },
{ content: 'coderhapsodie.ezdataflow.history.list.start'|trans },
{ content: 'coderhapsodie.ezdataflow.history.list.end'|trans },
{ content: 'coderhapsodie.ezdataflow.history.list.status'|trans },
{ },
],
body_rows,
empty_table_info_text: 'coderhapsodie.ezdataflow.history.list.empty'|trans,
} %}
{% block header %}
{% embed '@ibexadesign/ui/component/table/table_header.html.twig' %}
{% block actions %}
{{ actions ?? '' }}
{% endblock %}
{% endembed %}
{% endblock %}
{% endembed %}
{% if pager.currentPageResults|length %}
<table class="table">
<thead>
<tr>
<th>{{ 'coderhapsodie.ezdataflow.history.list.name'|trans }}</th>
<th>{{ 'coderhapsodie.ezdataflow.history.list.request'|trans }}</th>
<th>{{ 'coderhapsodie.ezdataflow.history.list.count'|trans }}</th>
<th>{{ 'coderhapsodie.ezdataflow.history.list.errors'|trans }}</th>
<th>{{ 'coderhapsodie.ezdataflow.history.list.start'|trans }}</th>
<th>{{ 'coderhapsodie.ezdataflow.history.list.end'|trans }}</th>
<th>{{ 'coderhapsodie.ezdataflow.history.list.status'|trans }}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for job in pager.currentPageResults %}
<tr>
<td>{{ job.label }}</td>
<td>{{ date(job.requested_date)|ez_short_datetime }}</td>
<td>{{ job.count|default('—') }}</td>
<td>{{ job.exceptions|length }}</td>
<td>{{ job.start_time ? date(job.start_time)|ez_short_datetime : '—' }}</td>
<td>{{ job.end_time ? date(job.end_time)|ez_short_datetime : '—' }}</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 }}">
<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>
</svg>
</a>
<a href="{{ path('coderhapsodie.ezdataflow.job.log', {id: job.id}) }}"
class="btn btn-icon mx-2 modal-history-details modal-history-log"
title="{{ 'coderhapsodie.ezdataflow.history.details.log'|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#article"></use>
</svg>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="ez-table-no-content">{{ 'coderhapsodie.ezdataflow.history.list.empty'|trans }}</p>
{% endif %}
{% if pager.haveToPaginate %}
{% include '@ibexadesign/ui/pagination.html.twig' with {
'pager': pager,
'paginaton_params': {
'routeName': paginate_route,
'routeParams': paginate_params|default({})
}
} %}
<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>
document.addEventListener('DOMContentLoaded', () => {
// Manage ajax pagination
const display = document.getElementById('{{ id }}');
display.addEventListener('click', (e) => {
const link = e.target.closest('.ibexa-pagination a');
if (!link) {
return;
}
const loader = document.getElementById('loading_{{ id }}');
e.preventDefault();
loader.hidden = false;
display.innerHTML = '';
fetch(link.href)
.then((r) => r.text())
.then((content) => {
const node = document.createElement('div');
node.innerHTML = content;
display.innerHTML = node.querySelector('#{{ id }}').innerHTML;
loader.hidden = true;
})
;
(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,152 +1,106 @@
{% set id = identifier|default('ezdataflow_schedule_results') %}
<div id="loading_{{ id }}" class="text-center" hidden>
<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="{{ ibexa_icon_path('spinner') }}"></use>
xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#spinner"></use>
</svg>
</div>
<div id="{{ id }}" class="history-details-aware">
{% set body_rows = [] %}
{% for item in pager.currentPageResults %}
{% set body_row_cols = [] %}
{% set body_row_cols = body_row_cols|merge([
{content: item.label},
{content: item.frequency},
{content: date(item.next)|ibexa_short_datetime},
{content: ('coderhapsodie.ezdataflow.' ~ (item.enabled ? 'yes' : 'no'))|trans},
]) %}
{% set col_raw %}
<a href="{{ path('coderhapsodie.ezdataflow.history.workflow', {id: item.id}) }}"
class="btn ibexa-btn ibexa-btn--ghost ibexa-btn--no-text modal-history"
title="{{ 'coderhapsodie.ezdataflow.workflow.list.history'|trans }}"
data-bs-toggle="modal"
data-bs-target="#modal-history"
>
<svg class="ibexa-icon ibexa-icon--small ibexa-icon--history">
<use xlink:href="{{ ibexa_icon_path('history') }}"></use>
</svg>
</a>
<a href="{{ path('coderhapsodie.ezdataflow.workflow.edit', {id: item.id}) }}"
class="btn ibexa-btn ibexa-btn--ghost ibexa-btn--no-text modal-edit"
title="{{ 'coderhapsodie.ezdataflow.workflow.list.edit'|trans }}"
data-bs-toggle="modal"
data-bs-target="#modal-edit-scheduled"
>
<svg class="ibexa-icon ibexa-icon--small ibexa-icon--edit">
<use xlink:href="{{ ibexa_icon_path('edit') }}"></use>
</svg>
</a>
{% if item.enabled %}
<a href="{{ path('coderhapsodie.ezdataflow.workflow.disable', {id: item.id}) }}"
class="btn ibexa-btn ibexa-btn--ghost ibexa-btn--no-text"
title="{{ 'coderhapsodie.ezdataflow.workflow.list.disable'|trans }}"
>
<svg class="ibexa-icon ibexa-icon--small ibexa-icon--logout" style="fill: #f7d000;">
<use xlink:href="{{ ibexa_icon_path('logout') }}"></use>
</svg>
</a>
{% else %}
<a href="{{ path('coderhapsodie.ezdataflow.workflow.enable', {id: item.id}) }}"
class="btn ibexa-btn ibexa-btn--ghost ibexa-btn--no-text"
title="{{ 'coderhapsodie.ezdataflow.workflow.list.enable'|trans }}"
>
<svg class="ibexa-icon ibexa-icon--small ibexa-icon--logout" style="fill: #00825c;">
<use xlink:href="{{ ibexa_icon_path('approved') }}"></use>
</svg>
</a>
{% endif %}
<a href="{{ path('coderhapsodie.ezdataflow.workflow.delete', {id: item.id}) }}"
class="btn ibexa-btn ibexa-btn--ghost ibexa-btn--no-text modal-delete"
title="{{ 'coderhapsodie.ezdataflow.workflow.list.delete'|trans }}"
data-name="{{ item.label }}"
data-bs-toggle="modal"
data-bs-target="#modal-delete-confirm"
>
<svg class="ibexa-icon ibexa-icon--small ibexa-icon--trash">
<use xlink:href="{{ ibexa_icon_path('trash') }}"></use>
</svg>
</a>
{% endset %}
{% set body_row_cols = body_row_cols|merge([{
has_action_btns: true,
content: col_raw,
raw: true,
}]) %}
{% set body_rows = body_rows|merge([{ cols: body_row_cols }]) %}
{% endfor %}
{% embed '@ibexadesign/ui/component/table/table.html.twig' with {
headline: 'coderhapsodie.ezdataflow.workflow.list.title'|trans,
head_cols: [
{ content: 'coderhapsodie.ezdataflow.workflow.list.name'|trans },
{ content: 'coderhapsodie.ezdataflow.workflow.list.frequency'|trans },
{ content: 'coderhapsodie.ezdataflow.workflow.list.next_execution'|trans },
{ content: 'coderhapsodie.ezdataflow.workflow.list.enabled'|trans },
{ },
],
body_rows,
empty_table_info_text: 'coderhapsodie.ezdataflow.workflow.list.empty'|trans,
} %}
{% block header %}
{% embed '@ibexadesign/ui/component/table/table_header.html.twig' %}
{% block actions %}
<button
type="button"
class="btn ibexa-btn ibexa-btn--tertiary ibexa-btn--small"
data-bs-toggle="modal"
data-bs-target="#modal-new-scheduled"
>
<svg class="ibexa-icon ibexa-icon--small ibexa-icon--create">
<use xlink:href="{{ ibexa_icon_path('create') }}"></use>
</svg>
<span class="ibexa-btn__label">
{{ 'coderhapsodie.ezdataflow.workflow.new.submit'|trans|desc('Create') }}
</span>
</button>
{% endblock %}
{% endembed %}
{% endblock %}
{% endembed %}
{% if pager.currentPageResults|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 pager.currentPageResults %}
<tr>
<td>{{ item.label }}</td>
<td>{{ item.frequency }}</td>
<td>{{ date(item.next)|ez_short_datetime }}</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 %}
{% include '@ibexadesign/ui/pagination.html.twig' with {
'pager': pager,
'paginaton_params': {
'routeName': paginate_route,
'routeParams': paginate_params|default({})
}
} %}
<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>
document.addEventListener('DOMContentLoaded', () => {
// Manage ajax pagination
const display = document.getElementById('{{ id }}');
display.addEventListener('click', (e) => {
const link = e.target.closest('.ibexa-pagination a');
if (!link) {
return;
}
const loader = document.getElementById('loading_{{ id }}');
e.preventDefault();
loader.hidden = false;
display.innerHTML = '';
fetch(link.href)
.then((r) => r.text())
.then((content) => {
const node = document.createElement('div');
node.innerHTML = content;
display.innerHTML = node.querySelector('#{{ id }}').innerHTML;
loader.hidden = true;
})
;
(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

@@ -4,8 +4,8 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Security;
use Ibexa\Bundle\Core\DependencyInjection\Security\PolicyProvider\PolicyProviderInterface;
use Ibexa\Bundle\Core\DependencyInjection\Security\PolicyProvider\YamlPolicyProvider;
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider\PolicyProviderInterface;
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider\YamlPolicyProvider;
class PolicyProvider extends YamlPolicyProvider implements PolicyProviderInterface
{

View File

@@ -5,8 +5,8 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Tab;
use CodeRhapsodie\EzDataflowBundle\Controller\DashboardController;
use Ibexa\Contracts\AdminUi\Tab\AbstractControllerBasedTab;
use Ibexa\Contracts\AdminUi\Tab\OrderedTabInterface;
use EzSystems\EzPlatformAdminUi\Tab\AbstractControllerBasedTab;
use EzSystems\EzPlatformAdminUi\Tab\OrderedTabInterface;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
class HistoryTab extends AbstractControllerBasedTab implements OrderedTabInterface

View File

@@ -5,8 +5,8 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Tab;
use CodeRhapsodie\EzDataflowBundle\Controller\DashboardController;
use Ibexa\Contracts\AdminUi\Tab\AbstractControllerBasedTab;
use Ibexa\Contracts\AdminUi\Tab\OrderedTabInterface;
use EzSystems\EzPlatformAdminUi\Tab\AbstractControllerBasedTab;
use EzSystems\EzPlatformAdminUi\Tab\OrderedTabInterface;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
class OneshotTab extends AbstractControllerBasedTab implements OrderedTabInterface

View File

@@ -5,8 +5,8 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\Tab;
use CodeRhapsodie\EzDataflowBundle\Controller\DashboardController;
use Ibexa\Contracts\AdminUi\Tab\AbstractControllerBasedTab;
use Ibexa\Contracts\AdminUi\Tab\OrderedTabInterface;
use EzSystems\EzPlatformAdminUi\Tab\AbstractControllerBasedTab;
use EzSystems\EzPlatformAdminUi\Tab\OrderedTabInterface;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
class RepeatingTab extends AbstractControllerBasedTab implements OrderedTabInterface

View File

@@ -4,21 +4,22 @@ declare(strict_types=1);
namespace CodeRhapsodie\EzDataflowBundle\UserSwitcher;
use Ibexa\Contracts\Core\Repository\PermissionResolver;
use Ibexa\Contracts\Core\Repository\UserService;
use eZ\Publish\API\Repository\PermissionResolver;
use eZ\Publish\API\Repository\UserService;
use eZ\Publish\API\Repository\Values\User\UserReference;
class UserSwitcher implements UserSwitcherInterface
{
/** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */
/** @var PermissionResolver */
private $permissionResolver;
/** @var \Ibexa\Contracts\Core\Repository\UserService */
/** @var UserService */
private $userService;
/** @var string|int */
private $adminLoginOrId;
/** @var \Ibexa\Contracts\Core\Repository\Values\User\UserReference[] */
/** @var UserReference[] */
private $userStack;
public function __construct(PermissionResolver $permissionResolver, UserService $userService, $adminLoginOrId)

View File

@@ -16,10 +16,10 @@ class ContentWriter extends RepositoryWriter implements DelegateWriterInterface
{
use LoggerAwareTrait;
/** @var \CodeRhapsodie\EzDataflowBundle\Core\Content\ContentCreatorInterface */
/** @var ContentCreatorInterface */
private $creator;
/** @var \CodeRhapsodie\EzDataflowBundle\Core\Content\ContentUpdaterInterface */
/** @var ContentUpdaterInterface */
private $updater;
public function __construct(ContentCreatorInterface $creator, ContentUpdaterInterface $updater)
@@ -29,7 +29,7 @@ class ContentWriter extends RepositoryWriter implements DelegateWriterInterface
}
/**
* @param \CodeRhapsodie\EzDataflowBundle\Model\ContentStructure $item
* @param ContentStructure $item
*/
public function write($item)
{
@@ -44,14 +44,14 @@ class ContentWriter extends RepositoryWriter implements DelegateWriterInterface
'content_type' => $item->getContentTypeIdentifier(),
'content_location' => $item->getLocations(),
]);
$this->creator->createFromStructure($item);
return;
return $this->creator->createFromStructure($item);
}
if ($item instanceof ContentUpdateStructure) {
$this->log('info', 'Update content', ['id' => $item->getId(), 'remote_id' => $item->getRemoteId()]);
$this->updater->updateFromStructure($item);
return $this->updater->updateFromStructure($item);
}
}

View File

@@ -4,12 +4,12 @@ namespace CodeRhapsodie\EzDataflowBundle\Tests\Core\FieldComparator;
use CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\DelegatorFieldComparator;
use CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\FieldComparatorInterface;
use Ibexa\Contracts\Core\Repository\Values\Content\Field;
use eZ\Publish\API\Repository\Values\Content\Field;
use PHPUnit\Framework\TestCase;
class DelegatorFieldComparatorTest extends TestCase
{
/** @var \CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\DelegatorFieldComparator */
/** @var DelegatorFieldComparator */
private $delegatorFieldComparator;
protected function setUp(): void

View File

@@ -7,21 +7,21 @@ namespace CodeRhapsodie\EzDataflowBundle\Tests\Filter;
use CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\FieldComparatorInterface;
use CodeRhapsodie\EzDataflowBundle\Filter\NotModifiedContentFilter;
use CodeRhapsodie\EzDataflowBundle\Model\ContentUpdateStructure;
use Ibexa\Contracts\Core\Repository\ContentService;
use Ibexa\Contracts\Core\Repository\Values\Content\Field;
use Ibexa\Core\Repository\Values\Content\Content;
use eZ\Publish\API\Repository\ContentService;
use eZ\Publish\API\Repository\Values\Content\Field;
use eZ\Publish\Core\Repository\Values\Content\Content;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class NotModifiedContentFilterTest extends TestCase
{
/** @var \Ibexa\Contracts\Core\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject */
/** @var ContentService|MockObject */
private $contentServiceMock;
/** @var \CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\FieldComparatorInterface|\PHPUnit\Framework\MockObject\MockObject */
/** @var FieldComparatorInterface|MockObject */
private $comparatorMock;
/** @var \CodeRhapsodie\EzDataflowBundle\Filter\NotModifiedContentFilter */
/** @var NotModifiedContentFilter */
private $notModifiedContentFilter;
protected function setUp(): void