mirror of
https://github.com/code-rhapsodie/ezdataflow-bundle.git
synced 2026-03-24 06:32:06 +01:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0367d76e1 | ||
|
|
2274bbf5e5 | ||
|
|
4a0070dc04 | ||
|
|
7ca493e3e8 | ||
|
|
473968791b | ||
|
|
dd655543ce | ||
|
|
d49d23b4fe | ||
|
|
7ab5b5cbd1 | ||
|
|
df1fcd5132 | ||
|
|
35d3788a59 | ||
|
|
c36fef9220 | ||
|
|
b4186ed3ce | ||
|
|
657c269eb3 | ||
|
|
6f1a719314 | ||
|
|
52f607616d | ||
|
|
0c13878f79 | ||
|
|
8b29a92aab | ||
|
|
c824c0ecbb | ||
|
|
760aea3948 | ||
|
|
e1213d4a74 | ||
|
|
5f3775cb6b | ||
|
|
ec42d6f765 | ||
|
|
deab8e8073 | ||
|
|
ca37b2438d | ||
|
|
d166f3aea8 | ||
|
|
9ed5fd3c2b | ||
|
|
f98fecdca9 |
55
.github/workflows/php.yml
vendored
Normal file
55
.github/workflows/php.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: PHP Composer
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-version: [7.4, 8.0, 8.1, 8.2]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP ${{ matrix.php-version }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
extensions: xdebug
|
||||
- name: PHP Version
|
||||
run: php -v
|
||||
|
||||
- name: PHP Lint
|
||||
run: find . -type f -name '*.php' -exec php -l {} \; | (! grep -v "No syntax errors detected" )
|
||||
|
||||
- name: Validate composer.json and composer.lock
|
||||
run: composer validate --strict
|
||||
|
||||
- name: Cache Composer packages
|
||||
id: composer-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: vendor
|
||||
key: ${{ runner.os }}-php${{ matrix.php-version }}-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-php${{ matrix.php-version }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer update --prefer-dist --no-progress --no-scripts
|
||||
|
||||
- name: PHPUnit
|
||||
run: php -d xdebug.mode=coverage vendor/bin/phpunit --log-junit junit.xml
|
||||
|
||||
- name: Publish Unit Test Results
|
||||
uses: EnricoMi/publish-unit-test-result-action@v1
|
||||
if: always()
|
||||
with:
|
||||
files: junit.xml
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ composer.lock
|
||||
.phpunit.result.cache
|
||||
.php_cs.cache
|
||||
.php_cs
|
||||
.php-cs-fixer.cache
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()->in(__DIR__.'/src');
|
||||
|
||||
return PhpCsFixer\Config::create()
|
||||
return (new PhpCsFixer\Config())
|
||||
->setRules([
|
||||
'@Symfony' => true,
|
||||
'declare_strict_types' => true,
|
||||
56
CHANGELOG.md
Normal file
56
CHANGELOG.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Version 4.0.0
|
||||
|
||||
* Add compatibility with Ibexa 4.0+ and drop compatibility for eZPlatform 2 and Ibexa 3
|
||||
|
||||
# Version 3.2.0
|
||||
|
||||
* Fixed History page pagination is hidden by footer on Ibexa 3.3 #38
|
||||
* Added filter on history page to filter out jobs with 0 items
|
||||
* Date and times will now be displayed using the user defined timezone, and stored using php timezone
|
||||
|
||||
# Version 3.1.0
|
||||
|
||||
* Allow `LocationCreateStruct` objects inside the `$locations` argument of `ContentCreateStructure` to have more control over the created locations.
|
||||
|
||||
# Version 3.0.1
|
||||
|
||||
* Bump minimum PHP version to PHP 7.3 like code-rhapsodie/dataflow-bundle dependency.
|
||||
* Allow PHP 8.x.
|
||||
* Add GitHub Action to run tests.
|
||||
|
||||
# Version 3.0.0
|
||||
|
||||
* Add compatibility with Ibexa Content 3.3
|
||||
* Add compatibility with Symfony 5.x
|
||||
|
||||
# Version 2.3.0
|
||||
|
||||
* Added a button to display exceptions / log in a modal
|
||||
* Add log in `CodeRhapsodie\EzDataflowBundle\Filter\NotModifiedContentFilter` and `CodeRhapsodie\EzDataflowBundle\Writer\ContentWriter`
|
||||
|
||||
# Version 2.2.0
|
||||
|
||||
* Added `NotModifiedContentFilter` and a bunch of `FieldComparator` classes
|
||||
|
||||
# version 2.1.0
|
||||
|
||||
* ContentWriter return created content
|
||||
|
||||
# version 2.0.1
|
||||
|
||||
* Enclosure js code into anonymous function
|
||||
|
||||
# version 2.0.0
|
||||
|
||||
* Update to use Dataflow v2.0+
|
||||
* Add compiler pass to change the Dataflow DBAL connection factory
|
||||
* Use the DBAL connection from siteaccess
|
||||
* Add `mode` argument on `ContentStructureFactory::transform()` function
|
||||
* Add `CodeRhapsodie\EzDataflowBundle\Factory\ContentStructureFactoryInterface`
|
||||
|
||||
# version 1.0.0
|
||||
|
||||
* Initial version to use Dataflow v1.0+ into eZ Platform
|
||||
* Add Admin UI
|
||||
* Add content writer
|
||||
* Add content structure
|
||||
138
README.md
138
README.md
@@ -1,11 +1,13 @@
|
||||
# Code Rhapsodie eZ Dataflow Bundle
|
||||
|
||||
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.
|
||||
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.
|
||||
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).
|
||||
|
||||
> Command line notice: When you use Dataflow commands, **use `--siteaccess` instead of `--connection`** expect for `code-rhapsodie:dataflow:dump-schema` command.
|
||||
|
||||
## User Interface (UI)
|
||||
|
||||
The UI lets you create workflow processes from any defined `DataflowTypes`, and set options to each.
|
||||
@@ -24,9 +26,9 @@ $ composer require code-rhapsodie/ezdataflow-bundle
|
||||
|
||||
### Step 2: Enable the bundle
|
||||
|
||||
#### Symfony 4 (new tree)
|
||||
> Note: The loading order between the Dataflow bundle and Ez Dataflow bundle is important. Dataflow must be loaded first.
|
||||
|
||||
For Symfony 4, add those two lines in the `config/bundles.php` file:
|
||||
Add those two lines in the `config/bundles.php` file:
|
||||
|
||||
```php
|
||||
<?php
|
||||
@@ -39,29 +41,10 @@ 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
|
||||
# app/config/routing.yml or config/routing.yaml
|
||||
# config/routing/ezdataflow.yaml
|
||||
|
||||
_cr.dataflow:
|
||||
resource: '@CodeRhapsodieEzDataflowBundle/Resources/config/routing.yaml'
|
||||
@@ -80,7 +63,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
|
||||
# app/config/config.yml or config/packages/code_rhapsodie_ez_dataflow.yaml
|
||||
# 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
|
||||
@@ -93,7 +76,7 @@ Before using the admin UI to manage your dataflows, you need to define them. Ple
|
||||
|
||||
## Use the ContentWriter
|
||||
|
||||
To add or update eZ Platform contents, you can use the `CodeRhapsodie\EzDataflowBundle\Writer\ContentWriter` writer.
|
||||
To add or update Ibexa contents, you can use the `CodeRhapsodie\EzDataflowBundle\Writer\ContentWriter` writer.
|
||||
|
||||
### Step 1: Inject the dependencies and add the writer
|
||||
|
||||
@@ -129,7 +112,7 @@ class MyDataflowType extends AbstractDataflowType
|
||||
|
||||
### Step 2: Add a step for prepare the content
|
||||
|
||||
To process eZ Platform content into your Dataflow, you need to transform the data into `ContentCreateStructure` or `ContentUpdateStructure` objects.
|
||||
To process Ibexa contents 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.
|
||||
@@ -137,19 +120,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 eZ Platform.
|
||||
To check if the content exists or not, I use the service `ContentService` provided by Ibexa.
|
||||
|
||||
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 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.
|
||||
* 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.
|
||||
|
||||
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 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:
|
||||
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:
|
||||
|
||||

|
||||
|
||||
@@ -174,7 +157,7 @@ class MyDataflowType extends AbstractDataflowType
|
||||
/**
|
||||
* @var ContentStructureFactory
|
||||
*/
|
||||
private contentStructureFactory;
|
||||
private $contentStructureFactory;
|
||||
|
||||
public function __construct(ContentWriter $contentWriter, ContentStructureFactory $contentStructureFactory)
|
||||
{
|
||||
@@ -198,9 +181,12 @@ class MyDataflowType extends AbstractDataflowType
|
||||
$remoteId,
|
||||
'eng-GB',
|
||||
'article2',
|
||||
54 //Parent location id
|
||||
54, //Parent location id
|
||||
ContentStructureFactoryInterface::MODE_INSERT_OR_UPDATE //Optional value. Other choice : ContentStructureFactoryInterface::MODE_INSERT_ONLY or ContentStructureFactoryInterface::MODE_UPDATE_ONLY
|
||||
);
|
||||
});
|
||||
// If you want the writer log
|
||||
$this->contentWriter->setLogger($this->logger);
|
||||
$builder->addWriter($this->contentWriter);
|
||||
}
|
||||
}
|
||||
@@ -208,11 +194,90 @@ class MyDataflowType extends AbstractDataflowType
|
||||
|
||||
This example uses `ContentStructureFactory` to check if the content exists and returns the adequate `ContentStrucure` to pass to the content writer.
|
||||
|
||||
## Use the NotModifiedContentFilter
|
||||
|
||||
When updating contents, you might want to ignore contents where the update would not result in any actual changes in fields values. In that case, you can add the `NotModifiedContentFilter` as one of your steps.
|
||||
|
||||
```php
|
||||
// In your DataflowType
|
||||
public function __construct(NotModifiedContentFilter $notModifiedContentFilter)
|
||||
{
|
||||
$this->notModifiedContentFilter = $notModifiedContentFilter;
|
||||
}
|
||||
|
||||
//[...]
|
||||
protected function buildDataflow(DataflowBuilder $builder, array $options): void
|
||||
{
|
||||
//[...]
|
||||
// If you want the filter log
|
||||
$this->notModifiedContentFilter->setLogger($this->logger);
|
||||
$builder->addStep($this->notModifiedContentFilter);
|
||||
//[...]
|
||||
}
|
||||
```
|
||||
|
||||
This filter compares each field value in the `ContentUpdateStructure` received to the fields values in the existing content object. If all values are identical, this filter will return `false`, otherwise, the `ContentUpdateStructure` will be returned as is.
|
||||
|
||||
Not all field types are supported by this filter. Il a field type is not supported, values will be assumed different. If your dataflow is dealing with content types containing unsupported field types, it is better to simply not use the `NotModifiedContentFilter` to prevent unnecessary overhead.
|
||||
|
||||
### Supported field types
|
||||
- ezstring
|
||||
- ezauthor
|
||||
- ezboolean
|
||||
- ezcountry
|
||||
- ezdate
|
||||
- ezdatetime
|
||||
- ezemail
|
||||
- ezfloat
|
||||
- ezisbn
|
||||
- ezobjectrelation
|
||||
- ezobjectrelationlist
|
||||
- ezkeyword
|
||||
- ezselection
|
||||
- eztext
|
||||
- eztime
|
||||
- eztags
|
||||
- novaseometas
|
||||
- ezurl
|
||||
- ezmatrix
|
||||
- ezgmaplocation
|
||||
- ezrichtext
|
||||
|
||||
### Add custom field comparator
|
||||
|
||||
If you want to add support for a field type, simply create your own comparator.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\AbstractFieldComparator;
|
||||
use Ibexa\Core\FieldType\Value;
|
||||
//[...]
|
||||
|
||||
class MyFieldComparator extends AbstractFieldComparator
|
||||
{
|
||||
//[...]
|
||||
protected function compareValues(Value $currentValue, Value $newValue): bool
|
||||
{
|
||||
// Return true if values are identical, false if values are different.
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```yaml
|
||||
# Service declaration
|
||||
App\FieldComparator\MyFieldComparator:
|
||||
parent: 'CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\AbstractFieldComparator'
|
||||
tags:
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'my_field_type_identifier' }
|
||||
```
|
||||
|
||||
# Admin UI
|
||||
|
||||
## Access to the eZ Dataflow UI
|
||||
|
||||
You can access the eZ Dataflow administration UI from your eZ Platform admin back-office.
|
||||
You can access the eZ Dataflow administration UI from your Ibexa admin back-office.
|
||||
|
||||

|
||||
|
||||
@@ -297,6 +362,10 @@ Go to the eZ Dataflow admin UI and click on the "Oneshot" tab. Click on the "+"
|
||||
|
||||
Finally, click on the "Create" button.
|
||||
|
||||
# Rights
|
||||
|
||||
If a non-administrator user needs read-only access to the dataflow interface, add the `Setup / Administrate` and `eZ Dataflow / View` policies in one of their roles.
|
||||
|
||||
# Issues and feature requests
|
||||
|
||||
Please report issues and request features at https://github.com/code-rhapsodie/ezdataflow-bundle/issues.
|
||||
@@ -310,4 +379,3 @@ already.
|
||||
# License
|
||||
|
||||
This package is licensed under the [MIT license](LICENSE).
|
||||
|
||||
|
||||
15
UPGRADE.md
Normal file
15
UPGRADE.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# From v1.0 to v2.0
|
||||
|
||||
When you use Dataflow commands, use `--siteaccess` instead of `--connection` except for `code-rhapsodie:dataflow:dump-schema`.
|
||||
|
||||
[BC] The return of `CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway::findForScheduled`
|
||||
and `CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway::findAllOrderedByLabel` has been changed.
|
||||
The iterable contains an associative array instead of an object.
|
||||
|
||||
[BC] In classes `CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway` and
|
||||
`CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway`, all methods return `Doctrine\ORM\Query` object has
|
||||
changed to return now a `Doctrine\DBAL\Query\QueryBuilder`
|
||||
|
||||
[BC] The return type of `CodeRhapsodie\EzDataflowBundle\Factory\ContentStructureFactory::transform` has been changed
|
||||
from `CodeRhapsodie\EzDataflowBundle\Model\ContentStructure` to `mixed`. In fact only `false` or
|
||||
`CodeRhapsodie\EzDataflowBundle\Model\ContentStructure` object will be returned.
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "code-rhapsodie/ezdataflow-bundle",
|
||||
"description": "Import/export bundle for eZ Platform based on Code-Rhapsodie Dataflow",
|
||||
"description": "Import/export bundle for Ibexa based on Code-Rhapsodie Dataflow",
|
||||
"type": "symfony-bundle",
|
||||
"keywords": ["dataflow", "import", "export", "data processing", "ez publish", "ez platform"],
|
||||
"keywords": ["dataflow", "import", "export", "data processing", "ibexa"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
@@ -41,22 +41,30 @@
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"code-rhapsodie/dataflow-bundle": "^1.0 || dev-master",
|
||||
"ezsystems/ezplatform-admin-ui": "^1.0",
|
||||
"ezsystems/ezpublish-kernel": "^7.0"
|
||||
"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"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7||^8",
|
||||
"doctrine/orm": "^2.4.5"
|
||||
"doctrine/dbal": "^2.0|^3.0",
|
||||
"phpunit/phpunit": "^7||^8||^9"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"php-http/discovery": true
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
"dev-master": "4.x-dev",
|
||||
"dev-v3.x": "3.x-dev",
|
||||
"dev-v2.x": "2.x-dev",
|
||||
"dev-v1.x": "1.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
49
phpunit.xml
49
phpunit.xml
@@ -1,32 +1,21 @@
|
||||
<?xml version = '1.0' encoding = 'UTF-8'?>
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- http://www.phpunit.de/manual/current/en/appendixes.configuration.html -->
|
||||
<phpunit
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="Tests/bootstrap.php"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
colors="false"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
</php>
|
||||
<testsuites>
|
||||
<testsuite name="Port tests suite">
|
||||
<directory suffix="Test.php">./Tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./</directory>
|
||||
<exclude>
|
||||
<directory>Tests/</directory>
|
||||
<directory>vendor/</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" backupStaticAttributes="false" bootstrap="tests/bootstrap.php" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" colors="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||
<coverage>
|
||||
<include>
|
||||
<directory>./src/</directory>
|
||||
</include>
|
||||
<exclude>
|
||||
<directory>tests/</directory>
|
||||
<directory>vendor/</directory>
|
||||
</exclude>
|
||||
</coverage>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1"/>
|
||||
</php>
|
||||
<testsuites>
|
||||
<testsuite name="EzDataflow tests suite">
|
||||
<directory suffix="Test.php">./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
|
||||
@@ -5,8 +5,9 @@ declare(strict_types=1);
|
||||
namespace CodeRhapsodie\EzDataflowBundle;
|
||||
|
||||
use CodeRhapsodie\EzDataflowBundle\DependencyInjection\CodeRhapsodieEzDataflowExtension;
|
||||
use CodeRhapsodie\EzDataflowBundle\DependencyInjection\Compiler\FieldComparatorCompilerPass;
|
||||
use CodeRhapsodie\EzDataflowBundle\Security\PolicyProvider;
|
||||
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\EzPublishCoreExtension;
|
||||
use Ibexa\Bundle\Core\DependencyInjection\IbexaCoreExtension;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
@@ -23,8 +24,10 @@ class CodeRhapsodieEzDataflowBundle extends Bundle
|
||||
{
|
||||
parent::build($container);
|
||||
|
||||
/** @var EzPublishCoreExtension $eZExtension */
|
||||
$eZExtension = $container->getExtension('ezpublish');
|
||||
$eZExtension->addPolicyProvider(new PolicyProvider());
|
||||
$container->addCompilerPass(new FieldComparatorCompilerPass());
|
||||
|
||||
/** @var IbexaCoreExtension $ibexaExtension */
|
||||
$ibexaExtension = $container->getExtension('ibexa');
|
||||
$ibexaExtension->addPolicyProvider(new PolicyProvider());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,17 +4,16 @@ declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Controller;
|
||||
|
||||
use CodeRhapsodie\DataflowBundle\Entity\Job;
|
||||
use CodeRhapsodie\DataflowBundle\Entity\ScheduledDataflow;
|
||||
use CodeRhapsodie\EzDataflowBundle\Form\CreateOneshotType;
|
||||
use CodeRhapsodie\EzDataflowBundle\Form\CreateScheduledType;
|
||||
use CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway;
|
||||
use CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway;
|
||||
use CodeRhapsodie\DataflowBundle\Entity\Job;
|
||||
use CodeRhapsodie\DataflowBundle\Entity\ScheduledDataflow;
|
||||
use Doctrine\ORM\Query;
|
||||
use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute;
|
||||
use EzSystems\EzPlatformAdminUi\Notification\NotificationHandlerInterface;
|
||||
use EzSystems\EzPlatformAdminUiBundle\Controller\Controller;
|
||||
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
||||
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 Pagerfanta\Pagerfanta;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -25,33 +24,28 @@ use Symfony\Component\Routing\Annotation\Route;
|
||||
*/
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
/** @var JobGateway */
|
||||
/** @var \CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway */
|
||||
private $jobGateway;
|
||||
/** @var NotificationHandlerInterface */
|
||||
private $notificationHandler;
|
||||
/** @var ScheduledDataflowGateway */
|
||||
/** @var \CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway */
|
||||
private $scheduledDataflowGateway;
|
||||
|
||||
public function __construct(JobGateway $jobGateway, NotificationHandlerInterface $notificationHandler, ScheduledDataflowGateway $scheduledDataflowGateway)
|
||||
public function __construct(JobGateway $jobGateway, ScheduledDataflowGateway $scheduledDataflowGateway)
|
||||
{
|
||||
$this->jobGateway = $jobGateway;
|
||||
$this->notificationHandler = $notificationHandler;
|
||||
$this->scheduledDataflowGateway = $scheduledDataflowGateway;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/", name="coderhapsodie.ezdataflow.main")
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function main(): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
|
||||
|
||||
return $this->render('@ezdesign/ezdataflow/Dashboard/main.html.twig');
|
||||
return $this->render('@ibexadesign/ezdataflow/Dashboard/main.html.twig');
|
||||
}
|
||||
|
||||
public function repeating(): Response
|
||||
public function repeating(Request $request): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
|
||||
|
||||
@@ -61,12 +55,24 @@ class DashboardController extends Controller
|
||||
'action' => $this->generateUrl('coderhapsodie.ezdataflow.workflow.create'),
|
||||
]);
|
||||
|
||||
return $this->render('@ezdesign/ezdataflow/Dashboard/repeating.html.twig', [
|
||||
'items' => $this->scheduledDataflowGateway->findAllOrderedByLabel(),
|
||||
return $this->render('@ibexadesign/ezdataflow/Dashboard/repeating.html.twig', [
|
||||
'pager' => $this->getPager($this->scheduledDataflowGateway->getListQueryForAdmin(), $request),
|
||||
'form' => $form->createView(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/repeating", name="coderhapsodie.ezdataflow.repeating")
|
||||
*/
|
||||
public function getRepeatingPage(Request $request): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
|
||||
|
||||
return $this->render('@ibexadesign/ezdataflow/Dashboard/repeating.html.twig', [
|
||||
'pager' => $this->getPager($this->scheduledDataflowGateway->getListQueryForAdmin(), $request),
|
||||
]);
|
||||
}
|
||||
|
||||
public function oneshot(Request $request): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
|
||||
@@ -77,7 +83,7 @@ class DashboardController extends Controller
|
||||
'action' => $this->generateUrl('coderhapsodie.ezdataflow.job.create'),
|
||||
]);
|
||||
|
||||
return $this->render('@ezdesign/ezdataflow/Dashboard/oneshot.html.twig', [
|
||||
return $this->render('@ibexadesign/ezdataflow/Dashboard/oneshot.html.twig', [
|
||||
'pager' => $this->getPager($this->jobGateway->getOneshotListQueryForAdmin(), $request),
|
||||
'form' => $form->createView(),
|
||||
]);
|
||||
@@ -85,16 +91,12 @@ class DashboardController extends Controller
|
||||
|
||||
/**
|
||||
* @Route("/oneshot", name="coderhapsodie.ezdataflow.oneshot")
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function getOneshotPage(Request $request): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
|
||||
|
||||
return $this->render('@ezdesign/ezdataflow/Dashboard/oneshot.html.twig', [
|
||||
return $this->render('@ibexadesign/ezdataflow/Dashboard/oneshot.html.twig', [
|
||||
'pager' => $this->getPager($this->jobGateway->getOneshotListQueryForAdmin(), $request),
|
||||
]);
|
||||
}
|
||||
@@ -102,49 +104,48 @@ class DashboardController extends Controller
|
||||
public function history(Request $request): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
|
||||
$filter = (int) $request->query->get('filter', JobGateway::FILTER_NONE);
|
||||
|
||||
return $this->render('@ezdesign/ezdataflow/Dashboard/history.html.twig', [
|
||||
'pager' => $this->getPager($this->jobGateway->getListQueryForAdmin(), $request),
|
||||
return $this->render('@ibexadesign/ezdataflow/Dashboard/history.html.twig', [
|
||||
'pager' => $this->getPager($this->jobGateway->getListQueryForAdmin($filter), $request),
|
||||
'filter' => $filter,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/history", name="coderhapsodie.ezdataflow.history")
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function getHistoryPage(Request $request): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
|
||||
$filter = (int) $request->query->get('filter', JobGateway::FILTER_NONE);
|
||||
|
||||
return $this->render('@ezdesign/ezdataflow/Dashboard/history.html.twig', [
|
||||
'pager' => $this->getPager($this->jobGateway->getListQueryForAdmin(), $request),
|
||||
return $this->render('@ibexadesign/ezdataflow/Dashboard/history.html.twig', [
|
||||
'pager' => $this->getPager($this->jobGateway->getListQueryForAdmin($filter), $request),
|
||||
'filter' => $filter,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/history/schedule/{id}", name="coderhapsodie.ezdataflow.history.workflow")
|
||||
*
|
||||
* @param Request $request
|
||||
* @param int $id
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function getHistoryForScheduled(Request $request, int $id): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
|
||||
|
||||
return $this->render('@ezdesign/ezdataflow/Dashboard/schedule_history.html.twig', [
|
||||
return $this->render('@ibexadesign/ezdataflow/Dashboard/schedule_history.html.twig', [
|
||||
'id' => $id,
|
||||
'pager' => $this->getPager($this->jobGateway->getListQueryForScheduleAdmin($id), $request),
|
||||
]);
|
||||
}
|
||||
|
||||
private function getPager(Query $query, Request $request): Pagerfanta
|
||||
private function getPager(QueryBuilder $query, Request $request): Pagerfanta
|
||||
{
|
||||
$pager = new Pagerfanta(new DoctrineORMAdapter($query));
|
||||
$pager = new Pagerfanta(new QueryAdapter($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));
|
||||
|
||||
|
||||
@@ -7,25 +7,26 @@ namespace CodeRhapsodie\EzDataflowBundle\Controller;
|
||||
use CodeRhapsodie\DataflowBundle\Entity\Job;
|
||||
use CodeRhapsodie\EzDataflowBundle\Form\CreateOneshotType;
|
||||
use CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway;
|
||||
use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute;
|
||||
use EzSystems\EzPlatformAdminUi\Notification\NotificationHandlerInterface;
|
||||
use EzSystems\EzPlatformAdminUiBundle\Controller\Controller;
|
||||
use Ibexa\Contracts\AdminUi\Controller\Controller;
|
||||
use Ibexa\Contracts\AdminUi\Notification\NotificationHandlerInterface;
|
||||
use Ibexa\Core\MVC\Symfony\Security\Authorization\Attribute;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @Route("/ezdataflow/job")
|
||||
*/
|
||||
class JobController extends Controller
|
||||
{
|
||||
/** @var JobGateway */
|
||||
/** @var \CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway */
|
||||
private $jobGateway;
|
||||
/** @var NotificationHandlerInterface */
|
||||
/** @var \Ibexa\Contracts\AdminUi\Notification\NotificationHandlerInterface */
|
||||
private $notificationHandler;
|
||||
/** @var TranslatorInterface */
|
||||
/** @var \Symfony\Contracts\Translation\TranslatorInterface */
|
||||
private $translator;
|
||||
|
||||
public function __construct(
|
||||
@@ -40,26 +41,34 @@ class JobController extends Controller
|
||||
|
||||
/**
|
||||
* @Route("/details/{id}", name="coderhapsodie.ezdataflow.job.details")
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function displayDetails(int $id): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
|
||||
|
||||
return $this->render('@ezdesign/ezdataflow/Item/details.html.twig', [
|
||||
return $this->render('@ibexadesign/ezdataflow/Item/details.html.twig', [
|
||||
'item' => $this->jobGateway->find($id),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/details/log/{id}", name="coderhapsodie.ezdataflow.job.log")
|
||||
*/
|
||||
public function displayLog(int $id): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'view'));
|
||||
$item = $this->jobGateway->find($id);
|
||||
$log = array_map(function ($line) {
|
||||
return preg_replace('~#\d+~', "\n$0", $line);
|
||||
}, $item->getExceptions());
|
||||
|
||||
return $this->render('@ibexadesign/ezdataflow/Item/log.html.twig', [
|
||||
'log' => $log,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/create", name="coderhapsodie.ezdataflow.job.create", methods={"POST"})
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function create(Request $request): Response
|
||||
{
|
||||
@@ -69,7 +78,7 @@ class JobController extends Controller
|
||||
$form = $this->createForm(CreateOneshotType::class, $newOneshot);
|
||||
$form->handleRequest($request);
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
/** @var Job $newOneshot */
|
||||
/** @var \CodeRhapsodie\DataflowBundle\Entity\Job $newOneshot */
|
||||
$newOneshot = $form->getData();
|
||||
$newOneshot->setStatus(Job::STATUS_PENDING);
|
||||
|
||||
@@ -82,12 +91,13 @@ class JobController extends Controller
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'redirect' => $this->generateUrl('coderhapsodie.ezdataflow.main', ['_fragment' => 'oneshot']),
|
||||
'redirect' => $this->generateUrl('coderhapsodie.ezdataflow.main', ['_fragment' => 'oneshot'],
|
||||
UrlGeneratorInterface::ABSOLUTE_URL),
|
||||
]);
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'form' => $this->renderView('@ezdesign/ezdataflow/parts/schedule_form.html.twig', [
|
||||
'form' => $this->renderView('@ibexadesign/ezdataflow/parts/form_modal.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'type_action' => 'new',
|
||||
'mode' => 'oneshot',
|
||||
|
||||
@@ -4,37 +4,36 @@ declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Controller;
|
||||
|
||||
use CodeRhapsodie\DataflowBundle\Entity\ScheduledDataflow;
|
||||
use CodeRhapsodie\EzDataflowBundle\Form\CreateScheduledType;
|
||||
use CodeRhapsodie\EzDataflowBundle\Form\UpdateScheduledType;
|
||||
use CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway;
|
||||
use CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway;
|
||||
use CodeRhapsodie\DataflowBundle\Entity\ScheduledDataflow;
|
||||
use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute;
|
||||
use EzSystems\EzPlatformAdminUi\Notification\NotificationHandlerInterface;
|
||||
use EzSystems\EzPlatformAdminUiBundle\Controller\Controller;
|
||||
use Ibexa\Contracts\AdminUi\Controller\Controller;
|
||||
use Ibexa\Contracts\AdminUi\Notification\NotificationHandlerInterface;
|
||||
use Ibexa\Core\MVC\Symfony\Security\Authorization\Attribute;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @Route("/ezdataflow/scheduled_workflow")
|
||||
*/
|
||||
class ScheduledDataflowController extends Controller
|
||||
{
|
||||
/** @var JobGateway */
|
||||
private $jobGateway;
|
||||
/** @var NotificationHandlerInterface */
|
||||
/** @var \Ibexa\Contracts\AdminUi\Notification\NotificationHandlerInterface */
|
||||
private $notificationHandler;
|
||||
/** @var ScheduledDataflowGateway */
|
||||
/** @var \CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway */
|
||||
private $scheduledDataflowGateway;
|
||||
/** @var TranslatorInterface */
|
||||
/** @var \Symfony\Contracts\Translation\TranslatorInterface */
|
||||
private $translator;
|
||||
|
||||
public function __construct(JobGateway $jobGateway, NotificationHandlerInterface $notificationHandler, ScheduledDataflowGateway $scheduledDataflowGateway, TranslatorInterface $translator)
|
||||
{
|
||||
$this->jobGateway = $jobGateway;
|
||||
public function __construct(
|
||||
NotificationHandlerInterface $notificationHandler,
|
||||
ScheduledDataflowGateway $scheduledDataflowGateway,
|
||||
TranslatorInterface $translator
|
||||
) {
|
||||
$this->notificationHandler = $notificationHandler;
|
||||
$this->scheduledDataflowGateway = $scheduledDataflowGateway;
|
||||
$this->translator = $translator;
|
||||
@@ -42,10 +41,6 @@ class ScheduledDataflowController extends Controller
|
||||
|
||||
/**
|
||||
* @Route("/create", name="coderhapsodie.ezdataflow.workflow.create", methods={"POST"})
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function create(Request $request): Response
|
||||
{
|
||||
@@ -56,21 +51,21 @@ class ScheduledDataflowController extends Controller
|
||||
|
||||
$form->handleRequest($request);
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
/** @var ScheduledDataflow $newWorkflow */
|
||||
/** @var \CodeRhapsodie\DataflowBundle\Entity\ScheduledDataflow $newWorkflow */
|
||||
$newWorkflow = $form->getData();
|
||||
|
||||
try {
|
||||
$this->scheduledDataflowGateway->save($newWorkflow);
|
||||
$this->notificationHandler->success($this->translator->trans('coderhapsodie.ezdataflow.workflow.create.success'));
|
||||
} catch (\Exception $e) {
|
||||
$this->notificationHandler->error($this->translator->trans('coderhapsodie.ezdataflow.workflow.create.error', ['message' => $e->getMessage()]));
|
||||
$this->notificationHandler->error($this->translator->trans('coderhapsodie.ezdataflow.workflow.create.error',
|
||||
['message' => $e->getMessage()]));
|
||||
}
|
||||
|
||||
return new JsonResponse(['redirect' => $this->generateUrl('coderhapsodie.ezdataflow.main')]);
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'form' => $this->renderView('@ezdesign/ezdataflow/parts/schedule_form.html.twig', [
|
||||
'form' => $this->renderView('@ibexadesign/ezdataflow/parts/form_modal.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'type_action' => 'new',
|
||||
]),
|
||||
@@ -79,10 +74,6 @@ class ScheduledDataflowController extends Controller
|
||||
|
||||
/**
|
||||
* @Route("/{id}/delete", name="coderhapsodie.ezdataflow.workflow.delete", methods={"post"})
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function delete(int $id): Response
|
||||
{
|
||||
@@ -91,22 +82,16 @@ 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 new JsonResponse(['code' => $e->getCode()]);
|
||||
$this->notificationHandler->error($this->translator->trans('coderhapsodie.ezdataflow.workflow.delete.error',
|
||||
['message' => $e->getMessage()]));
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('coderhapsodie.ezdataflow.main');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{id}/edit", name="coderhapsodie.ezdataflow.workflow.edit")
|
||||
*
|
||||
* @param Request $request
|
||||
* @param int $id
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function edit(Request $request, int $id): Response
|
||||
{
|
||||
@@ -116,21 +101,22 @@ class ScheduledDataflowController extends Controller
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
/** @var ScheduledDataflow $editDataflow */
|
||||
/** @var \CodeRhapsodie\DataflowBundle\Entity\ScheduledDataflow $editDataflow */
|
||||
$editDataflow = $form->getData();
|
||||
|
||||
try {
|
||||
$this->scheduledDataflowGateway->save($editDataflow);
|
||||
$this->notificationHandler->success($this->translator->trans('coderhapsodie.ezdataflow.workflow.edit.success'));
|
||||
} catch (\Exception $e) {
|
||||
$this->notificationHandler->error($this->translator->trans('coderhapsodie.ezdataflow.workflow.edit.error', ['message' => $e->getMessage()]));
|
||||
$this->notificationHandler->error($this->translator->trans('coderhapsodie.ezdataflow.workflow.edit.error',
|
||||
['message' => $e->getMessage()]));
|
||||
}
|
||||
|
||||
return new JsonResponse(['redirect' => $this->generateUrl('coderhapsodie.ezdataflow.main')]);
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'form' => $this->renderView('@ezdesign/ezdataflow/parts/schedule_form.html.twig', [
|
||||
'form' => $this->renderView('@ibexadesign/ezdataflow/parts/form_modal.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'type_action' => 'edit',
|
||||
]),
|
||||
@@ -139,10 +125,6 @@ class ScheduledDataflowController extends Controller
|
||||
|
||||
/**
|
||||
* @Route("/{id}/enable", name="coderhapsodie.ezdataflow.workflow.enable")
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function enableDataflow(int $id): Response
|
||||
{
|
||||
@@ -153,26 +135,10 @@ class ScheduledDataflowController extends Controller
|
||||
return $this->redirectToRoute('coderhapsodie.ezdataflow.main');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{id}/disable", name="coderhapsodie.ezdataflow.workflow.disable")
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function disableDataflow(int $id): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'edit'));
|
||||
|
||||
$this->changeDataflowStatus($id, false);
|
||||
|
||||
return $this->redirectToRoute('coderhapsodie.ezdataflow.main');
|
||||
}
|
||||
|
||||
private function changeDataflowStatus(int $id, bool $status)
|
||||
{
|
||||
try {
|
||||
/** @var ScheduledDataflow $workflow */
|
||||
/** @var \CodeRhapsodie\DataflowBundle\Entity\ScheduledDataflow $workflow */
|
||||
$workflow = $this->scheduledDataflowGateway->find($id);
|
||||
$workflow->setEnabled($status);
|
||||
$this->scheduledDataflowGateway->save($workflow);
|
||||
@@ -182,4 +148,16 @@ class ScheduledDataflowController extends Controller
|
||||
$this->notificationHandler->error(sprintf('An error occured : "%s".', $e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{id}/disable", name="coderhapsodie.ezdataflow.workflow.disable")
|
||||
*/
|
||||
public function disableDataflow(int $id): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(new Attribute('ezdataflow', 'edit'));
|
||||
|
||||
$this->changeDataflowStatus($id, false);
|
||||
|
||||
return $this->redirectToRoute('coderhapsodie.ezdataflow.main');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 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;
|
||||
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;
|
||||
|
||||
class ContentCreator implements ContentCreatorInterface
|
||||
{
|
||||
/** @var ContentService */
|
||||
/** @var \Ibexa\Contracts\Core\Repository\ContentService */
|
||||
private $contentService;
|
||||
|
||||
/** @var ContentTypeService */
|
||||
/** @var \Ibexa\Contracts\Core\Repository\ContentTypeService */
|
||||
private $contentTypeService;
|
||||
|
||||
/** @var ContentStructFieldFillerInterface */
|
||||
/** @var \CodeRhapsodie\EzDataflowBundle\Core\Field\ContentStructFieldFillerInterface */
|
||||
private $filler;
|
||||
|
||||
/** @var LocationMatcherInterface */
|
||||
/** @var \CodeRhapsodie\EzDataflowBundle\Matcher\LocationMatcherInterface */
|
||||
private $matcher;
|
||||
|
||||
public function __construct(ContentService $contentService, ContentTypeService $contentTypeService, ContentStructFieldFillerInterface $filler, LocationMatcherInterface $matcher)
|
||||
@@ -35,16 +35,12 @@ class ContentCreator implements ContentCreatorInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContentCreateStructure $structure
|
||||
*
|
||||
* @return Content
|
||||
*
|
||||
* @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
|
||||
* @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
|
||||
*/
|
||||
public function createFromStructure(ContentCreateStructure $structure): Content
|
||||
{
|
||||
@@ -58,17 +54,21 @@ class ContentCreator implements ContentCreatorInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $locations
|
||||
*
|
||||
* @return LocationCreateStruct[]
|
||||
* @return \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct[]
|
||||
*/
|
||||
private function getLocationCreateStructs(array $locations): array
|
||||
{
|
||||
$locationCreateStructs = [];
|
||||
|
||||
foreach ($locations as $locationOrIdOrRemoteId) {
|
||||
foreach ($locations as $locationOrIdOrRemoteIdOrStruct) {
|
||||
if ($locationOrIdOrRemoteIdOrStruct instanceof LocationCreateStruct) {
|
||||
$locationCreateStructs[] = $locationOrIdOrRemoteIdOrStruct;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$locationCreateStructs[] = new LocationCreateStruct([
|
||||
'parentLocationId' => $this->matcher->matchLocation($locationOrIdOrRemoteId)->id,
|
||||
'parentLocationId' => $this->matcher->matchLocation($locationOrIdOrRemoteIdOrStruct)->id,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Core\Content;
|
||||
|
||||
use CodeRhapsodie\EzDataflowBundle\Model\ContentCreateStructure;
|
||||
use eZ\Publish\API\Repository\Values\Content\Content;
|
||||
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
|
||||
|
||||
interface ContentCreatorInterface
|
||||
{
|
||||
|
||||
@@ -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 eZ\Publish\API\Repository\ContentService;
|
||||
use eZ\Publish\API\Repository\ContentTypeService;
|
||||
use eZ\Publish\API\Repository\Values\Content\Content;
|
||||
use Ibexa\Contracts\Core\Repository\ContentService;
|
||||
use Ibexa\Contracts\Core\Repository\ContentTypeService;
|
||||
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
|
||||
|
||||
class ContentUpdater implements ContentUpdaterInterface
|
||||
{
|
||||
/** @var ContentService */
|
||||
/** @var \Ibexa\Contracts\Core\Repository\ContentService */
|
||||
private $contentService;
|
||||
|
||||
/** @var ContentTypeService */
|
||||
/** @var \Ibexa\Contracts\Core\Repository\ContentTypeService */
|
||||
private $contentTypeService;
|
||||
|
||||
/** @var ContentStructFieldFillerInterface */
|
||||
/** @var \CodeRhapsodie\EzDataflowBundle\Core\Field\ContentStructFieldFillerInterface */
|
||||
private $filler;
|
||||
|
||||
public function __construct(ContentService $contentService, ContentTypeService $contentTypeService, ContentStructFieldFillerInterface $filler)
|
||||
@@ -30,17 +30,13 @@ class ContentUpdater implements ContentUpdaterInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContentUpdateStructure $structure
|
||||
*
|
||||
* @return Content
|
||||
*
|
||||
* @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
|
||||
* @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
|
||||
*/
|
||||
public function updateFromStructure(ContentUpdateStructure $structure): Content
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Core\Content;
|
||||
|
||||
use CodeRhapsodie\EzDataflowBundle\Model\ContentUpdateStructure;
|
||||
use eZ\Publish\API\Repository\Values\Content\Content;
|
||||
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
|
||||
|
||||
interface ContentUpdaterInterface
|
||||
{
|
||||
|
||||
@@ -6,9 +6,9 @@ namespace CodeRhapsodie\EzDataflowBundle\Core\Field;
|
||||
|
||||
use CodeRhapsodie\EzDataflowBundle\Exception\UnknownFieldException;
|
||||
use CodeRhapsodie\EzDataflowBundle\Exception\UnsupportedFieldTypeException;
|
||||
use eZ\Publish\API\Repository\Values\Content\ContentStruct;
|
||||
use eZ\Publish\API\Repository\Values\ContentType\ContentType;
|
||||
use eZ\Publish\Core\FieldType\Value;
|
||||
use Ibexa\Contracts\Core\FieldType\Value;
|
||||
use Ibexa\Contracts\Core\Repository\Values\Content\ContentStruct;
|
||||
use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType;
|
||||
|
||||
class ContentStructFieldFiller implements ContentStructFieldFillerInterface
|
||||
{
|
||||
@@ -17,8 +17,6 @@ class ContentStructFieldFiller implements ContentStructFieldFillerInterface
|
||||
|
||||
/**
|
||||
* ContentStructFieldFiller constructor.
|
||||
*
|
||||
* @param iterable $fieldValueCreators
|
||||
*/
|
||||
public function __construct(iterable $fieldValueCreators)
|
||||
{
|
||||
@@ -28,8 +26,8 @@ class ContentStructFieldFiller implements ContentStructFieldFillerInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws UnknownFieldException
|
||||
* @throws UnsupportedFieldTypeException
|
||||
* @throws \CodeRhapsodie\EzDataflowBundle\Exception\UnknownFieldException
|
||||
* @throws \CodeRhapsodie\EzDataflowBundle\Exception\UnsupportedFieldTypeException
|
||||
*/
|
||||
public function fillFields(ContentType $contentType, ContentStruct $contentStruct, array $fieldHashes): void
|
||||
{
|
||||
@@ -47,12 +45,9 @@ class ContentStructFieldFiller implements ContentStructFieldFillerInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fieldTypeIdentifier
|
||||
* @param mixed $hash
|
||||
* @param mixed $hash
|
||||
*
|
||||
* @return Value
|
||||
*
|
||||
* @throws UnsupportedFieldTypeException
|
||||
* @throws \CodeRhapsodie\EzDataflowBundle\Exception\UnsupportedFieldTypeException
|
||||
*/
|
||||
private function createFieldValue(string $fieldTypeIdentifier, $hash): Value
|
||||
{
|
||||
|
||||
@@ -4,15 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Core\Field;
|
||||
|
||||
use eZ\Publish\API\Repository\Values\Content\ContentStruct;
|
||||
use eZ\Publish\API\Repository\Values\ContentType\ContentType;
|
||||
use Ibexa\Contracts\Core\Repository\Values\Content\ContentStruct;
|
||||
use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType;
|
||||
|
||||
interface ContentStructFieldFillerInterface
|
||||
{
|
||||
/**
|
||||
* @param ContentType $contentType
|
||||
* @param ContentStruct $contentStruct
|
||||
* @param array $fieldHashes
|
||||
*/
|
||||
public function fillFields(ContentType $contentType, ContentStruct $contentStruct, array $fieldHashes): void;
|
||||
}
|
||||
|
||||
@@ -4,18 +4,14 @@ declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Core\Field;
|
||||
|
||||
use eZ\Publish\API\Repository\FieldType;
|
||||
use eZ\Publish\API\Repository\FieldTypeService;
|
||||
use eZ\Publish\Core\FieldType\Value;
|
||||
use Ibexa\Contracts\Core\FieldType\Value;
|
||||
use Ibexa\Contracts\Core\Repository\FieldTypeService;
|
||||
|
||||
class DefaultFieldValueCreator implements FieldValueCreatorInterface
|
||||
{
|
||||
/** @var FieldTypeService */
|
||||
/** @var \Ibexa\Contracts\Core\Repository\FieldTypeService */
|
||||
private $fieldTypeService;
|
||||
|
||||
/** @var FieldType[] */
|
||||
private $fieldTypes = [];
|
||||
|
||||
public function __construct(FieldTypeService $fieldTypeService)
|
||||
{
|
||||
$this->fieldTypeService = $fieldTypeService;
|
||||
@@ -28,22 +24,6 @@ class DefaultFieldValueCreator implements FieldValueCreatorInterface
|
||||
|
||||
public function createValue(string $fieldTypeIdentifier, $hash): Value
|
||||
{
|
||||
return $this->getFieldType($fieldTypeIdentifier)->fromHash($hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fieldTypeIdentifier
|
||||
*
|
||||
* @return FieldType
|
||||
*
|
||||
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
|
||||
*/
|
||||
private function getFieldType(string $fieldTypeIdentifier): FieldType
|
||||
{
|
||||
if (!isset($this->fieldTypes[$fieldTypeIdentifier])) {
|
||||
$this->fieldTypes[$fieldTypeIdentifier] = $this->fieldTypeService->getFieldType($fieldTypeIdentifier);
|
||||
}
|
||||
|
||||
return $this->fieldTypes[$fieldTypeIdentifier];
|
||||
return $this->fieldTypeService->getFieldType($fieldTypeIdentifier)->fromHash($hash);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,22 +4,14 @@ declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Core\Field;
|
||||
|
||||
use eZ\Publish\Core\FieldType\Value;
|
||||
use Ibexa\Contracts\Core\FieldType\Value;
|
||||
|
||||
interface FieldValueCreatorInterface
|
||||
{
|
||||
/**
|
||||
* @param string $fieldTypeIdentifier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports(string $fieldTypeIdentifier): bool;
|
||||
|
||||
/**
|
||||
* @param string $fieldTypeIdentifier
|
||||
* @param mixed $hash
|
||||
*
|
||||
* @return Value
|
||||
* @param mixed $hash
|
||||
*/
|
||||
public function createValue(string $fieldTypeIdentifier, $hash): Value;
|
||||
}
|
||||
|
||||
32
src/Core/FieldComparator/AbstractFieldComparator.php
Normal file
32
src/Core/FieldComparator/AbstractFieldComparator.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
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;
|
||||
|
||||
abstract class AbstractFieldComparator implements FieldComparatorInterface
|
||||
{
|
||||
/** @var \Ibexa\Contracts\Core\Repository\FieldTypeService */
|
||||
private $fieldTypeService;
|
||||
|
||||
public function __construct(FieldTypeService $fieldTypeService)
|
||||
{
|
||||
$this->fieldTypeService = $fieldTypeService;
|
||||
}
|
||||
|
||||
public function compare(Field $field, $hash): bool
|
||||
{
|
||||
$newValue = $this->fieldTypeService->getFieldType($field->fieldTypeIdentifier)->fromHash($hash);
|
||||
|
||||
return $this->compareValues($field->value, $newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if values are equals, false otherwise.
|
||||
*/
|
||||
abstract protected function compareValues(Value $currentValue, Value $newValue): bool;
|
||||
}
|
||||
36
src/Core/FieldComparator/DelegatorFieldComparator.php
Normal file
36
src/Core/FieldComparator/DelegatorFieldComparator.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Core\FieldComparator;
|
||||
|
||||
use Ibexa\Contracts\Core\Repository\Values\Content\Field;
|
||||
|
||||
class DelegatorFieldComparator implements FieldComparatorInterface
|
||||
{
|
||||
/** @var FieldComparatorInterface[] */
|
||||
private $delegates;
|
||||
|
||||
/**
|
||||
* FieldComparator constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->delegates = [];
|
||||
}
|
||||
|
||||
public function compare(Field $field, $hash): bool
|
||||
{
|
||||
if (isset($this->delegates[$field->fieldTypeIdentifier])) {
|
||||
return $this->delegates[$field->fieldTypeIdentifier]->compare($field, $hash);
|
||||
}
|
||||
|
||||
// No comparator to handle this field type, we assume the value is different.
|
||||
return false;
|
||||
}
|
||||
|
||||
public function registerDelegateFieldComparator(FieldComparatorInterface $typedFieldComparator, string $fieldTypeIdentifier): void
|
||||
{
|
||||
$this->delegates[$fieldTypeIdentifier] = $typedFieldComparator;
|
||||
}
|
||||
}
|
||||
15
src/Core/FieldComparator/FieldComparatorInterface.php
Normal file
15
src/Core/FieldComparator/FieldComparatorInterface.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Core\FieldComparator;
|
||||
|
||||
use Ibexa\Contracts\Core\Repository\Values\Content\Field;
|
||||
|
||||
interface FieldComparatorInterface
|
||||
{
|
||||
/**
|
||||
* @return bool true if identical, false otherwise
|
||||
*/
|
||||
public function compare(Field $field, $hash): bool;
|
||||
}
|
||||
18
src/Core/FieldComparator/MapLocationFieldComparator.php
Normal file
18
src/Core/FieldComparator/MapLocationFieldComparator.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Core\FieldComparator;
|
||||
|
||||
use Ibexa\Contracts\Core\FieldType\Value;
|
||||
|
||||
class MapLocationFieldComparator extends AbstractFieldComparator
|
||||
{
|
||||
protected function compareValues(Value $currentValue, Value $newValue): bool
|
||||
{
|
||||
return (string) $currentValue === (string) $newValue
|
||||
&& (float) $currentValue->longitude === (float) $newValue->longitude
|
||||
&& (float) $currentValue->latitude === (float) $newValue->latitude
|
||||
;
|
||||
}
|
||||
}
|
||||
29
src/Core/FieldComparator/MatrixFieldComparator.php
Normal file
29
src/Core/FieldComparator/MatrixFieldComparator.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Core\FieldComparator;
|
||||
|
||||
use Ibexa\Contracts\Core\FieldType\Value;
|
||||
|
||||
class MatrixFieldComparator extends AbstractFieldComparator
|
||||
{
|
||||
protected function compareValues(Value $currentValue, Value $newValue): bool
|
||||
{
|
||||
if (count($currentValue->rows) !== count($newValue->rows)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($newValue->rows as $index => $row) {
|
||||
if (count($currentValue->rows[$index]->getCells()) !== count($row->getCells())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!empty(array_diff_assoc($currentValue->rows[$index]->getCells(), $row->getCells()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
26
src/Core/FieldComparator/NovaSEOMetasFieldComparator.php
Normal file
26
src/Core/FieldComparator/NovaSEOMetasFieldComparator.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Core\FieldComparator;
|
||||
|
||||
use Ibexa\Contracts\Core\FieldType\Value;
|
||||
|
||||
class NovaSEOMetasFieldComparator extends AbstractFieldComparator
|
||||
{
|
||||
protected function compareValues(Value $currentValue, Value $newValue): bool
|
||||
{
|
||||
$map = [];
|
||||
foreach ($currentValue->metas as $meta) {
|
||||
$map[$meta->getName()] = $meta->getContent();
|
||||
}
|
||||
|
||||
foreach ($newValue->metas as $meta) {
|
||||
if (!isset($map[$meta->getName()]) || $map[$meta->getName()] !== $meta->getContent()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return count($currentValue->metas) === count($newValue->metas);
|
||||
}
|
||||
}
|
||||
15
src/Core/FieldComparator/SimpleFieldComparator.php
Normal file
15
src/Core/FieldComparator/SimpleFieldComparator.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Core\FieldComparator;
|
||||
|
||||
use Ibexa\Contracts\Core\FieldType\Value;
|
||||
|
||||
class SimpleFieldComparator extends AbstractFieldComparator
|
||||
{
|
||||
protected function compareValues(Value $currentValue, Value $newValue): bool
|
||||
{
|
||||
return (string) $currentValue === (string) $newValue;
|
||||
}
|
||||
}
|
||||
15
src/Core/FieldComparator/UrlFieldComparator.php
Normal file
15
src/Core/FieldComparator/UrlFieldComparator.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Core\FieldComparator;
|
||||
|
||||
use Ibexa\Contracts\Core\FieldType\Value;
|
||||
|
||||
class UrlFieldComparator extends AbstractFieldComparator
|
||||
{
|
||||
protected function compareValues(Value $currentValue, Value $newValue): bool
|
||||
{
|
||||
return $currentValue->link === $newValue->link && $currentValue->text === $newValue->text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\DependencyInjection\Compiler;
|
||||
|
||||
use CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\DelegatorFieldComparator;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
class FieldComparatorCompilerPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->has(DelegatorFieldComparator::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$delegatorDef = $container->findDefinition(DelegatorFieldComparator::class);
|
||||
|
||||
foreach ($container->findTaggedServiceIds('coderhapsodie.ezdataflow.field_comparator') as $id => $tags) {
|
||||
foreach ($tags as $attributes) {
|
||||
if (!isset($attributes['fieldType'])) {
|
||||
throw new \InvalidArgumentException(sprintf('Service "%s" must define the "fieldType" attribute on "coderhapsodie.ezdataflow.field_comparator" tags.', $id));
|
||||
}
|
||||
|
||||
$delegatorDef->addMethodCall(
|
||||
'registerDelegateFieldComparator',
|
||||
[new Reference($id), $attributes['fieldType']]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,7 @@ class Configuration implements ConfigurationInterface
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('code_rhapsodie_ez_dataflow');
|
||||
|
||||
if (method_exists($treeBuilder, 'getRootNode')) {
|
||||
$rootNode = $treeBuilder->getRootNode();
|
||||
} else {
|
||||
// BC for symfony/config < 4.2
|
||||
$rootNode = $treeBuilder->root('code_rhapsodie_ez_dataflow');
|
||||
}
|
||||
|
||||
$rootNode = $treeBuilder->getRootNode();
|
||||
$rootNode
|
||||
->children()
|
||||
->scalarNode('admin_login_or_id')
|
||||
|
||||
@@ -4,9 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\EventSubscriber;
|
||||
|
||||
use EzSystems\EzPlatformAdminUi\Menu\Event\ConfigureMenuEvent;
|
||||
use EzSystems\EzPlatformAdminUi\Menu\MainMenuBuilder;
|
||||
use Knp\Menu\ItemInterface;
|
||||
use Ibexa\AdminUi\Menu\Event\ConfigureMenuEvent;
|
||||
use Ibexa\AdminUi\Menu\MainMenuBuilder;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class MenuSubscriber implements EventSubscriberInterface
|
||||
@@ -21,7 +20,7 @@ class MenuSubscriber implements EventSubscriberInterface
|
||||
|
||||
public function onConfigureMenu(ConfigureMenuEvent $event)
|
||||
{
|
||||
/** @var ItemInterface $menu */
|
||||
/** @var \Knp\Menu\ItemInterface $menu */
|
||||
$menu = $event->getMenu();
|
||||
if (!isset($menu[MainMenuBuilder::ITEM_ADMIN])) {
|
||||
return;
|
||||
|
||||
@@ -9,8 +9,6 @@ class InvalidArgumentTypeException extends \Exception
|
||||
/**
|
||||
* @param string|array $expectedTypes
|
||||
* @param mixed $received
|
||||
*
|
||||
* @return InvalidArgumentTypeException
|
||||
*/
|
||||
public static function create($expectedTypes, $received): self
|
||||
{
|
||||
|
||||
@@ -6,12 +6,6 @@ namespace CodeRhapsodie\EzDataflowBundle\Exception;
|
||||
|
||||
class UnknownFieldException extends \Exception
|
||||
{
|
||||
/**
|
||||
* @param string $fieldIdentifier
|
||||
* @param string $contentTypeIdentifier
|
||||
*
|
||||
* @return UnknownFieldException
|
||||
*/
|
||||
public static function create(string $fieldIdentifier, string $contentTypeIdentifier): self
|
||||
{
|
||||
return new self(sprintf(
|
||||
|
||||
@@ -5,22 +5,19 @@ declare(strict_types=1);
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Factory;
|
||||
|
||||
use CodeRhapsodie\EzDataflowBundle\Model\ContentCreateStructure;
|
||||
use CodeRhapsodie\EzDataflowBundle\Model\ContentStructure;
|
||||
use CodeRhapsodie\EzDataflowBundle\Model\ContentUpdateStructure;
|
||||
use eZ\Publish\API\Repository\ContentService;
|
||||
use eZ\Publish\API\Repository\Exceptions\NotFoundException;
|
||||
use Ibexa\Contracts\Core\Repository\ContentService;
|
||||
use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException;
|
||||
|
||||
class ContentStructureFactory
|
||||
final class ContentStructureFactory implements ContentStructureFactoryInterface
|
||||
{
|
||||
/**
|
||||
* @var ContentService
|
||||
* @var \Ibexa\Contracts\Core\Repository\ContentService
|
||||
*/
|
||||
private $contentService;
|
||||
|
||||
/**
|
||||
* ContentStructureFactory constructor.
|
||||
*
|
||||
* @param ContentService $contentService
|
||||
*/
|
||||
public function __construct(ContentService $contentService)
|
||||
{
|
||||
@@ -28,27 +25,31 @@ class ContentStructureFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param string $remoteId
|
||||
* @param string $language
|
||||
* @param string $contentType
|
||||
* @param mixed $parentLocations
|
||||
* @param int $mode One of the constant ContentStructureFactoryInterface::MODE_*
|
||||
*
|
||||
* @return ContentStructure
|
||||
* @return false|\CodeRhapsodie\EzDataflowBundle\Model\ContentStructure
|
||||
*
|
||||
* @throws \CodeRhapsodie\EzDataflowBundle\Exception\InvalidArgumentTypeException
|
||||
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
|
||||
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException
|
||||
*/
|
||||
public function transform(array $data, string $remoteId, string $language, string $contentType, $parentLocations): ContentStructure
|
||||
public function transform(array $data, string $remoteId, string $language, string $contentType, $parentLocations, int $mode = ContentStructureFactoryInterface::MODE_INSERT_OR_UPDATE)
|
||||
{
|
||||
try {
|
||||
$content = $this->contentService->loadContentByRemoteId($remoteId);
|
||||
if ($mode === static::MODE_INSERT_ONLY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ContentUpdateStructure::createForContentId($content->id, $language, $data);
|
||||
} catch (NotFoundException $e) {
|
||||
// The content doesn't exist yet, so it will be created.
|
||||
}
|
||||
|
||||
if (self::MODE_UPDATE_ONLY === $mode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return new ContentCreateStructure(
|
||||
$contentType,
|
||||
$language,
|
||||
|
||||
20
src/Factory/ContentStructureFactoryInterface.php
Normal file
20
src/Factory/ContentStructureFactoryInterface.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Factory;
|
||||
|
||||
interface ContentStructureFactoryInterface
|
||||
{
|
||||
public const MODE_INSERT_OR_UPDATE = 1;
|
||||
public const MODE_INSERT_ONLY = 2;
|
||||
public const MODE_UPDATE_ONLY = 3;
|
||||
|
||||
/**
|
||||
* @param int|string $parentLocations Int for location id or string for remote location id
|
||||
* @param int $mode ContentStructureFactoryInterface
|
||||
*
|
||||
* @return false|\CodeRhapsodie\EzDataflowBundle\Model\ContentStructure
|
||||
*/
|
||||
public function transform(array $data, string $remoteId, string $language, string $contentType, $parentLocations, int $mode = ContentStructureFactoryInterface::MODE_INSERT_OR_UPDATE);
|
||||
}
|
||||
64
src/Filter/NotModifiedContentFilter.php
Normal file
64
src/Filter/NotModifiedContentFilter.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Filter;
|
||||
|
||||
use CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\FieldComparatorInterface;
|
||||
use CodeRhapsodie\EzDataflowBundle\Model\ContentUpdateStructure;
|
||||
use Ibexa\Contracts\Core\Repository\ContentService;
|
||||
use Psr\Log\LoggerAwareTrait;
|
||||
|
||||
/**
|
||||
* Filters ContentUpdateStructure that would not result in any actual changes in the content.
|
||||
*/
|
||||
class NotModifiedContentFilter
|
||||
{
|
||||
use LoggerAwareTrait;
|
||||
|
||||
/** @var \Ibexa\Contracts\Core\Repository\ContentService */
|
||||
private $contentService;
|
||||
|
||||
/** @var \CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\FieldComparatorInterface */
|
||||
private $comparator;
|
||||
|
||||
public function __construct(ContentService $contentService, FieldComparatorInterface $comparator)
|
||||
{
|
||||
$this->contentService = $contentService;
|
||||
$this->comparator = $comparator;
|
||||
}
|
||||
|
||||
public function __invoke($data)
|
||||
{
|
||||
if (!$data instanceof ContentUpdateStructure) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if ($data->getId()) {
|
||||
$content = $this->contentService->loadContent($data->getId(), [$data->getLanguageCode()]);
|
||||
} else {
|
||||
$content = $this->contentService->loadContentByRemoteId($data->getRemoteId(), [$data->getLanguageCode()]);
|
||||
}
|
||||
|
||||
foreach ($data->getFields() as $identifier => $hash) {
|
||||
$field = $content->getField($identifier, $data->getLanguageCode());
|
||||
if (null === $field || !$this->comparator->compare($field, $hash)) {
|
||||
// At least one field is different, continue the dataflow.
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
// All fields are identical, filter this item out.
|
||||
$this->log('info', 'Not modified content skipped', ['id' => $data->getId(), 'remote_id' => $data->getRemoteId()]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function log(string $level, string $message, array $context = [])
|
||||
{
|
||||
if (null === $this->logger) {
|
||||
return;
|
||||
}
|
||||
$this->logger->log($level, $message, $context);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ namespace CodeRhapsodie\EzDataflowBundle\Form;
|
||||
|
||||
use CodeRhapsodie\DataflowBundle\Entity\Job;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
@@ -30,7 +29,7 @@ class CreateOneshotType extends AbstractType
|
||||
'placeholder' => 'coderhapsodie.dataflow.options.placeholder',
|
||||
],
|
||||
])
|
||||
->add('requestedDate', DateTimeType::class, [
|
||||
->add('requestedDate', UserTimezoneAwareDateTimeType::class, [
|
||||
'label' => 'coderhapsodie.dataflow.requestedDate',
|
||||
'years' => range(date('Y'), date('Y') + 5),
|
||||
])
|
||||
|
||||
@@ -7,7 +7,6 @@ namespace CodeRhapsodie\EzDataflowBundle\Form;
|
||||
use CodeRhapsodie\DataflowBundle\Entity\ScheduledDataflow;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
@@ -38,7 +37,7 @@ class CreateScheduledType extends AbstractType
|
||||
],
|
||||
'label' => 'coderhapsodie.dataflow.frequency',
|
||||
])
|
||||
->add('next', DateTimeType::class, [
|
||||
->add('next', UserTimezoneAwareDateTimeType::class, [
|
||||
'years' => range(date('Y'), date('Y') + 5),
|
||||
'label' => 'coderhapsodie.dataflow.create.next',
|
||||
])
|
||||
|
||||
@@ -11,7 +11,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class DataflowTypeChoiceType extends AbstractType
|
||||
{
|
||||
/** @var DataflowTypeRegistryInterface */
|
||||
/** @var \CodeRhapsodie\DataflowBundle\Registry\DataflowTypeRegistryInterface */
|
||||
private $registry;
|
||||
|
||||
public function __construct(DataflowTypeRegistryInterface $registry)
|
||||
|
||||
@@ -6,7 +6,6 @@ namespace CodeRhapsodie\EzDataflowBundle\Form;
|
||||
|
||||
use CodeRhapsodie\DataflowBundle\Entity\ScheduledDataflow;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
@@ -32,7 +31,7 @@ class UpdateScheduledType extends AbstractType
|
||||
],
|
||||
'label' => 'coderhapsodie.dataflow.frequency',
|
||||
])
|
||||
->add('next', DateTimeType::class, [
|
||||
->add('next', UserTimezoneAwareDateTimeType::class, [
|
||||
'years' => range(date('Y'), date('Y') + 5),
|
||||
'label' => 'coderhapsodie.dataflow.update.next',
|
||||
])
|
||||
|
||||
51
src/Form/UserTimezoneAwareDateTimeTransformer.php
Normal file
51
src/Form/UserTimezoneAwareDateTimeTransformer.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Form;
|
||||
|
||||
use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException;
|
||||
use Ibexa\Contracts\Core\Repository\UserPreferenceService;
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
|
||||
class UserTimezoneAwareDateTimeTransformer implements DataTransformerInterface
|
||||
{
|
||||
/** @var \Ibexa\Contracts\Core\Repository\UserPreferenceService */
|
||||
private $userPreferenceService;
|
||||
|
||||
public function __construct(UserPreferenceService $userPreferenceService)
|
||||
{
|
||||
$this->userPreferenceService = $userPreferenceService;
|
||||
}
|
||||
|
||||
public function transform($value)
|
||||
{
|
||||
if (!$value instanceof \DateTimeInterface) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return (new \DateTime('now', $this->userTimezone()))->setTimestamp($value->getTimestamp());
|
||||
}
|
||||
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (!$value instanceof \DateTimeInterface) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$dateTimeWithUserTimeZone = new \DateTime($value->format('Y-m-d H:i:s'), $this->userTimezone());
|
||||
|
||||
return (new \DateTime())->setTimestamp($dateTimeWithUserTimeZone->getTimestamp());
|
||||
}
|
||||
|
||||
private function userTimezone(): \DateTimeZone
|
||||
{
|
||||
try {
|
||||
$tz = $this->userPreferenceService->getUserPreference('timezone')->value ?? 'UTC';
|
||||
} catch (NotFoundException $e) {
|
||||
$tz = 'UTC';
|
||||
}
|
||||
|
||||
return new \DateTimeZone($tz);
|
||||
}
|
||||
}
|
||||
31
src/Form/UserTimezoneAwareDateTimeType.php
Normal file
31
src/Form/UserTimezoneAwareDateTimeType.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Form;
|
||||
|
||||
use Ibexa\Contracts\Core\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 */
|
||||
private $userPreferenceService;
|
||||
|
||||
public function __construct(UserPreferenceService $userPreferenceService)
|
||||
{
|
||||
$this->userPreferenceService = $userPreferenceService;
|
||||
}
|
||||
|
||||
public function getParent()
|
||||
{
|
||||
return DateTimeType::class;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->addModelTransformer(new UserTimezoneAwareDateTimeTransformer($this->userPreferenceService));
|
||||
}
|
||||
}
|
||||
@@ -6,68 +6,56 @@ namespace CodeRhapsodie\EzDataflowBundle\Gateway;
|
||||
|
||||
use CodeRhapsodie\DataflowBundle\Entity\Job;
|
||||
use CodeRhapsodie\DataflowBundle\Repository\JobRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\DBAL\Query\QueryBuilder;
|
||||
|
||||
class JobGateway
|
||||
final class JobGateway
|
||||
{
|
||||
/** @var EntityManagerInterface */
|
||||
private $em;
|
||||
/** @var JobRepository */
|
||||
public const FILTER_NONE = 0;
|
||||
public const FILTER_NON_EMPTY = 1;
|
||||
|
||||
/** @var \CodeRhapsodie\DataflowBundle\Repository\JobRepository */
|
||||
private $jobRepository;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, JobRepository $jobRepository)
|
||||
public function __construct(JobRepository $jobRepository)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->jobRepository = $jobRepository;
|
||||
}
|
||||
|
||||
public function find(int $id): Job
|
||||
public function find(int $id): ?Job
|
||||
{
|
||||
return $this->jobRepository->find($id);
|
||||
}
|
||||
|
||||
public function findForScheduled(int $id): iterable
|
||||
public function getOneshotListQueryForAdmin(): QueryBuilder
|
||||
{
|
||||
return $this->jobRepository->findBy(['scheduledDataflow' => $id], ['requestedDate' => 'desc'], 20);
|
||||
return $this->jobRepository->createQueryBuilder('i')
|
||||
->andWhere('i.scheduled_dataflow_id IS NULL')
|
||||
->addOrderBy('i.requested_date', 'DESC');
|
||||
}
|
||||
|
||||
public function getOneshotListQueryForAdmin(): Query
|
||||
public function getListQueryForAdmin(int $filter): QueryBuilder
|
||||
{
|
||||
$query = $this->jobRepository->createQueryBuilder('i')
|
||||
->andWhere('i.scheduledDataflow IS NULL')
|
||||
->addOrderBy('i.requestedDate', 'DESC');
|
||||
$qb = $this->jobRepository->createQueryBuilder('w')
|
||||
->addOrderBy('w.requested_date', 'DESC')
|
||||
;
|
||||
|
||||
return $query->getQuery();
|
||||
if (self::FILTER_NON_EMPTY === $filter) {
|
||||
$qb->andWhere('w.count > 0');
|
||||
}
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function getListQueryForAdmin(): Query
|
||||
public function getListQueryForScheduleAdmin(int $id): QueryBuilder
|
||||
{
|
||||
$query = $this->jobRepository->createQueryBuilder('w')
|
||||
->addOrderBy('w.requestedDate', 'DESC');
|
||||
|
||||
return $query->getQuery();
|
||||
}
|
||||
|
||||
public function getListQueryForScheduleAdmin(int $id): Query
|
||||
{
|
||||
$query = $this->jobRepository->createQueryBuilder('w')
|
||||
->where('w.scheduledDataflow = :schedule_id')
|
||||
return $this->jobRepository->createQueryBuilder('w')
|
||||
->where('w.scheduled_dataflow_id = :schedule_id')
|
||||
->setParameter('schedule_id', $id)
|
||||
->addOrderBy('w.requestedDate', 'DESC');
|
||||
|
||||
return $query->getQuery();
|
||||
->addOrderBy('w.requested_date', 'DESC');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Job $job
|
||||
*
|
||||
* @throws \Doctrine\ORM\ORMException
|
||||
* @throws \Doctrine\ORM\OptimisticLockException
|
||||
*/
|
||||
public function save(Job $job)
|
||||
{
|
||||
$this->em->persist($job);
|
||||
$this->em->flush();
|
||||
$this->jobRepository->save($job);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,54 +6,39 @@ namespace CodeRhapsodie\EzDataflowBundle\Gateway;
|
||||
|
||||
use CodeRhapsodie\DataflowBundle\Entity\ScheduledDataflow;
|
||||
use CodeRhapsodie\DataflowBundle\Repository\ScheduledDataflowRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\DBAL\Query\QueryBuilder;
|
||||
|
||||
class ScheduledDataflowGateway
|
||||
final class ScheduledDataflowGateway
|
||||
{
|
||||
/** @var EntityManagerInterface */
|
||||
private $em;
|
||||
/** @var ScheduledDataflowRepository */
|
||||
/** @var \CodeRhapsodie\DataflowBundle\Repository\ScheduledDataflowRepository */
|
||||
private $scheduledDataflowRepository;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, ScheduledDataflowRepository $scheduledDataflowRepository)
|
||||
public function __construct(ScheduledDataflowRepository $scheduledDataflowRepository)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->scheduledDataflowRepository = $scheduledDataflowRepository;
|
||||
}
|
||||
|
||||
public function find(int $id): ScheduledDataflow
|
||||
public function find(int $id): ?ScheduledDataflow
|
||||
{
|
||||
return $this->scheduledDataflowRepository->find($id);
|
||||
}
|
||||
|
||||
public function findAllOrderedByLabel(): iterable
|
||||
public function getListQueryForAdmin(): QueryBuilder
|
||||
{
|
||||
return $this->scheduledDataflowRepository->findBy([], ['label' => 'asc']);
|
||||
return $this->scheduledDataflowRepository->createQueryBuilder('s')
|
||||
->addOrderBy('s.label', 'ASC');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ScheduledDataflow $scheduledDataflow
|
||||
*
|
||||
* @throws \Doctrine\ORM\ORMException
|
||||
* @throws \Doctrine\ORM\OptimisticLockException
|
||||
*/
|
||||
public function save(ScheduledDataflow $scheduledDataflow)
|
||||
{
|
||||
$this->em->persist($scheduledDataflow);
|
||||
$this->em->flush();
|
||||
$this->scheduledDataflowRepository->save($scheduledDataflow);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @throws \Doctrine\ORM\ORMException
|
||||
* @throws \Doctrine\ORM\OptimisticLockException
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function delete(int $id): void
|
||||
{
|
||||
$workflow = $this->find($id);
|
||||
|
||||
$this->em->remove($workflow);
|
||||
$this->em->flush();
|
||||
$this->scheduledDataflowRepository->delete($id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ declare(strict_types=1);
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Matcher;
|
||||
|
||||
use CodeRhapsodie\EzDataflowBundle\Exception\NoMatchFoundException;
|
||||
use eZ\Publish\API\Repository\Exceptions\NotFoundException;
|
||||
use eZ\Publish\API\Repository\LocationService;
|
||||
use eZ\Publish\API\Repository\Values\Content\Location;
|
||||
use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException;
|
||||
use Ibexa\Contracts\Core\Repository\LocationService;
|
||||
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
|
||||
|
||||
class LocationMatcher implements LocationMatcherInterface
|
||||
{
|
||||
/** @var LocationService */
|
||||
/** @var \Ibexa\Contracts\Core\Repository\LocationService */
|
||||
private $locationService;
|
||||
|
||||
public function __construct(LocationService $locationService)
|
||||
@@ -22,10 +22,8 @@ class LocationMatcher implements LocationMatcherInterface
|
||||
/**
|
||||
* @param mixed $valueToMatch
|
||||
*
|
||||
* @return Location
|
||||
*
|
||||
* @throws NoMatchFoundException
|
||||
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
|
||||
* @throws \CodeRhapsodie\EzDataflowBundle\Exception\NoMatchFoundException
|
||||
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException
|
||||
*/
|
||||
public function matchLocation($valueToMatch): Location
|
||||
{
|
||||
|
||||
@@ -4,14 +4,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Matcher;
|
||||
|
||||
use eZ\Publish\API\Repository\Values\Content\Location;
|
||||
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
|
||||
|
||||
interface LocationMatcherInterface
|
||||
{
|
||||
/**
|
||||
* @param mixed $valueToMatch
|
||||
*
|
||||
* @return Location
|
||||
*/
|
||||
public function matchLocation($valueToMatch): Location;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ declare(strict_types=1);
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Model;
|
||||
|
||||
use CodeRhapsodie\EzDataflowBundle\Exception\InvalidArgumentTypeException;
|
||||
use eZ\Publish\API\Repository\Values\Content\Location;
|
||||
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
|
||||
use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct;
|
||||
|
||||
class ContentCreateStructure extends ContentStructure
|
||||
{
|
||||
@@ -18,19 +19,16 @@ class ContentCreateStructure extends ContentStructure
|
||||
/**
|
||||
* ContentCreateStructure constructor.
|
||||
*
|
||||
* @param string $contentTypeIdentifier
|
||||
* @param string $languageCode
|
||||
* @param array $locations
|
||||
* A location can be any of the following:
|
||||
* <ul>
|
||||
* <li>an integer, the id of the Location object</li>
|
||||
* <li>a string, the remote id of the Location object</li>
|
||||
* <li>a Location object</li>
|
||||
* </ul>
|
||||
* @param array $fields
|
||||
* @param string|null $remoteId
|
||||
* @param array $locations
|
||||
* A location can be any of the following:
|
||||
* <ul>
|
||||
* <li>an integer, the id of the Location object</li>
|
||||
* <li>a string, the remote id of the Location object</li>
|
||||
* <li>a Location object</li>
|
||||
* <li>a LocationCreateStruct object</li>
|
||||
* </ul>
|
||||
*
|
||||
* @throws InvalidArgumentTypeException
|
||||
* @throws \CodeRhapsodie\EzDataflowBundle\Exception\InvalidArgumentTypeException
|
||||
*/
|
||||
public function __construct(string $contentTypeIdentifier, string $languageCode, array $locations, array $fields, ?string $remoteId = null)
|
||||
{
|
||||
@@ -41,35 +39,28 @@ class ContentCreateStructure extends ContentStructure
|
||||
$this->remoteId = $remoteId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getContentTypeIdentifier(): string
|
||||
{
|
||||
return $this->contentTypeIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getLocations(): array
|
||||
{
|
||||
return $this->locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $locations
|
||||
*
|
||||
* @throws InvalidArgumentTypeException
|
||||
* @throws \CodeRhapsodie\EzDataflowBundle\Exception\InvalidArgumentTypeException
|
||||
*/
|
||||
private function setLocations(array $locations)
|
||||
{
|
||||
foreach ($locations as $locationOrIdOrRemoteId) {
|
||||
if (!is_int($locationOrIdOrRemoteId)
|
||||
&& !is_string($locationOrIdOrRemoteId)
|
||||
&& !$locationOrIdOrRemoteId instanceof Location
|
||||
foreach ($locations as $locationOrIdOrRemoteIdOrStruct) {
|
||||
if (!is_int($locationOrIdOrRemoteIdOrStruct)
|
||||
&& !is_string($locationOrIdOrRemoteIdOrStruct)
|
||||
&& !$locationOrIdOrRemoteIdOrStruct instanceof Location
|
||||
&& !$locationOrIdOrRemoteIdOrStruct instanceof LocationCreateStruct
|
||||
) {
|
||||
throw InvalidArgumentTypeException::create(['int', 'string', Location::class], $locationOrIdOrRemoteId);
|
||||
throw InvalidArgumentTypeException::create(['int', 'string', Location::class, LocationCreateStruct::class], $locationOrIdOrRemoteIdOrStruct);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,25 +15,16 @@ abstract class ContentStructure
|
||||
/** @var array */
|
||||
protected $fields;
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRemoteId(): ?string
|
||||
{
|
||||
return $this->remoteId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLanguageCode(): string
|
||||
{
|
||||
return $this->languageCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFields(): array
|
||||
{
|
||||
return $this->fields;
|
||||
|
||||
@@ -15,13 +15,6 @@ class ContentUpdateStructure extends ContentStructure
|
||||
$this->fields = $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $languageCode
|
||||
* @param array $fields
|
||||
*
|
||||
* @return ContentUpdateStructure
|
||||
*/
|
||||
public static function createForContentId(int $id, string $languageCode, array $fields): self
|
||||
{
|
||||
$struct = new self($languageCode, $fields);
|
||||
@@ -30,13 +23,6 @@ class ContentUpdateStructure extends ContentStructure
|
||||
return $struct;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $remoteId
|
||||
* @param string $languageCode
|
||||
* @param array $fields
|
||||
*
|
||||
* @return ContentUpdateStructure
|
||||
*/
|
||||
public static function createForContentRemoteId(string $remoteId, string $languageCode, array $fields): self
|
||||
{
|
||||
$struct = new self($languageCode, $fields);
|
||||
@@ -45,9 +31,6 @@ class ContentUpdateStructure extends ContentStructure
|
||||
return $struct;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
|
||||
@@ -1,28 +1,46 @@
|
||||
imports:
|
||||
- { resource: services/comparators.yaml }
|
||||
|
||||
services:
|
||||
_defaults:
|
||||
public: false
|
||||
|
||||
coderhapsodie.dataflow.connection: "@ibexa.persistence.connection"
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Controller\DashboardController:
|
||||
public: true
|
||||
tags:
|
||||
- { name: controller.service_arguments }
|
||||
arguments:
|
||||
$jobGateway: '@CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway'
|
||||
$notificationHandler: '@EzSystems\EzPlatformAdminUi\Notification\NotificationHandlerInterface'
|
||||
$scheduledDataflowGateway: '@CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway'
|
||||
calls:
|
||||
- ['setContainer', ['@service_container']]
|
||||
- ['performAccessCheck', []]
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Controller\ScheduledDataflowController:
|
||||
public: true
|
||||
tags:
|
||||
- { name: controller.service_arguments }
|
||||
arguments:
|
||||
$jobGateway: '@CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway'
|
||||
$notificationHandler: '@EzSystems\EzPlatformAdminUi\Notification\NotificationHandlerInterface'
|
||||
$notificationHandler: '@Ibexa\Contracts\AdminUi\Notification\NotificationHandlerInterface'
|
||||
$scheduledDataflowGateway: '@CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway'
|
||||
$translator: '@translator'
|
||||
calls:
|
||||
- [ 'setContainer', [ '@service_container' ] ]
|
||||
- [ 'performAccessCheck', [ ] ]
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Controller\JobController:
|
||||
public: true
|
||||
tags:
|
||||
- { name: controller.service_arguments }
|
||||
arguments:
|
||||
$jobGateway: '@CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway'
|
||||
$notificationHandler: '@EzSystems\EzPlatformAdminUi\Notification\NotificationHandlerInterface'
|
||||
$notificationHandler: '@Ibexa\Contracts\AdminUi\Notification\NotificationHandlerInterface'
|
||||
$translator: '@translator'
|
||||
calls:
|
||||
- [ 'setContainer', [ '@service_container' ] ]
|
||||
- [ 'performAccessCheck', [ ] ]
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Writer\RepositoryWriter:
|
||||
abstract: true
|
||||
@@ -39,23 +57,23 @@ services:
|
||||
CodeRhapsodie\EzDataflowBundle\UserSwitcher\UserSwitcherInterface: '@CodeRhapsodie\EzDataflowBundle\UserSwitcher\UserSwitcher'
|
||||
CodeRhapsodie\EzDataflowBundle\UserSwitcher\UserSwitcher:
|
||||
arguments:
|
||||
$permissionResolver: '@eZ\Publish\API\Repository\PermissionResolver'
|
||||
$userService: '@eZ\Publish\API\Repository\UserService'
|
||||
$permissionResolver: '@Ibexa\Contracts\Core\Repository\PermissionResolver'
|
||||
$userService: '@Ibexa\Contracts\Core\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: '@eZ\Publish\API\Repository\ContentService'
|
||||
$contentTypeService: '@eZ\Publish\API\Repository\ContentTypeService'
|
||||
$contentService: '@Ibexa\Contracts\Core\Repository\ContentService'
|
||||
$contentTypeService: '@Ibexa\Contracts\Core\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: '@eZ\Publish\API\Repository\ContentService'
|
||||
$contentTypeService: '@eZ\Publish\API\Repository\ContentTypeService'
|
||||
$contentService: '@Ibexa\Contracts\Core\Repository\ContentService'
|
||||
$contentTypeService: '@Ibexa\Contracts\Core\Repository\ContentTypeService'
|
||||
$filler: '@CodeRhapsodie\EzDataflowBundle\Core\Field\ContentStructFieldFillerInterface'
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Core\Field\ContentStructFieldFillerInterface: '@CodeRhapsodie\EzDataflowBundle\Core\Field\ContentStructFieldFiller'
|
||||
@@ -66,20 +84,22 @@ services:
|
||||
CodeRhapsodie\EzDataflowBundle\Matcher\LocationMatcherInterface: '@CodeRhapsodie\EzDataflowBundle\Matcher\LocationMatcher'
|
||||
CodeRhapsodie\EzDataflowBundle\Matcher\LocationMatcher:
|
||||
arguments:
|
||||
$locationService: '@eZ\Publish\API\Repository\LocationService'
|
||||
$locationService: '@Ibexa\Contracts\Core\Repository\LocationService'
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Core\Field\DefaultFieldValueCreator:
|
||||
arguments:
|
||||
$fieldTypeService: '@eZ\Publish\API\Repository\FieldTypeService'
|
||||
$fieldTypeService: '@Ibexa\Contracts\Core\Repository\FieldTypeService'
|
||||
tags:
|
||||
- { name: 'coderhapsodie.ezdataflow.field_value_creator', priority: -1000 }
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\EventSubscriber\MenuSubscriber:
|
||||
tags: ['kernel.event_subscriber']
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Factory\ContentStructureFactoryInterface: '@CodeRhapsodie\EzDataflowBundle\Factory\ContentStructureFactory'
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Factory\ContentStructureFactory:
|
||||
arguments:
|
||||
$contentService: '@eZ\Publish\API\Repository\ContentService'
|
||||
$contentService: '@Ibexa\Contracts\Core\Repository\ContentService'
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Form\DataflowTypeChoiceType:
|
||||
arguments:
|
||||
@@ -95,30 +115,63 @@ services:
|
||||
CodeRhapsodie\EzDataflowBundle\Form\CreateOneshotType:
|
||||
tags: ['form.type']
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Form\UserTimezoneAwareDateTimeType:
|
||||
arguments:
|
||||
$userPreferenceService: '@Ibexa\Contracts\Core\Repository\UserPreferenceService'
|
||||
tags: ['form.type']
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Gateway\ScheduledDataflowGateway:
|
||||
arguments:
|
||||
$em: '@doctrine.orm.default_entity_manager'
|
||||
$scheduledDataflowRepository: '@CodeRhapsodie\DataflowBundle\Repository\ScheduledDataflowRepository'
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Gateway\JobGateway:
|
||||
arguments:
|
||||
$em: '@doctrine.orm.default_entity_manager'
|
||||
$jobRepository: '@CodeRhapsodie\DataflowBundle\Repository\JobRepository'
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Tab\RepeatingTab:
|
||||
parent: EzSystems\EzPlatformAdminUi\Tab\AbstractTab
|
||||
parent: Ibexa\Contracts\AdminUi\Tab\AbstractTab
|
||||
public: false
|
||||
arguments:
|
||||
$httpKernelRuntime: '@twig.runtime.httpkernel'
|
||||
tags:
|
||||
- {name: ezplatform.tab, group: coderhapsodie-ezdataflow}
|
||||
- {name: ibexa.admin_ui.tab, group: coderhapsodie-ezdataflow}
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Tab\OneshotTab:
|
||||
parent: EzSystems\EzPlatformAdminUi\Tab\AbstractTab
|
||||
parent: Ibexa\Contracts\AdminUi\Tab\AbstractTab
|
||||
public: false
|
||||
arguments:
|
||||
$httpKernelRuntime: '@twig.runtime.httpkernel'
|
||||
tags:
|
||||
- {name: ezplatform.tab, group: coderhapsodie-ezdataflow}
|
||||
- {name: ibexa.admin_ui.tab, group: coderhapsodie-ezdataflow}
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Tab\HistoryTab:
|
||||
parent: EzSystems\EzPlatformAdminUi\Tab\AbstractTab
|
||||
parent: Ibexa\Contracts\AdminUi\Tab\AbstractTab
|
||||
public: false
|
||||
arguments:
|
||||
$httpKernelRuntime: '@twig.runtime.httpkernel'
|
||||
tags:
|
||||
- {name: ezplatform.tab, group: coderhapsodie-ezdataflow}
|
||||
- {name: ibexa.admin_ui.tab, group: coderhapsodie-ezdataflow}
|
||||
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Filter\NotModifiedContentFilter:
|
||||
arguments:
|
||||
$contentService: '@Ibexa\Contracts\Core\Repository\ContentService'
|
||||
$comparator: '@CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\FieldComparatorInterface'
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\FieldComparatorInterface: '@CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\DelegatorFieldComparator'
|
||||
CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\DelegatorFieldComparator:
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\AbstractFieldComparator:
|
||||
arguments:
|
||||
$fieldTypeService: '@Ibexa\Contracts\Core\Repository\FieldTypeService'
|
||||
abstract: true
|
||||
|
||||
_cr.admin_tabs.ezdataflow_group:
|
||||
parent: Ibexa\AdminUi\Component\TabsComponent
|
||||
autowire: true
|
||||
autoconfigure: false
|
||||
public: false
|
||||
arguments:
|
||||
$groupIdentifier: 'coderhapsodie-ezdataflow'
|
||||
tags:
|
||||
- { name: ibexa.admin_ui.component, group: 'coderhapsodie-ezdataflow' }
|
||||
|
||||
42
src/Resources/config/services/comparators.yaml
Normal file
42
src/Resources/config/services/comparators.yaml
Normal file
@@ -0,0 +1,42 @@
|
||||
services:
|
||||
CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\SimpleFieldComparator:
|
||||
parent: 'CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\AbstractFieldComparator'
|
||||
tags:
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezauthor' }
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezboolean' }
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezcountry' }
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezdate' }
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezdatetime' }
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezemail' }
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezfloat' }
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezinteger' }
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezisbn' }
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezkeyword' }
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezobjectrelation' }
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezobjectrelationlist' }
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezrichtext' }
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezselection' }
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'eztext' }
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezstring' }
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'eztime' }
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'eztags' }
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\UrlFieldComparator:
|
||||
parent: 'CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\AbstractFieldComparator'
|
||||
tags:
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezurl' }
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\NovaSEOMetasFieldComparator:
|
||||
parent: 'CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\AbstractFieldComparator'
|
||||
tags:
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'novaseometas' }
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\MatrixFieldComparator:
|
||||
parent: 'CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\AbstractFieldComparator'
|
||||
tags:
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezmatrix' }
|
||||
|
||||
CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\MapLocationFieldComparator:
|
||||
parent: 'CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\AbstractFieldComparator'
|
||||
tags:
|
||||
- { name: 'coderhapsodie.ezdataflow.field_comparator', fieldType: 'ezgmaplocation' }
|
||||
1
src/Resources/translations/menu.en.yaml
Normal file
1
src/Resources/translations/menu.en.yaml
Normal file
@@ -0,0 +1 @@
|
||||
coderhapsodie.ezdataflow: eZ Dataflow
|
||||
1
src/Resources/translations/menu.fr.yaml
Normal file
1
src/Resources/translations/menu.fr.yaml
Normal file
@@ -0,0 +1 @@
|
||||
coderhapsodie.ezdataflow: eZ Dataflow
|
||||
@@ -21,10 +21,15 @@ 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.start: 'Started on'
|
||||
coderhapsodie.ezdataflow.history.list.end: 'Finished on'
|
||||
coderhapsodie.ezdataflow.history.list.view: 'View details'
|
||||
coderhapsodie.ezdataflow.history.list.status: Status
|
||||
coderhapsodie.ezdataflow.history.filter.label: Filter results
|
||||
coderhapsodie.ezdataflow.history.filter.none: All results
|
||||
coderhapsodie.ezdataflow.history.filter.non_empty_only: Only non empty
|
||||
coderhapsodie.ezdataflow.history.filter.with_error_only: Only with errors
|
||||
coderhapsodie.ezdataflow.job.status.pending: Pending
|
||||
coderhapsodie.ezdataflow.job.status.running: Running
|
||||
coderhapsodie.ezdataflow.job.status.complete: Completed
|
||||
@@ -36,16 +41,18 @@ coderhapsodie.ezdataflow.history.details.request: 'Requested on'
|
||||
coderhapsodie.ezdataflow.history.details.status: Status
|
||||
coderhapsodie.ezdataflow.history.details.start: 'Started on'
|
||||
coderhapsodie.ezdataflow.history.details.end: 'Finished on'
|
||||
coderhapsodie.ezdataflow.history.details.count: 'Items count'
|
||||
coderhapsodie.ezdataflow.history.details.count: 'Items successfully processed'
|
||||
coderhapsodie.ezdataflow.history.details.options: 'Run options'
|
||||
coderhapsodie.ezdataflow.history.details.errors: Errors
|
||||
coderhapsodie.ezdataflow.history.details.type: 'Name of the dataflow executed'
|
||||
coderhapsodie.ezdataflow.history.details.log: 'View log'
|
||||
coderhapsodie.ezdataflow.workflow.repeating.new.title: 'Add a new repeating dataflow'
|
||||
coderhapsodie.ezdataflow.workflow.new.cancel: Cancel
|
||||
coderhapsodie.ezdataflow.workflow.new.submit: Create
|
||||
coderhapsodie.ezdataflow.history.list.empty: 'No execution yet.'
|
||||
coderhapsodie.ezdataflow.workflow.list.empty: 'No repeating workflow configured yet'
|
||||
coderhapsodie.ezdataflow.workflow.history.title: 'Execution history'
|
||||
coderhapsodie.ezdataflow.workflow.log.title: 'Execution log'
|
||||
coderhapsodie.ezdataflow.workflow.list.delete: Delete
|
||||
coderhapsodie.ezdataflow.workflow.delete: 'Are you sure you want to delete this dataflow schedule?'
|
||||
coderhapsodie.ezdataflow.workflow.create.success: 'Dataflow schedule successfully added.'
|
||||
@@ -74,3 +81,4 @@ coderhapsodie.ezdataflow.workflow.edit.submit: Save
|
||||
coderhapsodie.dataflow.update.next: 'Next execution'
|
||||
coderhapsodie.ezdataflow.workflow.edit.success: 'Dataflow schedule successfully updated.'
|
||||
coderhapsodie.ezdataflow.workflow.edit.error: 'An error occurred during the dataflow schedule update: "%message%".'
|
||||
coderhapsodie.ezdataflow.notfound: 'Requested data is not found'
|
||||
|
||||
@@ -21,10 +21,15 @@ 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.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é
|
||||
@@ -40,24 +45,26 @@ coderhapsodie.ezdataflow.history.details.count: 'Nombre d''objets mis à jour'
|
||||
coderhapsodie.ezdataflow.history.details.options: 'Options de lancement'
|
||||
coderhapsodie.ezdataflow.history.details.errors: Erreurs
|
||||
coderhapsodie.ezdataflow.history.details.type: 'Nom du dataflow exécuté'
|
||||
coderhapsodie.ezdataflow.history.details.log: 'Voir le log'
|
||||
coderhapsodie.ezdataflow.workflow.repeating.new.title: 'Nouvel programmation d''un dataflow récurrent'
|
||||
coderhapsodie.ezdataflow.workflow.new.cancel: Annuler
|
||||
coderhapsodie.ezdataflow.workflow.new.submit: Créer
|
||||
coderhapsodie.ezdataflow.history.list.empty: 'Aucune exécution pour le moment.'
|
||||
coderhapsodie.ezdataflow.workflow.list.empty: 'Aucun dataflow n''a été programmé.'
|
||||
coderhapsodie.ezdataflow.workflow.history.title: 'Historique des exécutions'
|
||||
coderhapsodie.ezdataflow.workflow.log.title: 'Log de l''exécution'
|
||||
coderhapsodie.ezdataflow.workflow.list.delete: Supprimer
|
||||
coderhapsodie.ezdataflow.workflow.delete: 'Êtes-vous sûr de vouloir supprimer ce dataflow ?'
|
||||
coderhapsodie.ezdataflow.workflow.create.success: 'La programmation du dataflow a bien été ajoutée.'
|
||||
coderhapsodie.ezdataflow.workflow.create.error: 'Une erreur est survenue lors de l''ajout de la programamtion du dataflow : "%message%".'
|
||||
coderhapsodie.ezdataflow.workflow.delete.success: 'La programmation du dataflow a bien été supprimé.'
|
||||
coderhapsodie.ezdataflow.workflow.create.error: 'Une erreur est survenue lors de l''ajout de la programmation du dataflow : "%message%".'
|
||||
coderhapsodie.ezdataflow.workflow.delete.success: 'La programmation du dataflow a bien été supprimée.'
|
||||
coderhapsodie.ezdataflow.workflow.delete.error: 'Une erreur est survenue lors de la suppression de la programmation du dataflow : "%message%".'
|
||||
coderhapsodie.ezdataflow.workflow.oneshot.new.title: 'Nouvelle exécution ponctuelle'
|
||||
coderhapsodie.ezdataflow.job.create.success: 'Votre exécution a bien été ajoutée.'
|
||||
coderhapsodie.ezdataflow.job.create.error: 'Une erreur est survenue lors de l''ajout de l''exécution : "%message%".'
|
||||
coderhapsodie.dataflow.label: 'Nom de la programamtion du dataflow'
|
||||
coderhapsodie.dataflow.label: 'Nom de la programmation du dataflow'
|
||||
coderhapsodie.dataflow.oneshot.label: 'Nom de l''exécution ponctuelle du dataflow'
|
||||
coderhapsodie.dataflow.dataflowType: 'Dataflow a exécuter'
|
||||
coderhapsodie.dataflow.dataflowType: 'Dataflow à exécuter'
|
||||
coderhapsodie.dataflow.options: 'Options passées au dataflow (YAML)'
|
||||
coderhapsodie.dataflow.options.title: 'Entrez les options comme un tableau clé/valeur YAML'
|
||||
coderhapsodie.dataflow.options.placeholder: "option1: valeur1\noption2: valeur2\n"
|
||||
@@ -70,5 +77,6 @@ coderhapsodie.dataflow.create.enabled: 'Activé ?'
|
||||
coderhapsodie.ezdataflow.workflow.repeating.edit.title: "Édition de la programmation d'un dataflow"
|
||||
coderhapsodie.ezdataflow.workflow.edit.submit: Sauvegarder
|
||||
coderhapsodie.dataflow.update.next: 'Prochaine exécution'
|
||||
coderhapsodie.ezdataflow.workflow.edit.success: 'La programmation du dataflow a été mis à jour avec succès.'
|
||||
coderhapsodie.ezdataflow.workflow.edit.success: 'La programmation du dataflow a été mise à jour avec succès.'
|
||||
coderhapsodie.ezdataflow.workflow.edit.error: 'Une erreur est survenue lors de la modification de la programmation du dataflow : "%message%".'
|
||||
coderhapsodie.ezdataflow.notfound: 'Les données demandées sont introuvables'
|
||||
|
||||
@@ -1,7 +1,60 @@
|
||||
<h2>{{ 'coderhapsodie.ezdataflow.history.title'|trans }}</h2>
|
||||
{%- block content -%}
|
||||
{% set choices = [
|
||||
{
|
||||
'value': 0,
|
||||
'label': 'coderhapsodie.ezdataflow.history.filter.none'|trans,
|
||||
},
|
||||
{
|
||||
'value': 1,
|
||||
'label': 'coderhapsodie.ezdataflow.history.filter.non_empty_only'|trans,
|
||||
},
|
||||
] %}
|
||||
|
||||
<div class="ez-table-header">
|
||||
<div class="ez-table-header__headline">{{ 'coderhapsodie.ezdataflow.history.list.title'|trans }}</div>
|
||||
</div>
|
||||
{% set source %}
|
||||
<select id="ezdataflow_history_filter" class="form-control ibexa-input">
|
||||
{% for choice in choices %}
|
||||
<option value="{{ choice.value }}">
|
||||
{{ choice.label }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endset %}
|
||||
|
||||
{% include '@ezdesign/ezdataflow/parts/tab/job_list.html.twig' with {identifier: 'ezdataflow_history_results', paginate_route: 'coderhapsodie.ezdataflow.history'} %}
|
||||
{% set actions %}
|
||||
{{ include('@ibexadesign/ui/component/dropdown/dropdown.html.twig', {
|
||||
source: source,
|
||||
choices: choices,
|
||||
value: filter,
|
||||
}) }}
|
||||
{% endset %}
|
||||
|
||||
{{ 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 () {
|
||||
// Manage ajax pagination
|
||||
document.getElementById('ezdataflow_history_filter').addEventListener('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;
|
||||
})
|
||||
;
|
||||
});
|
||||
})
|
||||
</script>
|
||||
{%- endblock -%}
|
||||
|
||||
@@ -1,68 +1,101 @@
|
||||
{% extends "@ezdesign/layout.html.twig" %}
|
||||
{% extends "@ibexadesign/ui/layout.html.twig" %}
|
||||
|
||||
{% block body_class %}ibexa-ezdataflow{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
{% include '@ezdesign/parts/breadcrumbs.html.twig' with { items: [
|
||||
{% include '@ibexadesign/ui/breadcrumbs.html.twig' with { items: [
|
||||
{ value: 'breadcrumb.admin'|trans(domain='messages')|desc('Admin') },
|
||||
{ value: 'coderhapsodie.ezdataflow'|trans|desc('EzDataflow') }
|
||||
]} %}
|
||||
{% endblock %}
|
||||
|
||||
{% block page_title %}
|
||||
{% include '@ezdesign/parts/page_title.html.twig' with {
|
||||
{% block header %}
|
||||
{% include '@ibexadesign/ui/page_title.html.twig' with {
|
||||
title: 'coderhapsodie.ezdataflow'|trans|desc('EzDataflow'),
|
||||
iconName: 'workflow'
|
||||
} %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{{ ez_platform_tabs('coderhapsodie-ezdataflow', {}, '@ezdesign/ezdataflow/parts/tab/ezdataflow.html.twig') }}
|
||||
{{ ibexa_render_component_group('coderhapsodie-ezdataflow', {'filter': app.request.query.get('filter', 0)}) }}
|
||||
|
||||
<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>
|
||||
{% 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 %}
|
||||
|
||||
<script>
|
||||
$('#ez-modal--history-details').modal({keyboard: false, show: false});
|
||||
$('.history-details-aware').delegate('.modal-history-details', 'click', function(e) {
|
||||
e.preventDefault();
|
||||
$('#modal_content-details').html('');
|
||||
$('#ez-modal--history-details').modal('show');
|
||||
$.ajax(this.href, {
|
||||
success: function(result) {
|
||||
$('#modal_content-details').html(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
$('#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');
|
||||
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;
|
||||
})
|
||||
;
|
||||
|
||||
$('#ez-tab-list-coderhapsodie-ezdataflow li a:eq(1)').addClass('active');
|
||||
$('#ez-tab-list-content-coderhapsodie-ezdataflow .tab-pane:eq(1)').addClass('active');
|
||||
return;
|
||||
}
|
||||
})
|
||||
}
|
||||
bindModalLinks('modal-history-details', '.modal-history-details');
|
||||
bindModalLinks('modal-history-log', '.modal-history-log');
|
||||
bindModalLinks('modal-history', '.modal-history');
|
||||
|
||||
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') {
|
||||
$('#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');
|
||||
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');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block stylesheets %}
|
||||
{{ parent() }}
|
||||
<style>
|
||||
.ezdataflow-date .ibexa-dropdown {
|
||||
min-width: 0;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,50 +1,57 @@
|
||||
{% form_theme form 'bootstrap_3_layout.html.twig' %}
|
||||
|
||||
<h2>{{ 'coderhapsodie.ezdataflow.oneshot.title'|trans }}</h2>
|
||||
|
||||
<div class="ez-table-header">
|
||||
<div class="ez-table-header__headline">{{ 'coderhapsodie.ezdataflow.oneshot.list.title'|trans }}</div>
|
||||
<div>
|
||||
<button type="button" class="btn btn-primary btn-modal-launcher" data-toggle="modal" data-target="#ez-modal--new-oneshot">
|
||||
<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>
|
||||
{%- 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>
|
||||
</svg>
|
||||
<span class="ibexa-btn__label">
|
||||
{{ 'coderhapsodie.ezdataflow.workflow.new.submit'|trans|desc('Create') }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ include('@ezdesign/ezdataflow/parts/tab/job_list.html.twig', {identifier: 'ezdataflow_oneshot_history', paginate_route: 'coderhapsodie.ezdataflow.oneshot'}) }}
|
||||
{{ 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,
|
||||
}) }}
|
||||
|
||||
{% if form is defined %}
|
||||
<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>
|
||||
{% if form is defined %}
|
||||
{{ include('@ibexadesign/ezdataflow/parts/form_modal.html.twig', {
|
||||
'id': 'modal-new-oneshot',
|
||||
'form': form,
|
||||
'mode': 'oneshot'
|
||||
}) }}
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
$('#ez-modal--new-oneshot').on('submit', 'form', function (e) {
|
||||
e.preventDefault();
|
||||
url = $(this).attr('action');
|
||||
data = new FormData(this);
|
||||
$.ajax({
|
||||
'type': 'POST',
|
||||
'url': url,
|
||||
'data': data,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function (result) {
|
||||
if (result.redirect) {
|
||||
window.location = result.redirect;
|
||||
return;
|
||||
}
|
||||
|
||||
let obj = $(result.form).find('.modal-body');
|
||||
$('#ez-modal--new-oneshot .modal-body').html($(obj).html());
|
||||
}
|
||||
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;
|
||||
}
|
||||
const node = document.createElement('div');
|
||||
node.innerHTML = result.form;
|
||||
document.querySelector('#modal-new-oneshot .form-fields').innerHTML = node.querySelector('.form-fields').innerHTML;
|
||||
})
|
||||
;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
{% endif %}
|
||||
{%- endblock -%}
|
||||
|
||||
@@ -1,176 +1,158 @@
|
||||
{% form_theme form 'bootstrap_3_layout.html.twig' %}
|
||||
{%- 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') }}
|
||||
{% 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 %}
|
||||
|
||||
<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>
|
||||
<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);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
<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>
|
||||
const bindFormSubmit = (modalId) => {
|
||||
document.querySelector('#'+modalId+' 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;
|
||||
}
|
||||
const node = document.createElement('div');
|
||||
node.innerHTML = result.form;
|
||||
document.querySelector('#'+modalId+' .form-fields').innerHTML = node.querySelector('.form-fields').innerHTML;
|
||||
})
|
||||
;
|
||||
});
|
||||
};
|
||||
bindFormSubmit('modal-new-scheduled');
|
||||
bindFormSubmit('modal-edit-scheduled');
|
||||
|
||||
<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>
|
||||
const editModal = document.getElementById('modal-edit-scheduled')
|
||||
document.addEventListener('click', (e) => {
|
||||
const link = e.target.closest('.modal-edit');
|
||||
if (link) {
|
||||
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;
|
||||
})
|
||||
;
|
||||
|
||||
<script>
|
||||
$('#ez-modal--history').modal({keyboard: false, show: false});
|
||||
$('.modal-history').each(function (index, elem) {
|
||||
$(elem).click(function (e) {
|
||||
e.preventDefault();
|
||||
$('#modal_content').html('');
|
||||
$('#ez-modal--history').modal('show');
|
||||
$.ajax(elem.href, {
|
||||
success: function (result) {
|
||||
$('#modal_content').html(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#ez-modal--history .close').click(function () {
|
||||
$('#ez-modal--history').modal('hide');
|
||||
});
|
||||
|
||||
$('#ez-modal--delete-workflow').modal({keyboard: true, show: false});
|
||||
$('.modal-delete').each(function (index, elem) {
|
||||
$(elem).click(function (e) {
|
||||
e.preventDefault();
|
||||
$('#delete-modal--workflow-name').html($(elem).data('name'));
|
||||
$('#ez-modal--delete-workflow').modal('show');
|
||||
$('#ez-modal--delete-workflow-confirm').data('target', $(elem).data('path'));
|
||||
});
|
||||
});
|
||||
$('#ez-modal--delete-workflow-confirm').click(function () {
|
||||
let target = $(this).data('target');
|
||||
if (target && target !== '') {
|
||||
$.ajax(target, {
|
||||
method: 'POST',
|
||||
complete: function () {
|
||||
$('#ez-modal--delete-workflow').modal('hide');
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$('#ez-modal--edit-scheduled').modal({keyboard: true, show: false});
|
||||
$('.modal-edit').each(function (index, elem) {
|
||||
$(elem).click(function (e) {
|
||||
e.preventDefault();
|
||||
$('#schedule_edit').html('');
|
||||
$('#ez-modal--edit-scheduled').modal('show');
|
||||
$.ajax(elem.href, {
|
||||
success: function (result) {
|
||||
if (result.redirect) {
|
||||
window.location = result.redirect;
|
||||
return;
|
||||
}
|
||||
|
||||
$('#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);
|
||||
}
|
||||
});
|
||||
});
|
||||
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;
|
||||
|
||||
$('#ez-modal--new-scheduled form').on('submit', function (e) {
|
||||
e.preventDefault();
|
||||
url = $(this).attr('action');
|
||||
data = new FormData(this);
|
||||
$.ajax({
|
||||
'type': 'POST',
|
||||
'url': url,
|
||||
'data': data,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function (result) {
|
||||
if (result.redirect) {
|
||||
window.location = result.redirect;
|
||||
return;
|
||||
}
|
||||
let obj = $(result.form).find('.modal-body');
|
||||
$('#ez-modal--new-scheduled .modal-body').html($(obj).html());
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</script>
|
||||
{%- endblock -%}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<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}} %}
|
||||
{% 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}
|
||||
} %}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{% import '@ezdesign/ezdataflow/macros.twig' as macros %}
|
||||
{% import '@ibexadesign/ezdataflow/macros.twig' as macros %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container ez-main-container">
|
||||
<div class="container ez-main-container history-details-aware">
|
||||
{% if item is not null %}
|
||||
<h2>{{ 'coderhapsodie.ezdataflow.history.job.title'|trans }} n°{{ item.id }}</h2>
|
||||
|
||||
<table class="table ez-table ez-table--list">
|
||||
@@ -19,7 +20,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ 'coderhapsodie.ezdataflow.history.details.request'|trans }}</td>
|
||||
<td>{{ item.requestedDate|date('d/m/Y H:i:s') }}</td>
|
||||
<td>{{ date(item.requestedDate)|ibexa_short_datetime }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ 'coderhapsodie.ezdataflow.history.details.status'|trans }}</td>
|
||||
@@ -27,11 +28,11 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ 'coderhapsodie.ezdataflow.history.details.start'|trans }}</td>
|
||||
<td>{{ item.startTime|date('d/m/Y H:i:s') }}</td>
|
||||
<td>{{ item.startTime ? date(item.startTime)|ibexa_short_datetime : '—' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ 'coderhapsodie.ezdataflow.history.details.end'|trans }}</td>
|
||||
<td>{{ item.endTime|date('d/m/Y H:i:s') }}</td>
|
||||
<td>{{ item.endTime ? date(item.endTime)|ibexa_short_datetime : '—' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ 'coderhapsodie.ezdataflow.history.details.count'|trans }}</td>
|
||||
@@ -51,5 +52,8 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="ez-table-no-content">{{ 'coderhapsodie.ezdataflow.notfound'|trans }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{% for line in log %}
|
||||
<p>{{ line|nl2br }}</p>
|
||||
{% endfor %}
|
||||
@@ -0,0 +1,10 @@
|
||||
{% 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 %}
|
||||
@@ -0,0 +1,23 @@
|
||||
{% 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 %}
|
||||
@@ -1,24 +0,0 @@
|
||||
{% 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) }}
|
||||
@@ -1,27 +0,0 @@
|
||||
<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>
|
||||
|
||||
@@ -1,67 +1,117 @@
|
||||
{% set id = identifier|default('ezdataflow_history_results') %}
|
||||
|
||||
{% import '@ezdesign/ezdataflow/macros.twig' as macros %}
|
||||
{% import '@ibexadesign/ezdataflow/macros.twig' as macros %}
|
||||
|
||||
<div id="loading_{{ id }}" class="text-center d-none">
|
||||
<div id="loading_{{ id }}" class="text-center" hidden>
|
||||
<svg class="ez-icon ez-icon--extra-large">
|
||||
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#spinner"></use>
|
||||
<use xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xlink:href="{{ ibexa_icon_path('spinner') }}"></use>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div id="{{ id }}" class="history-details-aware">
|
||||
{% 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.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>{{ job.requestedDate|date('d/m/Y H:i:s') }}</td>
|
||||
<td>{{ job.count|default('-') }}</td>
|
||||
<td>{{ job.startTime ? job.startTime|date('d/m/Y H:i:s') : '-' }}</td>
|
||||
<td>{{ job.endTime ? job.endTime|date('d/m/Y H:i:s') : '-' }}</td>
|
||||
<td>{{ 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>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="ez-table-no-content">{{ 'coderhapsodie.ezdataflow.history.list.empty'|trans }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% 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.haveToPaginate %}
|
||||
<div class="ez-pagination justify-content-center align-items-center ez-pagination__spacing">
|
||||
{{ pagerfanta(pager, 'ez', {routeName: paginate_route, routeParams: paginate_params|default({})}) }}
|
||||
</div>
|
||||
{% include '@ibexadesign/ui/pagination.html.twig' with {
|
||||
'pager': pager,
|
||||
'paginaton_params': {
|
||||
'routeName': paginate_route,
|
||||
'routeParams': paginate_params|default({})
|
||||
}
|
||||
} %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Manage ajax pagination
|
||||
$('#{{ id }}').delegate('.ez-pagination a', 'click', function (e) {
|
||||
e.preventDefault();
|
||||
$('#loading_{{ id }}').removeClass('d-none');
|
||||
$('#{{ id }}')
|
||||
.html('')
|
||||
.load(this.href + ' #{{ id }}>*', null, function () {
|
||||
$('#loading_{{ id }}').addClass('d-none');
|
||||
});
|
||||
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;
|
||||
})
|
||||
;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,55 +1,152 @@
|
||||
{% if items|length %}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ 'coderhapsodie.ezdataflow.workflow.list.name'|trans }}</th>
|
||||
<th>{{ 'coderhapsodie.ezdataflow.workflow.list.frequency'|trans }}</th>
|
||||
<th>{{ 'coderhapsodie.ezdataflow.workflow.list.next_execution'|trans }}</th>
|
||||
<th>{{ 'coderhapsodie.ezdataflow.workflow.list.enabled'|trans }}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in items %}
|
||||
<tr>
|
||||
<td>{{ item.label }}</td>
|
||||
<td>{{ item.frequency }}</td>
|
||||
<td>{{ item.next|date('d/m/Y H:i:s') }}</td>
|
||||
<td>{{ ('coderhapsodie.ezdataflow.' ~ (item.enabled ? 'yes' : 'no'))|trans }}</td>
|
||||
<td class="ez-table__cell ez-table__cell--has-action-btns text-right">
|
||||
<a href="{{ path('coderhapsodie.ezdataflow.history.workflow', {id: item.id}) }}" class="btn btn-icon mx-2 modal-history" title="{{ 'coderhapsodie.ezdataflow.workflow.list.history'|trans }}">
|
||||
<svg class="ez-icon ez-icon--small-medium">
|
||||
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#history"></use>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="{{ path('coderhapsodie.ezdataflow.workflow.edit', {id: item.id}) }}" type="button" class="btn btn-icon mx-2 modal-edit" title="{{ 'coderhapsodie.ezdataflow.workflow.list.edit'|trans }}">
|
||||
<svg class="ez-icon ez-icon--small-medium ez-icon-edit">
|
||||
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#edit"></use>
|
||||
</svg>
|
||||
</a>
|
||||
{% if item.enabled %}
|
||||
<a href="{{ path('coderhapsodie.ezdataflow.workflow.disable', {id: item.id}) }}" class="btn btn-icon mx-2" title="{{ 'coderhapsodie.ezdataflow.workflow.list.disable'|trans }}">
|
||||
<svg class="ez-icon ez-icon--small-medium" style="fill: #f7d000;">
|
||||
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#logout"></use>
|
||||
</svg>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ path('coderhapsodie.ezdataflow.workflow.enable', {id: item.id}) }}" class="btn btn-icon mx-2" title="{{ 'coderhapsodie.ezdataflow.workflow.list.enable'|trans }}">
|
||||
<svg class="ez-icon ez-icon--small-medium" style="fill: #00825c;">
|
||||
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#approved"></use>
|
||||
</svg>
|
||||
</a>
|
||||
{% endif %}
|
||||
<button type="button" class="btn btn-icon mx-2 ez-btn--content-trash modal-delete" data-name="{{ item.label }}" data-path="{{ path('coderhapsodie.ezdataflow.workflow.delete', {id: item.id}) }}" title="{{ 'coderhapsodie.ezdataflow.workflow.list.delete'|trans }}">
|
||||
<svg class="ez-icon ez-icon--small-medium ez-icon-trash">
|
||||
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/bundles/ezplatformadminui/img/ez-icons.svg#trash"></use>
|
||||
{% set id = identifier|default('ezdataflow_schedule_results') %}
|
||||
|
||||
<div id="loading_{{ id }}" class="text-center" hidden>
|
||||
<svg class="ez-icon ez-icon--extra-large">
|
||||
<use xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xlink:href="{{ ibexa_icon_path('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>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="ez-table-no-content">{{ 'coderhapsodie.ezdataflow.workflow.list.empty'|trans }}</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
|
||||
{% if pager.haveToPaginate %}
|
||||
{% include '@ibexadesign/ui/pagination.html.twig' with {
|
||||
'pager': pager,
|
||||
'paginaton_params': {
|
||||
'routeName': paginate_route,
|
||||
'routeParams': paginate_params|default({})
|
||||
}
|
||||
} %}
|
||||
{% 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;
|
||||
})
|
||||
;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Security;
|
||||
|
||||
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider\PolicyProviderInterface;
|
||||
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider\YamlPolicyProvider;
|
||||
use Ibexa\Bundle\Core\DependencyInjection\Security\PolicyProvider\PolicyProviderInterface;
|
||||
use Ibexa\Bundle\Core\DependencyInjection\Security\PolicyProvider\YamlPolicyProvider;
|
||||
|
||||
class PolicyProvider extends YamlPolicyProvider implements PolicyProviderInterface
|
||||
{
|
||||
|
||||
@@ -5,8 +5,8 @@ declare(strict_types=1);
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Tab;
|
||||
|
||||
use CodeRhapsodie\EzDataflowBundle\Controller\DashboardController;
|
||||
use EzSystems\EzPlatformAdminUi\Tab\AbstractControllerBasedTab;
|
||||
use EzSystems\EzPlatformAdminUi\Tab\OrderedTabInterface;
|
||||
use Ibexa\Contracts\AdminUi\Tab\AbstractControllerBasedTab;
|
||||
use Ibexa\Contracts\AdminUi\Tab\OrderedTabInterface;
|
||||
use Symfony\Component\HttpKernel\Controller\ControllerReference;
|
||||
|
||||
class HistoryTab extends AbstractControllerBasedTab implements OrderedTabInterface
|
||||
@@ -16,7 +16,7 @@ class HistoryTab extends AbstractControllerBasedTab implements OrderedTabInterfa
|
||||
*/
|
||||
public function getControllerReference(array $parameters): ControllerReference
|
||||
{
|
||||
return new ControllerReference(DashboardController::class.'::history');
|
||||
return new ControllerReference(DashboardController::class.'::history', [], $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,8 +5,8 @@ declare(strict_types=1);
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Tab;
|
||||
|
||||
use CodeRhapsodie\EzDataflowBundle\Controller\DashboardController;
|
||||
use EzSystems\EzPlatformAdminUi\Tab\AbstractControllerBasedTab;
|
||||
use EzSystems\EzPlatformAdminUi\Tab\OrderedTabInterface;
|
||||
use Ibexa\Contracts\AdminUi\Tab\AbstractControllerBasedTab;
|
||||
use Ibexa\Contracts\AdminUi\Tab\OrderedTabInterface;
|
||||
use Symfony\Component\HttpKernel\Controller\ControllerReference;
|
||||
|
||||
class OneshotTab extends AbstractControllerBasedTab implements OrderedTabInterface
|
||||
|
||||
@@ -5,8 +5,8 @@ declare(strict_types=1);
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Tab;
|
||||
|
||||
use CodeRhapsodie\EzDataflowBundle\Controller\DashboardController;
|
||||
use EzSystems\EzPlatformAdminUi\Tab\AbstractControllerBasedTab;
|
||||
use EzSystems\EzPlatformAdminUi\Tab\OrderedTabInterface;
|
||||
use Ibexa\Contracts\AdminUi\Tab\AbstractControllerBasedTab;
|
||||
use Ibexa\Contracts\AdminUi\Tab\OrderedTabInterface;
|
||||
use Symfony\Component\HttpKernel\Controller\ControllerReference;
|
||||
|
||||
class RepeatingTab extends AbstractControllerBasedTab implements OrderedTabInterface
|
||||
|
||||
@@ -4,22 +4,21 @@ declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\UserSwitcher;
|
||||
|
||||
use eZ\Publish\API\Repository\PermissionResolver;
|
||||
use eZ\Publish\API\Repository\UserService;
|
||||
use eZ\Publish\API\Repository\Values\User\UserReference;
|
||||
use Ibexa\Contracts\Core\Repository\PermissionResolver;
|
||||
use Ibexa\Contracts\Core\Repository\UserService;
|
||||
|
||||
class UserSwitcher implements UserSwitcherInterface
|
||||
{
|
||||
/** @var PermissionResolver */
|
||||
/** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */
|
||||
private $permissionResolver;
|
||||
|
||||
/** @var UserService */
|
||||
/** @var \Ibexa\Contracts\Core\Repository\UserService */
|
||||
private $userService;
|
||||
|
||||
/** @var string|int */
|
||||
private $adminLoginOrId;
|
||||
|
||||
/** @var UserReference[] */
|
||||
/** @var \Ibexa\Contracts\Core\Repository\Values\User\UserReference[] */
|
||||
private $userStack;
|
||||
|
||||
public function __construct(PermissionResolver $permissionResolver, UserService $userService, $adminLoginOrId)
|
||||
|
||||
@@ -9,9 +9,6 @@ trait UserSwitcherAwareTrait
|
||||
/** @var UserSwitcherInterface */
|
||||
protected $userSwitcher;
|
||||
|
||||
/**
|
||||
* @param UserSwitcherInterface $userSwitcher
|
||||
*/
|
||||
public function setUserSwitcher(UserSwitcherInterface $userSwitcher): void
|
||||
{
|
||||
$this->userSwitcher = $userSwitcher;
|
||||
|
||||
@@ -4,19 +4,22 @@ declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Writer;
|
||||
|
||||
use CodeRhapsodie\DataflowBundle\DataflowType\Writer\DelegateWriterInterface;
|
||||
use CodeRhapsodie\EzDataflowBundle\Core\Content\ContentCreatorInterface;
|
||||
use CodeRhapsodie\EzDataflowBundle\Core\Content\ContentUpdaterInterface;
|
||||
use CodeRhapsodie\EzDataflowBundle\Model\ContentCreateStructure;
|
||||
use CodeRhapsodie\EzDataflowBundle\Model\ContentStructure;
|
||||
use CodeRhapsodie\EzDataflowBundle\Model\ContentUpdateStructure;
|
||||
use CodeRhapsodie\DataflowBundle\DataflowType\Writer\WriterInterface;
|
||||
use Psr\Log\LoggerAwareTrait;
|
||||
|
||||
class ContentWriter extends RepositoryWriter implements WriterInterface
|
||||
class ContentWriter extends RepositoryWriter implements DelegateWriterInterface
|
||||
{
|
||||
/** @var ContentCreatorInterface */
|
||||
use LoggerAwareTrait;
|
||||
|
||||
/** @var \CodeRhapsodie\EzDataflowBundle\Core\Content\ContentCreatorInterface */
|
||||
private $creator;
|
||||
|
||||
/** @var ContentUpdaterInterface */
|
||||
/** @var \CodeRhapsodie\EzDataflowBundle\Core\Content\ContentUpdaterInterface */
|
||||
private $updater;
|
||||
|
||||
public function __construct(ContentCreatorInterface $creator, ContentUpdaterInterface $updater)
|
||||
@@ -26,20 +29,45 @@ class ContentWriter extends RepositoryWriter implements WriterInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContentStructure $item
|
||||
* @param \CodeRhapsodie\EzDataflowBundle\Model\ContentStructure $item
|
||||
*/
|
||||
public function write($item)
|
||||
{
|
||||
if (!$item instanceof ContentStructure) {
|
||||
$this->log('warning', 'Data is not a ContentStucture');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($item instanceof ContentCreateStructure) {
|
||||
$this->log('info', 'Save content', [
|
||||
'content_type' => $item->getContentTypeIdentifier(),
|
||||
'content_location' => $item->getLocations(),
|
||||
]);
|
||||
$this->creator->createFromStructure($item);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($item instanceof ContentUpdateStructure) {
|
||||
$this->log('info', 'Update content', ['id' => $item->getId(), 'remote_id' => $item->getRemoteId()]);
|
||||
$this->updater->updateFromStructure($item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports($item): bool
|
||||
{
|
||||
return $item instanceof ContentStructure;
|
||||
}
|
||||
|
||||
private function log(string $level, string $message, array $context = [])
|
||||
{
|
||||
if (null === $this->logger) {
|
||||
return;
|
||||
}
|
||||
$this->logger->log($level, $message, $context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace CodeRhapsodie\EzDataflowBundle\Writer;
|
||||
|
||||
use CodeRhapsodie\DataflowBundle\DataflowType\Writer\WriterInterface;
|
||||
use CodeRhapsodie\EzDataflowBundle\UserSwitcher\UserSwitcherAwareInterface;
|
||||
use CodeRhapsodie\EzDataflowBundle\UserSwitcher\UserSwitcherAwareTrait;
|
||||
use CodeRhapsodie\DataflowBundle\DataflowType\Writer\WriterInterface;
|
||||
|
||||
abstract class RepositoryWriter implements WriterInterface, UserSwitcherAwareInterface
|
||||
{
|
||||
|
||||
49
tests/Core/FieldComparator/DelegatorFieldComparatorTest.php
Normal file
49
tests/Core/FieldComparator/DelegatorFieldComparatorTest.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
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 PHPUnit\Framework\TestCase;
|
||||
|
||||
class DelegatorFieldComparatorTest extends TestCase
|
||||
{
|
||||
/** @var \CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\DelegatorFieldComparator */
|
||||
private $delegatorFieldComparator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$type1FieldComparatorMock = $this->createMock(FieldComparatorInterface::class);
|
||||
$type1FieldComparatorMock->method('compare')->willReturnCallback(function (Field $field, $hash) {
|
||||
return $hash === 'rightValue1';
|
||||
});
|
||||
$type2FieldComparatorMock = $this->createMock(FieldComparatorInterface::class);
|
||||
$type2FieldComparatorMock->method('compare')->willReturnCallback(function (Field $field, $hash) {
|
||||
return $hash === 'rightValue2';
|
||||
});
|
||||
$this->delegatorFieldComparator = new DelegatorFieldComparator();
|
||||
$this->delegatorFieldComparator->registerDelegateFieldComparator($type1FieldComparatorMock, 'type1');
|
||||
$this->delegatorFieldComparator->registerDelegateFieldComparator($type2FieldComparatorMock, 'type2');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider fieldProvider
|
||||
*/
|
||||
public function testField(string $type, bool $expected, $hash)
|
||||
{
|
||||
$field = new Field(['fieldTypeIdentifier' => $type]);
|
||||
$return = $this->delegatorFieldComparator->compare($field, $hash);
|
||||
|
||||
$this->assertSame($expected, $return);
|
||||
}
|
||||
|
||||
public function fieldProvider(): iterable
|
||||
{
|
||||
yield ['type1', true, 'rightValue1'];
|
||||
yield ['type1', false, 'wrongValue'];
|
||||
yield ['type2', true, 'rightValue2'];
|
||||
yield ['type2', false, 'wrongValue'];
|
||||
yield ['otherType', false, 'rightValue1'];
|
||||
}
|
||||
}
|
||||
138
tests/Filter/NotModifiedContentFilterTest.php
Normal file
138
tests/Filter/NotModifiedContentFilterTest.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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 PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class NotModifiedContentFilterTest extends TestCase
|
||||
{
|
||||
/** @var \Ibexa\Contracts\Core\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $contentServiceMock;
|
||||
|
||||
/** @var \CodeRhapsodie\EzDataflowBundle\Core\FieldComparator\FieldComparatorInterface|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $comparatorMock;
|
||||
|
||||
/** @var \CodeRhapsodie\EzDataflowBundle\Filter\NotModifiedContentFilter */
|
||||
private $notModifiedContentFilter;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->contentServiceMock = $this->createMock(ContentService::class);
|
||||
$this->comparatorMock = $this->createMock(FieldComparatorInterface::class);
|
||||
$this->notModifiedContentFilter = new NotModifiedContentFilter($this->contentServiceMock, $this->comparatorMock);
|
||||
}
|
||||
|
||||
public function testNotContentUpdateStructure()
|
||||
{
|
||||
$data = 'notAStruct';
|
||||
$returnValue = ($this->notModifiedContentFilter)($data);
|
||||
|
||||
$this->assertSame($data, $returnValue);
|
||||
}
|
||||
|
||||
public function testIdenticalContent()
|
||||
{
|
||||
$id = 10;
|
||||
$field1 = 'field1';
|
||||
$value1 = 'value1';
|
||||
$field2 = 'field2';
|
||||
$value2 = 'value2';
|
||||
$contentField1 = new Field();
|
||||
$contentField2 = new Field();
|
||||
$data = ContentUpdateStructure::createForContentId($id, 'lang', [
|
||||
$field1 => $value1,
|
||||
$field2 => $value2,
|
||||
]);
|
||||
$content = $this->createMock(Content::class);
|
||||
|
||||
$content
|
||||
->expects($this->exactly(2))
|
||||
->method('getField')
|
||||
->withConsecutive([$field1], [$field2])
|
||||
->willReturnOnConsecutiveCalls($contentField1, $contentField2)
|
||||
;
|
||||
$this->contentServiceMock
|
||||
->expects($this->once())
|
||||
->method('loadContent')
|
||||
->with($id)
|
||||
->willReturn($content)
|
||||
;
|
||||
$this->comparatorMock
|
||||
->expects($this->exactly(2))
|
||||
->method('compare')
|
||||
->withConsecutive([$contentField1, $value1], [$contentField2, $value2])
|
||||
->willReturn(true)
|
||||
;
|
||||
|
||||
$return = ($this->notModifiedContentFilter)($data);
|
||||
|
||||
$this->assertFalse($return);
|
||||
}
|
||||
|
||||
public function testDifferentContent()
|
||||
{
|
||||
$id = 10;
|
||||
$field1 = 'field1';
|
||||
$value1 = 'value1';
|
||||
$field2 = 'field2';
|
||||
$value2 = 'value2';
|
||||
$field3 = 'field3';
|
||||
$value3 = 'value3';
|
||||
$contentField1 = new Field();
|
||||
$contentField2 = new Field();
|
||||
$data = ContentUpdateStructure::createForContentId($id, 'lang', [
|
||||
$field1 => $value1,
|
||||
$field2 => $value2,
|
||||
$field3 => $value3,
|
||||
]);
|
||||
$content = $this->createMock(Content::class);
|
||||
|
||||
$content
|
||||
->expects($this->exactly(2))
|
||||
->method('getField')
|
||||
->withConsecutive([$field1], [$field2])
|
||||
->willReturnOnConsecutiveCalls($contentField1, $contentField2)
|
||||
;
|
||||
$this->contentServiceMock
|
||||
->expects($this->once())
|
||||
->method('loadContent')
|
||||
->with($id)
|
||||
->willReturn($content)
|
||||
;
|
||||
$this->comparatorMock
|
||||
->expects($this->exactly(2))
|
||||
->method('compare')
|
||||
->withConsecutive([$contentField1, $value1], [$contentField2, $value2])
|
||||
->willReturnOnConsecutiveCalls(true, false)
|
||||
;
|
||||
|
||||
$return = ($this->notModifiedContentFilter)($data);
|
||||
|
||||
$this->assertSame($data, $return);
|
||||
}
|
||||
|
||||
public function testLoadEmptyByRemoteId()
|
||||
{
|
||||
$remoteId = 'abc';
|
||||
$data = ContentUpdateStructure::createForContentRemoteId($remoteId, 'lang', []);
|
||||
|
||||
$this->contentServiceMock
|
||||
->expects($this->once())
|
||||
->method('loadContentByRemoteId')
|
||||
->with($remoteId)
|
||||
->willReturn(new Content())
|
||||
;
|
||||
$return = ($this->notModifiedContentFilter)($data);
|
||||
|
||||
$this->assertFalse($return);
|
||||
}
|
||||
}
|
||||
11
tests/bootstrap.php
Normal file
11
tests/bootstrap.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
// Skip autoloading if already done by phpunit alias (including from meta repo if this is vendor)
|
||||
if (defined('PHPUNIT_COMPOSER_INSTALL')) {
|
||||
return;
|
||||
}
|
||||
$autoloadFile = __DIR__ . '/../vendor/autoload.php';
|
||||
if (!file_exists($autoloadFile)) {
|
||||
throw new RuntimeException('Install dependencies to run test suite.');
|
||||
}
|
||||
require_once $autoloadFile;
|
||||
Reference in New Issue
Block a user