From ec4ed1ffd7a80e00f7b30a84c64937d702ac36fb Mon Sep 17 00:00:00 2001 From: Myddleware Date: Thu, 11 Jan 2024 09:53:55 +0100 Subject: [PATCH 001/452] Task parallelization (#1109) * Add column to lock a rule (send and read actions) Signed-off-by: myddleware * Manage read lock for rule Signed-off-by: myddleware * Manage lock on rule send action Signed-off-by: myddleware * Add jobLock field into document table Signed-off-by: myddleware * Fix bug Signed-off-by: myddleware * Add lock on the document Signed-off-by: myddleware * Add job lock index (and apply the other index changes) Signed-off-by: myddleware * Manage document lock Signed-off-by: myddleware * Manage lock on read and send functions Signed-off-by: myddleware * Manage unlock Signed-off-by: myddleware * Change crontab scheduler function Signed-off-by: myddleware * Remove parameter force job Remove lock on job (we can now run several job in the same time) Signed-off-by: myddleware * Remove unlock for rule and document on normal process as the locks should alredy be removed Signed-off-by: myddleware * Unlock rule in case of reading error Signed-off-by: myddleware * Fix lock on rule and document Signed-off-by: myddleware * Add indexes on rule table Signed-off-by: myddleware * Fix bug for mass rerun error Signed-off-by: myddleware * Add unset lock in case of fatal error Signed-off-by: myddleware * Manage unset for the synchro command Signed-off-by: myddleware * Change the place of the begin transaction to include the create document Signed-off-by: myddleware * Remove some transactions Signed-off-by: myddleware * Add parameter refreshDate to method updateDocumentData Signed-off-by: myddleware * Continue to run other documents when a document is locked. Signed-off-by: myddleware * Fix bug on Airtable : if data has been removed by a customcode, the process for this record has to be stopped Signed-off-by: myddleware * feat: reduce size of description, running and max * feat: working cron results * feat: edit running instances & max instance * feat: WIP: sort by id * feat import future crontabj.s * feat: edit crontab page * feat: adapt function to handle dates * feat: add order info, and sort table date modified * feat: order flag * feat: output * feat: working run at * feat: runtime, comment run at * feat: status code * feat: translation * feat: export js logic to dedicated file * Change the way to lock the send action, the lock isn't on the rule anymore but on the document Signed-off-by: myddleware * Change lock management using queries Signed-off-by: myddleware * Remove query on send lock Signed-off-by: myddleware * Fix close job Signed-off-by: myddleware * Fix bug on document selection with lock Signed-off-by: myddleware * SugarCRM : updatre connector Signed-off-by: myddleware * Fix bugs Signed-off-by: myddleware * BugFix : lock table document during selection Signed-off-by: myddleware * Fix : bug on transaction not closed Signed-off-by: myddleware * SugarCRM : manage team name field Solution : manage error incase of simulation (no rule in parameter) Bugfix : set status error sending if we couldn't connect to the target application Signed-off-by: myddleware --------- Signed-off-by: myddleware Co-authored-by: AlexMyddleware <106162060+AlexMyddleware@users.noreply.github.com> --- assets/app.js | 1 + assets/js/crontab.js | 96 +++ src/Command/CheckJobCommand.php | 2 +- src/Command/CronRunCommand.php | 32 +- src/Command/MassActionCommand.php | 4 +- src/Command/NotificationCommand.php | 2 +- src/Command/ReadRecordCommand.php | 7 +- src/Command/RerunErrorCommand.php | 7 +- src/Command/SynchroCommand.php | 18 +- src/Controller/ApiController.php | 6 +- src/Controller/JobSchedulerController.php | 8 +- src/Controller/TaskController.php | 13 + src/Entity/Document.php | 35 +- src/Entity/Rule.php | 25 +- src/Form/JobSchedulerCronType.php | 3 + src/Manager/DocumentManager.php | 340 +++++++--- src/Manager/JobManager.php | 50 +- src/Manager/RuleManager.php | 630 +++++++++++------- src/Repository/DocumentRepository.php | 13 + src/Repository/RuleRepository.php | 13 + src/Solutions/airtable.php | 44 +- src/Solutions/database.php | 3 +- src/Solutions/moodle.php | 2 +- src/Solutions/solution.php | 2 +- src/Solutions/sugarcrm.php | 94 ++- templates/JobScheduler/crontab_list.html.twig | 6 +- templates/JobScheduler/edit_crontab.html.twig | 14 + templates/JobScheduler/show_crontab.html.twig | 168 +++-- translations/messages.en.yml | 8 + webpack.config.js | 1 + yarn.lock | 3 +- 31 files changed, 1125 insertions(+), 525 deletions(-) create mode 100644 assets/js/crontab.js diff --git a/assets/app.js b/assets/app.js index 1edce9b14..3b5673b6a 100755 --- a/assets/app.js +++ b/assets/app.js @@ -41,6 +41,7 @@ require('./js/task.js') require('./js/connector.js') require('./js/smtp.js') require('./js/filter.js') +require('./js/crontab.js') require('./js/rule_relation_filter.js') diff --git a/assets/js/crontab.js b/assets/js/crontab.js new file mode 100644 index 000000000..4d29dc2a4 --- /dev/null +++ b/assets/js/crontab.js @@ -0,0 +1,96 @@ +// Creates a function that will be called when a button with the class crontab_class +// is clicked. The function will send a request to the server for the function with the following name crontab_show + +console.log('crontabbou.js'); + +// if an element with the class table-head-result is clicked, then call the function sortTable with the id of the element as an argument +$(document).on('click', '.table-head-result', function() { + console.log('click on table-head-result'); + var id = $(this).attr('id'); + console.log(id); + sortTable(id); +}); + +console.log('coucou camille'); +function sortTable(n) { + console.log('inside function sortTable'); + var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0; + table = document.getElementById("myTable"); + switching = true; + dir = "asc"; + + // Convert n to a number + var columnIndex = parseInt(n, 10); // Parse n as an integer + console.log("Column Index: ", columnIndex); + + console.log(n) + + // Update the h5 content to show the current sorting direction + var orderInfo = document.getElementById("crontab-order-info"); + orderInfo.innerText = "Sorting: " + (dir === "asc" ? "Ascending" : "Descending"); + + while (switching) { + switching = false; + rows = table.rows; + + for (i = 1; i < (rows.length - 1); i++) { + shouldSwitch = false; + x = rows[i].getElementsByTagName("TD")[n]; + y = rows[i + 1].getElementsByTagName("TD")[n]; + + // Check if we are dealing with dates + if (columnIndex === 1 || columnIndex === 2 || columnIndex === 4) { // assuming date columns are 1 (Date created) and 2 (Date update) and 4 (Run at) + var xDate = new Date(x.innerHTML); + var yDate = new Date(y.innerHTML); + } else { + var xContent = isNaN(parseInt(x.innerHTML)) ? x.innerHTML.toLowerCase() : parseInt(x.innerHTML); + var yContent = isNaN(parseInt(y.innerHTML)) ? y.innerHTML.toLowerCase() : parseInt(y.innerHTML); + } + + if (columnIndex === 1 || columnIndex === 2 || columnIndex === 4) { // date columns + if (dir == "asc") { + if (xDate > yDate) { + shouldSwitch = true; + break; + } + } else if (dir == "desc") { + if (xDate < yDate) { + shouldSwitch = true; + break; + } + } + } else { // other columns + if (dir == "asc") { + if (xContent > yContent) { + shouldSwitch = true; + break; + } + } else if (dir == "desc") { + if (xContent < yContent) { + shouldSwitch = true; + break; + } + } + } + } + + if (shouldSwitch) { + rows[i].parentNode.insertBefore(rows[i + 1], rows[i]); + switching = true; + switchcount++; + } else { + if (switchcount == 0 && dir == "asc") { + dir = "desc"; + switching = true; + // Update the h5 content when the direction changes + orderInfo.innerText = "Sorting: Descending"; + } + } + } + + // Final update in case no switching was done and the direction is already descending + if (!switching && dir === "desc") { + orderInfo.innerText = "Sorting: Descending"; + } + } + \ No newline at end of file diff --git a/src/Command/CheckJobCommand.php b/src/Command/CheckJobCommand.php index 665226cca..6d8bd6394 100644 --- a/src/Command/CheckJobCommand.php +++ b/src/Command/CheckJobCommand.php @@ -66,7 +66,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $period = $input->getArgument('period'); - $data = $this->jobManager->initJob('checkJob', 1); + $data = $this->jobManager->initJob('checkJob'); if (false === $data['success']) { $output->writeln('0;'.$data['message'].''); $this->logger->error($data['message']); diff --git a/src/Command/CronRunCommand.php b/src/Command/CronRunCommand.php index 0c7633184..3fd171cb2 100644 --- a/src/Command/CronRunCommand.php +++ b/src/Command/CronRunCommand.php @@ -56,10 +56,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (!($entity)) { throw new Exception("Couldn't fetch Cronjobs"); } - $valueCron = $entity->getValue(); + $valueCron = $entity->getValue(); if ( $valueCron == 1 - && !empty($valueCron)) + && !empty($valueCron)) { $jobsToRun = $jobRepo->findAll(); @@ -109,16 +109,25 @@ protected function execute(InputInterface $input, OutputInterface $output): int } else { $style->success('cronjob started successfully and is running in background'); } - - $style->info($job->getFullCommand().' : Begin '.date('Y-m-d h:i:s')); - $this->waitProcesses($processes); - $style->info($job->getFullCommand().' : End '.date('Y-m-d h:i:s')); - $processes = []; } - $style->success('All jobs are finished.'); + sleep(1); + + $style->section('Summary'); + + if (count($processes) > 0) { + $style->text('waiting for all running jobs ...'); + + // wait for all processes + $this->waitProcesses($processes); + + $style->success('All jobs are finished.'); + } else { + $style->info('No jobs were executed. See reasons below.'); + } return CronJobResult::EXIT_CODE_SUCCEEDED; - }else{ + + } else { $style->error('Your crontabs are disabled'); return CronJobResult::EXIT_CODE_FAILED; } @@ -138,7 +147,7 @@ public function waitProcesses(array $processes): void try { $process->checkTimeout(); - if (true === $process->isRunning()) { + if ($process->isRunning() === true) { break; } } catch (ProcessTimedOutException $e) { @@ -170,8 +179,7 @@ private function runJob(CronJob $job): Process $process->disableOutput(); $timeout = $this->commandHelper->getTimeout(); - - if (null !== $timeout && $timeout > 0) { + if ($timeout !== null && $timeout > 0) { $process->setTimeout($timeout); } diff --git a/src/Command/MassActionCommand.php b/src/Command/MassActionCommand.php index b89e8c032..12ec28b0d 100644 --- a/src/Command/MassActionCommand.php +++ b/src/Command/MassActionCommand.php @@ -99,7 +99,6 @@ protected function configure() ->addArgument('action', InputArgument::REQUIRED, 'Action (rerun, cancel, remove, restore or changeStatus)') ->addArgument('dataType', InputArgument::REQUIRED, 'Data type (rule or document)') ->addArgument('ids', InputArgument::REQUIRED, 'Rule or document ids') // id séparés par des ";" - ->addArgument('force', InputArgument::OPTIONAL, 'Force run even if another task is running.') ->addArgument('forceAll', InputArgument::OPTIONAL, 'Set Y to process action on all documents (not only open and error ones)') ->addArgument('fromStatus', InputArgument::OPTIONAL, 'Get all document with this status(Only with changeStatus action)') ->addArgument('toStatus', InputArgument::OPTIONAL, 'Set this status (Only with changeStatus action)') @@ -119,7 +118,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int $fromStatus = $input->getArgument('fromStatus'); $toStatus = $input->getArgument('toStatus'); $api = $input->getArgument('api'); - $force = $input->getArgument('force') ? $input->getArgument('force') : false; // to avoid unwanted apostrophes in SQL queries $action = str_replace('\'', '', $action); $dataType = str_replace('\'', '', $dataType); @@ -134,7 +132,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $paramJobString = "Mass $action on data type $dataType"; - $data = $this->jobManager->initJob($paramJobString, $force); + $data = $this->jobManager->initJob($paramJobString); if (false === $data['success']) { $output->writeln('1;'.$data['message'].''); diff --git a/src/Command/NotificationCommand.php b/src/Command/NotificationCommand.php index 26b445f7e..bd56b8904 100644 --- a/src/Command/NotificationCommand.php +++ b/src/Command/NotificationCommand.php @@ -77,7 +77,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int // Standard notification else { try { - $data = $this->jobManager->initJob('notification', '1'); + $data = $this->jobManager->initJob('notification'); if (false === $data['success']) { $output->writeln('0;'.$data['message'].''); diff --git a/src/Command/ReadRecordCommand.php b/src/Command/ReadRecordCommand.php index 783343c71..96ad0ee6f 100644 --- a/src/Command/ReadRecordCommand.php +++ b/src/Command/ReadRecordCommand.php @@ -60,7 +60,6 @@ protected function configure() ->addArgument('ruleId', InputArgument::REQUIRED, 'Rule used to read the records') ->addArgument('filterQuery', InputArgument::REQUIRED, 'Filter used to read data in the source application, eg : id') ->addArgument('filterValues', InputArgument::REQUIRED, 'Values corresponding to the fileter separated by comma, eg : 1256,4587') - ->addArgument('force', InputArgument::OPTIONAL, 'Force run even if another task is running.') ->addArgument('api', InputArgument::OPTIONAL, 'Call from API') ; } @@ -74,10 +73,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int $ruleId = $input->getArgument('ruleId'); $filterQuery = $input->getArgument('filterQuery'); $filterValues = $input->getArgument('filterValues'); - $force = $input->getArgument('force'); - if (empty($force)) { - $force = false; - } $rule = $this->ruleRepository->findOneBy(['id' => $ruleId, 'deleted' => false]); if (null === $rule) { @@ -88,7 +83,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int // Set the API value $this->jobManager->setApi((bool) $api); - $data = $this->jobManager->initJob('read records with filter '.$filterQuery.' IN ('.$filterValues.')', $force); + $data = $this->jobManager->initJob('read records with filter '.$filterQuery.' IN ('.$filterValues.')'); if (false === $data['success']) { $output->writeln('0;'.$data['message'].''); $this->logger->error($data['message']); diff --git a/src/Command/RerunErrorCommand.php b/src/Command/RerunErrorCommand.php index 86e1e5a77..e3e08d5bb 100644 --- a/src/Command/RerunErrorCommand.php +++ b/src/Command/RerunErrorCommand.php @@ -58,7 +58,6 @@ protected function configure() ->setDescription('Synchronisation des données') ->addArgument('limit', InputArgument::REQUIRED, 'Nombre maximum de flux en erreur traité') ->addArgument('attempt', InputArgument::REQUIRED, 'Nombre maximum de tentative') - ->addArgument('force', InputArgument::OPTIONAL, 'Force run even if another task is running.') ->addArgument('api', InputArgument::OPTIONAL, 'Call from API') ; } @@ -71,15 +70,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int $limit = $input->getArgument('limit'); $attempt = $input->getArgument('attempt'); $api = $input->getArgument('api'); - $force = $input->getArgument('force'); - if (empty($force)) { - $force = false; - } // Set the API value $this->jobManager->setApi((bool) $api); - $data = $this->jobManager->initJob('Rerun error : limit '.$limit.', attempt '.$attempt, $force); + $data = $this->jobManager->initJob('Rerun error : limit '.$limit.', attempt '.$attempt); if (false === $data['success']) { $output->writeln('0;'.$data['message'].''); diff --git a/src/Command/SynchroCommand.php b/src/Command/SynchroCommand.php index f3a59701f..b141366a0 100644 --- a/src/Command/SynchroCommand.php +++ b/src/Command/SynchroCommand.php @@ -35,6 +35,7 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\Persistence\ManagerRegistry; class SynchroCommand extends Command { @@ -54,7 +55,8 @@ public function __construct( DocumentManager $documentManager, RuleManager $ruleManager, EntityManagerInterface $entityManager, - DocumentRepository $documentRepository + DocumentRepository $documentRepository, + ManagerRegistry $registry ) { parent::__construct(); $this->logger = $logger; @@ -63,6 +65,7 @@ public function __construct( $this->ruleManager = $ruleManager; $this->documentManager = $documentManager; $this->documentRepository = $documentRepository; + $this->registry = $registry; } protected function configure() @@ -71,7 +74,7 @@ protected function configure() ->setName('myddleware:synchro') ->setDescription('Execute all active Myddleware transfer rules') ->addArgument('rule', InputArgument::REQUIRED, 'Rule id, you can put several rule id separated by coma') - ->addArgument('force', InputArgument::OPTIONAL, 'Force run even if another task is running.') + ->addArgument('force', InputArgument::OPTIONAL, 'Force run even if the rule is inactive.') ->addArgument('api', InputArgument::OPTIONAL, 'Call from API') ; } @@ -93,7 +96,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int // Clear message in case this task is run by jobscheduler. In this case message has to be refreshed. $this->jobManager->message = ''; $this->jobManager->setApi($api); - $data = $this->jobManager->initJob('Synchro : '.$rule, $force); + $data = $this->jobManager->initJob('Synchro : '.$rule); if (true === $data['success']) { $output->writeln('1;'.$this->jobManager->getId()); // Not removed, user for manual job and webservices @@ -142,6 +145,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->jobManager->sendDocuments(); } catch (\Exception $e) { $this->jobManager->message .= 'Error rule '.$value.' '.$e->getMessage(); + // Reset entity manager in case it has been closed by the exception + if (!$this->entityManager->isOpen()) { + $this->entityManager = $this->registry->resetManager(); + } + // Unset all the read and send locks of the rule in case of fatal error (if the losk correspond to the current job) + if (!$this->jobManager->unsetRuleLock()) { + $this->jobManager->message .= 'Failed to unset the lock for the rule '.$value.'. '; + } } } } @@ -152,6 +163,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } catch (\Exception $e) { $this->jobManager->message .= $e->getMessage(); } + // Close job if it has been created if (true === $this->jobManager->createdJob) { $this->jobManager->closeJob(); diff --git a/src/Controller/ApiController.php b/src/Controller/ApiController.php index 39b577b53..280835fff 100644 --- a/src/Controller/ApiController.php +++ b/src/Controller/ApiController.php @@ -67,7 +67,6 @@ public function synchroAction(Request $request): JsonResponse // Get input data $data = json_decode($request->getContent(), true); - $force = !(('ALL' === $data['rule'])); // Check parameter if (empty($data['rule'])) { @@ -79,7 +78,7 @@ public function synchroAction(Request $request): JsonResponse $application->setAutoExit(false); $arguments = [ 'command' => 'myddleware:synchro', - 'force' => $force, + 'force' => (empty($data['force']) ? false : true), 'api' => 1, '--env' => $this->env, ]; @@ -148,7 +147,6 @@ public function readRecordAction(Request $request): JsonResponse $application->setAutoExit(false); $arguments = [ 'command' => 'myddleware:readrecord', - 'force' => 1, 'api' => 1, '--env' => $this->env, ]; @@ -331,7 +329,6 @@ public function massActionAction(Request $request): JsonResponse $application->setAutoExit(false); $arguments = [ 'command' => 'myddleware:massaction', - 'force' => 1, 'api' => 1, '--env' => $this->env, ]; @@ -401,7 +398,6 @@ public function rerunErrorAction(Request $request): JsonResponse $application->setAutoExit(false); $arguments = [ 'command' => 'myddleware:rerunerror', - 'force' => 1, 'api' => 1, '--env' => $this->env, ]; diff --git a/src/Controller/JobSchedulerController.php b/src/Controller/JobSchedulerController.php index 3ea951633..032c5a175 100644 --- a/src/Controller/JobSchedulerController.php +++ b/src/Controller/JobSchedulerController.php @@ -19,6 +19,7 @@ use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Component\Form\Extension\Core\Type\TextType; use Shapecode\Bundle\CronBundle\Entity\CronJob as CronJob; +use Shapecode\Bundle\CronBundle\Entity\CronJobResult; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -428,11 +429,14 @@ public function showCrontab($id): Response throw $this->createNotFoundException('Unable to find crontab entity.'); } - /** @var UserRepository $userRepository */ - $userRepository = $this->entityManager->getRepository(User::class); + // Fetch the CronJobResults and sort them by 'id' in ascending order + $entityResult = $this->entityManager->getRepository(CronJobResult::class)->findBy( + ['cronJob' => $id], + ); return $this->render('JobScheduler/show_crontab.html.twig', [ 'entity' => $entity, + 'entityResult' => $entityResult, ]); } diff --git a/src/Controller/TaskController.php b/src/Controller/TaskController.php index f51667d4f..57a259677 100644 --- a/src/Controller/TaskController.php +++ b/src/Controller/TaskController.php @@ -28,6 +28,8 @@ use App\Entity\Config; use App\Entity\Job; use App\Entity\Log; +use App\Entity\Document; +use App\Entity\Rule; use App\Manager\JobManager; use App\Repository\DocumentRepository; use App\Repository\JobRepository; @@ -74,6 +76,9 @@ public function __construct( $this->params[$config->getName()] = $config->getvalue(); } } + if (empty($this->DocumentRepository)) { + $this->documentRepository = $this->entityManager->getRepository(Document::class); + } } /** @@ -170,6 +175,14 @@ public function stopTask(Job $taskStop): RedirectResponse $log->setMessage('The task has been manually stopped. '); $log->setJob($taskStop); $em->persist($log); + + // Remove lock on document locked by this job + $this->documentRepository->removeLock($taskStop->getId()); + + // Remove lock (send and read) on rule locked by this job + $ruleRepository = $this->entityManager->getRepository(Rule::class); + $ruleRepository->removeLock($taskStop->getId()); + $em->flush(); return $this->redirect($this->generateURL('task_view', ['id' => $taskStop->getId()])); diff --git a/src/Entity/Document.php b/src/Entity/Document.php index c968cbb21..1586a15cd 100755 --- a/src/Entity/Document.php +++ b/src/Entity/Document.php @@ -34,13 +34,17 @@ /** * @ORM\Entity(repositoryClass="App\Repository\DocumentRepository") * @ORM\Table(name="document", indexes={ - * @ORM\Index(name="index_ruleid_status", columns={"rule_id","status"}), - * @ORM\Index(name="index_parent_id", columns={"parent_id"}), - * @ORM\Index(name="global_status", columns={"global_status"}), - * @ORM\Index(name="source_id", columns={"source_id"}), - * @ORM\Index(name="target_id", columns={"target_id"}), - * @ORM\Index(name="rule_id", columns={"rule_id"}), - * @ORM\Index(name="date_modified", columns={"date_modified"}) + * @ORM\Index(name="index_rule_gbstatus_status", columns={"rule_id","global_status","status","deleted"}), + * @ORM\Index(name="index_gbstatus", columns={"global_status","deleted"}), + * @ORM\Index(name="index_parent_id", columns={"parent_id","deleted"}), + * @ORM\Index(name="index_rule_source", columns={"rule_id","source_id","deleted"}), + * @ORM\Index(name="index_rule_target", columns={"rule_id","target_id","deleted"}), + * @ORM\Index(name="index_rule_date_modified", columns={"rule_id","date_modified","deleted"}), + * @ORM\Index(name="index_rule_status_modified", columns={"rule_id","status","source_date_modified","deleted"}), + * @ORM\Index(name="index_source_id", columns={"source_id","deleted"}), + * @ORM\Index(name="index_target_id", columns={"target_id","deleted"}), + * @ORM\Index(name="index_date_modified", columns={"date_modified","deleted"}), + * @ORM\Index(name="index_job_lock", columns={"job_lock"}) * }) */ class Document @@ -138,6 +142,12 @@ class Document * @ORM\OneToMany(targetEntity="Log", mappedBy="document") */ private $logs; + + /** + * @ORM\Column(name="job_lock", type="string", length=23, nullable=false) + */ + private string $jobLock; + public function __construct() { @@ -465,5 +475,16 @@ public function setModifiedBy(?User $modifiedBy): self return $this; } + + public function setJobLock($jobLock): self + { + $this->jobLock = $jobLock; + return $this; + } + + public function getJobLock(): string + { + return $this->jobLock; + } } diff --git a/src/Entity/Rule.php b/src/Entity/Rule.php index cc8de0861..e94a95d4b 100755 --- a/src/Entity/Rule.php +++ b/src/Entity/Rule.php @@ -35,7 +35,10 @@ * @ORM\Entity(repositoryClass="App\Repository\RuleRepository") * @ORM\HasLifecycleCallbacks() * - * @ORM\Table(name="rule", indexes={@ORM\Index(name="Krule_name", columns={"name"})}) + * @ORM\Table(name="rule", indexes={ + * @ORM\Index(name="Krule_name", columns={"name"}), + * @ORM\Index(name="index_read_job_lock", columns={"read_job_lock"}) + * }) */ class Rule { @@ -159,6 +162,11 @@ class Rule * @ORM\OrderBy({"sourceDateModified" : "ASC"}) */ private $documents; + + /** + * @ORM\Column(name="read_job_lock", type="string", length=23, nullable=true, options={"default":NULL}) + */ + private string $readJobLock; public function __construct() { @@ -286,6 +294,20 @@ public function getNameSlug(): string { return $this->nameSlug; } + + public function setReadJobLock($readJobLock): self + { + $this->readJobLock = $readJobLock; + return $this; + } + + public function getReadJobLock(): string + { + if (empty($this->readJobLock)) { + return ''; + } + return $this->readJobLock; + } public function setConnectorSource(Connector $connectorSource): self { @@ -675,4 +697,5 @@ public function isModuleTargetSet(): bool { return isset($this->moduleTarget); } + } diff --git a/src/Form/JobSchedulerCronType.php b/src/Form/JobSchedulerCronType.php index 412c2b523..0b5320398 100644 --- a/src/Form/JobSchedulerCronType.php +++ b/src/Form/JobSchedulerCronType.php @@ -4,6 +4,7 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; +use Symfony\Component\Form\Extension\Core\Type\IntegerType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; @@ -20,6 +21,8 @@ public function buildForm(FormBuilderInterface $builder, array $options) ]) ->add('command', TextType::class, ['mapped' => false]) ->add('description', TextType::class) + ->add('runningInstances', IntegerType::class) + ->add('maxInstances', IntegerType::class) ->add('period', TextType::class) ->add('save', SubmitType::class, [ 'attr' => [ diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 431f93c34..55fda7552 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -60,6 +60,7 @@ class documentcore protected $documentType; protected bool $jobActive = true; protected $attempt; + protected $jobLock; protected $userId; protected $status; protected $document_data; @@ -203,7 +204,18 @@ public function setDocument($id_doc) $this->ruleMode = $this->document_data['mode']; $this->documentType = $this->document_data['type']; $this->attempt = $this->document_data['attempt']; - + $this->jobLock = $this->document_data['job_lock']; + // A document can be loaded only if there is no lock or if the lock is on the current job. + if ( + !empty($this->jobLock) + AND $this->jobLock != $this->jobId + ) { + throw new \Exception('This document is locked by the task '.$this->jobLock.'. '); + // No setlock if $this->jobLock == $this->jobId + } elseif (!empty($this->jobLock)) { + $this->setLock(); + } + // Get source data and create data attribut $this->sourceData = $this->getDocumentData('S'); $this->data = $this->sourceData; @@ -218,10 +230,10 @@ public function setDocument($id_doc) $this->logger->error('Failed to retrieve Document '.$id_doc.'.'); } } catch (\Exception $e) { - $this->message .= 'Failed to retrieve document : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; - $this->typeError = 'E'; - $this->logger->error($this->message); - $this->createDocLog(); + // Remove the lock because there is not status changed (lock is usually remove when we change the status) + $this->unsetLock(); + // Stop the process + throw new \Exception('Failed to load the document : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); } } @@ -229,63 +241,73 @@ public function setDocument($id_doc) // Clear parameter is used when we call the same instance of the Document to manage several documents (from RuleManager class) public function setParam($param, $clear = false, $clearRule = true) { - if ($clear) { - $this->clearAttributes($clearRule); - } - // Chargement des solution si elles sont présentent dans les paramètres de construction - if (!empty($param['solutionTarget'])) { - $this->solutionTarget = $param['solutionTarget']; - } - if (!empty($param['solutionSource'])) { - $this->solutionSource = $param['solutionSource']; - } - if (!empty($param['jobId'])) { - $this->jobId = $param['jobId']; - } - if (!empty($param['api'])) { - $this->api = $param['api']; - } - if (!empty($param['parentId'])) { - $this->parentId = $param['parentId']; - } - if (!empty($param['ruleDocuments'])) { - $this->ruleDocuments = $param['ruleDocuments']; - } + try { + if ($clear) { + $this->clearAttributes($clearRule); + } + // Chargement des solution si elles sont présentent dans les paramètres de construction + if (!empty($param['solutionTarget'])) { + $this->solutionTarget = $param['solutionTarget']; + } + if (!empty($param['solutionSource'])) { + $this->solutionSource = $param['solutionSource']; + } + if (!empty($param['jobId'])) { + $this->jobId = $param['jobId']; + } + if (!empty($param['api'])) { + $this->api = $param['api']; + } + if (!empty($param['parentId'])) { + $this->parentId = $param['parentId']; + } + if (!empty($param['ruleDocuments'])) { + $this->ruleDocuments = $param['ruleDocuments']; + } - // Init attribut of the class Document - if (!empty($param['id_doc_myddleware'])) { - // Instanciate attribut sourceData - $this->setDocument($param['id_doc_myddleware']); - } else { - $this->id = uniqid('', true); - $this->dateCreated = gmdate('Y-m-d H:i:s'); - $this->ruleName = $param['rule']['name_slug']; - $this->ruleMode = $param['rule']['mode']; - $this->ruleId = $param['rule']['id']; - $this->ruleFields = $param['ruleFields']; - $this->data = $param['data']; - $this->sourceId = $this->data['id']; - $this->userId = $param['rule']['created_by']; - $this->status = 'New'; - $this->attempt = 0; - // Set the deletion type if myddleware deletion flag is true - if (!empty($this->data['myddleware_deletion'])) { - $this->documentType = 'D'; - } - } - // Ajout des paramètre de la règle - if (empty($this->ruleParams)) { - $this->setRuleParam(); - } - // Mise à jour des tableaux s'ils existent. - if (!empty($param['ruleFields'])) { - $this->ruleFields = $param['ruleFields']; - } - if (!empty($param['ruleRelationships'])) { - $this->ruleRelationships = $param['ruleRelationships']; + // Init attribut of the class Document + if (!empty($param['id_doc_myddleware'])) { + // Instanciate attribut sourceData + $this->setDocument($param['id_doc_myddleware']); + } else { + $this->id = uniqid('', true); + $this->dateCreated = gmdate('Y-m-d H:i:s'); + $this->ruleName = $param['rule']['name_slug']; + $this->ruleMode = $param['rule']['mode']; + $this->ruleId = $param['rule']['id']; + $this->ruleFields = $param['ruleFields']; + $this->data = $param['data']; + $this->sourceId = $this->data['id']; + $this->userId = $param['rule']['created_by']; + $this->status = 'New'; + $this->attempt = 0; + $this->jobLock = $this->jobId; + // Set the deletion type if myddleware deletion flag is true + if (!empty($this->data['myddleware_deletion'])) { + $this->documentType = 'D'; + } + } + // Ajout des paramètre de la règle + if (empty($this->ruleParams)) { + $this->setRuleParam(); + } + // Mise à jour des tableaux s'ils existent. + if (!empty($param['ruleFields'])) { + $this->ruleFields = $param['ruleFields']; + } + if (!empty($param['ruleRelationships'])) { + $this->ruleRelationships = $param['ruleRelationships']; + } + // Init type error for each new document + $this->typeError = 'S'; + } catch (\Exception $e) { + $this->message .= $e->getMessage(); + $this->typeError = 'E'; + $this->logger->error($this->message); + $this->createDocLog(); + return false; } - // Init type error for each new document - $this->typeError = 'S'; + return true; } // Clear all class attributes @@ -335,11 +357,11 @@ public function createDocument(): bool return false; } // Création du header de la requête - $query_header = 'INSERT INTO document (id, rule_id, date_created, date_modified, created_by, modified_by, source_id, source_date_modified, mode, type, parent_id) VALUES'; + $query_header = 'INSERT INTO document (id, rule_id, date_created, date_modified, created_by, modified_by, source_id, source_date_modified, mode, type, parent_id, job_lock) VALUES'; // Création de la requête d'entête $date_modified = $this->data['date_modified']; // Source_id could contain accent - $query_header .= "('$this->id','$this->ruleId','$this->dateCreated','$this->dateCreated','$this->userId','$this->userId','".utf8_encode($this->sourceId)."','$date_modified','$this->ruleMode','$this->documentType','$this->parentId')"; + $query_header .= "('$this->id','$this->ruleId','$this->dateCreated','$this->dateCreated','$this->userId','$this->userId','".utf8_encode($this->sourceId)."','$date_modified','$this->ruleMode','$this->documentType','$this->parentId', '')"; $stmt = $this->connection->prepare($query_header); $result = $stmt->executeQuery(); $this->updateStatus('New'); @@ -348,7 +370,6 @@ public function createDocument(): bool } catch (\Exception $e) { $this->message .= 'Failed to create document (id source : '.$this->sourceId.'): '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->logger->error($this->message); - return false; } } @@ -448,6 +469,76 @@ public function setDocIdRefError($docIdRefError) $this->docIdRefError = $docIdRefError; } + // Set the document lock + protected function setLock() { + try { + // Get the job lock on the document + $documentQuery = 'SELECT * FROM document WHERE id = :doc_id'; + $stmt = $this->connection->prepare($documentQuery); + $stmt->bindValue(':doc_id', $this->id); + $documentResult = $stmt->executeQuery(); + $documentData = $documentResult->fetchAssociative(); // 1 row + + // If document already lock by the current job, we return true; + if ($documentData['job_lock'] == $this->jobId) { + return array('success' => true); + // If document not locked, we lock it. + } elseif (empty($documentData['job_lock'])) { + $now = gmdate('Y-m-d H:i:s'); + $query = ' UPDATE document + SET + date_modified = :now, + job_lock = :job_id + WHERE + id = :id + '; + $stmt = $this->connection->prepare($query); + $stmt->bindValue(':now', $now); + $stmt->bindValue(':job_id', $this->jobId); + $stmt->bindValue(':id', $this->id); + $result = $stmt->executeQuery(); + return array('success' => true); + // Error for all other cases + } else { + return array('success' => false, 'error' => 'The document is locked by the task '.$documentData['job_lock'].'. '); + } + } catch (\Exception $e) { + // $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + return array('success' => false, 'error' => 'Failed to lock the document '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + } + } + + // Set the document lock + public function unsetLock() { + try { + // Get the job lock on the document + $documentQuery = 'SELECT * FROM document WHERE id = :doc_id'; + $stmt = $this->connection->prepare($documentQuery); + $stmt->bindValue(':doc_id', $this->id); + $documentResult = $stmt->executeQuery(); + $documentData = $documentResult->fetchAssociative(); // 1 row + + // If document already lock by the current job, we return true; + if ($documentData['job_lock'] == $this->jobId) { + $now = gmdate('Y-m-d H:i:s'); + $query = " UPDATE document + SET + date_modified = :now, + job_lock = '' + WHERE + id = :id + "; + $stmt = $this->connection->prepare($query); + $stmt->bindValue(':now', $now); + $stmt->bindValue(':id', $this->id); + $result = $stmt->executeQuery(); + return true; + } + } catch (\Exception $e) { + return false; + } + } + // Permet d'indiquer si le filtreest rempli ou pas protected function checkFilter($fieldValue, $operator, $filterValue): bool { @@ -680,18 +771,20 @@ public function checkPredecessorDocument(): bool // Set the status Predecessor_OK $this->updateStatus('Predecessor_OK'); - // Check compatibility between rule mode et document tupe - // A rule in create mode can't update data excpt for a child rule - if ( - 'C' == $this->ruleMode - and 'U' == $this->documentType - and !$this->isChild() - ) { - $this->message .= 'Rule mode only allows to create data. Filter because this document updates or deletes data.'; - $this->updateStatus('Filter'); - // In case we flter the document, we return false to stop the process when this method is called in the rerun process - return false; - } + // Check compatibility between rule mode et document type + // A rule in create mode can't update data except for a child rule + if ( + $this->ruleMode == 'C' + and $this->documentType == 'U' + ) { + // Check child in a second time to avoid to run a query each time + if (!$this->isChild()) { + $this->message .= 'Rule mode only allows to create data. Filter because this document updates data.'; + $this->updateStatus('Filter'); + // In case we flter the document, we return false to stop the process when this method is called in the rerun process + return false; + } + } return true; } catch (\Exception $e) { @@ -803,10 +896,23 @@ public function checkParentDocument(): bool if ('U' == $this->documentType) { $this->updateTargetId($this->targetId); $this->updateType('U'); + // Check compatibility between rule mode et document type + // A rule in create mode can't update data except for a child rule + if ( + $this->ruleMode == 'C' + and $this->documentType == 'U' + ) { + // Check child in a second time to avoid to run a query each time + if (!$this->isChild()) { + $this->message .= 'Rule mode only allows to create data. Filter because this document updates data.'; + $this->updateStatus('Filter'); + // In case we flter the document, we return false to stop the process when this method is called in the rerun process + return false; + } + } } } $this->updateStatus('Relate_OK'); - return true; } catch (\Exception $e) { $this->message .= 'Failed to check document related : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; @@ -841,6 +947,20 @@ public function transformDocument(): bool $this->targetId = $target['Myddleware_element_id']; if ($this->updateTargetId($this->targetId)) { $this->updateType('U'); + // Check compatibility between rule mode et document type + // A rule in create mode can't update data except for a child rule + if ( + $this->ruleMode == 'C' + and $this->documentType == 'U' + ) { + // Check child in a second time to avoid to run a query each time + if (!$this->isChild()) { + $this->message .= 'Rule mode only allows to create data. Filter because this document updates data.'; + $this->updateStatus('Filter'); + // In case we flter the document, we return false to stop the process when this method is called in the rerun process + return false; + } + } } else { throw new \Exception('The type of this document is Update. Failed to update the target id '.$this->targetId.' on this document. This document is queued. '); } @@ -976,6 +1096,20 @@ public function getTargetDataDocument(): bool } else { $this->updateStatus('Ready_to_send'); $this->updateType('U'); + // Check compatibility between rule mode et document type + // A rule in create mode can't update data except for a child rule + if ( + $this->ruleMode == 'C' + and $this->documentType == 'U' + ) { + // Check child in a second time to avoid to run a query each time + if (!$this->isChild()) { + $this->message .= 'Rule mode only allows to create data. Filter because this document updates data.'; + $this->updateStatus('Filter'); + // In case we flter the document, we return false to stop the process when this method is called in the rerun process + return false; + } + } } $this->updateTargetId($history['id']); } @@ -1263,6 +1397,7 @@ protected function insertDataTable($data, $type): bool } } } + // We save the relationship field too if (!empty($this->ruleRelationships)) { foreach ($this->ruleRelationships as $ruleRelationship) { @@ -1282,6 +1417,7 @@ protected function insertDataTable($data, $type): bool $documentData->setType($type); // Source $documentData->setData(json_encode($dataInsert)); // Encode in JSON $this->entityManager->persist($documentData); + $this->entityManager->flush(); } catch (\Exception $e) { $this->message .= 'Failed : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->typeError = 'E'; @@ -1919,7 +2055,6 @@ public function changeDeleteFlag($deleteFlag) */ public function updateStatus($new_status) { - $this->connection->beginTransaction(); // -- BEGIN TRANSACTION try { // On ajoute un contôle dans le cas on voudrait changer le statut $new_status = $this->beforeStatusChange($new_status); @@ -1936,7 +2071,8 @@ public function updateStatus($new_status) date_modified = :now, global_status = :globalStatus, attempt = :attempt, - status = :new_status + status = :new_status, + job_lock = :jobLock WHERE id = :id '; @@ -1947,21 +2083,21 @@ public function updateStatus($new_status) ) { echo 'status '.$new_status.' id = '.$this->id.' '.$now.chr(10); } - // Suppression de la dernière virgule $stmt = $this->connection->prepare($query); $stmt->bindValue(':now', $now); $stmt->bindValue(':globalStatus', $globalStatus); $stmt->bindValue(':attempt', $this->attempt); $stmt->bindValue(':new_status', $new_status); $stmt->bindValue(':id', $this->id); + // Remove the lock on the document in the class and in the database + $this->jobLock = ''; + $stmt->bindValue(':jobLock', $this->jobLock); $result = $stmt->executeQuery(); $this->message .= 'Status : '.$new_status; - $this->connection->commit(); // -- COMMIT TRANSACTION $this->status = $new_status; $this->afterStatusChange($new_status); $this->createDocLog(); } catch (\Exception $e) { - $this->connection->rollBack(); // -- ROLLBACK TRANSACTION $this->message .= 'Error status update : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->typeError = 'E'; $this->logger->error($this->message); @@ -1974,7 +2110,6 @@ public function updateStatus($new_status) */ public function updateDeleteFlag($deleted) { - $this->connection->beginTransaction(); // -- BEGIN TRANSACTION try { $now = gmdate('Y-m-d H:i:s'); $query = ' UPDATE document @@ -1997,10 +2132,8 @@ public function updateDeleteFlag($deleted) $stmt->bindValue(':id', $this->id); $result = $stmt->executeQuery(); $this->message .= (!empty($deleted) ? 'Remove' : 'Restore').' document'; - $this->connection->commit(); // -- COMMIT TRANSACTION $this->createDocLog(); } catch (\Exception $e) { - $this->connection->rollBack(); // -- ROLLBACK TRANSACTION $this->message .= 'Failed to '.(!empty($deleted) ? 'Remove ' : 'Restore ').' : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->typeError = 'E'; $this->logger->error($this->message); @@ -2045,7 +2178,6 @@ protected function afterStatusChange($new_status) */ public function updateType($new_type) { - $this->connection->beginTransaction(); // -- BEGIN TRANSACTION try { $now = gmdate('Y-m-d H:i:s'); $query = ' UPDATE document @@ -2062,12 +2194,10 @@ public function updateType($new_type) $stmt->bindValue(':id', $this->id); $result = $stmt->executeQuery(); $this->message .= 'Type : '.$new_type; - $this->connection->commit(); // -- COMMIT TRANSACTION $this->createDocLog(); // Change the document type for the current process $this->documentType = $new_type; } catch (\Exception $e) { - $this->connection->rollBack(); // -- ROLLBACK TRANSACTION $this->message .= 'Error type : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->typeError = 'E'; $this->logger->error($this->message); @@ -2080,7 +2210,6 @@ public function updateType($new_type) */ public function updateTargetId($target_id): bool { - $this->connection->beginTransaction(); // -- BEGIN TRANSACTION try { $now = gmdate('Y-m-d H:i:s'); $query = ' UPDATE document @@ -2098,14 +2227,12 @@ public function updateTargetId($target_id): bool $stmt->bindValue(':id', $this->id); $result = $stmt->executeQuery(); $this->message .= 'Target id : '.$target_id; - $this->connection->commit(); // -- COMMIT TRANSACTION $this->createDocLog(); // Change the target id for the current process $this->targetId = $target_id; return true; } catch (\Exception $e) { - $this->connection->rollBack(); // -- ROLLBACK TRANSACTION $this->message .= 'Error target id : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->typeError = 'E'; $this->logger->error($this->message); @@ -2116,7 +2243,7 @@ public function updateTargetId($target_id): bool } // Function to manually edit the data inside a Myddleware Document - public function updateDocumentData(string $docId, array $newValues, string $dataType) + public function updateDocumentData(string $docId, array $newValues, string $dataType, bool $refreshData = false) { // check if data of that type with this docid and this data fields if (empty($docId)) { @@ -2150,18 +2277,22 @@ public function updateDocumentData(string $docId, array $newValues, string $data // Compare data $oldData = json_decode($documentDataEntity->getData()); if(!empty($oldData)){ - foreach ($newValues as $key => $Value) { - foreach ($oldData as $oldKey => $oldValue) { - if ($oldKey === $key) { - if ($oldValue !== $Value) { - $newValues[$oldKey] = $Value; - $this->message .= ($dataType == 'S' ? 'Source' : ($dataType == 'T' ? 'Target' : 'History')).' document value changed from '.$oldValue.' to '.$Value.'. '; - } - } else { - $newValues[$oldKey] = $oldValue; - } - } - } + if (!$refreshData) { + foreach ($newValues as $key => $Value) { + foreach ($oldData as $oldKey => $oldValue) { + if ($oldKey === $key) { + if ($oldValue !== $Value) { + $newValues[$oldKey] = $Value; + $this->message .= ($dataType == 'S' ? 'Source' : ($dataType == 'T' ? 'Target' : 'History')).' document value changed from '.$oldValue.' to '.$Value.'. '; + } + } else { + $newValues[$oldKey] = $oldValue; + } + } + } + } else { + $this->message .= ($dataType == 'S' ? 'Source' : ($dataType == 'T' ? 'Target' : 'History')).' document value changed by '.print_r($newValues,true).'. '; + } $this->typeError = 'I'; $this->createDocLog(); // Update the data of the right type @@ -2367,7 +2498,6 @@ public function getStatus() */ protected function createDocLog() { - $this->connection->beginTransaction(); // -- BEGIN TRANSACTION try { $now = gmdate('Y-m-d H:i:s'); $query_header = 'INSERT INTO log (created, type, msg, rule_id, doc_id, ref_doc_id, job_id) VALUES (:created,:typeError,:message,:rule_id,:doc_id,:ref_doc_id,:job_id)'; @@ -2381,9 +2511,7 @@ protected function createDocLog() $stmt->bindValue(':job_id', $this->jobId); $result = $stmt->executeQuery(); $this->message = ''; - $this->connection->commit(); // -- COMMIT TRANSACTION } catch (\Exception $e) { - $this->connection->rollBack(); // -- ROLLBACK TRANSACTION $this->logger->error('Failed to create log : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); } } diff --git a/src/Manager/JobManager.php b/src/Manager/JobManager.php index 8731e339a..450f4fdf0 100644 --- a/src/Manager/JobManager.php +++ b/src/Manager/JobManager.php @@ -206,6 +206,11 @@ public function setRule($filter): bool return false; } } + + // Unset the lock on the rule + public function unsetRuleLock() { + return $this->ruleManager->unsetRuleLock(); + } // Permet de contrôler si un docuement de la même règle pour le même enregistrement n'est pas close public function createDocuments() @@ -267,6 +272,7 @@ public function sendDocuments() public function runError($limit, $attempt) { try { + $ruleId = ''; // Récupération de tous les flux en erreur ou des flux en attente (new) qui ne sont pas sur règles actives (règle child pour des règles groupées) $sqlParams = " SELECT * FROM document @@ -285,43 +291,35 @@ public function runError($limit, $attempt) if (!empty($documentsError)) { // include_once 'rule.php'; foreach ($documentsError as $documentError) { - $this->ruleManager->setRule($documentError['rule_id']); - $this->ruleManager->setJobId($this->id); - $this->ruleManager->setManual($this->manual); - $this->ruleManager->setApi($this->api); + // Load the rule only if it has changed + if ($ruleId != $documentError['rule_id']) { + $this->ruleManager->setRule($documentError['rule_id']); + $this->ruleManager->setJobId($this->id); + $this->ruleManager->setManual($this->manual); + $this->ruleManager->setApi($this->api); + } $errorActionDocument = $this->ruleManager->actionDocument($documentError['id'], 'rerun'); if (!empty($errorActionDocument)) { $this->message .= print_r($errorActionDocument, true); } + $ruleId = $documentError['rule_id']; } } } catch (Exception $e) { - $this->logger->error('Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); - $this->message .= 'Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; + $this->logger->error('Error AAA : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + $this->message .= 'Error AAA : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; } } /** * @throws \Doctrine\DBAL\Exception */ - public function initJob(string $paramJob, bool $force = false): array + public function initJob(string $paramJob): array { $this->paramJob = $paramJob; $this->id = uniqid('', true); $this->start = microtime(true); - // Check if a job is already running except if force = true (api call or manuel call) - if (!$force) { - $sqlJobOpen = "SELECT * FROM job WHERE status = 'Start' LIMIT 1"; - $stmt = $this->connection->prepare($sqlJobOpen); - $result = $stmt->executeQuery(); - $job = $result->fetchAssociative(); // 1 row - // Error if one job is still running - if (!empty($job)) { - $this->message .= $this->tools->getTranslation(['messages', 'rule', 'another_task_running']).';'.$job['id']; - return ['success' => false, 'message' => $this->message]; - } - } // Create Job $insertJob = $this->insertJob(); if ($insertJob) { @@ -356,7 +354,6 @@ public function actionMassTransfer($event, $datatype, $param) $paramJob[] = $event; $paramJob[] = $datatype; $paramJob[] = implode(',', $param); - $paramJob[] = 1; // Force run even if another task is running return $this->runBackgroundJob('massaction', $paramJob); } else { @@ -398,7 +395,7 @@ public function runBackgroundJob($job, $param) } catch (IOException $e) { throw new Exception('An error occurred while creating your directory'); } - exec($php.' '.__DIR__.'/../../bin/console myddleware:'.$job.' '.$params.' 1 --env='.$this->env.' > '.$fileTmp.' &'); + exec($php.' '.__DIR__.'/../../bin/console myddleware:'.$job.' '.$params.' --env='.$this->env.' > '.$fileTmp.' &'); $cpt = 0; // Boucle tant que le fichier n'existe pas while (!file_exists($fileTmp)) { @@ -509,7 +506,7 @@ public function massAction($action, $dataType, $ids, $forceAll, $fromStatus, $to $error = $this->ruleManager->actionDocument($document['id'], $action, $toStatus); // Save the error if exists if (!empty($error)) { - $errors[] = $error[0]; + $errors[] = current($error); } } } else { @@ -1312,7 +1309,7 @@ public function getLogData() */ protected function updateJob(): bool { - $this->connection->beginTransaction(); // -- BEGIN TRANSACTION + // $this->connection->beginTransaction(); // -- BEGIN TRANSACTION try { $close = $this->logData['Close']; $cancel = $this->logData['Cancel']; @@ -1341,10 +1338,11 @@ protected function updateJob(): bool $stmt->bindValue('error', $error); $stmt->bindValue('message', $message); $stmt->bindValue('id', $this->id); - $result = $stmt->executeQuery(); - $this->connection->commit(); // -- COMMIT TRANSACTION + $result = $stmt->executeQuery(); + + // $this->connection->commit(); // -- COMMIT TRANSACTION } catch (Exception $e) { - $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + // $this->connection->rollBack(); // -- ROLLBACK TRANSACTION $this->logger->error('Failed to update Job : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); $this->message .= 'Failed to update Job : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; diff --git a/src/Manager/RuleManager.php b/src/Manager/RuleManager.php index f989c91d1..230d7b05f 100644 --- a/src/Manager/RuleManager.php +++ b/src/Manager/RuleManager.php @@ -27,6 +27,7 @@ use App\Entity\Config; use App\Entity\DocumentData; +use App\Entity\Document; use App\Entity\Rule; use App\Entity\RuleParam; use App\Entity\RuleParamAudit as RuleParamAudit; @@ -177,6 +178,54 @@ public function setApi($api) $this->api = $api; } + // Unset the lock on the rule + protected function setRuleLock() { + try { + // Get the rule details + $rule = $this->entityManager->getRepository(Rule::class)->findOneBy(['id' => $this->ruleId, 'deleted' => false]); + // If read lock empty, we set the lock with the job id + if (empty($rule->getReadJobLock())) { + $rule->setReadJobLock($this->jobId); + $this->entityManager->persist($rule); + $this->entityManager->flush(); + return true; + } + } catch (Exception $e) { + $this->logger->error('Failed set the lock on the rule '.$this->ruleId.' : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + } + return false; + } + + // Unset the lock on the rule + public function unsetRuleLock() { + try { + // Get the rule details + $rule = $this->entityManager->getRepository(Rule::class)->findOneBy(['id' => $this->ruleId, 'deleted' => false]); + // If read lock empty, we set the lock with the job id + $readJobLock = $rule->getReadJobLock(); + if ( + !empty($readJobLock) + AND $readJobLock == $this->jobId + ) { + $rule->setReadJobLock(''); + $this->entityManager->persist($rule); + $this->entityManager->flush(); + } elseif (!empty($readJobLock)) { + return false; + } + } catch (Exception $e) { + $this->logger->error('Failed unset the lock on the rule '.$this->ruleId.' : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + return false; + } + return true; + } + + protected function getRuleLock() { + // Get the rule details + $rule = $this->entityManager->getRepository(Rule::class)->findOneBy(['id' => $this->ruleId, 'deleted' => false]); + return $rule->getReadJobLock(); + } + /** * Generate a document for the current rule for a specific id in the source application. We don't use the reference for the function read. * If parameter readSource is false, it means that the data source are already in the parameter param, so no need to read in the source application. @@ -185,7 +234,6 @@ public function setApi($api) */ public function generateDocuments($idSource, $readSource = true, $param = '', $idFiledName = 'id') { - $this->connection->beginTransaction(); // -- BEGIN TRANSACTION suspend auto-commit try { $documents = []; if ($readSource) { @@ -241,11 +289,8 @@ public function generateDocuments($idSource, $readSource = true, $param = '', $i $documents[] = $childDocument; } } - $this->commit(false); // -- COMMIT TRANSACTION - return $documents; } catch (\Exception $e) { - $this->connection->rollBack(); // -- ROLLBACK TRANSACTION $error = 'Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->logger->error($error); $errorObj = new \stdClass(); @@ -329,26 +374,36 @@ public function createDocuments() ) ) ) { - // lecture des données dans la source - $readSource = $this->readSource(); - if (empty($readSource['error'])) { - $readSource['error'] = ''; - } - - // Si erreur - if (!isset($readSource['count'])) { - return $readSource; - } + // Check the rule isn't locked + if (!$this->setRuleLock()) { + return array('error' => 'The rule '.$this->ruleId.' is locked by the task '.$this->getRuleLock().'. Failed to read the source application. '); + } + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION suspend auto-commit try { + // lecture des données dans la source + $readSource = $this->readSource(); + if (empty($readSource['error'])) { + $readSource['error'] = ''; + } + // If error we unlock the rule and we return the result + if (!isset($readSource['count'])) { + $this->unsetRuleLock(); + $this->connection->commit(); + return $readSource; + } + if ($readSource['count'] > 0) { + // Before creating the documents, we check the job id is the one in the rule lock + if ($this->getRuleLock() != $this->jobId) { + throw new \Exception('The rule '.$this->ruleId.' is locked by the task '.$this->getRuleLock().'. Failed to generate the documents. '); + } $param['rule'] = $this->rule; $param['ruleFields'] = $this->ruleFields; $param['ruleRelationships'] = $this->ruleRelationships; // Set the param of the rule one time for all $this->documentManager->setRuleId($this->ruleId); $this->documentManager->setRuleParam(); - $i = 0; if ($this->dataSource['values']) { // Set all config parameters $this->setConfigParam(); @@ -358,17 +413,13 @@ public function createDocuments() } // Boucle sur chaque document foreach ($this->dataSource['values'] as $row) { - if ($i >= $this->limitReadCommit) { - $this->commit(true); // -- COMMIT TRANSACTION - $i = 0; - } - ++$i; $param['data'] = $row; $param['jobId'] = $this->jobId; $param['api'] = $this->api; // Set the param values and clear all document attributes but not rule attributes - $this->documentManager->setParam($param, true, false); - $createDocument = $this->documentManager->createDocument(); + if($this->documentManager->setParam($param, true)) { + $createDocument = $this->documentManager->createDocument(); + } if (!$createDocument) { $readSource['error'] .= $this->documentManager->getMessage(); } @@ -379,16 +430,22 @@ public function createDocuments() } // If params has been added in the output of the rule we saved it $this->updateParams(); + + // No error management because we don't want any rollback because of the lock. + // If the lock isn't removed, the next task will generate an error + $this->unsetRuleLock(); // Rollback if the job has been manually stopped if ('Start' != $this->getJobStatus()) { - throw new \Exception('The task has been stopped manually during the document creation. No document generated. '); + throw new \Exception('The task has been stopped manually. No document generated. '); } - $this->commit(false); // -- COMMIT TRANSACTION + $this->connection->commit(); } catch (\Exception $e) { $this->connection->rollBack(); // -- ROLLBACK TRANSACTION $this->logger->error('Failed to create documents : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); $readSource['error'] = 'Failed to create documents : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; + // The process is finished even if there is an exception so we unlock the rule + $this->unsetRuleLock(); } } // On affiche pas d'erreur si la lecture est désactivée @@ -619,26 +676,21 @@ public function filterDocuments($documents = null): array // Pour tous les docuements sélectionnés on vérifie les prédécesseurs if (!empty($documents)) { - $this->connection->beginTransaction(); // -- BEGIN TRANSACTION suspend auto-commit try { + if ('Start' != $this->getJobStatus()) { + throw new \Exception('The task has been stopped manually. No document generated. '); + } $this->setRuleFilter(); - $i = 0; foreach ($documents as $document) { - if ($i >= $this->limitReadCommit) { - $this->commit(true); // -- COMMIT TRANSACTION - $i = 0; - } - ++$i; $param['id_doc_myddleware'] = $document['id']; $param['jobId'] = $this->jobId; $param['api'] = $this->api; // Set the param values and clear all document attributes - $this->documentManager->setParam($param, true); - $response[$document['id']] = $this->documentManager->filterDocument($this->ruleFilters); + if($this->documentManager->setParam($param, true)) { + $response[$document['id']] = $this->documentManager->filterDocument($this->ruleFilters); + } } - $this->commit(false); // -- COMMIT TRANSACTION } catch (\Exception $e) { - $this->connection->rollBack(); // -- ROLLBACK TRANSACTION $this->logger->error('Failed to filter documents : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); $readSource['error'] = 'Failed to filter documents : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; } @@ -664,26 +716,21 @@ public function checkPredecessorDocuments($documents = null): array } // Pour tous les docuements sélectionnés on vérifie les prédécesseurs if (!empty($documents)) { - $this->connection->beginTransaction(); // -- BEGIN TRANSACTION suspend auto-commit try { - $i = 0; + if ('Start' != $this->getJobStatus()) { + throw new \Exception('The task has been stopped manually. No document generated. '); + } foreach ($documents as $document) { - if ($i >= $this->limitReadCommit) { - $this->commit(true); // -- COMMIT TRANSACTION - $i = 0; - } - ++$i; $param['id_doc_myddleware'] = $document['id']; $param['jobId'] = $this->jobId; $param['api'] = $this->api; $param['ruleRelationships'] = $this->ruleRelationships; // Set the param values and clear all document attributes - $this->documentManager->setParam($param, true); - $response[$document['id']] = $this->documentManager->checkPredecessorDocument(); + if($this->documentManager->setParam($param, true)) { + $response[$document['id']] = $this->documentManager->checkPredecessorDocument(); + } } - $this->commit(false); // -- COMMIT TRANSACTION } catch (\Exception $e) { - $this->connection->rollBack(); // -- ROLLBACK TRANSACTION $this->logger->error('Failed to check predecessors : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); $readSource['error'] = 'Failed to check predecessors : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; } @@ -725,27 +772,22 @@ public function checkParentDocuments($documents = null): array } } } - $this->connection->beginTransaction(); // -- BEGIN TRANSACTION suspend auto-commit try { - $i = 0; + if ('Start' != $this->getJobStatus()) { + throw new \Exception('The task has been stopped manually. No document generated. '); + } // Pour tous les docuements sélectionnés on vérifie les parents foreach ($documents as $document) { - if ($i >= $this->limitReadCommit) { - $this->commit(true); // -- COMMIT TRANSACTION - $i = 0; - } - ++$i; $param['id_doc_myddleware'] = $document['id']; $param['jobId'] = $this->jobId; $param['api'] = $this->api; $param['ruleRelationships'] = $this->ruleRelationships; // Set the param values and clear all document attributes - $this->documentManager->setParam($param, true); - $response[$document['id']] = $this->documentManager->checkParentDocument(); + if($this->documentManager->setParam($param, true)) { + $response[$document['id']] = $this->documentManager->checkParentDocument(); + } } - $this->commit(false); // -- COMMIT TRANSACTION } catch (\Exception $e) { - $this->connection->rollBack(); // -- ROLLBACK TRANSACTION $this->logger->error('Failed to check parents : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); $readSource['error'] = 'Failed to check parents : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; } @@ -788,26 +830,22 @@ public function transformDocuments($documents = null): array } } } - $this->connection->beginTransaction(); // -- BEGIN TRANSACTION suspend auto-commit + try { - $i = 0; + if ('Start' != $this->getJobStatus()) { + throw new \Exception('The task has been stopped manually. No document generated. '); + } // Transformation de tous les docuements sélectionnés foreach ($documents as $document) { - if ($i >= $this->limitReadCommit) { - $this->commit(true); // -- COMMIT TRANSACTION - $i = 0; - } - ++$i; $param['id_doc_myddleware'] = $document['id']; // Set the param values and clear all document attributes - $this->documentManager->setParam($param, true); - $response[$document['id']] = $this->documentManager->transformDocument(); + if($this->documentManager->setParam($param, true)) { + $response[$document['id']] = $this->documentManager->transformDocument(); + } } - $this->commit(false); // -- COMMIT TRANSACTION } catch (\Exception $e) { - $this->connection->rollBack(); // -- ROLLBACK TRANSACTION $this->logger->error('Failed to transform documents : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); - $readSource['error'] = 'Failed to transform documents : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; + $response['error'] = 'Failed to transform documents : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; } } @@ -837,16 +875,12 @@ public function getTargetDataDocuments($documents = null): array if (!empty($documents)) { // Connexion à la solution cible pour rechercher les données $this->connexionSolution('target'); - $this->connection->beginTransaction(); // -- BEGIN TRANSACTION suspend auto-commit try { - $i = 0; + if ('Start' != $this->getJobStatus()) { + throw new \Exception('The task has been stopped manually. No document generated. '); + } // Récupération de toutes les données dans la cible pour chaque document foreach ($documents as $document) { - if ($i >= $this->limitReadCommit) { - $this->commit(true); // -- COMMIT TRANSACTION - $i = 0; - } - ++$i; $param['id_doc_myddleware'] = $document['id']; $param['solutionTarget'] = $this->solutionTarget; $param['ruleFields'] = $this->ruleFields; @@ -854,13 +888,12 @@ public function getTargetDataDocuments($documents = null): array $param['jobId'] = $this->jobId; $param['api'] = $this->api; // Set the param values and clear all document attributes - $this->documentManager->setParam($param, true); - $response[$document['id']] = $this->documentManager->getTargetDataDocument(); - $response['doc_status'] = $this->documentManager->getStatus(); + if($this->documentManager->setParam($param, true)) { + $response[$document['id']] = $this->documentManager->getTargetDataDocument(); + $response['doc_status'] = $this->documentManager->getStatus(); + } } - $this->commit(false); // -- COMMIT TRANSACTION } catch (\Exception $e) { - $this->connection->rollBack(); // -- ROLLBACK TRANSACTION $this->logger->error('Failed to create documents : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); $readSource['error'] = 'Failed to create documents : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; } @@ -888,7 +921,6 @@ public function sendDocuments(): array $sendTarget['error'] .= 'Failed to logout from the target solution'; } } - return $sendTarget; } @@ -1126,7 +1158,7 @@ protected function changeDeleteFlag($id_document, $deleteFlag) /** * @throws \Doctrine\DBAL\Exception */ - protected function changeStatus($id_document, $toStatus, $message = null, $docIdRefError = null) + protected function changeStatus($id_document, $toStatus, $message = null, $docIdRefError = null, $typeError = null) { $param['id_doc_myddleware'] = $id_document; $param['jobId'] = $this->jobId; @@ -1136,6 +1168,9 @@ protected function changeStatus($id_document, $toStatus, $message = null, $docId if (!empty($message)) { $this->documentManager->setMessage($message); } + if (!empty($typeError)) { + $this->documentManager->setTypeError($typeError); + } if (!empty($docIdRefError)) { $this->documentManager->setDocIdRefError($docIdRefError); } @@ -1160,19 +1195,19 @@ protected function runMyddlewareJob($ruleId, $event = null, $documentId = null) throw new \Exception($this->tools->getTranslation(['messages', 'rule', 'failed_create_directory'])); } if ($documentId !== null) { - exec($php.' '.__DIR__.'/../../bin/console myddleware:readrecord '.$ruleId.' id '.$documentId.' 1 --env='.$this->env.' > '.$fileTmp.' &', $output); + exec($php.' '.__DIR__.'/../../bin/console myddleware:readrecord '.$ruleId.' id '.$documentId.' --env='.$this->env.' > '.$fileTmp.' &', $output); } //if user clicked on cancel all transfers of a rule elseif ('cancelDocumentJob' === $event) { exec($php.' '.__DIR__.'/../../bin/console myddleware:massaction cancel rule '.$ruleId.' --env='.$this->env.' > '.$fileTmp.' &', $output); //if user clicked on delete all transfers from a rule } elseif ('deleteDocumentJob' === $event) { - exec($php.' '.__DIR__.'/../../bin/console myddleware:massaction remove rule '.$ruleId.' 1 Y --env='.$this->env.' > '.$fileTmp.' &', $output); + exec($php.' '.__DIR__.'/../../bin/console myddleware:massaction remove rule '.$ruleId.' Y --env='.$this->env.' > '.$fileTmp.' &', $output); } elseif ('ALL' == $ruleId) { // We don't set the parameter force to 1 when we synchronize all rules exec($php.' '.__DIR__.'/../../bin/console myddleware:synchro '.$ruleId.' --env='.$this->env.' > '.$fileTmp.' &', $output); } else { - exec($php.' '.__DIR__.'/../../bin/console myddleware:synchro '.$ruleId.' 1 --env='.$this->env.' > '.$fileTmp.' &', $output); + exec($php.' '.__DIR__.'/../../bin/console myddleware:synchro '.$ruleId.' --env='.$this->env.' > '.$fileTmp.' &', $output); } $cpt = 0; // Boucle tant que le fichier n'existe pas @@ -1225,126 +1260,130 @@ protected function runMyddlewareJob($ruleId, $event = null, $documentId = null) */ protected function rerun($id_document): array { - $session = new Session(); - $msg_error = []; - $msg_success = []; - $msg_info = []; - // Récupération du statut du document - $param['id_doc_myddleware'] = $id_document; - $param['jobId'] = $this->jobId; - $param['api'] = $this->api; - // Set the param values and clear all document attributes - $this->documentManager->setParam($param, true); - $status = $this->documentManager->getStatus(); - // Si la règle n'est pas chargée alors on l'initialise. - if (empty($this->ruleId)) { - $this->ruleId = $this->documentManager->getRuleId(); - $this->setRule($this->ruleId); - $this->setRuleRelationships(); - $this->setRuleParam(); - $this->setRuleField(); - } + try { + $session = new Session(); + $msg_error = []; + $msg_success = []; + $msg_info = []; + // Récupération du statut du document + $param['id_doc_myddleware'] = $id_document; + $param['jobId'] = $this->jobId; + $param['api'] = $this->api; + // Set the param values and clear all document attributes + $this->documentManager->setParam($param, true); + $status = $this->documentManager->getStatus(); + // Si la règle n'est pas chargée alors on l'initialise. + if (empty($this->ruleId)) { + $this->ruleId = $this->documentManager->getRuleId(); + $this->setRule($this->ruleId); + $this->setRuleRelationships(); + $this->setRuleParam(); + $this->setRuleField(); + } - $response[$id_document] = false; - // On lance des méthodes différentes en fonction du statut en cours du document et en fonction de la réussite ou non de la fonction précédente - if (in_array($status, ['New', 'Filter_KO'])) { - $response = $this->filterDocuments([['id' => $id_document]]); - if (true === $response[$id_document]) { - $msg_success[] = 'Transfer id '.$id_document.' : Status change => Filter_OK'; - } elseif (-1 == $response[$id_document]) { - $msg_info[] = 'Transfer id '.$id_document.' : Status change => Filter'; - } else { - $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer => Filter_KO'; - } - // Update status if an action has been executed - $status = $this->documentManager->getStatus(); - } - if (in_array($status, ['Filter_OK', 'Predecessor_KO'])) { - $response = $this->checkPredecessorDocuments([['id' => $id_document]]); - if (true === $response[$id_document]) { - $msg_success[] = 'Transfer id '.$id_document.' : Status change => Predecessor_OK'; - } else { - $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer => Predecessor_KO'; - } - // Update status if an action has been executed - $status = $this->documentManager->getStatus(); - } - if (in_array($status, ['Predecessor_OK', 'Relate_KO'])) { - $response = $this->checkParentDocuments([['id' => $id_document]]); - if (true === $response[$id_document]) { - $msg_success[] = 'Transfer id '.$id_document.' : Status change => Relate_OK'; - } else { - $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer => Relate_KO'; - } - // Update status if an action has been executed - $status = $this->documentManager->getStatus(); - } - if (in_array($status, ['Relate_OK', 'Error_transformed'])) { - $response = $this->transformDocuments([['id' => $id_document]]); - if (true === $response[$id_document]) { - $msg_success[] = 'Transfer id '.$id_document.' : Status change : Transformed'; - } else { - $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer : Error_transformed'; - } - // Update status if an action has been executed - $status = $this->documentManager->getStatus(); - } - if (in_array($status, ['Transformed', 'Error_checking', 'Not_found'])) { - $response = $this->getTargetDataDocuments([['id' => $id_document]]); - if (true === $response[$id_document]) { - if ('S' == $this->rule['mode']) { - $msg_success[] = 'Transfer id '.$id_document.' : Status change : '.$response['doc_status']; - } else { - $msg_success[] = 'Transfer id '.$id_document.' : Status change : '.$response['doc_status']; - } - } else { - $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer : '.$response['doc_status']; - } - // Update status if an action has been executed - $status = $this->documentManager->getStatus(); - } - // Si la règle est en mode recherche alors on n'envoie pas de données - // Si on a un statut compatible ou si le doc vient de passer dans l'étape précédente et qu'il n'est pas no_send alors on envoie les données - if ( - 'S' != $this->rule['mode'] - && ( - in_array($status, ['Ready_to_send', 'Error_sending']) - || ( - true === $response[$id_document] - && ( - empty($response['doc_status']) - || ( - !empty($response['doc_status']) - && 'No_send' != $response['doc_status'] - ) - ) - ) - ) - ) { - $response = $this->sendTarget('', $id_document); - if ( - !empty($response[$id_document]['id']) - && empty($response[$id_document]['error']) - && empty($response['error']) // Error can be on the document or can be a general error too - ) { - $msg_success[] = 'Transfer id '.$id_document.' : Status change : Send'; - } else { - $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer : Error_sending. '.(!empty($response['error']) ? $response['error'] : $response[$id_document]['error']); - } - } - // If the job is manual, we display error in the UI - if ($this->manual) { - if (!empty($msg_error)) { - $session->set('error', $msg_error); - } - if (!empty($msg_success)) { - $session->set('success', $msg_success); - } - if (!empty($msg_info)) { - $session->set('info', $msg_info); - } + $response[$id_document] = false; + // On lance des méthodes différentes en fonction du statut en cours du document et en fonction de la réussite ou non de la fonction précédente + if (in_array($status, ['New', 'Filter_KO'])) { + $response = $this->filterDocuments([['id' => $id_document]]); + if (true === $response[$id_document]) { + $msg_success[] = 'Transfer id '.$id_document.' : Status change => Filter_OK'; + } elseif (-1 == $response[$id_document]) { + $msg_info[] = 'Transfer id '.$id_document.' : Status change => Filter'; + } else { + $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer => Filter_KO'; + } + // Update status if an action has been executed + $status = $this->documentManager->getStatus(); + } + if (in_array($status, ['Filter_OK', 'Predecessor_KO'])) { + $response = $this->checkPredecessorDocuments([['id' => $id_document]]); + if (true === $response[$id_document]) { + $msg_success[] = 'Transfer id '.$id_document.' : Status change => Predecessor_OK'; + } else { + $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer => Predecessor_KO'; + } + // Update status if an action has been executed + $status = $this->documentManager->getStatus(); + } + if (in_array($status, ['Predecessor_OK', 'Relate_KO'])) { + $response = $this->checkParentDocuments([['id' => $id_document]]); + if (true === $response[$id_document]) { + $msg_success[] = 'Transfer id '.$id_document.' : Status change => Relate_OK'; + } else { + $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer => Relate_KO'; + } + // Update status if an action has been executed + $status = $this->documentManager->getStatus(); + } + if (in_array($status, ['Relate_OK', 'Error_transformed'])) { + $response = $this->transformDocuments([['id' => $id_document]]); + if (true === $response[$id_document]) { + $msg_success[] = 'Transfer id '.$id_document.' : Status change : Transformed'; + } else { + $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer : Error_transformed'; + } + // Update status if an action has been executed + $status = $this->documentManager->getStatus(); + } + if (in_array($status, ['Transformed', 'Error_checking', 'Not_found'])) { + $response = $this->getTargetDataDocuments([['id' => $id_document]]); + if (true === $response[$id_document]) { + if ('S' == $this->rule['mode']) { + $msg_success[] = 'Transfer id '.$id_document.' : Status change : '.$response['doc_status']; + } else { + $msg_success[] = 'Transfer id '.$id_document.' : Status change : '.$response['doc_status']; + } + } else { + $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer : '.$response['doc_status']; + } + // Update status if an action has been executed + $status = $this->documentManager->getStatus(); + } + // Si la règle est en mode recherche alors on n'envoie pas de données + // Si on a un statut compatible ou si le doc vient de passer dans l'étape précédente et qu'il n'est pas no_send alors on envoie les données + if ( + 'S' != $this->rule['mode'] + && ( + in_array($status, ['Ready_to_send', 'Error_sending']) + || ( + true === $response[$id_document] + && ( + empty($response['doc_status']) + || ( + !empty($response['doc_status']) + && 'No_send' != $response['doc_status'] + ) + ) + ) + ) + ) { + $response = $this->sendTarget('', $id_document); + if ( + !empty($response[$id_document]['id']) + && empty($response[$id_document]['error']) + && empty($response['error']) // Error can be on the document or can be a general error too + ) { + $msg_success[] = 'Transfer id '.$id_document.' : Status change : Send'; + } else { + $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer : Error_sending. '.(!empty($response[$id_document]['error']) ? $response[$id_document]['error'] : $response['error'] ); + } + } + // If the job is manual, we display error in the UI + if ($this->manual) { + if (!empty($msg_error)) { + $session->set('error', $msg_error); + } + if (!empty($msg_success)) { + $session->set('success', $msg_success); + } + if (!empty($msg_info)) { + $session->set('info', $msg_info); + } + } + } catch (Exception $e) { + $this->logger->error('Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + $msg_error[] = 'Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; } - return $msg_error; } @@ -1557,7 +1596,7 @@ public function isChild(): bool protected function sendTarget($type, $documentId = null): array { - try { + try { // Permet de charger dans la classe toutes les relations de la règle $response = []; $response['error'] = ''; @@ -1569,7 +1608,6 @@ protected function sendTarget($type, $documentId = null): array $type = $documentData['type']; } } - // Récupération du contenu de la table target pour tous les documents à envoyer à la cible $send['data'] = $this->getSendDocuments($type, $documentId); @@ -1628,8 +1666,12 @@ protected function sendTarget($type, $documentId = null): array $response['error'] = 'Type transfer '.$type.' unknown. '; } } else { - $response[$documentId] = false; - $response['error'] = $connect['error']; + $response[$documentId] = false; + // If we couldn't connect the target application, we set the status error_sending to all documents not sent + foreach ($send['data'] as $idDoc => $data) { + $this->changeStatus($idDoc, 'Error_sending', 'Failed to connect to the target application.', null, 'E'); + } + throw new \Exception('Failed to connect to the target application.'); } } @@ -1690,7 +1732,6 @@ protected function massSendTarget($type, $documentId = null) } foreach($arrayDocumentsIds as $documentId){ - // $send['data'][$documentId] = $this->getSendDocuments($type, $documentId); $sendDataDocumentArrayElement = $this->getSendDocuments($type, $documentId); $send['data'] = (object) [$documentId => $sendDataDocumentArrayElement[$documentId]]; } @@ -1908,23 +1949,43 @@ protected function checkDuplicate($transformedData) protected function selectDocuments($status, $type = '') { + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION try { - $query_documents = " SELECT * - FROM document + // Select documents depending of the status + $queryDocuments = " SELECT d + FROM App\Entity\Document d WHERE - rule_id = :ruleId - AND status = :status - AND document.deleted = 0 - ORDER BY document.source_date_modified ASC - LIMIT $this->limit + d.rule = :ruleId + AND d.status = :status + AND d.deleted = 0 + AND ( + d.jobLock = '' + OR d.jobLock = :jobId + ) + ORDER BY d.sourceDateModified ASC "; - $stmt = $this->connection->prepare($query_documents); - $stmt->bindValue(':ruleId', $this->ruleId); - $stmt->bindValue(':status', $status); - $result = $stmt->executeQuery(); - - return $result->fetchAllAssociative(); + + $query = $this->entityManager->createQuery($queryDocuments); + $query->setParameters([ + 'ruleId' => $this->entityManager->getRepository(Rule::class)->findOneBy(['id' => $this->ruleId, 'deleted' => false]), + 'status' => $status, + 'jobId' => $this->jobId, + ]); + $query->setMaxResults($this->limit); + // Lock the table during the query until all documents are locked + $query->setLockMode(\Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE); + $documents = $query->getArrayResult(); // array of ForumUser objects getArrayResult + // Lock all the document + if (!empty($documents)) { + foreach ($documents as $document) { + // Lock focument without checking bacause we have selected only document with no lock + $this->setDocumentLock($document['id'], false); + } + } + $this->connection->commit(); // -- COMMIT TRANSACTION - release documents + return $documents; } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION - release documents $this->logger->error('Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); } } @@ -1959,20 +2020,35 @@ public function getSendDocuments($type, $documentId=null, $table = 'target', $pa // Si un document est en paramètre alors on filtre la requête sur le document if (!empty($documentId)) { $documentFilter = " document.id = '$documentId' - AND document.deleted = 0 "; + AND document.deleted = 0 + AND ( + document.job_lock = '' + OR document.job_lock = '$this->jobId' + ) + "; } elseif (!empty($parentDocId)) { $documentFilter = " document.parent_id = '$parentDocId' AND document.rule_id = '$parentRuleId' - AND document.deleted = 0 "; + AND document.deleted = 0 + AND ( + document.job_lock = '' + OR document.job_lock = '$this->jobId' + ) + "; // No limit when it comes to child rule. A document could have more than $limit child documents $limit = ''; } // Sinon on récupère tous les documents élligible pour l'envoi else { - $documentFilter = " document.rule_id = '$this->ruleId' + $documentFilter = " document.rule_id = '$this->ruleId' AND document.status = 'Ready_to_send' - AND document.deleted = 0 - AND document.type = '$type' "; + AND document.deleted = 0 + AND document.type = '$type' + AND ( + document.job_lock = '' + OR document.job_lock = '$this->jobId' + ) + "; } // Sélection de tous les documents au statut transformed en attente de création pour la règle en cours $sql = "SELECT document.id id_doc_myddleware, document.target_id, document.source_date_modified @@ -1983,7 +2059,6 @@ public function getSendDocuments($type, $documentId=null, $table = 'target', $pa $stmt = $this->connection->prepare($sql); $result = $stmt->executeQuery(); $documents = $result->fetchAllAssociative(); - foreach ($documents as $document) { // If the rule is a parent, we have to get the data of all rules child $childRules = $this->getChildRules(); @@ -2008,14 +2083,34 @@ public function getSendDocuments($type, $documentId=null, $table = 'target', $pa } } } - $data = $this->getDocumentData($document['id_doc_myddleware'], strtoupper(substr($table, 0, 1))); - if (!empty($data)) { - $return[$document['id_doc_myddleware']] = array_merge($document, $data); + + // Lock the document + $documentLock = $this->setDocumentLock($document['id_doc_myddleware']); + if($documentLock['success']) { + // Get document data + $data = $this->getDocumentData($document['id_doc_myddleware'], strtoupper(substr($table, 0, 1))); + if (!empty($data)) { + // Document is added to the result to be sent + $return[$document['id_doc_myddleware']] = array_merge($document, $data); + } else { + $error = 'No data found in the document'; + } } else { - $return['error'] = 'No data found in the document'; - } + $error = $documentLock['error']; + } + // If error we create a log on the document but we keep the status ready to send + if (!empty($error)) { + $param['id_doc_myddleware'] = $document['id_doc_myddleware']; + $param['jobId'] = $this->jobId; + $param['api'] = $this->api; + // Set the param values and clear all document attributes + if($this->documentManager->setParam($param, true)) { + $this->documentManager->generateDocLog('W', $error); + } else { + $this->logger->error('Job '.$this->jobId.' : Failed to create log for the document '.$document['id_doc_myddleware'].'. '); + } + } } - if (!empty($return)) { return $return; } @@ -2023,6 +2118,56 @@ public function getSendDocuments($type, $documentId=null, $table = 'target', $pa return null; } + // Set the document lock + protected function setDocumentLock($docId, $check = true) { + try { + // Get the job lock on the document + if ($check) { + $documentQuery = 'SELECT * FROM document WHERE id = :doc_id'; + $stmt = $this->connection->prepare($documentQuery); + $stmt->bindValue(':doc_id', $docId); + $documentResult = $stmt->executeQuery(); + $documentData = $documentResult->fetchAssociative(); // 1 row + } + + // If document already lock by the current job (rerun action for example), we return true; + if ( + $check + AND $documentData['job_lock'] == $this->jobId + ) { + return array('success' => true); + // If document not locked, we lock it. + } elseif ( + !$check + OR ( + $check + AND empty($documentData['job_lock']) + ) + ) { + $now = gmdate('Y-m-d H:i:s'); + $query = ' UPDATE document + SET + date_modified = :now, + job_lock = :job_id + WHERE + id = :id + '; + $stmt = $this->connection->prepare($query); + $stmt->bindValue(':now', $now); + $stmt->bindValue(':job_id', $this->jobId); + $stmt->bindValue(':id', $docId); + $result = $stmt->executeQuery(); + return array('success' => true); + // Error for all other cases + } else { + return array('success' => false, 'error' => 'The document is locked by the task '.$documentData['job_lock'].'. '); + } + } catch (\Exception $e) { + // $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + return array('success' => false, 'error' => 'Failed to lock the document '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + } + } + // Permet de charger tous les champs de la règle protected function setRuleField() { @@ -2220,23 +2365,6 @@ protected function setConfigParam() } } - /** - * Checks whether a job is still active then commits the transaction. - * - * @throws \Doctrine\DBAL\Exception - */ - protected function commit($newTransaction) - { - // Rollback if the job has been manually stopped - if ('Start' != $this->getJobStatus()) { - throw new \Exception('The task has been stopped manually during the document creation. No document generated. '); - } - $this->connection->commit(); // -- COMMIT TRANSACTION - $this->entityManager->flush(); - if ($newTransaction) { - $this->connection->beginTransaction(); - } - } /** * Parameter de la règle choix utilisateur. diff --git a/src/Repository/DocumentRepository.php b/src/Repository/DocumentRepository.php index ea34b4cbf..cc02bf289 100644 --- a/src/Repository/DocumentRepository.php +++ b/src/Repository/DocumentRepository.php @@ -429,4 +429,17 @@ public static function findStatusType(EntityManagerInterface $entityManager) $finalResults = array_flip($curatedResults); return $finalResults; } + + // Remove lock from document using a job id + public function removeLock($jobId) { + $empty = null; + $q = $this->createQueryBuilder('d') + ->update() + ->set('d.jobLock', ':empty') + ->where('d.jobLock = :jobLock') + ->setParameter('jobLock', $jobId) + ->setParameter('empty', $empty) + ->getQuery(); + $q->execute(); + } } diff --git a/src/Repository/RuleRepository.php b/src/Repository/RuleRepository.php index a41783d6b..7c5b68b09 100644 --- a/src/Repository/RuleRepository.php +++ b/src/Repository/RuleRepository.php @@ -305,4 +305,17 @@ public static function findNameSlug(EntityManagerInterface $entityManager) return $finalResults; } + // Remove lock from rule using a job id + public function removeLock($jobId) { + $empty = null; + $qr = $this->createQueryBuilder('r') + ->update() + ->set('r.readJobLock', ':empty') + ->where('r.readJobLock = :readJobLock') + ->setParameter('readJobLock', $jobId) + ->setParameter('empty', $empty) + ->getQuery(); + $qr->execute(); + } + } diff --git a/src/Solutions/airtable.php b/src/Solutions/airtable.php index 49aeef991..8a1b879c7 100644 --- a/src/Solutions/airtable.php +++ b/src/Solutions/airtable.php @@ -403,6 +403,12 @@ public function upsert(string $method, array $param): array ++$i; continue; } + + // If no data we skip this record + if (empty($data)) { + unset($records[$idDoc]); + continue; + } $body['records'][$i]['fields'] = $data; @@ -435,7 +441,7 @@ public function upsert(string $method, array $param): array ++$i; } - // Airtable fiueld can contains space which is not compatible in Myddleware. + // Airtable field can contains space which is not compatible in Myddleware. // Because we replace space by ___ in Myddleware, we change ___ to space before sending data to Airtable if (!empty($body['records'])) { foreach ($body['records'] as $keyRecord => $record) { @@ -473,23 +479,25 @@ public function upsert(string $method, array $param): array $content = $response->toArray(); if (!empty($content)) { $i = 0; - foreach ($records as $idDoc => $data) { - $record = $content['records'][$i]; - if (!empty($record['id'])) { - $result[$idDoc] = [ - 'id' => $record['id'], - 'error' => false, - ]; - } else { - $result[$idDoc] = [ - 'id' => '-1', - 'error' => 'Failed to send data. Message from Airtable : '.print_r($content['records'][$i], true), - ]; - } - ++$i; - // Modification du statut du flux - $this->updateDocumentStatus($idDoc, $result[$idDoc], $param); - } + if (!empty($records)) { + foreach ($records as $idDoc => $data) { + $record = $content['records'][$i]; + if (!empty($record['id'])) { + $result[$idDoc] = [ + 'id' => $record['id'], + 'error' => false, + ]; + } else { + $result[$idDoc] = [ + 'id' => '-1', + 'error' => 'Failed to send data. Message from Airtable : '.print_r($content['records'][$i], true), + ]; + } + ++$i; + // Modification du statut du flux + $this->updateDocumentStatus($idDoc, $result[$idDoc], $param); + } + } } else { throw new Exception('Failed to send the record but no error returned by Airtable. '); } diff --git a/src/Solutions/database.php b/src/Solutions/database.php index fec2f3320..cc55443ad 100644 --- a/src/Solutions/database.php +++ b/src/Solutions/database.php @@ -207,7 +207,6 @@ public function get_module_fields($module, $type = 'source', $param = null): arr public function readData($param) { $result = []; - $result['date_ref'] = $param['date_ref']; // Decode field name (converted in method get_module_fields) $param['fields'] = array_map('rawurldecode', $param['fields']); try { @@ -215,6 +214,8 @@ public function readData($param) if (empty($param['date_ref'])) { $param['date_ref'] = 0; } + $result['date_ref'] = $param['date_ref']; + if (empty($param['limit'])) { $param['limit'] = 100; } diff --git a/src/Solutions/moodle.php b/src/Solutions/moodle.php index fb27229e6..0d20bb4b8 100644 --- a/src/Solutions/moodle.php +++ b/src/Solutions/moodle.php @@ -261,8 +261,8 @@ public function read($param): array } } } catch (\Exception $e) { - $result['error'] = 'Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->logger->error($result['error']); + throw new \Exception('Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); } return $result; } diff --git a/src/Solutions/solution.php b/src/Solutions/solution.php index 0091f5e71..b0ac2cc75 100644 --- a/src/Solutions/solution.php +++ b/src/Solutions/solution.php @@ -353,7 +353,7 @@ public function readData($param) $result['date_ref'] = $param['date_ref']; } } catch (\Exception $e) { - $result['error'] = 'Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; + $result['error'] = (!empty($param['rule']['id']) ? 'Error in rule '.$param['rule']['id'].' : ' : 'Error : ').$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; } return $result; diff --git a/src/Solutions/sugarcrm.php b/src/Solutions/sugarcrm.php index 534da49ac..12721f23a 100644 --- a/src/Solutions/sugarcrm.php +++ b/src/Solutions/sugarcrm.php @@ -153,7 +153,7 @@ public function get_modules($type = 'source') /** * @throws \Exception */ - protected function isManyToManyRel($module): bool + protected function isManyToManyRel($module) { if ( !empty($module) @@ -274,14 +274,62 @@ public function get_module_fields($module, $type = 'source', $param = null): arr } } - // public function readData($param) - + // public function readRelationship($param) /** * @throws \Exception */ + public function readRelationship($param, $rel) { + // Set the parameters for relationship reading + $filterArgs = [ + 'max_num' => $param['limit'], + 'offset' => 0, + 'fields' => implode(',',$param['fields']), + 'order_by' => 'date_modified', + 'deleted' => $param['ruleParams']['deletion'], + ]; + // The call for relationship required the id of the root record + if (empty($param['query'])) { + throw new \Exception('No query parameter, failed to read relationship'); + } + // The query parameter must be the reference id. + // For example if we try to read the users from a team, query must contains the team id + $refereceId = current($param['query']); + + // Get the record using the input parameters + $getRecords = $this->sugarAPI->filterRelated(key($param['query']), $refereceId, $rel->rhs_table)->execute($filterArgs); + $response = $getRecords->getResponse(); + // Format response if http return = 200 + if ('200' == $response->getStatus()) { + $body = $getRecords->getResponse()->getBody(false); + if (!empty($body->records)) { + $records = $body->records; + } + } else { + $bodyError = $response->getBody(); + throw new \Exception('Status '.$response->getStatus().' : '.$bodyError['error'].', '.$bodyError['error_message']); + } + // Format records to result format + if (!empty($records)) { + foreach ($records as $record) { + // The record id is build with both ids from the relationship + $recordId = $refereceId.'_'.$record->id; + $result[$recordId]['id'] = $recordId; + $result[$recordId]['date_modified'] = $record->date_modified; + // Get both ids + $result[$recordId][$rel->join_key_lhs] = $refereceId; + $result[$recordId][$rel->join_key_rhs] = $record->id; + } + } + return $result; + } + public function read($param) { $result = []; + $rel = $this->isManyToManyRel($param['module']); + if ($rel !== false) { + return $this->readRelationship($param, $rel); + } // Manage delete option to enable $deleted = false; @@ -332,10 +380,11 @@ public function read($param) $bodyError = $response->getBody(); throw new \Exception('Status '.$response->getStatus().' : '.$bodyError['error'].', '.$bodyError['error_message']); } + // Format records to result format - if (!empty($records)) { + if (!empty($records)) { + // Manage deletion by adding the flag Myddleware_deletion to the record foreach ($records as $record) { - // Manage deletion by adding the flag Myddleware_deletion to the record if ( true == $deleted and !empty($record->deleted) @@ -345,8 +394,21 @@ public function read($param) // At least one non deleted record read $onlyDeletion = false; } - - foreach ($param['fields'] as $field) { + } + // Error if only deletion records read + if ($onlyDeletion) { + if (count($result) >= $param['limit']) { + throw new \Exception('Only deletion records read. It is not possible to determine the reference date with only deletion. Please increase the rule limit to include non deletion records.'); + } else { + // If only deletion without new or modified record, we send no result. We wait for new or modified record. + // Otherwise we will read the deleted record until a new or modified record is read because Sugar doesn't return modified date for deleted record. + return array(); + } + } + + // Build the results + foreach ($records as $record) { + foreach ($param['fields'] as $field) { // Sugar returns multilist value as array if ( !empty($record->$field) @@ -354,7 +416,12 @@ public function read($param) ) { // Some fields can be an object like teamname field if (is_object($record->$field[0])) { - $record->$field = $record->$field[0]->name; + $fieldObjectList = array(); + // Get all ids of the object list + foreach($record->$field as $fieldObject) { + $fieldObjectList[] = $fieldObject->id; + } + $record->$field = implode(',', $fieldObjectList); } else { $record->$field = implode(',', $record->$field); } @@ -366,17 +433,6 @@ public function read($param) $result[$record->id]['date_modified'] = end($records)->date_modified; } } - - // Error if only deletion records read - if ($onlyDeletion) { - if (count($result) >= $param['limit']) { - throw new \Exception('Only deletion records read. It is not possible to determine the reference date with only deletion. Please increase the rule limit to include non deletion records.'); - } else { - // If only deletion without new or modified record, we send no result. We wait for new or modified record. - // Otherwise we will read the deleted record until a new or modified record is read because Sugar doesn't return modified date for deleted record. - return array(); - } - } } return $result; } diff --git a/templates/JobScheduler/crontab_list.html.twig b/templates/JobScheduler/crontab_list.html.twig index 4c7085863..bc7580b92 100644 --- a/templates/JobScheduler/crontab_list.html.twig +++ b/templates/JobScheduler/crontab_list.html.twig @@ -44,6 +44,8 @@ + + @@ -54,7 +56,9 @@ {{ value.command }} {{ value.period }} - {{ value.description }} + {{ value.description }} + {{ value.runningInstances }} + {{ value.maxInstances }} {{ value.lastUse|date("d/m/Y H:i:s", timezone) }} {{ value.nextRun|date("d/m/Y H:i:s", timezone) }} {{ value.enable }} diff --git a/templates/JobScheduler/edit_crontab.html.twig b/templates/JobScheduler/edit_crontab.html.twig index e492059a8..034bcc7b7 100644 --- a/templates/JobScheduler/edit_crontab.html.twig +++ b/templates/JobScheduler/edit_crontab.html.twig @@ -18,6 +18,20 @@ {{ form_widget( edit_form.description) }} +
+
+ +
+ {{ form_widget( edit_form.runningInstances) }} +
+
+
+
+ +
+ {{ form_widget( edit_form.maxInstances) }} +
+
diff --git a/templates/JobScheduler/show_crontab.html.twig b/templates/JobScheduler/show_crontab.html.twig index 8fea2b9dd..b8a883525 100644 --- a/templates/JobScheduler/show_crontab.html.twig +++ b/templates/JobScheduler/show_crontab.html.twig @@ -1,54 +1,118 @@ {% extends 'base.html.twig' %} -{% block titlesm %} {{ 'crontab.title'|trans }}{% endblock %} +{% block titlesm %} + {{ 'crontab.title'|trans }} +{% endblock %} {% block body %} -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - > - - - - - - - - - - - - - - - -
{{ entity.id }}
{{ entity.createdAt|date('Y-m-d H:i:s') }}
{{ entity.updatedAt|date('d/m/Y') }}
{{ entity.command }}
{{ entity.arguments }}
{{ entity.description }}
{{ entity.runningInstances }}
{{ entity.maxInstances }}
{{ entity.number }}
{% if entity.enable %} true {% else %} false {% endif %}
-
-
-
-{% endblock %} \ No newline at end of file +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + > + + + + + + + + + + + + + + + +
+ + {{ entity.id }}
+ + {{ entity.createdAt|date('Y-m-d H:i:s') }}
+ + {{ entity.updatedAt|date('d/m/Y') }}
+ + {{ entity.command }}
+ + {{ entity.arguments }}
+ + {{ entity.description }}
+ + {{ entity.runningInstances }}
+ + {{ entity.maxInstances }}
+ + {{ entity.number }}
+ + + {% if entity.enable %} + true + {% else %} + false + {% endif %} +
+
+
+ + +

{{ 'crontab.results'|trans }}

+
{{ "Sorting: Default" }}
+ + + + + + + + + + + + + + + {% for result in entityResult %} + + + + + + + + + + {% endfor %} + +
{{ 'jobscheduler.id'|trans }}{{ 'jobscheduler.date_created'|trans }}{{ 'jobscheduler.date_modified'|trans }}{{ 'jobscheduler.output'|trans }}{{ 'jobscheduler.run_at'|trans }}{{ 'jobscheduler.run_time'|trans }}{{ 'jobscheduler.status_code'|trans }}
{{ result.id }}{{ result.createdAt|date('Y-m-d H:i:s') }}{{ result.updatedAt|date('Y-m-d H:i:s') }}{{ result.output|raw }}{{ result.runAt|date('Y-m-d H:i:s') }}{{ result.runTime }}{{ result.statusCode }}
+
+ +
+ + + + {% endblock %} diff --git a/translations/messages.en.yml b/translations/messages.en.yml index a559b7cab..4bcce1d04 100644 --- a/translations/messages.en.yml +++ b/translations/messages.en.yml @@ -758,7 +758,12 @@ email_alert: Kind regards, jobscheduler: + id: Id command: Command + output: Output + run_at: Run at + run_time: Run time + status_code: Status code date_created: Date created active: Active list: List @@ -888,6 +893,9 @@ crontab: disableAllCrons: Your crontabs have been disabled enableAllCrons: Your crontabs have been enable create: Create crontab + running_instances: Running instances + max_instances: Max instances + results: Results massdisable: Disable all tasks massenable: Enable all tasks enableCron: On diff --git a/webpack.config.js b/webpack.config.js index cfe9678e3..4a5f39861 100755 --- a/webpack.config.js +++ b/webpack.config.js @@ -27,6 +27,7 @@ Encore .addEntry('rulelist', './assets/js/rulelist.js') .addEntry('fiche', './assets/js/fiche.js') .addEntry('filter', './assets/js/filter.js') + .addEntry('crontab', './assets/js/crontab.js') // enables the Symfony UX Stimulus bridge (used in assets/bootstrap.js) .enableStimulusBridge('./assets/controllers.json') diff --git a/yarn.lock b/yarn.lock index b844a4870..023a49e57 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1506,7 +1506,6 @@ acorn-import-assertions@^1.7.6: resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== - acorn@^8.0.5, acorn@^8.5.0, acorn@^8.7.1: version "8.8.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" @@ -4017,7 +4016,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.2.0, minimist@^1.2.5: +minimist@^1.2.0: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== From 332704155961b3f9cbd57809e13c24ec384fc8c6 Mon Sep 17 00:00:00 2001 From: Myddleware Date: Thu, 11 Jan 2024 10:07:23 +0100 Subject: [PATCH 002/452] Hotfix (#1110) * Hotfix333 (#1064) * SuiteCRM : Add field filecontents for notes module Signed-off-by: myddleware * Fix : Catch all errors in formula Signed-off-by: myddleware * Fix : catch formula errors Signed-off-by: myddleware * Fix : remove slashes to compare data Signed-off-by: myddleware * Manage null value for comparaison Signed-off-by: myddleware * Fix bug on relate when a several source ids exist for a target id and one of them is deleted Signed-off-by: myddleware * Fix bug target id Signed-off-by: myddleware --------- Signed-off-by: myddleware * Fix : Do not search duplicates on an empty field Signed-off-by: myddleware * Change index on job table Signed-off-by: myddleware * Rebuild all document table indexes Fix home page queries according to the new indexes Signed-off-by: myddleware * Change index on contacts for document search purpose Signed-off-by: myddleware * Feat : manage the cancellation of the document using formula and variable mdw_cancel_document Signed-off-by: myddleware * Feat : improve performance rerunerror job Signed-off-by: myddleware * Moolde : manage error return Signed-off-by: myddleware * Change hotfix version Signed-off-by: myddleware * Fix : Remove user filter from document search query Signed-off-by: myddleware * Solution : get logic to calculate ref date in getReferenceCall even if no result Sendinblue : event read function - change logic to not use offset anymore Signed-off-by: myddleware * Remove unuse method Signed-off-by: myddleware * Fix : No rerun if no document Signed-off-by: myddleware * Change return Signed-off-by: myddleware --------- Signed-off-by: myddleware --- .env | 2 +- src/Controller/FilterController.php | 5 +- src/Controller/FluxController.php | 7 +- src/Entity/Job.php | 2 +- src/Manager/DocumentManager.php | 56 +++++---- src/Manager/JobManager.php | 9 +- src/Repository/DocumentRepository.php | 9 +- src/Solutions/moodle.php | 2 +- src/Solutions/sendinblue.php | 170 ++++++++++---------------- src/Solutions/solution.php | 16 +-- src/Solutions/suitecrm.php | 12 +- 11 files changed, 134 insertions(+), 156 deletions(-) diff --git a/.env b/.env index 5e73d9fa9..6241cee00 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -MYDDLEWARE_VERSION=3.3.4a +MYDDLEWARE_VERSION=3.3.4b APP_SECRET=Thissecretisnotsosecretchangeit APP_ENV=prod diff --git a/src/Controller/FilterController.php b/src/Controller/FilterController.php index 604f51757..afccfab1c 100644 --- a/src/Controller/FilterController.php +++ b/src/Controller/FilterController.php @@ -731,16 +731,13 @@ protected function searchDocuments($data, $page = 1, $limit = 1000) { document.type, document.attempt, document.global_status, - users.username, rule.name as rule_name, rule.module_source, rule.module_target, rule.id as rule_id FROM document INNER JOIN rule - ON document.rule_id = rule.id - INNER JOIN users - ON document.created_by = users.id " + ON document.rule_id = rule.id " .$join. " WHERE document.deleted = 0 " diff --git a/src/Controller/FluxController.php b/src/Controller/FluxController.php index 1c5be7e4c..9594546fd 100644 --- a/src/Controller/FluxController.php +++ b/src/Controller/FluxController.php @@ -504,22 +504,17 @@ protected function searchDocuments($data, $page = 1, $limit = 1000) { document.type, document.attempt, document.global_status, - users.username, rule.name as rule_name, rule.id as rule_id FROM document INNER JOIN rule - ON document.rule_id = rule.id - INNER JOIN users - ON document.created_by = users.id " + ON document.rule_id = rule.id " .$join. " WHERE document.deleted = 0 " .$where. " ORDER BY document.date_modified DESC" ." LIMIT ". $limit; - - $stmt = $this->getDoctrine()->getManager()->getConnection()->prepare($query); // Add parameters to the query // Source content diff --git a/src/Entity/Job.php b/src/Entity/Job.php index 3583480b6..3da3c3e50 100755 --- a/src/Entity/Job.php +++ b/src/Entity/Job.php @@ -33,7 +33,7 @@ /** * @ORM\Entity(repositoryClass="App\Repository\JobRepository") * @ORM\Table(name="job", indexes={ - * @ORM\Index(name="index_status", columns={"status"}) + * @ORM\Index(name="index_status_begin", columns={"status","begin"}) *}) */ class Job diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 55fda7552..7030f2dbd 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -936,7 +936,14 @@ public function transformDocument(): bool try { // Transformation des données et insertion dans la table target $transformed = $this->updateTargetTable(); - if ($transformed) { + if (!empty($transformed)) { + // If the value mdw_cancel_document is found in the target data of the document after transformation we cancel the document + if (array_search('mdw_cancel_document',$transformed) !== false) { + $this->message .= 'The document contains the value mdw_cancel_document. '; + $this->typeError = 'W'; + $this->updateStatus('Cancel'); + return false; + } // If the type of this document is Create and if the field Myddleware_element_id isn't empty, // it means that the target ID is mapped in the rule field // In this case, we force the document's type to Update because Myddleware will update the record into the target application @@ -1062,7 +1069,6 @@ public function getTargetDataDocument(): bool if (!empty($target[$duplicate_field])) { $searchFields[$duplicate_field] = $target[$duplicate_field]; } - $searchFields[$duplicate_field] = $target[$duplicate_field]; } if (!empty($searchFields)) { $history = $this->getDocumentHistory($searchFields); @@ -1227,10 +1233,12 @@ protected function checkNoChange($history): bool // If one is different we stop the function if (!empty($this->ruleFields)) { foreach ($this->ruleFields as $field) { - if ( - trim($history[$field['target_field_name']]) != trim($target[$field['target_field_name']]) - ) { - // We check if both are empty not depending of the type 0 = "" + if (stripslashes(trim($history[$field['target_field_name']])) != stripslashes(trim($target[$field['target_field_name']]))) { + // Null text is considered as empty for comparaison + if ($target[$field['target_field_name']] == 'null') { + $target[$field['target_field_name']] = ''; + } + // We check if both are empty not depending of the type 0 = "" if ( empty($history[$field['target_field_name']]) and empty($target[$field['target_field_name']]) @@ -1450,7 +1458,7 @@ protected function updateHistoryTable($dataTarget): bool } // Mise à jour de la table des données cibles - protected function updateTargetTable(): bool + protected function updateTargetTable() { try { // Loop on every target field and calculate the value @@ -1491,14 +1499,14 @@ protected function updateTargetTable(): bool throw new \Exception('No target data found. Failed to create target data. '); } - return true; + return $targetField; } catch (Exception $e) { $this->message .= 'Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->typeError = 'E'; $this->logger->error($this->message); } - return false; + return null; } /* @@ -2343,8 +2351,8 @@ protected function getTargetId($ruleRelationship, $record_id) if ('-1' == $direction) { $sqlParams = " SELECT source_id record_id, - document.id document_id, - document.type document_type + GROUP_CONCAT(DISTINCT document.id) document_id, + GROUP_CONCAT(DISTINCT document.type) types FROM document WHERE document.rule_id = :ruleRelateId @@ -2355,13 +2363,14 @@ protected function getTargetId($ruleRelationship, $record_id) document.global_status = 'Close' OR document.status = 'No_send' ) - ORDER BY date_created DESC + GROUP BY source_id + HAVING types NOT LIKE '%D%' LIMIT 1"; } elseif ('1' == $direction) { $sqlParams = " SELECT target_id record_id, - document.id document_id, - document.type document_type + GROUP_CONCAT(DISTINCT document.id) document_id, + GROUP_CONCAT(DISTINCT document.type) types FROM document WHERE document.rule_id = :ruleRelateId @@ -2372,7 +2381,8 @@ protected function getTargetId($ruleRelationship, $record_id) document.global_status = 'Close' OR document.status = 'No_send' ) - ORDER BY date_created DESC + GROUP BY target_id + HAVING types NOT LIKE '%D%' LIMIT 1"; } else { throw new \Exception('Failed to find the direction of the relationship with the rule_id '.$ruleRelationship['field_id'].'. '); @@ -2420,16 +2430,18 @@ protected function getTargetId($ruleRelationship, $record_id) $stmt->bindValue(':record_id', $record_id); $result = $stmt->executeQuery(); $result = $result->fetchAssociative(); - } - if (!empty($result['record_id'])) { - // If the latest valid document sent is a deleted one, then the target id can't be use as the record has been deleted from the target solution - if ('D' == $result['document_type']) { - return null; - } + // In cas of several document found we get only the last one + if ( + !empty($result['document_id']) + AND strpos($result['document_id'], ',') + ) { + $result['document_id'] = end(explode(',',$result['document_id'])); + } + } + if (!empty($result['record_id'])) { return $result; } - return null; } catch (\Exception $e) { $this->message .= 'Error getTargetId : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; diff --git a/src/Manager/JobManager.php b/src/Manager/JobManager.php index 450f4fdf0..0a3d943af 100644 --- a/src/Manager/JobManager.php +++ b/src/Manager/JobManager.php @@ -269,7 +269,7 @@ public function sendDocuments() } // Ecriture dans le système source et mise à jour de la table document - public function runError($limit, $attempt) + public function runError($limit, $attempt) { try { $ruleId = ''; @@ -282,14 +282,17 @@ public function runError($limit, $attempt) global_status = 'Error' AND deleted = 0 AND attempt <= :attempt - ORDER BY ruleorder.order ASC, source_date_modified ASC + ORDER BY ruleorder.order ASC, document.rule_id, source_date_modified ASC LIMIT $limit"; $stmt = $this->connection->prepare($sqlParams); $stmt->bindValue('attempt', $attempt); $result = $stmt->executeQuery(); $documentsError = $result->fetchAllAssociative(); if (!empty($documentsError)) { - // include_once 'rule.php'; + $ruleId = null; + $this->ruleManager->setJobId($this->id); + $this->ruleManager->setManual($this->manual); + $this->ruleManager->setApi($this->api); foreach ($documentsError as $documentError) { // Load the rule only if it has changed if ($ruleId != $documentError['rule_id']) { diff --git a/src/Repository/DocumentRepository.php b/src/Repository/DocumentRepository.php index cc02bf289..818704427 100644 --- a/src/Repository/DocumentRepository.php +++ b/src/Repository/DocumentRepository.php @@ -176,16 +176,11 @@ public function countTransferRule(User $user = null) ->select('COUNT(d) as nb, rule.name') ->join('d.rule', 'rule') ->andWhere('d.deleted = 0') - ->andWhere('d.globalStatus = :close') - ->setParameter('close', 'Close') + ->andWhere('d.status = :send') + ->setParameter('send', 'Send') ->groupBy('rule.name') ->orderBy('nb', 'DESC'); - if ($user && !$user->isAdmin()) { - $qb->andWhere('d.createdBy = :user') - ->setParameter('user', $user); - } - return $qb->getQuery()->getResult(); } diff --git a/src/Solutions/moodle.php b/src/Solutions/moodle.php index 0d20bb4b8..87de84960 100644 --- a/src/Solutions/moodle.php +++ b/src/Solutions/moodle.php @@ -484,7 +484,7 @@ public function updateData($param): array // Check if there is a warning if ( - !empty($xml) + !empty($xml->SINGLE->KEY) AND $xml->count() != 0 // Empty xml AND $xml->SINGLE->KEY->attributes()->__toString() == 'warnings' AND !empty($xml->SINGLE->KEY->MULTIPLE->SINGLE->KEY[3]) diff --git a/src/Solutions/sendinblue.php b/src/Solutions/sendinblue.php index a9e0eb3f1..8ce2f1596 100644 --- a/src/Solutions/sendinblue.php +++ b/src/Solutions/sendinblue.php @@ -183,12 +183,11 @@ public function read($param) $result = []; // Function are differents depending on the type of record we read from Sendinblue switch ($param['module']) { - case 'transactionalEmailActivity': + case 'transactionalEmailActivity': // event is required if (empty($param['ruleParams']['event'])) { throw new \Exception('No event selected. Please select an event on your rule. '); } - // As we build the id (it doesn't exist in Sendinblue), we add it to the param field $param['fields'][] = 'id'; @@ -219,50 +218,18 @@ public function read($param) } // Set call parameters when we read transactional email using reference date } else { - // Because offset is managed in this function, we don't need the +1 in the rule param limit - if ($param['limit'] > 1) { - --$param['limit']; - } - $event = $param['ruleParams']['event']; // if simulation, we init the date start to today - 30 days if ('simulation' == $param['call_type']) { - $dateStartObj = new \DateTime('NOW'); - $dateStartObj->sub(new \DateInterval('P30D')); + $dateRefObj = new \DateTime('NOW'); + $dateRefObj->sub(new \DateInterval('P30D')); } else { - $dateStartObj = new \DateTime($param['date_ref']); + // TO BE CHANGED + $dateRefObj = date_create($param['date_ref'], new \DateTimeZone("Europe/Paris")); } // Only date (not datetime) are used to filter transaction email activity - $dateStart = $dateStartObj->format('Y-m-d'); - $dateEnd = $this->getDateEnd($dateStartObj); - - // Make sure that offset exists - if (empty($param['ruleParams']['offset'])) { - $param['ruleParams']['offset'] = 0; - } else { - // Change offset if the parameter exists on the rule - $offset = $param['ruleParams']['offset']; - } - - // Max call limit = 100 - // Nb call depend on limit param - if ($param['limit'] > $this->limitEmailActivity) { - $nbCall = floor($param['limit'] / $this->limitEmailActivity); - // Add 1 call if needid (using modulo function) - if ($param['limit'] % $this->limitEmailActivity != 0) { - $limitLastCall = $param['limit'] % $this->limitEmailActivity; - ++$nbCall; - } - $limitCall = $this->limitEmailActivity; - } else { - // If rule limit < $limitCall , there is no need to call more records - $limitCall = $param['limit']; - } - - // If rule limit modulo limitcall is null then last call will be equal to limitcall - if ($param['limit'] % $this->limitEmailActivity == 0) { - $limitLastCall = $this->limitEmailActivity; - } + $dateStart = $dateRefObj->format('Y-m-d'); + $dateEnd = $dateRefObj->format('Y-m-d'); } $apiInstance = new \SendinBlue\Client\Api\TransactionalEmailsApi(new \GuzzleHttp\Client(), $this->config); @@ -270,66 +237,47 @@ public function read($param) if (false !== $contactRequested) { $apiContactInstance = new \SendinBlue\Client\Api\ContactsApi(new \GuzzleHttp\Client(), $this->config); } - - for ($i = 1; $i <= $nbCall; ++$i) { - // The limit can be different for the last call (in case of several call) - if ( - $i == $nbCall - and $nbCall > 1 - ) { - $limitCall = $limitLastCall; - } - $resultApi = $apiInstance->getEmailEventReport($limitCall, $offset, $dateStart, $dateEnd, null, null, $event, null, $messageId, null, 'asc'); - - if (!empty(current($resultApi)['events'])) { - $events = current($resultApi)['events']; - - // Add records read into result array - foreach ($events as $record) { - ++$offset; - $record['id'] = $record['messageId'].'__'.$record['event']; - - // if the contactid is requested, we use the email to get it - if (false !== $contactRequested) { - try { - $resultContactApi = $apiContactInstance->getContactInfo($record['email']); - if (!empty(current($resultContactApi)['id'])) { - $record['contactId'] = current($resultContactApi)['id']; - } - } catch (\Exception $e) { - $record['contactId'] = ''; - } - } - - $records[] = $record; - // IF the date change, we set the offset to 0 (because filter is only on date and not dateTime - /// Date start we be changed with the date of the current record - // Date ref will also be changed - $dateRecordObj = new \DateTime($record['date']); - if ( - empty($param['query']['id']) // No offset management if search by id - and $dateRecordObj->format('Y-m-d') != $dateStartObj->format('Y-m-d') - ) { - $dateStartObj = $dateRecordObj; - $dateStart = $dateStartObj->format('Y-m-d'); - $dateEnd = $this->getDateEnd($dateStartObj); - - // Offset = 1 not 0 becquse we have alredy read the first record of the day - $offset = 1; - } - } - - // If the limit hasn't been reached, it means there is no more result to read. We stop the read action. - if (count($events) < $this->limitEmailActivity) { - break; - } - } - } - // Save the offset value on the rule - // No offset management if search by id - if (empty($param['query']['id'])) { - $result['ruleParams'][] = ['name' => 'offset', 'value' => $offset]; - } + $offset = 0; + $exit = false; + do { + $resultApi = $apiInstance->getEmailEventReport($this->limitEmailActivity, $offset, $dateStart, $dateEnd, null, null, $event, null, $messageId, null, 'desc'); + + // Exit the loop if no result + if (empty(current($resultApi)['events'])) { + $exit = true; + break; + } + // Add records read into result array + $events = current($resultApi)['events']; + foreach ($events as $record) { + $offset++; + $record['id'] = $record['messageId'].'__'.$record['event']; + + // if the contactid is requested, we use the email to get it + if (false !== $contactRequested) { + try { + $resultContactApi = $apiContactInstance->getContactInfo($record['email']); + if (!empty(current($resultContactApi)['id'])) { + $record['contactId'] = current($resultContactApi)['id']; + } + } catch (\Exception $e) { + $record['contactId'] = ''; + } + } + + $dateRecordObj = \DateTime::createFromFormat(DATE_RFC3339_EXTENDED, $record['date']); + if ($dateRefObj->format('U') > $dateRecordObj->format('U')) { + $exit = true; + break; + } + $records[] = $record; + } + + // If the limit hasn't been reached, it means there is no more result to read. We stop the read action. + if (count($events) < $this->limitEmailActivity) { + $exit = true; + } + } while (!$exit); break; case 'contacts': $apiInstance = new \SendinBlue\Client\Api\ContactsApi(new \GuzzleHttp\Client(), $this->config); @@ -423,11 +371,27 @@ public function read($param) } } } - return $result; } - protected function getDateEnd($dateObj): string + // Method de find the date ref after a read call + protected function getReferenceCall($param, $result) + { + if ($param['module'] == 'transactionalEmailActivity') { + $currentDate = new DateTime(); + $dateRefObj = new \DateTime($param['date_ref']); + // If date ref < today then we force the referenece date to date+1 at midnight + if ($dateRefObj->format('Y-m-d') < $currentDate->format('Y-m-d')) { + $dateRefObj->modify('+1 day'); + $dateRefObj->setTime(0, 0, 0); + return $dateRefObj->format('Y-m-d H:i:s'); + } + } + // Call parent function (calsse solution + return parent::getReferenceCall($param, $result); + } + +/* protected function getDateEnd($dateObj): string { $dateEndObj = clone $dateObj; $dateEndObj->add(new \DateInterval('P30D')); @@ -438,7 +402,7 @@ protected function getDateEnd($dateObj): string } return $dateEndObj->format('Y-m-d'); - } + } */ //fonction for get all your transactional email activity public function EmailTransactional($param): \SendinBlue\Client\Model\GetEmailEventReport diff --git a/src/Solutions/solution.php b/src/Solutions/solution.php index b0ac2cc75..4cbdbd165 100644 --- a/src/Solutions/solution.php +++ b/src/Solutions/solution.php @@ -341,17 +341,15 @@ public function readData($param) break; } } - - // Calculate the reference call - $result['date_ref'] = $this->getReferenceCall($param, $result); - if (empty($result['date_ref'])) { - throw new \Exception('Failed to get the reference call.'); - } } else { // Init values if no result $result['count'] = 0; - $result['date_ref'] = $param['date_ref']; } + // Calculate the reference call + $result['date_ref'] = $this->getReferenceCall($param, $result); + if (empty($result['date_ref'])) { + throw new \Exception('Failed to get the reference call.'); + } } catch (\Exception $e) { $result['error'] = (!empty($param['rule']['id']) ? 'Error in rule '.$param['rule']['id'].' : ' : 'Error : ').$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; } @@ -1037,6 +1035,10 @@ protected function getParamLogin($connId): array // Method de find the date ref after a read call protected function getReferenceCall($param, $result) { + // Keep the same date ref if no result + if (empty($result['count'])) { + return $param['date_ref']; + } // Result is sorted, the last one is the oldest one return end($result['values'])['date_modified']; } diff --git a/src/Solutions/suitecrm.php b/src/Solutions/suitecrm.php index f6f5dd3bd..20c981882 100644 --- a/src/Solutions/suitecrm.php +++ b/src/Solutions/suitecrm.php @@ -352,7 +352,17 @@ public function get_module_fields($module, $type = 'source', $param = null): arr } } } - + // Add field filecontents for notes module + if ($module == 'Notes') { + $this->moduleFields['filecontents'] = [ + 'label' => 'File contents', + 'type' => 'text', + 'type_bdd' => 'text', + 'required' => 0, + 'required_relationship' => 0, + 'relate' => false, + ]; + } return $this->moduleFields; } catch (\Exception $e) { return false; From fbc263b8dbaf177d47084718e98c1f9a4489cbf8 Mon Sep 17 00:00:00 2001 From: myddleware Date: Thu, 11 Jan 2024 10:14:21 +0100 Subject: [PATCH 003/452] Init version Signed-off-by: myddleware --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index 6241cee00..1683b5eb6 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -MYDDLEWARE_VERSION=3.3.4b +MYDDLEWARE_VERSION=3.4.0a APP_SECRET=Thissecretisnotsosecretchangeit APP_ENV=prod From 18b39f4af7317a39932d07e3492e25af99a7c44f Mon Sep 17 00:00:00 2001 From: myddleware Date: Fri, 12 Jan 2024 16:55:50 +0100 Subject: [PATCH 004/452] Prestashop : manage reference date if no result Signed-off-by: myddleware --- src/Solutions/prestashop.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Solutions/prestashop.php b/src/Solutions/prestashop.php index b2cc58c5f..8ed37b9bf 100644 --- a/src/Solutions/prestashop.php +++ b/src/Solutions/prestashop.php @@ -593,7 +593,10 @@ public function read($param): ?array */ protected function getReferenceCall($param, $result) { - // IF the reference is a date + // Keep the same date ref if no result + if (empty($result['count'])) { + return $param['date_ref']; + } if ($this->referenceIsDate($param['module'])) { // Add 1 second to the date ref because the read function is a >= not a > $date = new \DateTime(end($result['values'])['date_modified']); From 9b80281b137fc3b93cc0c05d98ad8715073dd45c Mon Sep 17 00:00:00 2001 From: myddleware Date: Mon, 22 Jan 2024 11:56:57 +0100 Subject: [PATCH 005/452] Airtable : New API version + fix Signed-off-by: myddleware --- src/Solutions/airtable.php | 63 ++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/src/Solutions/airtable.php b/src/Solutions/airtable.php index 8a1b879c7..6006dbcc1 100644 --- a/src/Solutions/airtable.php +++ b/src/Solutions/airtable.php @@ -57,7 +57,7 @@ class airtablecore extends solution * This is initialised to 'Contacts' by default as I've assumed that would be the most common possible value. * However, this can of course be changed to any table value already present in your Airtable base. */ - protected array $tableName = []; + protected array $tableName; /** * Can't be greater than 100. @@ -106,7 +106,11 @@ public function login($paramConnexion) $this->token = $this->paramConnexion['apikey']; // We test the connection to the API with a request on Module/Table (change the value of tableName to fit your needs) $client = HttpClient::create(); - $options = ['auth_bearer' => $this->token]; + $options = [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $this->token, + ], + ]; $response = $client->request('GET', $this->airtableURL.$this->projectID.'/'.$this->tableName[$this->projectID], $options); $statusCode = $response->getStatusCode(); $contentType = $response->getHeaders()['content-type'][0]; @@ -274,9 +278,16 @@ public function readData($param): array ++$currentCount; foreach ($param['fields'] as $field) { if (!empty($record['fields'][$field])) { - // If teh value is an array (relation), we take the first entry + // If the value is an array (relation), we take the first entry if (is_array($record['fields'][$field])) { - $result['values'][$record['id']][$field] = $record['fields'][$field][0]; + // if the array has a length of 1, we take the first entry + if (count($record['fields'][$field]) === 1) { + $result['values'][$record['id']][$field] = $record['fields'][$field][0]; + } else { + // if the array has a larger length than 1 we convert the array into a string separated by comma and space + $result['values'][$record['id']][$field] = implode(',', $record['fields'][$field]); + } + } else { $result['values'][$record['id']][$field] = $record['fields'][$field]; } @@ -403,12 +414,6 @@ public function upsert(string $method, array $param): array ++$i; continue; } - - // If no data we skip this record - if (empty($data)) { - unset($records[$idDoc]); - continue; - } $body['records'][$i]['fields'] = $data; @@ -441,7 +446,7 @@ public function upsert(string $method, array $param): array ++$i; } - // Airtable field can contains space which is not compatible in Myddleware. + // Airtable fiueld can contains space which is not compatible in Myddleware. // Because we replace space by ___ in Myddleware, we change ___ to space before sending data to Airtable if (!empty($body['records'])) { foreach ($body['records'] as $keyRecord => $record) { @@ -479,25 +484,23 @@ public function upsert(string $method, array $param): array $content = $response->toArray(); if (!empty($content)) { $i = 0; - if (!empty($records)) { - foreach ($records as $idDoc => $data) { - $record = $content['records'][$i]; - if (!empty($record['id'])) { - $result[$idDoc] = [ - 'id' => $record['id'], - 'error' => false, - ]; - } else { - $result[$idDoc] = [ - 'id' => '-1', - 'error' => 'Failed to send data. Message from Airtable : '.print_r($content['records'][$i], true), - ]; - } - ++$i; - // Modification du statut du flux - $this->updateDocumentStatus($idDoc, $result[$idDoc], $param); - } - } + foreach ($records as $idDoc => $data) { + $record = $content['records'][$i]; + if (!empty($record['id'])) { + $result[$idDoc] = [ + 'id' => $record['id'], + 'error' => false, + ]; + } else { + $result[$idDoc] = [ + 'id' => '-1', + 'error' => 'Failed to send data. Message from Airtable : '.print_r($content['records'][$i], true), + ]; + } + ++$i; + // Modification du statut du flux + $this->updateDocumentStatus($idDoc, $result[$idDoc], $param); + } } else { throw new Exception('Failed to send the record but no error returned by Airtable. '); } From 9d8acb7c3b2fdb533baca864da408d0dc33645de Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 24 Jan 2024 02:14:51 +0100 Subject: [PATCH 006/452] Change status from filter to no_send when a document is changed from UPDATE in a CREATE rule after the check duplicate process. Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 7030f2dbd..f643eb5c0 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -905,7 +905,7 @@ public function checkParentDocument(): bool // Check child in a second time to avoid to run a query each time if (!$this->isChild()) { $this->message .= 'Rule mode only allows to create data. Filter because this document updates data.'; - $this->updateStatus('Filter'); + $this->updateStatus('No_send'); // In case we flter the document, we return false to stop the process when this method is called in the rerun process return false; } From 6f8e5cd49ef69762316e69ddf8ec19a82f1bcb0b Mon Sep 17 00:00:00 2001 From: myddleware Date: Thu, 8 Feb 2024 21:05:34 +0100 Subject: [PATCH 007/452] Change status if rule in crate_only and document is update Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index f643eb5c0..c9827d980 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -905,7 +905,7 @@ public function checkParentDocument(): bool // Check child in a second time to avoid to run a query each time if (!$this->isChild()) { $this->message .= 'Rule mode only allows to create data. Filter because this document updates data.'; - $this->updateStatus('No_send'); + $this->updateStatus('Filter'); // In case we flter the document, we return false to stop the process when this method is called in the rerun process return false; } @@ -1111,7 +1111,8 @@ public function getTargetDataDocument(): bool // Check child in a second time to avoid to run a query each time if (!$this->isChild()) { $this->message .= 'Rule mode only allows to create data. Filter because this document updates data.'; - $this->updateStatus('Filter'); + $this->updateStatus('No_send'); + $this->updateTargetId($history['id']); // In case we flter the document, we return false to stop the process when this method is called in the rerun process return false; } From 7efdb3ea14a029342d8e43ff46e5e834423003fe Mon Sep 17 00:00:00 2001 From: myddleware Date: Thu, 8 Feb 2024 22:16:24 +0100 Subject: [PATCH 008/452] Manage rule in UPDATE_ONLY Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index c9827d980..520c0568c 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -1136,12 +1136,14 @@ public function getTargetDataDocument(): bool ) { $this->checkNoChange($history); } - // Error if rule mode is update only and the document is a creation + // Cancel if rule mode is update only and the document is a creation if ( 'C' == $this->documentType and 'U' == $this->ruleMode ) { - throw new \Exception('The document is a creation but the rule mode is UPDATE ONLY. '); + $this->message .= 'The document is a creation but the rule mode is UPDATE ONLY.'; + $this->updateStatus('Filter'); + return false; } } catch (\Exception $e) { $this->message .= $e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; @@ -1152,10 +1154,8 @@ public function getTargetDataDocument(): bool $this->updateStatus('Error_checking'); } $this->logger->error($this->message); - return false; } - return true; } From acb6a315395b9b8d6a466518dcdb66a4a89e9843 Mon Sep 17 00:00:00 2001 From: myddleware Date: Sun, 11 Feb 2024 00:14:47 +0100 Subject: [PATCH 009/452] Fix bug for rerun record by id Signed-off-by: myddleware --- src/Command/MassActionCommand.php | 2 +- src/Command/ReadRecordCommand.php | 6 +- src/Manager/JobManager.php | 87 ++++++++++------ src/Manager/RuleManager.php | 165 +----------------------------- 4 files changed, 61 insertions(+), 199 deletions(-) diff --git a/src/Command/MassActionCommand.php b/src/Command/MassActionCommand.php index 12ec28b0d..d22a6f8ab 100644 --- a/src/Command/MassActionCommand.php +++ b/src/Command/MassActionCommand.php @@ -98,7 +98,7 @@ protected function configure() ->setDescription('Action massive sur les flux') ->addArgument('action', InputArgument::REQUIRED, 'Action (rerun, cancel, remove, restore or changeStatus)') ->addArgument('dataType', InputArgument::REQUIRED, 'Data type (rule or document)') - ->addArgument('ids', InputArgument::REQUIRED, 'Rule or document ids') // id séparés par des ";" + ->addArgument('ids', InputArgument::REQUIRED, 'Rule or document ids') // id séparés par des "," ->addArgument('forceAll', InputArgument::OPTIONAL, 'Set Y to process action on all documents (not only open and error ones)') ->addArgument('fromStatus', InputArgument::OPTIONAL, 'Get all document with this status(Only with changeStatus action)') ->addArgument('toStatus', InputArgument::OPTIONAL, 'Set this status (Only with changeStatus action)') diff --git a/src/Command/ReadRecordCommand.php b/src/Command/ReadRecordCommand.php index 96ad0ee6f..1ce59c688 100644 --- a/src/Command/ReadRecordCommand.php +++ b/src/Command/ReadRecordCommand.php @@ -93,11 +93,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output->writeln('1;'.$this->jobManager->getId()); // This is requiered to display the log (link creation with job id) when the job is run manually - // If the query is by id, we use the flag that leads to massIdRerun() - // Otherwise we use the regular rerun - if ($filterQuery === 'id') { - $this->jobManager->readRecord($rule, $filterQuery, $filterValues, 1); - }else $this->jobManager->readRecord($rule, $filterQuery, $filterValues); + $this->jobManager->readRecord($rule, $filterQuery, $filterValues); // Close job if it has been created $responseCloseJob = $this->jobManager->closeJob(); diff --git a/src/Manager/JobManager.php b/src/Manager/JobManager.php index 0a3d943af..44271eb3c 100644 --- a/src/Manager/JobManager.php +++ b/src/Manager/JobManager.php @@ -528,13 +528,18 @@ public function massAction($action, $dataType, $ids, $forceAll, $fromStatus, $to return true; } - // Fonction permettant d'annuler massivement des documents - - // In order to add extra components to the function without disturbing its regular use, we added a flag argument. - // This $usesDocumentIds flag is either null or 1 - public function readRecord($ruleId, $filterQuery, $filterValues, $usesDocumentIds = null): bool + // Function to create one of several document using a query + public function readRecord($ruleId, $filterQuery, $filterValues): bool { try { + $responseFilter = []; + $responseCheckPredecessor = []; + $responseCheckParent = []; + $responseTransform = []; + $responseGetTargetData = []; + $responseSend = []; + $documentIds = []; + // Get the filter values $filterValuesArray = explode(',', $filterValues); if (empty($filterValuesArray)) { @@ -557,11 +562,6 @@ public function readRecord($ruleId, $filterQuery, $filterValues, $usesDocumentId $this->ruleManager->setManual($this->manual); $this->ruleManager->setApi($this->api); - // We create an array that will match the initial structure of the function - if ($usesDocumentIds === 1) { - $arrayOfDocumentIds = []; - } - // Try to read data for each values foreach ($filterValuesArray as $value) { // Generate documents @@ -569,38 +569,67 @@ public function readRecord($ruleId, $filterQuery, $filterValues, $usesDocumentId if (!empty($documents->error)) { throw new Exception($documents->error); } - // We assign the id to an id section of the array - if ($usesDocumentIds === 1 && !empty($documents)) { - $arrayOfDocumentIds[] = $documents[0]->id; - continue; - } elseif (!empty($documents)) { - // Run documents - foreach ($documents as $doc) { - $errors = $this->ruleManager->actionDocument($doc->id, 'rerun'); - // Check errors - if (!empty($errors)) { - $this->message .= 'Document '.$doc->id.' in error (rule '.$ruleId.') : '.$errors[0].'. '; - } - } + if (!empty($documents[0]->id)) { + $documentIds[]['id'] = $documents[0]->id; } } - // Since the actionDocument takes a string and not an array of ids, we recompose the ids into a string separated by commas - if ($usesDocumentIds === 1) { - $stringOfDocumentIds = implode(',', $arrayOfDocumentIds); - $errors = $this->ruleManager->actionDocument($stringOfDocumentIds, 'rerun'); + // Filter documents + if (!empty($documentIds)) { + $responseFilter = $this->ruleManager->filterDocuments($documentIds); + } + + // Check predecessor + $documentIds = $this->refreshDocumentList($documentIds, $responseFilter); + if (!empty($documentIds)) { + $responseCheckPredecessor = $this->ruleManager->checkPredecessorDocuments($documentIds); + } + + // Check parent + $documentIds = $this->refreshDocumentList($documentIds, $responseCheckPredecessor); + if (!empty($documentIds)) { + $responseCheckParent = $this->ruleManager->checkParentDocuments($documentIds); + } + + // Transform document + $documentIds = $this->refreshDocumentList($documentIds, $responseCheckParent); + if (!empty($documentIds)) { + $responseTransform = $this->ruleManager->transformDocuments($documentIds); + } + + // Get history + $documentIds = $this->refreshDocumentList($documentIds, $responseTransform); + if (!empty($documentIds)) { + $responseGetTargetData = $this->ruleManager->getTargetDataDocuments($documentIds); + } + + // Send document + $documentIds = $this->refreshDocumentList($documentIds, $responseGetTargetData); + if (!empty($documentIds)) { + $responseSend = $this->ruleManager->sendDocuments($documentIds); } } catch (Exception $e) { $this->message .= 'Error : '.$e->getMessage(); $this->logger->error('Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); - return false; } - return true; } + // Remove the document from the list if the retrun value is not true + public function refreshDocumentList($documentIds, $response) { + $documentListRefresh = []; + if (!empty($response)) { + foreach($response as $docId => $return) { + if ($return) { + $documentListRefresh[]['id'] = $docId; + } + } + } + return $documentListRefresh; + } + // Remove all data flagged deleted in the database public function pruneDatabase(): void { diff --git a/src/Manager/RuleManager.php b/src/Manager/RuleManager.php index 230d7b05f..41ad2f2f9 100644 --- a/src/Manager/RuleManager.php +++ b/src/Manager/RuleManager.php @@ -931,14 +931,7 @@ public function actionDocument($id_document, $event, $param1 = null) { switch ($event) { case 'rerun': - // We use the method massIdRerun if there is a , in the id string, which implies that it is actually several ids - // Otherwise we use the regular rerun - if ((strpos($id_document, ',') !== false)) { - return $this->massIdRerun($id_document); - } else { - return $this->rerun($id_document); - } - break; + return $this->rerun($id_document); case 'cancel': return $this->cancel($id_document); case 'remove': @@ -1387,162 +1380,6 @@ protected function rerun($id_document): array return $msg_error; } - // Function to rerun several documents by their ids. The process of creating the document (status: New) is separated from the other statuses (Filter, Predecessor...etc) - // if we have 5 documents, we will have 5 statuses New then the rest of the operations will happen after that. - protected function massIdRerun(string $documentIds) - { - $session = new Session(); - $msg_error = []; - $msg_success = []; - $msg_info = []; - // Récupération du statut du document - $param['id_doc_myddleware'] = $documentIds; - $param['jobId'] = $this->jobId; - $param['api'] = $this->api; - // Set the param values and clear all document attributes - $this->documentManager->setParam($param, true); - $status = $this->documentManager->getStatus(); - // Si la règle n'est pas chargée alors on l'initialise. - if (empty($this->ruleId)) { - $this->ruleId = $this->documentManager->getRuleId(); - $this->setRule($this->ruleId); - $this->setRuleRelationships(); - $this->setRuleParam(); - $this->setRuleField(); - } - - // Manually setting the status to New - // In this method, the statuses are set mannually because the method for getting statuses doesn't understand the string of several ids. - $status = "New"; - - $response[$documentIds] = false; - - $arrayIdDocument = []; - - //we separate the string into an array in order to be able to match the associative array id => structure - $arrayDocIdOriginal = explode(",", $documentIds); - foreach ($arrayDocIdOriginal as $document) { - $arrayIdDocument[] = ['id' => $document]; - } - // On lance des méthodes différentes en fonction du statut en cours du document et en fonction de la réussite ou non de la fonction précédente - if (in_array($status, ['New', 'Filter_KO'])) { - $response = $this->filterDocuments($arrayIdDocument); - if (true === $this->verifyMultiIdResponse($response)) { - $msg_success[] = 'Transfer id '.$documentIds.' : Status change => Filter_OK'; - // Update status if an action has been executed - $status = 'Filter_OK'; - } elseif (-1 == $response[$documentIds]) { - $msg_info[] = 'Transfer id '.$documentIds.' : Status change => Filter'; - } else { - // Update status if an action has been executed - $status = 'Filter_KO'; - $msg_error[] = 'Transfer id '.$documentIds.' : Error, status transfer => Filter_KO'; - } - } - if (in_array($status, ['Filter_OK', 'Predecessor_KO'])) { - $response = $this->checkPredecessorDocuments($arrayIdDocument); - if (true === $this->verifyMultiIdResponse($response)) { - // Update status if an action has been executed - $status = 'Predecessor_OK'; - $msg_success[] = 'Transfer id '.$documentIds.' : Status change => Predecessor_OK'; - } else { - $msg_error[] = 'Transfer id '.$documentIds.' : Error, status transfer => Predecessor_KO'; - // Update status if an action has been executed - $status = 'Predecessor_KO'; - } - } - if (in_array($status, ['Predecessor_OK', 'Relate_KO'])) { - $response = $this->checkParentDocuments($arrayIdDocument); - if (true === $this->verifyMultiIdResponse($response)) { - // Update status if an action has been executed - $status = 'Relate_OK'; - $msg_success[] = 'Transfer id '.$documentIds.' : Status change => Relate_OK'; - } else { - $msg_error[] = 'Transfer id '.$documentIds.' : Error, status transfer => Relate_KO'; - // Update status if an action has been executed - $status = 'Relate_KO'; - } - } - if (in_array($status, ['Relate_OK', 'Error_transformed'])) { - $response = $this->transformDocuments($arrayIdDocument); - if (true === $this->verifyMultiIdResponse($response)) { - // Update status if an action has been executed - $status = 'Transformed'; - $msg_success[] = 'Transfer id '.$documentIds.' : Status change : Transformed'; - } else { - $msg_error[] = 'Transfer id '.$documentIds.' : Error, status transfer : Error_transformed'; - // Update status if an action has been executed - $status = 'Error_transformed'; - } - } - if (in_array($status, ['Transformed', 'Error_checking', 'Not_found'])) { - $response = $this->getTargetDataDocuments($arrayIdDocument); - if (true === $this->verifyMultiIdResponse($response)) { - if ('S' == $this->rule['mode']) { - $msg_success[] = 'Transfer id '.$documentIds.' : Status change : '.$response['doc_status']; - } else { - $msg_success[] = 'Transfer id '.$documentIds.' : Status change : '.$response['doc_status']; - } - } else { - $msg_error[] = 'Transfer id '.$documentIds.' : Error, status transfer : '.$response['doc_status']; - } - // Update status if an action has been executed - $status = $this->documentManager->getStatus(); - } - // Si la règle est en mode recherche alors on n'envoie pas de données - // Si on a un statut compatible ou si le doc vient de passer dans l'étape précédente et qu'il n'est pas no_send alors on envoie les données - if ( - 'S' != $this->rule['mode'] - && ( - in_array($status, ['Ready_to_send', 'Error_sending']) - || ( - true === $response[$documentIds] - && !empty($response['doc_status']) - && in_array($response['doc_status'], ['Ready_to_send', 'Error_sending']) - ) - ) - ) { - $response = $this->massSendTarget('', $documentIds); - if ( - !empty($response[$documentIds]['id']) - && empty($response[$documentIds]['error']) - && empty($response['error']) // Error can be on the document or can be a general error too - ) { - $msg_success[] = 'Transfer id '.$documentIds.' : Status change : Send'; - } else { - $msg_error[] = 'Transfer id '.$documentIds.' : Error, status transfer : Error_sending. '.(!empty($response['error']) ? $response['error'] : $response[$documentIds]['error']); - } - } - // If the job is manual, we display error in the UI - if ($this->manual) { - if (!empty($msg_error)) { - $session->set('error', $msg_error); - } - if (!empty($msg_success)) { - $session->set('success', $msg_success); - } - if (!empty($msg_info)) { - $session->set('info', $msg_info); - } - } - - return $msg_error; - } - - // Function to verify the state of the response. It will be valid if at least one document valid. - // That way if all of the documents fail we can abort the process, but if some succeed, then the failed ones will be logged. - public function verifyMultiIdResponse(array $response) - { - $atLeastOneDocumentValid = false; - - foreach ($response as $documentState) { - if ($documentState === true || $documentState === 'Ready_to_send') { - $atLeastOneDocumentValid = true; - break; - } - } - return $atLeastOneDocumentValid; - } protected function clearSendData($sendData) { From 00ed4d633b68cfce94d030f1d8c6f5831072a070 Mon Sep 17 00:00:00 2001 From: myddleware Date: Tue, 13 Feb 2024 14:11:31 +0100 Subject: [PATCH 010/452] SugarCRM : date modified was empty if the last record read was a deletion Signed-off-by: myddleware --- src/Solutions/sugarcrm.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Solutions/sugarcrm.php b/src/Solutions/sugarcrm.php index 12721f23a..eb43a2ba0 100644 --- a/src/Solutions/sugarcrm.php +++ b/src/Solutions/sugarcrm.php @@ -326,6 +326,7 @@ public function readRelationship($param, $rel) { public function read($param) { $result = []; + $maxDateModified = ''; $rel = $this->isManyToManyRel($param['module']); if ($rel !== false) { return $this->readRelationship($param, $rel); @@ -394,6 +395,10 @@ public function read($param) // At least one non deleted record read $onlyDeletion = false; } + // Keep the date modified max (date modified is empty for deletion record) + if (!empty($record->date_modified)) { + $maxDateModified = $record->date_modified; + } } // Error if only deletion records read if ($onlyDeletion) { @@ -430,7 +435,7 @@ public function read($param) } // No date modified returned if record deleted, we set a default date (the last reference date read) if (!empty($result[$record->id]['myddleware_deletion'])) { - $result[$record->id]['date_modified'] = end($records)->date_modified; + $result[$record->id]['date_modified'] = $maxDateModified; } } } From a1c8922dbca3c396b95f5fdcd97484f00f4cc12b Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 14 Feb 2024 13:05:54 +0100 Subject: [PATCH 011/452] SuiteCRM : remove favorite parameter Signed-off-by: myddleware --- src/Solutions/suitecrm.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Solutions/suitecrm.php b/src/Solutions/suitecrm.php index 20c981882..495932c16 100644 --- a/src/Solutions/suitecrm.php +++ b/src/Solutions/suitecrm.php @@ -444,7 +444,6 @@ public function read($param) 'link_name_to_fields_array' => $link_name_to_fields_array, 'max_results' => $this->limitCall, 'deleted' => $deleted, - 'Favorites' => '', ]; $get_entry_list_result = $this->call('get_entry_list', $get_entry_list_parameters); // Construction des données de sortie From 7b5c58eb0e14fc0b4cf1a2c5d59bd7df9868e86f Mon Sep 17 00:00:00 2001 From: myddleware Date: Fri, 16 Feb 2024 19:16:43 +0100 Subject: [PATCH 012/452] Add the possibility to adapt the list of bidirectional rules Signed-off-by: myddleware --- src/Controller/DefaultController.php | 2 +- src/Manager/RuleManager.php | 7 +++++-- src/Solutions/solution.php | 8 ++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index 6a2f9aab1..c5261966f 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -1917,7 +1917,7 @@ public function ruleStepThree(Request $request) $bidirectional_params['module']['source'] = $module['source']; $bidirectional_params['module']['cible'] = $module['cible']; - $bidirectional = RuleManager::getBidirectionalRules($this->connection, $bidirectional_params); + $bidirectional = RuleManager::getBidirectionalRules($this->connection, $bidirectional_params, $solution_source, $solution_cible); if ($bidirectional) { $rule_params = array_merge($rule_params, $bidirectional); } diff --git a/src/Manager/RuleManager.php b/src/Manager/RuleManager.php index 41ad2f2f9..506c85823 100644 --- a/src/Manager/RuleManager.php +++ b/src/Manager/RuleManager.php @@ -1049,9 +1049,13 @@ protected function getRuleDocuments($ruleId, $sourceId = true, $targetId = false // Permet de récupérer les règles potentiellement biderectionnelle. // Cette fonction renvoie les règles qui utilisent les même connecteurs et modules que la règle en cours mais en sens inverse (source et target inversées) // On est sur une méthode statique c'est pour cela que l'on récupère la connexion e paramètre et non dans les attributs de la règle - public static function getBidirectionalRules($connection, $params): ?array + public static function getBidirectionalRules($connection, $params, $solutionSource, $solutionTarget): ?array { try { + // Call solutions in cas target ans source modules hasn't the same name (ex Moodle manual_enrol_users and get_enrolments_by_date) + $params = $solutionSource->beforeGetBidirectionalRules($params, 'source'); + $params = $solutionTarget->beforeGetBidirectionalRules($params, 'target'); + // Récupération des règles opposées à la règle en cours de création $queryBidirectionalRules = 'SELECT id, @@ -1094,7 +1098,6 @@ public static function getBidirectionalRules($connection, $params): ?array } catch (\Exception $e) { return null; } - return null; } diff --git a/src/Solutions/solution.php b/src/Solutions/solution.php index 4cbdbd165..120ab4bd9 100644 --- a/src/Solutions/solution.php +++ b/src/Solutions/solution.php @@ -975,6 +975,14 @@ protected function checkDataBeforeDelete($param, $data) return $data; } + /** + * @throws \Exception + */ + public function beforeGetBidirectionalRules($param, $type) + { + return $param; + } + /** * @throws \Doctrine\DBAL\Exception * @throws \Exception From 6192588b7ce41bc2c9352fa1f3be463300128032 Mon Sep 17 00:00:00 2001 From: myddleware Date: Thu, 22 Feb 2024 18:43:29 +0100 Subject: [PATCH 013/452] Fix : manage error for history call Signed-off-by: myddleware --- src/Solutions/solution.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Solutions/solution.php b/src/Solutions/solution.php index 120ab4bd9..e065906eb 100644 --- a/src/Solutions/solution.php +++ b/src/Solutions/solution.php @@ -296,6 +296,13 @@ public function readData($param) // Read data $readResult = $this->read($param); + // In case of error in an history call, we return directly the error + if ( + $param['call_type'] == 'history' + AND !empty($readResult['error']) + ) { + return $readResult; + } // Save the new rule params into attribut dataSource if (!empty($readResult['ruleParams'])) { From 8b8c42b9d7cb05405551f9c22b5193b53ed0cabd Mon Sep 17 00:00:00 2001 From: myddleware Date: Thu, 22 Feb 2024 18:49:33 +0100 Subject: [PATCH 014/452] Moodle : add function local_myddleware_search_enrolment Signed-off-by: myddleware --- src/Solutions/moodle.php | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/Solutions/moodle.php b/src/Solutions/moodle.php index 87de84960..ada6b87bd 100644 --- a/src/Solutions/moodle.php +++ b/src/Solutions/moodle.php @@ -46,8 +46,9 @@ class moodlecore extends solution protected array $FieldsDuplicate = [ 'users' => ['email', 'username'], 'courses' => ['shortname', 'idnumber'], + 'manual_enrol_users' => ['userid', 'courseid'], + 'manual_unenrol_users' => ['userid', 'courseid'], ]; - protected array $createOnlyFields = [ 'courses' => ['lang'], ]; @@ -604,6 +605,12 @@ protected function formatResponse($method, $response, $param) // Get the function name protected function getFunctionName($param): string { + if ( + $param['call_type'] == 'history' + AND in_array($param['module'], array('manual_enrol_users', 'manual_unenrol_users')) + ) { + return 'local_myddleware_search_enrolment'; + } // In case of duplicate search (search with a criteria) if ( !empty($param['query']) @@ -615,7 +622,7 @@ protected function getFunctionName($param): string } elseif ('courses' == $param['module']) { return 'core_course_get_courses_by_field'; } - // In case of read by date or search a specific record with an id for specific modules user or course + // In case of read by date or search a specific record with an id for specific modules user or course } else { if ('users' == $param['module']) { return 'local_myddleware_get_users_by_date'; @@ -638,9 +645,23 @@ protected function getFunctionName($param): string */ protected function setParameters($param): array { + // Get the function name $functionName = $this->getFunctionName($param); - $parameters['time_modified'] = $this->dateTimeFromMyddleware($param['date_ref']); + // Specific parameters for function local_myddleware_search_enrolment + if ($functionName == 'local_myddleware_search_enrolment') { + if ( + empty($param['query']['userid']) + OR empty($param['query']['courseid']) + ) { + throw new \Exception('CourseId and UserId are both requiered to check if an enrolment exists. One of them is empty here. '); + } + $parameters['userid'] = $param['query']['userid']; + $parameters['courseid'] = $param['query']['courseid']; + return $parameters; + } + // If standard function called to search by criteria + $parameters['time_modified'] = $this->dateTimeFromMyddleware($param['date_ref']); if (in_array($functionName, ['core_user_get_users', 'core_course_get_courses_by_field'])) { if (!empty($param['query'])) { foreach ($param['query'] as $key => $value) { @@ -654,7 +675,8 @@ protected function setParameters($param): array } else { throw new \Exception('Filter criteria empty. Not allowed to run function '.$functionName.' without filter criteria.'); } - } elseif (!empty($param['query']['id'])) { + } + elseif (!empty($param['query']['id'])) { $parameters['id'] = $param['query']['id']; } From ec8735cee38e7317e809243b5ac2a3750d2f7273 Mon Sep 17 00:00:00 2001 From: myddleware Date: Thu, 22 Feb 2024 18:51:03 +0100 Subject: [PATCH 015/452] Moodle : add info in error message Signed-off-by: myddleware --- src/Solutions/moodle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Solutions/moodle.php b/src/Solutions/moodle.php index ada6b87bd..a42877aa5 100644 --- a/src/Solutions/moodle.php +++ b/src/Solutions/moodle.php @@ -213,7 +213,7 @@ public function read($param): array $xml = $this->formatResponse('read', $response, $param); if (!empty($xml->ERRORCODE)) { - throw new \Exception("Error $xml->ERRORCODE : $xml->MESSAGE"); + throw new \Exception("Error code $xml->ERRORCODE : $xml->MESSAGE. ".(!empty($xml->DEBUGINFO) ? "Info : $xml->DEBUGINFO" : "")); } // Transform the data to Myddleware format if (!empty($xml->MULTIPLE->SINGLE)) { From 620909ac6127c38af7246ce98bbc0c870a64d256 Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 6 Mar 2024 23:09:22 +0100 Subject: [PATCH 016/452] Fix Api bugs on deletion record function Signed-off-by: myddleware --- src/Controller/ApiController.php | 89 ++++++++++++++++++++------------ src/Manager/DocumentManager.php | 1 + src/Manager/JobManager.php | 2 +- 3 files changed, 59 insertions(+), 33 deletions(-) diff --git a/src/Controller/ApiController.php b/src/Controller/ApiController.php index 280835fff..1c49cfcb4 100644 --- a/src/Controller/ApiController.php +++ b/src/Controller/ApiController.php @@ -3,7 +3,10 @@ namespace App\Controller; use App\Manager\JobManager; +use App\Manager\FormulaManager; use App\Manager\RuleManager; +use App\Manager\DocumentManager; +use App\Manager\SolutionManager; use App\Repository\DocumentRepository; use App\Repository\JobRepository; use App\Repository\RuleRepository; @@ -32,6 +35,9 @@ class ApiController extends AbstractController private KernelInterface $kernel; private LoggerInterface $logger; private JobManager $jobManager; + private SolutionManager $solutionManager; + private DocumentManager $documentManager; + private FormulaManager $formulaManager; private ParameterBagInterface $parameterBag; private EntityManagerInterface $entityManager; @@ -43,12 +49,18 @@ public function __construct( JobRepository $jobRepository, DocumentRepository $documentRepository, ParameterBagInterface $parameterBag, - EntityManagerInterface $entityManager + EntityManagerInterface $entityManager, + FormulaManager $formulaManager, + DocumentManager $documentManager, + SolutionManager $solutionManager ) { $this->ruleRepository = $ruleRepository; $this->jobRepository = $jobRepository; $this->documentRepository = $documentRepository; $this->jobManager = $jobManager; + $this->solutionManager = $solutionManager; + $this->formulaManager = $formulaManager; + $this->documentManager = $documentManager; $this->logger = $logger; $this->kernel = $kernel; $this->env = $kernel->getEnvironment(); @@ -103,7 +115,7 @@ public function synchroAction(Request $request): JsonResponse $return['jobId'] = substr($content, 2, 23); $job = $this->jobRepository->find($return['jobId']); // Get the job statistics - $this->jobManager->setId($job->getId()); + $this->jobManager->setId($this->jobManager->getId()); $jobData = $this->jobManager->getLogData(); if (!empty($jobData['jobError'])) { throw new Exception('Failed to get the job statistics. '.$jobData['jobError']); @@ -174,7 +186,7 @@ public function readRecordAction(Request $request): JsonResponse // Get the job statistics $job = $this->jobRepository->find($return['jobId']); - $this->jobManager->setId($job->getId()); + $this->jobManager->setId($this->jobManager->getId()); $jobData = $this->jobManager->getLogData(); if (!empty($jobData['jobError'])) { throw new Exception('Failed to get the job statistics. '.$jobData['jobError']); @@ -193,15 +205,19 @@ public function readRecordAction(Request $request): JsonResponse */ public function deleteRecordAction(Request $request): JsonResponse { - try { - $connection = $this->container->get('database_connection'); - $connection->beginTransaction(); // -- BEGIN TRANSACTION +// $this->logger->error('test SF01 '); + try { + $connection = $this->entityManager->getConnection(); + // $connection = $this->container->get('database_connection'); + // $connection->beginTransaction(); // -- BEGIN TRANSACTION $return = []; $return['error'] = ''; // Get input data - $data = $request->request->all(); + $data = json_decode($request->getContent(), true); +// $return['error'] = 'test SF01 '.print_r($data,true); +// return $this->json($return); // Check parameter if (empty($data['rule'])) { @@ -210,6 +226,9 @@ public function deleteRecordAction(Request $request): JsonResponse if (empty($data['recordId'])) { throw new Exception('recordId is missing. recordId is the id of the record you want to delete. '); } +// $return['error'] = 'test SF02'; +// $return['error'] = 'test SF02 '.print_r($data,true); +// return $this->json($return); // Set the document values foreach ($data as $key => $value) { @@ -227,35 +246,43 @@ public function deleteRecordAction(Request $request): JsonResponse } } $docParam['values']['myddleware_deletion'] = true; // Force deleted record type + + // $this->logger->error('test'); +// $return['error'] = 'test SF03 '.print_r($docParam,true); +// return $this->json($return); // Create job instance - $job = $this->container->get('myddleware_job.job'); - $job->setApi(1); - $job->initJob('Delete record '.$data['recordId'].' in rule '.$data['rule']); + $this->jobManager->setApi(1); + $this->jobManager->initJob('Delete record '.$data['recordId'].' in rule '.$data['rule']); +// $return['error'] = 'test SF04 '.print_r($docParam,true); +// return $this->json($return); // Instantiate the rule $ruleParam['ruleId'] = $data['rule']; - $ruleParam['jobId'] = $job->id; + $ruleParam['jobId'] = $this->jobManager->getId(); $ruleParam['api'] = 1; + + $rule = new RuleManager( - $this->container->get('logger'), + $this->logger, $connection, $this->entityManager, $this->parameterBag, - // $ruleParam, $this->formulaManager, $this->solutionManager, $this->documentManager ); - +// $return['error'] = 'test SF05 '.print_r($ruleParam,true); +// return $this->json($return); + $rule->setRule($data['rule']); $document = $rule->generateDocuments($data['recordId'], false, $docParam); // Stop the process if error during the data transfer creation as we won't be able to manage it in Myddleware if (!empty($document->error)) { throw new Exception('Error during data transfer creation (rule '.$data['rule'].') : '.$document->error.'. '); } - $connection->commit(); // -- COMMIT TRANSACTION + // $connection->commit(); // -- COMMIT TRANSACTION } catch (Exception $e) { - $connection->rollBack(); // -- ROLLBACK TRANSACTION + // $connection->rollBack(); // -- ROLLBACK TRANSACTION $this->logger->error($e->getMessage()); $return['error'] .= $e->getMessage(); // Stop the process if document hasn't been created @@ -278,22 +305,22 @@ public function deleteRecordAction(Request $request): JsonResponse // Close job if it has been created try { - $connection->beginTransaction(); // -- BEGIN TRANSACTION - if (true === $job->createdJob) { - $job->closeJob(); + // $connection->beginTransaction(); // -- BEGIN TRANSACTION + if (true === $this->jobManager->createdJob) { + $this->jobManager->closeJob(); } // Get the job statistics even if the job has failed - if (!empty($job->id)) { - $return['jobId'] = $job->id; - $jobData = $job->getLogData(); + if (!empty($this->jobManager->id)) { + $return['jobId'] = $this->jobManager->id; + $jobData = $this->jobManager->getLogData(); if (!empty($jobData['jobError'])) { $return['error'] .= $jobData['jobError']; } $return['jobData'] = $jobData; } - $connection->commit(); // -- COMMIT TRANSACTION + // $connection->commit(); // -- COMMIT TRANSACTION } catch (Exception $e) { - $connection->rollBack(); // -- ROLLBACK TRANSACTION + // $connection->rollBack(); // -- ROLLBACK TRANSACTION $this->logger->error('Failed to get the job statistics. '.$e->getMessage()); $return['error'] .= 'Failed to get the job statistics. '.$e->getMessage(); } @@ -311,7 +338,7 @@ public function massActionAction(Request $request): JsonResponse $return['error'] = ''; // Get input data - $data = $request->request->all(); + $data = json_decode($request->getContent(), true); // Check parameter if (empty($data['action'])) { @@ -358,9 +385,8 @@ public function massActionAction(Request $request): JsonResponse $return['jobId'] = substr($content, 0, 23); // Get the job statistics - $job = $this->container->get('myddleware_job.job'); - $job->id = $return['jobId']; - $jobData = $job->getLogData(); + $this->jobManager->id = $return['jobId']; + $jobData = $this->jobManager->getLogData(); if (!empty($jobData['jobError'])) { throw new Exception('Failed to get the job statistics. '.$jobData['jobError']); } @@ -383,7 +409,7 @@ public function rerunErrorAction(Request $request): JsonResponse $return['error'] = ''; // Get input data - $data = $request->request->all(); + $data = json_decode($request->getContent(), true); // Check parameter if (empty($data['limit'])) { @@ -423,9 +449,8 @@ public function rerunErrorAction(Request $request): JsonResponse $return['jobId'] = substr($content, 0, 23); // Get the job statistics - $job = $this->container->get('myddleware_job.job'); - $job->id = $return['jobId']; - $jobData = $job->getLogData(); + $this->jobManager->id = $return['jobId']; + $jobData = $this->jobManager->getLogData(); $return['jobData'] = $jobData; } catch (Exception $e) { $this->logger->error($e->getMessage()); diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 520c0568c..d2ec8d109 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -1470,6 +1470,7 @@ protected function updateTargetTable() throw new \Exception('Failed to transform the field '.$ruleField['target_field_name'].'.'); } $targetField[$ruleField['target_field_name']] = $value; + // If the target value equals mdw_no_send_field, the field isn't sent to the target if ($value === "mdw_no_send_field") { unset($targetField[$ruleField['target_field_name']]); $this->notSentFields[] = $ruleField['target_field_name']; diff --git a/src/Manager/JobManager.php b/src/Manager/JobManager.php index 44271eb3c..ab267d30c 100644 --- a/src/Manager/JobManager.php +++ b/src/Manager/JobManager.php @@ -622,7 +622,7 @@ public function refreshDocumentList($documentIds, $response) { $documentListRefresh = []; if (!empty($response)) { foreach($response as $docId => $return) { - if ($return) { + if ($return == true) { $documentListRefresh[]['id'] = $docId; } } From 3f2b8cc0490a77c5346f91f38e8146a332966333 Mon Sep 17 00:00:00 2001 From: myddleware Date: Tue, 12 Mar 2024 11:25:18 +0100 Subject: [PATCH 017/452] Add massaction action : unlock Signed-off-by: myddleware --- src/Command/MassActionCommand.php | 20 +++----------------- src/Manager/DocumentManager.php | 8 +++++--- src/Manager/JobManager.php | 4 ++++ src/Manager/RuleManager.php | 27 +++++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/Command/MassActionCommand.php b/src/Command/MassActionCommand.php index d22a6f8ab..939b1c532 100644 --- a/src/Command/MassActionCommand.php +++ b/src/Command/MassActionCommand.php @@ -27,39 +27,25 @@ // Documentation : Overview - The myddleware:massaction command allows you to do various actions on one or multiple documents in Myddleware. This is especially useful when you need to batch process document cancellations or reload without manually intervening for each. For windows and laragon, use php bin/console. - php bin/console myddleware:massaction cancel document [document_ids] [optional_arguments] - Parameters - document_ids: A comma-separated list of the document IDs that you want to cancel. For example: 64f8847a69b2a2.66477108,64f8841bbd8cd9.89905176. - Optional Arguments - forceAll: Usage: forceAll [Y/N] This argument is used when you want to cancel documents regardless of their current status. By default, only documents with the status "open" or "error" can be canceled. If you want to cancel documents with other statuses, set this argument to Y. Example: forceAll Y - Examples - Basic Usage: Cancelling specific documents based on their IDs: - php bin/console myddleware:massaction cancel document 64f8847a69b2a2.66477108,64f8841bbd8cd9.89905176 - + php bin/console myddleware:massaction cancel document 64f8847a69b2a2.66477108,64f8841bbd8cd9.89905176 this will cancel theses documents only if they are in open or error status. - Using forceAll: Cancelling documents regardless of their status: - php bin/console myddleware:massaction cancel document 64f8847a69b2a2.66477108,64f8841bbd8cd9.89905176 forceAll Y - - - *********************************************************************************/ namespace App\Command; @@ -96,7 +82,7 @@ protected function configure() $this ->setName('myddleware:massaction') ->setDescription('Action massive sur les flux') - ->addArgument('action', InputArgument::REQUIRED, 'Action (rerun, cancel, remove, restore or changeStatus)') + ->addArgument('action', InputArgument::REQUIRED, 'Action (rerun, cancel, remove, restore, changeStatus, unlock)') ->addArgument('dataType', InputArgument::REQUIRED, 'Data type (rule or document)') ->addArgument('ids', InputArgument::REQUIRED, 'Rule or document ids') // id séparés par des "," ->addArgument('forceAll', InputArgument::OPTIONAL, 'Set Y to process action on all documents (not only open and error ones)') @@ -144,7 +130,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output->writeln('1;'.$this->jobManager->getId()); // Do not remove, used for manual job and webservices (display logs) // Récupération des paramètres - if (!in_array($action, ['rerun', 'cancel', 'remove', 'restore', 'changeStatus'])) { + if (!in_array($action, ['rerun', 'cancel', 'remove', 'restore', 'changeStatus', 'unlock'])) { throw new Exception('Action '.$action.' unknown. Please use action rerun, cancel or remove.'); } if (!in_array($dataType, ['document', 'rule'])) { diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index d2ec8d109..1a9fd7783 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -509,7 +509,7 @@ protected function setLock() { } // Set the document lock - public function unsetLock() { + public function unsetLock($force = false) { try { // Get the job lock on the document $documentQuery = 'SELECT * FROM document WHERE id = :doc_id'; @@ -517,9 +517,11 @@ public function unsetLock() { $stmt->bindValue(':doc_id', $this->id); $documentResult = $stmt->executeQuery(); $documentData = $documentResult->fetchAssociative(); // 1 row - // If document already lock by the current job, we return true; - if ($documentData['job_lock'] == $this->jobId) { + if ( + $documentData['job_lock'] == $this->jobId + OR $force === true + ) { $now = gmdate('Y-m-d H:i:s'); $query = " UPDATE document SET diff --git a/src/Manager/JobManager.php b/src/Manager/JobManager.php index ab267d30c..0598812ab 100644 --- a/src/Manager/JobManager.php +++ b/src/Manager/JobManager.php @@ -481,6 +481,10 @@ public function massAction($action, $dataType, $ids, $forceAll, $fromStatus, $to if ('changeStatus' == $action) { $where .= " AND document.status = '$fromStatus' "; } + // Filter on document locked + if ('unlock' == $action) { + $where .= " AND document.job_lock != '' AND document.job_lock IS NOT NULL "; + } // Build the query $sqlParams = ' SELECT diff --git a/src/Manager/RuleManager.php b/src/Manager/RuleManager.php index 506c85823..55d4721d2 100644 --- a/src/Manager/RuleManager.php +++ b/src/Manager/RuleManager.php @@ -940,6 +940,8 @@ public function actionDocument($id_document, $event, $param1 = null) return $this->changeDeleteFlag($id_document, false); case 'changeStatus': return $this->changeStatus($id_document, $param1); + case 'unlock': + return $this->unlockDocument($id_document); default: return 'Action '.$event.' unknown. Failed to run this action. '; } @@ -1125,6 +1127,31 @@ protected function cancel($id_document) } } } + + /** + * @throws \Doctrine\DBAL\Exception + */ + protected function unlockDocument($id_document) + { + $param['id_doc_myddleware'] = $id_document; + $param['jobId'] = $this->jobId; + $param['api'] = $this->api; + // Set the param values and clear all document attributes + $this->documentManager->setParam($param, true); + $this->documentManager->unsetLock(true); + $session = new Session(); + $message = $this->documentManager->getMessage(); + + // Si on a pas de jobId cela signifie que l'opération n'est pas massive mais sur un seul document + // On affiche alors le message directement dans Myddleware + if (empty($this->jobId)) { + if (empty($message)) { + $session->set('success', ['Data transfer has been successfully unlocked.']); + } else { + $session->set('error', [$this->documentManager->getMessage()]); + } + } + } /** * @throws \Doctrine\DBAL\Exception From 8a0ce1466056b20b25ad3b05a76cb1feeee39acd Mon Sep 17 00:00:00 2001 From: myddleware Date: Fri, 15 Mar 2024 10:19:34 +0100 Subject: [PATCH 018/452] Salesforce : manageg relate Salesforce's fields Signed-off-by: myddleware --- src/Solutions/salesforce.php | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/Solutions/salesforce.php b/src/Solutions/salesforce.php index 08ff8fb1d..1f77f4e8a 100644 --- a/src/Solutions/salesforce.php +++ b/src/Solutions/salesforce.php @@ -400,19 +400,40 @@ public function readData($param): array // Traitement des informations reçues // foreach($query_request_data['records'] as $record){ for ($i = 0; $i < $currentCount; $i++) { - $record = $query_request_data['records'][$i]; - foreach (array_keys($record) as $key) { + $record = $query_request_data['records'][$i]; + foreach (array_keys($record) as $key) { if($key == $DateRefField){ $record[$key] = $this->dateTimeToMyddleware($record[$key]); $row['date_modified'] = $record[$key]; } + // Manage relationship fields stored in a sub array + elseif(substr($key,-3) == '__r') { + foreach($record[$key] as $fieldKey => $fieldValue) { + // Don't save attributes + if($fieldKey != 'attributes'){ + // In case there are 2 levels of relationship (think about a recursive function here) + if(substr($fieldKey,-3) == '__r') { + foreach($fieldValue as $fieldKeyLevel2 => $fieldValueLevel2) { + if($fieldKeyLevel2 != 'attributes'){ + $row[mb_strtolower($fieldKeyLevel2)] = $fieldValueLevel2; + $row[$param['module'].'.'.$key.'.'.$fieldKey.'.'.$fieldKeyLevel2] = $fieldValueLevel2; + } + } + } + else { + $row[$param['module'].'.'.$key.'.'.$fieldKey] = $fieldValue; + } + } + } + } // On enlève le tableau "attributes" ajouté par Salesforce afin d'extraire les éléments souhaités - else if(!($key == 'attributes')){ + elseif(!($key == 'attributes')){ if($key == 'Id') $row[mb_strtolower($key)] = $record[$key]; else { - if($key == 'CreatedDate') + if($key == 'CreatedDate') { $record[$key] = $this->dateTimeToMyddleware($record[$key]); + } $row[$key] = $record[$key]; } } @@ -427,6 +448,7 @@ public function readData($param): array $row[$key] = $MailinAddress; } } + } $result['date_ref'] = $record[$DateRefField]; $result['values'][$record['Id']] = $row; @@ -453,14 +475,13 @@ public function readData($param): array && !empty($previousRefDate) && $previousRefDate == $result['date_ref'] ); - return $result; } catch (\Exception $e) { $error = $e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->logger->error($error); $result['error'] = $error; - return $result; } + return $result; } /** From 13674628a6cf445c987db7f715e4cb3033d2b16f Mon Sep 17 00:00:00 2001 From: myddleware Date: Fri, 22 Mar 2024 16:44:44 +0100 Subject: [PATCH 019/452] Quick fix on Moodle and SugarCRM Signed-off-by: myddleware --- src/Solutions/moodle.php | 17 ++++++++++------- src/Solutions/salesforce.php | 9 ++++----- src/Solutions/sugarcrm.php | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/Solutions/moodle.php b/src/Solutions/moodle.php index a42877aa5..dc69394d9 100644 --- a/src/Solutions/moodle.php +++ b/src/Solutions/moodle.php @@ -189,10 +189,14 @@ public function get_module_fields($module, $type = 'source', $param = null): arr public function read($param): array { try { - // No read action in case of history on enrolment module + // No read action in case of history on enrolment module (except if user_id and course_id are duplicate search parameters) if ( in_array($param['module'], array('manual_enrol_users', 'manual_unenrol_users')) AND $param['call_type'] == 'history' + AND ( + empty($param['query']['userid']) + OR empty($param['query']['courseid']) + ) ) { return array(); } @@ -645,18 +649,17 @@ protected function getFunctionName($param): string */ protected function setParameters($param): array { - // Get the function name - $functionName = $this->getFunctionName($param); + $functionName = $this->getFunctionName($param); // Specific parameters for function local_myddleware_search_enrolment if ($functionName == 'local_myddleware_search_enrolment') { if ( empty($param['query']['userid']) - OR empty($param['query']['courseid']) + OR empty($param['query']['courseid']) ) { - throw new \Exception('CourseId and UserId are both requiered to check if an enrolment exists. One of them is empty here. '); + throw new \Exception('CourseId and UserId are both requiered to check if an enrolment exists. One of them is empty here or is not added as duplicate serach parameter in the rule. '); } - $parameters['userid'] = $param['query']['userid']; - $parameters['courseid'] = $param['query']['courseid']; + $parameters['userid'] = $param['query']['userid']; + $parameters['courseid'] = $param['query']['courseid']; return $parameters; } diff --git a/src/Solutions/salesforce.php b/src/Solutions/salesforce.php index 1f77f4e8a..7548138a5 100644 --- a/src/Solutions/salesforce.php +++ b/src/Solutions/salesforce.php @@ -380,7 +380,7 @@ public function readData($param): array $queryWhere = $this->getWhere($param); // Gestion du ORDER $queryOrder = $this->getOrder($param); - + // Gstion du LIMIT $queryLimit .= "+LIMIT+" . $param['limit']; // Ajout de la limite souhaitée // On lit les données dans Salesforce @@ -392,7 +392,7 @@ public function readData($param): array $query = $baseQuery.$querySelect.$queryFrom.$queryWhere.$queryOrder.$queryLimit.$queryOffset; $query_request_data = $this->call($query, false); $query_request_data = $this->formatResponse($param,$query_request_data); - + // Affectation du nombre de résultat à $result['count'] if (isset($query_request_data['totalSize'])){ $currentCount = $query_request_data['totalSize']; @@ -447,8 +447,7 @@ public function readData($param): array $MailinAddress = rtrim($MailinAddress,' '); $row[$key] = $MailinAddress; } - } - + } } $result['date_ref'] = $record[$DateRefField]; $result['values'][$record['Id']] = $row; @@ -465,7 +464,7 @@ public function readData($param): array } else { $result['error'] = $query_request_data; - } + } } // On continue si : // 1. Le nombre de résultat du dernier appel est égal à la limite diff --git a/src/Solutions/sugarcrm.php b/src/Solutions/sugarcrm.php index eb43a2ba0..2b6c30214 100644 --- a/src/Solutions/sugarcrm.php +++ b/src/Solutions/sugarcrm.php @@ -407,7 +407,7 @@ public function read($param) } else { // If only deletion without new or modified record, we send no result. We wait for new or modified record. // Otherwise we will read the deleted record until a new or modified record is read because Sugar doesn't return modified date for deleted record. - return array(); + throw new \Exception('Only deletion records read. It is not possible to determine the reference date with only deletion. Waiting for a new record to be created in the source application for this rule.'); } } From 4fa7d3181ff03c40e9e020699cb618c38d16a283 Mon Sep 17 00:00:00 2001 From: Myddleware Date: Thu, 4 Apr 2024 00:18:20 +0200 Subject: [PATCH 020/452] Workflow (#1130) Feature : workflow (generate document and send notification) --- src/Entity/Document.php | 34 +++ src/Entity/Job.php | 37 +++ src/Entity/Rule.php | 36 +++ src/Entity/Workflow.php | 275 ++++++++++++++++++++ src/Entity/WorkflowAction.php | 215 +++++++++++++++ src/Entity/WorkflowLog.php | 204 +++++++++++++++ src/Manager/DocumentManager.php | 220 +++++++++++++--- src/Manager/RuleManager.php | 34 +++ src/Manager/ToolsManager.php | 34 +++ src/Repository/WorkflowActionRepository.php | 38 +++ src/Repository/WorkflowLogRepository.php | 38 +++ src/Repository/WorkflowRepository.php | 38 +++ src/Utils/workflowVariables.php | 3 + 13 files changed, 1169 insertions(+), 37 deletions(-) create mode 100644 src/Entity/Workflow.php create mode 100644 src/Entity/WorkflowAction.php create mode 100644 src/Entity/WorkflowLog.php create mode 100644 src/Repository/WorkflowActionRepository.php create mode 100644 src/Repository/WorkflowLogRepository.php create mode 100644 src/Repository/WorkflowRepository.php create mode 100644 src/Utils/workflowVariables.php diff --git a/src/Entity/Document.php b/src/Entity/Document.php index 1586a15cd..c3bbaf4d9 100755 --- a/src/Entity/Document.php +++ b/src/Entity/Document.php @@ -143,6 +143,11 @@ class Document */ private $logs; + /** + * @ORM\OneToMany(targetEntity="WorkflowLog", mappedBy="document") + */ + private $triggerDocuments; + /** * @ORM\Column(name="job_lock", type="string", length=23, nullable=false) */ @@ -153,6 +158,7 @@ public function __construct() { $this->datas = new ArrayCollection(); $this->logs = new ArrayCollection(); + $this->triggerDocuments = new ArrayCollection(); } public function setId($id): self @@ -452,6 +458,34 @@ public function removeLog(Log $log): self return $this; } + /** + * @return Collection|triggerDocument[] + */ + public function getTriggerDocuments(): Collection + { + return $this->triggerDocuments; + } + + public function addTriggerDocument(Workflowlog $triggerDocuments): self + { + if (!$this->triggerDocuments->contains($triggerDocument)) { + $this->triggerDocuments[] = $triggerDocument; + $triggerDocument->setDocument($this); + } + return $this; + } + + public function removeTriggerDocument(Workflowlog $triggerDocument): self + { + if ($this->triggerDocuments->removeElement($triggerDocument)) { + // set the owning side to null (unless already changed) + if ($triggerDocument->getDocument() === $this) { + $triggerDocument->setDocument(null); + } + } + return $this; + } + public function getCreatedBy(): ?User { return $this->createdBy; diff --git a/src/Entity/Job.php b/src/Entity/Job.php index 3da3c3e50..315305ce3 100755 --- a/src/Entity/Job.php +++ b/src/Entity/Job.php @@ -103,11 +103,20 @@ class Job * @ORM\OneToMany(targetEntity="Log", mappedBy="job") */ private $logs; + + /** + * @var WorkflowLog[] + * + * @ORM\OneToMany(targetEntity="WorkflowLog", mappedBy="job") + */ + private $workflowLogs; + public function __construct() { $this->begin = new DateTime(); $this->logs = new ArrayCollection(); + $this->workflowLogs = new ArrayCollection(); } public function setId($id): self @@ -284,4 +293,32 @@ public function removeLog(Log $log): self return $this; } + + /** + * @return Collection|WorkflowLog[] + */ + public function getWorkflowLogs(): Collection + { + return $this->workflowLogs; + } + + public function addWorkflowLogs(WorkflowLog $workflowLog): self + { + if (!$this->workflowLogs->contains($workflowLog)) { + $this->workflowLogs[] = $workflowLog; + $job->setJob($this); + } + return $this; + } + + public function removeWorkflowLog(WorkflowLog $workflowLog): self + { + if ($this->workflowLogs->removeElement($workflowLog)) { + // set the owning side to null (unless already changed) + if ($workflowLog->getWorkflow() === $this) { + $workflowLog->setJob(null); + } + } + return $this; + } } diff --git a/src/Entity/Rule.php b/src/Entity/Rule.php index e94a95d4b..a012cae37 100755 --- a/src/Entity/Rule.php +++ b/src/Entity/Rule.php @@ -155,6 +155,13 @@ class Rule */ private $audits; + /** + * @var Workflow[] + * + * @ORM\OneToMany(targetEntity="Workflow", mappedBy="rule") + */ + private $workflows; + /** * @var Document[] * @@ -177,6 +184,7 @@ public function __construct() $this->fields = new ArrayCollection(); $this->audits = new ArrayCollection(); $this->documents = new ArrayCollection(); + $this->workflows = new ArrayCollection(); } /** @@ -544,6 +552,34 @@ public function removeField(RuleField $field): self return $this; } + /** + * @return Collection|Workflow[] + */ + public function getWorkflows(): Collection + { + return $this->workflows; + } + + public function addWorkflows(Workflow $workflow): self + { + if (!$this->workflows->contains($workflow)) { + $this->workflows[] = $workflow; + $workflow->setRule($this); + } + return $this; + } + + public function removeWorkflow(Workflow $workflow): self + { + if ($this->workflows->removeElement($workflow)) { + // set the owning side to null (unless already changed) + if ($workflow->getRule() === $this) { + $workflow->setRule(null); + } + } + return $this; + } + /** * @return Collection|Document[] */ diff --git a/src/Entity/Workflow.php b/src/Entity/Workflow.php new file mode 100644 index 000000000..79a9d4d8c --- /dev/null +++ b/src/Entity/Workflow.php @@ -0,0 +1,275 @@ +. +*********************************************************************************/ + +namespace App\Entity; + +use DateTime; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; +use Doctrine\ORM\Mapping as ORM; +use Exception; + +/** + * @ORM\Table() + * @ORM\Entity(repositoryClass="App\Repository\WorkflowRepository") + * @ORM\Table(name="workflow", indexes={@ORM\Index(name="index_rule_id", columns={"rule_id"})}) + */ +class Workflow +{ + /** + * @ORM\Column(name="id", type="string") + * @ORM\Id + */ + private string $id; + + /** + * @ORM\ManyToOne(targetEntity="Rule", inversedBy="workflows") + * @ORM\JoinColumn(name="rule_id", referencedColumnName="id", nullable=false) + */ + private Rule $rule; + + /** + * @ORM\Column(name="date_created", type="datetime", nullable=false) + */ + private DateTime $dateCreated; + + /** + * @ORM\Column(name="date_modified", type="datetime", nullable=false) + */ + private DateTime $dateModified; + + /** + * @ORM\ManyToOne(targetEntity="User") + * @ORM\JoinColumn(name="created_by", referencedColumnName="id", nullable=false) + */ + private User $createdBy; + + /** + * @ORM\ManyToOne(targetEntity="User") + * @ORM\JoinColumn(name="modified_by", referencedColumnName="id", nullable=false) + */ + private User $modifiedBy; + + /** + * @ORM\Column(name="name", type="text", nullable=false) + */ + private string $name; + + /** + * @ORM\Column(name="description", type="text", nullable=true) + */ + private string $description; + + /** + * @ORM\Column(name="condition", type="text", nullable=false) + */ + private string $condition; + + /** + * @ORM\Column(name="deleted", type="boolean", options={"default":0}) + */ + private int $deleted; + + /** + * @var WorkflowAction[] + * + * @ORM\OneToMany(targetEntity="WorkflowAction", mappedBy="workflow") + */ + private $workflowActions; + + /** + * @var WorkflowLog[] + * + * @ORM\OneToMany(targetEntity="WorkflowLog", mappedBy="workflow") + */ + private $workflowLogs; + + public function __construct() + { + $this->workflowActions = new ArrayCollection(); + $this->workflowLogs = new ArrayCollection(); + } + + public function getId(): int + { + return $this->id; + } + + public function getRule(): ?Rule + { + return $this->rule; + } + + public function setRule(?Rule $rule): self + { + $this->rule = $rule; + return $this; + } + + public function setDateCreated($dateCreated): self + { + $this->dateCreated = $dateCreated; + return $this; + } + + public function getDateCreated(): DateTime + { + return $this->dateCreated; + } + + public function setDateModified($dateModified): self + { + $this->dateModified = $dateModified; + return $this; + } + + public function getDateModified(): DateTime + { + return $this->dateModified; + } + + public function getCreatedBy(): ?User + { + return $this->createdBy; + } + + public function setCreatedBy(?User $createdBy): self + { + $this->createdBy = $createdBy; + return $this; + } + + public function getModifiedBy(): ?User + { + return $this->modifiedBy; + } + + public function setModifiedBy(?User $modifiedBy): self + { + $this->modifiedBy = $modifiedBy; + return $this; + } + + public function setName($name): self + { + $this->name = $name; + return $this; + } + + public function getName(): string + { + return $this->name; + } + + public function setDescription($description): self + { + $this->description = $description; + return $this; + } + + public function getDescription(): string + { + return $this->description; + } + + public function getCondition(): array + { + return $this->condition; + } + + public function setCondition($condition): self + { + $this->condition = $condition; + return $this; + } + + public function setDeleted($deleted): self + { + $this->deleted = $deleted; + + return $this; + } + + public function getDeleted(): int + { + return $this->deleted; + } + + /** + * @return Collection|WorkflowAction[] + */ + public function getWorkflowActions(): Collection + { + return $this->workflowActions; + } + + public function addWorkflowActions(WorkflowAction $workflowAction): self + { + if (!$this->workflowActions->contains($workflowAction)) { + $this->workflowActions[] = $workflowAction; + $workflow->setWorkflow($this); + } + return $this; + } + + public function removeWorkflowAction(WorkflowAction $workflowAction): self + { + if ($this->workflowActions->removeElement($workflowAction)) { + // set the owning side to null (unless already changed) + if ($workflowAction->getWorkflow() === $this) { + $workflowAction->setWorkflow(null); + } + } + return $this; + } + + /** + * @return Collection|WorkflowLog[] + */ + public function getWorkflowLogs(): Collection + { + return $this->workflowLogs; + } + + public function addWorkflowLogs(WorkflowLog $workflowLog): self + { + if (!$this->workflowLogs->contains($workflowLog)) { + $this->workflowLogs[] = $workflowLog; + $workflow->setWorkflow($this); + } + return $this; + } + + public function removeWorkflowLog(WorkflowLog $workflowLog): self + { + if ($this->workflowLogs->removeElement($workflowLog)) { + // set the owning side to null (unless already changed) + if ($workflowLog->getWorkflow() === $this) { + $workflowLog->setWorkflow(null); + } + } + return $this; + } +} diff --git a/src/Entity/WorkflowAction.php b/src/Entity/WorkflowAction.php new file mode 100644 index 000000000..12f2595c7 --- /dev/null +++ b/src/Entity/WorkflowAction.php @@ -0,0 +1,215 @@ +. +*********************************************************************************/ + +namespace App\Entity; + +use DateTime; +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Table() + * @ORM\Entity(repositoryClass="App\Repository\WorkflowActionRepository") + * @ORM\Table(name="workflowaction", indexes={@ORM\Index(name="index_workflow_id", columns={"workflow_id"})}) + */ +class WorkflowAction +{ + /** + * @ORM\Column(name="id", type="string") + * @ORM\Id + */ + private string $id; + + /** + * @ORM\ManyToOne(targetEntity="Workflow", inversedBy="workflowActions") + * @ORM\JoinColumn(name="workflow_id", referencedColumnName="id", nullable=false) + */ + private Workflow $workflow; + + /** + * @ORM\Column(name="date_created", type="datetime", nullable=false) + */ + private DateTime $dateCreated; + + /** + * @ORM\Column(name="date_modified", type="datetime", nullable=false) + */ + private DateTime $dateModified; + + /** + * @ORM\ManyToOne(targetEntity="User") + * @ORM\JoinColumn(name="created_by", referencedColumnName="id", nullable=false) + */ + private User $createdBy; + + /** + * @ORM\ManyToOne(targetEntity="User") + * @ORM\JoinColumn(name="modified_by", referencedColumnName="id", nullable=false) + */ + private User $modifiedBy; + + /** + * @ORM\Column(name="name", type="text", nullable=false) + */ + private string $name; + + /** + * @ORM\Column(name="action", type="text", nullable=false) + */ + private string $action; + + /** + * @ORM\Column(name="arguments", type="array", nullable=false) + */ + private $arguments; + + /** + * @ORM\Column(name="description", type="text", nullable=true) + */ + private string $description; + + /** + * @ORM\Column(name="order", type="integer", length=3, nullable=false) + */ + private int $order; + + + public function getId(): string + { + return $this->id; + } + + public function getWorkflow(): ?Workflow + { + return $this->workflow; + } + + public function setWorkflow(?Workflow $workflow): self + { + $this->workflow = $workflow; + return $this; + } + + public function setDateCreated($dateCreated): self + { + $this->dateCreated = $dateCreated; + + return $this; + } + + public function getDateCreated(): DateTime + { + return $this->dateCreated; + } + + public function setDateModified($dateModified): self + { + $this->dateModified = $dateModified; + + return $this; + } + + public function getDateModified(): DateTime + { + return $this->dateModified; + } + + public function getCreatedBy(): ?User + { + return $this->createdBy; + } + + public function setCreatedBy(?User $createdBy): self + { + $this->createdBy = $createdBy; + return $this; + } + + public function getModifiedBy(): ?User + { + return $this->modifiedBy; + } + + public function setModifiedBy(?User $modifiedBy): self + { + $this->modifiedBy = $modifiedBy; + return $this; + } + + public function setName($name): self + { + $this->name = $name; + return $this; + } + + public function getName(): string + { + return $this->name; + } + + public function getAction(): array + { + return $this->action; + } + + public function setAction($action): self + { + $this->action = $action; + return $this; + } + + public function getArguments(): array + { + return $this->arguments; + } + + public function setArguments($arguments): self + { + $this->arguments = $arguments; + return $this; + } + + public function setDescription($description): self + { + $this->description = $description; + return $this; + } + + public function getDescription(): string + { + return $this->description; + } + + public function setOrder($order): self + { + $this->order = $order; + + return $this; + } + + public function getOrder(): string + { + return $this->order; + } +} diff --git a/src/Entity/WorkflowLog.php b/src/Entity/WorkflowLog.php new file mode 100644 index 000000000..b0c80262c --- /dev/null +++ b/src/Entity/WorkflowLog.php @@ -0,0 +1,204 @@ +. +*********************************************************************************/ + +namespace App\Entity; + +use DateTime; +use Doctrine\ORM\Mapping as ORM; +use Exception; + +/** + * @ORM\Table() + * @ORM\Entity(repositoryClass="App\Repository\WorkflowLogRepository") + * @ORM\Table(name="workflowlog", indexes={@ORM\Index(name="index_workflow_id", columns={"workflow_id"})}) + * @ORM\Table(name="workflowlog", indexes={@ORM\Index(name="index_job_id", columns={"job_id"})}) + */ +class WorkflowLog +{ + /** + * @ORM\Column(name="id", type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + */ + private int $id; + + /** + * @ORM\ManyToOne(targetEntity="Workflow", inversedBy="workflowLogs") + * @ORM\JoinColumn(name="workflow_id", referencedColumnName="id", nullable=false) + */ + private Workflow $workflow; + + /** + * @ORM\ManyToOne(targetEntity="Job", inversedBy="workflowLogs") + * @ORM\JoinColumn(name="job_id", referencedColumnName="id", nullable=false) + */ + private Job $job; + + /** + * @ORM\ManyToOne(targetEntity="Document", inversedBy="triggerDocuments") + * @ORM\JoinColumn(name="trigger_document_id", referencedColumnName="id", nullable=false) + */ + private Document $triggerDocument; + + /** + * @ORM\ManyToOne(targetEntity="Document", inversedBy="generateDocuments") + * @ORM\JoinColumn(name="generate_document_id", referencedColumnName="id", nullable=true) + */ + private ?Document $generateDocument= null; + + /** + * @ORM\ManyToOne(targetEntity="WorkflowAction") + * @ORM\JoinColumn(name="action_id", referencedColumnName="id", nullable=true) + */ + private WorkflowAction $action; + + /** + * @ORM\Column(name="status", type="string", nullable=true, options={"default":NULL}) + */ + private ?string $status; + + /** + * @ORM\Column(name="date_created", type="datetime", nullable=false) + */ + private DateTime $dateCreated; + + /** + * @ORM\ManyToOne(targetEntity="User") + * @ORM\JoinColumn(name="created_by", referencedColumnName="id", nullable=true) + */ + private User $createdBy; + + /** + * @ORM\Column(name="message", type="text", nullable=true) + */ + private string $message; + + + public function getId(): int + { + return $this->id; + } + + public function getWorkflow(): ?Workflow + { + return $this->workflow; + } + + public function setWorkflow(?Workflow $workflow): self + { + $this->workflow = $workflow; + return $this; + } + + public function getJob(): ?Job + { + return $this->job; + } + + public function setJob(?Job $job): self + { + $this->job = $job; + return $this; + } + + public function getTriggerDocument(): ?Document + { + return $this->triggerDocument; + } + + public function setTriggerDocument(?Document $document): self + { + $this->triggerDocument = $document; + return $this; + } + + public function getGenerateDocument(): ?Document + { + return $this->generateDocument; + } + + public function setGenerateDocument(?Document $document): self + { + $this->generateDocument = $document; + return $this; + } + + public function setStatus($status): self + { + $this->status = $status; + return $this; + } + + public function getStatus(): ?string + { + return $this->status; + } + + public function setDateCreated($dateCreated): self + { + $this->dateCreated = $dateCreated; + + return $this; + } + + public function getDateCreated(): DateTime + { + return $this->dateCreated; + } + + + public function getCreatedBy(): ?User + { + return $this->createdBy; + } + + public function setCreatedBy(?User $createdBy): self + { + $this->createdBy = $createdBy; + return $this; + } + + public function getAction(): array + { + return $this->action; + } + + public function setAction($action): self + { + $this->action = $action; + return $this; + } + + public function setMessage($message): self + { + $this->message = $message; + return $this; + } + + public function getMessage(): string + { + return $this->message; + } +} diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 1a9fd7783..1875d94ea 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -28,6 +28,10 @@ use App\Entity\Document; use App\Entity\DocumentData; use App\Entity\DocumentData as DocumentDataEntity; +use App\Entity\Workflow; +use App\Entity\WorkflowLog; +use App\Entity\WorkflowAction; +use App\Entity\Job; use App\Entity\DocumentRelationship as DocumentRelationship; use App\Repository\DocumentRepository; use App\Repository\RuleRelationShipRepository; @@ -36,6 +40,8 @@ use Exception; use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; +use Swift_Mailer; +use Swift_Message; class documentcore { @@ -51,6 +57,7 @@ class documentcore protected $ruleId; protected $ruleFields; protected $ruleRelationships; + protected $ruleWorkflows; protected $ruleParams; protected $sourceId; protected $targetId; @@ -298,6 +305,9 @@ public function setParam($param, $clear = false, $clearRule = true) if (!empty($param['ruleRelationships'])) { $this->ruleRelationships = $param['ruleRelationships']; } + if (!empty($param['ruleWorkflows'])) { + $this->ruleWorkflows = $param['ruleWorkflows']; + } // Init type error for each new document $this->typeError = 'S'; } catch (\Exception $e) { @@ -320,6 +330,7 @@ protected function clearAttributes($clearRule = true) $this->ruleId = ''; $this->ruleFields = []; $this->ruleRelationships = []; + $this->ruleWorkflows = []; $this->ruleParams = []; } $this->id = ''; @@ -2109,6 +2120,7 @@ public function updateStatus($new_status) $this->status = $new_status; $this->afterStatusChange($new_status); $this->createDocLog(); + $this->runWorkflow(); } catch (\Exception $e) { $this->message .= 'Error status update : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->typeError = 'E'; @@ -2503,8 +2515,177 @@ public function getStatus() { return $this->status; } + + protected function runWorkflow() { + try { + // Check if at least on workflow exist for the rule + if (!empty($this->ruleWorkflows)) { + // includ variables used in the formula + include __DIR__.'/../Utils/workflowVariables.php'; + if (file_exists( __DIR__.'/../Custom/Utils/workflowVariables.php')) { + include __DIR__.'/../Custom/Utils/workflowVariables.php'; + } + // Execute every workflow of the rule + foreach ($this->ruleWorkflows as $ruleWorkflow) { + // Check the condition + $this->formulaManager->init($ruleWorkflow['condition']); // mise en place de la règle dans la classe + $this->formulaManager->generateFormule(); // Genère la nouvelle formule à la forme PhP + $f = $this->formulaManager->execFormule(); + eval('$condition = '.$f.';'); // exec + // Execute the action if the condition is met + if ($condition == 1) { + // Execute all actions + if (!empty($ruleWorkflow['actions'])) { + // Call each actions + foreach($ruleWorkflow['actions'] as $action) { + // Check if the action has already been executed for the current document + // Only if attempt > 0, if it is the first attempt then the action has never been executed + if ($this->attempt > 0) { + // Search action for the current document + $workflowLogEntity = $this->entityManager->getRepository(WorkflowLog::class) + ->findOneBy([ + 'triggerDocument' => $this->id, + 'action' => $action['id'], + ] + ); + // If the current action has been found for the current document, we don't execute the current action + if ( + !empty($workflowLogEntity) + AND $workflowLogEntity->getStatus() == 'Success' + ) { + // GenerateDocument can be empty depending the action + if (!empty($workflowLogEntity->getGenerateDocument())) { + $this->docIdRefError = $workflowLogEntity->getGenerateDocument()->getId(); + } + $this->generateDocLog('W','Action ' . $action['id'] . ' already executed for this document. '); + continue; + } + } - /** + // Execute action depending of the function in the workflow + $arguments = unserialize($action['arguments']); + switch ($action['action']) { + case 'generateDocument': + $this->generateDocument($arguments['ruleId'],$this->sourceData[$arguments['searchValue']],$arguments['searchField'],$arguments['rerun'], $action); + break; + case 'sendNotification': + try { + $workflowStatus = 'Success'; + $error = ''; + // Method sendMessage throws an exception if it fails + $this->tools->sendMessage($arguments['to'],$arguments['subject'],$arguments['message']); + } catch (\Exception $e) { + $workflowStatus = 'Error'; + $error = 'Failed to create workflow log : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; + $this->logger->error($error); + $this->generateDocLog('E',$error); + } + $this->createWorkflowLog($action, $workflowStatus, $error); + break; + default: + throw new \Exception('Function '.key($action).' unknown.'); + } + } + } + } + } + } + } catch (\Exception $e) { + $this->logger->error('Failed to create workflow log : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + $this->generateDocLog('E','Failed to create workflow log : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + } + } + + // Generate a document using the rule id and search parameters + protected function generateDocument($ruleId, $searchValue = null, $searchField = 'id', $rerun = true, $action = null) + { + try { + // Instantiate the rule + $rule = new RuleManager($this->logger, $this->connection, $this->entityManager, $this->parameterBagInterface, $this->formulaManager, $this->solutionManager, clone $this); + $rule->setRule($ruleId); + $rule->setJobId($this->jobId); + + if (empty($searchValue)) { + $searchValue = $this->sourceId; + } + + // Generate the documents depending on the search parameter + $documents = $rule->generateDocuments($searchValue, true, '', $searchField); + if (!empty($documents->error)) { + throw new \Exception($documents->error); + } + // Run documents + if ( + !empty($documents) + and $rerun + ) { + foreach ($documents as $doc) { + $errors = $rule->actionDocument($doc->id, 'rerun'); + // Check errors + if (!empty($errors)) { + $this->message .= 'Document ' . $doc->id . ' in error (rule ' . $ruleId . ' : ' . $errors[0] . '. '; + } + // Generate the workflow log for each document if it has been generated by a workflow + if (!empty($action['id'])) { + $error = ''; + if (!empty($errors)) { + $error = $this->message; + $status = 'Error'; + } else { + $status = 'Success'; + } + $this->createWorkflowLog($action, $status, $error, $doc->id); + } + } + } + } catch (\Exception $e) { + $this->logger->error('Error : ' . $e->getMessage() . ' ' . $e->getFile() . ' Line : ( ' . $e->getLine() . ' )'); + $this->generateDocLog('E',$this->message); + } + } + + // Create a workflow log + protected function createWorkflowLog($action, $status, $error=null, $generateDocumentId=null) { + try { + // Generate the workflow log + $workflowLog = new WorkflowLog(); + // Set the current document + $triggerDocumentEntity = $this->entityManager->getRepository(Document::class)->find($this->id); + $workflowLog->setTriggerDocument($triggerDocumentEntity); + // Set the current action + $workflowActionEntity = $this->entityManager->getRepository(WorkflowAction::class)->find($action['id']); + $workflowLog->setAction($workflowActionEntity); + // Set the generated document if the action has generated a document + if (!empty($generateDocumentId)) { + $generateDocumentEntity = $this->entityManager->getRepository(Document::class)->find($generateDocumentId); + $this->docIdRefError = $generateDocumentId; + $workflowLog->setGenerateDocument($generateDocumentEntity); + } + // Set the workflow + $workflowEntity = $this->entityManager->getRepository(Workflow::class)->find($action['workflow_id']); + $workflowLog->setWorkflow($workflowEntity); + // Set the job + $jobEntity = $this->entityManager->getRepository(Job::class)->find($this->jobId); + $workflowLog->setJob($jobEntity); + // Set the creation date + $workflowLog->setDateCreated(new \DateTime()); + // Set the status depending on the error message + if (!empty($errors)) { + $workflowLog->setMessage($error); + $workflowLog->setStatus($status); + } else { + $workflowLog->setStatus('Success');; + } + $this->entityManager->persist($workflowLog); + $this->entityManager->flush(); + // Generate a document log. + $this->generateDocLog('S','Action '.$action['id'].' executed. '.(!empty($generateDocumentId) ? 'The document '.$generateDocumentId.' has been generated. ' : '')); + } catch (\Exception $e) { + $this->logger->error('Error : ' . $e->getMessage() . ' ' . $e->getFile() . ' Line : ( ' . $e->getLine() . ' )'); + $this->generateDocLog('E','Error : ' . $e->getMessage() . ' ' . $e->getFile() . ' Line : ( ' . $e->getLine() . ' )'); + } + } + /** * @throws \Doctrine\DBAL\Exception * Les id de la soluton, de la règle et du document * $type peut contenir : I (info;), W(warning), E(erreur), S(succès) @@ -2527,6 +2708,7 @@ protected function createDocLog() $stmt->bindValue(':job_id', $this->jobId); $result = $stmt->executeQuery(); $this->message = ''; + $this->docIdRefError = ''; } catch (\Exception $e) { $this->logger->error('Failed to create log : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); } @@ -2542,42 +2724,6 @@ public function generateDocLog($errorType, $message) $this->createDocLog(); } - // Generate a document using the rule id and search parameters - protected function generateDocument($ruleId, $searchValue = null, $searchField = 'id', $rerun = true) - { - try { - // Instantiate the rule - $rule = new RuleManager($this->logger, $this->connection, $this->entityManager, $this->parameterBagInterface, $this->formulaManager, $this->solutionManager, clone $this); - $rule->setRule($ruleId); - $rule->setJobId($this->jobId); - - if (empty($searchValue)) { - $searchValue = $this->sourceId; - } - // $this->sourceId = engagé ID - // Cherche tous les pôles de l'enregistrement correspondant à la règle - $documents = $rule->generateDocuments($searchValue, true, '', $searchField); - if (!empty($documents->error)) { - throw new \Exception($documents->error); - } - // Run documents - if ( - !empty($documents) - and $rerun - ) { - foreach ($documents as $doc) { - $errors = $rule->actionDocument($doc->id, 'rerun'); - // Check errors - if (!empty($errors)) { - $this->message .= 'Document ' . $doc->id . ' in error (rule ' . $ruleId . ' : ' . $errors[0] . '. '; - } - } - } - } catch (\Exception $e) { - $this->message .= 'Error : ' . $e->getMessage(); - $this->logger->error('Error : ' . $e->getMessage() . ' ' . $e->getFile() . ' Line : ( ' . $e->getLine() . ' )'); - } - } } class DocumentManager extends documentcore diff --git a/src/Manager/RuleManager.php b/src/Manager/RuleManager.php index 55d4721d2..bbd96068b 100644 --- a/src/Manager/RuleManager.php +++ b/src/Manager/RuleManager.php @@ -61,6 +61,7 @@ class rulecore protected $sourceFields; protected $targetFields; protected $ruleRelationships; + protected $ruleWorkflows; protected $ruleFilters; protected $solutionSource; protected $solutionTarget; @@ -153,6 +154,7 @@ public function setRule($idRule) $this->setRuleParam(); $this->setLimit(); $this->setRuleRelationships(); + $this->setRuleWorkflows(); // Set the rule fields (we use the name_slug in $this->rule) $this->setRuleField(); } @@ -270,6 +272,7 @@ public function generateDocuments($idSource, $readSource = true, $param = '', $i $docParam['rule'] = $this->rule; $docParam['ruleFields'] = $this->ruleFields; $docParam['ruleRelationships'] = $this->ruleRelationships; + $docParam['ruleWorkflows'] = $this->ruleWorkflows; $docParam['data'] = $docData; $docParam['jobId'] = $this->jobId; $docParam['api'] = $this->api; @@ -401,6 +404,7 @@ public function createDocuments() $param['rule'] = $this->rule; $param['ruleFields'] = $this->ruleFields; $param['ruleRelationships'] = $this->ruleRelationships; + $param['ruleWorkflows'] = $this->ruleWorkflows; // Set the param of the rule one time for all $this->documentManager->setRuleId($this->ruleId); $this->documentManager->setRuleParam(); @@ -725,6 +729,7 @@ public function checkPredecessorDocuments($documents = null): array $param['jobId'] = $this->jobId; $param['api'] = $this->api; $param['ruleRelationships'] = $this->ruleRelationships; + $param['ruleWorkflows'] = $this->ruleWorkflows; // Set the param values and clear all document attributes if($this->documentManager->setParam($param, true)) { $response[$document['id']] = $this->documentManager->checkPredecessorDocument(); @@ -758,6 +763,7 @@ public function checkParentDocuments($documents = null): array if (!empty($documents)) { $param['jobId'] = $this->jobId; $param['ruleRelationships'] = $this->ruleRelationships; + $param['ruleWorkflows'] = $this->ruleWorkflows; // Set all config parameters $this->setConfigParam(); // If migration mode, we select all documents to improve performance. For example, we won't execute queries is method document->getTargetId @@ -782,6 +788,7 @@ public function checkParentDocuments($documents = null): array $param['jobId'] = $this->jobId; $param['api'] = $this->api; $param['ruleRelationships'] = $this->ruleRelationships; + $param['ruleWorkflows'] = $this->ruleWorkflows; // Set the param values and clear all document attributes if($this->documentManager->setParam($param, true)) { $response[$document['id']] = $this->documentManager->checkParentDocument(); @@ -814,6 +821,7 @@ public function transformDocuments($documents = null): array if (!empty($documents)) { $param['ruleFields'] = $this->ruleFields; $param['ruleRelationships'] = $this->ruleRelationships; + $param['ruleWorkflows'] = $this->ruleWorkflows; $param['jobId'] = $this->jobId; $param['api'] = $this->api; // Set all config parameters @@ -885,6 +893,7 @@ public function getTargetDataDocuments($documents = null): array $param['solutionTarget'] = $this->solutionTarget; $param['ruleFields'] = $this->ruleFields; $param['ruleRelationships'] = $this->ruleRelationships; + $param['ruleWorkflows'] = $this->ruleWorkflows; $param['jobId'] = $this->jobId; $param['api'] = $this->api; // Set the param values and clear all document attributes @@ -1484,6 +1493,7 @@ protected function sendTarget($type, $documentId = null): array $send['ruleFields'] = $this->ruleFields; $send['ruleParams'] = $this->ruleParams; $send['ruleRelationships'] = $this->ruleRelationships; + $send['ruleWorkflows'] = $this->ruleWorkflows; $send['jobId'] = $this->jobId; // Si des données sont prêtes à être créées if (!empty($send['data'])) { @@ -1609,6 +1619,7 @@ protected function massSendTarget($type, $documentId = null) $send['ruleFields'] = $this->ruleFields; $send['ruleParams'] = $this->ruleParams; $send['ruleRelationships'] = $this->ruleRelationships; + $send['ruleWorkflows'] = $this->ruleWorkflows; $send['jobId'] = $this->jobId; // Si des données sont prêtes à être créées if (!empty($send['data'])) { @@ -2119,6 +2130,29 @@ protected function setRuleRelationships() } } + // Permet de charger toutes les relations de la règle + protected function setRuleWorkflows() + { + try { + $sqlWorkflows = 'SELECT * FROM workflow WHERE rule_id = :ruleId'; + $stmt = $this->connection->prepare($sqlWorkflows); + $stmt->bindValue(':ruleId', $this->ruleId); + $result = $stmt->executeQuery(); + $this->ruleWorkflows = $result->fetchAllAssociative(); + if (!empty($this->ruleWorkflows)) { + foreach($this->ruleWorkflows as $key => $workflow) { + $sqlActions = 'SELECT * FROM workflowaction WHERE workflow_id = :workflowid ORDER BY `order` ASC'; + $stmt = $this->connection->prepare($sqlActions); + $stmt->bindValue(':workflowid', $workflow['id']); + $result = $stmt->executeQuery(); + $this->ruleWorkflows[$key]['actions'] = $result->fetchAllAssociative(); + } + } + } catch (\Exception $e) { + $this->logger->error('Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + } + } + // Set the limit rule protected function setLimit() { diff --git a/src/Manager/ToolsManager.php b/src/Manager/ToolsManager.php index 47acd3559..a247e4192 100644 --- a/src/Manager/ToolsManager.php +++ b/src/Manager/ToolsManager.php @@ -32,6 +32,9 @@ use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Process\PhpExecutableFinder; use Symfony\Component\Yaml\Yaml; +use Swift_Mailer; +use Swift_Message; +use Swift_SmtpTransport; class toolscore { @@ -215,6 +218,37 @@ public function getParamValue($paramName) } return null; } + + // Send a message using Brevo or SMTP parameters + public function sendMessage($to, $subject, $message) { + // Use Brevo if the key is set + if (!empty($_ENV['SENDINBLUE_APIKEY'])) { + try { + $sendinblue = \SendinBlue\Client\Configuration::getDefaultConfiguration()->setApiKey('api-key', $_ENV['SENDINBLUE_APIKEY']); + $apiInstance = new \SendinBlue\Client\Api\TransactionalEmailsApi(new \GuzzleHttp\Client(), $sendinblue); + $sendSmtpEmail = new \SendinBlue\Client\Model\SendSmtpEmail(); // \SendinBlue\Client\Model\SendSmtpEmail | Values to send a transactional email + $sendSmtpEmail['to'] = array(array('email' => $to)); + $sendSmtpEmail['subject'] = $subject; + $sendSmtpEmail['htmlContent'] = $message; + $sendSmtpEmail['sender'] = array('email' => $this->configParams['email_from'] ?? 'no-reply@myddleware.com'); + $result = $apiInstance->sendTransacEmail($sendSmtpEmail); + } catch (Exception $e) { + throw new Exception('Exception when calling TransactionalEmailsApi->sendTransacEmail: '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + } + } else { + $swiftMessage = + (new Swift_Message($subject)) + ->setFrom($this->configParams['email_from'] ?? 'no-reply@myddleware.com') + ->setBody($message); + // Send message + $message->setTo($to); + $send = $this->mailer->send($message); + if (!$send) { + $this->logger->error('Failed to send alert email : '.$message.' to '.$to); + throw new Exception('Failed to send alert email : '.$message.' to '.$to); + } + } + } } class ToolsManager extends toolscore diff --git a/src/Repository/WorkflowActionRepository.php b/src/Repository/WorkflowActionRepository.php new file mode 100644 index 000000000..08ec6af83 --- /dev/null +++ b/src/Repository/WorkflowActionRepository.php @@ -0,0 +1,38 @@ +. +*********************************************************************************/ + +namespace App\Repository; + +use App\Entity\WorkflowAction; +use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\Persistence\ManagerRegistry; + +class WorkflowActionRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, WorkflowAction::class); + } +} diff --git a/src/Repository/WorkflowLogRepository.php b/src/Repository/WorkflowLogRepository.php new file mode 100644 index 000000000..b0c097096 --- /dev/null +++ b/src/Repository/WorkflowLogRepository.php @@ -0,0 +1,38 @@ +. +*********************************************************************************/ + +namespace App\Repository; + +use App\Entity\WorkflowLog; +use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\Persistence\ManagerRegistry; + +class WorkflowLogRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, WorkflowLog::class); + } +} diff --git a/src/Repository/WorkflowRepository.php b/src/Repository/WorkflowRepository.php new file mode 100644 index 000000000..20469fe0d --- /dev/null +++ b/src/Repository/WorkflowRepository.php @@ -0,0 +1,38 @@ +. +*********************************************************************************/ + +namespace App\Repository; + +use App\Entity\Workflow; +use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\Persistence\ManagerRegistry; + +class WorkflowRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Workflow::class); + } +} diff --git a/src/Utils/workflowVariables.php b/src/Utils/workflowVariables.php new file mode 100644 index 000000000..557a8182c --- /dev/null +++ b/src/Utils/workflowVariables.php @@ -0,0 +1,3 @@ +status; + From c19ee6a42dd4fc9a96e95d937be4ea2cf7be42c9 Mon Sep 17 00:00:00 2001 From: myddleware Date: Thu, 4 Apr 2024 23:09:39 +0200 Subject: [PATCH 021/452] Compare field : manage date field Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 1875d94ea..36f8c2ae7 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -1259,7 +1259,14 @@ protected function checkNoChange($history): bool ) { continue; } - + // In case of date with different format (2024-03-14T11:04:16+00:00 == 2024-03-14T12:04:16+01:00) + if ( + !empty(strtotime($history[$field['target_field_name']])) + AND !empty(strtotime($target[$field['target_field_name']])) + AND strtotime($history[$field['target_field_name']]) == strtotime($target[$field['target_field_name']]) + ) { + continue; + } return false; } } From d2bec31bb18c51b6bb928745c3a5a1cd0c8d7e51 Mon Sep 17 00:00:00 2001 From: myddleware Date: Fri, 5 Apr 2024 18:07:27 +0200 Subject: [PATCH 022/452] Avoid to have several result after the serach duplicates Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 7 ++- src/Solutions/salesforce.php | 104 +++++++++++++++++++++++--------- 2 files changed, 82 insertions(+), 29 deletions(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 36f8c2ae7..25ffa4717 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -1329,7 +1329,11 @@ protected function getDocumentHistory($searchFields) } // If read method returns a result else { - // Select the first result + // Error il multiple duplicate records found + if (count($dataTarget['values']) > 1) { + $this->message .= count($dataTarget['values']).' duplicates found. Only one duplicate maximum can be found.'; + return -1; + } $record = current($dataTarget['values']); $updateHistory = $this->updateHistoryTable($record); if (true === $updateHistory) { @@ -1338,7 +1342,6 @@ protected function getDocumentHistory($searchFields) // Erreur dans la mise à jour de la table historique else { $this->message .= $dataTarget['error']; - return -1; } } diff --git a/src/Solutions/salesforce.php b/src/Solutions/salesforce.php index 7548138a5..74e5b52fd 100644 --- a/src/Solutions/salesforce.php +++ b/src/Solutions/salesforce.php @@ -73,6 +73,8 @@ class salesforcecore extends solution { ); protected string $versionApi = 'v38.0'; + + protected bool $sendDeletion = true; // Connexion à Salesforce - Instancie la classe salesforce et affecte access_token et instance_url public function login($paramConnexion) { @@ -656,10 +658,12 @@ public function updateData($param): array ) { $parameters['Pricebook2Id'] = $param['ruleParams']['Pricebook2Id']; } - + if (empty($target_id)) { + throw new \Exception ('The target id is requiered for an update.'); + } $parameters = json_encode($parameters); // Appel de la requête - $query_request_data = $this->call($query_url, $parameters, true); + $query_request_data = $this->call($query_url, $parameters, 'PATCH'); if ($query_request_data === true) { $result[$idDoc] = array( @@ -687,6 +691,54 @@ public function updateData($param): array return $result; } + /** + * @throws \Doctrine\DBAL\Exception + * @throws \Exception + */ + public function deleteData($param): array + { + if(!(isset($param['data']))) { + throw new \Exception ('Data missing for update'); + } + foreach($param['data'] as $idDoc => $data) { + try{ + // Check control before update + $data = $this->checkDataBeforeDelete($param, $data, $idDoc); + // Instanciation de l'URL d'appel + if (empty($data['target_id'])) { + throw new \Exception ('The target id is requiered for a deletion.'); + } + $query_url = $this->instance_url."/services/data/".$this->versionApi."/sobjects/".$param['module'].'/'.$data['target_id']; + + // Appel de la requête + $query_request_data = $this->call($query_url, true, 'DELETE'); + + if ($query_request_data === true) { + $result[$idDoc] = array( + 'id' => $data['target_id'], + 'error' => false + ); + } + else { + $result[$idDoc] = array( + 'id' => '-1', + 'error' => 'Failed to update Data in Salesforce : '.print_r($query_request_data['errors'],true), + ); + } + } + catch (\Exception $e) { + $error = 'Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; + $result[$idDoc] = array( + 'id' => '-1', + 'error' => $error + ); + } + // Modification du statut du flux + $this->updateDocumentStatus($idDoc,$result[$idDoc],$param); + } + return $result; + } + // Permet de formater la réponse si besoin protected function formatResponse($param,$query_request_data) { return $query_request_data; @@ -718,8 +770,13 @@ protected function getFrom($param): string protected function getWhere($param): string { if (!empty($param['query'])) { + $queryWhere = "+WHERE+"; foreach ($param['query'] as $key => $value) { - $queryWhere = "+WHERE+".$key."+=+'".$value."'"; + $queryWhere .= $key."+=+'".$value."'"; + // Add the AND if not the last entry of the array + if ($key !== array_key_last($param['query'])) { + $queryWhere .= "+AND+"; + } } } else { // On va chercher le nom du champ pour la date de référence: Création ou Modification @@ -796,44 +853,37 @@ public function getRefFieldName($param): string } return ""; } - - // Fonction permettant de faire l'appel REST /** * @throws \Exception */ - protected function call($url, $parameters, $update = false){ + protected function call($url, $parameters, $method = null){ ob_start(); $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); // important (testé en Local wamp) afin de ne pas vérifier le certificat SSL if($parameters === false){ // Si l'appel ne possède pas de paramètres, on exécute un GET en curl - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: OAuth '.$this->access_token)); - } - elseif ($update === false) { // Si l'appel en revanche possède des paramètres dans $parameters, on exécute un POST en curl - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE); - if(!isset($parameters['grant_type'])) // A ne pas ajouter pour la connexion - curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: OAuth " . $this->access_token, "Content-type: application/json")); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); // important (testé en Local wamp) afin de ne pas vérifier le certificat SSL - curl_setopt($ch, CURLOPT_POST, TRUE); - curl_setopt($ch, CURLOPT_POSTFIELDS, $parameters); - } - else { - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); + } else { + // No Authorization in case of login action + if(!isset($parameters['grant_type'])) { + curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: OAuth " . $this->access_token, "Content-type: application/json")); + } + // PATCH or DELETE + if (!empty($method)) { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); + // POST + } else { + curl_setopt($ch, CURLOPT_POST, TRUE); + } curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); // important (testé en Local wamp) afin de ne pas vérifier le certificat SSL - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH'); - curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: OAuth " . $this->access_token, "Content-type: application/json")); curl_setopt($ch, CURLOPT_POSTFIELDS, $parameters); } $query_request_body = curl_exec($ch); // Si on est sur un update et que l'on a un retour 204 on renvoie true - if ($update === true) { + if (!empty($method)) { if(curl_getinfo($ch, CURLINFO_HTTP_CODE) == '204') { return true; } From 6a7e3185f0a5e233a9ee41771680033eb41e7fe2 Mon Sep 17 00:00:00 2001 From: myddleware Date: Fri, 5 Apr 2024 18:08:33 +0200 Subject: [PATCH 023/452] Salesforce connector : add delete action Signed-off-by: myddleware --- src/Solutions/salesforce.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Solutions/salesforce.php b/src/Solutions/salesforce.php index 74e5b52fd..d583c4063 100644 --- a/src/Solutions/salesforce.php +++ b/src/Solutions/salesforce.php @@ -699,7 +699,7 @@ public function deleteData($param): array { if(!(isset($param['data']))) { throw new \Exception ('Data missing for update'); - } + } foreach($param['data'] as $idDoc => $data) { try{ // Check control before update From 58d27e5c51243603219fce30bd47ede2ff5a9fe5 Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 10 Apr 2024 16:21:49 +0200 Subject: [PATCH 024/452] Database connector : manage reference field when it is a numeric Signed-off-by: myddleware --- src/Solutions/database.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Solutions/database.php b/src/Solutions/database.php index cc55443ad..6b4d118a7 100644 --- a/src/Solutions/database.php +++ b/src/Solutions/database.php @@ -215,7 +215,7 @@ public function readData($param) $param['date_ref'] = 0; } $result['date_ref'] = $param['date_ref']; - + if (empty($param['limit'])) { $param['limit'] = 100; } @@ -347,7 +347,10 @@ public function readData($param) // If the reference isn't a valid date (it could be an ID in case there is no date in the table) we set the current date if ((bool) strtotime($value)) { $row['date_modified'] = $value; - } else { + // If the ref field is a numeric (increment), we transform it to a date (timestamp) to be able to save the corresponding date to the document + } elseif (is_numeric($value)) { + $row['date_modified'] = date('Y-m-d H:i:s', $value); + } else { $row['date_modified'] = date('Y-m-d H:i:s'); } $result['date_ref'] = $row['date_modified']; @@ -379,7 +382,6 @@ public function readData($param) } catch (Exception $e) { $result['error'] = 'Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; } - return $result; } From 119e5e5f097ac82f1d8d7ceb59738eb551696182 Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 10 Apr 2024 16:30:40 +0200 Subject: [PATCH 025/452] Database connector : manage ref rule field in case of the reference is an id Signed-off-by: myddleware --- src/Solutions/database.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Solutions/database.php b/src/Solutions/database.php index 6b4d118a7..06cc5f1ab 100644 --- a/src/Solutions/database.php +++ b/src/Solutions/database.php @@ -347,13 +347,15 @@ public function readData($param) // If the reference isn't a valid date (it could be an ID in case there is no date in the table) we set the current date if ((bool) strtotime($value)) { $row['date_modified'] = $value; + $result['date_ref'] = $row['date_modified']; // If the ref field is a numeric (increment), we transform it to a date (timestamp) to be able to save the corresponding date to the document } elseif (is_numeric($value)) { $row['date_modified'] = date('Y-m-d H:i:s', $value); + $result['date_ref'] = $value; } else { $row['date_modified'] = date('Y-m-d H:i:s'); + $result['date_ref'] = $row['date_modified']; } - $result['date_ref'] = $row['date_modified']; } } elseif ('history' == $param['call_type']) { // Id is fieldId for a history action if ($key === $param['ruleParams']['targetFieldId']) { From e86740868321443d2bb82e5c3c2acef55f6a1f6a Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 10 Apr 2024 16:33:29 +0200 Subject: [PATCH 026/452] Change comment Signed-off-by: myddleware --- src/Solutions/database.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Solutions/database.php b/src/Solutions/database.php index 06cc5f1ab..523169215 100644 --- a/src/Solutions/database.php +++ b/src/Solutions/database.php @@ -344,7 +344,7 @@ public function readData($param) $row['id'] = $value; } if ($key === $param['ruleParams']['fieldDateRef']) { - // If the reference isn't a valid date (it could be an ID in case there is no date in the table) we set the current date + // If the reference is a valid date, we save it if ((bool) strtotime($value)) { $row['date_modified'] = $value; $result['date_ref'] = $row['date_modified']; @@ -352,6 +352,7 @@ public function readData($param) } elseif (is_numeric($value)) { $row['date_modified'] = date('Y-m-d H:i:s', $value); $result['date_ref'] = $value; + // In all other cases, we set the current date } else { $row['date_modified'] = date('Y-m-d H:i:s'); $result['date_ref'] = $row['date_modified']; From 0daeda9703a7cad4a4262a7581080129495d0d24 Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 17 Apr 2024 00:39:04 +0200 Subject: [PATCH 027/452] Fix workflow process when the workflow is executed after send Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 10 +++++++--- src/Manager/RuleManager.php | 18 ++++++++++++++++-- src/Solutions/salesforce.php | 2 +- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 25ffa4717..f7c795fcb 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -2130,7 +2130,10 @@ public function updateStatus($new_status) $this->status = $new_status; $this->afterStatusChange($new_status); $this->createDocLog(); - $this->runWorkflow(); + // runWorkflow can't be executed if updateStatus is called from the solution class + if ($new_status!='Send') { + $this->runWorkflow(); + } } catch (\Exception $e) { $this->message .= 'Error status update : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->typeError = 'E'; @@ -2462,7 +2465,8 @@ protected function getTargetId($ruleRelationship, $record_id) !empty($result['document_id']) AND strpos($result['document_id'], ',') ) { - $result['document_id'] = end(explode(',',$result['document_id'])); + $documentList = explode(',',$result['document_id']); + $result['document_id'] = end($documentList); } } if (!empty($result['record_id'])) { @@ -2526,7 +2530,7 @@ public function getStatus() return $this->status; } - protected function runWorkflow() { + public function runWorkflow() { try { // Check if at least on workflow exist for the rule if (!empty($this->ruleWorkflows)) { diff --git a/src/Manager/RuleManager.php b/src/Manager/RuleManager.php index bbd96068b..3f71ea585 100644 --- a/src/Manager/RuleManager.php +++ b/src/Manager/RuleManager.php @@ -1551,7 +1551,22 @@ protected function sendTarget($type, $documentId = null): array throw new \Exception('Failed to connect to the target application.'); } } - + // Run workflow after send + if ( + !empty($response) + AND empty($response['error']) + AND !empty($this->ruleWorkflows) + ) { + foreach($response as $docId => $value) { + $param['id_doc_myddleware'] = $docId; + $param['jobId'] = $this->jobId; + $param['api'] = $this->api; + $param['ruleWorkflows'] = $this->ruleWorkflows; + // Set the param values and clear all document attributes + $this->documentManager->setParam($param, true); + $this->documentManager->runWorkflow(); + } + } } catch (\Exception $e) { $response['error'] = 'Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; if (!$this->api) { @@ -1559,7 +1574,6 @@ protected function sendTarget($type, $documentId = null): array } $this->logger->error($response['error']); } - return $response; } diff --git a/src/Solutions/salesforce.php b/src/Solutions/salesforce.php index d583c4063..1217699fc 100644 --- a/src/Solutions/salesforce.php +++ b/src/Solutions/salesforce.php @@ -772,7 +772,7 @@ protected function getWhere($param): string if (!empty($param['query'])) { $queryWhere = "+WHERE+"; foreach ($param['query'] as $key => $value) { - $queryWhere .= $key."+=+'".$value."'"; + $queryWhere .= $key."+=+'".str_replace(' ', '+', $value)."'"; // Add the AND if not the last entry of the array if ($key !== array_key_last($param['query'])) { $queryWhere .= "+AND+"; From 5f57060c4f3e7eeca4477b696549985a688e5528 Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 17 Apr 2024 00:48:56 +0200 Subject: [PATCH 028/452] Add parameterBagInterface Signed-off-by: myddleware --- src/Solutions/solution.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Solutions/solution.php b/src/Solutions/solution.php index e065906eb..6068350ce 100644 --- a/src/Solutions/solution.php +++ b/src/Solutions/solution.php @@ -153,7 +153,7 @@ protected function updateDocumentStatus($idDoc, $value, $param, $forceStatus = n try { $param['id_doc_myddleware'] = $idDoc; $param['api'] = $this->api; - $documentManager = new DocumentManager($this->logger, $this->connection, $this->entityManager, $this->documentRepository, $this->ruleRelationshipsRepository, $this->formulaManager); + $documentManager = new DocumentManager($this->logger, $this->connection, $this->entityManager, $this->documentRepository, $this->ruleRelationshipsRepository, $this->formulaManager, null, $this->parameterBagInterface); $documentManager->setParam($param); // If a message exist, we add it to the document logs if (!empty($value['error'])) { From 054f5ff731dd5cbb9f96f0371e606269972d7216 Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 17 Apr 2024 00:53:51 +0200 Subject: [PATCH 029/452] Catch all formula error Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index f7c795fcb..d9ad8a1ad 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -1598,7 +1598,7 @@ public function getTransformValue($source, $ruleField) // Trigger to redefine formula $f = $this->changeFormula($f); eval($f.';'); // exec - } catch (\ParseError $e) { + } catch (\Throwable $e) { throw new \Exception('FATAL error because of Invalid formula "'.$ruleField['formula'].';" : '.$e->getMessage()); } // Execute eval only if formula is valid From b4f31ad88326493cd283de875d7076e4d8551dfe Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 17 Apr 2024 01:11:47 +0200 Subject: [PATCH 030/452] Add column active and deleted on workflow tables Signed-off-by: myddleware --- src/Entity/Workflow.php | 17 ++++++++++++++++- src/Entity/WorkflowAction.php | 33 ++++++++++++++++++++++++++++++++- src/Manager/RuleManager.php | 4 ++-- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/Entity/Workflow.php b/src/Entity/Workflow.php index 79a9d4d8c..7f01af2b9 100644 --- a/src/Entity/Workflow.php +++ b/src/Entity/Workflow.php @@ -86,6 +86,11 @@ class Workflow * @ORM\Column(name="condition", type="text", nullable=false) */ private string $condition; + + /** + * @ORM\Column(name="active", type="boolean", options={"default":1}) + */ + private int $active; /** * @ORM\Column(name="deleted", type="boolean", options={"default":0}) @@ -208,7 +213,6 @@ public function setCondition($condition): self public function setDeleted($deleted): self { $this->deleted = $deleted; - return $this; } @@ -217,6 +221,17 @@ public function getDeleted(): int return $this->deleted; } + public function setActive($active): self + { + $this->active = $active; + return $this; + } + + public function getActive(): int + { + return $this->active; + } + /** * @return Collection|WorkflowAction[] */ diff --git a/src/Entity/WorkflowAction.php b/src/Entity/WorkflowAction.php index 12f2595c7..7cbfa5668 100644 --- a/src/Entity/WorkflowAction.php +++ b/src/Entity/WorkflowAction.php @@ -94,7 +94,16 @@ class WorkflowAction */ private int $order; - + /** + * @ORM\Column(name="active", type="boolean", options={"default":1}) + */ + private int $active; + + /** + * @ORM\Column(name="deleted", type="boolean", options={"default":0}) + */ + private int $deleted; + public function getId(): string { return $this->id; @@ -212,4 +221,26 @@ public function getOrder(): string { return $this->order; } + + public function setActive($active): self + { + $this->active = $active; + return $this; + } + + public function getActive(): int + { + return $this->active; + } + + public function setDeleted($deleted): self + { + $this->deleted = $deleted; + return $this; + } + + public function getDeleted(): int + { + return $this->deleted; + } } diff --git a/src/Manager/RuleManager.php b/src/Manager/RuleManager.php index 3f71ea585..13809b2ba 100644 --- a/src/Manager/RuleManager.php +++ b/src/Manager/RuleManager.php @@ -2148,14 +2148,14 @@ protected function setRuleRelationships() protected function setRuleWorkflows() { try { - $sqlWorkflows = 'SELECT * FROM workflow WHERE rule_id = :ruleId'; + $sqlWorkflows = 'SELECT * FROM workflow WHERE rule_id = :ruleId AND deleted = 0 AND active = 1'; $stmt = $this->connection->prepare($sqlWorkflows); $stmt->bindValue(':ruleId', $this->ruleId); $result = $stmt->executeQuery(); $this->ruleWorkflows = $result->fetchAllAssociative(); if (!empty($this->ruleWorkflows)) { foreach($this->ruleWorkflows as $key => $workflow) { - $sqlActions = 'SELECT * FROM workflowaction WHERE workflow_id = :workflowid ORDER BY `order` ASC'; + $sqlActions = 'SELECT * FROM workflowaction WHERE workflow_id = :workflowid AND deleted = 0 ORDER BY `order` ASC'; $stmt = $this->connection->prepare($sqlActions); $stmt->bindValue(':workflowid', $workflow['id']); $result = $stmt->executeQuery(); From 76ef5e1341f610f8f8846554e33aae74e35af215 Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 17 Apr 2024 01:12:25 +0200 Subject: [PATCH 031/452] Add active condition for workflow action Signed-off-by: myddleware --- src/Manager/RuleManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Manager/RuleManager.php b/src/Manager/RuleManager.php index 13809b2ba..3b22e52cf 100644 --- a/src/Manager/RuleManager.php +++ b/src/Manager/RuleManager.php @@ -2155,7 +2155,7 @@ protected function setRuleWorkflows() $this->ruleWorkflows = $result->fetchAllAssociative(); if (!empty($this->ruleWorkflows)) { foreach($this->ruleWorkflows as $key => $workflow) { - $sqlActions = 'SELECT * FROM workflowaction WHERE workflow_id = :workflowid AND deleted = 0 ORDER BY `order` ASC'; + $sqlActions = 'SELECT * FROM workflowaction WHERE workflow_id = :workflowid AND deleted = 0 AND active = 1 ORDER BY `order` ASC'; $stmt = $this->connection->prepare($sqlActions); $stmt->bindValue(':workflowid', $workflow['id']); $result = $stmt->executeQuery(); From 31e04cf53c1e44864b75c2f7f12b2f8518590d2a Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 17 Apr 2024 23:34:15 +0200 Subject: [PATCH 032/452] Add lookup formula to manage relationship Signed-off-by: myddleware --- src/Controller/DefaultController.php | 4 +- src/Manager/DocumentManager.php | 35 ++++--- src/Manager/FormulaFunctionManager.php | 124 ++++++++++++++++++++++++- 3 files changed, 145 insertions(+), 18 deletions(-) diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index c5261966f..1a1bd2292 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -1408,8 +1408,10 @@ public function ruleSimulation(Request $request): Response 'related_rule' => '', ]; + // Add rule id for simulation purpose when using lookup function + $this->documentManager->setRuleId($ruleKey); // Transformation - $response = $this->documentManager->getTransformValue($record, $target_fields); + $response = $this->documentManager->getTransformValue($record, $target_fields); if (!isset($response['message'])) { $r['after'][$name_fields_target] = $this->documentManager->getTransformValue($record, $target_fields); } diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index d9ad8a1ad..06d5552d3 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -1589,25 +1589,30 @@ public function getTransformValue($source, $ruleField) $this->formulaManager->init($ruleField['formula']); // mise en place de la règle dans la classe $this->formulaManager->generateFormule(); // Genère la nouvelle formule à la forme PhP - // Exécute la règle si pas d'erreur de syntaxe - if ( - $f = $this->formulaManager->execFormule() - ) { - // Try the formula first + // Execute formula + if ($f = $this->formulaManager->execFormule()) { + // Manage lookup formula by adding the current rule as the first parameter + if (strpos($f, 'lookup') !== false ) { + $currentRule = $this->ruleId; + $connection = $this->connection; + $entityManager = $this->entityManager; + $myddlewareUserId = $this->userId; + $sourceFieldName = $ruleField['source_field_name']; + $docId = $this->id; + $f = str_replace('lookup(', 'lookup($entityManager, $connection, $currentRule, $docId, $myddlewareUserId, $sourceFieldName, ', $f); + } try { // Trigger to redefine formula $f = $this->changeFormula($f); - eval($f.';'); // exec + eval('$rFormula = '.$f.';'); // exec + if (isset($rFormula)) { + // Return result + return $rFormula; + } else { + throw new \Exception('Invalid formula (failed to retrieve formula) : '.$ruleField['formula']); + } } catch (\Throwable $e) { - throw new \Exception('FATAL error because of Invalid formula "'.$ruleField['formula'].';" : '.$e->getMessage()); - } - // Execute eval only if formula is valid - eval('$rFormula = '.$f.';'); // exec - if (isset($rFormula)) { - // affectation du résultat - return $rFormula; - } else { - throw new \Exception('Invalid formula (failed to retrieve formula) : '.$ruleField['formula']); + throw new \Exception('Failed to execute the formula "'.$ruleField['formula'].';" : '.$e->getMessage()); } } else { throw new \Exception('Invalid formula (failed to execute) : '.$ruleField['formula']); diff --git a/src/Manager/FormulaFunctionManager.php b/src/Manager/FormulaFunctionManager.php index 2cf5d599f..6f0df2275 100644 --- a/src/Manager/FormulaFunctionManager.php +++ b/src/Manager/FormulaFunctionManager.php @@ -25,11 +25,13 @@ namespace App\Manager; +use App\Entity\DocumentRelationship as DocumentRelationship; + class formulafunctioncore { - protected array $names = ['changeTimeZone', 'changeFormatDate', 'changeValue', 'changeMultiValue', 'getValueFromArray']; + protected array $names = ['changeTimeZone', 'changeFormatDate', 'changeValue', 'changeMultiValue', 'getValueFromArray','lookup']; protected string $path = "App\Manager\FormulaFunctionManager::"; - + public function getNamesFunctions(): array { return $this->names; @@ -119,6 +121,124 @@ public static function getValueFromArray($key, $array) return $array[$key]; } } + + public static function lookup($entityManager, $connection, $currentRule, $docId, $myddlewareUserId, $sourceFieldName, $fieldValue, $rule, $errorIfEmpty=false, $errodIfNoFound=true, $parent=false) + { + // Manage error if empty + if (empty($fieldValue)) { + if ($errorIfEmpty) { + throw new \Exception('The field '.$sourceFieldName.' is empty. Failed to find the relate value. '); + } else { + return ''; + } + } + // In case of simulation during rule creation (not edition), we don't have the current rule id. + // We set direction = 1 by default + if ($currentRule === 0) { + $direction = 1; + } else { + // Get rules detail + $ruleQuery = "SELECT * FROM rule WHERE id = :ruleId"; + $stmt = $connection->prepare($ruleQuery); + $stmt->bindValue(':ruleId', $currentRule); + $result = $stmt->executeQuery(); + $ruleRef = $result->fetchAssociative(); + $stmt->bindValue(':ruleId', $rule); + $result = $stmt->executeQuery(); + $ruleLink = $result->fetchAssociative(); + } + + // Query to search the relate record id is different depending on the direction of the relationship + if ( + ( + !empty($ruleRef) + AND $ruleRef['conn_id_source'] == $ruleLink['conn_id_source'] + AND $ruleRef['conn_id_target'] == $ruleLink['conn_id_target'] + ) + OR (!empty($direction)) // Manage simulation + ){ + $sqlParams = " SELECT + target_id record_id, + GROUP_CONCAT(DISTINCT document.id) document_id, + GROUP_CONCAT(DISTINCT document.type) types + FROM document + WHERE + document.rule_id = :ruleRelateId + AND document.source_id = :record_id + AND document.deleted = 0 + AND document.target_id != '' + AND ( + document.global_status = 'Close' + OR document.status = 'No_send' + ) + GROUP BY target_id + HAVING types NOT LIKE '%D%' + LIMIT 1"; + $direction = 1; + } elseif ( + $ruleRef['conn_id_source'] == $ruleLink['conn_id_target'] + AND $ruleRef['conn_id_target'] == $ruleLink['conn_id_source'] + ){ + $sqlParams = " SELECT + source_id record_id, + GROUP_CONCAT(DISTINCT document.id) document_id, + GROUP_CONCAT(DISTINCT document.type) types + FROM document + WHERE + document.rule_id = :ruleRelateId + AND document.source_id != '' + AND document.deleted = 0 + AND document.target_id = :record_id + AND ( + document.global_status = 'Close' + OR document.status = 'No_send' + ) + GROUP BY source_id + HAVING types NOT LIKE '%D%' + LIMIT 1"; + $direction = -1; + } else { + throw new \Exception('The connectors do not match between rule '.$currentRule.' and rule '.$rule.'. '); + } + // Get the record id + $stmt = $connection->prepare($sqlParams); + $stmt->bindValue(':ruleRelateId', $rule); + $stmt->bindValue(':record_id', $fieldValue); + $result = $stmt->executeQuery(); + $result = $result->fetchAssociative(); + // Manage error if no result found + if (empty($result['record_id'])) { + if ($errodIfNoFound) { + throw new \Exception('Failed to retrieve a related document. No data for the field '.$sourceFieldName.'. There is not record with the ID '.('1' == $direction ? 'source' : 'target').' '.$fieldValue.' in the rule '.$ruleLink['name'].'. This document is queued. '); + } else { + return ''; + } + } + // In cas of several document found we get only the last one + if ( + !empty($result['document_id']) + AND strpos($result['document_id'], ',') + ) { + $documentList = explode(',',$result['document_id']); + $result['document_id'] = end($documentList); + } + // No doc id in case of simulation + if (!empty($docId)) { + // Add the relationship in the table document Relationship + try { + $documentRelationship = new DocumentRelationship(); + $documentRelationship->setDocId($docId); + $documentRelationship->setDocRelId($result['document_id']); + $documentRelationship->setDateCreated(new \DateTime()); + $documentRelationship->setCreatedBy((int) $myddlewareUserId); + $documentRelationship->setSourceField($sourceFieldName); + $entityManager->persist($documentRelationship); + } catch (\Exception $e) { + throw new \Exception('Failed to save the document relationship for the field '.$sourceFieldName.' : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + } + } + return $result['record_id']; + } } class FormulaFunctionManager extends formulafunctioncore From b3961fef72532f70906d8dc47811ff932be4c68e Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 17 Apr 2024 23:51:23 +0200 Subject: [PATCH 033/452] Add translation Signed-off-by: myddleware --- src/DataFixtures/LoadFunctionData.php | 2 +- src/Manager/FormulaFunctionManager.php | 6 +++--- translations/messages.en.yml | 1 + translations/messages.fr.yml | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/DataFixtures/LoadFunctionData.php b/src/DataFixtures/LoadFunctionData.php index e3a46f2b4..2e230d6e5 100644 --- a/src/DataFixtures/LoadFunctionData.php +++ b/src/DataFixtures/LoadFunctionData.php @@ -35,7 +35,7 @@ class LoadFunctionData implements FixtureInterface private $manager; protected $functionData = [ 'mathematical' => ['round', 'ceil', 'abs'], - 'text' => ['trim', 'ltrim', 'rtrim', 'lower', 'upper', 'substr', 'striptags', 'changeValue', 'htmlEntityDecode', 'replace', 'utf8encode', 'utf8decode', 'htmlentities', 'htmlspecialchars', 'strlen', 'urlencode', 'chr', 'json_decode', 'json_encode', 'getValueFromArray'], + 'text' => ['trim', 'ltrim', 'rtrim', 'lower', 'upper', 'substr', 'striptags', 'changeValue', 'htmlEntityDecode', 'replace', 'utf8encode', 'utf8decode', 'htmlentities', 'htmlspecialchars', 'strlen', 'urlencode', 'chr', 'json_decode', 'json_encode', 'getValueFromArray','lookup'], 'date' => ['date', 'microtime', 'changeTimeZone', 'changeFormatDate'], 'constant' => ['mdw_no_send_field'], ]; diff --git a/src/Manager/FormulaFunctionManager.php b/src/Manager/FormulaFunctionManager.php index 6f0df2275..24293615d 100644 --- a/src/Manager/FormulaFunctionManager.php +++ b/src/Manager/FormulaFunctionManager.php @@ -122,10 +122,10 @@ public static function getValueFromArray($key, $array) } } - public static function lookup($entityManager, $connection, $currentRule, $docId, $myddlewareUserId, $sourceFieldName, $fieldValue, $rule, $errorIfEmpty=false, $errodIfNoFound=true, $parent=false) + public static function lookup($entityManager, $connection, $currentRule, $docId, $myddlewareUserId, $sourceFieldName, $field, $rule, $errorIfEmpty=false, $errodIfNoFound=true, $parent=false) { // Manage error if empty - if (empty($fieldValue)) { + if (empty($field)) { if ($errorIfEmpty) { throw new \Exception('The field '.$sourceFieldName.' is empty. Failed to find the relate value. '); } else { @@ -203,7 +203,7 @@ public static function lookup($entityManager, $connection, $currentRule, $docId, // Get the record id $stmt = $connection->prepare($sqlParams); $stmt->bindValue(':ruleRelateId', $rule); - $stmt->bindValue(':record_id', $fieldValue); + $stmt->bindValue(':record_id', $field); $result = $stmt->executeQuery(); $result = $result->fetchAssociative(); // Manage error if no result found diff --git a/translations/messages.en.yml b/translations/messages.en.yml index 4bcce1d04..58ad6d913 100644 --- a/translations/messages.en.yml +++ b/translations/messages.en.yml @@ -491,6 +491,7 @@ function: htmlentities: htmlentities ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $encoding = ini_get("default_charset") [, bool $double_encode = true ]]] ) / Convert all applicable characters to HTML entities htmlspecialchars: htmlspecialchars ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $encoding = ini_get("default_charset") [, bool $double_encode = true ]]] ) / Convert special characters to HTML entities chr: chr ( int $bytevalue ) Generate a single-byte string from a number + lookup: lookup ( string $field, string $rule, errorIfEmpty (default = false), errodIfNoFound (default = true)). Search the corresponding value of the $field in the rule $rule. mathematical: round: Rounding a floating point number ceil: Rounding up diff --git a/translations/messages.fr.yml b/translations/messages.fr.yml index 914c8cebb..6bd61b2b6 100644 --- a/translations/messages.fr.yml +++ b/translations/messages.fr.yml @@ -489,6 +489,7 @@ function: htmlentities: htmlentities ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $encoding = ini_get("default_charset") [, bool $double_encode = true ]]] ) / Convertit tous les caractères éligibles en entités HTML htmlspecialchars: htmlspecialchars ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $encoding = ini_get("default_charset") [, bool $double_encode = true ]]] ) / Convertit les caractères spéciaux en entités HTML chr: chr ( int $bytevalue ) Générer une chaîne d'un octet à partir d'un nombre + lookup: lookup ( string $field, string $rule, errorIfEmpty (default = false), errodIfNoFound (default = true)). Cherche la correspondance du champ $field dans la règle $rule. mathematical: round: Arrondit un nombre à virgule flottante ceil: Arrondit au nombre supérieur From 592bc2349690d8ed0a5dfbd7321c844cc060970a Mon Sep 17 00:00:00 2001 From: myddleware Date: Thu, 18 Apr 2024 16:42:13 +0200 Subject: [PATCH 034/452] PostgreSQL : add views in the module list Signed-off-by: myddleware --- src/Solutions/postgresql.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Solutions/postgresql.php b/src/Solutions/postgresql.php index 29256c9b7..37b49cbac 100644 --- a/src/Solutions/postgresql.php +++ b/src/Solutions/postgresql.php @@ -43,10 +43,17 @@ protected function generatePdo(): \PDO // Generate query protected function get_query_show_tables(): string { - return "SELECT * + // Read tables and views + return "SELECT schemaname, tablename FROM pg_catalog.pg_tables WHERE schemaname != 'pg_catalog' - AND schemaname != 'information_schema'"; + AND schemaname != 'information_schema' + UNION + SELECT schemaname, viewname tablename + FROM pg_catalog.pg_views + WHERE schemaname != 'pg_catalog' + AND schemaname != 'information_schema' + "; } // Get all tables from the database From 29a0d5cc48f7c5a2a9c48451f9c38ac17863bf67 Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 24 Apr 2024 22:43:23 +0200 Subject: [PATCH 035/452] Add workflow function : changeStatus Add workflow variables Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 30 +++++++++++++++++++++++++----- src/Manager/RuleManager.php | 17 ++++++++++------- src/Utils/workflowVariables.php | 5 ++++- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 06d5552d3..701f7acee 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -2134,11 +2134,14 @@ public function updateStatus($new_status) $this->message .= 'Status : '.$new_status; $this->status = $new_status; $this->afterStatusChange($new_status); - $this->createDocLog(); + // We don't clear the message because we could need it in the workflow, we clear it after the workflow execution + $this->createDocLog(false); // runWorkflow can't be executed if updateStatus is called from the solution class if ($new_status!='Send') { $this->runWorkflow(); } + $this->message = ''; + $this->docIdRefError = ''; } catch (\Exception $e) { $this->message .= 'Error status update : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->typeError = 'E'; @@ -2595,7 +2598,22 @@ public function runWorkflow() { $this->tools->sendMessage($arguments['to'],$arguments['subject'],$arguments['message']); } catch (\Exception $e) { $workflowStatus = 'Error'; - $error = 'Failed to create workflow log : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; + $error = 'Failed to send notification : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; + $this->logger->error($error); + $this->generateDocLog('E',$error); + } + $this->createWorkflowLog($action, $workflowStatus, $error); + break; + case 'updateStatus': + try { + $workflowStatus = 'Success'; + $error = ''; + $this->typeError = 'W'; + $this->message = 'Status change using workflow. '; + $this->updateStatus($arguments['status']); + } catch (\Exception $e) { + $workflowStatus = 'Error'; + $error = 'Failed change status : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->logger->error($error); $this->generateDocLog('E',$error); } @@ -2712,7 +2730,7 @@ protected function createWorkflowLog($action, $status, $error=null, $generateDoc * $message contient le message de l'erreur avec potentiellement des variable &1, &2... * $data contient les varables du message de type array('id_contact', 'nom_contact') */ - protected function createDocLog() + protected function createDocLog($clearMessage=true) { try { $now = gmdate('Y-m-d H:i:s'); @@ -2726,8 +2744,10 @@ protected function createDocLog() $stmt->bindValue(':ref_doc_id', $this->docIdRefError); $stmt->bindValue(':job_id', $this->jobId); $result = $stmt->executeQuery(); - $this->message = ''; - $this->docIdRefError = ''; + if ($clearMessage) { + $this->message = ''; + $this->docIdRefError = ''; + } } catch (\Exception $e) { $this->logger->error('Failed to create log : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); } diff --git a/src/Manager/RuleManager.php b/src/Manager/RuleManager.php index 3b22e52cf..18f3ed5e8 100644 --- a/src/Manager/RuleManager.php +++ b/src/Manager/RuleManager.php @@ -1551,6 +1551,7 @@ protected function sendTarget($type, $documentId = null): array throw new \Exception('Failed to connect to the target application.'); } } + // Run workflow after send if ( !empty($response) @@ -1558,13 +1559,15 @@ protected function sendTarget($type, $documentId = null): array AND !empty($this->ruleWorkflows) ) { foreach($response as $docId => $value) { - $param['id_doc_myddleware'] = $docId; - $param['jobId'] = $this->jobId; - $param['api'] = $this->api; - $param['ruleWorkflows'] = $this->ruleWorkflows; - // Set the param values and clear all document attributes - $this->documentManager->setParam($param, true); - $this->documentManager->runWorkflow(); + if (!empty($value)) { + $param['id_doc_myddleware'] = $docId; + $param['jobId'] = $this->jobId; + $param['api'] = $this->api; + $param['ruleWorkflows'] = $this->ruleWorkflows; + // Set the param values and clear all document attributes + $this->documentManager->setParam($param, true); + $this->documentManager->runWorkflow(); + } } } } catch (\Exception $e) { diff --git a/src/Utils/workflowVariables.php b/src/Utils/workflowVariables.php index 557a8182c..bcef0e6c8 100644 --- a/src/Utils/workflowVariables.php +++ b/src/Utils/workflowVariables.php @@ -1,3 +1,6 @@ status; - +$documentType = $this->documentType; +$attempt = $this->attempt; +$message = $this->message; +$typeError = $this->typeError; From ac6080c9eb8d420729f5eda49f5f6fe888c814c2 Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 24 Apr 2024 22:48:40 +0200 Subject: [PATCH 036/452] Add parameter for lookup function : createDocRelationship=true) Signed-off-by: myddleware --- src/Manager/FormulaFunctionManager.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Manager/FormulaFunctionManager.php b/src/Manager/FormulaFunctionManager.php index 24293615d..e2420c54f 100644 --- a/src/Manager/FormulaFunctionManager.php +++ b/src/Manager/FormulaFunctionManager.php @@ -122,7 +122,7 @@ public static function getValueFromArray($key, $array) } } - public static function lookup($entityManager, $connection, $currentRule, $docId, $myddlewareUserId, $sourceFieldName, $field, $rule, $errorIfEmpty=false, $errodIfNoFound=true, $parent=false) + public static function lookup($entityManager, $connection, $currentRule, $docId, $myddlewareUserId, $sourceFieldName, $field, $rule, $errorIfEmpty=false, $errodIfNoFound=true, $createDocRelationship=true) { // Manage error if empty if (empty($field)) { @@ -223,7 +223,10 @@ public static function lookup($entityManager, $connection, $currentRule, $docId, $result['document_id'] = end($documentList); } // No doc id in case of simulation - if (!empty($docId)) { + if ( + !empty($docId) + AND $createDocRelationship + ) { // Add the relationship in the table document Relationship try { $documentRelationship = new DocumentRelationship(); From 3228ffe4e2bcdd085d31e19c7dbd77c96317d48f Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 24 Apr 2024 23:35:59 +0200 Subject: [PATCH 037/452] Formula : Manage dot into a variable Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 5 +++++ src/Manager/FormulaFunctionManager.php | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 701f7acee..7dbb10543 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -1582,6 +1582,11 @@ public function getTransformValue($source, $ruleField) // We skip my_value because it is a constante if ('my_value' != $ruleField['source_field_name']) { $fieldNameDyn = $ruleField['source_field_name']; // value : variable name + // Replace dot by a string because dot can't be into a variable in php, the formula can't work + if (strpos($ruleField['source_field_name'], '.') !== false) { + $fieldNameDyn = str_replace('.', '___dot___', $ruleField['source_field_name']); + $ruleField['formula'] = str_replace($ruleField['source_field_name'], $fieldNameDyn, $ruleField['formula']); + } $$fieldNameDyn = $source[$ruleField['source_field_name']]; // Dynamic variable (e.g $name = name) } } diff --git a/src/Manager/FormulaFunctionManager.php b/src/Manager/FormulaFunctionManager.php index e2420c54f..0ee262d2b 100644 --- a/src/Manager/FormulaFunctionManager.php +++ b/src/Manager/FormulaFunctionManager.php @@ -209,7 +209,7 @@ public static function lookup($entityManager, $connection, $currentRule, $docId, // Manage error if no result found if (empty($result['record_id'])) { if ($errodIfNoFound) { - throw new \Exception('Failed to retrieve a related document. No data for the field '.$sourceFieldName.'. There is not record with the ID '.('1' == $direction ? 'source' : 'target').' '.$fieldValue.' in the rule '.$ruleLink['name'].'. This document is queued. '); + throw new \Exception('Failed to retrieve a related document. No data for the field '.$sourceFieldName.'. There is not record with the ID '.('1' == $direction ? 'source' : 'target').' '.$field.' in the rule '.$ruleLink['name'].'. This document is queued. '); } else { return ''; } From a73552c8bb99996bd3b42bf6940a80c2a74271c7 Mon Sep 17 00:00:00 2001 From: myddleware Date: Fri, 26 Apr 2024 10:24:27 +0200 Subject: [PATCH 038/452] Workflow add source data in variable Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 143 +++++++++++++++++--------------- translations/messages.en.yml | 1 + translations/messages.fr.yml | 1 + 3 files changed, 80 insertions(+), 65 deletions(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 7dbb10543..70d5b1f4c 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -107,6 +107,7 @@ class documentcore 'Error_checking' => 'Error', 'Error_sending' => 'Error', 'Not_found' => 'Error', + 'Error_workflow' => 'Error', ]; private array $notSentFields = []; @@ -164,6 +165,7 @@ public static function lstStatus(): array 'Error_transformed' => 'flux.status.error_transformed', 'Error_checking' => 'flux.status.error_checking', 'Error_sending' => 'flux.status.error_sending', + 'Error_workflow' => 'flux.status.Error_workflow', ]; } @@ -2547,7 +2549,12 @@ public function runWorkflow() { try { // Check if at least on workflow exist for the rule if (!empty($this->ruleWorkflows)) { - // includ variables used in the formula + // Add all source data in variables + foreach($this->sourceData as $key => $value) { + $fieldName = 'source_'.$key; + $$fieldName = $value; + } + // include variables used in the formula include __DIR__.'/../Utils/workflowVariables.php'; if (file_exists( __DIR__.'/../Custom/Utils/workflowVariables.php')) { include __DIR__.'/../Custom/Utils/workflowVariables.php'; @@ -2561,80 +2568,86 @@ public function runWorkflow() { eval('$condition = '.$f.';'); // exec // Execute the action if the condition is met if ($condition == 1) { - // Execute all actions - if (!empty($ruleWorkflow['actions'])) { - // Call each actions - foreach($ruleWorkflow['actions'] as $action) { - // Check if the action has already been executed for the current document - // Only if attempt > 0, if it is the first attempt then the action has never been executed - if ($this->attempt > 0) { - // Search action for the current document - $workflowLogEntity = $this->entityManager->getRepository(WorkflowLog::class) - ->findOneBy([ - 'triggerDocument' => $this->id, - 'action' => $action['id'], - ] - ); - // If the current action has been found for the current document, we don't execute the current action - if ( - !empty($workflowLogEntity) - AND $workflowLogEntity->getStatus() == 'Success' - ) { - // GenerateDocument can be empty depending the action - if (!empty($workflowLogEntity->getGenerateDocument())) { - $this->docIdRefError = $workflowLogEntity->getGenerateDocument()->getId(); + try { + // Execute all actions + if (!empty($ruleWorkflow['actions'])) { + // Call each actions + foreach($ruleWorkflow['actions'] as $action) { + // Check if the action has already been executed for the current document + // Only if attempt > 0, if it is the first attempt then the action has never been executed + if ($this->attempt > 0) { + // Search action for the current document + $workflowLogEntity = $this->entityManager->getRepository(WorkflowLog::class) + ->findOneBy([ + 'triggerDocument' => $this->id, + 'action' => $action['id'], + ] + ); + // If the current action has been found for the current document, we don't execute the current action + if ( + !empty($workflowLogEntity) + AND $workflowLogEntity->getStatus() == 'Success' + ) { + // GenerateDocument can be empty depending the action + if (!empty($workflowLogEntity->getGenerateDocument())) { + $this->docIdRefError = $workflowLogEntity->getGenerateDocument()->getId(); + } + $this->generateDocLog('W','Action ' . $action['id'] . ' already executed for this document. '); + continue; } - $this->generateDocLog('W','Action ' . $action['id'] . ' already executed for this document. '); - continue; } - } - // Execute action depending of the function in the workflow - $arguments = unserialize($action['arguments']); - switch ($action['action']) { - case 'generateDocument': - $this->generateDocument($arguments['ruleId'],$this->sourceData[$arguments['searchValue']],$arguments['searchField'],$arguments['rerun'], $action); - break; - case 'sendNotification': - try { - $workflowStatus = 'Success'; - $error = ''; - // Method sendMessage throws an exception if it fails - $this->tools->sendMessage($arguments['to'],$arguments['subject'],$arguments['message']); - } catch (\Exception $e) { - $workflowStatus = 'Error'; - $error = 'Failed to send notification : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; - $this->logger->error($error); - $this->generateDocLog('E',$error); - } - $this->createWorkflowLog($action, $workflowStatus, $error); - break; - case 'updateStatus': - try { - $workflowStatus = 'Success'; - $error = ''; - $this->typeError = 'W'; - $this->message = 'Status change using workflow. '; - $this->updateStatus($arguments['status']); - } catch (\Exception $e) { - $workflowStatus = 'Error'; - $error = 'Failed change status : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; - $this->logger->error($error); - $this->generateDocLog('E',$error); - } - $this->createWorkflowLog($action, $workflowStatus, $error); - break; - default: - throw new \Exception('Function '.key($action).' unknown.'); + // Execute action depending of the function in the workflow + $arguments = unserialize($action['arguments']); + switch ($action['action']) { + case 'generateDocument': + $this->generateDocument($arguments['ruleId'],$this->sourceData[$arguments['searchValue']],$arguments['searchField'],$arguments['rerun'], $action); + break; + case 'sendNotification': + try { + $workflowStatus = 'Success'; + $error = ''; + // Method sendMessage throws an exception if it fails + $this->tools->sendMessage($arguments['to'],$arguments['subject'],$arguments['message']); + } catch (\Exception $e) { + $workflowStatus = 'Error'; + $error = 'Failed to send notification : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; + $this->logger->error($error); + $this->generateDocLog('E',$error); + } + $this->createWorkflowLog($action, $workflowStatus, $error); + break; + case 'updateStatus': + try { + $workflowStatus = 'Success'; + $error = ''; + $this->typeError = 'W'; + $this->message = 'Status change using workflow. '; + $this->updateStatus($arguments['status']); + } catch (\Exception $e) { + $workflowStatus = 'Error'; + $error = 'Failed change status : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; + $this->logger->error($error); + $this->generateDocLog('E',$error); + } + $this->createWorkflowLog($action, $workflowStatus, $error); + break; + default: + throw new \Exception('Function '.key($action).' unknown.'); + } } } + } catch (\Exception $e) { + $this->logger->error('Failed to run the workflow '.$ruleWorkflow['name'].' : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + $this->generateDocLog('E','Failed to run the workflow '.$ruleWorkflow['name'].' : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + $this->updateStatus('Error_workflow'); } } } } } catch (\Exception $e) { - $this->logger->error('Failed to create workflow log : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); - $this->generateDocLog('E','Failed to create workflow log : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + $this->logger->error('Failed to run all workflows : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + $this->generateDocLog('E','Failed to run all workflows : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); } } diff --git a/translations/messages.en.yml b/translations/messages.en.yml index 58ad6d913..b95a27027 100644 --- a/translations/messages.en.yml +++ b/translations/messages.en.yml @@ -377,6 +377,7 @@ flux: error_checking: Checking error error_sending: Sending error not_found: Not found + Error_workflow: Workflow error type: update: Update create: Create diff --git a/translations/messages.fr.yml b/translations/messages.fr.yml index 6bd61b2b6..131252fe8 100644 --- a/translations/messages.fr.yml +++ b/translations/messages.fr.yml @@ -375,6 +375,7 @@ flux: error_checking: Vérification en erreur error_sending: Envoi en erreur Not_found: Non trouvée + Error_workflow: Workflow en erreur type: update: Update create: Create From c8e16c119d5121ff429f6076a7848710aed483d1 Mon Sep 17 00:00:00 2001 From: myddleware Date: Fri, 26 Apr 2024 11:32:34 +0200 Subject: [PATCH 039/452] Lookup : manage rule order with this new function Signed-off-by: myddleware --- src/Manager/JobManager.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Manager/JobManager.php b/src/Manager/JobManager.php index 0598812ab..1b57e3cea 100644 --- a/src/Manager/JobManager.php +++ b/src/Manager/JobManager.php @@ -865,8 +865,11 @@ public function orderRules(): bool // Si la règle n'a pas de relation on initialise l'ordre à 1 sinon on met 99 $sql = "SELECT rule.id, - GROUP_CONCAT(rulerelationship.field_id SEPARATOR ';') field_id + GROUP_CONCAT(rulerelationship.field_id SEPARATOR ';') field_id, + GROUP_CONCAT(rulefield.formula SEPARATOR ';') formula FROM rule + LEFT OUTER JOIN rulefield + ON rule.id = rulefield.rule_id LEFT OUTER JOIN rulerelationship ON rule.id = rulerelationship.rule_id WHERE @@ -877,6 +880,15 @@ public function orderRules(): bool $rules = $result->fetchAllAssociative(); if (!empty($rules)) { + // Add the rule in formula to the list of rules in relationship + foreach ($rules as $key => $rule) { + $matches = array(); + // Get the second parameters (rule id) of the lookup functions + preg_match_all('/lookup\(\{[^}]+\},"([^"]+)"/', $rule['formula'], $matches); + // Transform result and add it to the other rules (old relationships) + $rules[$key]['field_id'] .= ';'.implode(';', array_unique($matches[1])); + $rules[$key]['field_id'] = trim($rules[$key]['field_id'],';'); + } // Création d'un tableau en clé valeur et sauvegarde d'un tableau de référence $ruleKeyValue = []; foreach ($rules as $key => $rule) { @@ -922,7 +934,6 @@ public function orderRules(): bool } $rules = $rulesRef; } - // On vide la table RuleOrder $sql = 'DELETE FROM ruleorder'; $stmt = $this->connection->prepare($sql); @@ -943,7 +954,6 @@ public function orderRules(): bool $this->connection->rollBack(); // -- ROLLBACK TRANSACTION $this->message .= 'Failed to update table RuleOrder : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->logger->error($this->message); - return false; } From 1fe1774c3b166ae12c735712ad3b17f4a153a417 Mon Sep 17 00:00:00 2001 From: myddleware Date: Fri, 26 Apr 2024 11:59:58 +0200 Subject: [PATCH 040/452] Add target and history fields Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 70d5b1f4c..6b2e3afe9 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -2549,6 +2549,8 @@ public function runWorkflow() { try { // Check if at least on workflow exist for the rule if (!empty($this->ruleWorkflows)) { + $targetFields = false; + $historyFields = false; // Add all source data in variables foreach($this->sourceData as $key => $value) { $fieldName = 'source_'.$key; @@ -2561,6 +2563,37 @@ public function runWorkflow() { } // Execute every workflow of the rule foreach ($this->ruleWorkflows as $ruleWorkflow) { + // Add target fields if requested and if not already calculated + if ( + strpos($ruleWorkflow['condition'], 'target_') !== false + AND !$targetFields + ) { + $targetFields = true; + $target = $this->getDocumentData('T'); + // Add all source data in variables + if (!empty($target)) { + foreach($target as $key => $value) { + $fieldName = 'target_'.$key; + $$fieldName = $value; + } + } + } + // Add history fields if requested and if not already calculated + if ( + strpos($ruleWorkflow['condition'], 'history_') !== false + AND !$historyFields + ) { + $historyFields = true; + $history = $this->getDocumentData('H'); + // Add all source data in variables + if (!empty($history)) { + foreach($history as $key => $value) { + $fieldName = 'history_'.$key; + $$fieldName = $value; + } + } + } + // Check the condition $this->formulaManager->init($ruleWorkflow['condition']); // mise en place de la règle dans la classe $this->formulaManager->generateFormule(); // Genère la nouvelle formule à la forme PhP From 41dcf647ba5560911adde075f74fdf70d1201632 Mon Sep 17 00:00:00 2001 From: myddleware Date: Fri, 3 May 2024 11:21:54 +0200 Subject: [PATCH 041/452] Workflow : error management Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 6b2e3afe9..d87b9d352 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -2637,32 +2637,18 @@ public function runWorkflow() { $this->generateDocument($arguments['ruleId'],$this->sourceData[$arguments['searchValue']],$arguments['searchField'],$arguments['rerun'], $action); break; case 'sendNotification': - try { - $workflowStatus = 'Success'; - $error = ''; - // Method sendMessage throws an exception if it fails - $this->tools->sendMessage($arguments['to'],$arguments['subject'],$arguments['message']); - } catch (\Exception $e) { - $workflowStatus = 'Error'; - $error = 'Failed to send notification : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; - $this->logger->error($error); - $this->generateDocLog('E',$error); - } + $workflowStatus = 'Success'; + $error = ''; + // Method sendMessage throws an exception if it fails + $this->tools->sendMessage($arguments['to'],$arguments['subject'],$arguments['message']); $this->createWorkflowLog($action, $workflowStatus, $error); break; case 'updateStatus': - try { - $workflowStatus = 'Success'; - $error = ''; - $this->typeError = 'W'; - $this->message = 'Status change using workflow. '; - $this->updateStatus($arguments['status']); - } catch (\Exception $e) { - $workflowStatus = 'Error'; - $error = 'Failed change status : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; - $this->logger->error($error); - $this->generateDocLog('E',$error); - } + $workflowStatus = 'Success'; + $error = ''; + $this->typeError = 'W'; + $this->message = 'Status change using workflow. '; + $this->updateStatus($arguments['status']); $this->createWorkflowLog($action, $workflowStatus, $error); break; default: From 88c598d73f3ccff6b2adbd55d67a38424242ccf8 Mon Sep 17 00:00:00 2001 From: myddleware Date: Fri, 3 May 2024 15:08:44 +0200 Subject: [PATCH 042/452] Workflow : fix bug in case sourceData is empty Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index d87b9d352..7a87dd51b 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -2551,6 +2551,10 @@ public function runWorkflow() { if (!empty($this->ruleWorkflows)) { $targetFields = false; $historyFields = false; + // Can be empty depending on the context of the workflow call + if (empty($this->sourceData)) { + $this->sourceData = $this->getDocumentData('S'); + } // Add all source data in variables foreach($this->sourceData as $key => $value) { $fieldName = 'source_'.$key; From 93fec8b6b9125c38f930641676fe37c4e91c8b62 Mon Sep 17 00:00:00 2001 From: myddleware Date: Fri, 3 May 2024 15:50:00 +0200 Subject: [PATCH 043/452] Formula : remove strip_tags Signed-off-by: myddleware --- src/Manager/FormulaManager.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Manager/FormulaManager.php b/src/Manager/FormulaManager.php index ed7ddfc7f..a906fddab 100644 --- a/src/Manager/FormulaManager.php +++ b/src/Manager/FormulaManager.php @@ -270,7 +270,6 @@ private function secureFormule() // ----------- secure $string = trim($string); - $string = strip_tags($string); // ----- remove control characters ----- $string = str_replace("\r", '', $string); // --- replace with empty space From b4f7c92db7768fe1dd411fa634d6275a80940a28 Mon Sep 17 00:00:00 2001 From: myddleware Date: Tue, 7 May 2024 09:59:49 +0200 Subject: [PATCH 044/452] Bug fix : secure mdw_cancel_document search Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 7a87dd51b..05f2a5749 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -953,7 +953,7 @@ public function transformDocument(): bool $transformed = $this->updateTargetTable(); if (!empty($transformed)) { // If the value mdw_cancel_document is found in the target data of the document after transformation we cancel the document - if (array_search('mdw_cancel_document',$transformed) !== false) { + if (array_search('mdw_cancel_document',$transformed, true) !== false) { $this->message .= 'The document contains the value mdw_cancel_document. '; $this->typeError = 'W'; $this->updateStatus('Cancel'); From a3fcfb0c38072935ea9ea016bd0942bcc83f0d82 Mon Sep 17 00:00:00 2001 From: myddleware Date: Fri, 10 May 2024 13:25:02 +0200 Subject: [PATCH 045/452] Bugfix : when too much doc related, the id of the document found was truncated Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 05f2a5749..d3aaff931 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -2395,7 +2395,7 @@ protected function getTargetId($ruleRelationship, $record_id) if ('-1' == $direction) { $sqlParams = " SELECT source_id record_id, - GROUP_CONCAT(DISTINCT document.id) document_id, + GROUP_CONCAT(DISTINCT document.id ORDER BY document.source_date_modified DESC) document_id, GROUP_CONCAT(DISTINCT document.type) types FROM document WHERE @@ -2413,7 +2413,7 @@ protected function getTargetId($ruleRelationship, $record_id) } elseif ('1' == $direction) { $sqlParams = " SELECT target_id record_id, - GROUP_CONCAT(DISTINCT document.id) document_id, + GROUP_CONCAT(DISTINCT document.id ORDER BY document.source_date_modified DESC) document_id, GROUP_CONCAT(DISTINCT document.type) types FROM document WHERE @@ -2475,13 +2475,13 @@ protected function getTargetId($ruleRelationship, $record_id) $result = $stmt->executeQuery(); $result = $result->fetchAssociative(); - // In cas of several document found we get only the last one + // In cas of several document found we get only the first one (which is the most recent one) if ( !empty($result['document_id']) AND strpos($result['document_id'], ',') ) { $documentList = explode(',',$result['document_id']); - $result['document_id'] = end($documentList); + $result['document_id'] = $documentList[0]; } } if (!empty($result['record_id'])) { From baa6c95ae55e528026f3b4cd05e9085713901a69 Mon Sep 17 00:00:00 2001 From: myddleware Date: Tue, 14 May 2024 01:15:28 +0200 Subject: [PATCH 046/452] Salesforce : manage empty relationship Signed-off-by: myddleware --- src/Solutions/salesforce.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Solutions/salesforce.php b/src/Solutions/salesforce.php index 1217699fc..eafa55a1b 100644 --- a/src/Solutions/salesforce.php +++ b/src/Solutions/salesforce.php @@ -394,7 +394,6 @@ public function readData($param): array $query = $baseQuery.$querySelect.$queryFrom.$queryWhere.$queryOrder.$queryLimit.$queryOffset; $query_request_data = $this->call($query, false); $query_request_data = $this->formatResponse($param,$query_request_data); - // Affectation du nombre de résultat à $result['count'] if (isset($query_request_data['totalSize'])){ $currentCount = $query_request_data['totalSize']; @@ -414,7 +413,10 @@ public function readData($param): array // Don't save attributes if($fieldKey != 'attributes'){ // In case there are 2 levels of relationship (think about a recursive function here) - if(substr($fieldKey,-3) == '__r') { + if( + substr($fieldKey,-3) == '__r' + AND !empty($fieldValue) + ) { foreach($fieldValue as $fieldKeyLevel2 => $fieldValueLevel2) { if($fieldKeyLevel2 != 'attributes'){ $row[mb_strtolower($fieldKeyLevel2)] = $fieldValueLevel2; From cfa8fc5e6574cd1730831b505580707cdcb50daa Mon Sep 17 00:00:00 2001 From: myddleware Date: Tue, 14 May 2024 02:18:19 +0200 Subject: [PATCH 047/452] Salesforce : Manage 3rd level of relationship Signed-off-by: myddleware --- src/Solutions/salesforce.php | 45 +++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/src/Solutions/salesforce.php b/src/Solutions/salesforce.php index eafa55a1b..01750ebe0 100644 --- a/src/Solutions/salesforce.php +++ b/src/Solutions/salesforce.php @@ -413,14 +413,39 @@ public function readData($param): array // Don't save attributes if($fieldKey != 'attributes'){ // In case there are 2 levels of relationship (think about a recursive function here) - if( - substr($fieldKey,-3) == '__r' - AND !empty($fieldValue) - ) { - foreach($fieldValue as $fieldKeyLevel2 => $fieldValueLevel2) { - if($fieldKeyLevel2 != 'attributes'){ - $row[mb_strtolower($fieldKeyLevel2)] = $fieldValueLevel2; - $row[$param['module'].'.'.$key.'.'.$fieldKey.'.'.$fieldKeyLevel2] = $fieldValueLevel2; + if(substr($fieldKey,-3) == '__r') { + if (!empty($fieldValue)) { + foreach($fieldValue as $fieldKeyLevel2 => $fieldValueLevel2) { + if($fieldKeyLevel2 != 'attributes'){ + // In case there are 3 levels of relationship (think about a recursive function here) + if(substr($fieldKeyLevel2,-3) == '__r') { + if(!empty($fieldValueLevel2)) { + foreach($fieldValueLevel2 as $fieldKeyLevel3 => $fieldValueLevel3) { + if($fieldKeyLevel3 != 'attributes'){ + $row[mb_strtolower($fieldKeyLevel3)] = $fieldValueLevel3; + $row[$param['module'].'.'.$key.'.'.$fieldKey.'.'.$fieldKeyLevel2.'.'.$fieldKeyLevel3] = $fieldValueLevel3; + } + } + // If a relationship is empty, we set all field under this relationship to empty + } else { + foreach($param['fields'] as $field) { + if (str_starts_with($field, $param['module'].'.'.$key.'.'.$fieldKey.'.'.$fieldKeyLevel2)) { + $row[$field] = ''; + } + } + } + } + else { + $row[$param['module'].'.'.$key.'.'.$fieldKey.'.'.$fieldKeyLevel2] = $fieldValueLevel2; + } + } + } + // If a relationship is empty, we set all field under this relationship to empty + } else { + foreach($param['fields'] as $field) { + if (str_starts_with($field, $param['module'].'.'.$key.'.'.$fieldKey)) { + $row[$field] = ''; + } } } } @@ -434,6 +459,10 @@ public function readData($param): array elseif(!($key == 'attributes')){ if($key == 'Id') $row[mb_strtolower($key)] = $record[$key]; + // If Id is requested in the field mapping + if (!empty($param['fields']['Id'])) { + $row[$key] = $record[$key]; + } else { if($key == 'CreatedDate') { $record[$key] = $this->dateTimeToMyddleware($record[$key]); From 79d8cea5bfb69268a4a3266773b84f3796e17740 Mon Sep 17 00:00:00 2001 From: myddleware Date: Tue, 14 May 2024 02:20:36 +0200 Subject: [PATCH 048/452] Manage dot in field for formula Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index d3aaff931..692e05596 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -1572,6 +1572,11 @@ public function getTransformValue($source, $ruleField) // We skip my_value because it is a constant if ('my_value' != $listFields) { $fieldNameDyn = $listFields; // value : variable name + // Replace dot by a string because dot can't be into a variable in php, the formula can't work + if (strpos($ruleField['source_field_name'], '.') !== false) { + $fieldNameDyn = str_replace('.', '___dot___', $listFields); + $ruleField['formula'] = str_replace($listFields, $fieldNameDyn, $ruleField['formula']); + } if (array_key_exists($listFields, $source)) { $$fieldNameDyn = (!empty($source[$listFields]) ? $source[$listFields] : ''); // Dynamic variable (e.g $name = name) } else { From 9e4d983ac661ed8748798fb1d4bc17f1fed9bdf8 Mon Sep 17 00:00:00 2001 From: AlexMyddleware <106162060+AlexMyddleware@users.noreply.github.com> Date: Tue, 14 May 2024 07:10:27 +0200 Subject: [PATCH 049/452] feat: https://github.com/Myddleware/myddleware/issues/1113 (#1125) * feat: issue 1097 add an empty search function and change the doc list button address * fix issue 1113 https://github.com/Myddleware/myddleware/issues/1113 * feat: change label to search instead of Save * feat: add php function to remove one filter 1067 https://github.com/Myddleware/myddleware/issues/1067 * feat: function js to send a request to the server, clear the form and the local storage 1067 https://github.com/Myddleware/myddleware/issues/1067 * feat: add the twig variable of the function path for ajax request 1067 https://github.com/Myddleware/myddleware/issues/1067 * style: comment console.logs * issue 1114: remove jobscheduler from main menu https://github.com/Myddleware/myddleware/issues/1114 * 1082 add the result to the function https://github.com/Myddleware/myddleware/issues/1082 * 1082 add the twig results with the dynamic show hide * also get the same for the tasks * translation for the crontab * 1133 use fetch all associative https://github.com/Myddleware/myddleware/issues/1133 --- assets/js/crontab.js | 4 +- assets/js/regle.js | 81 +++++++++++++++---- src/Controller/FilterController.php | 53 ++++++++++++ src/Controller/JobSchedulerController.php | 6 +- src/DataFixtures/LoadCronJobData.php | 9 +++ src/Form/Filter/CombinedFilterType.php | 1 + templates/JobScheduler/crontab_list.html.twig | 43 ++++++++++ templates/Task/list.html.twig | 25 ++++-- templates/base.html.twig | 11 +-- templates/testFilter.html.twig | 3 +- translations/messages.en.yml | 1 + translations/messages.fr.yml | 1 + 12 files changed, 201 insertions(+), 37 deletions(-) diff --git a/assets/js/crontab.js b/assets/js/crontab.js index 4d29dc2a4..d6733f89b 100644 --- a/assets/js/crontab.js +++ b/assets/js/crontab.js @@ -1,7 +1,7 @@ // Creates a function that will be called when a button with the class crontab_class // is clicked. The function will send a request to the server for the function with the following name crontab_show -console.log('crontabbou.js'); +// console.log('crontabbou.js'); // if an element with the class table-head-result is clicked, then call the function sortTable with the id of the element as an argument $(document).on('click', '.table-head-result', function() { @@ -11,7 +11,7 @@ $(document).on('click', '.table-head-result', function() { sortTable(id); }); -console.log('coucou camille'); +// console.log('coucou camille'); function sortTable(n) { console.log('inside function sortTable'); var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0; diff --git a/assets/js/regle.js b/assets/js/regle.js index b87a7df9d..ef6998f83 100755 --- a/assets/js/regle.js +++ b/assets/js/regle.js @@ -306,7 +306,7 @@ $(function () { var open = pairTypes[t]; var close = map[open]; - console.log('Checking pair type:', open+close); + // console.log('Checking pair type:', open+close); for (var i = 0; i < formula.length; i++) { if (formula[i] === open) { @@ -316,18 +316,18 @@ $(function () { 'position': i, 'pairNum': currentPair }); - console.log('Found opening symbol at', i, 'Pair Number:', currentPair); + // console.log('Found opening symbol at', i, 'Pair Number:', currentPair); } else if (formula[i] === close) { var last = stack.pop(); if (!last) { - console.log('Found closing symbol without matching opening symbol at', i); + // console.log('Found closing symbol without matching opening symbol at', i); errorAt = i; currentPair++; break; } else { - console.log('Found matching closing symbol for pair', last.pairNum, 'at', i); + // console.log('Found matching closing symbol for pair', last.pairNum, 'at', i); if (!pairs.includes(last.pairNum)) { pairs.push(last.pairNum); } @@ -337,7 +337,7 @@ $(function () { // If we still have unclosed brackets at the end of parsing, record an error if (stack.length > 0) { - console.log('Found unbalanced pair at the end of the formula'); + // console.log('Found unbalanced pair at the end of the formula'); var lastUnbalanced = stack.pop(); errorAt = lastUnbalanced.position; currentPair = lastUnbalanced.pairNum; @@ -351,11 +351,11 @@ $(function () { pairs.splice(index, 1); } - console.log('Pair Type:', open + close); - console.log('Status:', status); - console.log('Error Position:', errorAt); - console.log('Unbalanced Pair:', unbalancedPair); - console.log('Balanced Pairs:', pairs); + // console.log('Pair Type:', open + close); + // console.log('Status:', status); + // console.log('Error Position:', errorAt); + // console.log('Unbalanced Pair:', unbalancedPair); + // console.log('Balanced Pairs:', pairs); if (!status) { return { @@ -391,7 +391,7 @@ $(function () { }); - console.log('these are the values', values); + // console.log('these are the values', values); var bracketError = false; var emptyBracketError = false; @@ -505,7 +505,7 @@ $(function () { // empty the values array missingFieldList = []; values = []; - console.log('these are the values', values); + // console.log('these are the values', values); $.ajax({ @@ -824,13 +824,13 @@ $(function () { if ($(this).attr('disabled') != 'disabled') { if ($(this).is(":checked")) { if (remove) { - id = $(this).parent().parent().attr('data-id'); + id = $(this).attr('name'); massAddFlux(id, true, massFluxTab); $(this).prop("checked", false); } } else { if (remove == false) { - id = $(this).parent().parent().attr('data-id'); + id = $(this).attr('name'); massAddFlux(id, false, massFluxTab); $(this).prop("checked", true); } @@ -842,7 +842,6 @@ $(function () { }); $('input', '.listepagerflux td').on('change', function () { - // id = $(this).parent().parent().attr('data-id'); if ($(this).is(":checked")) { massAddFlux($(this).attr('name'), false, massFluxTab); } else { @@ -1977,6 +1976,58 @@ function massAddFlux(id, cond, massFluxTab) { $('#cancelreloadflux').find('span').html(massFluxTab.length); } +$(document).ready(function() { + $('.removeFilters').click(function() { + // Get the class list of the clicked element + var classList = $(this).attr('class').split(/\s+/); + // console.log(classList); + + // Find the filter class (it's the last class in the list) + var filterClass = classList[classList.length - 1]; + // console.log(filterClass); + + // Get the stored filters from local storage + var storedFilters = JSON.parse(localStorage.getItem('storedFilters')); + // console.log(storedFilters); + + // Remove the filter from the stored filters + delete storedFilters[filterClass]; + // console.log(storedFilters); + + // Save the updated filters back to local storage + localStorage.setItem('storedFilters', JSON.stringify(storedFilters)); + // console.log(localStorage.getItem('storedFilters')); + + // Make an AJAX request to the server to remove the filter from the session + $.ajax({ + url: path_remove_filter, + method: 'POST', + data: { filterName: 'FluxFilter' + toCamelCase(filterClass) }, + success: function(response) { + if (response.status === 'success') { + // console.log('Filter successfully removed Argonien'); + + // Clear the form field + var formFieldName = 'combined_filter[document][' + filterClass + ']'; + $('input[name="' + formFieldName + '"]').val(''); + // console.log('Filter input cleared'); + } else { + // console.log('Error removing filter: ' + response.message); + } + } + }); + }); +}); + +function toCamelCase(str) { + // Split the string into words + var words = str.split('_'); + + // Convert each word to title case (except for the first one), and join them back together + return words[0] + words.slice(1).map(function(word) { + return word.charAt(0).toUpperCase() + word.slice(1); + }).join(''); +} // Save the modified field data by using an ajax request function saveInputFlux(div, link) { diff --git a/src/Controller/FilterController.php b/src/Controller/FilterController.php index afccfab1c..914d81b48 100644 --- a/src/Controller/FilterController.php +++ b/src/Controller/FilterController.php @@ -50,6 +50,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\HttpFoundation\JsonResponse; use Pagerfanta\Exception\NotValidCurrentPageException; use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -127,6 +128,58 @@ public function __construct( } } + /** + * @Route("/document/list/empty-search", name="document_empty_search") + */ +public function emptySearchAction(Request $request): Response +{ + $formFilter = $this->createForm(FilterType::class, null); + $form = $this->createForm(CombinedFilterType::class, null, [ + 'entityManager' => $this->entityManager, + ]); + + // set the timezone + $timezone = !empty($timezone) ? $this->getUser()->getTimezone() : 'UTC'; + + // Return an empty array so that there will be no documents to display + $documents = array(); + + // default pagination + $compact = $this->nav_pagination([ + 'adapter_em_repository' => $documents, + 'maxPerPage' => 25, + 'page' => 1, + ], false); + + return $this->render('testFilter.html.twig', [ + 'form' => $form->createView(), + 'formFilter'=> $formFilter->createView(), + 'documents' => $documents, + 'nb' => $compact['nb'], + 'entities' => $compact['entities'], + 'pager' => $compact['pager'], + 'condition' => 0, + 'timezone' => $timezone, + 'csvdocumentids' => '', + 'nbDocuments' => 0, + ]); +} + +/** + * @Route("/remove-filter", name="remove_filter", methods={"POST"}) + */ +public function removeFilter(Request $request): JsonResponse +{ + $filterName = $request->request->get('filterName'); + + if ($filterName) { + $this->sessionService->{'remove'.$filterName}(); + return new JsonResponse(['status' => 'success']); + } + + return new JsonResponse(['status' => 'error', 'message' => 'No filter name provided']); +} + // Function to disylay the documents with filters diff --git a/src/Controller/JobSchedulerController.php b/src/Controller/JobSchedulerController.php index 032c5a175..8bc87e298 100644 --- a/src/Controller/JobSchedulerController.php +++ b/src/Controller/JobSchedulerController.php @@ -322,10 +322,14 @@ public function crontabList(): Response $entity = $this->entityManager->getRepository(CronJob::class)->findAll(); + // Fetch the cron_job_result data + $cronJobResults = $this->entityManager->getRepository(CronJobResult::class)->findAll(); + return $this->render('JobScheduler/crontab_list.html.twig', [ 'entity' => $entity, 'timezone' => $timezone, - 'entitiesCron' => $entitiesCron + 'entitiesCron' => $entitiesCron, + 'cronJobResults' => $cronJobResults, ]); } diff --git a/src/DataFixtures/LoadCronJobData.php b/src/DataFixtures/LoadCronJobData.php index 41463464d..b72c65a11 100644 --- a/src/DataFixtures/LoadCronJobData.php +++ b/src/DataFixtures/LoadCronJobData.php @@ -64,6 +64,15 @@ private function generateEntities() } } + if (!$foundCronJob) { + $sql = "SELECT * FROM cron_job LIMIT 1"; + $stmt = $this->manager->getConnection()->executeQuery($sql); + $result = $stmt->fetchAllAssociative(); + if (!empty($result)) { + $foundCronJob = true; + } + } + // If we didn't found the solution we create a new one, otherwise we update it if (!$foundCronJob) { $crontab = CronJob::create($cronJobData['command'], $cronJobData['period']); diff --git a/src/Form/Filter/CombinedFilterType.php b/src/Form/Filter/CombinedFilterType.php index 12e57b716..58dda9d20 100644 --- a/src/Form/Filter/CombinedFilterType.php +++ b/src/Form/Filter/CombinedFilterType.php @@ -27,6 +27,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) ]); // add save button to builder $builder->add('save', SubmitType::class, [ + 'label' => 'Search', 'attr' => ['class' => 'btn btn-primary mb-2'], ]); } diff --git a/templates/JobScheduler/crontab_list.html.twig b/templates/JobScheduler/crontab_list.html.twig index bc7580b92..c95e10d8b 100644 --- a/templates/JobScheduler/crontab_list.html.twig +++ b/templates/JobScheduler/crontab_list.html.twig @@ -95,6 +95,49 @@
+ +
+
+
{{ 'crontab.title_cronjobresult'|trans }}
+ + + + + + + + + + + + + {% for result in cronJobResults %} + + + + + + + + + + + + {% endfor %} + +
{{ result.id }}{{ result.cronJob.id }}{{ result.runAt|date("d/m/Y H:i:s", timezone) }}{{ result.runTime }}{{ result.statusCode }} +
+ {{ result.output|slice(0, 100) ~ '...' }} + +
+ +
{{ result.output|slice(0, 300) ~ '...' }}{{ result.createdAt|date("d/m/Y H:i:s", timezone) }}{{ result.updatedAt|date("d/m/Y H:i:s", timezone) }}
+
+
+

{{'help.title'|trans}} diff --git a/templates/Task/list.html.twig b/templates/Task/list.html.twig index 86d4e35d5..d5fa150bd 100644 --- a/templates/Task/list.html.twig +++ b/templates/Task/list.html.twig @@ -67,24 +67,33 @@ {% else %} - {% endif %} + {% endif %} {{ task.param }} - + {% if task.status|lower == 'end' %} {{ task.status }} {% else %} {{ task.status }} {% endif %} - + {{ task.begin|date("d/m/Y H:i:s", timezone) }} {{ task.end|date("d/m/Y H:i:s", timezone) }} - {{ task.open }} + {{ task.open }} {{ task.close }} {{ task.cancel }} - {{ task.error }} - {{ task.message|sensor }} - - {% endfor %} + {{ task.error }} + +
+ {{ task.message|slice(0, 100) ~ '...' }} + +
+ + + + {% endfor %} {{'list_task.th.id'|trans}} {{'list_task.th.status'|trans}} diff --git a/templates/base.html.twig b/templates/base.html.twig index c684628a5..8398c07f8 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -66,15 +66,6 @@ {{'menu_user.management_smtp'|trans}} - -
  • - - - - - - {{'menu_user.jobscheduler'|trans}} -
  • @@ -160,7 +151,7 @@
  • {# ------------- LOGS ------------- #}
    + +
    + {% if workflowLogs is not empty %} + {% if nb_workflow_logs > 0 %} +
    +

    {{'view_flux.nbWorflowLogs'|trans}}

    +

    + {{ nb_workflow_logs }} +

    +
    + {% endif %} +
    + + + + + + + + + + + + + + + + + + {% for log in workflowLogs %} + + + + + + + + + + + + + + {% endfor %} + +
    IDWorkflowJobTrigger DocumentGenerate DocumentCreated ByStatusDate CreatedMessageAction NameAction Type
    {{ log.id }}{{ log.workflow.id }}{{ log.job.id }} {% if log.triggerDocument is not null %} + {{ log.triggerDocument.id }} + {% else %} + + {% endif %} {% if log.generateDocument is not null %} + {{ log.generateDocument.id }} + {% else %} + + {% endif %}{{ log.createdBy }} +
    {{ log.status }}
    +
    {{ log.dateCreated|date('Y-m-d H:i:s') }}{{ log.getMessage }}{{ log.action.name }}{{ log.action.action }}
    +
    + {% else %} +

    {{'view_flux.empty_workflow'|trans}}

    + {% endif %} +
    + + + + {# ------------- PARAMETRES JQUERY ------------- #} +{% endblock %} + +{% block cssin %}{% endblock cssin %} + diff --git a/templates/Workflow/new.html.twig b/templates/Workflow/new.html.twig new file mode 100644 index 000000000..f2f193906 --- /dev/null +++ b/templates/Workflow/new.html.twig @@ -0,0 +1,25 @@ +{% extends 'base.html.twig' %} +{% block title %}{{ parent() }} | {{ 'view_workflow.create'| trans}}{% endblock %} +{% block titlesm %}{{ 'view_workflow.create'| trans}}{% endblock titlesm %} +{% block body %} +
    +
    +
    + {{ form_start(form) }} + {{ form_row(form.name) }} + {{ form_row(form.description) }} + {{ form_row(form.Rule) }} + {{ form_row(form.condition) }} + {{ form_row(form.submit) }} + {{ form_end(form) }} + +
    +

    {{ 'view_workflow.title2'| trans}}

    + +

    {{'view_workflow.condition'| trans}}

    +

    {{ 'view_workflow.condition2'| trans}}

    +
    +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/templates/Workflow/show.html.twig b/templates/Workflow/show.html.twig new file mode 100644 index 000000000..ac1c758c8 --- /dev/null +++ b/templates/Workflow/show.html.twig @@ -0,0 +1,161 @@ +{% extends 'base.html.twig' %} + +{% block title %}{{ parent() }} | {{ 'view_workflow.title'|trans }}{% endblock %} +{% block titlesm %} + {{ 'view_workflow.title'|trans }} + + {{ workflow.name }} +{% endblock titlesm %} + +{% block body %} +
    + + +
    + {% for message in app.flashes('success') %} + + {% endfor %} + + {% if workflow is not null %} + + + + + + + + + + + + + + + + + +
    {{ 'view_workflow.description'|trans }}{{ 'view_workflow.rule'|trans }}{{ 'view_workflow.conditions'|trans }}{{ 'view_workflow.status'|trans }}
    {{ workflow.description }} + {{ workflow.rule.name }} + {{ workflow.condition }} + + {{ workflow.active ? 'Active' : 'Inactive' }} + +
    + +
    +

    {{ 'view_workflow.actions_list'|trans }}

    +
    +
    + +
    + + + + + + + + + + + + + {% for action in workflow.getWorkflowActions %} + + + + + + + + {% endfor %} + +
    {{ 'view_workflow.name'|trans }}{{ 'view_workflow.action'|trans }}{{ 'view_workflow.description'|trans }}{{ 'view_workflow.order'|trans }}{{ 'view_workflow.arguments'|trans }}
    {{ action.name }}{{ action.action }}{{ action.description }}{{ action.order }} + {% if action.arguments is iterable %} +
      + {% for key, argument in action.arguments %} +
    • {{ key }}: {{ argument }}
    • + {% endfor %} +
    + {% else %} + {{ action.arguments }} + {% endif %} +
    +
    + {% else %} +

    {{ 'list_workflow.empty'|trans }}

    + {% endif %} + + + {% if nb_workflow > 0 %} +
    +

    {{ 'view_workflow.logs'|trans }}

    + {{ nb_workflow }} +
    + {% endif %} +
    + + + + + + + + + + + + + + + + + + {% for log in workflowLogs %} + + + + + + + + + + + + + + {% endfor %} + +
    {{ 'view_workflow.log_id'|trans }}{{ 'view_workflow.workflow'|trans }}{{ 'view_workflow.job'|trans }}{{ 'view_workflow.trigger_document'|trans }}{{ 'view_workflow.generate_document'|trans }}{{ 'view_workflow.created_by'|trans }}{{ 'view_workflow.status'|trans }}{{ 'view_workflow.date_created'|trans }}{{ 'view_workflow.message'|trans }}{{ 'view_workflow.action_name'|trans }}{{ 'view_workflow.action_type'|trans }}
    {{ log.id }}{{ log.workflow.id }}{{ log.job.id }}{% if log.triggerDocument is not null %} + {{ log.triggerDocument.id }} + {% else %} + + {% endif %}{% if log.generateDocument is not null %} + {{ log.generateDocument.id }} + {% else %} + + {% endif %}{{ log.createdBy }} +
    {{ log.status }}
    +
    {{ log.dateCreated|date('Y-m-d H:i:s') }}{{ log.getMessage }}{{ log.action.name }}{{ log.action.action }}
    +
    +
    +
    + + {% block js %} + + + + + {% endblock js %} +{% endblock %} diff --git a/templates/WorkflowAction/edit.html.twig b/templates/WorkflowAction/edit.html.twig new file mode 100644 index 000000000..a53206d17 --- /dev/null +++ b/templates/WorkflowAction/edit.html.twig @@ -0,0 +1,28 @@ +{% extends 'base.html.twig' %} +{% block title %}{{ parent() }} | {{ 'view_workflow_action.edit'| trans}}{% endblock %} +{% block titlesm %}{{ 'view_workflow_action.edit'| trans}}{% endblock titlesm %} +{% block body %} +
    +
    +
    + {{ form_start(form) }} + {{ form_row(form.name) }} + {{ form_row(form.description) }} + {{ form_row(form.Workflow) }} + {{ form_row(form.action) }} + {{ form_row(form.Rule) }} + {{ form_row(form.status) }} + {{ form_row(form.to) }} + {{ form_row(form.subject) }} + {{ form_row(form.message) }} + {{ form_row(form.searchField) }} + {{ form_row(form.searchValue) }} + {{ form_row(form.rerun) }} + {{ form_row(form.order) }} + {{ form_row(form.active) }} + {{ form_row(form.submit) }} + {{ form_end(form) }} +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/templates/WorkflowAction/list.html.twig b/templates/WorkflowAction/list.html.twig new file mode 100644 index 000000000..f3ca6464d --- /dev/null +++ b/templates/WorkflowAction/list.html.twig @@ -0,0 +1,151 @@ +{#/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Myddleware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Myddleware. If not, see . +*********************************************************************************/ #} + +{% extends 'base.html.twig' %} +{% block title %}{{parent()}} | {{'title.workflow.list'|trans}}{% endblock %} +{% block titlesm %}{{'title.workflow.list'|trans}}{% endblock titlesm %} +{% block body %} +{% block js %} + + + + +{% endblock js %} +
    + + + +
    + {% for message in app.flashes('success') %} + + {% endfor %} + + + {% if nb_workflow > 0 %} +
    +

    {{'list_workflow.total'|trans}} :

    {{ nb_workflow }} +

    + + +
    + + {% if entities is not empty %} + + + + + + + + + + {% for workflow in entities %} + + + + + + + + + + + + + + + + + {% endfor %} + {% endif %} +
    {{'list_workflow.th.id'|trans}}{{'list_workflow.th.ruleName'|trans}}{{'list_workflow.th.name'|trans}}{{'list_workflow.th.description'|trans}}{{'list_workflow.th.workflowCondition'|trans}}{{'list_workflow.th.active'|trans}}{{'list_workflow.th.actions'|trans}}
    {{ workflow.id }} {{ workflow.rule.name }}{{ workflow.name }}{{ workflow.description }}{{ workflow.condition }} + + {{ workflow.active ? 'Active' : 'Inactive' }} + +
    + + +
    + {% if pager.haveToPaginate %} + {{ pagerfanta(pager, 'twitter_bootstrap4', {'routeName': 'task_list_page'}) }} + {% endif %} +
    + {% else %} +

    {{ 'list_workflow.empty'|trans }}

    + {% endif %} +
    +
    + + {# ------------- PARAMETRES JQUERY ------------- #} + + {# ------------- PARAMETRES JQUERY ------------- #} +{% endblock %} + +{% block cssin %}{% endblock cssin %} + diff --git a/templates/WorkflowAction/new.html.twig b/templates/WorkflowAction/new.html.twig new file mode 100644 index 000000000..bbbf0e550 --- /dev/null +++ b/templates/WorkflowAction/new.html.twig @@ -0,0 +1,28 @@ +{% extends 'base.html.twig' %} +{% block title %}{{ parent() }} | {{ 'view_workflow_action.create'| trans}}{% endblock %} +{% block titlesm %}{{ 'view_workflow_action.create'| trans}}{% endblock titlesm %} +{% block body %} +
    +
    +
    + {{ form_start(form) }} + {{ form_row(form.name) }} + {{ form_row(form.description) }} + {{ form_row(form.Workflow) }} + {{ form_row(form.action) }} + {{ form_row(form.Rule) }} + {{ form_row(form.status) }} + {{ form_row(form.to) }} + {{ form_row(form.subject) }} + {{ form_row(form.message) }} + {{ form_row(form.searchField) }} + {{ form_row(form.searchValue) }} + {{ form_row(form.rerun) }} + {{ form_row(form.order) }} + {{ form_row(form.active) }} + {{ form_row(form.submit) }} + {{ form_end(form) }} +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/templates/WorkflowAction/show.html.twig b/templates/WorkflowAction/show.html.twig new file mode 100644 index 000000000..c15ed0250 --- /dev/null +++ b/templates/WorkflowAction/show.html.twig @@ -0,0 +1,125 @@ +{% extends 'base.html.twig' %} + +{% block title %}{{ parent() }} | {{ 'view_workflow_action.title'|trans }}{% endblock %} +{% block titlesm %} + {{ 'view_workflow_action.title'|trans }} + + {{ workflow.name }} +{% endblock titlesm %} + +{% block body %} +
    + + +
    + {% for message in app.flashes('success') %} + + {% endfor %} + + {% if workflow is not null %} + + + + + + + + + + + + + + + + + + + + + +
    {{ 'view_workflow.description'|trans }}{{ 'view_workflow_action.workflow'|trans }}{{ 'view_workflow_action.action'|trans }}{{ 'view_workflow_action.order'|trans }}{{ 'view_workflow.arguments'|trans }}{{ 'view_workflow.status'|trans }}
    {{ workflow.description }} + {{ workflow.workflow.name }} + {{ workflow.action }}{{ workflow.order }} + {% if workflow.arguments is iterable %} +
      + {% for key, argument in workflow.arguments %} +
    • {{ key }}: {{ argument }}
    • + {% endfor %} +
    + {% else %} + {{ workflow.arguments }} + {% endif %} +
    + + {{ workflow.active ? 'Active' : 'Inactive' }} + +
    + {% endif %} + {% if nb_workflow > 0 %} +
    +

    {{ 'view_workflow.logs'|trans }}

    + {{ nb_workflow }} +
    + {% endif %} +
    +
    + + + + + + + + + + + + + + + + + {% for log in workflowLogs %} + + + + + + + + + + + + + {% endfor %} + +
    {{ 'view_workflow.log_id'|trans }}{{ 'view_workflow.workflow'|trans }}{{ 'view_workflow.job'|trans }}{{ 'view_workflow.trigger_document'|trans }}{{ 'view_workflow.generate_document'|trans }}{{ 'view_workflow.created_by'|trans }}{{ 'view_workflow.status'|trans }}{{ 'view_workflow.date_created'|trans }}{{ 'view_workflow.message'|trans }}{{ 'view_workflow.action_type'|trans }}
    {{ log.id }}{{ log.workflow.id }}{{ log.job.id }}{% if log.triggerDocument is not null %} + {{ log.triggerDocument.id }} + {% else %} + + {% endif %}{% if log.generateDocument is not null %} + {{ log.generateDocument.id }} + {% else %} + + {% endif %}{{ log.createdBy }} +
    {{ log.status }}
    +
    {{ log.dateCreated|date('Y-m-d H:i:s') }}{{ log.getMessage }}{{ log.action.action }}
    +
    +
    +
    +{% endblock %} + +{% block js %} + + + + +{% endblock js %} diff --git a/templates/base.html.twig b/templates/base.html.twig index 8398c07f8..9c677af72 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -122,6 +122,12 @@ {{'menu.rule.list'|trans}} +
  • + + + {{'menu.workflow.list'|trans}} + +
  • + + - - {% block js %} - - - - - {% endblock js %} +{% block title %} + {{ parent() }} + | + {{ 'view_workflow.title'|trans }} {% endblock %} + +{% block titlesm %} + {{ 'view_workflow.title'|trans }} + + {{ workflow.name }} +{% endblock titlesm %} + + +{% block body %} +
    + + + +
    + {% for message in app.flashes('success') %} + + {% endfor %} + + + {% if workflow is not null %} + +
    +
    + + {{ 'view_workflow.title'| trans | upper }} +
    +
    +
    +
    + {{ 'view_workflow.name'| trans | upper }} +
    {{ workflow.name }}
    +
    +
    + {{ 'view_workflow.create_by'| trans| upper }} +
    {{ workflow.createdBy.username }}
    +
    +
    +
    +
    + {{ 'view_workflow.rule'| trans | upper }} + +
    +
    + {{ 'view_workflow.status'| trans | upper }} +
    + +
    +
    +
    +
    +
    + {{ 'view_workflow.description'| trans | upper }} +
    {{ workflow.description }}
    +
    +
    +
    +
    + + {{ 'list_workflow.th.condition'| trans| upper }} +
    +
    +
    {{ workflow.condition }}
    +
    +
    +
    +
    + + {# LIST ACTION #} +
    +
    + + {{ 'view_workflow.actions_list'| trans | upper }} +
    + +
    + + + + + + + + + + + + + + {% for action in workflow.getWorkflowActions %} + + + + + + + + + {% endfor %} + +
    {{ 'view_workflow.name'|trans }}{{ 'view_workflow.action'|trans }}{{ 'view_workflow.description'|trans }}{{ 'view_workflow.order'|trans }}{{ 'view_workflow.arguments'|trans }}
    + {{ action.name }} + {{ action.action }}{{ action.description }}{{ action.order }} + {% if action.arguments is iterable %} +
      + {% for key, argument in action.arguments %} +
    • {{ key }}: + {{ argument }}
    • + {% endfor %} +
    + {% else %} + {{ action.arguments }} + {% endif %} +
    + + + + +
    +
    + {# LOG #} + + {% else %} +

    {{ 'list_workflow.empty'|trans }}

    + {% endif %} + + +
    +
    + + {{ 'view_workflow.logs'|trans | upper }} +
    + +
    + + + + + + + + + + + + + + + + + + {% for log in workflowLogs %} + + + + + + + + + + + + + + + + + {% endfor %} + +
    {{ 'view_workflow.log_id'|trans }}{{ 'view_workflow.workflow'|trans }}{{ 'view_workflow.job'|trans }}{{ 'view_workflow.trigger_document'|trans }}{{ 'view_workflow.generate_document'|trans }}{{ 'view_workflow.created_by'|trans }}{{ 'view_workflow.status'|trans }}{{ 'view_workflow.date_created'|trans }}{{ 'view_workflow.message'|trans }}{{ 'view_workflow.action_name'|trans }}{{ 'view_workflow.action_type'|trans }}
    {{ log.id }}
    + {{ log.workflow.id }} + + {{ log.job.id }} + + {% if log.triggerDocument is not null %} + {{ log.triggerDocument.id }} + {% else %} + + + {% endif %} + + {% if log.generateDocument is not null %} + {{ log.generateDocument.id }} + {% else %} + + + {% endif %} + {{ log.createdBy }} +
    {{ log.status }}
    +
    {{ log.dateCreated|date('Y-m-d H:i:s') }}{{ log.getMessage }} + {{ log.action.name }} + {{ log.action.action }}
    +
    +
    + + {% block js %} + + + + + {% endblock js %} + + {% endblock %} diff --git a/templates/WorkflowAction/new.html.twig b/templates/WorkflowAction/new.html.twig index 3aa0dcd49..b4a346353 100644 --- a/templates/WorkflowAction/new.html.twig +++ b/templates/WorkflowAction/new.html.twig @@ -2,27 +2,74 @@ {% block title %}{{ parent() }} | {{ 'view_workflow_action.create'| trans}}{% endblock %} {% block titlesm %}{{ 'view_workflow_action.create'| trans}}{% endblock titlesm %} {% block body %} -
    -
    -
    - {{ form_start(form) }} - {{ form_row(form.name) }} - {{ form_row(form.description) }} - {{ form_row(form.Workflow) }} - {{ form_row(form.action) }} - {{ form_row(form.rule) }} - {{ form_row(form.status) }} - {{ form_row(form.to) }} - {{ form_row(form.subject) }} - {{ form_row(form.message) }} - {{ form_row(form.searchField) }} - {{ form_row(form.searchValue) }} - {{ form_row(form.rerun) }} - {{ form_row(form.order) }} - {{ form_row(form.active) }} - {{ form_row(form.submit) }} - {{ form_end(form) }} +
    + {{ form_start(form) }} +
    +
    + {{ 'view_workflow.edit'|trans }}
    +
    +
    +
    + {{ form_row(form.name) }} +
    +
    + {{ form_row(form.Workflow) }} +
    +
    +
    +
    + {{ form_row(form.description) }} +
    +
    + {{ form_row(form.action) }} +
    +
    +
    +
    + {{ form_row(form.order) }} +
    +
    + {{ form_row(form.status) }} +
    +
    +
    +
    + {{ form_row(form.active) }} +
    +
    + {{ form_row(form.subject) }} +
    +
    +
    +
    + {{ form_row(form.message) }} +
    +
    + {{ form_row(form.searchField) }} +
    +
    +
    +
    + {{ form_row(form.searchValue) }} +
    +
    + {{ form_row(form.rerun) }} +
    +
    +
    +
    + {{ form_row(form.rule) }} +
    +
    + {{ form_row(form.to) }} +
    +
    +
    +
    +
    + {{ form_row(form.submit) }}
    + {{ form_end(form) }}
    -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/translations/messages.en.yml b/translations/messages.en.yml index 96a13ccc1..072113eae 100644 --- a/translations/messages.en.yml +++ b/translations/messages.en.yml @@ -480,6 +480,7 @@ list_workflow: active: Active actions: Actions order: Order + option: Options view_workflow: title: Workflow create: Create a new workflow @@ -510,6 +511,8 @@ view_workflow: message: Message action_name: Action Name action_type: Action Type + add_action: Add Action + create_by: Created By view_workflow_action: title: Workflow Action Details edit: Edit this Action diff --git a/translations/messages.fr.yml b/translations/messages.fr.yml index 552081cdb..b0e350ea1 100644 --- a/translations/messages.fr.yml +++ b/translations/messages.fr.yml @@ -477,6 +477,7 @@ list_workflow: active: Actif actions: Actions order: Ordre + option: Options view_workflow: title: Workflow create: Créer un workflow @@ -507,6 +508,8 @@ view_workflow: message: Message action_name: Nom de l'action action_type: Type d'action + add_action: Ajouter une action + create_by: Créé par view_workflow_action: title: Détail de l'action du workflow edit: Editer cette action From af5555d841dae4001c264b13f3a39156fb41de92 Mon Sep 17 00:00:00 2001 From: myddleware Date: Thu, 18 Jul 2024 17:26:58 +0200 Subject: [PATCH 112/452] Finish sendinblue Signed-off-by: myddleware --- src/Solutions/sendinblue.php | 94 ++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/Solutions/sendinblue.php b/src/Solutions/sendinblue.php index 149a7c4d7..0c2965841 100644 --- a/src/Solutions/sendinblue.php +++ b/src/Solutions/sendinblue.php @@ -186,8 +186,6 @@ public function get_module_fields($module, $type = 'source', $param = null): arr */ public function read($param) { -// echo '
    ';
    -// print_r($param);
             $result = [];
             // Function are differents depending on the type of record we read from Sendinblue
             switch ($param['module']) {
    @@ -287,11 +285,11 @@ public function read($param)
     					}
                     } while (!$exit);
                     break;
    +			// Manage contacts and data linked to the contacts
                 case 'contacts':
                 case 'contactSoftBounces':
                 case 'contactHardBounces':
                 case 'contactUnsubscriptions':
    -// echo '0
    '; $apiInstance = new \SendinBlue\Client\Api\ContactsApi(new \GuzzleHttp\Client(), $this->config); // Read with a specific id, email or phone if ( @@ -299,14 +297,12 @@ public function read($param) or !empty($param['query']['email']) or !empty($param['query']['SMS']) ) { -// echo '1
    '; // Search key for contact can be email, SMS or id if (!empty($param['query']['id'])) { if ($param['module'] == 'contacts') { $shearchKey = $param['query']['id']; - // In case of other modules, we extrat the contact id (first part) from the record id + // In case of other modules, we extract the contact id (first part) from the record id } else { -// echo '2
    '; $shearchKey = explode('_',$param['query']['id'])[0]; } } elseif ( @@ -321,8 +317,6 @@ public function read($param) // Get the info from contact, an exception is generated by getContactInfo if the contact isn't found try { // Search with first key -// print_r($shearchKey); -// echo '3
    '.$shearchKey.'
    '; $resultApi = $apiInstance->getContactInfo($shearchKey); } catch (\Exception $e) { // Search with second key if not found with first key @@ -358,16 +352,27 @@ public function read($param) if (!empty(current($resultApi))) { $records[] = current($resultApi); } + // Search all contact modified after the reference date } else { $dateRef = $this->dateTimeFromMyddleware($param['date_ref']); $modifiedSince = new \DateTime($dateRef); $resultApi = $apiInstance->getContacts($param['limit'], '0', $modifiedSince, 'asc'); $records = $resultApi->getContacts(); } -// echo '4
    '; -// print_r($records); + // In case we search data linked to teh contacts if ($param['module'] != 'contacts') { - $records = $this->getContactsStats($param, $records); + // Get the stats linked to the contact + $contactsStats = $this->getContactsStats($param, $records); + // Return records only if exist (contacts can have no record linked) + if (!empty($contactsStats['records'])) { + $records = $contactsStats['records']; + } else { + $records = array(); + } + // Return param, we could force the reference date + if (!empty($contactsStats['ruleParams'])) { + $result['ruleParams'] = $contactsStats['ruleParams']; + } } break; default: @@ -378,11 +383,8 @@ public function read($param) //Recover all contact from sendinblue if (!empty($records)) { $idField = $this->getIdName($param['module']); -// echo '5
    '; foreach ($records as $record) { -// print_r($record); foreach ($param['fields'] as $field) { -// echo '6
    '.$field.'
    '; if (!empty($record[$field])) { $result[$record[$idField]][$field] = $record[$field]; // Result attribute can be an object (example function getContacts()) @@ -400,38 +402,33 @@ public function read($param) } } } -// echo '10
    '; -// print_r($result); -// echo '
    '; -// return array(); return $result; } protected function getContactsStats($param, $records) { $compaignId = ''; + $newDateRef = ''; + $result = array(); if (!empty($records)) { $apiInstance = new \SendinBlue\Client\Api\ContactsApi(new \GuzzleHttp\Client(), $this->config); -// echo 'A
    '; -// print_r($records); + $dateRef = $this->dateTimeFromMyddleware($param['date_ref']); + // For each contact found in the first search foreach ($records as $record) { + // Get contact detail to retrive the statistics $recordDetail = $apiInstance->getContactInfo($record['id']); -// print_r($contactStat['campaignId']); -// echo 'B
    '; $stats = $recordDetail['statistics'][lcfirst(str_replace('contact','',$param['module']))]; if (!empty($recordDetail['statistics'][lcfirst(str_replace('contact','',$param['module']))])) { - // Get only the record requested using the campignId + // Get only the record requested using the campignId (only in case a specific record is requested) if (!empty($param['query']['id'])) { $compaignId = explode('_',$param['query']['id'])[2]; -// echo 'C
    '.$compaignId.'
    '; } + // For each statistics corresponding to the module foreach($recordDetail['statistics'][lcfirst(str_replace('contact','',$param['module']))] as $contactStat) { -// echo 'D
    '; $recordId = $record['id'].'_'.lcfirst(str_replace('contact','',$param['module'])).'_'.$contactStat['campaignId']; // Get the data when a specific record is requested if(!empty($compaignId)) { if($contactStat['campaignId'] == $compaignId) { -// echo 'F
    '; - $result[] = array( + $result['records'][] = array( 'id' => $recordId, 'contactId' => $record['id'], 'email' => $record['email'], @@ -439,19 +436,35 @@ protected function getContactsStats($param, $records) { 'campaignId' => $contactStat['campaignId'] ); break; - } else { - continue; - } + } // Get the data when search by date_ref } else { - // TO BE DEVELOPPED + if ($contactStat['eventTime'] > $dateRef) { + $result['records'][] = array( + 'id' => $recordId, + 'contactId' => $record['id'], + 'email' => $record['email'], + 'eventTime' => $contactStat['eventTime'], + 'campaignId' => $contactStat['campaignId'] + ); + } } - } } + // Save the max date ref from the contact list + if ( + empty($newDateRef) + OR $newDateRef < $record['modifiedAt'] + ) { + $newDateRef = $record['modifiedAt']; + } + } + // We force reference date using ruleParams to set the last contact read even if there is no statistics for these contact + // Because we don't want Myddleware to read again the same contacts (happens if no statitistic on the contacts read) + if (!empty($newDateRef)) { + $result['ruleParams'][] = array('name' => 'datereference', 'value' => $this->dateTimeToMyddleware($newDateRef)); } } -// print_r($result); return $result; } @@ -471,19 +484,6 @@ protected function getReferenceCall($param, $result) // Call parent function (calsse solution return parent::getReferenceCall($param, $result); } - -/* protected function getDateEnd($dateObj): string - { - $dateEndObj = clone $dateObj; - $dateEndObj->add(new \DateInterval('P30D')); - $dateNow = new \DateTime('NOW'); - // Date end can't be greater than today - if ($dateEndObj->format('Ymd') > $dateNow->format('Ymd')) { - $dateEndObj = $dateNow; - } - - return $dateEndObj->format('Y-m-d'); - } */ //fonction for get all your transactional email activity public function EmailTransactional($param): \SendinBlue\Client\Model\GetEmailEventReport @@ -617,7 +617,7 @@ protected function delete($param, $record) protected function dateTimeToMyddleware($dateTime) { $dto = new \DateTime($dateTime); - + $dto->setTimezone(new \DateTimeZone('UTC')); return $dto->format('Y-m-d H:i:s'); } From 1fc8780db8b1e21d4e15d1d63598cd4c0903d5e2 Mon Sep 17 00:00:00 2001 From: myddleware Date: Fri, 19 Jul 2024 00:18:38 +0200 Subject: [PATCH 113/452] Adapt contact reading to sort the result by modifiedAt Signed-off-by: myddleware --- src/Solutions/sendinblue.php | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/Solutions/sendinblue.php b/src/Solutions/sendinblue.php index 0c2965841..0282a582c 100644 --- a/src/Solutions/sendinblue.php +++ b/src/Solutions/sendinblue.php @@ -187,6 +187,8 @@ public function get_module_fields($module, $type = 'source', $param = null): arr public function read($param) { $result = []; + $offset = 0; + $records = []; // Function are differents depending on the type of record we read from Sendinblue switch ($param['module']) { case 'transactionalEmailActivity': @@ -196,12 +198,9 @@ public function read($param) } // As we build the id (it doesn't exist in Sendinblue), we add it to the param field $param['fields'][] = 'id'; - // ini call parameters $nbCall = 1; $limitCall = $this->limitEmailActivity; - $offset = 0; - $records = []; $dateStart = null; $dateEnd = null; $event = null; @@ -356,8 +355,24 @@ public function read($param) } else { $dateRef = $this->dateTimeFromMyddleware($param['date_ref']); $modifiedSince = new \DateTime($dateRef); - $resultApi = $apiInstance->getContacts($param['limit'], '0', $modifiedSince, 'asc'); - $records = $resultApi->getContacts(); + // Get all contacts modified since the date in parameter + do { + $recordsCall = []; + $resultApi = $apiInstance->getContacts($param['limit'], $offset, $modifiedSince, 'asc'); + $recordsCall = $resultApi->getContacts(); + if (!empty($recordsCall)) { + $records = array_merge($recordsCall, $records); + } + $offset += $param['limit']; + } while (!empty($recordsCall)); + // If several call, we sort by date modified (sort is by date created in Brevo) and limit the result + if ($offset > $param['limit']) { + // Order data in the date_modified order + $modified = array_column($records, 'modifiedAt'); + array_multisort($modified, SORT_ASC, $records); + // Get only the number of record requested + $records = array_slice($records, 0, $param['limit']); + } } // In case we search data linked to teh contacts if ($param['module'] != 'contacts') { From 9cfca5e761661c88d414b1de90d410019e4262b2 Mon Sep 17 00:00:00 2001 From: myddleware Date: Fri, 19 Jul 2024 01:48:33 +0200 Subject: [PATCH 114/452] Brevo : manage unsubscription Signed-off-by: myddleware --- src/Solutions/sendinblue.php | 14 ++++++++++---- src/Solutions/sugarcrm.php | 5 +++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Solutions/sendinblue.php b/src/Solutions/sendinblue.php index 0282a582c..284a7fc22 100644 --- a/src/Solutions/sendinblue.php +++ b/src/Solutions/sendinblue.php @@ -427,19 +427,25 @@ protected function getContactsStats($param, $records) { if (!empty($records)) { $apiInstance = new \SendinBlue\Client\Api\ContactsApi(new \GuzzleHttp\Client(), $this->config); $dateRef = $this->dateTimeFromMyddleware($param['date_ref']); + $moduleKey = lcfirst(str_replace('contact','',$param['module'])); // For each contact found in the first search foreach ($records as $record) { // Get contact detail to retrive the statistics $recordDetail = $apiInstance->getContactInfo($record['id']); - $stats = $recordDetail['statistics'][lcfirst(str_replace('contact','',$param['module']))]; - if (!empty($recordDetail['statistics'][lcfirst(str_replace('contact','',$param['module']))])) { + if (!empty($recordDetail['statistics'][$moduleKey])) { // Get only the record requested using the campignId (only in case a specific record is requested) if (!empty($param['query']['id'])) { $compaignId = explode('_',$param['query']['id'])[2]; } + // For unsubscriptions, we merge user and admin unsubscriptions + if ($moduleKey == 'unsubscriptions') { + $contactStats = array_merge($recordDetail['statistics'][$moduleKey]['userUnsubscription'],$recordDetail['statistics'][$moduleKey]['adminUnsubscription']); + } else { + $contactStats = $recordDetail['statistics'][$moduleKey]; + } // For each statistics corresponding to the module - foreach($recordDetail['statistics'][lcfirst(str_replace('contact','',$param['module']))] as $contactStat) { - $recordId = $record['id'].'_'.lcfirst(str_replace('contact','',$param['module'])).'_'.$contactStat['campaignId']; + foreach($contactStats as $contactStat) { + $recordId = $record['id'].'_'.$moduleKey.'_'.$contactStat['campaignId']; // Get the data when a specific record is requested if(!empty($compaignId)) { if($contactStat['campaignId'] == $compaignId) { diff --git a/src/Solutions/sugarcrm.php b/src/Solutions/sugarcrm.php index 2b6c30214..9948ce5b0 100644 --- a/src/Solutions/sugarcrm.php +++ b/src/Solutions/sugarcrm.php @@ -532,8 +532,6 @@ public function upsert($method, $param): array foreach ($param['data'] as $idDoc => $data) { // Check control before create/update $param['method'] = $method; - $data = $this->checkDataBeforeCreate($param, $data, $idDoc); - // Check if the module is a many-to-many relationship $rel = $this->isManyToManyRel($param['module']); if (!empty($rel)) { @@ -553,15 +551,18 @@ public function upsert($method, $param): array $bulkData['requests'][] = ['url' => '/'.$this->sugarAPIVersion.'/'.$rel->lhs_module.'/'.$data[$rel->join_key_lhs].'/link', 'method' => 'POST', 'data' => $dataRel]; // Create record } elseif ('create' == $method) { + $data = $this->checkDataBeforeCreate($param, $data, $idDoc); // Myddleware field empty when data transfer type is create unset($data['target_id']); $bulkData['requests'][] = ['url' => '/'.$this->sugarAPIVersion.'/'.$param['module'], 'method' => 'POST', 'data' => $data]; // Update record } elseif ('delete' == $method) { + $data = $this->checkDataBeforeDelete($param, $data, $idDoc); // The record id is stored in $data['target_id'] $targetId = $data['target_id']; $bulkData['requests'][] = ['url' => '/'.$this->sugarAPIVersion.'/'.$param['module'].'/'.$targetId, 'method' => 'DELETE']; } else { + $data = $this->checkDataBeforeUpdate($param, $data, $idDoc); // The record id is stored in $data['target_id'] $targetId = $data['target_id']; unset($data['target_id']); From ef5971dfec878c0df0d4f22bebd8a3ac143baa75 Mon Sep 17 00:00:00 2001 From: Camille Pichaud <95077335+CamillePMyddleware@users.noreply.github.com> Date: Tue, 23 Jul 2024 16:49:26 +0200 Subject: [PATCH 115/452] Front workflow 2 (#1163) * fix: erreur console and add edit action * feat: show workflowAction --------- Co-authored-by: cpichaud --- assets/js/workflows.js | 44 ++-- templates/Workflow/show.html.twig | 20 +- templates/WorkflowAction/edit.html.twig | 89 ++++++-- templates/WorkflowAction/show.html.twig | 271 ++++++++++++++---------- 4 files changed, 268 insertions(+), 156 deletions(-) diff --git a/assets/js/workflows.js b/assets/js/workflows.js index d590d011c..fe7752cac 100644 --- a/assets/js/workflows.js +++ b/assets/js/workflows.js @@ -46,35 +46,41 @@ document.addEventListener("DOMContentLoaded", function () { var mainToggleButton = document.querySelector(".toggle-button"); var mainCollapseContent = document.getElementById("workflow-content"); - mainCollapseContent.addEventListener("shown.bs.collapse", function () { - toggleIcon(mainToggleButton, mainCollapseContent); - }); + if (mainCollapseContent) { + mainCollapseContent.addEventListener("shown.bs.collapse", function () { + toggleIcon(mainToggleButton, mainCollapseContent); + }); - mainCollapseContent.addEventListener("hidden.bs.collapse", function () { - toggleIcon(mainToggleButton, mainCollapseContent); - }); + mainCollapseContent.addEventListener("hidden.bs.collapse", function () { + toggleIcon(mainToggleButton, mainCollapseContent); + }); + } // Sous-panneau des actions var subToggleButton = document.querySelectorAll(".toggle-button")[1]; var subCollapseContent = document.getElementById("actions-content"); - subCollapseContent.addEventListener("shown.bs.collapse", function () { - toggleIcon(subToggleButton, subCollapseContent); - }); + if (subCollapseContent) { + subCollapseContent.addEventListener("shown.bs.collapse", function () { + toggleIcon(subToggleButton, subCollapseContent); + }); - subCollapseContent.addEventListener("hidden.bs.collapse", function () { - toggleIcon(subToggleButton, subCollapseContent); - }); + subCollapseContent.addEventListener("hidden.bs.collapse", function () { + toggleIcon(subToggleButton, subCollapseContent); + }); + } // Panneau des logs var logsToggleButton = document.querySelectorAll(".toggle-button")[2]; var logsCollapseContent = document.getElementById("logs-content"); - logsCollapseContent.addEventListener("shown.bs.collapse", function () { - toggleIcon(logsToggleButton, logsCollapseContent); - }); + if (logsCollapseContent) { + logsCollapseContent.addEventListener("shown.bs.collapse", function () { + toggleIcon(logsToggleButton, logsCollapseContent); + }); - logsCollapseContent.addEventListener("hidden.bs.collapse", function () { - toggleIcon(logsToggleButton, logsCollapseContent); - }); -}); \ No newline at end of file + logsCollapseContent.addEventListener("hidden.bs.collapse", function () { + toggleIcon(logsToggleButton, logsCollapseContent); + }); + } +}); diff --git a/templates/Workflow/show.html.twig b/templates/Workflow/show.html.twig index 9d739a983..a51c60ab3 100644 --- a/templates/Workflow/show.html.twig +++ b/templates/Workflow/show.html.twig @@ -106,6 +106,8 @@ {{ 'view_workflow.description'|trans }} {{ 'view_workflow.order'|trans }} {{ 'view_workflow.arguments'|trans }} + {{'list_workflow.th.option'|trans}} + @@ -130,7 +132,23 @@ {{ action.arguments }} {% endif %} - + + + + + + + + + + + + + + + + + {% endfor %} diff --git a/templates/WorkflowAction/edit.html.twig b/templates/WorkflowAction/edit.html.twig index 6c795e0d0..004bc3d70 100644 --- a/templates/WorkflowAction/edit.html.twig +++ b/templates/WorkflowAction/edit.html.twig @@ -2,27 +2,74 @@ {% block title %}{{ parent() }} | {{ 'view_workflow_action.edit'| trans}}{% endblock %} {% block titlesm %}{{ 'view_workflow_action.edit'| trans}}{% endblock titlesm %} {% block body %} -
    -
    -
    - {{ form_start(form) }} - {{ form_row(form.name) }} - {{ form_row(form.description) }} - {{ form_row(form.Workflow) }} - {{ form_row(form.action) }} - {{ form_row(form.rule) }} - {{ form_row(form.status) }} - {{ form_row(form.to) }} - {{ form_row(form.subject) }} - {{ form_row(form.message) }} - {{ form_row(form.searchField) }} - {{ form_row(form.searchValue) }} - {{ form_row(form.rerun) }} - {{ form_row(form.order) }} - {{ form_row(form.active) }} - {{ form_row(form.submit) }} - {{ form_end(form) }} +
    + {{ form_start(form) }} +
    +
    + {{ 'view_workflow.edit'|trans }}
    +
    +
    +
    + {{ form_row(form.name) }} +
    +
    + {{ form_row(form.Workflow) }} +
    +
    +
    +
    + {{ form_row(form.description) }} +
    +
    + {{ form_row(form.action) }} +
    +
    +
    +
    + {{ form_row(form.order) }} +
    +
    + {{ form_row(form.status) }} +
    +
    +
    +
    + {{ form_row(form.active) }} +
    +
    + {{ form_row(form.subject) }} +
    +
    +
    +
    + {{ form_row(form.message) }} +
    +
    + {{ form_row(form.searchField) }} +
    +
    +
    +
    + {{ form_row(form.searchValue) }} +
    +
    + {{ form_row(form.rerun) }} +
    +
    +
    +
    + {{ form_row(form.rule) }} +
    +
    + {{ form_row(form.to) }} +
    +
    +
    +
    +
    + {{ form_row(form.submit) }}
    + {{ form_end(form) }}
    -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/WorkflowAction/show.html.twig b/templates/WorkflowAction/show.html.twig index c15ed0250..f88498d5a 100644 --- a/templates/WorkflowAction/show.html.twig +++ b/templates/WorkflowAction/show.html.twig @@ -1,125 +1,166 @@ {% extends 'base.html.twig' %} -{% block title %}{{ parent() }} | {{ 'view_workflow_action.title'|trans }}{% endblock %} +{% block title %} + {{ parent() }} + | + {{ 'view_workflow_action.title'|trans }} +{% endblock %} {% block titlesm %} - {{ 'view_workflow_action.title'|trans }} - - {{ workflow.name }} -{% endblock titlesm %} + {{ 'view_workflow_action.title'|trans }} + + {{ workflow.name }} +{% endblock titlesm %} + +{% block body %} +
    + + +
    + {% for message in app.flashes('success') %} + + {% endfor %} -{% block body %} -
    - + {% if workflow is not null %} +
    +
    + + {{ 'view_workflow.title'| trans | upper }} +
    +
    +
    +
    + {{ 'view_workflow.description'|trans|upper }} +
    {{ workflow.description }}
    +
    +
    + {{ 'view_workflow_action.workflow'|trans|upper }} + +
    +
    +
    +
    + {{ 'view_workflow_action.action'|trans|upper }} +
    {{ workflow.action }}
    +
    +
    + {{ 'view_workflow_action.order'|trans|upper }} +
    {{ workflow.order }}
    +
    +
    +
    +
    + {{ 'view_workflow.arguments'|trans|upper }} +
    + {% if workflow.arguments is iterable %} +
      + {% for key, argument in workflow.arguments %} +
    • {{ key }}: + {{ argument }}
    • + {% endfor %} +
    + {% else %} + {{ workflow.arguments }} + {% endif %} +
    +
    +
    + {{ 'view_workflow.status'|trans|upper }} +
    + +
    +
    +
    +
    +
    + {% endif %} -
    - {% for message in app.flashes('success') %} - - {% endfor %} + {% if nb_workflow > 0 %} +
    +

    {{ 'view_workflow.logs'|trans }}

    + {{ nb_workflow }} +
    + {% endif %} +
    +
    +
    +
    + + {{ 'view_workflow.logs'| trans | upper }} +
    +
    + + + + + + + + + + + + + + + + + {% for log in workflowLogs %} + + + + + + + + + + + + + {% endfor %} + +
    {{ 'view_workflow.log_id'|trans }}{{ 'view_workflow.workflow'|trans }}{{ 'view_workflow.job'|trans }}{{ 'view_workflow.trigger_document'|trans }}{{ 'view_workflow.generate_document'|trans }}{{ 'view_workflow.created_by'|trans }}{{ 'view_workflow.status'|trans }}{{ 'view_workflow.date_created'|trans }}{{ 'view_workflow.message'|trans }}{{ 'view_workflow.action_type'|trans }}
    {{ log.id }} + {{ log.workflow.id }} + + {{ log.job.id }} + + {% if log.triggerDocument is not null %} + {{ log.triggerDocument.id }} + {% else %} + + {% endif %} + + {% if log.generateDocument is not null %} + {{ log.generateDocument.id }} + {% else %} + + {% endif %} + {{ log.createdBy }} +
    {{ log.status }}
    +
    {{ log.dateCreated|date('Y-m-d H:i:s') }}{{ log.getMessage }}{{ log.action.action }}
    +
    +
    +
    - {% if workflow is not null %} - - - - - - - - - - - - - - - - - - - - - -
    {{ 'view_workflow.description'|trans }}{{ 'view_workflow_action.workflow'|trans }}{{ 'view_workflow_action.action'|trans }}{{ 'view_workflow_action.order'|trans }}{{ 'view_workflow.arguments'|trans }}{{ 'view_workflow.status'|trans }}
    {{ workflow.description }} - {{ workflow.workflow.name }} - {{ workflow.action }}{{ workflow.order }} - {% if workflow.arguments is iterable %} -
      - {% for key, argument in workflow.arguments %} -
    • {{ key }}: {{ argument }}
    • - {% endfor %} -
    - {% else %} - {{ workflow.arguments }} - {% endif %} -
    - - {{ workflow.active ? 'Active' : 'Inactive' }} - -
    - {% endif %} - {% if nb_workflow > 0 %} -
    -

    {{ 'view_workflow.logs'|trans }}

    - {{ nb_workflow }} -
    - {% endif %} -
    -
    - - - - - - - - - - - - - - - - - {% for log in workflowLogs %} - - - - - - - - - - - - - {% endfor %} - -
    {{ 'view_workflow.log_id'|trans }}{{ 'view_workflow.workflow'|trans }}{{ 'view_workflow.job'|trans }}{{ 'view_workflow.trigger_document'|trans }}{{ 'view_workflow.generate_document'|trans }}{{ 'view_workflow.created_by'|trans }}{{ 'view_workflow.status'|trans }}{{ 'view_workflow.date_created'|trans }}{{ 'view_workflow.message'|trans }}{{ 'view_workflow.action_type'|trans }}
    {{ log.id }}{{ log.workflow.id }}{{ log.job.id }}{% if log.triggerDocument is not null %} - {{ log.triggerDocument.id }} - {% else %} - - {% endif %}{% if log.generateDocument is not null %} - {{ log.generateDocument.id }} - {% else %} - - {% endif %}{{ log.createdBy }} -
    {{ log.status }}
    -
    {{ log.dateCreated|date('Y-m-d H:i:s') }}{{ log.getMessage }}{{ log.action.action }}
    -
    -
    -
    +
    +
    {% endblock %} {% block js %} - - - - + + + + {% endblock js %} From 10b60f99bd14f223f3e884fb3f4be7581d5a3670 Mon Sep 17 00:00:00 2001 From: AlexMyddleware <106162060+AlexMyddleware@users.noreply.github.com> Date: Mon, 29 Jul 2024 11:01:28 +0200 Subject: [PATCH 116/452] add the check for the empty condition string ({status} == " ?"1":"0") --- src/Form/Type/WorkflowType.php | 42 +++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/Form/Type/WorkflowType.php b/src/Form/Type/WorkflowType.php index 61c30e704..6d1492615 100644 --- a/src/Form/Type/WorkflowType.php +++ b/src/Form/Type/WorkflowType.php @@ -68,25 +68,31 @@ public function buildForm(FormBuilderInterface $builder, array $options) 'class' => 'form-control', ], ]); - $builder->add('condition', TextareaType::class, [ - 'label' => 'Condition', - 'data' => $existingCondition ?: '{status} == "', // Use existing condition if available, otherwise use default - 'constraints' => [ - new Callback([ - 'callback' => function($payload, ExecutionContextInterface $context) { - // Check for both possible patterns - if (strpos($payload, '{status} == "') === false && strpos($payload, '{status}=="') === false) { - $context->buildViolation('The condition must contain either "{status} == " or "{status}==""') - ->atPath('condition') - ->addViolation(); - } - }, - ]), - ], - 'attr' => [ - 'class' => 'form-control', + $builder->add('condition', TextareaType::class, [ + 'label' => 'Condition', + 'data' => $existingCondition ?: '{status} == "', // Use existing condition if available, otherwise use default + 'constraints' => [ + new Callback([ + 'callback' => function($payload, ExecutionContextInterface $context) { + // Check for both possible patterns + if (strpos($payload, '{status} == "') === false && strpos($payload, '{status}=="') === false) { + $context->buildViolation('The condition must contain either "{status} == " or "{status}==""') + ->atPath('condition') + ->addViolation(); + } + // Check for the specific pattern indicating an empty condition + if (preg_match('/\(\{status\} == " ?\?"1":"0"\)/', $payload)) { + $context->buildViolation('The condition is empty') + ->atPath('condition') + ->addViolation(); + } + }, + ]), ], - ]); + 'attr' => [ + 'class' => 'form-control', + ], + ]); $builder->add('submit', SubmitType::class, [ 'label' => 'Save', 'attr' => [ From e7020a0460833e6ecabb98181ddcf6b4e4f3bfc9 Mon Sep 17 00:00:00 2001 From: Camille Pichaud <95077335+CamillePMyddleware@users.noreply.github.com> Date: Fri, 2 Aug 2024 11:22:15 +0200 Subject: [PATCH 117/452] fix edit button PROJ-226 (#1165) Co-authored-by: cpichaud --- assets/js/fiche.js | 2 +- templates/Rule/edit/onglets/infos.html.twig | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/js/fiche.js b/assets/js/fiche.js index 6fd828f5f..0554a83c7 100755 --- a/assets/js/fiche.js +++ b/assets/js/fiche.js @@ -134,7 +134,7 @@ $(function() { }); // --For 'description' in the detail view of the rule -$('.edit-button').on('click', function() { +$('.edit-button-description').on('click', function() { var field = $(this).parent(); var editForm = field.find('.edit-form'); var valueField = field.find('.value'); diff --git a/templates/Rule/edit/onglets/infos.html.twig b/templates/Rule/edit/onglets/infos.html.twig index f6701ea77..4813b507c 100644 --- a/templates/Rule/edit/onglets/infos.html.twig +++ b/templates/Rule/edit/onglets/infos.html.twig @@ -187,11 +187,11 @@

    {{ 'solution.params.description' | trans }}

    {{ param.value }}
    - +
    {% endif %} From 7958b8ab1cc2393ff6355b1c466938a4aead17ee Mon Sep 17 00:00:00 2001 From: myddleware Date: Tue, 6 Aug 2024 12:05:31 +0200 Subject: [PATCH 118/452] Lookup function : add the possibility to link a rule with the same source and target connector Add the possibility to have a space in the source fields Remove duplicate check if one of the duplicate field is empty Signed-off-by: myddleware --- src/Controller/DefaultController.php | 3 --- src/Manager/FormulaFunctionManager.php | 23 +++++++++++++++++++++-- src/Manager/RuleManager.php | 5 +++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index 1544e409b..dfeb114d0 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -2430,9 +2430,6 @@ public function ruleValidation(Request $request): JsonResponse } } - // delete space - $field_source = str_replace(' ', '', $field_source); - // Insert $oneRuleField = new RuleField(); $oneRuleField->setRule($oneRule); diff --git a/src/Manager/FormulaFunctionManager.php b/src/Manager/FormulaFunctionManager.php index db438b06e..10c70a2ec 100644 --- a/src/Manager/FormulaFunctionManager.php +++ b/src/Manager/FormulaFunctionManager.php @@ -156,6 +156,15 @@ public static function lookup($entityManager, $connection, $currentRule, $docId, AND $ruleRef['conn_id_source'] == $ruleLink['conn_id_source'] AND $ruleRef['conn_id_target'] == $ruleLink['conn_id_target'] ) + // In case of the linked rule has the target connector = source connector, we use module to get the direction of the relationship + OR ( + !empty($ruleRef) + AND $ruleLink['conn_id_target'] == $ruleLink['conn_id_source'] + AND ( + $ruleRef['module_source'] == $ruleLink['module_source'] + OR $ruleRef['module_target'] == $ruleLink['module_target'] + ) + ) OR (!empty($direction)) // Manage simulation ){ $sqlParams = " SELECT @@ -179,8 +188,18 @@ public static function lookup($entityManager, $connection, $currentRule, $docId, LIMIT 1"; $direction = 1; } elseif ( - $ruleRef['conn_id_source'] == $ruleLink['conn_id_target'] - AND $ruleRef['conn_id_target'] == $ruleLink['conn_id_source'] + ( + $ruleRef['conn_id_source'] == $ruleLink['conn_id_target'] + AND $ruleRef['conn_id_target'] == $ruleLink['conn_id_source'] + ) + // In case of the linked rule has the target connector = source connector, we use module to get the direction of the relationship + OR ( + $ruleLink['conn_id_target'] == $ruleLink['conn_id_source'] + AND ( + $ruleRef['module_source'] == $ruleLink['module_target'] + OR $ruleRef['module_target'] == $ruleLink['module_source'] + ) + ) ){ $sqlParams = " SELECT source_id record_id, diff --git a/src/Manager/RuleManager.php b/src/Manager/RuleManager.php index 6c9880652..4b856b6e0 100644 --- a/src/Manager/RuleManager.php +++ b/src/Manager/RuleManager.php @@ -1841,6 +1841,11 @@ protected function checkDuplicate($transformedData) // Récupération des valeurs de la source pour chaque champ de recherche foreach ($duplicate_fields as $duplicate_field) { + // No duplicate check if one of the duplicate field is empty + if (empty($rowTransformedData[$duplicate_field])) { + $concatduplicate = ''; + break; + } $concatduplicate .= $rowTransformedData[$duplicate_field]; } // Empty data aren't used for duplicate search From fa0c27f419d6b4a4ab17d34f13da8cf84b19d1be Mon Sep 17 00:00:00 2001 From: Camille Pichaud <95077335+CamillePMyddleware@users.noreply.github.com> Date: Wed, 7 Aug 2024 11:30:07 +0200 Subject: [PATCH 119/452] Fix id task (#1172) * Fix: id task in twig * fix: workflows action * fix css --------- Co-authored-by: cpichaud --- assets/css/task.css | 89 +++++++++++++------------ templates/Workflow/list.html.twig | 2 +- templates/Workflow/show.html.twig | 2 +- templates/WorkflowAction/list.html.twig | 2 +- templates/WorkflowAction/show.html.twig | 2 +- 5 files changed, 52 insertions(+), 45 deletions(-) diff --git a/assets/css/task.css b/assets/css/task.css index 8bcc77ec6..f6018b6ef 100755 --- a/assets/css/task.css +++ b/assets/css/task.css @@ -4,7 +4,7 @@ * @package Myddleware * @copyright Copyright (C) 2013 - 2015 St�phane Faure - CRMconsult EURL * @copyright Copyright (C) 2015 - 2016 St�phane Faure - Myddleware ltd - contact@myddleware.com - * @link http://www.myddleware.com + * @link http://www.myddleware.com This file is part of Myddleware. @@ -22,83 +22,90 @@ along with Myddleware. If not, see . *********************************************************************************/ -#task { +#task, #workflows { padding: 20px 0; text-align: center; } -#task th { - background: #ECECEC; - height: 30px; - padding: 2px; - text-align: center; +#task th, #workflows th { + background: #ECECEC; + height: 30px; + padding: 2px; + text-align: center; } -#task tr { - border-bottom: 1px solid #ccc; +#task tr, #workflows tr { + border-bottom: 1px solid #ccc; } -#task .fd_error, #task .fd_open, #task .fd_log_e { - background: #F2DEDF; +#task .fd_error, #task .fd_open, #task .fd_log_e, +#workflows .fd_error, #workflows .fd_open, #workflows .fd_log_e { + background: #F2DEDF; } -#task .fd_cancel, #task .fd_log_w { - background: #FFECCC; +#task .fd_cancel, #task .fd_log_w, +#workflows .fd_cancel, #workflows .fd_log_w { + background: #FFECCC; } -#task .gblstatus_error, #task .gblstatus_open, #task .log_e { - color: #D60405; +#task .gblstatus_error, #task .gblstatus_open, #task .log_e, +#workflows .gblstatus_error, #workflows .gblstatus_open, #workflows .log_e { + color: #D60405; } -#task .gblstatus_cancel, #task .log_w { - color: #F1B753; +#task .gblstatus_cancel, #task .log_w, +#workflows .gblstatus_cancel, #workflows .log_w { + color: #F1B753; } -#flux .gblstatus_close, #task .log_s { - color: #9AA932; +#flux .gblstatus_close, #task .log_s, #workflows .log_s { + color: #9AA932; } -#task .gblstatus_close, #task .gblstatus_cancel, #task .gblstatus_error, #task .gblstatus_open { - font-weight: bold; +#task .gblstatus_close, #task .gblstatus_cancel, #task .gblstatus_error, #task .gblstatus_open, +#workflows .gblstatus_close, #workflows .gblstatus_cancel, #workflows .gblstatus_error, #workflows .gblstatus_open { + font-weight: bold; } -#task tr:hover, #task .fd_normal { - background: #ECECEC; +#task tr:hover, #task .fd_normal, +#workflows tr:hover, #workflows .fd_normal { + background: #ECECEC; } -#task table a, #task table a:hover { - color: #0F66A9; - font-weight: bold; +#task table a, #task table a:hover, +#workflows table a, #workflows table a:hover { + color: #0F66A9; + font-weight: bold; } -#task #headertab { - margin-top:20px; +#task #headertab, #workflows #headertab { + margin-top:20px; } -#task #headertab td { +#task #headertab td, #workflows #headertab td { height: 50px; - /* white-space: nowrap; */ + /* white-space: nowrap; */ /* max-width: 200px; */ min-width: 80px; - width: 80px; + width: 80px; padding: 5px; word-wrap: break-word; } -#task .listepager .count { - max-width: 80px; - width: 80px; - min-width: 80px; - text-align: center; +#task .listepager .count, #workflows .listepager .count { + max-width: 80px; + width: 80px; + min-width: 80px; + text-align: center; } .button_message_task_collapse { - display: none !important; + display: none !important; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-height: 1.2em; - /* display: block; */ + /* display: block; */ } .button_message_task_expand { @@ -106,14 +113,14 @@ } .button_param_task_collapse { - display: none !important; + display: none !important; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-height: 1.2em; - /* display: block; */ + /* display: block; */ } .button_param_task_expand { white-space: normal; -} \ No newline at end of file +} diff --git a/templates/Workflow/list.html.twig b/templates/Workflow/list.html.twig index ae4227395..2f662991e 100644 --- a/templates/Workflow/list.html.twig +++ b/templates/Workflow/list.html.twig @@ -26,7 +26,7 @@ {% block title %}{{parent()}} | {{'title.workflow.list'|trans}}{% endblock %} {% block titlesm %}{{'title.workflow.list'|trans}}{% endblock titlesm %} {% block body %} -
    +
    {{ 'Create new workflow'|trans }} diff --git a/templates/Workflow/show.html.twig b/templates/Workflow/show.html.twig index a51c60ab3..e4fea9bab 100644 --- a/templates/Workflow/show.html.twig +++ b/templates/Workflow/show.html.twig @@ -14,7 +14,7 @@ {% block body %} -
    +
    diff --git a/templates/WorkflowAction/list.html.twig b/templates/WorkflowAction/list.html.twig index f3ca6464d..1b4f00f30 100644 --- a/templates/WorkflowAction/list.html.twig +++ b/templates/WorkflowAction/list.html.twig @@ -32,7 +32,7 @@ {% endblock js %} -
    +
    {{ 'Create new workflow'|trans }} diff --git a/templates/WorkflowAction/show.html.twig b/templates/WorkflowAction/show.html.twig index f88498d5a..67b3aa269 100644 --- a/templates/WorkflowAction/show.html.twig +++ b/templates/WorkflowAction/show.html.twig @@ -12,7 +12,7 @@ {% endblock titlesm %} {% block body %} -
    +
    {{ 'view_workflow_action.back_to_workflow'|trans }} {{ 'view_workflow_action.edit'|trans }} From b0655c6faf0b83c6b014074b3581b88dc5a33899 Mon Sep 17 00:00:00 2001 From: Camille Pichaud <95077335+CamillePMyddleware@users.noreply.github.com> Date: Wed, 7 Aug 2024 16:19:59 +0200 Subject: [PATCH 120/452] Fix issues workflow action (#1173) * Fix: rule to ruleId #1169 * fix: add 'id' #1168 * Fix: field ruleId * controller working * Revert "controller working" This reverts commit c05d72c093badcf0d94e0e601cbe66be67a77c97. * add pagerFanta for cronJob, listview * add pagerFanta in view detail * fix: remove '>' * 1170 fix workflow action rerun so can be saved in db https://github.com/Myddleware/myddleware/issues/1170 * Add : pagerFanta for workflows #1166/#1167 * Fix: button toggle * add: pagerFanta in view detail 'workflowAction #1167 --------- Co-authored-by: cpichaud Co-authored-by: AlexMyddleware <106162060+AlexMyddleware@users.noreply.github.com> --- assets/js/workflows.js | 7 +- src/Controller/JobSchedulerController.php | 40 +- src/Controller/WorkflowActionController.php | 478 +++++++++--------- src/Controller/WorkflowController.php | 272 +++++----- src/Repository/WorkflowLogRepository.php | 10 + templates/JobScheduler/crontab_list.html.twig | 386 ++++++++------ templates/JobScheduler/show_crontab.html.twig | 59 ++- templates/Workflow/show.html.twig | 99 ++-- templates/WorkflowAction/edit.html.twig | 2 +- templates/WorkflowAction/new.html.twig | 2 +- templates/WorkflowAction/show.html.twig | 133 +++-- 11 files changed, 834 insertions(+), 654 deletions(-) diff --git a/assets/js/workflows.js b/assets/js/workflows.js index fe7752cac..18d01f742 100644 --- a/assets/js/workflows.js +++ b/assets/js/workflows.js @@ -2,10 +2,15 @@ $(document).ready(function () { $(".form-check-input").on("change", function () { var workflowId = $(this).data("id"); var newState = $(this).is(":checked") ? 1 : 0; + + var pathArray = window.location.pathname.split('/'); + var basePath = window.location.origin + '/' + pathArray[1] + '/' + pathArray[2]; + var currentUrl = `${basePath}/workflow/workflow/toggle/${workflowId}`; $.ajax({ - url: `/myddleware/public/workflow/workflow/toggle/${workflowId}`, + url: currentUrl, type: "POST", + data: { newState: newState }, beforeSend: function () { console.log( `Before sending the request for workflow ID: ${workflowId}` diff --git a/src/Controller/JobSchedulerController.php b/src/Controller/JobSchedulerController.php index 270f7c053..0faef5b64 100644 --- a/src/Controller/JobSchedulerController.php +++ b/src/Controller/JobSchedulerController.php @@ -22,6 +22,8 @@ use Shapecode\Bundle\CronBundle\Entity\CronJobResult; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Pagerfanta\Doctrine\ORM\QueryAdapter; +use Pagerfanta\Pagerfanta; /** * @Route("/rule/jobscheduler") @@ -306,9 +308,10 @@ public function createActionWithCron(Request $request, TranslatorInterface $tran } /** - * @Route("/crontab_list", name="jobscheduler_cron_list") + * @Route("/crontab_list", name="jobscheduler_cron_list", defaults={"page"=1}) + * @Route("/crontab_list/page-{page}", name="jobscheduler_cron_list_page", requirements={"page"="\d+"}) */ - public function crontabList(): Response + public function crontabList(int $page): Response { //Check if crontab is enabled $entitiesCron = $this->entityManager->getRepository(Config::class)->findBy(['name' => 'cron_enabled']); @@ -323,14 +326,21 @@ public function crontabList(): Response $entity = $this->entityManager->getRepository(CronJob::class)->findAll(); - // Fetch the cron_job_result data - $cronJobResults = $this->entityManager->getRepository(CronJobResult::class)->findBy(array(), null, $searchLimit, 0); + // Pagination for cron_job_result + $query = $this->entityManager->createQuery( + 'SELECT c FROM Shapecode\Bundle\CronBundle\Entity\CronJobResult c ORDER BY c.runAt DESC' + ); + + $adapter = new QueryAdapter($query); + $pager = new Pagerfanta($adapter); + $pager->setMaxPerPage(10); + $pager->setCurrentPage($page); return $this->render('JobScheduler/crontab_list.html.twig', [ 'entity' => $entity, 'timezone' => $timezone, 'entitiesCron' => $entitiesCron, - 'cronJobResults' => $cronJobResults, + 'pager' => $pager, ]); } @@ -425,25 +435,31 @@ public function updateCrontab(Request $request, $id) } /** - * @Route("/{id}/show_crontab", name="crontab_show") + * @Route("/{id}/show_crontab", name="crontab_show", defaults={"page"=1}) + * @Route("/{id}/show_crontab/page-{page}", name="crontab_show_page", requirements={"page"="\d+"}) */ - public function showCrontab($id): Response + public function showCrontab($id, int $page): Response { $entity = $this->entityManager->getRepository(CronJob::class)->find($id); if (!$entity) { throw $this->createNotFoundException('Unable to find crontab entity.'); } - // Fetch the CronJobResults and sort them by 'id' in ascending order - $entityResult = $this->entityManager->getRepository(CronJobResult::class)->findBy( - ['cronJob' => $id], - ); + $query = $this->entityManager->createQuery( + 'SELECT c FROM Shapecode\Bundle\CronBundle\Entity\CronJobResult c WHERE c.cronJob = :cronJob ORDER BY c.runAt DESC' + )->setParameter('cronJob', $id); + + $adapter = new QueryAdapter($query); + $pager = new Pagerfanta($adapter); + $pager->setMaxPerPage(10); + $pager->setCurrentPage($page); return $this->render('JobScheduler/show_crontab.html.twig', [ 'entity' => $entity, - 'entityResult' => $entityResult, + 'pager' => $pager, ]); } + /** * Disables all cron jobs. diff --git a/src/Controller/WorkflowActionController.php b/src/Controller/WorkflowActionController.php index 224da2ca1..6651a6ebe 100644 --- a/src/Controller/WorkflowActionController.php +++ b/src/Controller/WorkflowActionController.php @@ -1,4 +1,5 @@ logger = $logger; - $this->ruleManager = $ruleManager; - $this->formuleManager = $formuleManager; - $this->solutionManager = $solutionManager; - $this->documentManager = $documentManager; - $this->sessionService = $sessionService; - $this->entityManager = $entityManager; - $this->ruleRepository = $ruleRepository; - $this->jobRepository = $jobRepository; - $this->documentRepository = $documentRepository; - $this->connection = $connection; - $this->translator = $translator; - $this->authorizationChecker = $authorizationChecker; - $this->home = $home; - $this->tools = $tools; - $this->jobManager = $jobManager; - $this->template = $template; - } +/** + * @Route("/workflowAction") + */ +class WorkflowActionController extends AbstractController +{ + private FormulaManager $formuleManager; + private SessionService $sessionService; + private ParameterBagInterface $params; + private EntityManagerInterface $entityManager; + private HomeManager $home; + private ToolsManager $tools; + private TranslatorInterface $translator; + private AuthorizationCheckerInterface $authorizationChecker; + private JobManager $jobManager; + private LoggerInterface $logger; + private TemplateManager $template; + private RuleRepository $ruleRepository; + private JobRepository $jobRepository; + private DocumentRepository $documentRepository; + private SolutionManager $solutionManager; + private RuleManager $ruleManager; + private DocumentManager $documentManager; + protected Connection $connection; + // To allow sending a specific record ID to rule simulation + protected $simulationQueryField; + private ConfigRepository $configRepository; + + public function __construct( + LoggerInterface $logger, + RuleManager $ruleManager, + FormulaManager $formuleManager, + SolutionManager $solutionManager, + DocumentManager $documentManager, + SessionService $sessionService, + EntityManagerInterface $entityManager, + RuleRepository $ruleRepository, + JobRepository $jobRepository, + DocumentRepository $documentRepository, + Connection $connection, + TranslatorInterface $translator, + AuthorizationCheckerInterface $authorizationChecker, + HomeManager $home, + ToolsManager $tools, + JobManager $jobManager, + TemplateManager $template, + ParameterBagInterface $params + ) { + $this->logger = $logger; + $this->ruleManager = $ruleManager; + $this->formuleManager = $formuleManager; + $this->solutionManager = $solutionManager; + $this->documentManager = $documentManager; + $this->sessionService = $sessionService; + $this->entityManager = $entityManager; + $this->ruleRepository = $ruleRepository; + $this->jobRepository = $jobRepository; + $this->documentRepository = $documentRepository; + $this->connection = $connection; + $this->translator = $translator; + $this->authorizationChecker = $authorizationChecker; + $this->home = $home; + $this->tools = $tools; + $this->jobManager = $jobManager; + $this->template = $template; + } - protected function getInstanceBdd() - { - } + protected function getInstanceBdd() + { + } // public function to delet the workflow by id (set deleted to 1) @@ -231,8 +232,8 @@ public function WorkflowActionActiveShowAction(string $id, Request $request) } else { $this->addFlash('error', 'Workflow Action not found'); } - - + + return $this->redirectToRoute('workflow_action_show', ['id' => $id]); } catch (Exception $e) { throw $this->createNotFoundException('Error : ' . $e); @@ -250,7 +251,7 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re $workflow = $em->getRepository(Workflow::class)->find($workflowId); if (!$workflow) { - throw $this->createNotFoundException('No workflow found for id '.$workflowId); + throw $this->createNotFoundException('No workflow found for id ' . $workflowId); } $workflowAction = new WorkflowAction(); @@ -273,36 +274,37 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re $StringStatus = []; foreach ($possiblesStatusesWithIntegers as $key => $value) { $StringStatus[$key] = $key; - } - // to get the searchValue, we need to get the rule, and from the rule we need to get the list of the source fields. The possible choisces are the source fields of the rule. The searchValue is a choicetype - // step 1: get the workflow of the action - $ruleForSearchValue = $workflowAction->getWorkflow()->getRule(); - // step 2: get the source fields of the rule - $sourceFields = $ruleForSearchValue->getSourceFields(); - - // step 3: modify the source field so that for each, the key is the value and the value is the value - foreach ($sourceFields as $key => $value) { - $sourceFields[$value] = $value; - unset($sourceFields[$key]); - } + // to get the searchValue, we need to get the rule, and from the rule we need to get the list of the source fields. The possible choisces are the source fields of the rule. The searchValue is a choicetype + // step 1: get the workflow of the action + $ruleForSearchValue = $workflowAction->getWorkflow()->getRule(); + // step 2: get the source fields of the rule + $sourceFields = $ruleForSearchValue->getSourceFields(); + $sourceFields['id'] = 'd'; + + // step 3: modify the source field so that for each, the key is the value and the value is the value + foreach ($sourceFields as $key => $value) { + $sourceFields[$value] = $value; + unset($sourceFields[$key]); + } - $sourceSearchValue = []; - // create an array of all the rules + $sourceSearchValue = []; + // create an array of all the rules - $rules = $em->getRepository(Rule::class)->findBy(['active' => true]); + $rules = $em->getRepository(Rule::class)->findBy(['active' => true]); - // fill the array with the source fields of each rule - foreach ($rules as $rule) { - // $sourceSearchValue[$rule->getId()] = $rule->getSourceFields(); - $ruleSourceFields = $rule->getSourceFields(); - foreach ($ruleSourceFields as $key => $value) { - $ruleSourceFields[$value] = $value; - unset($ruleSourceFields[$key]); - } - $sourceSearchValue[$rule->getId()] = $ruleSourceFields; + // fill the array with the source fields of each rule + foreach ($rules as $rule) { + // $sourceSearchValue[$rule->getId()] = $rule->getSourceFields(); + $ruleSourceFields = $rule->getSourceFields(); + foreach ($ruleSourceFields as $key => $value) { + $ruleSourceFields[$value] = $value; + unset($ruleSourceFields[$key]); } + $ruleSourceFields['id'] = 'id'; + $sourceSearchValue[$rule->getId()] = $ruleSourceFields; + } @@ -313,7 +315,7 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re 'Workflow' => $workflowAction->getWorkflow(), 'action' => null, 'status' => null, - 'rule' => null, + 'ruleId' => null, 'searchField' => null, 'searchValue' => null, 'order' => null, @@ -324,7 +326,7 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re 'rerun' => null // Add other WorkflowAction fields here as needed ]; - + $form = $this->createFormBuilder($formData) ->add('name', TextType::class, [ 'label' => 'Action Name', @@ -350,12 +352,13 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re 'transformDocument' => 'transformDocument', ], ]) - ->add('rule', EntityType::class, [ + ->add('ruleId', EntityType::class, [ 'class' => Rule::class, 'choices' => $em->getRepository(Rule::class)->findBy(['active' => true]), 'choice_label' => 'name', 'choice_value' => 'id', - 'required' => false + 'required' => false, + 'label' => 'Rule', ]) ->add('status', ChoiceType::class, [ 'label' => 'Status', @@ -404,7 +407,7 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re ], ]) ->add('submit', SubmitType::class, ['label' => 'Save']) - ->getForm(); + ->getForm(); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { @@ -431,7 +434,7 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re $active = $form->get('active')->getData(); $workflowAction->setActive($active); - + // get the to, the subject, and the message using getdata $arguments = []; $to = $form->get('to')->getData(); @@ -449,38 +452,36 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re $arguments['message'] = $message; } - $rule = $form->get('rule')->getData(); - if(!empty($rule)) { + $rule = $form->get('ruleId')->getData(); + if ($rule !== null) { $ruleIdForArgument = $rule->getId(); + $arguments['ruleId'] = $ruleIdForArgument; } - if (!empty($rule)) { - $arguments['rule'] = $ruleIdForArgument; - } - + // set the status $status = $form->get('status')->getData(); if (!empty($status)) { //since status is a integer, we have to map it to possible statuses name $arguments['status'] = $status; } - + // set the searchField $searchField = $form->get('searchField')->getData(); if (!empty($searchField)) { $arguments['searchField'] = $searchField; } - + // set the searchValue $searchValue = $form->get('searchValue')->getData(); if (!empty($searchValue)) { $arguments['searchValue'] = $searchValue; } - + $rerun = $form->get('rerun')->getData(); if (!empty($rerun)) { $arguments['rerun'] = $rerun; } - + $workflowAction->setArguments($arguments); $em->persist($workflowAction); $em->flush(); @@ -512,37 +513,49 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re // public function to show the detail view of a single workflow /** - * @Route("/showAction/{id}", name="workflow_action_show") + * @Route("/showAction/{id}", name="workflow_action_show", defaults={"page"=1}) + * @Route("/showAction/{id}/page-{page}", name="workflow_action_show_page", requirements={"page"="\d+"}) */ - public function WorkflowActionShowAction(string $id, Request $request) + public function WorkflowActionShowAction(string $id, Request $request, int $page): Response { try { $em = $this->getDoctrine()->getManager(); - $workflow = $em->getRepository(WorkflowAction::class)->findBy(['id' => $id, 'deleted' => 0]); - - // get the workflow logs of this action + $workflow = $em->getRepository(WorkflowAction::class)->findOneBy(['id' => $id, 'deleted' => 0]); $workflowLogs = $em->getRepository(WorkflowLog::class)->findBy(['action' => $id]); + if (!$workflow) { + $this->addFlash('error', 'Workflow Action not found'); + return $this->redirectToRoute('workflow_list'); + } - if ($workflow[0]) { + $workflowLogs = $em->getRepository(WorkflowLog::class)->findBy( + ['action' => $workflow], + ['dateCreated' => 'DESC'] + ); + + $adapter = new ArrayAdapter($workflowLogs); + $pager = new Pagerfanta($adapter); + $pager->setMaxPerPage(10); + $pager->setCurrentPage($page); + + if ($workflow) { $nb_workflow = count($workflowLogs); return $this->render( 'WorkflowAction/show.html.twig', [ - 'workflow' => $workflow[0], + 'workflow' => $workflow, 'workflowLogs' => $workflowLogs, - 'nb_workflow' => $nb_workflow + 'nb_workflow' => $nb_workflow, + 'pager' => $pager, ] ); } else { $this->addFlash('error', 'Workflow not found'); - return $this->redirectToRoute('workflow_list'); } } catch (Exception $e) { throw $this->createNotFoundException('Error : ' . $e); } } - // public function to edit a workflow /** * @Route("/editWorkflowAction/{id}", name="workflow_action_edit") @@ -565,38 +578,37 @@ public function WorkflowActionEditAction(string $id, Request $request) $StringStatus = []; foreach ($possiblesStatusesWithIntegers as $key => $value) { $StringStatus[$key] = $key; - } - // to get the searchValue, we need to get the rule, and from the rule we need to get the list of the source fields. The possible choisces are the source fields of the rule. The searchValue is a choicetype - // step 1: get the workflow of the action - $ruleForSearchValue = $workflowAction->getWorkflow()->getRule(); - // step 2: get the source fields of the rule - $sourceFields = $ruleForSearchValue->getSourceFields(); - - // step 3: modify the source field so that for each, the key is the value and the value is the value - foreach ($sourceFields as $key => $value) { - $sourceFields[$value] = $value; - unset($sourceFields[$key]); - } + // to get the searchValue, we need to get the rule, and from the rule we need to get the list of the source fields. The possible choisces are the source fields of the rule. The searchValue is a choicetype + // step 1: get the workflow of the action + $ruleForSearchValue = $workflowAction->getWorkflow()->getRule(); + // step 2: get the source fields of the rule + $sourceFields = $ruleForSearchValue->getSourceFields(); + $sourceFields['id'] = 'id'; + + // step 3: modify the source field so that for each, the key is the value and the value is the value + foreach ($sourceFields as $key => $value) { + $sourceFields[$value] = $value; + unset($sourceFields[$key]); + } - $sourceSearchValue = []; - // create an array of all the rules + $sourceSearchValue = []; + // create an array of all the rules - $rules = $em->getRepository(Rule::class)->findBy(['active' => true]); + $rules = $em->getRepository(Rule::class)->findBy(['active' => true]); - // fill the array with the source fields of each rule - foreach ($rules as $rule) { - // $sourceSearchValue[$rule->getId()] = $rule->getSourceFields(); - $ruleSourceFields = $rule->getSourceFields(); - foreach ($ruleSourceFields as $key => $value) { - $ruleSourceFields[$value] = $value; - unset($ruleSourceFields[$key]); - } - $sourceSearchValue[$rule->getId()] = $ruleSourceFields; + // fill the array with the source fields of each rule + foreach ($rules as $rule) { + // $sourceSearchValue[$rule->getId()] = $rule->getSourceFields(); + $ruleSourceFields = $rule->getSourceFields(); + foreach ($ruleSourceFields as $key => $value) { + $ruleSourceFields[$value] = $value; + unset($ruleSourceFields[$key]); } - - + $ruleSourceFields['id'] = 'id'; + $sourceSearchValue[$rule->getId()] = $ruleSourceFields; + } // Create a new array to hold the form data $formData = [ @@ -605,7 +617,7 @@ public function WorkflowActionEditAction(string $id, Request $request) 'Workflow' => $workflowAction->getWorkflow(), 'action' => $workflowAction->getAction(), 'status' => isset($arguments['status']) ? $StringStatus[$arguments['status']] : null, - 'rule' => $workflowAction->getWorkflow()->getRule() ?? null, + 'ruleId' => isset($arguments['ruleId']) ? $arguments['ruleId'] : null, 'searchField' => $arguments['searchField'] ?? null, 'searchValue' => $arguments['searchValue'] ?? null, 'order' => $workflowAction->getOrder(), @@ -613,10 +625,10 @@ public function WorkflowActionEditAction(string $id, Request $request) 'to' => $arguments['to'] ?? null, 'subject' => $arguments['subject'] ?? null, 'message' => $arguments['message'] ?? null, - 'rerun' => $arguments['rerun'] ?? false + 'rerun' => $arguments['rerun'] ?? 0 // Add other WorkflowAction fields here as needed ]; - + $form = $this->createFormBuilder($formData) ->add('name', TextType::class, [ 'label' => 'Action Name', @@ -642,12 +654,14 @@ public function WorkflowActionEditAction(string $id, Request $request) 'transformDocument' => 'transformDocument', ], ]) - ->add('rule', EntityType::class, [ + ->add('ruleId', EntityType::class, [ 'class' => Rule::class, 'choices' => $em->getRepository(Rule::class)->findBy(['active' => true]), 'choice_label' => 'name', 'choice_value' => 'id', - 'required' => false + 'required' => false, + 'label' => 'Rule', + 'data' => $em->getRepository(Rule::class)->find($formData['ruleId']) ]) ->add('status', ChoiceType::class, [ 'label' => 'Status', @@ -696,7 +710,7 @@ public function WorkflowActionEditAction(string $id, Request $request) ], ]) ->add('submit', SubmitType::class, ['label' => 'Save']) - ->getForm(); + ->getForm(); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { @@ -723,7 +737,7 @@ public function WorkflowActionEditAction(string $id, Request $request) $active = $form->get('active')->getData(); $workflowAction->setActive($active); - + // get the to, the subject, and the message using getdata $arguments = []; $to = $form->get('to')->getData(); @@ -741,36 +755,38 @@ public function WorkflowActionEditAction(string $id, Request $request) $arguments['message'] = $message; } - $rule = $form->get('rule')->getData(); + $rule = $form->get('ruleId')->getData(); if ($rule !== null) { $ruleIdForArgument = $rule->getId(); - $arguments['rule'] = $ruleIdForArgument; + $arguments['ruleId'] = $ruleIdForArgument; } - + // set the status $status = $form->get('status')->getData(); if (!empty($status)) { //since status is a integer, we have to map it to possible statuses name $arguments['status'] = $status; } - + // set the searchField $searchField = $form->get('searchField')->getData(); if (!empty($searchField)) { $arguments['searchField'] = $searchField; } - + // set the searchValue $searchValue = $form->get('searchValue')->getData(); if (!empty($searchValue)) { $arguments['searchValue'] = $searchValue; } - + $rerun = $form->get('rerun')->getData(); if (!empty($rerun)) { $arguments['rerun'] = $rerun; + } else { + $arguments['rerun'] = 0; } - + $workflowAction->setArguments($arguments); $em->persist($workflowAction); $em->flush(); @@ -827,57 +843,57 @@ public function emptyArgumentsBasedOnAction($id) } } - // public function to save the workflowAudit to the database - public function saveWorkflowAudit($workflowId) - { - - $em = $this->getDoctrine()->getManager(); - $workflowArray = $em->getRepository(Workflow::class)->findBy(['id' => $workflowId, 'deleted' => 0]); - $workflow = $workflowArray[0]; - - // get all the actions of the workflow - $actions = $workflow->getWorkflowActions(); - - $actionsArray = array_map(function($action) { - return [ - 'id' => $action->getId(), - 'workflow' => $action->getWorkflow()->getId(), - 'dateCreated' => $action->getDateCreated()->format('Y-m-d H:i:s'), - 'dateModified' => $action->getDateModified()->format('Y-m-d H:i:s'), - 'createdBy' => $action->getCreatedBy()->getUsername(), - 'modifiedBy' => $action->getModifiedBy()->getUsername(), - 'name' => $action->getName(), - 'action' => $action->getAction(), - 'description' => $action->getDescription(), - 'order' => $action->getOrder(), - 'active' => $action->getActive(), - 'deleted' => $action->getDeleted(), - 'arguments' => $action->getArguments(), - ]; - }, $actions->toArray()); - - // Encode every workflow parameters - $workflowdata = json_encode( - [ - 'workflowName' => $workflow->getName(), - 'rule' => $workflow->getRule()->getId(), - 'created_by' => $workflow->getCreatedBy()->getUsername(), - 'workflowDescription' => $workflow->getDescription(), - 'condition' => $workflow->getCondition(), - 'active' => $workflow->getActive(), - 'dateCreated' => $workflow->getDateCreated()->format('Y-m-d H:i:s'), - 'dateModified' => $workflow->getDateModified()->format('Y-m-d H:i:s'), - 'actions' => $actionsArray, - ] - ); - // Save the workflow audit - $oneworkflowAudit = new WorkflowAudit(); - $oneworkflowAudit->setworkflow($workflow); - $oneworkflowAudit->setDateCreated(new \DateTime()); - $oneworkflowAudit->setData($workflowdata); - $this->entityManager->persist($oneworkflowAudit); - $this->entityManager->flush(); - } + // public function to save the workflowAudit to the database + public function saveWorkflowAudit($workflowId) + { + + $em = $this->getDoctrine()->getManager(); + $workflowArray = $em->getRepository(Workflow::class)->findBy(['id' => $workflowId, 'deleted' => 0]); + $workflow = $workflowArray[0]; + + // get all the actions of the workflow + $actions = $workflow->getWorkflowActions(); + + $actionsArray = array_map(function ($action) { + return [ + 'id' => $action->getId(), + 'workflow' => $action->getWorkflow()->getId(), + 'dateCreated' => $action->getDateCreated()->format('Y-m-d H:i:s'), + 'dateModified' => $action->getDateModified()->format('Y-m-d H:i:s'), + 'createdBy' => $action->getCreatedBy()->getUsername(), + 'modifiedBy' => $action->getModifiedBy()->getUsername(), + 'name' => $action->getName(), + 'action' => $action->getAction(), + 'description' => $action->getDescription(), + 'order' => $action->getOrder(), + 'active' => $action->getActive(), + 'deleted' => $action->getDeleted(), + 'arguments' => $action->getArguments(), + ]; + }, $actions->toArray()); + + // Encode every workflow parameters + $workflowdata = json_encode( + [ + 'workflowName' => $workflow->getName(), + 'rule' => $workflow->getRule()->getId(), + 'created_by' => $workflow->getCreatedBy()->getUsername(), + 'workflowDescription' => $workflow->getDescription(), + 'condition' => $workflow->getCondition(), + 'active' => $workflow->getActive(), + 'dateCreated' => $workflow->getDateCreated()->format('Y-m-d H:i:s'), + 'dateModified' => $workflow->getDateModified()->format('Y-m-d H:i:s'), + 'actions' => $actionsArray, + ] + ); + // Save the workflow audit + $oneworkflowAudit = new WorkflowAudit(); + $oneworkflowAudit->setworkflow($workflow); + $oneworkflowAudit->setDateCreated(new \DateTime()); + $oneworkflowAudit->setData($workflowdata); + $this->entityManager->persist($oneworkflowAudit); + $this->entityManager->flush(); + } // Crée la pagination avec le Bundle Pagerfanta en fonction d'une requete private function nav_pagination($params, $orm = true) @@ -931,28 +947,26 @@ private function nav_pagination($params, $orm = true) return false; } - - // Décrypte les paramètres de connexion d'une solution - private function decrypt_params($tab_params) - { - // Instanciate object to decrypte data - $encrypter = new Encrypter(substr($this->getParameter('secret'), -16)); - if (is_array($tab_params)) { - $return_params = []; - foreach ($tab_params as $key => $value) { - if ( - is_string($value) - && !in_array($key, ['solution', 'module']) // Soe data aren't crypted - ) { - $return_params[$key] = $encrypter->decrypt($value); - } - } - - return $return_params; + + // Décrypte les paramètres de connexion d'une solution + private function decrypt_params($tab_params) + { + // Instanciate object to decrypte data + $encrypter = new Encrypter(substr($this->getParameter('secret'), -16)); + if (is_array($tab_params)) { + $return_params = []; + foreach ($tab_params as $key => $value) { + if ( + is_string($value) + && !in_array($key, ['solution', 'module']) // Soe data aren't crypted + ) { + $return_params[$key] = $encrypter->decrypt($value); } - - return $encrypter->decrypt($tab_params); } + return $return_params; + } + return $encrypter->decrypt($tab_params); + } } diff --git a/src/Controller/WorkflowController.php b/src/Controller/WorkflowController.php index 154b0227a..adfef4c56 100644 --- a/src/Controller/WorkflowController.php +++ b/src/Controller/WorkflowController.php @@ -1,4 +1,5 @@ logger = $logger; - $this->ruleManager = $ruleManager; - $this->formuleManager = $formuleManager; - $this->solutionManager = $solutionManager; - $this->documentManager = $documentManager; - $this->sessionService = $sessionService; - $this->entityManager = $entityManager; - $this->ruleRepository = $ruleRepository; - $this->jobRepository = $jobRepository; - $this->documentRepository = $documentRepository; - $this->connection = $connection; - $this->translator = $translator; - $this->authorizationChecker = $authorizationChecker; - $this->home = $home; - $this->tools = $tools; - $this->jobManager = $jobManager; - $this->template = $template; - } +/** + * @Route("/workflow") + */ +class WorkflowController extends AbstractController +{ + private FormulaManager $formuleManager; + private SessionService $sessionService; + private ParameterBagInterface $params; + private EntityManagerInterface $entityManager; + private HomeManager $home; + private ToolsManager $tools; + private TranslatorInterface $translator; + private AuthorizationCheckerInterface $authorizationChecker; + private JobManager $jobManager; + private LoggerInterface $logger; + private TemplateManager $template; + private RuleRepository $ruleRepository; + private JobRepository $jobRepository; + private DocumentRepository $documentRepository; + private SolutionManager $solutionManager; + private RuleManager $ruleManager; + private DocumentManager $documentManager; + private WorkflowLogRepository $workflowLogRepository; + + + protected Connection $connection; + // To allow sending a specific record ID to rule simulation + protected $simulationQueryField; + private ConfigRepository $configRepository; + + public function __construct( + LoggerInterface $logger, + RuleManager $ruleManager, + FormulaManager $formuleManager, + SolutionManager $solutionManager, + DocumentManager $documentManager, + SessionService $sessionService, + EntityManagerInterface $entityManager, + RuleRepository $ruleRepository, + JobRepository $jobRepository, + DocumentRepository $documentRepository, + Connection $connection, + TranslatorInterface $translator, + AuthorizationCheckerInterface $authorizationChecker, + HomeManager $home, + ToolsManager $tools, + JobManager $jobManager, + TemplateManager $template, + WorkflowLogRepository $workflowLogRepository, + ParameterBagInterface $params + ) { + $this->logger = $logger; + $this->ruleManager = $ruleManager; + $this->formuleManager = $formuleManager; + $this->solutionManager = $solutionManager; + $this->documentManager = $documentManager; + $this->sessionService = $sessionService; + $this->entityManager = $entityManager; + $this->ruleRepository = $ruleRepository; + $this->jobRepository = $jobRepository; + $this->documentRepository = $documentRepository; + $this->connection = $connection; + $this->translator = $translator; + $this->authorizationChecker = $authorizationChecker; + $this->home = $home; + $this->tools = $tools; + $this->jobManager = $jobManager; + $this->template = $template; + $this->workflowLogRepository = $workflowLogRepository; + } - protected function getInstanceBdd() - { - } + protected function getInstanceBdd() + { + } /* ****************************************************** @@ -168,13 +175,6 @@ public function WorkflowListAction(int $page = 1, Request $request) { try { - - - // $workflowLogs = $em->getRepository(WorkflowLog::class)->findBy( - // ['triggerDocument' => $id], - // ['id' => 'DESC'] - // ); - $session = $request->getSession(); $em = $this->getDoctrine()->getManager(); @@ -188,16 +188,15 @@ public function WorkflowListAction(int $page = 1, Request $request) ], false); - return $this->render( - 'Workflow/list.html.twig', - [ - 'entities' => $workflows, - 'nb_workflow' => count($workflows), - 'pager' => $compact['pager'], - ] - ); - throw $this->createNotFoundException('Error'); - + return $this->render( + 'Workflow/list.html.twig', + [ + 'entities' => $workflows, + 'nb_workflow' => count($workflows), + 'pager' => $compact['pager'], + ] + ); + throw $this->createNotFoundException('Error'); } catch (Exception $e) { throw $this->createNotFoundException('Error : ' . $e); } @@ -275,7 +274,7 @@ public function saveWorkflowAudit($workflowId) // get all the actions of the workflow $actions = $workflow->getWorkflowActions(); - $actionsArray = array_map(function($action) { + $actionsArray = array_map(function ($action) { return [ 'id' => $action->getId(), 'workflow' => $action->getWorkflow()->getId(), @@ -290,30 +289,30 @@ public function saveWorkflowAudit($workflowId) 'active' => $action->getActive(), 'deleted' => $action->getDeleted(), 'arguments' => $action->getArguments(), - ]; + ]; }, $actions->toArray()); - // Encode every workflow parameters - $workflowdata = json_encode( - [ - 'workflowName' => $workflow->getName(), - 'ruleId' => $workflow->getRule()->getId(), - 'created_by' => $workflow->getCreatedBy()->getUsername(), - 'workflowDescription' => $workflow->getDescription(), - 'condition' => $workflow->getCondition(), - 'active' => $workflow->getActive(), - 'dateCreated' => $workflow->getDateCreated()->format('Y-m-d H:i:s'), - 'dateModified' => $workflow->getDateModified()->format('Y-m-d H:i:s'), - 'actions' => $actionsArray, - ] - ); - // Save the workflow audit - $oneworkflowAudit = new WorkflowAudit(); - $oneworkflowAudit->setworkflow($workflow); - $oneworkflowAudit->setDateCreated(new \DateTime()); - $oneworkflowAudit->setData($workflowdata); - $this->entityManager->persist($oneworkflowAudit); - $this->entityManager->flush(); + // Encode every workflow parameters + $workflowdata = json_encode( + [ + 'workflowName' => $workflow->getName(), + 'ruleId' => $workflow->getRule()->getId(), + 'created_by' => $workflow->getCreatedBy()->getUsername(), + 'workflowDescription' => $workflow->getDescription(), + 'condition' => $workflow->getCondition(), + 'active' => $workflow->getActive(), + 'dateCreated' => $workflow->getDateCreated()->format('Y-m-d H:i:s'), + 'dateModified' => $workflow->getDateModified()->format('Y-m-d H:i:s'), + 'actions' => $actionsArray, + ] + ); + // Save the workflow audit + $oneworkflowAudit = new WorkflowAudit(); + $oneworkflowAudit->setworkflow($workflow); + $oneworkflowAudit->setDateCreated(new \DateTime()); + $oneworkflowAudit->setData($workflowdata); + $this->entityManager->persist($oneworkflowAudit); + $this->entityManager->flush(); } // public function to set the workflow to active or inactive @@ -373,7 +372,7 @@ public function WorkflowActiveShowAction(string $id, Request $request) public function toggleWorkflow(Request $request, EntityManagerInterface $em, WorkflowRepository $workflowRepository, string $id): JsonResponse { $workflow = $workflowRepository->find($id); - + if (!$workflow) { return new JsonResponse(['status' => 'error', 'message' => 'Workflow not found'], 404); } @@ -434,11 +433,11 @@ public function WorkflowCreateAction(Request $request) } } - // public function to show the detail view of a single workflow /** - * @Route("/show/{id}", name="workflow_show") + * @Route("/show/{id}", name="workflow_show", defaults={"page"=1}) + * @Route("/show/{id}/page-{page}", name="workflow_show_page", requirements={"page"="\d+"}) */ - public function WorkflowShowAction(string $id, Request $request) + public function WorkflowShowAction(string $id, Request $request, int $page): Response { try { $em = $this->getDoctrine()->getManager(); @@ -448,6 +447,12 @@ public function WorkflowShowAction(string $id, Request $request) ['workflow' => $id], ['dateCreated' => 'DESC'] ); + $query = $this->workflowLogRepository->findLogsByWorkflowId($id); + + $adapter = new QueryAdapter($query); + $pager = new Pagerfanta($adapter); + $pager->setMaxPerPage(10); + $pager->setCurrentPage($page); if ($workflow[0]) { $nb_workflow = count($workflowLogs); @@ -457,18 +462,17 @@ public function WorkflowShowAction(string $id, Request $request) 'workflow' => $workflow[0], 'workflowLogs' => $workflowLogs, 'nb_workflow' => $nb_workflow, + 'pager' => $pager, ] ); } else { $this->addFlash('error', 'Workflow not found'); - return $this->redirectToRoute('workflow_list'); } } catch (Exception $e) { throw $this->createNotFoundException('Error : ' . $e); } } - // public function to edit a workflow /** * @Route("/edit/{id}", name="workflow_edit") @@ -567,28 +571,26 @@ private function nav_pagination($params, $orm = true) return false; } - - // Décrypte les paramètres de connexion d'une solution - private function decrypt_params($tab_params) - { - // Instanciate object to decrypte data - $encrypter = new Encrypter(substr($this->getParameter('secret'), -16)); - if (is_array($tab_params)) { - $return_params = []; - foreach ($tab_params as $key => $value) { - if ( - is_string($value) - && !in_array($key, ['solution', 'module']) // Soe data aren't crypted - ) { - $return_params[$key] = $encrypter->decrypt($value); - } - } - - return $return_params; + + // Décrypte les paramètres de connexion d'une solution + private function decrypt_params($tab_params) + { + // Instanciate object to decrypte data + $encrypter = new Encrypter(substr($this->getParameter('secret'), -16)); + if (is_array($tab_params)) { + $return_params = []; + foreach ($tab_params as $key => $value) { + if ( + is_string($value) + && !in_array($key, ['solution', 'module']) // Soe data aren't crypted + ) { + $return_params[$key] = $encrypter->decrypt($value); } - - return $encrypter->decrypt($tab_params); } + return $return_params; + } -} \ No newline at end of file + return $encrypter->decrypt($tab_params); + } +} diff --git a/src/Repository/WorkflowLogRepository.php b/src/Repository/WorkflowLogRepository.php index b0c097096..e6eca551e 100644 --- a/src/Repository/WorkflowLogRepository.php +++ b/src/Repository/WorkflowLogRepository.php @@ -35,4 +35,14 @@ public function __construct(ManagerRegistry $registry) { parent::__construct($registry, WorkflowLog::class); } + + public function findLogsByWorkflowId(string $workflowId) + { + return $this->createQueryBuilder('wl') + ->where('wl.workflow = :workflowId') + ->setParameter('workflowId', $workflowId) + ->orderBy('wl.dateCreated', 'DESC') + ->getQuery(); + } } + diff --git a/templates/JobScheduler/crontab_list.html.twig b/templates/JobScheduler/crontab_list.html.twig index cdc830056..d637741fd 100644 --- a/templates/JobScheduler/crontab_list.html.twig +++ b/templates/JobScheduler/crontab_list.html.twig @@ -1,166 +1,230 @@ {% extends 'base.html.twig' %} -{% block titlesm %} {{ 'crontab.title'|trans }}{% endblock %} +{% block titlesm %} + {{ 'crontab.title'|trans }} +{% endblock %} {% block body %} -
    -

    {{ 'jobscheduler.total'|trans }}

    {{ entity | length }} -

    -
    -
    -

    {{ 'crontab.title_cron'|trans }}

    - {% for entityCron in entitiesCron %} - {% if entityCron.value == true %} - - {{ 'crontab.disableCron'|trans }} - - - {{ 'crontab.enableCron'|trans }} - - {% else %} - - {{ 'crontab.disableCron'|trans }} - - - {{ 'crontab.enableCron'|trans }} - - {% endif %} - {% endfor %} -

    {{'crontab.description_cron'|trans|raw}}

    -
    +
    +

    {{ 'jobscheduler.total'|trans }}

    +

    + {{ entity | length }} +

    +
    +
    +
    +

    {{ 'crontab.title_cron'|trans }}

    + {% for entityCron in entitiesCron %} + {% if entityCron.value == true %} + + {{ 'crontab.disableCron'|trans }} + + + {{ 'crontab.enableCron'|trans }} + + {% else %} + + {{ 'crontab.disableCron'|trans }} + + + {{ 'crontab.enableCron'|trans }} + + {% endif %} + {% endfor %} +

    {{'crontab.description_cron'|trans|raw}}

    +
    -
    -
    - {% for message in app.flashes('success') %} -
    - {{ message }} -
    - {% endfor %} - {% for message in app.flashes('error') %} -
    - {{ message }} -
    - {% endfor %} - - - - - - - - - - - - - - {% for value in entity %} - - - - - - - - - - - - {% endfor %} - - -
    -
    - {{ value.command|slice(0, 20) ~ '...' }} - -
    - -
    {{ value.period }}{{ value.description }}{{ value.runningInstances }}{{ value.maxInstances }}{{ value.lastUse|date("d/m/Y H:i:s", timezone) }}{{ value.nextRun|date("d/m/Y H:i:s", timezone) }}{{ value.enable }} - - - - - - - - - - - - - - - -
    -
    +
    +
    + {% for message in app.flashes('success') %} +
    + {{ message }} +
    + {% endfor %} + {% for message in app.flashes('error') %} +
    + {{ message }} +
    + {% endfor %} + + + + + + + + + + + + + + {% for value in entity %} + + + + + + + + + + + + {% endfor %} + + +
    + + + + + + + + + + + + + + + + + +
    +
    + {{ value.command|slice(0, 20) ~ '...' }} + +
    + +
    {{ value.period }}{{ value.description }}{{ value.runningInstances }}{{ value.maxInstances }}{{ value.lastUse|date("d/m/Y H:i:s", timezone) }}{{ value.nextRun|date("d/m/Y H:i:s", timezone) }}{{ value.enable }} + + + + + + + + + + + + + + + +
    +
    -
    -
    -
    {{ 'crontab.title_cronjobresult'|trans }}
    - - - - - - - - - - - - - {% for result in cronJobResults %} - - - - - - - - - - - - {% endfor %} - -
    {{ result.id }}{{ result.cronJob.id }}{{ result.runAt|date("d/m/Y H:i:s", timezone) }}{{ result.runTime }}{{ result.statusCode }} -
    - {{ result.output|slice(0, 100) ~ '...' }} - -
    - -
    {{ result.output|slice(0, 300) ~ '...' }}{{ result.createdAt|date("d/m/Y H:i:s", timezone) }}{{ result.updatedAt|date("d/m/Y H:i:s", timezone) }}
    -
    -
    +
    +
    +
    +
    {{ 'crontab.title_cronjobresult'|trans }}
    + + + + + + + + + + + + + + + + {% for result in pager.currentPageResults %} + + + + + + + + + + + {% endfor %} + +
    + + + + + + + + + + + + + + + +
    {{ result.id }}{{ result.cronJob.id }}{{ result.runAt|date('d/m/Y H:i:s') }}{{ result.runTime }}{{ result.statusCode }} +
    + ... + +
    + +
    {{ result.createdAt|date('d/m/Y H:i:s') }}{{ result.updatedAt|date('d/m/Y H:i:s') }}
    -
    -

    - {{'help.title'|trans}} - -

    -

    {{'crontab.help'|trans|raw}}

    -
    -
    -{% endblock %} \ No newline at end of file + +
    + +
    +

    + {{'help.title'|trans}} + +

    +

    {{'crontab.help'|trans|raw}}

    +
    +
    +
    +{% endblock %} diff --git a/templates/JobScheduler/show_crontab.html.twig b/templates/JobScheduler/show_crontab.html.twig index b8a883525..49f137a35 100644 --- a/templates/JobScheduler/show_crontab.html.twig +++ b/templates/JobScheduler/show_crontab.html.twig @@ -47,7 +47,7 @@ - > + {{ entity.runningInstances }} @@ -78,27 +78,24 @@
    +

    {{ 'crontab.results'|trans }}

    +
    {{ "Sorting: Default" }}
    - -

    {{ 'crontab.results'|trans }}

    -
    {{ "Sorting: Default" }}
    - - - - - - - - - + + + + + + + - {% for result in entityResult %} + {% for result in pager.currentPageResults %} - + @@ -109,10 +106,32 @@ {% endfor %}
    {{ 'jobscheduler.id'|trans }}{{ 'jobscheduler.date_created'|trans }}{{ 'jobscheduler.date_modified'|trans }}{{ 'jobscheduler.output'|trans }}{{ 'jobscheduler.run_at'|trans }}{{ 'jobscheduler.run_time'|trans }}{{ 'jobscheduler.status_code'|trans }}{{ 'jobscheduler.id'|trans }}{{ 'jobscheduler.date_created'|trans }}{{ 'jobscheduler.date_modified'|trans }}{{ 'jobscheduler.output'|trans }}{{ 'jobscheduler.run_at'|trans }}{{ 'jobscheduler.run_time'|trans }}{{ 'jobscheduler.status_code'|trans }}
    {{ result.id }}{{ result.id }} {{ result.createdAt|date('Y-m-d H:i:s') }} {{ result.updatedAt|date('Y-m-d H:i:s') }} {{ result.output|raw }}
    + {% if pager.haveToPaginate %} + + {% endif %}
    -
    - - - - {% endblock %} +
    +{% endblock %} diff --git a/templates/Workflow/show.html.twig b/templates/Workflow/show.html.twig index e4fea9bab..f7038fd59 100644 --- a/templates/Workflow/show.html.twig +++ b/templates/Workflow/show.html.twig @@ -198,48 +198,69 @@ - {% for log in workflowLogs %} + {% for log in pager.currentPageResults %} {{ log.id }} + + {{ log.workflow.id }} + + + {{ log.job.id }} + + + {% if log.triggerDocument is not null %} + {{ log.triggerDocument.id }} + {% else %} + + {% endif %} + + + {% if log.generateDocument is not null %} + {{ log.generateDocument.id }} + {% else %} + + {% endif %} + + {{ log.createdBy }} + +
    {{ log.status }}
    + + {{ log.dateCreated|date('Y-m-d H:i:s') }} + {{ log.getMessage }} + + {{ log.action.name }} + + {{ log.action.action }} - - - {{ log.workflow.id }} - - - {{ log.job.id }} - - - {% if log.triggerDocument is not null %} - {{ log.triggerDocument.id }} - {% else %} - - - {% endif %} - - - {% if log.generateDocument is not null %} - {{ log.generateDocument.id }} - {% else %} - - - {% endif %} - - {{ log.createdBy }} - -
    {{ log.status }}
    - - {{ log.dateCreated|date('Y-m-d H:i:s') }} - {{ log.getMessage }} - - - {{ log.action.name }} - - {{ log.action.action }} - - {% endfor %} - - + {% endfor %} + + + {% if pager.haveToPaginate %} + + {% endif %} +
    diff --git a/templates/WorkflowAction/edit.html.twig b/templates/WorkflowAction/edit.html.twig index 004bc3d70..6e8d60bea 100644 --- a/templates/WorkflowAction/edit.html.twig +++ b/templates/WorkflowAction/edit.html.twig @@ -59,7 +59,7 @@
    - {{ form_row(form.rule) }} + {{ form_row(form.ruleId) }}
    {{ form_row(form.to) }} diff --git a/templates/WorkflowAction/new.html.twig b/templates/WorkflowAction/new.html.twig index b4a346353..2bbbf95f8 100644 --- a/templates/WorkflowAction/new.html.twig +++ b/templates/WorkflowAction/new.html.twig @@ -59,7 +59,7 @@
    - {{ form_row(form.rule) }} + {{ form_row(form.ruleId) }}
    {{ form_row(form.to) }} diff --git a/templates/WorkflowAction/show.html.twig b/templates/WorkflowAction/show.html.twig index 67b3aa269..a5fc576d1 100644 --- a/templates/WorkflowAction/show.html.twig +++ b/templates/WorkflowAction/show.html.twig @@ -99,58 +99,87 @@ {{ 'view_workflow.logs'| trans | upper }}
    -
    - - - - - - - - - - - - - - - - - {% for log in workflowLogs %} - - - - - - - - - - - - - {% endfor %} - -
    {{ 'view_workflow.log_id'|trans }}{{ 'view_workflow.workflow'|trans }}{{ 'view_workflow.job'|trans }}{{ 'view_workflow.trigger_document'|trans }}{{ 'view_workflow.generate_document'|trans }}{{ 'view_workflow.created_by'|trans }}{{ 'view_workflow.status'|trans }}{{ 'view_workflow.date_created'|trans }}{{ 'view_workflow.message'|trans }}{{ 'view_workflow.action_type'|trans }}
    {{ log.id }} - {{ log.workflow.id }} - - {{ log.job.id }} - - {% if log.triggerDocument is not null %} - {{ log.triggerDocument.id }} - {% else %} - - {% endif %} - - {% if log.generateDocument is not null %} - {{ log.generateDocument.id }} - {% else %} - - {% endif %} - {{ log.createdBy }} -
    {{ log.status }}
    -
    {{ log.dateCreated|date('Y-m-d H:i:s') }}{{ log.getMessage }}{{ log.action.action }}
    -
    + {# templates/workflow/show.html.twig #} + +
    + + + + + + + + + + + + + + + + + {% for log in pager.currentPageResults %} + + + + + + + + + + + + + {% endfor %} + +
    {{ 'view_workflow.log_id'|trans }}{{ 'view_workflow.workflow'|trans }}{{ 'view_workflow.job'|trans }}{{ 'view_workflow.trigger_document'|trans }}{{ 'view_workflow.generate_document'|trans }}{{ 'view_workflow.created_by'|trans }}{{ 'view_workflow.status'|trans }}{{ 'view_workflow.date_created'|trans }}{{ 'view_workflow.message'|trans }}{{ 'view_workflow.action_type'|trans }}
    {{ log.id }} + {{ log.workflow.id }} + + {{ log.job.id }} + + {% if log.triggerDocument is not null %} + {{ log.triggerDocument.id }} + {% else %} + + {% endif %} + + {% if log.generateDocument is not null %} + {{ log.generateDocument.id }} + {% else %} + + {% endif %} + {{ log.createdBy }} +
    {{ log.status }}
    +
    {{ log.dateCreated|date('Y-m-d H:i:s') }}{{ log.getMessage }}{{ log.action.action }}
    + + {# Pagination links #} + {% if pager.haveToPaginate %} + + {% endif %} +
    From 23c09bc95db57c2d7a33bcd8b8db05317aa6aafc Mon Sep 17 00:00:00 2001 From: Camille Pichaud <95077335+CamillePMyddleware@users.noreply.github.com> Date: Wed, 7 Aug 2024 17:04:55 +0200 Subject: [PATCH 121/452] fix : toggle workflowAction (#1174) Co-authored-by: cpichaud --- assets/js/workflows.js | 30 ++++++++------------- src/Controller/WorkflowActionController.php | 23 ++++++++++++++++ templates/Workflow/show.html.twig | 2 +- templates/WorkflowAction/show.html.twig | 2 +- 4 files changed, 36 insertions(+), 21 deletions(-) diff --git a/assets/js/workflows.js b/assets/js/workflows.js index 18d01f742..18900438b 100644 --- a/assets/js/workflows.js +++ b/assets/js/workflows.js @@ -1,38 +1,30 @@ $(document).ready(function () { $(".form-check-input").on("change", function () { - var workflowId = $(this).data("id"); - var newState = $(this).is(":checked") ? 1 : 0; - + var $input = $(this); + var entityId = $input.data("id"); + var entityType = $input.data("type"); + var newState = $input.is(":checked") ? 1 : 0; + var pathArray = window.location.pathname.split('/'); var basePath = window.location.origin + '/' + pathArray[1] + '/' + pathArray[2]; - var currentUrl = `${basePath}/workflow/workflow/toggle/${workflowId}`; + var currentUrl = `${basePath}/${entityType}/${entityType}/toggle/${entityId}`; $.ajax({ url: currentUrl, type: "POST", data: { newState: newState }, beforeSend: function () { - console.log( - `Before sending the request for workflow ID: ${workflowId}` - ); + console.log(`Before sending the request for ${entityType} ID: ${entityId}`); }, success: function (response) { - console.log( - `Success response received for workflow ID: ${workflowId}`, - response - ); + console.log(`Success response received for ${entityType} ID: ${entityId}`, response); }, error: function (xhr, status, error) { - console.error( - `Error received for workflow ID: ${workflowId}`, - xhr, - status, - error - ); - alert("Erreur lors de la bascule du workflow"); + console.error(`Error received for ${entityType} ID: ${entityId}`, xhr, status, error); + alert("Erreur lors de la bascule"); }, complete: function (xhr, status) { - console.log(`Request completed for workflow ID: ${workflowId}`, status); + console.log(`Request completed for ${entityType} ID: ${entityId}`, status); }, }); }); diff --git a/src/Controller/WorkflowActionController.php b/src/Controller/WorkflowActionController.php index 6651a6ebe..410056b7c 100644 --- a/src/Controller/WorkflowActionController.php +++ b/src/Controller/WorkflowActionController.php @@ -70,6 +70,7 @@ use App\Repository\DocumentRepository; use Doctrine\ORM\EntityManagerInterface; use Pagerfanta\Doctrine\ORM\QueryAdapter; +use App\Repository\WorkflowActionRepository; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; @@ -969,4 +970,26 @@ private function decrypt_params($tab_params) return $encrypter->decrypt($tab_params); } + + #[Route('/workflowAction/toggle/{id}', name: 'workflow_action_toggle', methods: ['POST'])] + public function toggleWorkflowAction(Request $request, EntityManagerInterface $em, WorkflowActionRepository $workflowActionRepository, string $id): JsonResponse + { + $workflowAction = $workflowActionRepository->find($id); + + if (!$workflowAction) { + return new JsonResponse(['status' => 'error', 'message' => 'Workflow action not found'], 404); + } + + $workflowAction->setActive(!$workflowAction->getActive()); + $workflowAction->setDateModified(new \DateTime()); + + try { + $em->persist($workflowAction); + $em->flush(); + } catch (\Exception $e) { + return new JsonResponse(['status' => 'error', 'message' => 'Erreur lors de la sauvegarde du workflow action'], 500); + } + + return new JsonResponse(['status' => 'success', 'active' => $workflowAction->getActive()]); + } } diff --git a/templates/Workflow/show.html.twig b/templates/Workflow/show.html.twig index f7038fd59..e01975139 100644 --- a/templates/Workflow/show.html.twig +++ b/templates/Workflow/show.html.twig @@ -65,7 +65,7 @@
    {{ 'view_workflow.status'| trans | upper }}
    - +
    diff --git a/templates/WorkflowAction/show.html.twig b/templates/WorkflowAction/show.html.twig index a5fc576d1..304d7c07c 100644 --- a/templates/WorkflowAction/show.html.twig +++ b/templates/WorkflowAction/show.html.twig @@ -76,7 +76,7 @@
    {{ 'view_workflow.status'|trans|upper }}
    - +
    From 049e1e01fe9b6bca867622d0a3dace9b934c001a Mon Sep 17 00:00:00 2001 From: cpichaud Date: Wed, 7 Aug 2024 17:11:38 +0200 Subject: [PATCH 122/452] fix: btn toggle , listview and rule --- templates/Rule/edit/onglets/infos.html.twig | 2 +- templates/Workflow/list.html.twig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/Rule/edit/onglets/infos.html.twig b/templates/Rule/edit/onglets/infos.html.twig index 4813b507c..2db9eb2cc 100644 --- a/templates/Rule/edit/onglets/infos.html.twig +++ b/templates/Rule/edit/onglets/infos.html.twig @@ -216,7 +216,7 @@
    - +
    diff --git a/templates/Workflow/list.html.twig b/templates/Workflow/list.html.twig index 2f662991e..0db324327 100644 --- a/templates/Workflow/list.html.twig +++ b/templates/Workflow/list.html.twig @@ -67,7 +67,7 @@ {{ workflow.order }}
    - +
    From 8c4d391b7c3826fe5d0a5fe58b9403376502b14c Mon Sep 17 00:00:00 2001 From: myddleware Date: Thu, 8 Aug 2024 18:23:52 +0200 Subject: [PATCH 123/452] API : fix function deleteRecordAction Signed-off-by: myddleware --- src/Controller/ApiController.php | 78 +++++++++++++++++++------------- src/Manager/RuleManager.php | 5 ++ 2 files changed, 51 insertions(+), 32 deletions(-) diff --git a/src/Controller/ApiController.php b/src/Controller/ApiController.php index c02a25f6b..ce100c728 100644 --- a/src/Controller/ApiController.php +++ b/src/Controller/ApiController.php @@ -221,23 +221,6 @@ public function deleteRecordAction(Request $request): JsonResponse throw new Exception('recordId is missing. recordId is the id of the record you want to delete. '); } - // Set the document values - foreach ($data as $key => $value) { - switch ($key) { - case 'recordId': - $docParam['values']['id'] = $value; - break; - case 'reference': - $docParam['values']['date_modified'] = $value; - break; - case 'rule': - break; - default: - $docParam['values'][$key] = $value; - } - } - $docParam['values']['myddleware_deletion'] = true; // Force deleted record type - // Create job instance $this->jobManager->setApi(1); $this->jobManager->initJob('Delete record '.$data['recordId'].' in rule '.$data['rule']); @@ -256,6 +239,35 @@ public function deleteRecordAction(Request $request): JsonResponse $rule->setApi(1); $rule->setRule($data['rule']); + // Set the document values + foreach ($data as $key => $value) { + switch ($key) { + case 'recordId': + $docParam['values']['id'] = $value; + break; + case 'reference': + $docParam['values']['date_modified'] = $value; + break; + case 'rule': + break; + default: + $docParam['values'][$key] = $value; + } + } + + // Set all fields not set to empty + $sourceFields = $rule->getSourceFields(); + if (!empty($sourceFields)) { + foreach ($sourceFields as $sourceField) { + if (!array_key_exists($sourceField, $docParam['values'])) { + $docParam['values'][$sourceField] = ''; + } + } + } + + // Add deletion flag + $docParam['values']['myddleware_deletion'] = true; // Force deleted record type + $document = $rule->generateDocuments($data['recordId'], false, $docParam); // Stop the process if error during the data transfer creation as we won't be able to manage it in Myddleware if (!empty($document->error)) { @@ -270,19 +282,21 @@ public function deleteRecordAction(Request $request): JsonResponse return $this->json($return); } - // Send the document just created - try { - // db transaction managed into the method actionDocument - $errors = $rule->actionDocument($document[0]->id, 'rerun'); - // Check errors, but in this case the data transfer is created but Myddleware hasn't been able to send it. - // We don't roll back the work here as it will be possible to manage the data transfer in Myddleware - if (!empty($errors)) { - throw new Exception('Document in error (rule '.$data['rule'].') : '.$errors[0].'. '); - } - } catch (Exception $e) { - $this->logger->error($e->getMessage()); - $return['error'] .= $e->getMessage(); - } + // Send the document just created if requested + if (empty($data['asynchronousDeletion'])) { + try { + // db transaction managed into the method actionDocument + $errors = $rule->actionDocument($document[0]->id, 'rerun'); + // Check errors, but in this case the data transfer is created but Myddleware hasn't been able to send it. + // We don't roll back the work here as it will be possible to manage the data transfer in Myddleware + if (!empty($errors)) { + throw new Exception('Document in error (rule '.$data['rule'].') : '.$errors[0].'. '); + } + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + $return['error'] .= $e->getMessage(); + } + } // Close job if it has been created try { @@ -291,8 +305,8 @@ public function deleteRecordAction(Request $request): JsonResponse $this->jobManager->closeJob(); } // Get the job statistics even if the job has failed - if (!empty($this->jobManager->id)) { - $return['jobId'] = $this->jobManager->id; + if (!empty($this->jobManager->getId())) { + $return['jobId'] = $this->jobManager->getId(); $jobData = $this->jobManager->getLogData(); if (!empty($jobData['jobError'])) { $return['error'] .= $jobData['jobError']; diff --git a/src/Manager/RuleManager.php b/src/Manager/RuleManager.php index 4b856b6e0..1859fce80 100644 --- a/src/Manager/RuleManager.php +++ b/src/Manager/RuleManager.php @@ -170,6 +170,11 @@ public function getRule() return $this->rule; } + public function getSourceFields() + { + return $this->sourceFields; + } + public function setJobId($jobId) { $this->jobId = $jobId; From 4177b90889e0a8e74ca394d899fe31adddc9154b Mon Sep 17 00:00:00 2001 From: myddleware Date: Tue, 20 Aug 2024 12:05:57 +0200 Subject: [PATCH 124/452] ClearData job : add parameter to run only on actuve rule Signed-off-by: myddleware --- src/Command/ClearDataCommand.php | 5 ++++- src/Manager/JobManager.php | 9 ++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Command/ClearDataCommand.php b/src/Command/ClearDataCommand.php index 2cdc5d762..da60bb520 100644 --- a/src/Command/ClearDataCommand.php +++ b/src/Command/ClearDataCommand.php @@ -30,6 +30,7 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputArgument; class ClearDataCommand extends Command { @@ -51,6 +52,7 @@ protected function configure() $this ->setName('myddleware:cleardata') ->setDescription('SUPPRESSION DES DONNEES DU CLIENT') + ->addArgument('actvieRule', InputArgument::OPTIONAL, 'Clear only active rule') ; } @@ -61,6 +63,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int { // Clear message in case this task is run by jobscheduler. In this case message has to be refreshed. $data = $this->jobManager->initJob('cleardata'); + $actvieRule = $input->getArgument('actvieRule'); if (false === $data['success']) { $output->writeln('0;'.$data['message'].''); @@ -70,7 +73,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } // @TODO: this method executes SQL queries but it does not actually return anything at the moment, we need to make it return something if we want to catch this $response['message'] - $response = $this->jobManager->clearData(); + $response = $this->jobManager->clearData($actvieRule); // Display message on the console if (!empty($response['message'])) { if ($response['success']) { diff --git a/src/Manager/JobManager.php b/src/Manager/JobManager.php index 036ff5598..5479ef8b0 100644 --- a/src/Manager/JobManager.php +++ b/src/Manager/JobManager.php @@ -1063,8 +1063,12 @@ public function upgrade($output) * @throws \Doctrine\DBAL\Exception * @throws Exception */ - public function clearData() + public function clearData($actvieRule) { + $where = "ruleparam.name = 'delete'"; + if (!empty($actvieRule)) { + $where .= " AND rule.active = '1' "; + } // Récupération de chaque règle et du paramètre de temps de suppression $sqlParams = " SELECT rule.id, @@ -1074,8 +1078,7 @@ public function clearData() FROM rule INNER JOIN ruleparam ON rule.id = ruleparam.rule_id - WHERE - ruleparam.name = 'delete'"; + WHERE ".$where; $stmt = $this->connection->prepare($sqlParams); $result = $stmt->executeQuery(); $rules = $result->fetchAllAssociative(); From 636b2010d15395b40b861470184e6db1bfe8e4cc Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 21 Aug 2024 14:53:57 +0200 Subject: [PATCH 125/452] Workflow - notification : manage several recipient + add info to the message Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 20 +++++++++++++++++++- src/Manager/ToolsManager.php | 6 +++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 6bfe2d520..5fd80d904 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -2715,7 +2715,7 @@ public function runWorkflow($rerun=false) { } // Execute action depending of the function in the workflow - $arguments = unserialize($action['arguments']); + $arguments = $this->setWorkflowNotificationArguments($action); switch ($action['action']) { case 'generateDocument': $this->generateDocument($arguments['ruleId'],$this->sourceData[$arguments['searchValue']],$arguments['searchField'],$arguments['rerun'], $action); @@ -2767,6 +2767,24 @@ public function runWorkflow($rerun=false) { } } + protected function setWorkflowNotificationArguments($action) { + $arguments = unserialize($action['arguments']); + // In case of notification, we add informations on the message + if ($action['action'] == 'sendNotification') { + $arguments['message'] .= '

    Document ID : '.$this->id.'

    '; + if (!empty($this->sourceData)) { + $arguments['message'] .= 'Detail of the document :
    '; + foreach($this->sourceData as $key => $value) { + $arguments['message'] .= $key.' : '.$value.'
    '; + } + } + $arguments['message'] .= '
    Best regards
    Myddleware'; + // We transform the string to array in case there are several recipients + $arguments['to'] = explode(',',$arguments['to']); + } + return $arguments; + } + // Generate a document using the rule id and search parameters protected function generateDocument($ruleId, $searchValue = null, $searchField = 'id', $rerun = true, $action = null) { diff --git a/src/Manager/ToolsManager.php b/src/Manager/ToolsManager.php index a247e4192..8d29d0dc3 100644 --- a/src/Manager/ToolsManager.php +++ b/src/Manager/ToolsManager.php @@ -227,7 +227,11 @@ public function sendMessage($to, $subject, $message) { $sendinblue = \SendinBlue\Client\Configuration::getDefaultConfiguration()->setApiKey('api-key', $_ENV['SENDINBLUE_APIKEY']); $apiInstance = new \SendinBlue\Client\Api\TransactionalEmailsApi(new \GuzzleHttp\Client(), $sendinblue); $sendSmtpEmail = new \SendinBlue\Client\Model\SendSmtpEmail(); // \SendinBlue\Client\Model\SendSmtpEmail | Values to send a transactional email - $sendSmtpEmail['to'] = array(array('email' => $to)); + $recipients = array(); + foreach($to as $recipient) { + $recipients[] = array('email' => $recipient); + } + $sendSmtpEmail['to'] = $recipients; $sendSmtpEmail['subject'] = $subject; $sendSmtpEmail['htmlContent'] = $message; $sendSmtpEmail['sender'] = array('email' => $this->configParams['email_from'] ?? 'no-reply@myddleware.com'); From e72bd2b907d6e593f00aeb5484a742993497f715 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Mon, 26 Aug 2024 16:06:14 +0200 Subject: [PATCH 126/452] fix: specific workflow, and put name instead of id --- templates/Flux/view/view.html.twig | 2 +- templates/Workflow/show.html.twig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/Flux/view/view.html.twig b/templates/Flux/view/view.html.twig index b485be3e4..746af4993 100644 --- a/templates/Flux/view/view.html.twig +++ b/templates/Flux/view/view.html.twig @@ -789,7 +789,7 @@ {% for log in workflowLogs %} {{ log.id }} - {{ log.workflow.id }} + {{ log.workflow.name }} {{ log.job.id }} {% if log.triggerDocument is not null %} {{ log.triggerDocument.id }} diff --git a/templates/Workflow/show.html.twig b/templates/Workflow/show.html.twig index e01975139..1b62d1bf6 100644 --- a/templates/Workflow/show.html.twig +++ b/templates/Workflow/show.html.twig @@ -202,7 +202,7 @@ {{ log.id }} - {{ log.workflow.id }} + {{ log.workflow.name }} {{ log.job.id }} From 50763435f6b2dd6170f6918e8a993486a84be105 Mon Sep 17 00:00:00 2001 From: myddleware Date: Mon, 26 Aug 2024 23:18:24 +0200 Subject: [PATCH 127/452] Moodle connector : add function get_users_statistics Moodle connector : add firlds to function get_user_grades Signed-off-by: myddleware --- src/Solutions/lib/moodle/metadata.php | 12 ++++++++++++ src/Solutions/moodle.php | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Solutions/lib/moodle/metadata.php b/src/Solutions/lib/moodle/metadata.php index 22cb1a77a..387d06b11 100644 --- a/src/Solutions/lib/moodle/metadata.php +++ b/src/Solutions/lib/moodle/metadata.php @@ -87,6 +87,15 @@ 'customfields' => ['label' => 'Custom fields', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], 'timemodified' => ['label' => 'Time modified', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], ], + + 'get_users_statistics' => [ + 'id' => ['label' => 'ID', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'timemodified' => ['label' => 'Time modified', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'totallogins' => ['label' => 'Total logins', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'pagesviewed' => ['label' => 'Pages viewed', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'lastquizattempt' => ['label' => 'Last quiz attempt', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'lastaccess' => ['label' => 'Last access', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + ], 'courses' => [ 'id' => ['label' => 'ID', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], @@ -312,6 +321,9 @@ 'aggregationstatus' => ['label' => 'Aggregation status', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], 'aggregationweight' => ['label' => 'Aggregation weight', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], 'itemname' => ['label' => 'Item name', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'itemtype' => ['label' => 'Item type', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'itemmodule' => ['label' => 'Item module', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'iteminstance' => ['label' => 'Item instance', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], 'course_fullname' => ['label' => 'Course fullname', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], 'course_shortname' => ['label' => 'Course shortname', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], 'userid' => ['label' => 'User ID', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0, 'required_relationship' => 1, 'relate' => true], diff --git a/src/Solutions/moodle.php b/src/Solutions/moodle.php index dc69394d9..ddd2ca1d6 100644 --- a/src/Solutions/moodle.php +++ b/src/Solutions/moodle.php @@ -35,6 +35,7 @@ class moodlecore extends solution protected $moodleClient; protected array $required_fields = [ 'default' => ['id'], + 'get_users_statistics_by_date' => ['id', 'timemodified'], 'get_users_completion' => ['id', 'timemodified'], 'get_users_last_access' => ['id', 'lastaccess'], 'get_course_completion_by_date' => ['id', 'timecompleted'], @@ -118,6 +119,7 @@ public function get_modules($type = 'source'): array 'courses' => 'Courses', 'get_users_completion' => 'Get course activity completion', 'get_users_last_access' => 'Get users last access', + 'get_users_statistics_by_date' => 'Get users statistics', 'get_enrolments_by_date' => 'Get enrolments', 'get_course_completion_by_date' => 'Get course completion', 'get_user_compentencies_by_date' => 'Get user compentency', @@ -215,7 +217,6 @@ public function read($param): array $serverurl = $this->paramConnexion['url'].'/webservice/rest/server.php'.'?wstoken='.$this->paramConnexion['token'].'&wsfunction='.$functionName; $response = $this->moodleClient->post($serverurl, $parameters); $xml = $this->formatResponse('read', $response, $param); - if (!empty($xml->ERRORCODE)) { throw new \Exception("Error code $xml->ERRORCODE : $xml->MESSAGE. ".(!empty($xml->DEBUGINFO) ? "Info : $xml->DEBUGINFO" : "")); } From 68136f1b26feae6393588ebb4a91d7ffeec79e30 Mon Sep 17 00:00:00 2001 From: myddleware Date: Tue, 27 Aug 2024 14:54:33 +0200 Subject: [PATCH 128/452] Moodle connector : Change module name Signed-off-by: myddleware --- src/Solutions/lib/moodle/metadata.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Solutions/lib/moodle/metadata.php b/src/Solutions/lib/moodle/metadata.php index 387d06b11..c713a9e5a 100644 --- a/src/Solutions/lib/moodle/metadata.php +++ b/src/Solutions/lib/moodle/metadata.php @@ -88,7 +88,7 @@ 'timemodified' => ['label' => 'Time modified', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], ], - 'get_users_statistics' => [ + 'get_users_statistics_by_date' => [ 'id' => ['label' => 'ID', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], 'timemodified' => ['label' => 'Time modified', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], 'totallogins' => ['label' => 'Total logins', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], From 7b2d9bbd5fdd044ce6d6925a6596859d5c77b6c8 Mon Sep 17 00:00:00 2001 From: myddleware Date: Thu, 29 Aug 2024 22:39:52 +0200 Subject: [PATCH 129/452] Add workflow function changeData Rebuild method updateDocumentData Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 45 +++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 5fd80d904..3edecf45a 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -2401,29 +2401,32 @@ public function updateDocumentData(string $docId, array $newValues, string $data if (empty($documentDataEntity)) { throw new Exception("No document data found for the document ".$docId." and the type ".$dataType."."); } - // Compare data - $oldData = json_decode($documentDataEntity->getData()); - if(!empty($oldData)){ - if (!$refreshData) { - foreach ($newValues as $key => $Value) { - foreach ($oldData as $oldKey => $oldValue) { - if ($oldKey === $key) { - if ($oldValue !== $Value) { - $newValues[$oldKey] = $Value; - $this->message .= ($dataType == 'S' ? 'Source' : ($dataType == 'T' ? 'Target' : 'History')).' document value changed from '.$oldValue.' to '.$Value.'. '; - } + // Get the current values + $values = json_decode($documentDataEntity->getData(),1); + if(!empty($values)){ + // In case we replace all values + if ($refreshData) { + $values = $newValues; + $this->message .= ($dataType == 'S' ? 'Source' : ($dataType == 'T' ? 'Target' : 'History')).' document value changed by '.print_r($newValues,true).'. '; + } else { + // Replace only the new values + foreach ($newValues as $key => $value) { + if(isset($values[$key])) { + // Remove the field if code mdw_no_send_field + if (str_contains($value,'mdw_no_send_field')) { + unset($values[$key]); + $this->message .= ($dataType == 'S' ? 'Source' : ($dataType == 'T' ? 'Target' : 'History')).' field '.$key.' removed from document. '; } else { - $newValues[$oldKey] = $oldValue; + $this->message .= ($dataType == 'S' ? 'Source' : ($dataType == 'T' ? 'Target' : 'History')).' value changed from '.$values[$key].' to '.$value.'. '; + $values[$key] = $value; } } } - } else { - $this->message .= ($dataType == 'S' ? 'Source' : ($dataType == 'T' ? 'Target' : 'History')).' document value changed by '.print_r($newValues,true).'. '; - } + } $this->typeError = 'I'; $this->createDocLog(); // Update the data of the right type - $documentDataEntity->setData(json_encode($newValues, true)); + $documentDataEntity->setData(json_encode($values, true)); $this->entityManager->persist($documentDataEntity); $this->entityManager->flush(); } @@ -2735,6 +2738,16 @@ public function runWorkflow($rerun=false) { $this->updateStatus($arguments['status'], true); $this->createWorkflowLog($action, $workflowStatus, $error); break; + case 'changeData': + $workflowStatus = 'Success'; + $error = ''; + $this->typeError = 'W'; + $this->message = 'Change fields values. '; + if (!empty($arguments['fields'])) { + $this->updateDocumentData($this->id, $arguments['fields'], 'T'); + } + $this->createWorkflowLog($action, $workflowStatus, $error); + break; case 'transformDocument': $workflowStatus = 'Success'; $error = ''; From 215265796bb6cd95ec3cfb700a29bb01d29dda43 Mon Sep 17 00:00:00 2001 From: myddleware Date: Fri, 30 Aug 2024 17:04:14 +0200 Subject: [PATCH 130/452] New feature : Add variables entity Signed-off-by: myddleware --- src/Entity/Variable.php | 82 ++++++++++ src/Entity/VariableAudit.php | 222 ++++++++++++++++++++++++++ src/Manager/DocumentManager.php | 13 +- src/Manager/RuleManager.php | 20 ++- src/Repository/VariableRepository.php | 38 +++++ 5 files changed, 373 insertions(+), 2 deletions(-) create mode 100644 src/Entity/Variable.php create mode 100644 src/Entity/VariableAudit.php create mode 100644 src/Repository/VariableRepository.php diff --git a/src/Entity/Variable.php b/src/Entity/Variable.php new file mode 100644 index 000000000..6fa586600 --- /dev/null +++ b/src/Entity/Variable.php @@ -0,0 +1,82 @@ +. +*********************************************************************************/ + +namespace App\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity(repositoryClass="App\Repository\VariableRepository") + */ +class Variable +{ + /** + * @ORM\Column(name="id", type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + */ + private int $id; + + /** + * @ORM\Column(name="name", type="string", length=100, nullable=false) + */ + private string $name; + + /** + * @ORM\Column(name="value", type="text", nullable=false) + */ + private string $value; + + + public function getId(): int + { + return $this->id; + } + + public function setName($name): self + { + $this->name = $name; + + return $this; + } + + public function getName(): string + { + return $this->name; + } + + public function setValue($value): self + { + $this->value = $value; + + return $this; + } + + public function getValue(): string + { + return $this->value; + } + +} diff --git a/src/Entity/VariableAudit.php b/src/Entity/VariableAudit.php new file mode 100644 index 000000000..63521bfe7 --- /dev/null +++ b/src/Entity/VariableAudit.php @@ -0,0 +1,222 @@ +. +*********************************************************************************/ + +namespace App\Entity; + +use DateTime; +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity(repositoryClass="App\Repository\VariableAuditRepository") + * @ORM\Table(name="variableaudit", indexes={ + * @ORM\Index(name="index_variable_id", columns={"variable_id"}) + *}) + */ +class VariableAudit +{ + /** + * @var int + * + * @ORM\Column(name="id", type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + */ + private $id; + + /** + * @ORM\PrePersist() + */ + public function preSave() + { + $this->id = uniqid(); + } + + /** + * @var int + * + * @ORM\Column(name="variable_id", type="integer") + */ + private $variableId; + + /** + * @var DateTime + * + * @ORM\Column(name="modified", type="datetime", nullable=false) + */ + private $dateModified; + + /** + * @var int + * + * @ORM\Column(name="before_value", type="string", nullable=true) + */ + private $before; + + /** + * @var int + * + * @ORM\Column(name="after_value", type="string", nullable=true) + */ + private $after; + + /** + * @var string + * + * @ORM\Column(name="user", type="string", nullable=true) + */ + private $byUser; + + + /** + * Get id. + * + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Set variableId. + * + * @param string $variableId + * + * @return VariableAudit + */ + public function setVariableId($variableId) + { + $this->variableId = $variableId; + + return $this; + } + + /** + * Get variableId. + * + * @return string + */ + public function getDoc() + { + return $this->variableId; + } + + /** + * Set dateModified. + * + * @param DateTime $dateModified + * + * @return VariableAudit + */ + public function setDateModified($dateModified) + { + $this->dateModified = $dateModified; + + return $this; + } + + /** + * Get dateModified. + * + * @return DateTime + */ + public function getDateModified() + { + return $this->dateModified; + } + + /** + * Set before. + * + * @param string $before + * + * @return VariableAudit + */ + public function setBefore($before) + { + $this->before = $before; + + return $this; + } + + /** + * Get before. + * + * @return string + */ + public function getBefore() + { + return $this->before; + } + + /** + * Set after. + * + * @param string $after + * + * @return VariableAudit + */ + public function setAfter($after) + { + $this->after = $after; + + return $this; + } + + /** + * Get after. + * + * @return string + */ + public function getAfter() + { + return $this->after; + } + + /** + * Set byUser. + * + * @param string $byUser + * + * @return VariableAudit + */ + public function setByUser($byUser) + { + $this->byUser = $byUser; + + return $this; + } + + /** + * Get byUser. + * + * @return string + */ + public function getByUser() + { + return $this->byUser; + } + +} diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 3edecf45a..486ed429d 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -58,6 +58,7 @@ class documentcore protected $ruleFields; protected $ruleRelationships; protected $ruleFilters; + protected $variables; protected $ruleWorkflows; protected bool $workflowAction = false; protected $ruleParams; @@ -326,6 +327,9 @@ public function setParam($param, $clear = false, $clearRule = true) if (!empty($param['ruleWorkflows'])) { $this->ruleWorkflows = $param['ruleWorkflows']; } + if (!empty($param['variables'])) { + $this->variables = $param['variables']; + } // Init type error for each new document $this->typeError = 'S'; } catch (\Exception $e) { @@ -1613,10 +1617,17 @@ protected function updateTargetTable() public function getTransformValue($source, $ruleField) { try { - // include custom variables that could be used in the formula + // Include custom variables that could be used in the formula // TO BE REMOVED if (file_exists( __DIR__.'/../Custom/Utils/myddlewareVariables.php')) { include __DIR__.'/../Custom/Utils/myddlewareVariables.php'; } + // Include variable in the database + if (!empty($this->variables)) { + foreach($this->variables as $key => $value) { + $fieldNameDyn = 'mydvar_'.$key; + $$fieldNameDyn = $value; + } + } // Manage formula if (!empty($ruleField['formula'])) { // -- -- -- Formula management diff --git a/src/Manager/RuleManager.php b/src/Manager/RuleManager.php index 1859fce80..6fa3f7840 100644 --- a/src/Manager/RuleManager.php +++ b/src/Manager/RuleManager.php @@ -31,6 +31,7 @@ use App\Entity\Rule; use App\Entity\RuleParam; use App\Entity\RuleParamAudit as RuleParamAudit; +use App\Entity\Variable; use App\Repository\DocumentRepository; use App\Repository\RuleOrderRepository; use App\Repository\RuleRelationShipRepository; @@ -59,6 +60,7 @@ class rulecore protected $rule; protected $ruleFields; protected $ruleParams; + protected $variables; protected $sourceFields; protected $targetFields; protected $ruleRelationships; @@ -162,6 +164,7 @@ public function setRule($idRule) $this->setRuleWorkflows(); // Set the rule fields (we use the name_slug in $this->rule) $this->setRuleField(); + $this->setVariable(); } } @@ -848,7 +851,6 @@ public function transformDocuments($documents = null): array { // Permet de charger dans la classe toutes les relations de la règle $response = []; - // Sélection de tous les docuements de la règle au statut 'New' si aucun document n'est en paramètre if (empty($documents)) { $documents = $this->selectDocuments('Relate_OK'); @@ -857,6 +859,7 @@ public function transformDocuments($documents = null): array $param['ruleFields'] = $this->ruleFields; $param['ruleRelationships'] = $this->ruleRelationships; $param['ruleWorkflows'] = $this->ruleWorkflows; + $param['variables'] = $this->variables; $param['jobId'] = $this->jobId; $param['api'] = $this->api; // Set all config parameters @@ -2201,6 +2204,21 @@ protected function setRuleParam() } } + // Set variable from the database + protected function setVariable() + { + try { + $variablesEntity = $this->entityManager->getRepository(Variable::class)->findAll(); + if (!empty($variablesEntity)) { + foreach ($variablesEntity as $variable) { + $this->variables[$variable->getName()] = $variable->getvalue(); + } + } + } catch (\Exception $e) { + $this->logger->error('Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + } + } + // Permet de charger toutes les relations de la règle protected function setRuleRelationships() { diff --git a/src/Repository/VariableRepository.php b/src/Repository/VariableRepository.php new file mode 100644 index 000000000..aa8c1528d --- /dev/null +++ b/src/Repository/VariableRepository.php @@ -0,0 +1,38 @@ +. +*********************************************************************************/ + +namespace App\Repository; + +use App\Entity\Variable; +use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\Persistence\ManagerRegistry; + +class VariableRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Variable::class); + } +} From 7d06768724c52ba312fa09e919a46d8377b6dd51 Mon Sep 17 00:00:00 2001 From: myddleware Date: Thu, 5 Sep 2024 15:54:24 +0200 Subject: [PATCH 131/452] Lookup add parameter to force direction Signed-off-by: myddleware --- src/Manager/FormulaFunctionManager.php | 6 ++++-- src/Solutions/moodle.php | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Manager/FormulaFunctionManager.php b/src/Manager/FormulaFunctionManager.php index 10c70a2ec..8cd4241aa 100644 --- a/src/Manager/FormulaFunctionManager.php +++ b/src/Manager/FormulaFunctionManager.php @@ -122,7 +122,7 @@ public static function getValueFromArray($key, $array) } } - public static function lookup($entityManager, $connection, $currentRule, $docId, $myddlewareUserId, $sourceFieldName, $field, $rule, $errorIfEmpty=false, $errodIfNoFound=true, $parent=false) + public static function lookup($entityManager, $connection, $currentRule, $docId, $myddlewareUserId, $sourceFieldName, $field, $rule, $errorIfEmpty=false, $errodIfNoFound=true, $parent=false, $forceDirection=null) { // Manage error if empty if (empty($field)) { @@ -134,7 +134,9 @@ public static function lookup($entityManager, $connection, $currentRule, $docId, } // In case of simulation during rule creation (not edition), we don't have the current rule id. // We set direction = 1 by default - if ($currentRule === 0) { + if ($forceDirection !== null) { + $direction = $forceDirection; + } elseif ($currentRule === 0) { $direction = 1; } else { // Get rules detail diff --git a/src/Solutions/moodle.php b/src/Solutions/moodle.php index ddd2ca1d6..6e9e5075c 100644 --- a/src/Solutions/moodle.php +++ b/src/Solutions/moodle.php @@ -267,7 +267,7 @@ public function read($param): array } } } catch (\Exception $e) { - $this->logger->error($result['error']); + $this->logger->error('Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); throw new \Exception('Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); } return $result; From b8d579e404e709f4cacbe1d6c59662697bc5a361 Mon Sep 17 00:00:00 2001 From: myddleware Date: Thu, 5 Sep 2024 20:50:25 +0200 Subject: [PATCH 132/452] Moodle connector : add module get_quiz_attempts Signed-off-by: myddleware --- src/Solutions/lib/moodle/metadata.php | 18 ++++++++++++++++++ src/Solutions/moodle.php | 2 ++ 2 files changed, 20 insertions(+) diff --git a/src/Solutions/lib/moodle/metadata.php b/src/Solutions/lib/moodle/metadata.php index c713a9e5a..89ebeef06 100644 --- a/src/Solutions/lib/moodle/metadata.php +++ b/src/Solutions/lib/moodle/metadata.php @@ -336,4 +336,22 @@ 'userid' => ['label' => 'User ID', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0, 'required_relationship' => 1, 'relate' => true], 'timeadded' => ['label' => 'Time added', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], ], + + 'get_quiz_attempts' => [ + 'id' => ['label' => 'ID', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'userid' => ['label' => 'User ID', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0, 'required_relationship' => 1, 'relate' => true], + 'attempt' => ['label' => 'Attempt', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'state' => ['label' => 'State', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'timefinish' => ['label' => 'Time finish', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'timemodified' => ['label' => 'Time modified', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'timemodifiedoffline' => ['label' => 'Time modified offline', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'timecheckstate' => ['label' => 'Time check state', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'sumgrades' => ['label' => 'Sum grades', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'gradednotificationsenttime' => ['label' => 'Graded notification sent time', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'uniqueid' => ['label' => 'Unique ID', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'quizid' => ['label' => 'Quiz ID', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'courseid' => ['label' => 'Course ID', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'name' => ['label' => 'Name', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'grade' => ['label' => 'Grade', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + ], ]; diff --git a/src/Solutions/moodle.php b/src/Solutions/moodle.php index 6e9e5075c..7e6444855 100644 --- a/src/Solutions/moodle.php +++ b/src/Solutions/moodle.php @@ -40,6 +40,7 @@ class moodlecore extends solution 'get_users_last_access' => ['id', 'lastaccess'], 'get_course_completion_by_date' => ['id', 'timecompleted'], 'get_user_grades' => ['id', 'timemodified'], + 'get_quiz_attempts' => ['id', 'timemodified'], 'groups' => ['id', 'timemodified'], 'group_members' => ['id', 'timeadded'], ]; @@ -125,6 +126,7 @@ public function get_modules($type = 'source'): array 'get_user_compentencies_by_date' => 'Get user compentency', 'get_competency_module_completion_by_date' => 'Get compentency module completion', 'get_user_grades' => 'Get user grades', + 'get_quiz_attempts' => 'Get quiz attempts', 'groups' => 'Groups', 'group_members' => 'Group members', ]; From bd8f91a62b366e8478a86c6bb2193b0ab2e59381 Mon Sep 17 00:00:00 2001 From: myddleware Date: Mon, 9 Sep 2024 17:35:04 +0200 Subject: [PATCH 133/452] Moodle add fields to user statistics module Signed-off-by: myddleware --- src/Solutions/lib/moodle/metadata.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Solutions/lib/moodle/metadata.php b/src/Solutions/lib/moodle/metadata.php index 89ebeef06..a55b29843 100644 --- a/src/Solutions/lib/moodle/metadata.php +++ b/src/Solutions/lib/moodle/metadata.php @@ -90,6 +90,9 @@ 'get_users_statistics_by_date' => [ 'id' => ['label' => 'ID', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'username' => ['label' => 'Username', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 1], + 'lastname' => ['label' => 'Lastname', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 1], + 'email' => ['label' => 'Email', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 1], 'timemodified' => ['label' => 'Time modified', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], 'totallogins' => ['label' => 'Total logins', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], 'pagesviewed' => ['label' => 'Pages viewed', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], From 85420319bbd5d92722769dd4b1e000ee82fee31b Mon Sep 17 00:00:00 2001 From: myddleware Date: Tue, 10 Sep 2024 10:20:04 +0200 Subject: [PATCH 134/452] Change prefix variable Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 486ed429d..6ce5cc694 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -1624,7 +1624,7 @@ public function getTransformValue($source, $ruleField) // Include variable in the database if (!empty($this->variables)) { foreach($this->variables as $key => $value) { - $fieldNameDyn = 'mydvar_'.$key; + $fieldNameDyn = 'mdwvar_'.$key; $$fieldNameDyn = $value; } } From 94b61a8ce3f11541f8a6f61b74deca2429338865 Mon Sep 17 00:00:00 2001 From: Camille Pichaud <95077335+CamillePMyddleware@users.noreply.github.com> Date: Tue, 10 Sep 2024 13:11:47 +0200 Subject: [PATCH 135/452] New workflow (#1177) * modify title and workflow in rule info * add: sorted workflow * fix: label add change rule field * add: select2 and new field for globalStatus * add filter --------- Co-authored-by: cpichaud --- assets/app.js | 3 + assets/css/flux.css | 27 ++ assets/css/rule.css | 7 +- assets/css/task.css | 3 + assets/css/workflow.css | 9 + assets/js/filter.js | 9 + assets/js/workflows.js | 89 +++- package.json | 1 + src/Controller/WorkflowActionController.php | 21 +- src/Controller/WorkflowController.php | 43 +- src/Form/Filter/DocFilterType.php | 2 +- src/Form/Type/WorkflowActionType.php | 2 +- src/Form/Type/WorkflowType.php | 2 +- templates/Rule/edit/onglets/infos.html.twig | 461 ++++++++++--------- templates/Workflow/_workflow_table.html.twig | 49 ++ templates/Workflow/edit.html.twig | 2 +- templates/Workflow/list.html.twig | 125 +++-- templates/Workflow/new.html.twig | 2 +- templates/Workflow/show.html.twig | 6 +- templates/WorkflowAction/show.html.twig | 4 +- templates/testFilter.html.twig | 108 +++-- translations/messages.en.yml | 6 + translations/messages.fr.yml | 6 + yarn.lock | 5 + 24 files changed, 626 insertions(+), 366 deletions(-) create mode 100644 templates/Workflow/_workflow_table.html.twig diff --git a/assets/app.js b/assets/app.js index cb741e34c..bec6b4481 100755 --- a/assets/app.js +++ b/assets/app.js @@ -50,3 +50,6 @@ require("./js/workflows.js"); // start the Stimulus application import './bootstrap'; +import 'select2/dist/css/select2.css'; +import 'select2'; + diff --git a/assets/css/flux.css b/assets/css/flux.css index b16ff63ed..49a7e437d 100755 --- a/assets/css/flux.css +++ b/assets/css/flux.css @@ -169,3 +169,30 @@ li span { /* NEW FILTER */ +.select2-container--default .select2-selection--multiple .select2-selection__choice { + background-color: #198bca !important; + border: 1px solid #198bca !important; + color: white !important; + /* margin: 6px !important; */ +} + +.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{ + border-right: 0px solid #198bca; + color: white !important; +} + +#globalStatus label{ + margin-bottom: 8px; +} + +.select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple { + border: 1px solid #ced4da !important; +} + +.select2-container--default.select2-container--focus .select2-selection--multiple{ + border: 1px solid #ced4da; +} + +.select2-container--default .select2-search--inline .select2-search__field{ + margin: 6px; +} \ No newline at end of file diff --git a/assets/css/rule.css b/assets/css/rule.css index 9eecf2b2f..39fd3842b 100755 --- a/assets/css/rule.css +++ b/assets/css/rule.css @@ -553,4 +553,9 @@ color: #000000!important; .workflow_status.form-check-input { margin-left: 7px !important; -} \ No newline at end of file +} + + +/* FORM SEARCH WORKFLOW */ + + diff --git a/assets/css/task.css b/assets/css/task.css index f6018b6ef..802746beb 100755 --- a/assets/css/task.css +++ b/assets/css/task.css @@ -75,6 +75,9 @@ #task table a, #task table a:hover, #workflows table a, #workflows table a:hover { color: #0F66A9; + text-decoration: none; +} +#workflows table a:hover{ font-weight: bold; } diff --git a/assets/css/workflow.css b/assets/css/workflow.css index fbb5042f9..9d19edc68 100644 --- a/assets/css/workflow.css +++ b/assets/css/workflow.css @@ -50,4 +50,13 @@ font-size: 1rem; } +.card-title-workflow{ + font-size: x-large; + font-weight: 700; +} + +.text-left{ + text-align: left !important; +} + diff --git a/assets/js/filter.js b/assets/js/filter.js index deeee8100..2f418e3c6 100644 --- a/assets/js/filter.js +++ b/assets/js/filter.js @@ -242,3 +242,12 @@ function rearrangeFilters() { $(filter).appendTo("#filters-container"); }); } + +$(document).ready(function() { + $('#globalStatus select').select2({ + placeholder: "Select Global Status", + allowClear: true, + width: 'resolve' + }); +}); + diff --git a/assets/js/workflows.js b/assets/js/workflows.js index 18900438b..4c91cf4f0 100644 --- a/assets/js/workflows.js +++ b/assets/js/workflows.js @@ -1,12 +1,41 @@ $(document).ready(function () { + + function fetchFilteredData() { + var workflowName = $('#workflow_name').val(); + var ruleName = $('#rule_name').val(); + + + $.ajax({ + url: workflowListUrl, + type: 'GET', + data: { + workflow_name: workflowName, + rule_name: ruleName + }, + success: function (response) { + console.log("Réponse reçue :", response); + $('#workflowTableContainer').html(response); + + // $('#workflowTableContainer').html($(response).find('#workflowTableContainer').html()); + }, + error: function (xhr, status, error) { + console.error("Erreur lors de la recherche :", status, error); + } + }); + } + + $('#workflow_name, #rule_name').on('keyup', function () { + fetchFilteredData(); + }); + $(".form-check-input").on("change", function () { var $input = $(this); var entityId = $input.data("id"); var entityType = $input.data("type"); var newState = $input.is(":checked") ? 1 : 0; - var pathArray = window.location.pathname.split('/'); - var basePath = window.location.origin + '/' + pathArray[1] + '/' + pathArray[2]; + var pathArray = window.location.pathname.split("/"); + var basePath = window.location.origin + "/" + pathArray[1] + "/" + pathArray[2]; var currentUrl = `${basePath}/${entityType}/${entityType}/toggle/${entityId}`; $.ajax({ @@ -28,6 +57,51 @@ $(document).ready(function () { }, }); }); + + + $(".form-check-input").on("change", function () { + var $input = $(this); + var entityId = $input.data("id"); + var entityType = $input.data("type"); + var newState = $input.is(":checked") ? 1 : 0; + + var pathArray = window.location.pathname.split("/"); + var basePath = + window.location.origin + "/" + pathArray[1] + "/" + pathArray[2]; + var currentUrl = `${basePath}/${entityType}/${entityType}/toggle/${entityId}`; + + $.ajax({ + url: currentUrl, + type: "POST", + data: { newState: newState }, + beforeSend: function () { + console.log( + `Before sending the request for ${entityType} ID: ${entityId}` + ); + }, + success: function (response) { + console.log( + `Success response received for ${entityType} ID: ${entityId}`, + response + ); + }, + error: function (xhr, status, error) { + console.error( + `Error received for ${entityType} ID: ${entityId}`, + xhr, + status, + error + ); + alert("Erreur lors de la bascule"); + }, + complete: function (xhr, status) { + console.log( + `Request completed for ${entityType} ID: ${entityId}`, + status + ); + }, + }); + }); }); document.addEventListener("DOMContentLoaded", function () { @@ -80,4 +154,15 @@ document.addEventListener("DOMContentLoaded", function () { toggleIcon(logsToggleButton, logsCollapseContent); }); } + + document.addEventListener("DOMContentLoaded", function () { + var tooltipTriggerList = [].slice.call( + document.querySelectorAll('[data-toggle="tooltip"]') + ); + var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { + return new bootstrap.Tooltip(tooltipTriggerEl); + }); + }); + + }); diff --git a/package.json b/package.json index f44d46af2..b0ef25097 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "jquery": "3.6.0", "jquery-ui": "^1.13.2", "popper.js": "^1.16.1", + "select2": "^4.1.0-rc.0", "yarn": "^1.22.17" } } diff --git a/src/Controller/WorkflowActionController.php b/src/Controller/WorkflowActionController.php index 410056b7c..e47eb2e29 100644 --- a/src/Controller/WorkflowActionController.php +++ b/src/Controller/WorkflowActionController.php @@ -155,9 +155,7 @@ public function __construct( $this->template = $template; } - protected function getInstanceBdd() - { - } + protected function getInstanceBdd() {} // public function to delet the workflow by id (set deleted to 1) @@ -355,7 +353,7 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re ]) ->add('ruleId', EntityType::class, [ 'class' => Rule::class, - 'choices' => $em->getRepository(Rule::class)->findBy(['active' => true]), + 'choices' => $em->getRepository(Rule::class)->findBy(['deleted' => 0]), 'choice_label' => 'name', 'choice_value' => 'id', 'required' => false, @@ -413,8 +411,6 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re if ($form->isSubmitted() && $form->isValid()) { - - $workflowAction->setModifiedBy($this->getUser()); $action = $form->get('action')->getData(); @@ -434,8 +430,7 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re $active = $form->get('active')->getData(); $workflowAction->setActive($active); - - + // get the to, the subject, and the message using getdata $arguments = []; $to = $form->get('to')->getData(); @@ -657,7 +652,7 @@ public function WorkflowActionEditAction(string $id, Request $request) ]) ->add('ruleId', EntityType::class, [ 'class' => Rule::class, - 'choices' => $em->getRepository(Rule::class)->findBy(['active' => true]), + 'choices' => $em->getRepository(Rule::class)->findBy(['deleted' => 0]), 'choice_label' => 'name', 'choice_value' => 'id', 'required' => false, @@ -975,21 +970,21 @@ private function decrypt_params($tab_params) public function toggleWorkflowAction(Request $request, EntityManagerInterface $em, WorkflowActionRepository $workflowActionRepository, string $id): JsonResponse { $workflowAction = $workflowActionRepository->find($id); - + if (!$workflowAction) { return new JsonResponse(['status' => 'error', 'message' => 'Workflow action not found'], 404); } - + $workflowAction->setActive(!$workflowAction->getActive()); $workflowAction->setDateModified(new \DateTime()); - + try { $em->persist($workflowAction); $em->flush(); } catch (\Exception $e) { return new JsonResponse(['status' => 'error', 'message' => 'Erreur lors de la sauvegarde du workflow action'], 500); } - + return new JsonResponse(['status' => 'success', 'active' => $workflowAction->getActive()]); } } diff --git a/src/Controller/WorkflowController.php b/src/Controller/WorkflowController.php index adfef4c56..01f0f8b7a 100644 --- a/src/Controller/WorkflowController.php +++ b/src/Controller/WorkflowController.php @@ -174,20 +174,47 @@ protected function getInstanceBdd() public function WorkflowListAction(int $page = 1, Request $request) { try { - $session = $request->getSession(); $em = $this->getDoctrine()->getManager(); - - $workflows = $em->getRepository(Workflow::class)->findBy(['deleted' => 0], ['order' => 'ASC']); - - // List of task limited to 1000 and rder by status (start first) and date begin + + // Récupérer les filtres depuis la requête + $workflowName = $request->query->get('workflow_name'); + $ruleName = $request->query->get('rule_name'); + + // Modifier la requête pour inclure les filtres + $queryBuilder = $em->getRepository(Workflow::class)->createQueryBuilder('w') + ->where('w.deleted = 0'); + + if ($workflowName) { + $queryBuilder->andWhere('w.name LIKE :workflowName') + ->setParameter('workflowName', '%' . $workflowName . '%'); + } + + if ($ruleName) { + $queryBuilder->join('w.rule', 'r') + ->andWhere('r.name LIKE :ruleName') + ->setParameter('ruleName', '%' . $ruleName . '%'); + } + + $queryBuilder->orderBy('w.order', 'ASC'); + $workflows = $queryBuilder->getQuery()->getResult(); + + // Pagination $compact = $this->nav_pagination([ 'adapter_em_repository' => $workflows, 'maxPerPage' => $this->params['pager'] ?? 25, 'page' => $page, ], false); - - + + // check if the request is an AJAX request + if ($request->isXmlHttpRequest()) { + return $this->render('Workflow/_workflow_table.html.twig', [ + 'entities' => $workflows, + 'pager' => $compact['pager'], + ]); + } + + // if not an AJAX request, render the page return $this->render( 'Workflow/list.html.twig', [ @@ -196,7 +223,7 @@ public function WorkflowListAction(int $page = 1, Request $request) 'pager' => $compact['pager'], ] ); - throw $this->createNotFoundException('Error'); + } catch (Exception $e) { throw $this->createNotFoundException('Error : ' . $e); } diff --git a/src/Form/Filter/DocFilterType.php b/src/Form/Filter/DocFilterType.php index 31ed06f77..4b2700b83 100644 --- a/src/Form/Filter/DocFilterType.php +++ b/src/Form/Filter/DocFilterType.php @@ -29,7 +29,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) ->add('globalStatus',Filters\ChoiceFilterType::class, [ 'choices' => DocumentManager::lstGblStatus(), 'attr' => [ - 'class' => 'form-control mt-2', + 'class' => 'form-control mt-2 select2', 'id' => 'globalStatus', 'placeholder' => 'Global Status', ], diff --git a/src/Form/Type/WorkflowActionType.php b/src/Form/Type/WorkflowActionType.php index 9569adc7a..802fe06d4 100644 --- a/src/Form/Type/WorkflowActionType.php +++ b/src/Form/Type/WorkflowActionType.php @@ -34,7 +34,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) ]) ->add('rule', EntityType::class, [ 'class' => Rule::class, - 'choices' => $options['entityManager']->getRepository(Rule::class)->findBy(['active' => true]), + 'choices' => $options['entityManager']->getRepository(Rule::class)->findBy(['deleted' => 0]), 'choice_label' => 'name', 'choice_value' => 'id', 'constraints' => [ diff --git a/src/Form/Type/WorkflowType.php b/src/Form/Type/WorkflowType.php index 6d1492615..eaa220655 100644 --- a/src/Form/Type/WorkflowType.php +++ b/src/Form/Type/WorkflowType.php @@ -45,7 +45,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) $builder->add('description', TextType::class, ['label' => 'Description']); $builder->add('Rule', EntityType::class, [ 'class' => Rule::class, - 'choices' => $options['entityManager']->getRepository(Rule::class)->findBy(['active' => true]), + 'choices' => $options['entityManager']->getRepository(Rule::class)->findBy(['deleted' => 0]), 'choice_label' => 'name', 'choice_value' => 'id', 'constraints' => [ diff --git a/templates/Rule/edit/onglets/infos.html.twig b/templates/Rule/edit/onglets/infos.html.twig index 2db9eb2cc..621f0ed17 100644 --- a/templates/Rule/edit/onglets/infos.html.twig +++ b/templates/Rule/edit/onglets/infos.html.twig @@ -20,230 +20,247 @@ You should have received a copy of the GNU General Public License along with Myddleware. If not, see . -*********************************************************************************/ #} +*********************************************************************************/ #} {% block body %} - + - + -
    -
    -
    - {# TABLE RULE #} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {% if rule.getReadJobLock is not empty %} - - - - - {% endif %} - {% if params_suite.bidirectional is defined and params_suite.bidirectional is not empty %} - - - - - {% endif %} - {% if duplicate_target is defined and duplicate_target is not empty %} - - - - - {% endif %} - {% if params_suite.customParams is defined and params_suite.customParams is not empty %} - {% for r in params_suite.customParams %} - - - - - {% endfor %} - {% endif %} -
    {{ 'view_rule.title.rule' | trans }}
    {{ 'view_rule.infos.active' | trans }} -
    - - -
    -
    {{ 'view_rule.infos.id' | trans }}{{ rule.GetId }}
    {{ 'view_rule.infos.name' | trans }}{{ rule.GetName }}
    {{ 'view_rule.infos.datecreated' | trans }}{{ rule.GetDateCreated | date("d-m-Y H:i:s") }}
    {{ 'view_rule.infos.datemodified' | trans }}{{ rule.GetDateModified | date("d-m-Y H:i:s") }}
    {{ 'view_rule.infos.mode' | trans }}{{ params_suite.mode }}
    {{ 'view_rule.infos.read_job_lock' | trans }} - -
    {{ 'view_rule.infos.bidirectional' | trans }}{{ params_suite.bidirectionalName }}
    {{ 'view_rule.infos.duplicate_fields' | trans }}{{ duplicate_target }}
    {{ r.name }}{{ r.value }}
    -
    - {# TABLE CONNECTOR #} -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    {{ 'view_rule.title.connector' | trans }}
    {{ 'view_rule.title.source' | trans }}{{ 'view_rule.title.target' | trans }}
    {{ 'view_rule.connector.title' | trans }}{{ connector.lbl_source }}{{ 'view_rule.connector.title' | trans }}{{ connector.lbl_target }}
    {{ 'view_rule.connector.solution' | trans }} -

    {{ connector.solution_source }}

    - {{ connector.solution_source }} -
    {{ 'view_rule.connector.solution' | trans }} -

    {{ connector.solution_target }}

    - {{ connector.solution_target }} -
    {{ 'view_rule.connector.module' | trans }}{{ rule.getModuleSource }}{{ 'view_rule.connector.module' | trans }} {{ rule.getModuleTarget }}
    -
    -
    -
    -
    -
    -
    - {% for param in rule.params %} - {% if param.name == 'description' %} -
    -

    {{ 'solution.params.description' | trans }}

    -
    {{ param.value }}
    - - -
    - {% endif %} - {% endfor %} -
    - {# TABLE WORKFLOW #} -
    - - - - - - - -
    Workflows
    - {% if hasWorkflows %} - - {% for workflow in workflows %} - - - - - {% endfor %} -
    - - {{ workflow.name }} - - -
    - -
    -
    - {% else %} - No workflow - {% endif %} -
    -
    -
    +
    +
    +
    + {# TABLE RULE #} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% if rule.getReadJobLock is not empty %} + + + + + {% endif %} + {% if params_suite.bidirectional is defined and params_suite.bidirectional is not empty %} + + + + + {% endif %} + {% if duplicate_target is defined and duplicate_target is not empty %} + + + + + {% endif %} + {% if params_suite.customParams is defined and params_suite.customParams is not empty %} + {% for r in params_suite.customParams %} + + + + + {% endfor %} + {% endif %} +
    {{ 'view_rule.title.rule' | trans }}
    {{ 'view_rule.infos.active' | trans }} +
    + + +
    +
    {{ 'view_rule.infos.id' | trans }}
    {{ rule.GetId }}
    {{ 'view_rule.infos.name' | trans }}{{ rule.GetName }}
    {{ 'view_rule.infos.datecreated' | trans }}{{ rule.GetDateCreated | date("d-m-Y H:i:s") }}
    {{ 'view_rule.infos.datemodified' | trans }}{{ rule.GetDateModified | date("d-m-Y H:i:s") }}
    {{ 'view_rule.infos.mode' | trans }} + {{ params_suite.mode }} +
    {{ 'view_rule.infos.read_job_lock' | trans }} + +
    {{ 'view_rule.infos.bidirectional' | trans }} + {{ params_suite.bidirectionalName }} +
    {{ 'view_rule.infos.duplicate_fields' | trans }} + {{ duplicate_target }} +
    {{ r.name }} + {{ r.value }} +
    -
    -

    - {{ 'help.title' | trans }} - -

    -

    {{ 'help.viewrule.info' | trans | raw }}

    +{# TABLE CONNECTOR #} +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    {{ 'view_rule.title.connector' | trans }}
    {{ 'view_rule.title.source' | trans }}{{ 'view_rule.title.target' | trans }}
    {{ 'view_rule.connector.title' | trans }} + {{ connector.lbl_source }} + {{ 'view_rule.connector.title' | trans }} + {{ connector.lbl_target }} +
    {{ 'view_rule.connector.solution' | trans }} +

    {{ connector.solution_source }}

    + {{ connector.solution_source }} +
    {{ 'view_rule.connector.solution' | trans }} +

    {{ connector.solution_target }}

    + {{ connector.solution_target }} +
    {{ 'view_rule.connector.module' | trans }}{{ rule.getModuleSource }}{{ 'view_rule.connector.module' | trans }} + {{ rule.getModuleTarget }}
    +
    +
    + {% for param in rule.params %} + {% if param.name == 'description' %} +
    +

    {{ 'solution.params.description' | trans }}

    +
    {{ param.value }}
    + + +
    + {% endif %} + {% endfor %}
    -{% block javascripts %} - {{ encore_entry_script_tags('fiche') }} -{% endblock %} - -{% endblock %} +{# TABLE WORKFLOW #} +
    + + + + + + + +
    {{ 'view_rule.infos.workflows' | trans }}
    + {% if hasWorkflows %} + {% set sorted_workflows = workflows|sort((a, b) => b.order <=> a.order) %} + + + + + + + + + + + {% for workflow in sorted_workflows %} + + + + + + {% endfor %} + +
    {{ 'view_rule.infos.name' | trans }}{{ 'view_rule.infos.order' | trans }}{{ 'view_rule.infos.status' | trans }}
    + + {{ workflow.name }} + + + {{ workflow.order }} + +
    + +
    +
    + {% else %} + No workflow + {% endif %} +
    +

    +{{ 'help.title' | trans }} +

    {{ 'help.viewrule.info' | trans | raw }}

    {% block javascripts %}{{ encore_entry_script_tags('fiche') }}{% endblock %}{% endblock %} diff --git a/templates/Workflow/_workflow_table.html.twig b/templates/Workflow/_workflow_table.html.twig new file mode 100644 index 000000000..454d27e58 --- /dev/null +++ b/templates/Workflow/_workflow_table.html.twig @@ -0,0 +1,49 @@ + + {% if entities is not empty %} + + + + + + + + + + {% for workflow in entities %} + + + + + + + + + + {% endfor %} + {% endif %} +
    {{ 'list_workflow.th.name'|trans }}{{ 'list_workflow.th.ruleName'|trans }}{{ 'list_workflow.th.description'|trans }}{{ 'list_workflow.th.condition'|trans }}{{ 'list_workflow.th.order'|trans }}{{ 'list_workflow.th.active'|trans }}{{ 'list_workflow.th.option'|trans }}
    + {{ workflow.name }} + + + {{ workflow.rule.name }} + {{ workflow.description }}{{ workflow.condition }}{{ workflow.order }} +
    + +
    +
    + + + + + + + + + + + + + + + +
    diff --git a/templates/Workflow/edit.html.twig b/templates/Workflow/edit.html.twig index 8d8fbee28..fbe07d8b0 100644 --- a/templates/Workflow/edit.html.twig +++ b/templates/Workflow/edit.html.twig @@ -20,7 +20,7 @@ {{ 'view_workflow.delete'| trans }}
    {{ form_start(form) }}
    -
    +
    {{ 'view_workflow.edit'| trans }}
    diff --git a/templates/Workflow/list.html.twig b/templates/Workflow/list.html.twig index 0db324327..e665027bc 100644 --- a/templates/Workflow/list.html.twig +++ b/templates/Workflow/list.html.twig @@ -20,77 +20,70 @@ You should have received a copy of the GNU General Public License along with Myddleware. If not, see . -*********************************************************************************/ #} +*********************************************************************************/ #} {% extends 'base.html.twig' %} -{% block title %}{{parent()}} | {{'title.workflow.list'|trans}}{% endblock %} -{% block titlesm %}{{'title.workflow.list'|trans}}{% endblock titlesm %} -{% block body %} +{% block title %} + {{parent()}} + | + {{'title.workflow.list'|trans}} +{% endblock %} +{% block titlesm %} + {{'title.workflow.list'|trans}} +{% endblock titlesm %} +{% block body %}
    - - -
    - {% for message in app.flashes('success') %} - - {# ------------- PARAMETRES JQUERY ------------- #} - - {# ------------- PARAMETRES JQUERY ------------- #} + {# ------------- PARAMETRES JQUERY ------------- #} + + {# ------------- PARAMETRES JQUERY ------------- #} {% endblock %} {% block cssin %}{% endblock cssin %} - diff --git a/templates/Workflow/new.html.twig b/templates/Workflow/new.html.twig index 42ac9d319..0afe490cb 100644 --- a/templates/Workflow/new.html.twig +++ b/templates/Workflow/new.html.twig @@ -9,7 +9,7 @@ {{ form_start(form) }}
    -
    +
    {{ 'view_workflow.edit'| trans }}
    diff --git a/templates/Workflow/show.html.twig b/templates/Workflow/show.html.twig index 1b62d1bf6..5b9afd995 100644 --- a/templates/Workflow/show.html.twig +++ b/templates/Workflow/show.html.twig @@ -37,7 +37,7 @@ {% if workflow is not null %}
    -
    +
    @@ -89,7 +89,7 @@ {# LIST ACTION #}
    -
    +
    @@ -173,7 +173,7 @@
    -
    +
    diff --git a/templates/WorkflowAction/show.html.twig b/templates/WorkflowAction/show.html.twig index 304d7c07c..597ed6112 100644 --- a/templates/WorkflowAction/show.html.twig +++ b/templates/WorkflowAction/show.html.twig @@ -28,7 +28,7 @@ {% if workflow is not null %}
    -
    +
    @@ -93,7 +93,7 @@
    -
    +
    diff --git a/templates/testFilter.html.twig b/templates/testFilter.html.twig index ca9c44fbe..818fbce66 100644 --- a/templates/testFilter.html.twig +++ b/templates/testFilter.html.twig @@ -90,7 +90,7 @@ @@ -335,52 +335,72 @@ {% block javascript %} + + + {{ encore_entry_script_tags('app') }} {{ encore_entry_script_tags('filter') }} -{% endblock javascript %} - - - + + + -{% endblock %} + // Initialisation de Select2 et autres scripts après le chargement du document + $(document).ready(function() { + // Initialisation de Select2 sur le champ globalStatus + $('#globalStatus .select2').select2({ + placeholder: "Select Global Status", + allowClear: true, + width: 'resolve' + }); + + // Script pour récupérer et afficher les messages d'erreur liés aux documents + $('.get-log-msg').click(function() { + var docId = $(this).data('doc-id'); + var testLastMessage = "{{ path('document_last_error_message', {'docId': 'PLACEHOLDER_ID'}) }}"; + var testlastmessagefinal = testLastMessage.replace("PLACEHOLDER_ID", docId); + + console.log(docId); + console.log(testlastmessagefinal); -{% block cssin %}{% endblock cssin %} + $.ajax({ + url: testlastmessagefinal, + method: 'POST', + success: function(data) { + $('#log-msg-' + docId).text(data); + alert(data); + } + }); + }); + }); + +{% endblock javascript %} +{% endblock %} +{% block cssin %} + {{ encore_entry_link_tags('app') }} + {{ encore_entry_link_tags('filter') }} + + +{% endblock cssin %} diff --git a/translations/messages.en.yml b/translations/messages.en.yml index 072113eae..f50354a84 100644 --- a/translations/messages.en.yml +++ b/translations/messages.en.yml @@ -185,6 +185,9 @@ view_rule: read_job_lock: Unlock job read_job_lock_success: The job has been unlocked read_job_lock_error: An error occurred while unlocking the job + order: Order + status: Status + workflows: Workflows connector: title: Connector solution: Solution @@ -471,6 +474,8 @@ view_task: list_workflow: total: Total of workflows empty: No workflows + searchruleName: Search by rule name + searchName: Search by name th: id: Id ruleName: Rule name @@ -522,6 +527,7 @@ view_workflow_action: action: Action order: Order back_to_workflow: Back to the workflow + create: Create a new action view_account: title: My account user_info: My information diff --git a/translations/messages.fr.yml b/translations/messages.fr.yml index b0e350ea1..3c27ef368 100644 --- a/translations/messages.fr.yml +++ b/translations/messages.fr.yml @@ -183,6 +183,9 @@ view_rule: read_job_lock: DébloquerDébloqué read_job_lock_success: La régle a bien été débloqué read_job_lock_error: Impossible de débloquer la règle + order: Ordre + status: Statut + workflows: Workflows connector: title: Connecteur solution: Solution @@ -468,6 +471,8 @@ view_task: list_workflow: total: Total des workflows empty: Pas workflows + searchruleName: Rechercher par nom de règle + searchName: Rechercher par nom th: id: Id RuleName: Nom de la règle @@ -519,6 +524,7 @@ view_workflow_action: action: Action order: Ordre back_to_workflow: Retour au workflow + create: Créer une action view_account: title: Mon compte user_info: Mes informations diff --git a/yarn.lock b/yarn.lock index 394d2a260..83c7795ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5238,6 +5238,11 @@ select-hose@^2.0.0: resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= +select2@^4.1.0-rc.0: + version "4.1.0-rc.0" + resolved "https://registry.yarnpkg.com/select2/-/select2-4.1.0-rc.0.tgz#ba3cd3901dda0155e1c0219ab41b74ba51ea22d8" + integrity sha512-Hr9TdhyHCZUtwznEH2CBf7967mEM0idtJ5nMtjvk3Up5tPukOLXbHUNmh10oRfeNIhj+3GD3niu+g6sVK+gK0A== + selfsigned@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.0.1.tgz#8b2df7fa56bf014d19b6007655fff209c0ef0a56" From d3ab3385b839e97ba6d2ddb1c05c2aaed0323c7e Mon Sep 17 00:00:00 2001 From: Camille Pichaud <95077335+CamillePMyddleware@users.noreply.github.com> Date: Tue, 10 Sep 2024 13:13:00 +0200 Subject: [PATCH 136/452] add page variables (#1179) Co-authored-by: cpichaud --- assets/css/task.css | 15 +-- src/Controller/VariableController.php | 151 ++++++++++++++++++++++++++ src/Entity/Variable.php | 15 ++- templates/base.html.twig | 6 + templates/variable/create.html.twig | 29 +++++ templates/variable/edit.html.twig | 29 +++++ templates/variable/list.html.twig | 64 +++++++++++ templates/variable/show.html.twig | 43 ++++++++ translations/messages.en.yml | 24 +++- translations/messages.fr.yml | 24 +++- 10 files changed, 384 insertions(+), 16 deletions(-) create mode 100644 src/Controller/VariableController.php create mode 100644 templates/variable/create.html.twig create mode 100644 templates/variable/edit.html.twig create mode 100644 templates/variable/list.html.twig create mode 100644 templates/variable/show.html.twig diff --git a/assets/css/task.css b/assets/css/task.css index 802746beb..492677bf5 100755 --- a/assets/css/task.css +++ b/assets/css/task.css @@ -22,19 +22,19 @@ along with Myddleware. If not, see . *********************************************************************************/ -#task, #workflows { +#task, #workflows, #variables { padding: 20px 0; text-align: center; } -#task th, #workflows th { +#task th, #workflows th, #variables th { background: #ECECEC; height: 30px; padding: 2px; text-align: center; } -#task tr, #workflows tr { +#task tr, #workflows tr, #variables tr { border-bottom: 1px solid #ccc; } @@ -73,7 +73,8 @@ } #task table a, #task table a:hover, -#workflows table a, #workflows table a:hover { +#workflows table a, #workflows table a:hover, +#variables table a, #variables table a:hover { color: #0F66A9; text-decoration: none; } @@ -81,11 +82,11 @@ font-weight: bold; } -#task #headertab, #workflows #headertab { +#task #headertab, #workflows #headertab, #variables #headertab { margin-top:20px; } -#task #headertab td, #workflows #headertab td { +#task #headertab td, #workflows #headertab td, #variables #headertab td { height: 50px; /* white-space: nowrap; */ /* max-width: 200px; */ @@ -95,7 +96,7 @@ word-wrap: break-word; } -#task .listepager .count, #workflows .listepager .count { +#task .listepager .count, #workflows .listepager .count, #variables .listepager .count { max-width: 80px; width: 80px; min-width: 80px; diff --git a/src/Controller/VariableController.php b/src/Controller/VariableController.php new file mode 100644 index 000000000..d03757bed --- /dev/null +++ b/src/Controller/VariableController.php @@ -0,0 +1,151 @@ +. + *********************************************************************************/ + +namespace App\Controller; + +use App\Entity\Variable; +use Pagerfanta\Pagerfanta; +use Pagerfanta\Adapter\ArrayAdapter; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Annotation\Route; +use Symfony\Contracts\Translation\TranslatorInterface; +use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; +use Symfony\Component\Form\Extension\Core\Type\TextareaType; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + +class VariableController extends AbstractController +{ + /** + * @Route("variables", name="variable_list", defaults={"page"=1}) + * @Route("variables/page-{page}", name="variable_list_page", requirements={"page"="\d+"}) + */ + public function listView(int $page = 1, EntityManagerInterface $em, Request $request): Response + { + try { + $variables = $em->getRepository(Variable::class)->findBy([], ['id' => 'ASC']); + + $adapter = new ArrayAdapter($variables); + $pager = new Pagerfanta($adapter); + $pager->setMaxPerPage(10); + $pager->setCurrentPage($page); + + return $this->render('variable/list.html.twig', [ + 'variables' => $pager->getCurrentPageResults(), + 'pager' => $pager, + 'nb_variables' => count($variables), + ]); + } catch (\Exception $e) { + throw $this->createNotFoundException('Error: ' . $e->getMessage()); + } + } + + /** + * @Route("/variables/new", name="variable_create") + */ + public function create(EntityManagerInterface $em, Request $request, TranslatorInterface $translator): Response + { + $variable = new Variable(); + + $form = $this->createFormBuilder($variable) + ->add('name', TextType::class, [ + 'label' => $translator->trans('variable.table_headers.name'), + ]) + ->add('value', TextareaType::class, [ + 'label' => $translator->trans('variable.table_headers.value'), + ]) + ->add('save', SubmitType::class, [ + 'label' => $translator->trans('variable.save'), + ]) + ->getForm(); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $em->persist($variable); + $em->flush(); + + return $this->redirectToRoute('variable_list'); + } + + return $this->render('variable/create.html.twig', [ + 'form' => $form->createView(), + ]); + } + + /** + * @Route("/variables/{id}/edit", name="variable_edit") + */ + public function edit(EntityManagerInterface $em, Request $request, Variable $variable, TranslatorInterface $translator): Response + { + $form = $this->createFormBuilder($variable) + ->add('name', TextType::class, [ + 'label' => $translator->trans('variable.table_headers.name'), + ]) + ->add('value', TextareaType::class, [ + 'label' => $translator->trans('variable.table_headers.value'), + ]) + ->add('save', SubmitType::class, [ + 'label' => $translator->trans('variable.save'), + ]) + ->getForm(); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $em->flush(); + + return $this->redirectToRoute('variable_list'); + } + + return $this->render('variable/edit.html.twig', [ + 'form' => $form->createView(), + 'variable' => $variable, + ]); + } + + /** + * @Route("/variables/{id}/delete", name="variable_delete") + */ + public function delete(EntityManagerInterface $em, Variable $variable): Response + { + $em->remove($variable); + $em->flush(); + + return $this->redirectToRoute('variable_list'); + } + + /** + * @Route("/variables/{id}", name="variable_show") + */ + public function show(Variable $variable): Response + { + return $this->render('variable/show.html.twig', [ + 'variable' => $variable + ]); + } +} diff --git a/src/Entity/Variable.php b/src/Entity/Variable.php index 6fa586600..7168a4d78 100644 --- a/src/Entity/Variable.php +++ b/src/Entity/Variable.php @@ -42,12 +42,12 @@ class Variable /** * @ORM\Column(name="name", type="string", length=100, nullable=false) */ - private string $name; + private string $name = ''; /** * @ORM\Column(name="value", type="text", nullable=false) */ - private string $value; + private string $value = ''; public function getId(): int @@ -55,12 +55,17 @@ public function getId(): int return $this->id; } - public function setName($name): self + public function setName(string $name): self { - $this->name = $name; - + if (strpos($name, 'mdwvar_') !== 0) { + $this->name = 'mdwvar_' . $name; + } else { + $this->name = $name; + } + return $this; } + public function getName(): string { diff --git a/templates/base.html.twig b/templates/base.html.twig index 9c677af72..d70efbd26 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -128,6 +128,12 @@ {{'menu.workflow.list'|trans}} +
  • + + + {{'menu.variable.list'|trans}} + +
  • - {% for param in rule.params %} - {% if param.name == 'description' %} -
    -

    {{ 'solution.params.description' | trans }}

    -
    {{ param.value }}
    - - -
    - {% endif %} - {% endfor %} + + + + + + + + {% for param in rule.params %} + {% if param.name == 'description' %} + + + + + {% endif %} + {% endfor %} + +
    {{ 'solution.params.description' | trans }}
    +
    +
    {{ param.value }}
    +
    +
    + + +
    + {# TABLE WORKFLOW #}
    From c18dae5d24ff88acaa609b6a3a56909ae46f0b38 Mon Sep 17 00:00:00 2001 From: myddleware Date: Tue, 24 Sep 2024 23:14:41 +0200 Subject: [PATCH 146/452] Add remove lock action before closing a job. Signed-off-by: myddleware --- src/DataFixtures/LoadCronJobData.php | 1 + src/DataFixtures/LoadJobSchedulerData.php | 84 ----------------------- src/Manager/JobManager.php | 11 ++- src/Repository/DocumentRepository.php | 3 +- 4 files changed, 12 insertions(+), 87 deletions(-) delete mode 100644 src/DataFixtures/LoadJobSchedulerData.php diff --git a/src/DataFixtures/LoadCronJobData.php b/src/DataFixtures/LoadCronJobData.php index b72c65a11..80a97131a 100644 --- a/src/DataFixtures/LoadCronJobData.php +++ b/src/DataFixtures/LoadCronJobData.php @@ -40,6 +40,7 @@ class LoadCronJobData implements FixtureInterface ['period' => '0 0 * * *', 'command' => 'myddleware:notification ALL', 'enable' => 0, 'description' => 'Send notification every day'], ['period' => '0 0 * * *', 'command' => 'myddleware:cleardata', 'enable' => 0, 'description' => 'Clean data'], ['period' => '0 0 1 * *', 'command' => 'myddleware:prunedatabase', 'enable' => 0, 'description' => 'Delete all rules and document with the flag deleted = 1'], + ['period' => '0 0 * * *', 'command' => 'myddleware:checkjob', 'enable' => 1, 'description' => 'Check if a job is not closed after 15 minutes of inactivity.'], ]; public function load(ObjectManager $manager) diff --git a/src/DataFixtures/LoadJobSchedulerData.php b/src/DataFixtures/LoadJobSchedulerData.php deleted file mode 100644 index 1e39e68c3..000000000 --- a/src/DataFixtures/LoadJobSchedulerData.php +++ /dev/null @@ -1,84 +0,0 @@ -. -*********************************************************************************/ - -namespace App\DataFixtures; - -use App\Entity\JobScheduler; -use Doctrine\Common\DataFixtures\FixtureInterface; -use Doctrine\Persistence\ObjectManager; - -class LoadJobSchedulerData implements FixtureInterface -{ - private $manager; - protected $jobSchedulerData = [ - ['command' => 'synchro', 'paramName1' => 'rule', 'paramValue1' => 'ALL', 'paramName2' => '', 'paramValue2' => '', 'period' => 5, 'jobOrder' => 10, 'active' => 1], - ['command' => 'rerunerror', 'paramName1' => 'limit', 'paramValue1' => '100', 'paramName2' => 'attempt', 'paramValue2' => '5', 'period' => 60, 'jobOrder' => 100, 'active' => 1], - ['command' => 'rerunerror', 'paramName1' => 'limit', 'paramValue1' => '100', 'paramName2' => 'attempt', 'paramValue2' => '10', 'period' => 1440, 'jobOrder' => 110, 'active' => 1], - ['command' => 'notification', 'paramName1' => 'type', 'paramValue1' => 'alert', 'paramName2' => '', 'paramValue2' => '', 'period' => 60, 'jobOrder' => 200, 'active' => 1], - ['command' => 'notification', 'paramName1' => '', 'paramValue1' => '', 'paramName2' => '', 'paramValue2' => '', 'period' => 1440, 'jobOrder' => 210, 'active' => 1], - ['command' => 'cleardata', 'paramName1' => '', 'paramValue1' => '', 'paramName2' => '', 'paramValue2' => '', 'period' => 1440, 'jobOrder' => 300, 'active' => 1], - ]; - - public function load(ObjectManager $manager) - { - $this->manager = $manager; - $this->generateEntities(); - $this->manager->flush(); - } - - public function getOrder(): int - { - return 1; - } - - private function generateEntities() - { - foreach ($this->jobSchedulerData as $jobScheduler) { - $this->newEntity($jobScheduler); - } - } - - private function newEntity($jobScheduler) - { - // Add jobs only if the table is empty - $jobSchedulers = $this->manager->getRepository(JobScheduler::class)->findAll(); - if (empty($jobSchedulers)) { - $jobSchedulerObject = new JobScheduler(); - $jobSchedulerObject->setDateCreated(new \DateTime('now')); - $jobSchedulerObject->setDateModified(new \DateTime('now')); - $jobSchedulerObject->setCreatedBy('1'); - $jobSchedulerObject->setModifiedBy('1'); - $jobSchedulerObject->setCommand($jobScheduler['command']); - $jobSchedulerObject->setParamName1($jobScheduler['paramName1']); - $jobSchedulerObject->setParamValue1($jobScheduler['paramValue1']); - $jobSchedulerObject->setParamName2($jobScheduler['paramName2']); - $jobSchedulerObject->setParamValue2($jobScheduler['paramValue2']); - $jobSchedulerObject->setPeriod($jobScheduler['period']); - $jobSchedulerObject->setActive($jobScheduler['active']); - $jobSchedulerObject->setJobOrder($jobScheduler['jobOrder']); - $this->manager->persist($jobSchedulerObject); - } - } -} diff --git a/src/Manager/JobManager.php b/src/Manager/JobManager.php index 5479ef8b0..00711a337 100644 --- a/src/Manager/JobManager.php +++ b/src/Manager/JobManager.php @@ -347,6 +347,13 @@ public function closeJob(): bool { // Get job data $this->logData = $this->getLogData(); + + // No lock should remain but we check to be sure nothing remains locked + // Remove lock on document locked by this job + $this->documentRepository->removeLock($this->id); + + // Remove lock (send and read) on rule locked by this job + $this->ruleRepository->removeLock($this->id); // Update table job return $this->updateJob(); @@ -1490,10 +1497,12 @@ public function checkJob($period) //clone because, the job that is not the current job $jobManagerChekJob = clone $this; $jobManagerChekJob->setId($job['id']); - $jobManagerChekJob->closeJob(); + $jobManagerChekJob->closeJob(); + $this->setMessage('Task '.$job['id'].' successfully closed. '); } } catch (Exception $e) { $this->logger->error('Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + $this->setMessage('Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); return false; } return true; diff --git a/src/Repository/DocumentRepository.php b/src/Repository/DocumentRepository.php index 818704427..c13581f2f 100644 --- a/src/Repository/DocumentRepository.php +++ b/src/Repository/DocumentRepository.php @@ -427,13 +427,12 @@ public static function findStatusType(EntityManagerInterface $entityManager) // Remove lock from document using a job id public function removeLock($jobId) { - $empty = null; $q = $this->createQueryBuilder('d') ->update() ->set('d.jobLock', ':empty') ->where('d.jobLock = :jobLock') ->setParameter('jobLock', $jobId) - ->setParameter('empty', $empty) + ->setParameter('empty', '') ->getQuery(); $q->execute(); } From 67b1f2d085591a2a92f1459c8a33c5afa2cf2f21 Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 25 Sep 2024 11:39:29 +0200 Subject: [PATCH 147/452] Add error when we try to transform a document without source data Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 9a09310f6..5f0830091 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -1637,6 +1637,10 @@ protected function updateTargetTable() public function getTransformValue($source, $ruleField) { try { + // Error if no source data + if (empty($source)) { + throw new \Exception('Source data are empty. Failed to transform data.' ); + } // Include custom variables that could be used in the formula // TO BE REMOVED if (file_exists( __DIR__.'/../Custom/Utils/myddlewareVariables.php')) { include __DIR__.'/../Custom/Utils/myddlewareVariables.php'; From 4f4fae768533068a175ec22cd1ff924e18ac386d Mon Sep 17 00:00:00 2001 From: myddleware Date: Wed, 25 Sep 2024 14:27:40 +0200 Subject: [PATCH 148/452] Fix checkjob function Signed-off-by: myddleware --- src/Manager/JobManager.php | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/Manager/JobManager.php b/src/Manager/JobManager.php index 00711a337..56186d2c1 100644 --- a/src/Manager/JobManager.php +++ b/src/Manager/JobManager.php @@ -1479,20 +1479,30 @@ public function checkJob($period) if (empty($period)) { $period = $this->checkJobPeriod; } - //Search only jobs with status start - $sqlParams = "SELECT DISTINCT job.id - FROM job - INNER JOIN log - ON job.id = log.job_id - WHERE - job.status = 'start' - AND TIMESTAMPDIFF(SECOND, log.created, NOW()) > :period;"; + // Search only jobs with status start and last log created before the limit + // We use the begin of teh job if no log + $sqlParams = " + SELECT DISTINCT job.id + FROM job + LEFT OUTER JOIN log + ON job.id = log.job_id + WHERE + job.status = 'start' + AND ( + ( + log.created IS NULL + AND TIMESTAMPDIFF(SECOND, job.begin, NOW()) > :period + ) OR ( + log.created IS NOT NULL + AND TIMESTAMPDIFF(SECOND, (SELECT MAX(log2.created) from log as log2 where log2.job_id = job.id), NOW()) > :period + ) + ) + "; $stmt = $this->connection->prepare($sqlParams); $stmt->bindValue('period', $period); $result = $stmt->executeQuery(); $jobs = $result->fetchAllAssociative(); - foreach ($jobs as $job) { //clone because, the job that is not the current job $jobManagerChekJob = clone $this; From a87fbb8fb8e3f27d4971fa538039d3237e4cbcaf Mon Sep 17 00:00:00 2001 From: myddleware Date: Thu, 26 Sep 2024 18:45:22 +0200 Subject: [PATCH 149/452] Manage workflow action generateDocument if no value set Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 5f0830091..163684ec6 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -2759,7 +2759,10 @@ public function runWorkflow($rerun=false) { $arguments = $this->setWorkflowNotificationArguments($action); switch ($action['action']) { case 'generateDocument': - $this->generateDocument($arguments['ruleId'],$this->sourceData[$arguments['searchValue']],$arguments['searchField'],$arguments['rerun'], $action); + // Set default value if empty + $searchField = (!empty($arguments['searchField']) ? $arguments['searchField'] : 'id'); + $searchValue = (!empty($arguments['searchValue']) AND !empty($this->sourceData[$arguments['searchValue']]) ? $this->sourceData[$arguments['searchValue']] : ''); + $this->generateDocument($arguments['ruleId'],$searchValue, $searchField ,$arguments['rerun'], $action); break; case 'sendNotification': $workflowStatus = 'Success'; From ecaf6f734de91a11c6adf41bdc2cf33ae0a1064b Mon Sep 17 00:00:00 2001 From: myddleware Date: Fri, 27 Sep 2024 20:41:36 +0200 Subject: [PATCH 150/452] Notification remove log alert Disable rule when the limit is reached Signed-off-by: myddleware --- src/Manager/NotificationManager.php | 22 +++++++++++----------- src/Manager/RuleManager.php | 9 +++++++-- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/Manager/NotificationManager.php b/src/Manager/NotificationManager.php index d826deb29..8fcc32379 100644 --- a/src/Manager/NotificationManager.php +++ b/src/Manager/NotificationManager.php @@ -109,7 +109,7 @@ public function sendAlert(): bool try { $this->sendAlertTaskTooLong(); - $this->sendAlertLimitReached(); + // $this->sendAlertLimitReached(); return true; @@ -191,23 +191,23 @@ public function sendAlertLimitReached() //Send Alerte if (!empty($newErrorLogs)) { - $textMail = "Des nouveaux logs d'erreur ont été trouvés :\n\n"; + $textMail = "Des nouveaux logs d'erreur ont été trouvés :\n\n"; - // TODO: à translate - foreach ($newErrorLogs as $log) { - $textMail .= "Date de création: " . $log['created']->format('Y-m-d H:i:s') . "\n"; - $textMail .= "Type: " . $log['type'] . "\n"; - $textMail .= "Message: " . $log['message'] . "\n\n"; - } + // TODO: à translate + foreach ($newErrorLogs as $log) { + $textMail .= "Date de création: " . $log['created']->format('Y-m-d H:i:s') . "\n"; + $textMail .= "Type: " . $log['type'] . "\n"; + $textMail .= "Message: " . $log['message'] . "\n\n"; + } - // TODO: check : envoyez l'e-mail - $this->send($textMail, "Alerte: Nouveaux logs d'erreur trouvés"); + // TODO: check : envoyez l'e-mail + $this->send($textMail, "Alerte: Nouveaux logs d'erreur trouvés"); } // Update alert_date_ref $currentDate = new \DateTime(); $this->configRepository->setAlertDateRef($currentDate->format('Y-m-d H:i:s')); - } + } protected function send($textMail, $subject) { // Get the email adresses of all ADMIN diff --git a/src/Manager/RuleManager.php b/src/Manager/RuleManager.php index 6fa3f7840..1894676a9 100644 --- a/src/Manager/RuleManager.php +++ b/src/Manager/RuleManager.php @@ -684,9 +684,14 @@ protected function validateReadDataSource() // send the alert to the user $this->notificationManager->sendAlertSameDocReference($JobSettings); + + // Disable the rule to avoid to send the alert every time the cronjob runs + $rule = $this->entityManager->getRepository(Rule::class)->findOneBy(['id' => $this->ruleId, 'deleted' => false]); + $rule->setActive(false); + $this->entityManager->persist($rule); + $this->entityManager->flush(); - - return ['error' => 'All records read have the same reference date in rule '.$this->rule['name'].'. Myddleware cannot guarantee that all data will be read. Job interrupted. Please increase the number of data read by changing the limit attribute in job and rule classes.']; + return ['error' => 'All records read have the same reference date in rule '.$this->rule['name'].'. Myddleware cannot guarantee that all data will be read. Job interrupted. Please increase the number of data read by changing the limit attribute in job and rule classes. The rule has been disabled. ']; } return true; From f9417b90530eb40229b65a2a6b2331cfbd1c9ead Mon Sep 17 00:00:00 2001 From: Camille Pichaud <95077335+CamillePMyddleware@users.noreply.github.com> Date: Mon, 30 Sep 2024 09:41:22 +0200 Subject: [PATCH 151/452] Workflow premium (#1200) * fix: rule id, and add changeData * fix: title workflow action * fix: pagination workflow * fix: pagination workflowaction * add show more/showless for workflow_list * change Search field in workflow_list * fix: checkbox * fix: edit workflowaction * fix: test connection connector, and add new page detail * remmove, target for filter page --------- Co-authored-by: cpichaud --- assets/app.js | 1 + assets/css/workflow.css | 3 + assets/js/connector_detail.js | 144 ++ assets/js/filter.js | 2 +- assets/js/workflows.js | 4 +- src/Controller/ConnectorController.php | 1526 +++++++++--------- src/Controller/WorkflowActionController.php | 80 +- src/Controller/WorkflowController.php | 186 +-- src/Form/Type/WorkflowActionType.php | 1 + templates/Connector/detail/detail.html.twig | 109 ++ templates/Connector/list.html.twig | 235 +-- templates/Rule/edit/onglets/infos.html.twig | 100 +- templates/Workflow/_workflow_table.html.twig | 19 +- templates/Workflow/list.html.twig | 125 +- templates/WorkflowAction/list.html.twig | 302 ++-- templates/WorkflowAction/show.html.twig | 4 +- translations/messages.en.yml | 3 + translations/messages.fr.yml | 3 + 18 files changed, 1501 insertions(+), 1346 deletions(-) create mode 100644 assets/js/connector_detail.js create mode 100644 templates/Connector/detail/detail.html.twig diff --git a/assets/app.js b/assets/app.js index bec6b4481..7b03b2851 100755 --- a/assets/app.js +++ b/assets/app.js @@ -44,6 +44,7 @@ require('./js/filter.js') require('./js/crontab.js') require('./js/rule_relation_filter.js') require('./js/editAction.js') +require('./js/connector_detail.js') require("./js/workflows.js"); diff --git a/assets/css/workflow.css b/assets/css/workflow.css index 9d19edc68..929907bc2 100644 --- a/assets/css/workflow.css +++ b/assets/css/workflow.css @@ -59,4 +59,7 @@ text-align: left !important; } +.description_workflow_list{ + width: 300px; +} diff --git a/assets/js/connector_detail.js b/assets/js/connector_detail.js new file mode 100644 index 000000000..e8e97e028 --- /dev/null +++ b/assets/js/connector_detail.js @@ -0,0 +1,144 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2021 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Myddleware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Myddleware. If not, see . +*********************************************************************************/ +const $ = require('jquery'); + +$(function () { + $('#connexion_detail').on('click', function () { + + var datas = ''; + var parent = 'source'; + var status = $('#source_status img'); + + $('.title').each(function() { + + if ($(this).text() != 'solution' && $(this).text() != 'nom') { + + var input = $(this).parent().find('.params'); + + if (input.length > 0) { + if (input.is('input')) { + if (input.attr('data-param') != undefined) { + datas += input.attr('data-param') + "::" + input.val().replace(/;/g, "") + ";"; + } + } else { + var dataParam = input.attr('data-param'); + var textValue = input.text().trim(); + + if ($(this).text().toLowerCase().includes('password') && textValue === '******') { + textValue = password; + } + if (dataParam !== undefined) { + datas += dataParam + "::" + textValue.replace(/;/g, "") + ";"; + } + } + } + } + }); + + $.ajax({ + type: "POST", + url: '../../inputs', + data: { + champs: datas, + parent: parent, + solution: $('.vignette').attr('alt'), + mod: 2 + }, + beforeSend: function () { + status.removeAttr("src"); + status.attr("src", "../" + path_img + "loader.gif"); + }, + success: function (json) { + if (!json.success) { + status.removeAttr("src"); + status.attr("src", "../" + path_img + "status_offline.png"); + $('#msg_status span.error').html(json.message); + $('#msg_status').show(); + return false; + } + + $.ajax({ + type: "POST", + data: { + solutionjs: true, + detectjs: true + }, + url: '../callback/', + success: function (data) { + param = data.split(';'); + + if (param[0] == 1) { + link = param[1]; + + $.ajax({ + type: "POST", + data: { + solutionjs: true + }, + url: '../callback/', + success: function (data) { + + if (data != 1) { + var win = window.open(link, 'Connexion', 'scrollbars=1,resizable=1,height=560,width=770'); + if (data != 401) { + var timer = setInterval(function () { + if (win.closed) { + clearInterval(timer); + console.log('Popup window closed'); + if (confirm("Reconnect")) { + $('#connexion_detail').trigger(); + } + } + }, 1000); + } else { + $('#connexion_detail').trigger(); + } + } else { + status.removeAttr("src"); + status.attr("src", "../" + path_img + "status_online.png"); + $('#msg_status').hide(); + $('#msg_status span.error').html(''); + $('#step_modules_confirme').removeAttr('disabled'); + } + } + }); + } else { + if (!json.success) { + status.removeAttr("src"); + status.attr("src", "../" + path_img + "status_offline.png"); + $('#msg_status span.error').html(r[0]); + $('#msg_status').show(); + } else { + status.removeAttr("src"); + status.attr("src", "../" + path_img + "status_online.png"); + $('#msg_status').hide(); + $('#msg_status span.error').html(''); + } + } + } + }); + }, + }); + }); +}); diff --git a/assets/js/filter.js b/assets/js/filter.js index 2f418e3c6..23f2a3be9 100644 --- a/assets/js/filter.js +++ b/assets/js/filter.js @@ -118,7 +118,7 @@ $(function () { // Show default filters function showDefaultFilters() { - ["name", "sourceId", "target", "globalStatus"].forEach(function ( + ["name", "sourceId", "globalStatus"].forEach(function ( filterName ) { $("#" + filterName).removeAttr("hidden"); diff --git a/assets/js/workflows.js b/assets/js/workflows.js index 03df6d1e0..e956deb2b 100644 --- a/assets/js/workflows.js +++ b/assets/js/workflows.js @@ -45,7 +45,7 @@ $(document).ready(function () { fetchFilteredData(); }); - $(".form-check-input").on("change", function () { + $(".workflow-check-input").on("change", function () { var $input = $(this); var entityId = $input.data("id"); var entityType = $input.data("type"); @@ -76,7 +76,7 @@ $(document).ready(function () { }); - $(".form-check-input").on("change", function () { + $(".workflow-check-input").on("change", function () { var $input = $(this); var entityId = $input.data("id"); var entityType = $input.data("type"); diff --git a/src/Controller/ConnectorController.php b/src/Controller/ConnectorController.php index ab9f93947..669c03263 100755 --- a/src/Controller/ConnectorController.php +++ b/src/Controller/ConnectorController.php @@ -1,746 +1,780 @@ -. -*********************************************************************************/ - -namespace App\Controller; - -use App\Entity\Config; -use App\Entity\Connector; -use App\Entity\Rule; -use App\Entity\Solution; -use App\Form\ConnectorType; -use App\Manager\permission; -use App\Manager\SolutionManager; -use App\Manager\ToolsManager; -use App\Repository\RuleRepository; -use App\Service\SessionService; -use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\NonUniqueResultException; -use Exception; -use Pagerfanta\Adapter\ArrayAdapter; -use Pagerfanta\Doctrine\ORM\QueryAdapter; -use Pagerfanta\Exception\NotValidCurrentPageException; -use Pagerfanta\Pagerfanta; -use Psr\Log\LoggerInterface; -use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -use Symfony\Component\HttpFoundation\RedirectResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; -use Symfony\Contracts\Translation\TranslatorInterface; - -/** - * @Route("/rule") - */ -class ConnectorController extends AbstractController -{ - protected $params; - private SessionService $sessionService; - private TranslatorInterface $translator; - private EntityManagerInterface $entityManager; - private SolutionManager $solutionManager; - private LoggerInterface $logger; - - public function __construct( - SolutionManager $solutionManager, - SessionService $sessionService, - TranslatorInterface $translator, - EntityManagerInterface $entityManager, - LoggerInterface $logger - ) { - $this->solutionManager = $solutionManager; - $this->sessionService = $sessionService; - $this->translator = $translator; - $this->entityManager = $entityManager; - $this->logger = $logger; - // Init parameters - $configRepository = $this->entityManager->getRepository(Config::class); - $configs = $configRepository->findAll(); - if (!empty($configs)) { - foreach ($configs as $config) { - $this->params[$config->getName()] = $config->getvalue(); - } - } - } - - /** - * CALLBACK POUR LES APIS. - * - * @Route("/connector/callback/", name="connector_callback", options={"expose"=true}) - */ - public function callBack() - { - try { - // Nom de la solution - if (!$this->sessionService->isSolutionNameExist()) { - return new Response(''); - } - - $solution_name = $this->sessionService->getSolutionName(); - $solution = $this->solutionManager->get($solution_name); - - // ETAPE 2 : Récupération du retour de la Popup en GET et génération du token final - if (isset($_GET[$solution->nameFieldGet])) { - $connectorSource = $this->sessionService->getParamConnectorSource(); - - $solution->init($connectorSource); // Affecte les variables - - $solution->setAuthenticate($_GET[$solution->nameFieldGet]); - - if ($solution->refresh_token) { // Si RefreshToken - $this->sessionService->setParamConnectorSourceRefreshToken($solution->getRefreshToken()); - } - - $solution->login($connectorSource); - - // Sauvegarde des 2 jetons en session afin de les enregistrer dans les paramètres du connecteur - $this->sessionService->setParamConnectorSourceToken($solution->getAccessToken()); - - if ($solution->refresh_token) { // Si RefreshToken - $this->sessionService->setParamConnectorSourceRefreshToken($solution->getRefreshToken()); - } - - return $this->redirect($this->generateUrl('connector_callback')); - } - - // SOLUTION AVEC POPUP --------------------------------------------------------------------- - // ATAPE 1 si la solution utilise un callback et le js - if ($solution->callback && $solution->js) { - if (!$this->sessionService->isParamConnectorSourceExist()) { - $params_connexion_solution = $this->sessionService->getParamConnectorSource(); - } - if (!$this->sessionService->isParamConnectorSourceTokenExist()) { - $params_connexion_solution['token'] = $this->sessionService->getParamConnectorSourceToken(); - } - if (!$this->sessionService->isParamConnectorSourceRefreshTokenExist()) { - $params_connexion_solution['refreshToken'] = $this->sessionService->getParamConnectorSourceRefreshToken(); - } - - $solution->init($params_connexion_solution); // Affecte les variables - - $error = $solution->login($params_connexion_solution); - - // Gestion des erreurs retour méthode login - if (!empty($error)) { - return new Response(''); - } - - // Autorisation de l'application - if (!empty($_POST['solutionjs'])) { - // Déclenche la pop up - if (!empty($_POST['detectjs'])) { - $callbackUrl = $solution->getCreateAuthUrl((isset($_SERVER['HTTPS']) ? 'https://' : 'http://').$_SERVER['HTTP_HOST'].$this->generateUrl('connector_callback')); - if (!$this->sessionService->isParamConnectorSourceTokenExist()) { - $solution->setAccessToken($this->sessionService->getParamConnectorSourceToken()); - } - // Redirection vers une autorisation manuel - else { - return new Response($solution->js.';'.urldecode($callbackUrl)); // Url de l'authentification prêt à être ouvert en popup - } - - // 1er test de validité du Token - $testToken = $solution->testToken(); - - if (!empty($testToken['error']['code'])) { - if (401 == $testToken['error']['code'] || 404 == $testToken['error']['code']) { - $this->sessionService->setParamConnectorSourceToken(null); - $url = $solution->getCreateAuthUrl($callbackUrl); - - return new Response($solution->js.';'.urldecode($url)); // Url de l'authentification prêt à être ouvert en popup - } - } - - return new Response($solution->js.';'.$callbackUrl); // tentative de connexion - } // detect js - - if ($this->sessionService->isParamConnectorSourceTokenExist()) { - $solution->setAccessToken($this->sessionService->getParamConnectorSourceToken()); - } - // 2nd Test la validité du token - $testToken = $solution->testToken(); - - // Erreur sans ouvrir la popup - if (404 == $testToken['error']['code'] || 0 === $testToken['error']['code']) { - return new Response('2;'.$testToken['error']['message']); // Error Not Found - } - - if (isset($testToken['error']['code']) && !empty($testToken['error']['code']) && !empty($testToken['error']['message'])) { - return new Response($testToken['error']['code'].';'.$testToken['error']['message']); - } - - if ($this->sessionService->isParamConnectorSourceTokenExist()) { - if (isset($testToken['error']['message']) && !empty($testToken['error']['message'])) { - return new Response($testToken['error']['message'].';'); // Erreur de connexion - } - - $solution->connexion_valide = true; - - return new Response(1); // Connexion réussi - } - } - - return new Response(''); // Ferme la popup automatiquement - } // fin - // SOLUTION AVEC POPUP --------------------------------------------------------------------- - - throw new Exception('Failed load class'); - } catch (Exception $e) { - return new Response($e->getMessage()); - } - - return new Response(''); - } - - /** - * Contrôle si le fichier upload est valide puis le déplace. - * - * @Route("/connector/upload/{solution}", name="upload", options={"expose"=true}) - */ - public function upload($solution): Response - { - if (isset($solution)) { - $output_dir = __DIR__.'/../Custom/Solutions/'.trim($solution).'/file/'; - // Get canonicalized absolute pathname - $path = realpath($output_dir); - // If it exist, check if it's a directory - if (false === $path || !is_dir($path)) { - try { - if (!mkdir($output_dir, 755, true)) { - echo '0;'.'Directory '.$output_dir.' doesn\'t exist. Failed to create this directory. Please check directory Custom is readable by webuser. You can create manually the directory for the Sage wsdl too. '; - exit; - } - } catch (Exception $e) { - echo '0;'.$e->getMessage().'. Please check you have the web user has the permission to write in the directory '.__DIR__.'/../Custom . '; - exit; - } - } - } - - // Supprime ancien fichier de config s'il existe - if (isset($_GET['file']) && '' != $_GET['file']) { - $name_without_space = str_replace(' ', '_', $_GET['file']); - $path_delete_old = $output_dir.$name_without_space; - if (file_exists($path_delete_old)) { - unlink($path_delete_old); - echo '

    '.$this->translator->trans('create_connector.upload_delete').' : '.htmlentities($name_without_space).'

    '; - } - } - - if ('all' == $solution) { - if ($this->sessionService->isUploadNameExist()) { - echo '1;'.$this->sessionService->getUploadName(); - $this->sessionService->removeUpload(); - exit; - } - - if ($this->sessionService->isUploadErrorExist()) { - echo '0;'.$this->sessionService->getUploadError(); - $this->sessionService->removeUpload(); - exit; - } - } - - if (isset($_FILES['myfile']) && isset($output_dir) && is_dir($output_dir)) { - if ($_FILES['myfile']['error'] > 0) { - $error = $_FILES['file']['error']; - echo '0;'.$error; - $this->sessionService->setUploadError($error); - } else { - // A list of permitted file extensions - $configRepository = $this->getDoctrine()->getManager()->getRepository(Config::class); - $extensionAllowed = $configRepository->findOneBy(['name' => 'extension_allowed']); - if (!empty($extensionAllowed)) { - $allowedJson = $extensionAllowed->getValue(); - if (!empty($allowedJson)) { - $allowed = json_decode($allowedJson); - } - } - $extension = pathinfo($_FILES['myfile']['name'], PATHINFO_EXTENSION); - - if (!in_array(strtolower($extension), $allowed)) { - echo '0;'.$this->translator->trans('create_connector.upload_error_ext'); - exit; - } - - $name_without_space = str_replace(' ', '_', $_FILES['myfile']['name']); - $new_name = time().'_'.$name_without_space; - - if (move_uploaded_file($_FILES['myfile']['tmp_name'], $output_dir.$new_name)) { - echo '1;'.$this->translator->trans('create_connector.upload_success').' : '.$new_name; - $this->sessionService->setUploadName($new_name); - exit; - } - - echo '0;'.$this->translator->trans('create_connector.upload_error'); - exit; - - exit; - } - } else { - return $this->render('Connector/upload.html.twig', ['solution' => $solution] - ); - } - } - - /** - * CREATION D UN CONNECTEUR LISTE. - * - * @Route("/connector/create", name="regle_connector_create") - */ - public function create(): Response - { - $solution = $this->entityManager->getRepository(Solution::class)->solutionActive(); - $lstArray = []; - if ($solution) { - foreach ($solution as $s) { - $lstArray[$s->getName()] = ucfirst($s->getName()); - } - } - - $lst_solution = ToolsManager::composeListHtml($lstArray, $this->translator->trans('create_rule.step1.list_empty')); - $this->sessionService->setConnectorAnimation(false); - $this->sessionService->setConnectorAddMessage('list'); - - return $this->render('Connector/index.html.twig', [ - 'solutions' => $lst_solution, ] - ); - } - - /** - * CREATION D'UN CONNECTEUR. - * - * @return RedirectResponse|Response - * - * @Route("/connector/insert", name="regle_connector_insert") - * - * @throws Exception - */ - public function connectorInsert(Request $request) - { - $type = ''; - - $solution = $this->getDoctrine() - ->getManager() - ->getRepository(Solution::class) - ->findOneBy(['name' => $this->sessionService->getParamConnectorSourceSolution()]); - - $connector = new Connector(); - $connector->setSolution($solution); - - if (null != $connector->getSolution()) { - $fieldsLogin = $this->solutionManager->get($connector->getSolution()->getName())->getFieldsLogin(); - } else { - $fieldsLogin = []; - } - - $form = $this->createForm(ConnectorType::class, $connector, [ - 'method' => 'PUT', - 'attr' => ['fieldsLogin' => $fieldsLogin, 'secret' => $this->getParameter('secret')], - ]); - - if ('POST' == $request->getMethod() && $this->sessionService->isParamConnectorExist()) { - try { - $form->handleRequest($request); - $form->submit($request->request->get($form->getName())); - if ($form->isValid()) { - $solution = $connector->getSolution(); - $multi = $solution->getSource() + $solution->getTarget(); - if ($this->sessionService->getConnectorAnimation()) { - // animation add connector - $type = $this->sessionService->getParamConnectorAddType(); - // si la solution ajouté n'existe pas dans la page en cours on va la rajouter manuellement - $solution = $this->sessionService->getParamConnectorSourceSolution(); - if (!in_array($solution, json_decode($this->sessionService->getSolutionType($type)))) { - $this->sessionService->setParamConnectorValues($type.';'.$solution.';'.$multi.';'.$solution->getId()); - } - } - - $connectorParams = $connector->getConnectorParams(); - $connector->setConnectorParams(null); - $connector->setNameSlug($connector->getName()); - $connector->setDateCreated(new \DateTime()); - $connector->setDateModified(new \DateTime()); - $connector->setCreatedBy($this->getUser()->getId()); - $connector->setModifiedBy($this->getUser()->getId()); - $connector->setDeleted(0); - - $this->entityManager->persist($connector); - $this->entityManager->flush(); - - foreach ($connectorParams as $key => $cp) { - $cp->setConnector($connector); - $this->entityManager->persist($cp); - $this->entityManager->flush(); - } - - $this->sessionService->removeConnector(); - if ( - !empty($this->sessionService->getConnectorAddMessage()) - && 'list' == $this->sessionService->getConnectorAddMessage() - ) { - $this->sessionService->removeConnectorAdd(); - - return $this->redirect($this->generateUrl('regle_connector_list')); - } - // animation - $message = ''; - if (!empty($this->sessionService->getConnectorAddMessage())) { - $message = $this->sessionService->getConnectorAddMessage(); - } - $this->sessionService->removeConnectorAdd(); - - return $this->render('Connector/createout_valid.html.twig', [ - 'message' => $message, - 'type' => $type, - ] - ); - } - dump($form); - exit(); - - return $this->redirect($this->generateUrl('regle_connector_list')); - //----------- - } catch (Exception $e) { - $this->logger->error('Error : '.$e->getMessage().' File : '.$e->getFile().' Line : '.$e->getLine()); - throw $this->createNotFoundException('Error : '.$e->getMessage().' File : '.$e->getFile().' Line : '.$e->getLine()); - } - } else { - $this->logger->error('Error : '.$e->getMessage().' File : '.$e->getFile().' Line : '.$e->getLine()); - throw $this->createNotFoundException('Error'); - } - } - - /** - * LISTE DES CONNECTEURS. - * - * @Route("/connector/list", name="regle_connector_list", defaults={"page"=1}) - * @Route("/connector/list/page-{page}", name="regle_connector_page", requirements={"page"="\d+"}) - */ - public function connectorList($page = 1): Response - { - try { - // --------------- - $compact['nb'] = 0; - - $compact = $this->nav_pagination([ - 'adapter_em_repository' => $this->entityManager->getRepository(Connector::class) - ->findListConnectorByUser($this->getUser()->isAdmin(), $this->getUser()->getId()), - 'maxPerPage' => $this->params['pager'] ?? 20, - 'page' => $page, - ]); - - // Si tout se passe bien dans la pagination - if ($compact) { - // Si aucun connecteur - if ($compact['nb'] < 1 && !intval($compact['nb'])) { - $compact['entities'] = ''; - $compact['pager'] = ''; - } - - return $this->render('Connector/list.html.twig', [ - 'nb' => $compact['nb'], - 'entities' => $compact['entities'], - 'pager' => $compact['pager'], - ] - ); - } - - throw $this->createNotFoundException('Error'); - // --------------- - } catch (Exception $e) { - throw $this->createNotFoundException('Error : '.$e); - } - } - - /** - * SUPPRESSION DU CONNECTEUR. - * - * @Route("/connector/delete/{id}", name="connector_delete") - */ - public function connectorDelete(Request $request, $id): RedirectResponse - { - $session = $request->getSession(); - if (isset($id)) { - // Check permission - if ($this->getUser()->isAdmin()) { - $list_fields_sql = ['id' => $id]; - } else { - $list_fields_sql = - ['id' => $id, - 'createdBy' => $this->getUser()->getId(), - ]; - } - - // Get the connector using its id - $connector = $this->getDoctrine() - ->getManager() - ->getRepository(Connector::class) - ->findOneBy($list_fields_sql); - - if (null === $connector) { - return $this->redirect($this->generateUrl('regle_connector_list')); - } - try { - /** @var RuleRepository $ruleRepository */ - $ruleRepository = $this->getDoctrine()->getManager()->getRepository(Rule::class); - // Check if a rule uses this connector (source and target) - $rule = $ruleRepository->findOneBy([ - 'connectorTarget' => $connector, - 'deleted' => 0, - ]); - if (empty($rule)) { - $rule = $ruleRepository->findOneBy([ - 'connectorSource' => $connector, - 'deleted' => 0, - ]); - } - // Error message in case a rule using this connector exists - if (!empty($rule)) { - $session->set('error', [$this->translator->trans('error.connector.remove_with_rule').' '.$rule->getName()]); - } else { - // Flag the connector as deleted - $connector->setDeleted(1); - $this->getDoctrine()->getManager()->persist($connector); - $this->getDoctrine()->getManager()->flush(); - } - } catch (\Doctrine\DBAL\DBALException $e) { - $session->set('error', [$e->getPrevious()->getMessage()]); - } - - return $this->redirect($this->generateUrl('regle_connector_list')); - } - } - - /** - * FICHE D'UN CONNECTEUR. - * - * @Route("/connector/view/{id}", name="connector_open") - * - * @throws Exception - * @throws NonUniqueResultException - * @throws NonUniqueResultException - */ - public function connectorOpen(Request $request, $id) - { - $qb = $this->entityManager->getRepository(Connector::class)->createQueryBuilder('c'); - $qb->select('c', 'cp')->leftjoin('c.connectorParams', 'cp'); - - if ($this->getUser()->isAdmin()) { - $qb->where('c.id =:id AND c.deleted = 0')->setParameter('id', $id); - } else { - $qb->where('c.id =:id and c.createdBy =:createdBy AND c.deleted = 0')->setParameter(['id' => $id, 'createdBy' => $this->getUser()->getId()]); - } - // Detecte si la session est le support --------- - // Infos du connecteur - $connector = $qb->getQuery()->getOneOrNullResult(); - - if (!$connector) { - throw $this->createNotFoundException("This connector doesn't exist"); - } - - if ($this->getUser()->isAdmin()) { - $qb->where('c.id =:id')->setParameter('id', $id); - } else { - $qb->where('c.id =:id and c.createdBy =:createdBy')->setParameter(['id' => $id, 'createdBy' => $this->getUser()->getId()]); - } - // Detecte si la session est le support --------- - // Infos du connecteur - $connector = $qb->getQuery()->getOneOrNullResult(); - - if (!$connector) { - throw $this->createNotFoundException("This connector doesn't exist"); - } - - // Create connector form - // $form = $this->createForm(new ConnectorType($this->container), $connector, ['action' => $this->generateUrl('connector_open', ['id' => $id])]); - - if (null != $connector->getSolution()) { - $fieldsLogin = $this->solutionManager->get($connector->getSolution()->getName())->getFieldsLogin(); - } else { - $fieldsLogin = []; - } - - $form = $this->createForm(ConnectorType::class, $connector, [ - 'action' => $this->generateUrl('connector_open', ['id' => $id]), - 'method' => 'POST', - 'attr' => ['fieldsLogin' => $fieldsLogin, 'secret' => $this->getParameter('secret')], - ]); - - // If the connector has been changed - if ('POST' == $request->getMethod()) { - try { - $form->handleRequest($request); - // SAVE - $params = $connector->getConnectorParams(); - // SAVE PARAMS CONNECTEUR - if (count($params) > 0) { - $this->entityManager->persist($connector); - $this->entityManager->flush(); - - return $this->redirect($this->generateUrl('regle_connector_list')); - } - - return new Response(0); - } catch (Exception $e) { - return new Response($e->getMessage()); - } - } - // Display the connector - else { - return $this->render('Connector/edit/fiche.html.twig', [ - 'connector' => $connector, - 'form' => $form->createView(), - 'connector_name' => $connector->getName(), - ] - ); - } - } - - /* ****************************************************** - * ANIMATION - ****************************************************** */ - - /** - * LISTE DES CONNECTEURS POUR ANIMATION. - * - * @Route("/connector/list/solution", name="regle_connector_by_solution") - */ - public function connectorListSolution(Request $request): Response - { - $id = $request->get('id', null); - - if (null != $id) { - if ($this->getUser()->isAdmin()) { - $list_fields_sql = ['solution' => (int) $id, - 'deleted' => 0, - ]; - } else { - $list_fields_sql = - ['solution' => (int) $id, - 'deleted' => 0, - 'createdBy' => $this->getUser()->getId(), - ]; - } - $listConnector = $this->entityManager->getRepository(Connector::class)->findBy($list_fields_sql); - $lstArray = []; - foreach ($listConnector as $c) { - $lstArray[$c->getId()] = ucfirst($c->getName()); - } - $lst = ToolsManager::composeListHtml($lstArray, $this->translator->trans('create_rule.step1.choose_connector')); - - return new Response($lst); - } - - return new Response(''); - } - - /** - * CREATION D'UN CONNECTEUR LISTE animation. - * - * @Route("/connector/createout/{type}", name="regle_connector_create_out") - */ - public function createOut($type): Response - { - $solution = $this->entityManager->getRepository(Solution::class)->solutionConnectorType($type); - $lstArray = []; - if ($solution) { - foreach ($solution as $s) { - $lstArray[$s['name']] = ucfirst($s['name']); - } - } - - $lst_solution = ToolsManager::composeListHtml($lstArray, $this->translator->trans('create_rule.step1.list_empty')); - - $this->sessionService->setConnectorAddMessage($this->translator->trans('create_rule.step1.connector')); - $this->sessionService->setParamConnectorAddType(strip_tags($type)); - $this->sessionService->setConnectorAnimation(true); - - return $this->render('Connector/createout.html.twig', [ - 'solutions' => $lst_solution, - ] - ); - } - - /** - * RETOURNE LES INFOS POUR L AJOUT D UN CONNECTEUR EN JQUERY. - * - * @Route("/connector/insert/solution", name="regle_connector_insert_solution") - */ - public function connectorInsertSolutionAction(): Response - { - if ($this->sessionService->isConnectorValuesExist()) { - $values = $this->sessionService->getConnectorValues(); - $this->sessionService->removeConnectorValues(); - - return new Response($values); - } - - return new Response(0); - } - - /* ****************************************************** - * METHODES PRATIQUES - ****************************************************** */ - - // Crée la pagination avec le Bundle Pagerfanta en fonction d'une requete - private function nav_pagination($params, $orm = true) - { - /* - * adapter_em_repository = requete - * maxPerPage = integer - * page = page en cours - */ - - if (is_array($params)) { - $compact = []; - //On passe l’adapter au bundle qui va s’occuper de la pagination - if ($orm) { - $queryBuilder = $params['adapter_em_repository']; - $pagerfanta = new Pagerfanta(new QueryAdapter($queryBuilder)); - $compact['pager'] = $pagerfanta; - } else { - $compact['pager'] = new Pagerfanta(new ArrayAdapter($params['adapter_em_repository'])); - } - - //On définit le nombre d’article à afficher par page (que l’on a biensur définit dans le fichier param) - $compact['pager']->setMaxPerPage($params['maxPerPage']); - try { - $compact['entities'] = $compact['pager'] - //On indique au pager quelle page on veut - ->setCurrentPage($params['page']) - //On récupère les résultats correspondant - ->getCurrentPageResults(); - - $compact['nb'] = $compact['pager']->getNbResults(); - } catch (NotValidCurrentPageException $e) { - //Si jamais la page n’existe pas on léve une 404 - throw $this->createNotFoundException('Page not found. '.$e->getMessage()); - } - - return $compact; - } - - return false; - } -} +. +*********************************************************************************/ + +namespace App\Controller; + +use App\Entity\Config; +use App\Entity\Connector; +use App\Entity\Rule; +use App\Entity\Solution; +use App\Form\ConnectorType; +use App\Manager\permission; +use App\Manager\SolutionManager; +use App\Manager\ToolsManager; +use App\Repository\RuleRepository; +use App\Service\SessionService; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\NonUniqueResultException; +use Exception; +use Pagerfanta\Adapter\ArrayAdapter; +use Pagerfanta\Doctrine\ORM\QueryAdapter; +use Pagerfanta\Exception\NotValidCurrentPageException; +use Pagerfanta\Pagerfanta; +use Psr\Log\LoggerInterface; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Annotation\Route; +use Symfony\Contracts\Translation\TranslatorInterface; + +/** + * @Route("/rule") + */ +class ConnectorController extends AbstractController +{ + protected $params; + private SessionService $sessionService; + private TranslatorInterface $translator; + private EntityManagerInterface $entityManager; + private SolutionManager $solutionManager; + private LoggerInterface $logger; + + public function __construct( + SolutionManager $solutionManager, + SessionService $sessionService, + TranslatorInterface $translator, + EntityManagerInterface $entityManager, + LoggerInterface $logger + ) { + $this->solutionManager = $solutionManager; + $this->sessionService = $sessionService; + $this->translator = $translator; + $this->entityManager = $entityManager; + $this->logger = $logger; + // Init parameters + $configRepository = $this->entityManager->getRepository(Config::class); + $configs = $configRepository->findAll(); + if (!empty($configs)) { + foreach ($configs as $config) { + $this->params[$config->getName()] = $config->getvalue(); + } + } + } + + /** + * CALLBACK POUR LES APIS. + * + * @Route("/connector/callback/", name="connector_callback", options={"expose"=true}) + */ + public function callBack() + { + try { + // Nom de la solution + if (!$this->sessionService->isSolutionNameExist()) { + return new Response(''); + } + + $solution_name = $this->sessionService->getSolutionName(); + $solution = $this->solutionManager->get($solution_name); + + // ETAPE 2 : Récupération du retour de la Popup en GET et génération du token final + if (isset($_GET[$solution->nameFieldGet])) { + $connectorSource = $this->sessionService->getParamConnectorSource(); + + $solution->init($connectorSource); // Affecte les variables + + $solution->setAuthenticate($_GET[$solution->nameFieldGet]); + + if ($solution->refresh_token) { // Si RefreshToken + $this->sessionService->setParamConnectorSourceRefreshToken($solution->getRefreshToken()); + } + + $solution->login($connectorSource); + + // Sauvegarde des 2 jetons en session afin de les enregistrer dans les paramètres du connecteur + $this->sessionService->setParamConnectorSourceToken($solution->getAccessToken()); + + if ($solution->refresh_token) { // Si RefreshToken + $this->sessionService->setParamConnectorSourceRefreshToken($solution->getRefreshToken()); + } + + return $this->redirect($this->generateUrl('connector_callback')); + } + + // SOLUTION AVEC POPUP --------------------------------------------------------------------- + // ATAPE 1 si la solution utilise un callback et le js + if ($solution->callback && $solution->js) { + if (!$this->sessionService->isParamConnectorSourceExist()) { + $params_connexion_solution = $this->sessionService->getParamConnectorSource(); + } + if (!$this->sessionService->isParamConnectorSourceTokenExist()) { + $params_connexion_solution['token'] = $this->sessionService->getParamConnectorSourceToken(); + } + if (!$this->sessionService->isParamConnectorSourceRefreshTokenExist()) { + $params_connexion_solution['refreshToken'] = $this->sessionService->getParamConnectorSourceRefreshToken(); + } + + $solution->init($params_connexion_solution); // Affecte les variables + + $error = $solution->login($params_connexion_solution); + + // Gestion des erreurs retour méthode login + if (!empty($error)) { + return new Response(''); + } + + // Autorisation de l'application + if (!empty($_POST['solutionjs'])) { + // Déclenche la pop up + if (!empty($_POST['detectjs'])) { + $callbackUrl = $solution->getCreateAuthUrl((isset($_SERVER['HTTPS']) ? 'https://' : 'http://').$_SERVER['HTTP_HOST'].$this->generateUrl('connector_callback')); + if (!$this->sessionService->isParamConnectorSourceTokenExist()) { + $solution->setAccessToken($this->sessionService->getParamConnectorSourceToken()); + } + // Redirection vers une autorisation manuel + else { + return new Response($solution->js.';'.urldecode($callbackUrl)); // Url de l'authentification prêt à être ouvert en popup + } + + // 1er test de validité du Token + $testToken = $solution->testToken(); + + if (!empty($testToken['error']['code'])) { + if (401 == $testToken['error']['code'] || 404 == $testToken['error']['code']) { + $this->sessionService->setParamConnectorSourceToken(null); + $url = $solution->getCreateAuthUrl($callbackUrl); + + return new Response($solution->js.';'.urldecode($url)); // Url de l'authentification prêt à être ouvert en popup + } + } + + return new Response($solution->js.';'.$callbackUrl); // tentative de connexion + } // detect js + + if ($this->sessionService->isParamConnectorSourceTokenExist()) { + $solution->setAccessToken($this->sessionService->getParamConnectorSourceToken()); + } + // 2nd Test la validité du token + $testToken = $solution->testToken(); + + // Erreur sans ouvrir la popup + if (404 == $testToken['error']['code'] || 0 === $testToken['error']['code']) { + return new Response('2;'.$testToken['error']['message']); // Error Not Found + } + + if (isset($testToken['error']['code']) && !empty($testToken['error']['code']) && !empty($testToken['error']['message'])) { + return new Response($testToken['error']['code'].';'.$testToken['error']['message']); + } + + if ($this->sessionService->isParamConnectorSourceTokenExist()) { + if (isset($testToken['error']['message']) && !empty($testToken['error']['message'])) { + return new Response($testToken['error']['message'].';'); // Erreur de connexion + } + + $solution->connexion_valide = true; + + return new Response(1); // Connexion réussi + } + } + + return new Response(''); // Ferme la popup automatiquement + } // fin + // SOLUTION AVEC POPUP --------------------------------------------------------------------- + + throw new Exception('Failed load class'); + } catch (Exception $e) { + return new Response($e->getMessage()); + } + + return new Response(''); + } + + /** + * Contrôle si le fichier upload est valide puis le déplace. + * + * @Route("/connector/upload/{solution}", name="upload", options={"expose"=true}) + */ + public function upload($solution): Response + { + if (isset($solution)) { + $output_dir = __DIR__.'/../Custom/Solutions/'.trim($solution).'/file/'; + // Get canonicalized absolute pathname + $path = realpath($output_dir); + // If it exist, check if it's a directory + if (false === $path || !is_dir($path)) { + try { + if (!mkdir($output_dir, 755, true)) { + echo '0;'.'Directory '.$output_dir.' doesn\'t exist. Failed to create this directory. Please check directory Custom is readable by webuser. You can create manually the directory for the Sage wsdl too. '; + exit; + } + } catch (Exception $e) { + echo '0;'.$e->getMessage().'. Please check you have the web user has the permission to write in the directory '.__DIR__.'/../Custom . '; + exit; + } + } + } + + // Supprime ancien fichier de config s'il existe + if (isset($_GET['file']) && '' != $_GET['file']) { + $name_without_space = str_replace(' ', '_', $_GET['file']); + $path_delete_old = $output_dir.$name_without_space; + if (file_exists($path_delete_old)) { + unlink($path_delete_old); + echo '

    '.$this->translator->trans('create_connector.upload_delete').' : '.htmlentities($name_without_space).'

    '; + } + } + + if ('all' == $solution) { + if ($this->sessionService->isUploadNameExist()) { + echo '1;'.$this->sessionService->getUploadName(); + $this->sessionService->removeUpload(); + exit; + } + + if ($this->sessionService->isUploadErrorExist()) { + echo '0;'.$this->sessionService->getUploadError(); + $this->sessionService->removeUpload(); + exit; + } + } + + if (isset($_FILES['myfile']) && isset($output_dir) && is_dir($output_dir)) { + if ($_FILES['myfile']['error'] > 0) { + $error = $_FILES['file']['error']; + echo '0;'.$error; + $this->sessionService->setUploadError($error); + } else { + // A list of permitted file extensions + $configRepository = $this->getDoctrine()->getManager()->getRepository(Config::class); + $extensionAllowed = $configRepository->findOneBy(['name' => 'extension_allowed']); + if (!empty($extensionAllowed)) { + $allowedJson = $extensionAllowed->getValue(); + if (!empty($allowedJson)) { + $allowed = json_decode($allowedJson); + } + } + $extension = pathinfo($_FILES['myfile']['name'], PATHINFO_EXTENSION); + + if (!in_array(strtolower($extension), $allowed)) { + echo '0;'.$this->translator->trans('create_connector.upload_error_ext'); + exit; + } + + $name_without_space = str_replace(' ', '_', $_FILES['myfile']['name']); + $new_name = time().'_'.$name_without_space; + + if (move_uploaded_file($_FILES['myfile']['tmp_name'], $output_dir.$new_name)) { + echo '1;'.$this->translator->trans('create_connector.upload_success').' : '.$new_name; + $this->sessionService->setUploadName($new_name); + exit; + } + + echo '0;'.$this->translator->trans('create_connector.upload_error'); + exit; + + exit; + } + } else { + return $this->render('Connector/upload.html.twig', ['solution' => $solution] + ); + } + } + + /** + * CREATION D UN CONNECTEUR LISTE. + * + * @Route("/connector/create", name="regle_connector_create") + */ + public function create(): Response + { + $solution = $this->entityManager->getRepository(Solution::class)->solutionActive(); + $lstArray = []; + if ($solution) { + foreach ($solution as $s) { + $lstArray[$s->getName()] = ucfirst($s->getName()); + } + } + + $lst_solution = ToolsManager::composeListHtml($lstArray, $this->translator->trans('create_rule.step1.list_empty')); + $this->sessionService->setConnectorAnimation(false); + $this->sessionService->setConnectorAddMessage('list'); + + return $this->render('Connector/index.html.twig', [ + 'solutions' => $lst_solution, ] + ); + } + + /** + * CREATION D'UN CONNECTEUR. + * + * @return RedirectResponse|Response + * + * @Route("/connector/insert", name="regle_connector_insert") + * + * @throws Exception + */ + public function connectorInsert(Request $request) + { + $type = ''; + + $solution = $this->getDoctrine() + ->getManager() + ->getRepository(Solution::class) + ->findOneBy(['name' => $this->sessionService->getParamConnectorSourceSolution()]); + + $connector = new Connector(); + $connector->setSolution($solution); + + if (null != $connector->getSolution()) { + $fieldsLogin = $this->solutionManager->get($connector->getSolution()->getName())->getFieldsLogin(); + } else { + $fieldsLogin = []; + } + + $form = $this->createForm(ConnectorType::class, $connector, [ + 'method' => 'PUT', + 'attr' => ['fieldsLogin' => $fieldsLogin, 'secret' => $this->getParameter('secret')], + ]); + + if ('POST' == $request->getMethod() && $this->sessionService->isParamConnectorExist()) { + try { + $form->handleRequest($request); + $form->submit($request->request->get($form->getName())); + if ($form->isValid()) { + $solution = $connector->getSolution(); + $multi = $solution->getSource() + $solution->getTarget(); + if ($this->sessionService->getConnectorAnimation()) { + // animation add connector + $type = $this->sessionService->getParamConnectorAddType(); + // si la solution ajouté n'existe pas dans la page en cours on va la rajouter manuellement + $solution = $this->sessionService->getParamConnectorSourceSolution(); + if (!in_array($solution, json_decode($this->sessionService->getSolutionType($type)))) { + $this->sessionService->setParamConnectorValues($type.';'.$solution.';'.$multi.';'.$solution->getId()); + } + } + + $connectorParams = $connector->getConnectorParams(); + $connector->setConnectorParams(null); + $connector->setNameSlug($connector->getName()); + $connector->setDateCreated(new \DateTime()); + $connector->setDateModified(new \DateTime()); + $connector->setCreatedBy($this->getUser()->getId()); + $connector->setModifiedBy($this->getUser()->getId()); + $connector->setDeleted(0); + + $this->entityManager->persist($connector); + $this->entityManager->flush(); + + foreach ($connectorParams as $key => $cp) { + $cp->setConnector($connector); + $this->entityManager->persist($cp); + $this->entityManager->flush(); + } + + $this->sessionService->removeConnector(); + if ( + !empty($this->sessionService->getConnectorAddMessage()) + && 'list' == $this->sessionService->getConnectorAddMessage() + ) { + $this->sessionService->removeConnectorAdd(); + + return $this->redirect($this->generateUrl('regle_connector_list')); + } + // animation + $message = ''; + if (!empty($this->sessionService->getConnectorAddMessage())) { + $message = $this->sessionService->getConnectorAddMessage(); + } + $this->sessionService->removeConnectorAdd(); + + return $this->render('Connector/createout_valid.html.twig', [ + 'message' => $message, + 'type' => $type, + ] + ); + } + dump($form); + exit(); + + return $this->redirect($this->generateUrl('regle_connector_list')); + //----------- + } catch (Exception $e) { + $this->logger->error('Error : '.$e->getMessage().' File : '.$e->getFile().' Line : '.$e->getLine()); + throw $this->createNotFoundException('Error : '.$e->getMessage().' File : '.$e->getFile().' Line : '.$e->getLine()); + } + } else { + $this->logger->error('Error : '.$e->getMessage().' File : '.$e->getFile().' Line : '.$e->getLine()); + throw $this->createNotFoundException('Error'); + } + } + + /** + * LISTE DES CONNECTEURS. + * + * @Route("/connector/list", name="regle_connector_list", defaults={"page"=1}) + * @Route("/connector/list/page-{page}", name="regle_connector_page", requirements={"page"="\d+"}) + */ + public function connectorList($page = 1): Response + { + try { + // --------------- + $compact['nb'] = 0; + + $compact = $this->nav_pagination([ + 'adapter_em_repository' => $this->entityManager->getRepository(Connector::class) + ->findListConnectorByUser($this->getUser()->isAdmin(), $this->getUser()->getId()), + 'maxPerPage' => $this->params['pager'] ?? 20, + 'page' => $page, + ]); + + // Si tout se passe bien dans la pagination + if ($compact) { + // Si aucun connecteur + if ($compact['nb'] < 1 && !intval($compact['nb'])) { + $compact['entities'] = ''; + $compact['pager'] = ''; + } + + return $this->render('Connector/list.html.twig', [ + 'nb' => $compact['nb'], + 'entities' => $compact['entities'], + 'pager' => $compact['pager'], + ] + ); + } + + throw $this->createNotFoundException('Error'); + // --------------- + } catch (Exception $e) { + throw $this->createNotFoundException('Error : '.$e); + } + } + + /** + * SUPPRESSION DU CONNECTEUR. + * + * @Route("/connector/delete/{id}", name="connector_delete") + */ + public function connectorDelete(Request $request, $id): RedirectResponse + { + $session = $request->getSession(); + if (isset($id)) { + // Check permission + if ($this->getUser()->isAdmin()) { + $list_fields_sql = ['id' => $id]; + } else { + $list_fields_sql = + ['id' => $id, + 'createdBy' => $this->getUser()->getId(), + ]; + } + + // Get the connector using its id + $connector = $this->getDoctrine() + ->getManager() + ->getRepository(Connector::class) + ->findOneBy($list_fields_sql); + + if (null === $connector) { + return $this->redirect($this->generateUrl('regle_connector_list')); + } + try { + /** @var RuleRepository $ruleRepository */ + $ruleRepository = $this->getDoctrine()->getManager()->getRepository(Rule::class); + // Check if a rule uses this connector (source and target) + $rule = $ruleRepository->findOneBy([ + 'connectorTarget' => $connector, + 'deleted' => 0, + ]); + if (empty($rule)) { + $rule = $ruleRepository->findOneBy([ + 'connectorSource' => $connector, + 'deleted' => 0, + ]); + } + // Error message in case a rule using this connector exists + if (!empty($rule)) { + $session->set('error', [$this->translator->trans('error.connector.remove_with_rule').' '.$rule->getName()]); + } else { + // Flag the connector as deleted + $connector->setDeleted(1); + $this->getDoctrine()->getManager()->persist($connector); + $this->getDoctrine()->getManager()->flush(); + } + } catch (\Doctrine\DBAL\DBALException $e) { + $session->set('error', [$e->getPrevious()->getMessage()]); + } + + return $this->redirect($this->generateUrl('regle_connector_list')); + } + } + + /** + * FICHE D'UN CONNECTEUR. + * + * @Route("/connector/view/{id}", name="connector_open") + * + * @throws Exception + * @throws NonUniqueResultException + * @throws NonUniqueResultException + */ + public function connectorOpen(Request $request, $id) + { + $qb = $this->entityManager->getRepository(Connector::class)->createQueryBuilder('c'); + $qb->select('c', 'cp')->leftjoin('c.connectorParams', 'cp'); + + if ($this->getUser()->isAdmin()) { + $qb->where('c.id =:id AND c.deleted = 0')->setParameter('id', $id); + } else { + $qb->where('c.id =:id and c.createdBy =:createdBy AND c.deleted = 0')->setParameter(['id' => $id, 'createdBy' => $this->getUser()->getId()]); + } + // Detecte si la session est le support --------- + // Infos du connecteur + $connector = $qb->getQuery()->getOneOrNullResult(); + + if (!$connector) { + throw $this->createNotFoundException("This connector doesn't exist"); + } + + if ($this->getUser()->isAdmin()) { + $qb->where('c.id =:id')->setParameter('id', $id); + } else { + $qb->where('c.id =:id and c.createdBy =:createdBy')->setParameter(['id' => $id, 'createdBy' => $this->getUser()->getId()]); + } + // Detecte si la session est le support --------- + // Infos du connecteur + $connector = $qb->getQuery()->getOneOrNullResult(); + + if (!$connector) { + throw $this->createNotFoundException("This connector doesn't exist"); + } + + // Create connector form + // $form = $this->createForm(new ConnectorType($this->container), $connector, ['action' => $this->generateUrl('connector_open', ['id' => $id])]); + + if (null != $connector->getSolution()) { + $fieldsLogin = $this->solutionManager->get($connector->getSolution()->getName())->getFieldsLogin(); + } else { + $fieldsLogin = []; + } + + $form = $this->createForm(ConnectorType::class, $connector, [ + 'action' => $this->generateUrl('connector_open', ['id' => $id]), + 'method' => 'POST', + 'attr' => ['fieldsLogin' => $fieldsLogin, 'secret' => $this->getParameter('secret')], + ]); + + // If the connector has been changed + if ('POST' == $request->getMethod()) { + try { + $form->handleRequest($request); + // SAVE + $params = $connector->getConnectorParams(); + // SAVE PARAMS CONNECTEUR + if (count($params) > 0) { + $this->entityManager->persist($connector); + $this->entityManager->flush(); + + return $this->redirect($this->generateUrl('regle_connector_list')); + } + + return new Response(0); + } catch (Exception $e) { + return new Response($e->getMessage()); + } + } + // Display the connector + else { + return $this->render('Connector/edit/fiche.html.twig', [ + 'connector' => $connector, + 'form' => $form->createView(), + 'connector_name' => $connector->getName(), + ] + ); + } + } + + /* ****************************************************** + * ANIMATION + ****************************************************** */ + + /** + * LISTE DES CONNECTEURS POUR ANIMATION. + * + * @Route("/connector/list/solution", name="regle_connector_by_solution") + */ + public function connectorListSolution(Request $request): Response + { + $id = $request->get('id', null); + + if (null != $id) { + if ($this->getUser()->isAdmin()) { + $list_fields_sql = ['solution' => (int) $id, + 'deleted' => 0, + ]; + } else { + $list_fields_sql = + ['solution' => (int) $id, + 'deleted' => 0, + 'createdBy' => $this->getUser()->getId(), + ]; + } + $listConnector = $this->entityManager->getRepository(Connector::class)->findBy($list_fields_sql); + $lstArray = []; + foreach ($listConnector as $c) { + $lstArray[$c->getId()] = ucfirst($c->getName()); + } + $lst = ToolsManager::composeListHtml($lstArray, $this->translator->trans('create_rule.step1.choose_connector')); + + return new Response($lst); + } + + return new Response(''); + } + + /** + * CREATION D'UN CONNECTEUR LISTE animation. + * + * @Route("/connector/createout/{type}", name="regle_connector_create_out") + */ + public function createOut($type): Response + { + $solution = $this->entityManager->getRepository(Solution::class)->solutionConnectorType($type); + $lstArray = []; + if ($solution) { + foreach ($solution as $s) { + $lstArray[$s['name']] = ucfirst($s['name']); + } + } + + $lst_solution = ToolsManager::composeListHtml($lstArray, $this->translator->trans('create_rule.step1.list_empty')); + + $this->sessionService->setConnectorAddMessage($this->translator->trans('create_rule.step1.connector')); + $this->sessionService->setParamConnectorAddType(strip_tags($type)); + $this->sessionService->setConnectorAnimation(true); + + return $this->render('Connector/createout.html.twig', [ + 'solutions' => $lst_solution, + ] + ); + } + + /** + * RETOURNE LES INFOS POUR L AJOUT D UN CONNECTEUR EN JQUERY. + * + * @Route("/connector/insert/solution", name="regle_connector_insert_solution") + */ + public function connectorInsertSolutionAction(): Response + { + if ($this->sessionService->isConnectorValuesExist()) { + $values = $this->sessionService->getConnectorValues(); + $this->sessionService->removeConnectorValues(); + + return new Response($values); + } + + return new Response(0); + } + + /* ****************************************************** + * METHODES PRATIQUES + ****************************************************** */ + + // Crée la pagination avec le Bundle Pagerfanta en fonction d'une requete + private function nav_pagination($params, $orm = true) + { + /* + * adapter_em_repository = requete + * maxPerPage = integer + * page = page en cours + */ + + if (is_array($params)) { + $compact = []; + //On passe l’adapter au bundle qui va s’occuper de la pagination + if ($orm) { + $queryBuilder = $params['adapter_em_repository']; + $pagerfanta = new Pagerfanta(new QueryAdapter($queryBuilder)); + $compact['pager'] = $pagerfanta; + } else { + $compact['pager'] = new Pagerfanta(new ArrayAdapter($params['adapter_em_repository'])); + } + + //On définit le nombre d’article à afficher par page (que l’on a biensur définit dans le fichier param) + $compact['pager']->setMaxPerPage($params['maxPerPage']); + try { + $compact['entities'] = $compact['pager'] + //On indique au pager quelle page on veut + ->setCurrentPage($params['page']) + //On récupère les résultats correspondant + ->getCurrentPageResults(); + + $compact['nb'] = $compact['pager']->getNbResults(); + } catch (NotValidCurrentPageException $e) { + //Si jamais la page n’existe pas on léve une 404 + throw $this->createNotFoundException('Page not found. '.$e->getMessage()); + } + + return $compact; + } + + return false; + } + +/** + * @Route("/connector/{id}/detail", name="connector_detail") + */ +public function detailAction(int $id) +{ + $connector = $this->entityManager->getRepository(Connector::class)->find($id); + + if (!$connector) { + throw $this->createNotFoundException('The connector does not exist'); + } + + $paramConnexion = []; + + foreach ($connector->getConnectorParams() as $param) { + $paramConnexion[$param->getName()] = $param->getValue(); + } + $encrypter = new \Illuminate\Encryption\Encrypter(substr($this->getParameter('secret'), -16)); + foreach ($paramConnexion as $key => $value) { + if (is_string($value)) { + try { + $paramConnexion[$key] = $encrypter->decrypt($value); + } catch (\Exception $e) { + + } + } + } + + // Passez les paramètres décryptés à la vue + return $this->render('Connector/detail/detail.html.twig', [ + 'connector' => $connector, + 'paramConnexion' => $paramConnexion, + ]); +} +} diff --git a/src/Controller/WorkflowActionController.php b/src/Controller/WorkflowActionController.php index 6084f7b14..ac9c1a94c 100644 --- a/src/Controller/WorkflowActionController.php +++ b/src/Controller/WorkflowActionController.php @@ -349,6 +349,7 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re 'sendNotification' => 'sendNotification', 'generateDocument' => 'generateDocument', 'transformDocument' => 'transformDocument', + 'changeData' => 'changeData', ], ]) ->add('ruleId', EntityType::class, [ @@ -358,6 +359,7 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re 'choice_value' => 'id', 'required' => false, 'label' => 'Rule', + 'data' => $formData['ruleId'] ? $em->getRepository(Rule::class)->find($formData['ruleId']) : null, ]) ->add('status', ChoiceType::class, [ 'label' => 'Status', @@ -648,6 +650,7 @@ public function WorkflowActionEditAction(string $id, Request $request) 'sendNotification' => 'sendNotification', 'generateDocument' => 'generateDocument', 'transformDocument' => 'transformDocument', + 'changeData' => 'changeData', ], ]) ->add('ruleId', EntityType::class, [ @@ -657,7 +660,7 @@ public function WorkflowActionEditAction(string $id, Request $request) 'choice_value' => 'id', 'required' => false, 'label' => 'Rule', - 'data' => $em->getRepository(Rule::class)->find($formData['ruleId']) + 'data' => $formData['ruleId'] ? $em->getRepository(Rule::class)->find($formData['ruleId']) : null, ]) ->add('status', ChoiceType::class, [ 'label' => 'Status', @@ -891,81 +894,6 @@ public function saveWorkflowAudit($workflowId) $this->entityManager->flush(); } - // Crée la pagination avec le Bundle Pagerfanta en fonction d'une requete - private function nav_pagination($params, $orm = true) - { - /* - * adapter_em_repository = requete - * maxPerPage = integer - * page = page en cours - */ - - if (is_array($params)) { - /* DOC : - * $pager->setCurrentPage($page); - $pager->getNbResults(); - $pager->getMaxPerPage(); - $pager->getNbPages(); - $pager->haveToPaginate(); - $pager->hasPreviousPage(); - $pager->getPreviousPage(); - $pager->hasNextPage(); - $pager->getNextPage(); - $pager->getCurrentPageResults(); - */ - - $compact = []; - - //On passe l’adapter au bundle qui va s’occuper de la pagination - if ($orm) { - $compact['pager'] = new Pagerfanta(new QueryAdapter($params['adapter_em_repository'])); - } else { - $compact['pager'] = new Pagerfanta(new ArrayAdapter($params['adapter_em_repository'])); - } - - //On définit le nombre d’article à afficher par page (que l’on a biensur définit dans le fichier param) - $compact['pager']->setMaxPerPage($params['maxPerPage']); - try { - $compact['entities'] = $compact['pager'] - //On indique au pager quelle page on veut - ->setCurrentPage($params['page']) - //On récupère les résultats correspondant - ->getCurrentPageResults(); - - $compact['nb'] = $compact['pager']->getNbResults(); - } catch (\Pagerfanta\Exception\NotValidCurrentPageException $e) { - //Si jamais la page n’existe pas on léve une 404 - throw $this->createNotFoundException("Cette page n'existe pas."); - } - - return $compact; - } - - return false; - } - - // Décrypte les paramètres de connexion d'une solution - private function decrypt_params($tab_params) - { - // Instanciate object to decrypte data - $encrypter = new Encrypter(substr($this->getParameter('secret'), -16)); - if (is_array($tab_params)) { - $return_params = []; - foreach ($tab_params as $key => $value) { - if ( - is_string($value) - && !in_array($key, ['solution', 'module']) // Soe data aren't crypted - ) { - $return_params[$key] = $encrypter->decrypt($value); - } - } - - return $return_params; - } - - return $encrypter->decrypt($tab_params); - } - #[Route('/workflowAction/toggle/{id}', name: 'workflow_action_toggle', methods: ['POST'])] public function toggleWorkflowAction(Request $request, EntityManagerInterface $em, WorkflowActionRepository $workflowActionRepository, string $id): JsonResponse { diff --git a/src/Controller/WorkflowController.php b/src/Controller/WorkflowController.php index 01f0f8b7a..06d0f834f 100644 --- a/src/Controller/WorkflowController.php +++ b/src/Controller/WorkflowController.php @@ -154,16 +154,14 @@ public function __construct( $this->workflowLogRepository = $workflowLogRepository; } - protected function getInstanceBdd() - { - } + protected function getInstanceBdd() {} /* ****************************************************** * RULE ****************************************************** */ - /** +/** * LISTE DES WORKFLOWs. * * @return RedirectResponse|Response @@ -174,58 +172,53 @@ protected function getInstanceBdd() public function WorkflowListAction(int $page = 1, Request $request) { try { - $session = $request->getSession(); - $em = $this->getDoctrine()->getManager(); - + // Récupérer les filtres depuis la requête $workflowName = $request->query->get('workflow_name'); $ruleName = $request->query->get('rule_name'); - - // Modifier la requête pour inclure les filtres - $queryBuilder = $em->getRepository(Workflow::class)->createQueryBuilder('w') - ->where('w.deleted = 0'); - + + // Utilisation de findBy pour récupérer les workflows + $criteria = ['deleted' => 0]; + $orderBy = ['order' => 'ASC']; + $workflows = $this->entityManager->getRepository(Workflow::class)->findBy($criteria, $orderBy); + if ($workflowName) { - $queryBuilder->andWhere('w.name LIKE :workflowName') - ->setParameter('workflowName', '%' . $workflowName . '%'); + $workflows = array_filter($workflows, function($workflow) use ($workflowName) { + return stripos($workflow->getName(), $workflowName) !== false; + }); } - + if ($ruleName) { - $queryBuilder->join('w.rule', 'r') - ->andWhere('r.name LIKE :ruleName') - ->setParameter('ruleName', '%' . $ruleName . '%'); + $workflows = array_filter($workflows, function($workflow) use ($ruleName) { + return stripos($workflow->getRule()->getName(), $ruleName) !== false; + }); } - - $queryBuilder->orderBy('w.order', 'ASC'); - $workflows = $queryBuilder->getQuery()->getResult(); - - // Pagination - $compact = $this->nav_pagination([ - 'adapter_em_repository' => $workflows, - 'maxPerPage' => $this->params['pager'] ?? 25, - 'page' => $page, - ], false); - - // check if the request is an AJAX request + + // Pagination avec ArrayAdapter car findBy retourne un tableau + $adapter = new ArrayAdapter($workflows); + $pager = new Pagerfanta($adapter); + $pager->setMaxPerPage(15); + $pager->setCurrentPage($page); + + // Si la requête est AJAX, rendre uniquement la table des workflows if ($request->isXmlHttpRequest()) { return $this->render('Workflow/_workflow_table.html.twig', [ - 'entities' => $workflows, - 'pager' => $compact['pager'], + 'entities' => $pager->getCurrentPageResults(), + 'pager' => $pager, ]); } - - // if not an AJAX request, render the page + + // Si ce n'est pas une requête AJAX, rendre la page complète return $this->render( 'Workflow/list.html.twig', [ - 'entities' => $workflows, - 'nb_workflow' => count($workflows), - 'pager' => $compact['pager'], + 'entities' => $pager->getCurrentPageResults(), + 'nb_workflow' => $pager->getNbResults(), + 'pager_workflow_list' => $pager, ] ); - } catch (Exception $e) { - throw $this->createNotFoundException('Error : ' . $e); + throw $this->createNotFoundException('Erreur : ' . $e->getMessage()); } } @@ -236,32 +229,30 @@ public function WorkflowListAction(int $page = 1, Request $request) public function WorkflowListByRuleAction(string $ruleId, int $page = 1, Request $request) { try { - $session = $request->getSession(); - $em = $this->getDoctrine()->getManager(); - - // Get workflows filtered by rule id - $entities = $em->getRepository(Workflow::class)->findBy(['rule' => $ruleId, 'deleted' => 0]); + // Récupération des workflows par règle + $workflows = $this->entityManager->getRepository(Workflow::class)->findBy( + ['rule' => $ruleId, 'deleted' => 0], + ['order' => 'ASC'] + ); - // List of task limited to 1000 and order by status (start first) and date begin - $compact = $this->nav_pagination([ - 'adapter_em_repository' => $entities, - 'maxPerPage' => $this->params['pager'] ?? 25, - 'page' => $page, - ], false); + // Pagination avec ArrayAdapter + $adapter = new ArrayAdapter($workflows); + $pager = new Pagerfanta($adapter); + $pager->setMaxPerPage(15); + $pager->setCurrentPage($page); - return $this->render( - 'Workflow/list.html.twig', - [ - 'entities' => $entities, - 'nb_workflow' => count($entities), - 'pager' => $compact['pager'], - ] - ); + // Rendu des workflows paginés + return $this->render('Workflow/list.html.twig', [ + 'entities' => $pager->getCurrentPageResults(), + 'nb_workflow' => $pager->getNbResults(), + 'pager_workflow_list' => $pager, + ]); } catch (Exception $e) { - throw $this->createNotFoundException('Error : ' . $e); + throw $this->createNotFoundException('Erreur : ' . $e->getMessage()); } } + // public function to delet the workflow by id (set deleted to 1) /** * @Route("/delete/{id}", name="workflow_delete") @@ -500,6 +491,8 @@ public function WorkflowShowAction(string $id, Request $request, int $page): Res throw $this->createNotFoundException('Error : ' . $e); } } + + // public function to edit a workflow /** * @Route("/edit/{id}", name="workflow_edit") @@ -545,79 +538,4 @@ public function WorkflowEditAction(string $id, Request $request) throw $this->createNotFoundException('Error : ' . $e); } } - - // Crée la pagination avec le Bundle Pagerfanta en fonction d'une requete - private function nav_pagination($params, $orm = true) - { - /* - * adapter_em_repository = requete - * maxPerPage = integer - * page = page en cours - */ - - if (is_array($params)) { - /* DOC : - * $pager->setCurrentPage($page); - $pager->getNbResults(); - $pager->getMaxPerPage(); - $pager->getNbPages(); - $pager->haveToPaginate(); - $pager->hasPreviousPage(); - $pager->getPreviousPage(); - $pager->hasNextPage(); - $pager->getNextPage(); - $pager->getCurrentPageResults(); - */ - - $compact = []; - - //On passe l’adapter au bundle qui va s’occuper de la pagination - if ($orm) { - $compact['pager'] = new Pagerfanta(new QueryAdapter($params['adapter_em_repository'])); - } else { - $compact['pager'] = new Pagerfanta(new ArrayAdapter($params['adapter_em_repository'])); - } - - //On définit le nombre d’article à afficher par page (que l’on a biensur définit dans le fichier param) - $compact['pager']->setMaxPerPage($params['maxPerPage']); - try { - $compact['entities'] = $compact['pager'] - //On indique au pager quelle page on veut - ->setCurrentPage($params['page']) - //On récupère les résultats correspondant - ->getCurrentPageResults(); - - $compact['nb'] = $compact['pager']->getNbResults(); - } catch (\Pagerfanta\Exception\NotValidCurrentPageException $e) { - //Si jamais la page n’existe pas on léve une 404 - throw $this->createNotFoundException("Cette page n'existe pas."); - } - - return $compact; - } - - return false; - } - - // Décrypte les paramètres de connexion d'une solution - private function decrypt_params($tab_params) - { - // Instanciate object to decrypte data - $encrypter = new Encrypter(substr($this->getParameter('secret'), -16)); - if (is_array($tab_params)) { - $return_params = []; - foreach ($tab_params as $key => $value) { - if ( - is_string($value) - && !in_array($key, ['solution', 'module']) // Soe data aren't crypted - ) { - $return_params[$key] = $encrypter->decrypt($value); - } - } - - return $return_params; - } - - return $encrypter->decrypt($tab_params); - } } diff --git a/src/Form/Type/WorkflowActionType.php b/src/Form/Type/WorkflowActionType.php index 802fe06d4..5c67ee475 100644 --- a/src/Form/Type/WorkflowActionType.php +++ b/src/Form/Type/WorkflowActionType.php @@ -65,6 +65,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) 'sendNotification' => 'sendNotification', 'generateDocument' => 'generateDocument', 'transformDocument' => 'transformDocument', + 'changeData' => 'changeData', ], 'attr' => [ 'class' => 'form-control', diff --git a/templates/Connector/detail/detail.html.twig b/templates/Connector/detail/detail.html.twig new file mode 100644 index 000000000..99b767560 --- /dev/null +++ b/templates/Connector/detail/detail.html.twig @@ -0,0 +1,109 @@ +{#/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Myddleware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Myddleware. If not, see . +*********************************************************************************/ #} +{% extends 'base.html.twig' %} +{% block title %} + {{parent()}} + | + {{'view_connector.info.title'|trans}} +{% endblock %} +{% block titlesm %} + {{'view_connector.info.title'|trans}} +{% endblock titlesm %} + +{% block body %} +
    +
    +
    +

    {{ 'view_connector.info.title'|trans }}

    + +
    + + + + + + + + + + {% for key, value in paramConnexion %} + + + + + {% endfor %} + + + +
    {{ 'view_connector.info.solution'|trans }} + {{ connector.solution.name }} +

    {{ connector.solution.name }}

    +
    {{ 'create_connector.connexion'|trans }}{{ connector.name }}
    {{ key }} + {% if key matches '/password|pwd|secret|token/i' and value is not null %} + ****** + {% else %} + {{ value }} + {% endif %} +
    + + +
    + +
    +
    + + +{% endblock %} + + +{% block javascripts %} + {{ encore_entry_script_tags('connector') }} +{% endblock %} diff --git a/templates/Connector/list.html.twig b/templates/Connector/list.html.twig index 1bf971145..9250b211f 100644 --- a/templates/Connector/list.html.twig +++ b/templates/Connector/list.html.twig @@ -1,115 +1,120 @@ -{#/********************************************************************************* - * This file is part of Myddleware. - - * @package Myddleware - * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL - * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com - * @link http://www.myddleware.com - - This file is part of Myddleware. - - Myddleware is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Myddleware is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Myddleware. If not, see . -*********************************************************************************/ #} - -{% extends 'base.html.twig' %} -{% block title %} - {{parent()}} - | - {{'title.connector.list'|trans}} -{% endblock %} -{% block titlesm %} - {{'title.connector.list'|trans}} -{% endblock titlesm %} -{% block body %} -
    -
    - {% if nb > 0 %} -
    -

    {{'list_connector.total'|trans}}

    -

    {{'list_connector.total2'|trans}}

    -

    - {{ nb }} -

    -
    -
    -
    -
    - - {% if entities is not empty %} - - - - - - - - - {% for connector in entities %} - - - - - - {% endfor %} - - - - - - - {% endif %} -
    {{'list_connector.th.name'|trans}}{{'list_connector.th.date_created'|trans}}{{'list_connector.th.option'|trans}}
    - -

    - {{connector.0.getName}} -
    -
    - {{connector.0.getDateCreated|date("d/m/Y") }} - {% if app.user.getUsername == 'support' %} -

    - {{ connector.0.getCreatedBy.username }} -

    - {% endif %} -
    - - - - - - - - - - -
    {{'list_connector.th.name'|trans}}{{'list_connector.th.date_created'|trans}}{{'list_connector.th.option'|trans}}
    -
    -
    -
    - {% if pager.haveToPaginate %} - {{ pagerfanta(pager, 'twitter_bootstrap4', {'routeName': 'regle_connector_page'}) }} - {% endif %} -
    - {% else %} -
    -

    {{'list_connector.empty'|trans}}

    - {{'list_connector.create_first_connector'|trans}} -
    - {% endif %} -
    -
    - -{% endblock %} +{#/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Myddleware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Myddleware. If not, see . +*********************************************************************************/ #} + +{% extends 'base.html.twig' %} +{% block title %} + {{parent()}} + | + {{'title.connector.list'|trans}} +{% endblock %} +{% block titlesm %} + {{'title.connector.list'|trans}} +{% endblock titlesm %} +{% block body %} +
    +
    + {% if nb > 0 %} +
    +

    {{'list_connector.total'|trans}}

    +

    {{'list_connector.total2'|trans}}

    +

    + {{ nb }} +

    +
    +
    +
    +
    + + {% if entities is not empty %} + + + + + + + + + {% for connector in entities %} + + + + + + {% endfor %} + + + + + + + {% endif %} +
    {{'list_connector.th.name'|trans}}{{'list_connector.th.date_created'|trans}}{{'list_connector.th.option'|trans}}
    + +

    + {{connector.0.getName}} +
    +
    + {{connector.0.getDateCreated|date("d/m/Y") }} + {% if app.user.getUsername == 'support' %} +

    + {{ connector.0.getCreatedBy.username }} +

    + {% endif %} +
    + + + + + + + + + + + + + + + +
    {{'list_connector.th.name'|trans}}{{'list_connector.th.date_created'|trans}}{{'list_connector.th.option'|trans}}
    +
    +
    +
    + {% if pager.haveToPaginate %} + {{ pagerfanta(pager, 'twitter_bootstrap4', {'routeName': 'regle_connector_page'}) }} + {% endif %} +
    + {% else %} +
    +

    {{'list_connector.empty'|trans}}

    + {{'list_connector.create_first_connector'|trans}} +
    + {% endif %} +
    +
    + +{% endblock %} diff --git a/templates/Rule/edit/onglets/infos.html.twig b/templates/Rule/edit/onglets/infos.html.twig index 8846df4ab..c0545e859 100644 --- a/templates/Rule/edit/onglets/infos.html.twig +++ b/templates/Rule/edit/onglets/infos.html.twig @@ -68,8 +68,7 @@
    -
    +
    {# TABLE RULE #} @@ -95,68 +94,65 @@ + - - - - - - - - - - - - - - - - - - - - - {% if rule.getReadJobLock is not empty %} - - - + + + - {% endif %} - {% if params_suite.bidirectional is defined and params_suite.bidirectional is not empty %} - - + + - {% endif %} - {% if duplicate_target is defined and duplicate_target is not empty %} - + + + + + - {% endif %} - {% if params_suite.customParams is defined and params_suite.customParams is not empty %} - {% for r in params_suite.customParams %} + {% if rule.getReadJobLock is not empty %} + + + + + {% endif %} + {% if params_suite.bidirectional is defined and params_suite.bidirectional is not empty %} - + - {% endfor %} - {% endif %} -
    {{ 'view_rule.infos.id' | trans }}{{ rule.GetId }}
    {{ rule.GetId }}
    {{ 'view_rule.infos.name' | trans }}{{ rule.GetName }}
    {{ 'view_rule.infos.datecreated' | trans }}{{ rule.GetDateCreated | date("d-m-Y H:i:s") }}
    {{ 'view_rule.infos.datemodified' | trans }}{{ rule.GetDateModified | date("d-m-Y H:i:s") }}
    {{ 'view_rule.infos.mode' | trans }} - {{ params_suite.mode }} -
    {{ 'view_rule.infos.read_job_lock' | trans }} - -
    {{ 'view_rule.infos.name' | trans }}{{ rule.GetName }}
    {{ 'view_rule.infos.bidirectional' | trans }} - {{ params_suite.bidirectionalName }} - {{ 'view_rule.infos.datecreated' | trans }}{{ rule.GetDateCreated | date("d-m-Y H:i:s") }}
    {{ 'view_rule.infos.duplicate_fields' | trans }}{{ 'view_rule.infos.datemodified' | trans }}{{ rule.GetDateModified | date("d-m-Y H:i:s") }}
    {{ 'view_rule.infos.mode' | trans }} - {{ duplicate_target }} + {{ params_suite.mode }}
    {{ 'view_rule.infos.read_job_lock' | trans }} + +
    {{ r.name }}{{ 'view_rule.infos.bidirectional' | trans }} - {{ r.value }} + {{ params_suite.bidirectionalName }}
    -
    + {% endif %} + {% if duplicate_target is defined and duplicate_target is not empty %} + + {{ 'view_rule.infos.duplicate_fields' | trans }} + + {{ duplicate_target }} + + + {% endif %} + {% if params_suite.customParams is defined and params_suite.customParams is not empty %} + {% for r in params_suite.customParams %} + + {{ r.name }} + + {{ r.value }} + + + {% endfor %} + {% endif %} + +
    {# TABLE CONNECTOR #}
    diff --git a/templates/Workflow/_workflow_table.html.twig b/templates/Workflow/_workflow_table.html.twig index 454d27e58..09327d8cf 100644 --- a/templates/Workflow/_workflow_table.html.twig +++ b/templates/Workflow/_workflow_table.html.twig @@ -3,7 +3,7 @@ - + @@ -18,12 +18,25 @@ {{ workflow.rule.name }} - + - - - - '; - } - - return new Response('
    {{ 'list_workflow.th.name'|trans }} {{ 'list_workflow.th.ruleName'|trans }}{{ 'list_workflow.th.description'|trans }}{{ 'list_workflow.th.description'|trans }} {{ 'list_workflow.th.condition'|trans }} {{ 'list_workflow.th.order'|trans }} {{ 'list_workflow.th.active'|trans }} {{ workflow.description }}{% if workflow.description|length > 60 %} + {{ workflow.description|slice(0, 30) ~ '...' }} +
    + {{ workflow.description|slice(0, 100) ~ '...' }} + +
    + + {% else %} + {{ workflow.description }} + {% endif %} +
    {{ workflow.condition }} {{ workflow.order }}
    - +
    diff --git a/templates/Workflow/list.html.twig b/templates/Workflow/list.html.twig index e665027bc..37d034f01 100644 --- a/templates/Workflow/list.html.twig +++ b/templates/Workflow/list.html.twig @@ -23,85 +23,82 @@ *********************************************************************************/ #} {% extends 'base.html.twig' %} + {% block title %} - {{parent()}} - | - {{'title.workflow.list'|trans}} + {{ parent() }} | {{ 'title.workflow.list'|trans }} {% endblock %} + {% block titlesm %} - {{'title.workflow.list'|trans}} + {{ 'title.workflow.list'|trans }} {% endblock titlesm %} -{% block body %} -
    - +{% block body %} +
    -
    - {% for message in app.flashes('success') %} - - {% endfor %} + +
    + {% for message in app.flashes('success') %} + + {% endfor %} - {% if nb_workflow > 0 %} -
    -

    {{'list_workflow.total'|trans}}: -

    -

    - {{ nb_workflow }} -

    -
    -
    + {% if nb_workflow > 0 %} +
    +

    {{ 'list_workflow.total'|trans }}:

    +

    + {{ nb_workflow }} +

    +
    +
    -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    +
    +
    + +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    - + +
    +
    + {% include 'Workflow/_workflow_table.html.twig' %} +
    +
    + {% if pager_workflow_list.haveToPaginate %} + {{ pagerfanta(pager_workflow_list, 'twitter_bootstrap4', {'routeName': 'workflow_list_page'}) }} + {% endif %} +
    + {% else %} +

    {{ 'list_workflow.empty'|trans }}

    + {% endif %}
    +{# ------------- PARAMETRES JQUERY ------------- #} + +{# ------------- PARAMETRES JQUERY ------------- #} - - -
    -
    - {% include 'Workflow/_workflow_table.html.twig' %} -
    -
    - {% if pager.haveToPaginate %} - {{ pagerfanta(pager, 'twitter_bootstrap4', {'routeName': 'task_list_page'}) }} - {% endif %} -
    - {% else %} -

    {{ 'list_workflow.empty'|trans }}

    - {% endif %} -
    -
    - - {# ------------- PARAMETRES JQUERY ------------- #} - - {# ------------- PARAMETRES JQUERY ------------- #} {% endblock %} {% block cssin %}{% endblock cssin %} diff --git a/templates/WorkflowAction/list.html.twig b/templates/WorkflowAction/list.html.twig index 1b4f00f30..b91f51566 100644 --- a/templates/WorkflowAction/list.html.twig +++ b/templates/WorkflowAction/list.html.twig @@ -1,151 +1,151 @@ -{#/********************************************************************************* - * This file is part of Myddleware. - - * @package Myddleware - * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL - * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com - * @link http://www.myddleware.com - - This file is part of Myddleware. - - Myddleware is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Myddleware is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Myddleware. If not, see . -*********************************************************************************/ #} - -{% extends 'base.html.twig' %} -{% block title %}{{parent()}} | {{'title.workflow.list'|trans}}{% endblock %} -{% block titlesm %}{{'title.workflow.list'|trans}}{% endblock titlesm %} -{% block body %} -{% block js %} - - - - -{% endblock js %} -
    - - - -
    - {% for message in app.flashes('success') %} - - {% endfor %} - - - {% if nb_workflow > 0 %} -
    -

    {{'list_workflow.total'|trans}} :

    {{ nb_workflow }} -

    - - -
    - - {% if entities is not empty %} - - - - - - - - - - {% for workflow in entities %} - - - - - - - - - - - - - - - - - {% endfor %} - {% endif %} -
    {{'list_workflow.th.id'|trans}}{{'list_workflow.th.ruleName'|trans}}{{'list_workflow.th.name'|trans}}{{'list_workflow.th.description'|trans}}{{'list_workflow.th.workflowCondition'|trans}}{{'list_workflow.th.active'|trans}}{{'list_workflow.th.actions'|trans}}
    {{ workflow.id }} {{ workflow.rule.name }}{{ workflow.name }}{{ workflow.description }}{{ workflow.condition }} - - {{ workflow.active ? 'Active' : 'Inactive' }} - -
    - - -
    - {% if pager.haveToPaginate %} - {{ pagerfanta(pager, 'twitter_bootstrap4', {'routeName': 'task_list_page'}) }} - {% endif %} -
    - {% else %} -

    {{ 'list_workflow.empty'|trans }}

    - {% endif %} -
    -
    - - {# ------------- PARAMETRES JQUERY ------------- #} - - {# ------------- PARAMETRES JQUERY ------------- #} -{% endblock %} - -{% block cssin %}{% endblock cssin %} - +{#/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Myddleware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Myddleware. If not, see . +*********************************************************************************/ #} + +{% extends 'base.html.twig' %} +{% block title %}{{parent()}} | {{'title.workflow.list'|trans}}{% endblock %} +{% block titlesm %}{{'title.workflow.list'|trans}}{% endblock titlesm %} +{% block body %} +{% block js %} + + + + +{% endblock js %} +
    + + + +
    + {% for message in app.flashes('success') %} + + {% endfor %} + + + {% if nb_workflow > 0 %} +
    +

    {{'list_workflow.total'|trans}} :

    {{ nb_workflow }} +

    + + +
    + + {% if entities is not empty %} + + + + + + + + + + {% for workflow in entities %} + + + + + + + + + + + + + + + + + {% endfor %} + {% endif %} +
    {{'list_workflow.th.id'|trans}}{{'list_workflow.th.ruleName'|trans}}{{'list_workflow.th.name'|trans}}{{'list_workflow.th.description'|trans}}{{'list_workflow.th.workflowCondition'|trans}}{{'list_workflow.th.active'|trans}}{{'list_workflow.th.actions'|trans}}
    {{ workflow.id }} {{ workflow.rule.name }}{{ workflow.name }}{{ workflow.description }}{{ workflow.condition }} + + {{ workflow.active ? 'Active' : 'Inactive' }} + +
    + + +
    + {% if pager.haveToPaginate %} + {{ pagerfanta(pager, 'twitter_bootstrap4', {'routeName': 'workflow_list'}) }} + {% endif %} +
    + {% else %} +

    {{ 'list_workflow.empty'|trans }}

    + {% endif %} +
    +
    + + {# ------------- PARAMETRES JQUERY ------------- #} + + {# ------------- PARAMETRES JQUERY ------------- #} +{% endblock %} + +{% block cssin %}{% endblock cssin %} + diff --git a/templates/WorkflowAction/show.html.twig b/templates/WorkflowAction/show.html.twig index 597ed6112..64afb86e8 100644 --- a/templates/WorkflowAction/show.html.twig +++ b/templates/WorkflowAction/show.html.twig @@ -32,7 +32,7 @@ - {{ 'view_workflow.title'| trans | upper }} + {{ 'view_workflow_action.title2'| trans | upper }}
    @@ -76,7 +76,7 @@
    {{ 'view_workflow.status'|trans|upper }}
    - +
    diff --git a/translations/messages.en.yml b/translations/messages.en.yml index e022e0915..4b5756564 100644 --- a/translations/messages.en.yml +++ b/translations/messages.en.yml @@ -291,6 +291,8 @@ list_connector: view_connector: btn: + edit: Edit + back: Back to the list save: Save delete: Delete confirm_delete: Do you confirm that this connector should be deleted ? @@ -519,6 +521,7 @@ view_workflow: add_action: Add Action create_by: Created By view_workflow_action: + title2: Workflow Action title: Workflow Action Details edit: Edit this Action delete: Delete this Action diff --git a/translations/messages.fr.yml b/translations/messages.fr.yml index ee0973b9a..285699432 100644 --- a/translations/messages.fr.yml +++ b/translations/messages.fr.yml @@ -290,6 +290,8 @@ list_connector: view_connector: btn: + edit: Editer + back: Retour save: Sauvegarder delete: Supprimer confirm_delete: Confirmez-vous la suppression de ce connecteur ? @@ -516,6 +518,7 @@ view_workflow: add_action: Ajouter une action create_by: Créé par view_workflow_action: + title2: Action du workflow title: Détail de l'action du workflow edit: Editer cette action delete: Supprimer cette action From 039f7853755f1f99705e731c65b8898701472acb Mon Sep 17 00:00:00 2001 From: myddleware Date: Mon, 30 Sep 2024 11:18:56 +0200 Subject: [PATCH 152/452] Workflow action : Fix bug on generate document Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 163684ec6..7528a63e5 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -2761,7 +2761,7 @@ public function runWorkflow($rerun=false) { case 'generateDocument': // Set default value if empty $searchField = (!empty($arguments['searchField']) ? $arguments['searchField'] : 'id'); - $searchValue = (!empty($arguments['searchValue']) AND !empty($this->sourceData[$arguments['searchValue']]) ? $this->sourceData[$arguments['searchValue']] : ''); + $searchValue = ((!empty($arguments['searchValue']) AND !empty($this->sourceData[$arguments['searchValue']])) ? $this->sourceData[$arguments['searchValue']] : ''); $this->generateDocument($arguments['ruleId'],$searchValue, $searchField ,$arguments['rerun'], $action); break; case 'sendNotification': From 20cdf8254cff3156bd1f8cafd6ae0c480be39863 Mon Sep 17 00:00:00 2001 From: myddleware Date: Tue, 8 Oct 2024 17:59:47 +0200 Subject: [PATCH 153/452] Workflow : fix bug on changeData action when the field to be changed is empty Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 7528a63e5..9c95345b3 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -2446,7 +2446,7 @@ public function updateDocumentData(string $docId, array $newValues, string $data } else { // Replace only the new values foreach ($newValues as $key => $value) { - if(isset($values[$key])) { + if(array_key_exists($key,$values)){ // Remove the field if code mdw_no_send_field if (str_contains($value,'mdw_no_send_field')) { unset($values[$key]); @@ -2455,6 +2455,8 @@ public function updateDocumentData(string $docId, array $newValues, string $data $this->message .= ($dataType == 'S' ? 'Source' : ($dataType == 'T' ? 'Target' : 'History')).' value changed from '.$values[$key].' to '.$value.'. '; $values[$key] = $value; } + } else { + $this->message .= ($dataType == 'S' ? 'Source' : ($dataType == 'T' ? 'Target' : 'History')).' field '.$key.' not found in the document. '; } } } From 6b6541bbb4c450bf8422aa28c51250c0076a2844 Mon Sep 17 00:00:00 2001 From: Camille Pichaud <95077335+CamillePMyddleware@users.noreply.github.com> Date: Wed, 9 Oct 2024 17:22:03 +0200 Subject: [PATCH 154/452] Change data update (#1202) * git: add changeData * remove add action in create workflow * add changeData in edit view and clean create view --------- Co-authored-by: cpichaud --- assets/js/editAction.js | 140 +++++--------- assets/js/workflows.js | 194 ++++++++++++++++---- src/Controller/WorkflowActionController.php | 103 ++++++++++- templates/Workflow/new.html.twig | 11 +- templates/Workflow/show.html.twig | 15 +- templates/WorkflowAction/edit.html.twig | 166 +++++++++-------- templates/WorkflowAction/new.html.twig | 168 +++++++++-------- templates/WorkflowAction/show.html.twig | 179 +++++++++--------- 8 files changed, 605 insertions(+), 371 deletions(-) diff --git a/assets/js/editAction.js b/assets/js/editAction.js index 879c27b96..6809d34b6 100644 --- a/assets/js/editAction.js +++ b/assets/js/editAction.js @@ -1,104 +1,54 @@ - $(document).ready(function() { - $('#form_action').change(function() { - console.log('this is the value of the action' + $(this).val()); - if ($(this).val() === 'sendNotification') { - console.log('we show the subject'); - $('#form_subject').parent().show(); - $('#form_to').parent().show(); - $('#form_message').parent().show(); - // hide searchField - $('#form_searchField').parent().hide(); - // set the value of the searchField to null - $('#form_searchField').val(''); - $('#form_searchValue').parent().hide(); - // set the value of the searchValue to null - $('#form_searchValue').val(''); - // hide rule field - $('#form_rule').parent().hide(); - // set the value of the rule to null - $('#form_rule').val(''); - $('#form_status').parent().hide(); - //set the value of the status to null - $('#form_rerun').parent().hide(); - //set the value of the rerun to null - } else if ($(this).val() === 'updateStatus') { - $('#form_status').parent().show(); - $('#form_subject').parent().hide(); - // set the value of the subject to null - $('#form_subject').val(''); - $('#form_to').parent().hide(); - // set the value of the to to null - $('#form_to').val(''); - $('#form_message').parent().hide(); - // set the value of the message to null - $('#form_message').val(''); - $('#form_searchField').parent().hide(); - // set the value of the searchField to null - $('#form_searchField').val(''); - $('#form_searchValue').parent().hide(); - // set the value of the searchValue to null - $('#form_searchValue').val(''); - $('#form_rule').parent().hide(); - // set the value of the rule to null - $('#form_rule').val(''); - $('#form_rerun').parent().hide(); - // set the value of the rerun to null - $('#form_rerun').val(''); - } else if ($(this).val() === 'transformDocument') { - $('#form_status').parent().hide(); - // set the value of the status to null - $('#form_status').val(''); - $('#form_subject').parent().hide(); - // set the value of the subject to null - $('#form_subject').val(''); - $('#form_to').parent().hide(); - // set the value of the to to null - $('#form_to').val(''); - $('#form_message').parent().hide(); - // set the value of the message to null - $('#form_message').val(''); - $('#form_searchField').parent().hide(); - // set the value of the searchField to null - $('#form_searchField').val(''); - $('#form_searchValue').parent().hide(); - // set the value of the searchValue to null - $('#form_searchValue').val(''); - $('#form_rule').parent().hide(); - // set the value of the rule to null - $('#form_rule').val(''); - $('#form_rerun').parent().hide(); - // set the value of the rerun to null - $('#form_rerun').val(''); - } else if ($(this).val() === 'generateDocument') { - $('#form_subject').parent().hide(); - // set the value of the subject to null - $('#form_subject').val(''); - $('#form_to').parent().hide(); - // set the value of the to to null - $('#form_to').val(''); - $('#form_message').parent().hide(); - // set the value of the message to null - $('#form_message').val(''); - $('#form_status').parent().hide(); - // set the value of the status to null - $('#form_status').val(''); - $('#form_searchField').parent().show(); - $('#form_searchValue').parent().show(); - $('#form_rule').parent().show(); - $('#form_rerun').parent().show(); - } +$(document).ready(function() { + $('#form_action').change(function() { + $('#form_subject').parent().hide(); + $('#form_to').parent().hide(); + $('#form_message').parent().hide(); + $('#form_status').parent().hide(); + $('#form_searchField').parent().hide(); + $('#form_searchValue').parent().hide(); + $('#form_rule').parent().hide(); + $('#form_rerun').parent().hide(); + $('#form_subject').val(''); + $('#form_to').val(''); + $('#form_message').val(''); + $('#form_status').val(''); + $('#form_searchField').val(''); + $('#form_searchValue').val(''); + $('#form_rule').val(''); + $('#form_rerun').val(''); + $('#form_targetField').val(''); + $('#form_targetFieldValue').val(''); - }).trigger('change'); - }); + if ($(this).val() === 'sendNotification') { + $('#form_subject').parent().show(); + $('#form_to').parent().show(); + $('#form_targetField').parent().hide(); + $('#form_targetFieldValueContainer').hide(); + $('#form_message').parent().show(); + } else if ($(this).val() === 'updateStatus') { + $('#form_status').parent().show(); + $('#form_targetField').parent().hide(); + $('#form_targetFieldValueContainer').hide(); + } else if ($(this).val() === 'transformDocument') { + $('#form_targetField').parent().hide(); + $('#form_targetFieldValueContainer').hide(); + } else if ($(this).val() === 'generateDocument') { + $('#form_searchField').parent().show(); + $('#form_searchValue').parent().show(); + $('#form_rule').parent().show(); + $('#form_rerun').parent().show(); + $('#form_targetField').parent().hide(); + $('#form_targetFieldValueContainer').hide(); + } - $(document).ready(function() { + }).trigger('change'); +}); + +$(document).ready(function() { $('#form_rule').change(function() { var selectedRule = $(this).val(); - - // Hide all optgroups in form_searchField $('#form_searchField optgroup').hide(); - // Show only the optgroup that matches the selected rule $('#form_searchField optgroup[label="' + selectedRule + '"]').show(); }).trigger('change'); }); \ No newline at end of file diff --git a/assets/js/workflows.js b/assets/js/workflows.js index e956deb2b..b1a3c44d2 100644 --- a/assets/js/workflows.js +++ b/assets/js/workflows.js @@ -1,47 +1,49 @@ $(document).ready(function () { + // Function to hide or show the Rule field + function toggleRuleField() { + var actionValue = $("#form_action").val(); - // Function to hide or show the Rule field - function toggleRuleField() { - var actionValue = $('#form_action').val(); - - if (actionValue === 'transformDocument' || actionValue === 'sendNotification' || actionValue === 'updateStatus') { - $('#form_ruleId').closest('.form-group').hide(); - } else { - $('#form_ruleId').closest('.form-group').show(); - } + if ( + actionValue === "transformDocument" || + actionValue === "sendNotification" || + actionValue === "updateStatus" + ) { + $("#form_ruleId").closest(".form-group").hide(); + } else { + $("#form_ruleId").closest(".form-group").show(); } - + } + + toggleRuleField(); + + $("#form_action").on("change", function () { toggleRuleField(); - - $('#form_action').on('change', function () { - toggleRuleField(); - }); + }); function fetchFilteredData() { - var workflowName = $('#workflow_name').val(); - var ruleName = $('#rule_name').val(); - + var workflowName = $("#workflow_name").val(); + var ruleName = $("#rule_name").val(); $.ajax({ - url: workflowListUrl, - type: 'GET', + url: workflowListUrl, + type: "GET", data: { workflow_name: workflowName, - rule_name: ruleName + rule_name: ruleName, }, success: function (response) { console.log("Réponse reçue :", response); - $('#workflowTableContainer').html(response); + $("#workflowTableContainer").html(response); // $('#workflowTableContainer').html($(response).find('#workflowTableContainer').html()); }, error: function (xhr, status, error) { console.error("Erreur lors de la recherche :", status, error); - } + }, }); } - $('#workflow_name, #rule_name').on('keyup', function () { + $("#workflow_name, #rule_name").on("keyup", function () { fetchFilteredData(); }); @@ -52,7 +54,8 @@ $(document).ready(function () { var newState = $input.is(":checked") ? 1 : 0; var pathArray = window.location.pathname.split("/"); - var basePath = window.location.origin + "/" + pathArray[1] + "/" + pathArray[2]; + var basePath = + window.location.origin + "/" + pathArray[1] + "/" + pathArray[2]; var currentUrl = `${basePath}/${entityType}/${entityType}/toggle/${entityId}`; $.ajax({ @@ -60,22 +63,34 @@ $(document).ready(function () { type: "POST", data: { newState: newState }, beforeSend: function () { - console.log(`Before sending the request for ${entityType} ID: ${entityId}`); + console.log( + `Before sending the request for ${entityType} ID: ${entityId}` + ); }, success: function (response) { - console.log(`Success response received for ${entityType} ID: ${entityId}`, response); + console.log( + `Success response received for ${entityType} ID: ${entityId}`, + response + ); }, error: function (xhr, status, error) { - console.error(`Error received for ${entityType} ID: ${entityId}`, xhr, status, error); + console.error( + `Error received for ${entityType} ID: ${entityId}`, + xhr, + status, + error + ); alert("Erreur lors de la bascule"); }, complete: function (xhr, status) { - console.log(`Request completed for ${entityType} ID: ${entityId}`, status); + console.log( + `Request completed for ${entityType} ID: ${entityId}`, + status + ); }, }); }); - $(".workflow-check-input").on("change", function () { var $input = $(this); var entityId = $input.data("id"); @@ -181,5 +196,122 @@ document.addEventListener("DOMContentLoaded", function () { }); }); - -}); + // WORKFLOWACTION TARGETFIELD + const ruleIdField = document.getElementById("form_ruleId"); + const targetFieldContainer = document.getElementById("targetFieldContainer"); + const targetFieldSelect = document.getElementById("form_targetField"); + const targetFieldValueContainer = document.getElementById("targetFieldValueContainer"); // Le nouveau champ de saisie de valeur + + // Cacher au démarrage + targetFieldContainer.style.display = "none"; + targetFieldValueContainer.style.display = "none"; + const addFieldButton = document.getElementById('addFieldButton'); + const dynamicFieldsContainer = document.getElementById("dynamicFieldsContainer"); + + // Masquer le bouton et le conteneur des champs dynamiques au démarrage + addFieldButton.style.display = "none"; + dynamicFieldsContainer.style.display = "none"; + + ruleIdField.addEventListener("change", function () { + console.log('toto'); + + const ruleId = ruleIdField.value; + + if (ruleId !== "") { + // Afficher le bouton et le conteneur pour ajouter des champs + addFieldButton.style.display = "block"; + dynamicFieldsContainer.style.display = "block"; + + // Envoyer la requête AJAX pour récupérer les champs liés à la règle sélectionnée + const updatedUrl = workflowTargetFieldUrl.replace("ruleFields", ruleId); + $.ajax({ + url: updatedUrl, + type: "GET", + success: function (data) { + console.log("Réponse reçue :", data); + dynamicFieldsContainer.innerHTML = ''; // Vider les anciens champs + + // Ajouter un premier champ Target Field (select) avec les options disponibles + addNewTargetField(data.fields); + }, + error: function (xhr, status, error) { + console.error("Erreur lors de la récupération des champs:", status, error); + }, + }); + } else { + // Cacher les champs si aucune règle n'est sélectionnée + addFieldButton.style.display = "none"; + dynamicFieldsContainer.style.display = "none"; + dynamicFieldsContainer.innerHTML = ''; // Vider les champs si aucune règle n'est sélectionnée + } + }); + + // Fonction pour ajouter un champ targetField (select) et targetFieldValue (input) + function addNewTargetField(fields) { + const newFieldRow = document.createElement('div'); + newFieldRow.classList.add('row', 'mb-4'); + + // Champ targetField (select avec options) + const targetFieldDiv = document.createElement('div'); + targetFieldDiv.classList.add('col-md-6'); + const targetFieldLabel = document.createElement('label'); + targetFieldLabel.innerText = 'Target Field'; + const targetFieldSelect = document.createElement('select'); + targetFieldSelect.name = 'targetFields[]'; + targetFieldSelect.classList.add('form-control'); + + // Ajouter les options au select + fields.forEach(function(field) { + const option = document.createElement('option'); + option.value = field; + option.text = field; + targetFieldSelect.appendChild(option); + }); + + targetFieldDiv.appendChild(targetFieldLabel); + targetFieldDiv.appendChild(targetFieldSelect); + + // Champ targetFieldValue + const targetFieldValueDiv = document.createElement('div'); + targetFieldValueDiv.classList.add('col-md-6'); + const targetFieldValueLabel = document.createElement('label'); + targetFieldValueLabel.innerText = 'New Value'; + const targetFieldValueInput = document.createElement('input'); + targetFieldValueInput.name = 'targetFieldValues[]'; + targetFieldValueInput.type = 'text'; + targetFieldValueInput.classList.add('form-control'); + targetFieldValueDiv.appendChild(targetFieldValueLabel); + targetFieldValueDiv.appendChild(targetFieldValueInput); + + // Bouton de suppression + const removeButtonDiv = document.createElement('div'); + removeButtonDiv.classList.add('col-md-12', 'd-flex', 'justify-content-end', 'mt-2'); + const removeButton = document.createElement('button'); + removeButton.type = 'button'; + removeButton.classList.add('btn', 'btn-danger'); + removeButton.innerText = 'Supprimer'; + removeButton.addEventListener('click', function () { + newFieldRow.remove(); + }); + removeButtonDiv.appendChild(removeButton); + + newFieldRow.appendChild(targetFieldDiv); + newFieldRow.appendChild(targetFieldValueDiv); + newFieldRow.appendChild(removeButtonDiv); + dynamicFieldsContainer.appendChild(newFieldRow); + } + + addFieldButton.addEventListener('click', function () { + + $.ajax({ + url: workflowTargetFieldUrl.replace("ruleFields", ruleIdField.value), + type: "GET", + success: function (data) { + addNewTargetField(data.fields); + }, + error: function (xhr, status, error) { + console.error("Erreur lors de la récupération des champs:", status, error); + }, + }); + }); +}); \ No newline at end of file diff --git a/src/Controller/WorkflowActionController.php b/src/Controller/WorkflowActionController.php index ac9c1a94c..ea0f66a15 100644 --- a/src/Controller/WorkflowActionController.php +++ b/src/Controller/WorkflowActionController.php @@ -42,6 +42,7 @@ use App\Entity\WorkflowLog; use App\Form\ConnectorType; use App\Manager\JobManager; +use App\Entity\DocumentData; use App\Manager\HomeManager; use App\Manager\RuleManager; use Doctrine\ORM\Mapping\Id; @@ -85,6 +86,7 @@ use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\IntegerType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; +use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; @@ -322,11 +324,13 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re 'to' => null, 'subject' => null, 'message' => null, - 'rerun' => null + 'rerun' => null, + 'targetFields' => null, + 'targetFieldValues' => null, // Add other WorkflowAction fields here as needed ]; - $form = $this->createFormBuilder($formData) + $form = $this->createFormBuilder($formData, ['allow_extra_fields' => true]) ->add('name', TextType::class, [ 'label' => 'Action Name', 'required' => true, @@ -388,7 +392,18 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re 'required' => false ]) - + ->add('targetField', ChoiceType::class, [ + 'label' => 'Target Field', + 'choices' => [], + 'required' => false, + 'mapped' => false, + ]) + ->add('targetFieldValues', CollectionType::class, [ + 'entry_type' => TextType::class, + 'allow_add' => true, + 'allow_delete' => true, + 'mapped' => false, + ]) ->add('order', IntegerType::class, [ 'label' => 'Order', @@ -432,7 +447,7 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re $active = $form->get('active')->getData(); $workflowAction->setActive($active); - + // get the to, the subject, and the message using getdata $arguments = []; $to = $form->get('to')->getData(); @@ -480,6 +495,17 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re $arguments['rerun'] = $rerun; } + $formData = $request->request->all(); + $targetFields = $formData['targetFields'] ?? []; + $targetFieldValues = $formData['targetFieldValues'] ?? []; + + if (!empty($targetFields) && !empty($targetFieldValues)) { + foreach ($targetFields as $index => $targetField) { + if (isset($targetFieldValues[$index])) { + $arguments['fields'][$targetField] = $targetFieldValues[$index]; + } + } + } $workflowAction->setArguments($arguments); $em->persist($workflowAction); $em->flush(); @@ -509,6 +535,26 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re } } + /** + * @Route("/get-target-fields/{ruleId}", name="get_target_fields", methods={"GET"}) + */ + public function getTargetFields(string $ruleId, EntityManagerInterface $em): JsonResponse + { + $ruleFields = $em->getRepository(RuleField::class)->findBy(['rule' => $ruleId]); + + if (!$ruleFields) { + return new JsonResponse(['fields' => []], 404); + } + + // get the target fields + $fields = []; + foreach ($ruleFields as $ruleField) { + $fields[] = $ruleField->getTarget(); + } + + return new JsonResponse(['fields' => $fields]); + } + // public function to show the detail view of a single workflow /** * @Route("/showAction/{id}", name="workflow_action_show", defaults={"page"=1}) @@ -690,6 +736,18 @@ public function WorkflowActionEditAction(string $id, Request $request) ]) + ->add('targetFields', CollectionType::class, [ + 'entry_type' => TextType::class, + 'allow_add' => true, + 'allow_delete' => true, + 'mapped' => false, + ]) + ->add('targetFieldValues', CollectionType::class, [ + 'entry_type' => TextType::class, + 'allow_add' => true, + 'allow_delete' => true, + 'mapped' => false, + ]) ->add('order', IntegerType::class, [ 'label' => 'Order', @@ -786,6 +844,19 @@ public function WorkflowActionEditAction(string $id, Request $request) $arguments['rerun'] = 0; } + + + $formData = $request->request->all(); + $targetFields = $formData['targetFields'] ?? []; + $targetFieldValues = $formData['targetFieldValues'] ?? []; + if (is_array($targetFields) && is_array($targetFieldValues)) { + foreach ($targetFields as $index => $targetField) { + if (isset($targetFieldValues[$index])) { + $arguments['fields'][$targetField] = $targetFieldValues[$index]; + } + } + } + $workflowAction->setArguments($arguments); $em->persist($workflowAction); $em->flush(); @@ -915,4 +986,28 @@ public function toggleWorkflowAction(Request $request, EntityManagerInterface $e return new JsonResponse(['status' => 'success', 'active' => $workflowAction->getActive()]); } + + // /** + // * @Route("/update-field-value", name="update_field_value", methods={"POST"}) + // */ + // public function updateFieldValue(Request $request, EntityManagerInterface $em): JsonResponse + // { + // $targetField = $request->request->get('targetField'); + // $newValue = $request->request->get('newValue'); + // $docId = 'oihjkjn'; + + // if ($targetField && $newValue) { + // $documentData = new DocumentData(); + // $documentData->setDocId($docId); + // $documentData->setType('S'); + // $documentData->setData(json_encode([$targetField => $newValue])); + + // $em->persist($documentData); + // $em->flush(); + + // return new JsonResponse(['message' => 'Mise à jour réussie']); + // } + + // return new JsonResponse(['error' => 'Données invalides'], 400); + // } } diff --git a/templates/Workflow/new.html.twig b/templates/Workflow/new.html.twig index 0afe490cb..49b3d2349 100644 --- a/templates/Workflow/new.html.twig +++ b/templates/Workflow/new.html.twig @@ -10,7 +10,7 @@ {{ form_start(form) }}
    - {{ 'view_workflow.edit'| trans }} + {{ 'view_workflow.create'| trans }}
    @@ -41,14 +41,5 @@
    {{ form_end(form) }} - -
    -
    - {{ 'view_workflow.action'| trans }} -
    -
    - -
    -
    {% endblock %} \ No newline at end of file diff --git a/templates/Workflow/show.html.twig b/templates/Workflow/show.html.twig index 5b9afd995..17f681462 100644 --- a/templates/Workflow/show.html.twig +++ b/templates/Workflow/show.html.twig @@ -65,7 +65,7 @@
    {{ 'view_workflow.status'| trans | upper }}
    - +
    @@ -123,9 +123,20 @@
    {% if action.arguments is iterable %}
      + {% for key, argument in action.arguments %}
    • {{ key }}: - {{ argument }}
    • + {% if argument is iterable %} +
        + {% for subKey, subArgument in argument %} +
      • {{ subKey }}: + {{ subArgument }}
      • + {% endfor %} +
      + {% else %} + {{ argument }} + {% endif %} + {% endfor %}
    {% else %} diff --git a/templates/WorkflowAction/edit.html.twig b/templates/WorkflowAction/edit.html.twig index 6e8d60bea..b1e83476b 100644 --- a/templates/WorkflowAction/edit.html.twig +++ b/templates/WorkflowAction/edit.html.twig @@ -1,75 +1,95 @@ {% extends 'base.html.twig' %} -{% block title %}{{ parent() }} | {{ 'view_workflow_action.edit'| trans}}{% endblock %} -{% block titlesm %}{{ 'view_workflow_action.edit'| trans}}{% endblock titlesm %} -{% block body %} -
    - {{ form_start(form) }} -
    -
    - {{ 'view_workflow.edit'|trans }} -
    -
    -
    -
    - {{ form_row(form.name) }} -
    -
    - {{ form_row(form.Workflow) }} -
    -
    -
    -
    - {{ form_row(form.description) }} -
    -
    - {{ form_row(form.action) }} -
    -
    -
    -
    - {{ form_row(form.order) }} -
    -
    - {{ form_row(form.status) }} -
    -
    -
    -
    - {{ form_row(form.active) }} -
    -
    - {{ form_row(form.subject) }} -
    -
    -
    -
    - {{ form_row(form.message) }} -
    -
    - {{ form_row(form.searchField) }} -
    -
    -
    -
    - {{ form_row(form.searchValue) }} -
    -
    - {{ form_row(form.rerun) }} -
    -
    -
    -
    - {{ form_row(form.ruleId) }} -
    -
    - {{ form_row(form.to) }} -
    -
    -
    -
    -
    - {{ form_row(form.submit) }} -
    - {{ form_end(form) }} -
    +{% block title %} + {{ parent() }} + | + {{ 'view_workflow_action.edit'| trans}} {% endblock %} +{% block titlesm %} + {{ 'view_workflow_action.edit'| trans}} +{% endblock titlesm %} +{% block body %} +
    + {{ form_start(form) }} +
    +
    + {{ 'view_workflow.edit'|trans }} +
    +
    +
    +
    + {{ form_row(form.name) }} +
    +
    + {{ form_row(form.Workflow) }} +
    +
    +
    +
    + {{ form_row(form.description) }} +
    +
    + {{ form_row(form.action) }} +
    +
    +
    +
    + {{ form_row(form.order) }} +
    +
    + {{ form_row(form.status) }} +
    +
    +
    +
    + {{ form_row(form.active) }} +
    +
    + {{ form_row(form.subject) }} +
    +
    +
    +
    + {{ form_row(form.message) }} +
    +
    + {{ form_row(form.searchField) }} +
    +
    +
    +
    + {{ form_row(form.searchValue) }} +
    +
    + {{ form_row(form.rerun) }} +
    +
    +
    +
    + {{ form_row(form.ruleId) }} +
    +
    + {{ form_row(form.to) }} +
    +
    +
    + + +
    +
    + +
    +
    + {{ form_row(form.submit) }} +
    + {{ form_end(form) }} +
    + + + {% endblock %} diff --git a/templates/WorkflowAction/new.html.twig b/templates/WorkflowAction/new.html.twig index 2bbbf95f8..0f6919efa 100644 --- a/templates/WorkflowAction/new.html.twig +++ b/templates/WorkflowAction/new.html.twig @@ -1,75 +1,99 @@ {% extends 'base.html.twig' %} -{% block title %}{{ parent() }} | {{ 'view_workflow_action.create'| trans}}{% endblock %} -{% block titlesm %}{{ 'view_workflow_action.create'| trans}}{% endblock titlesm %} +{% block title %} + {{ parent() }} + | + {{ 'view_workflow_action.create'| trans}} +{% endblock %} +{% block titlesm %} + {{ 'view_workflow_action.create'| trans}} +{% endblock titlesm %} {% block body %} -
    - {{ form_start(form) }} -
    -
    - {{ 'view_workflow.edit'|trans }} -
    -
    -
    -
    - {{ form_row(form.name) }} -
    -
    - {{ form_row(form.Workflow) }} -
    -
    -
    -
    - {{ form_row(form.description) }} -
    -
    - {{ form_row(form.action) }} -
    -
    -
    -
    - {{ form_row(form.order) }} -
    -
    - {{ form_row(form.status) }} -
    -
    -
    -
    - {{ form_row(form.active) }} -
    -
    - {{ form_row(form.subject) }} -
    -
    -
    -
    - {{ form_row(form.message) }} -
    -
    - {{ form_row(form.searchField) }} -
    -
    -
    -
    - {{ form_row(form.searchValue) }} -
    -
    - {{ form_row(form.rerun) }} -
    -
    -
    -
    - {{ form_row(form.ruleId) }} -
    -
    - {{ form_row(form.to) }} -
    -
    -
    -
    -
    - {{ form_row(form.submit) }} -
    - {{ form_end(form) }} -
    +
    + {{ form_start(form) }} +
    +
    + {{ 'view_workflow.edit'|trans }} +
    +
    +
    +
    + {{ form_row(form.name) }} +
    +
    + {{ form_row(form.Workflow) }} +
    +
    +
    +
    + {{ form_row(form.description) }} +
    +
    + {{ form_row(form.action) }} +
    +
    +
    +
    + {{ form_row(form.order) }} +
    +
    + {{ form_row(form.status) }} +
    +
    +
    +
    + {{ form_row(form.active) }} +
    +
    + {{ form_row(form.subject) }} +
    +
    +
    +
    + {{ form_row(form.message) }} +
    +
    + {{ form_row(form.searchField) }} +
    +
    +
    +
    + {{ form_row(form.searchValue) }} +
    +
    + {{ form_row(form.rerun) }} +
    +
    +
    +
    + {{ form_row(form.ruleId) }} +
    +
    +
    + + +
    +
    + +
    +
    + {{ form_row(form.to) }} +
    +
    +
    +
    +
    + {{ form_row(form.submit) }} +
    + {{ form_end(form) }} +
    + + + {% endblock %} diff --git a/templates/WorkflowAction/show.html.twig b/templates/WorkflowAction/show.html.twig index 64afb86e8..f28b3a843 100644 --- a/templates/WorkflowAction/show.html.twig +++ b/templates/WorkflowAction/show.html.twig @@ -65,18 +65,29 @@
      {% for key, argument in workflow.arguments %}
    • {{ key }}: - {{ argument }}
    • + {% if argument is iterable %} +
        + {% for subKey, subArgument in argument %} +
      • {{ subKey }}: + {{ subArgument }}
      • + {% endfor %} +
      + {% else %} + {{ argument }} + {% endif %} + {% endfor %}
    {% else %} {{ workflow.arguments }} {% endif %} +
    {{ 'view_workflow.status'|trans|upper }} -
    - +
    +
    @@ -99,87 +110,87 @@ {{ 'view_workflow.logs'| trans | upper }} - {# templates/workflow/show.html.twig #} - -
    - - - - - - - - - - - - - - - - - {% for log in pager.currentPageResults %} - - - - - - - - - - - - - {% endfor %} - -
    {{ 'view_workflow.log_id'|trans }}{{ 'view_workflow.workflow'|trans }}{{ 'view_workflow.job'|trans }}{{ 'view_workflow.trigger_document'|trans }}{{ 'view_workflow.generate_document'|trans }}{{ 'view_workflow.created_by'|trans }}{{ 'view_workflow.status'|trans }}{{ 'view_workflow.date_created'|trans }}{{ 'view_workflow.message'|trans }}{{ 'view_workflow.action_type'|trans }}
    {{ log.id }} - {{ log.workflow.id }} - - {{ log.job.id }} - - {% if log.triggerDocument is not null %} - {{ log.triggerDocument.id }} - {% else %} - - {% endif %} - - {% if log.generateDocument is not null %} - {{ log.generateDocument.id }} - {% else %} - - {% endif %} - {{ log.createdBy }} -
    {{ log.status }}
    -
    {{ log.dateCreated|date('Y-m-d H:i:s') }}{{ log.getMessage }}{{ log.action.action }}
    - - {# Pagination links #} - {% if pager.haveToPaginate %} - - {% endif %} -
    + {# templates/workflow/show.html.twig #} + +
    + + + + + + + + + + + + + + + + + {% for log in pager.currentPageResults %} + + + + + + + + + + + + + {% endfor %} + +
    {{ 'view_workflow.log_id'|trans }}{{ 'view_workflow.workflow'|trans }}{{ 'view_workflow.job'|trans }}{{ 'view_workflow.trigger_document'|trans }}{{ 'view_workflow.generate_document'|trans }}{{ 'view_workflow.created_by'|trans }}{{ 'view_workflow.status'|trans }}{{ 'view_workflow.date_created'|trans }}{{ 'view_workflow.message'|trans }}{{ 'view_workflow.action_type'|trans }}
    {{ log.id }} + {{ log.workflow.id }} + + {{ log.job.id }} + + {% if log.triggerDocument is not null %} + {{ log.triggerDocument.id }} + {% else %} + + {% endif %} + + {% if log.generateDocument is not null %} + {{ log.generateDocument.id }} + {% else %} + + {% endif %} + {{ log.createdBy }} +
    {{ log.status }}
    +
    {{ log.dateCreated|date('Y-m-d H:i:s') }}{{ log.getMessage }}{{ log.action.action }}
    + + {# Pagination links #} + {% if pager.haveToPaginate %} + + {% endif %} +
    From 43eb9799e09f92c048012b12df3da46ef88ec964 Mon Sep 17 00:00:00 2001 From: Camille Pichaud <95077335+CamillePMyddleware@users.noreply.github.com> Date: Wed, 9 Oct 2024 17:31:06 +0200 Subject: [PATCH 155/452] Homepage premium (#1203) * new homepage * update section graph * clean and change color * change nav bar --------- Co-authored-by: cpichaud --- assets/css/custom.css | 500 +- assets/css/home.css | 103 + assets/css/layout.css | 591 +- assets/css/menu.css | 302 +- assets/css/rule.css | 4 +- assets/images/template/logo.jpg | Bin 0 -> 15736 bytes .../images/template/{logo.png => logo1.png} | Bin assets/js/home.js | 256 +- src/Controller/ApiController.php | 954 +-- src/Controller/DefaultController.php | 6236 ++++++++--------- src/Manager/HomeManager.php | 170 +- src/Repository/DocumentRepository.php | 24 +- templates/Home/index.html.twig | 267 +- templates/base.html.twig | 390 +- translations/messages.en.yml | 21 +- translations/messages.fr.yml | 21 +- 16 files changed, 4933 insertions(+), 4906 deletions(-) create mode 100644 assets/images/template/logo.jpg rename assets/images/template/{logo.png => logo1.png} (100%) mode change 100755 => 100644 diff --git a/assets/css/custom.css b/assets/css/custom.css index 9d47a3653..3e6184da6 100755 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -1,250 +1,250 @@ -#logo { - background: url('./../images/template/logo.png') no-repeat; - background-size: 158px; - height: 113px; - width: 160px; - display: inline-block; -} -menu .caret, #myd_top .caret { - background: url('./../images/template/arrow-menu.png') no-repeat; - height: 13px; - width: 14px; - border: none; -} - -#fd-title-small { - background: url('./../images/template/fd-title-small.png') no-repeat; - height: 71px; - width: 180px; - display: inline-block; - border: none; - margin: 20px 20px 20px -13px; - padding-top: 20px; - color: white; - text-align: center; - line-height: 0.55em; -} - -#fd-title-large { - background: url('./../images/template/fd-title-large.png') no-repeat; - height: 71px; - width: 600px; - padding: 15px 0 0 16px; - display: inline-block; - border: none; - margin: 20px 20px 20px -13px; - color: white; - text-align: left; - line-height: 1em; -} - -#fd-title-large p { - margin: 0 0 2px !important; -} - -#fd-title { - background: url('./../images/template/fd-title.png') no-repeat; - height: 71px; - width: 293px; - display: inline-block; - border: none; - margin: 20px 20px 20px 0px; - padding-top: 15px; - color: white; - font-size: 0.9em; - line-height: 0.6em; - padding-left: 8px; - text-align: left; -} - -/* buttons */ - -.btn-myd, .btn-mydinv, .btn-mydinv:hover, .btn-myd:hover { - color: #404042; - font-weight: bold; - background: url('./../images/template/btn.png') no-repeat; - height: 31px; - min-width: 196px; - border: none; - display:inline-block; - text-align: center; -} - -.btn-small-myd, .btn-small-mydinv, .btn-small-mydinv:hover, .btn-small-myd:hover { - color: #404042; - font-weight: bold; - background: url('./../images/template/btn-small.png') no-repeat; - height: 31px; - width: 50px; - border: none; - display:inline-block; -} - -.btn-middle-myd, .btn-myddle-mydinv, .btn-middle-mydinv:hover, .btn-middle-myd:hover { - color: #404042; - font-weight: bold; - background: url('./../images/template/btn-middle.png') no-repeat; - height: 31px; - width: 100px; - border: none; - display:inline-block; -} - -.btn-large-myd, .btn-large-mydinv, .btn-large-mydinv:hover, .btn-large-myd:hover { - color: #404042; - font-weight: bold; - background: url('./../images/template/btn-large.png') no-repeat; - height: 31px; - width: 340px; - border: none; - display:inline-block; -} - -.btn-myd, .btn-small-myd, .btn-middle-myd, .btn-large-myd { - background-position: left top; -} - -.btn-myd:hover, .btn-small-myd:hover, .btn-middle-myd:hover, .btn-large-myd:hover { - background-position: left bottom; - text-decoration:none; - cursor:pointer; -} - -.btn-mydinv, .btn-small-mydinv, .btn-middle-mydinv, .btn-large-mydinv { - background-position: left bottom; -} - -.btn-mydinv:hover, .btn-small-mydinv:hover, .btn-middle-mydinv:hover, .btn-large-mydinv:hover { - background-position: left top; - text-decoration:none; - cursor:pointer; -} - -.myd_div_loading{ - background: url('../images/loading.gif') no-repeat center center; -} - -.myd_div_loading_logo{ - background: url('../images/logo/logo.png') no-repeat center center; -} - - -#animation .loader-source { - background: url('../images/loader-source.gif'); - background-repeat:no-repeat; -} - -#animation .loader-cible { - background: url('../images/loader-cible.gif'); - background-repeat:no-repeat; -} - -#animation-myddleware-back-1 > .animation-myddleware-logo { - background: url('../images/logo/text.png'); -} - -#animation-myddleware-back-2 > .animation-myddleware-logo { - background: url('../images/logo/up.png'); -} - -#animation-myddleware-back-3 > .animation-myddleware-logo { - background: url('../images/logo/down.png') right top; -} - -#animation .arcleft-c { - background: url('../images/template/left-arc-color.png') no-repeat right; - height: 165px; - -webkit-transition: all 0.5s ease-in-out; - -moz-transition: all 0.5s ease-in-out; - -o-transition: all 0.5s ease-in-out; - -ms-transition: all 0.5s ease-in-out; - transition: all 0.5s ease-in-out; -} - -#animation .arcleft-g { - background: url('../images/template/left-arc-gray.png') no-repeat right; - height: 165px; - -webkit-transition: all 0.5s ease-in-out; - -moz-transition: all 0.5s ease-in-out; - -o-transition: all 0.5s ease-in-out; - -ms-transition: all 0.5s ease-in-out; - transition: all 0.5s ease-in-out; -} - -#animation .arcright-c { - background: url('../images/template/right-arc-color.png') no-repeat left; - height: 165px; - -webkit-transition: all 0.5s ease-in-out; - -moz-transition: all 0.5s ease-in-out; - -o-transition: all 0.5s ease-in-out; - -ms-transition: all 0.5s ease-in-out; - transition: all 0.5s ease-in-out; -} - -#animation .arcright-g { - background: url('../images/template/right-arc-gray.png') no-repeat left; - height: 165px; - -webkit-transition: all 0.5s ease-in-out; - -moz-transition: all 0.5s ease-in-out; - -o-transition: all 0.5s ease-in-out; - -ms-transition: all 0.5s ease-in-out; - transition: all 0.5s ease-in-out; -} - -#animation .animation-myddleware-logo { - background-size: 105px 105px; - background-repeat: no-repeat; -} - -#animation .animation-content-left-toggle-btn, #animation .animation-content-right-toggle-btn { - background: #CACACA; - background-image: url('../images/data.png'); - background-size: 15px 22px; - background-position: 10px 5px; - background-repeat: no-repeat; -} - -/* Documents list form (with filters) */ -#document-list-form { - margin-bottom: 2vh; - padding: 2em; -} - - -/* CUSTOM BUTTON BOOSTRAP */ - -.btn-outline-success:hover{ - background-color: #19ca42 !important; - color:white !important; -} - -.btn-outline-success{ - color: #19ca42 !important; - border-color: #19ca42 !important; -} - -.btn-outline-primary:hover{ - background-color: #198BCA !important; - color:white !important; -} - -.btn-outline-primary{ - color: #198BCA !important; - border-color: #198BCA !important; -} - -.btn-primary { - background-color: #198BCA!important; - border-color: #198BCA!important; -} - - -.btn-primary:hover { - background-color: #0F66A9!important; - border-color: #0F66A9!important; -} - -.btn{ - border-radius: 0% !important; -} - +#logo { + background: url('./../images/template/logo.jpg') no-repeat; + background-size: 209px; + height: 185px; + width: 240px; + display: inline-block; +} +menu .caret, #myd_top .caret { + background: url('./../images/template/arrow-menu.png') no-repeat; + height: 13px; + width: 14px; + border: none; +} + +#fd-title-small { + background: url('./../images/template/fd-title-small.png') no-repeat; + height: 71px; + width: 180px; + display: inline-block; + border: none; + margin: 20px 20px 20px -13px; + padding-top: 20px; + color: white; + text-align: center; + line-height: 0.55em; +} + +#fd-title-large { + background: url('./../images/template/fd-title-large.png') no-repeat; + height: 71px; + width: 600px; + padding: 15px 0 0 16px; + display: inline-block; + border: none; + margin: 20px 20px 20px -13px; + color: white; + text-align: left; + line-height: 1em; +} + +#fd-title-large p { + margin: 0 0 2px !important; +} + +#fd-title { + background: url('./../images/template/fd-title.png') no-repeat; + height: 71px; + width: 293px; + display: inline-block; + border: none; + margin: 20px 20px 20px 0px; + padding-top: 15px; + color: white; + font-size: 0.9em; + line-height: 0.6em; + padding-left: 8px; + text-align: left; +} + +/* buttons */ + +.btn-myd, .btn-mydinv, .btn-mydinv:hover, .btn-myd:hover { + color: #404042; + font-weight: bold; + background: url('./../images/template/btn.png') no-repeat; + height: 31px; + min-width: 196px; + border: none; + display:inline-block; + text-align: center; +} + +.btn-small-myd, .btn-small-mydinv, .btn-small-mydinv:hover, .btn-small-myd:hover { + color: #404042; + font-weight: bold; + background: url('./../images/template/btn-small.png') no-repeat; + height: 31px; + width: 50px; + border: none; + display:inline-block; +} + +.btn-middle-myd, .btn-myddle-mydinv, .btn-middle-mydinv:hover, .btn-middle-myd:hover { + color: #404042; + font-weight: bold; + background: url('./../images/template/btn-middle.png') no-repeat; + height: 31px; + width: 100px; + border: none; + display:inline-block; +} + +.btn-large-myd, .btn-large-mydinv, .btn-large-mydinv:hover, .btn-large-myd:hover { + color: #404042; + font-weight: bold; + background: url('./../images/template/btn-large.png') no-repeat; + height: 31px; + width: 340px; + border: none; + display:inline-block; +} + +.btn-myd, .btn-small-myd, .btn-middle-myd, .btn-large-myd { + background-position: left top; +} + +.btn-myd:hover, .btn-small-myd:hover, .btn-middle-myd:hover, .btn-large-myd:hover { + background-position: left bottom; + text-decoration:none; + cursor:pointer; +} + +.btn-mydinv, .btn-small-mydinv, .btn-middle-mydinv, .btn-large-mydinv { + background-position: left bottom; +} + +.btn-mydinv:hover, .btn-small-mydinv:hover, .btn-middle-mydinv:hover, .btn-large-mydinv:hover { + background-position: left top; + text-decoration:none; + cursor:pointer; +} + +.myd_div_loading{ + background: url('../images/loading.gif') no-repeat center center; +} + +.myd_div_loading_logo{ + background: url('../images/logo/logo.png') no-repeat center center; +} + + +#animation .loader-source { + background: url('../images/loader-source.gif'); + background-repeat:no-repeat; +} + +#animation .loader-cible { + background: url('../images/loader-cible.gif'); + background-repeat:no-repeat; +} + +#animation-myddleware-back-1 > .animation-myddleware-logo { + background: url('../images/logo/text.png'); +} + +#animation-myddleware-back-2 > .animation-myddleware-logo { + background: url('../images/logo/up.png'); +} + +#animation-myddleware-back-3 > .animation-myddleware-logo { + background: url('../images/logo/down.png') right top; +} + +#animation .arcleft-c { + background: url('../images/template/left-arc-color.png') no-repeat right; + height: 165px; + -webkit-transition: all 0.5s ease-in-out; + -moz-transition: all 0.5s ease-in-out; + -o-transition: all 0.5s ease-in-out; + -ms-transition: all 0.5s ease-in-out; + transition: all 0.5s ease-in-out; +} + +#animation .arcleft-g { + background: url('../images/template/left-arc-gray.png') no-repeat right; + height: 165px; + -webkit-transition: all 0.5s ease-in-out; + -moz-transition: all 0.5s ease-in-out; + -o-transition: all 0.5s ease-in-out; + -ms-transition: all 0.5s ease-in-out; + transition: all 0.5s ease-in-out; +} + +#animation .arcright-c { + background: url('../images/template/right-arc-color.png') no-repeat left; + height: 165px; + -webkit-transition: all 0.5s ease-in-out; + -moz-transition: all 0.5s ease-in-out; + -o-transition: all 0.5s ease-in-out; + -ms-transition: all 0.5s ease-in-out; + transition: all 0.5s ease-in-out; +} + +#animation .arcright-g { + background: url('../images/template/right-arc-gray.png') no-repeat left; + height: 165px; + -webkit-transition: all 0.5s ease-in-out; + -moz-transition: all 0.5s ease-in-out; + -o-transition: all 0.5s ease-in-out; + -ms-transition: all 0.5s ease-in-out; + transition: all 0.5s ease-in-out; +} + +#animation .animation-myddleware-logo { + background-size: 105px 105px; + background-repeat: no-repeat; +} + +#animation .animation-content-left-toggle-btn, #animation .animation-content-right-toggle-btn { + background: #CACACA; + background-image: url('../images/data.png'); + background-size: 15px 22px; + background-position: 10px 5px; + background-repeat: no-repeat; +} + +/* Documents list form (with filters) */ +#document-list-form { + margin-bottom: 2vh; + padding: 2em; +} + + +/* CUSTOM BUTTON BOOSTRAP */ + +.btn-outline-success:hover{ + background-color: #19ca42 !important; + color:white !important; +} + +.btn-outline-success{ + color: #19ca42 !important; + border-color: #19ca42 !important; +} + +.btn-outline-primary:hover{ + background-color: #198BCA !important; + color:white !important; +} + +.btn-outline-primary{ + color: #198BCA !important; + border-color: #198BCA !important; +} + +.btn-primary { + background-color: #198BCA!important; + border-color: #198BCA!important; +} + + +.btn-primary:hover { + background-color: #0F66A9!important; + border-color: #0F66A9!important; +} + +.btn{ + border-radius: 0% !important; +} + diff --git a/assets/css/home.css b/assets/css/home.css index d0747c206..937d6e85e 100755 --- a/assets/css/home.css +++ b/assets/css/home.css @@ -154,4 +154,107 @@ color: #d40015; border: solid 0.5px #DC3545; background-color: rgb(249, 155, 155); +} + +/* NEW HOME */ + +#myd_title:has(.homepage_welcome) { + height: 300px !important; +} + +.homepage_welcome { + text-align: center; + font-size: xx-large; +} + +.homepage_welcome_text{ + font-size: large; + color: #777777; +} + +#bnt_welcome{ + border-radius: 30px !important; +} + +.solution_homepage{ + min-height: 265px !important; + margin-top: 50px !important; + background-color: #f8f9f9 !important; +} + +.inline-images { + list-style-type: none; + padding: 0; + margin: 0; + display: flex; + flex-wrap: nowrap; + justify-content: space-between; +} + +.inline-images li { + margin-right: 15px; +} + +.inline-images img { + max-width: 100px; + height: auto; +} + +.solution_liste_homepage{ + padding: 5px; + background-color: white; + border: 0.5px solid #7777; + box-shadow: rgb(0 0 0 / 15%) 0px 15px 25px, rgb(0 0 0 / 5%) 0px 5px 10px; +} + +.title_section_homepage{ + font-weight: bold !important; +} +.title_solution_homepage{ + margin-top: 25px; +} + +.contact-panel { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} + +.contact-panel h4 { + margin-bottom: 10px; +} + +.contact-panel p { + margin-bottom: 5px; +} + +.icon_small_homepage{ + width: 50px; + height: 60px; +} + +.icon-container { + display: flex; + justify-content: center; + align-items: center; +} + +#countNbDocuments { + font-size: xx-large; + font-weight: bold; +} + +.title_countNbDocuments{ + color :#777777; +} + +.error_rule_homepage{ + text-decoration: none; + color: #777777; +} + +.error_rule_homepage:hover{ + color: #03c4eb; + font-weight: bold; } \ No newline at end of file diff --git a/assets/css/layout.css b/assets/css/layout.css index 78f04214c..625c2d871 100755 --- a/assets/css/layout.css +++ b/assets/css/layout.css @@ -1,296 +1,295 @@ -/********************************************************************************* - * This file is part of Myddleware. - - * @package Myddleware - * @copyright Copyright (C) 2013 - 2015 St�phane Faure - CRMconsult EURL - * @copyright Copyright (C) 2015 - 2016 St�phane Faure - Myddleware ltd - contact@myddleware.com - * @link http://www.myddleware.com - - This file is part of Myddleware. - - Myddleware is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Myddleware is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Myddleware. If not, see . -*********************************************************************************/ - -#myd_top { - min-height: 40px; - background: #444446; - border-bottom: 4px solid #198BCA; - position: fixed; - top: 0; - left: 0; - right: 0; - z-index: 99; - font-size: larger; -} - -#myd_title { - height: 50px; - background: #198BCA; - margin: 0 auto; - color: white; - padding: 14px; - font-weight: bold; -} - -#myd_title a, #myd_title a:hover { - color: #ffffff; -} - -.myd_loading > img{ - display: none; - width: 43px; - height: 11px; -} - -header { - height: auto; - margin: 0; -} - -section { - margin-bottom: 15vh!important; - /* width: 80vw; */ - min-height: 570px; - background: white !important; -} - -footer { - padding: 5px; - height: 35px; - min-height: 35px; - max-height: 35px; - background: #444446; - position: fixed; - right:0; - bottom: 0; - left: 0; - min-width: 1200px; - width: 100% auto; - color: white; - text-align: center; - z-index: 99; -} - -.clr { - clear: both; -} - -.ctr { - text-align: center; -} - -.left { - float: left; - text-align:left; -} - -.right { - text-align:right; - float: right; -} - - -.task { - width: 150px; - max-width: 150px; - margin: 5px; - display: inline-block; -} - - -/* tabs */ - -#tabs { - border: 0px !important; -} - -#tabs ul.ui-widget-header { - background: none; - border: none; -} - -#tabs ul.ui-widget-header li { - height: 80px; - min-width: 173px; - max-width: 173px; - margin: -1px; -} - -#tabs .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { - background: #D7D8DA; - border-left: 2px solid #B7B7B9; - border-right: 2px solid #B7B7B9; - color: #AAAAAC; - opacity: 1; -} - -#tabs .ui-state-active { - background: white; - color: #454547; -} - -#tabs .ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { - color: #454547; -} - -#tabs .ui-corner-all, #tabs .ui-corner-top, #tabs .ui-corner-right, #tabs .ui-corner-tr, -#tabs .ui-corner-left, #tabs .ui-corner-tl, -#tabs.ui-corner-br, #tabs .ui-corner-bottom, #tabs .ui-corner-bl { - border-top-right-radius: 0px; - border-top-left-radius: 0px; -} - -#tabs .ui-corner-all, #tabs .ui-corner-bottom, #tabs .ui-corner-right, #tabs .ui-corner-br { - border-bottom-right-radius: 0px; -} -#tabs .ui-corner-all, #tabs .ui-corner-bottom, #tabs .ui-corner-left, #tabs .ui-corner-bl { - border-bottom-left-radius: 0px; -} - -.ui-tabs, .ui-tabs-nav { - padding: 0em !important; -} - -#tabs .ui-tabs .ui-tabs-nav li.ui-tabs-active, #tabs .ui-state-hover, -#tabs .ui-state-active { - border-top: 0px !important; -} - -#tabs .ui-state-disabled { - background: #7D7D7F; - border-top: 9px solid #9E9937; -} - -#tabs .ui-state-disabled a.ui-tabs-anchor { - color: #343436; -} - -#tabs ul.ui-tabs-nav { - width: 1200px; -} - -#notification { - margin: 0 auto; - background:white; - display:none; -} - -#notification #zone_notification { - margin: 0 auto 20px; - text-align: left; - width: 80vw; - max-width: 80vw; - max-height: 120px; - overflow-y:scroll; - padding: 0; - - -moz-box-shadow: 5px 5px 5px 0px #656565; - -webkit-box-shadow: 5px 5px 5px 0px #656565; - -o-box-shadow: 5px 5px 5px 0px #656565; - box-shadow: 5px 5px 5px 0px #656565; - filter:progid:DXImageTransform.Microsoft.Shadow(color=#656565, Direction=134, Strength=5); - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - border-radius: 5px; -} - -#notification #zone_notification li { - list-style-type: none; - padding-left:15px; - min-height: 30px; -} - -#notification #zone_notification li .glyphicon { - float: left; - margin: 0 5px; -} - -#notification #zone_notification .info { - color: #0F66A9; - background: #BFE2FC; -} - -#notification #zone_notification .warning { - color: #f1b753; - background: #ffeccc; -} - -#notification #zone_notification .error { - color: #d60405; - background: #f2dedf; -} - -#notification #zone_notification .success { - color: #9aa932; - background: white; -} - -/* help content */ -.help-content { - border-top: 1px solid #ccc; - margin: 20px 0; - padding-top: 20px; - text-align: justify; - line-height: 2em; -} - -.help-content a { - text-decoration: underline; -} - -.help-content a:hover { - color:#0F66A9; -} - -#blockaddfield { - margin: 10px 0px; - float:right; -} - -/* Update SF4 */ - -/* Page Create connector */ - -.create-connector-tab { - min-width: 100%!important; - max-width: 100%!important; - width: 100%; - text-align: center; - align-items: center; -} - -.create-connector-tab a { - width: 100%; -} - -/* Page reset password (rule/account/reset-password) */ -#reset-password-container { - max-width: 500px; - -} - -/* wrap form within a card */ -#reset-password-col { - margin-top: 50px; - margin-bottom: 50px; - padding: 50px; - border: solid 1px rgba(0,0,0, 0.1); - box-shadow: 3px 3px 3px rgba(0,0,0,0.1); -} - -/* NB CONNECTOR */ - .nb-connector p{ - margin-bottom: 0.35rem !important; - } - +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 St�phane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 St�phane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Myddleware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Myddleware. If not, see . +*********************************************************************************/ + +#myd_top { + min-height: 40px; + position: fixed; + top: 0; + right: -135px; + z-index: 99; + font-size: larger; +} + +#myd_title { + box-shadow: rgba(0, 0, 0, 0.06) 0px 2px 4px 0px inset; + height: 50px; + background: #f8f9f9; + margin: 0 auto; + color: rgb(0, 0, 0); + padding: 14px; + font-weight: bold; +} + +#myd_title a, #myd_title a:hover { + color: #000000; + text-decoration: none; +} + +.myd_loading > img{ + display: none; + width: 43px; + height: 11px; +} + +header { + height: auto; + margin: 0; +} + +section { + margin-bottom: 15vh!important; + /* width: 80vw; */ + min-height: 570px; + background: white !important; +} + +footer { + padding: 5px; + height: 35px; + min-height: 35px; + max-height: 35px; + background: #444446; + position: fixed; + right:0; + bottom: 0; + left: 0; + min-width: 1200px; + width: 100% auto; + color: white; + text-align: center; + z-index: 99; +} + +.clr { + clear: both; +} + +.ctr { + text-align: center; +} + +.left { + float: left; + text-align:left; +} + +.right { + text-align:right; + float: right; +} + + +.task { + width: 150px; + max-width: 150px; + margin: 5px; + display: inline-block; +} + + +/* tabs */ + +#tabs { + border: 0px !important; +} + +#tabs ul.ui-widget-header { + background: none; + border: none; +} + +#tabs ul.ui-widget-header li { + height: 80px; + min-width: 173px; + max-width: 173px; + margin: -1px; +} + +#tabs .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { + background: #D7D8DA; + border-left: 2px solid #B7B7B9; + border-right: 2px solid #B7B7B9; + color: #AAAAAC; + opacity: 1; +} + +#tabs .ui-state-active { + background: white; + color: #454547; +} + +#tabs .ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { + color: #454547; +} + +#tabs .ui-corner-all, #tabs .ui-corner-top, #tabs .ui-corner-right, #tabs .ui-corner-tr, +#tabs .ui-corner-left, #tabs .ui-corner-tl, +#tabs.ui-corner-br, #tabs .ui-corner-bottom, #tabs .ui-corner-bl { + border-top-right-radius: 0px; + border-top-left-radius: 0px; +} + +#tabs .ui-corner-all, #tabs .ui-corner-bottom, #tabs .ui-corner-right, #tabs .ui-corner-br { + border-bottom-right-radius: 0px; +} +#tabs .ui-corner-all, #tabs .ui-corner-bottom, #tabs .ui-corner-left, #tabs .ui-corner-bl { + border-bottom-left-radius: 0px; +} + +.ui-tabs, .ui-tabs-nav { + padding: 0em !important; +} + +#tabs .ui-tabs .ui-tabs-nav li.ui-tabs-active, #tabs .ui-state-hover, +#tabs .ui-state-active { + border-top: 0px !important; +} + +#tabs .ui-state-disabled { + background: #7D7D7F; + border-top: 9px solid #9E9937; +} + +#tabs .ui-state-disabled a.ui-tabs-anchor { + color: #343436; +} + +#tabs ul.ui-tabs-nav { + width: 1200px; +} + +#notification { + margin: 0 auto; + background:white; + display:none; +} + +#notification #zone_notification { + margin: 0 auto 20px; + text-align: left; + width: 80vw; + max-width: 80vw; + max-height: 120px; + overflow-y:scroll; + padding: 0; + + -moz-box-shadow: 5px 5px 5px 0px #656565; + -webkit-box-shadow: 5px 5px 5px 0px #656565; + -o-box-shadow: 5px 5px 5px 0px #656565; + box-shadow: 5px 5px 5px 0px #656565; + filter:progid:DXImageTransform.Microsoft.Shadow(color=#656565, Direction=134, Strength=5); + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; +} + +#notification #zone_notification li { + list-style-type: none; + padding-left:15px; + min-height: 30px; +} + +#notification #zone_notification li .glyphicon { + float: left; + margin: 0 5px; +} + +#notification #zone_notification .info { + color: #0F66A9; + background: #BFE2FC; +} + +#notification #zone_notification .warning { + color: #f1b753; + background: #ffeccc; +} + +#notification #zone_notification .error { + color: #d60405; + background: #f2dedf; +} + +#notification #zone_notification .success { + color: #9aa932; + background: white; +} + +/* help content */ +.help-content { + border-top: 1px solid #ccc; + margin: 20px 0; + padding-top: 20px; + text-align: justify; + line-height: 2em; +} + +.help-content a { + text-decoration: underline; +} + +.help-content a:hover { + color:#0F66A9; +} + +#blockaddfield { + margin: 10px 0px; + float:right; +} + +/* Update SF4 */ + +/* Page Create connector */ + +.create-connector-tab { + min-width: 100%!important; + max-width: 100%!important; + width: 100%; + text-align: center; + align-items: center; +} + +.create-connector-tab a { + width: 100%; +} + +/* Page reset password (rule/account/reset-password) */ +#reset-password-container { + max-width: 500px; + +} + +/* wrap form within a card */ +#reset-password-col { + margin-top: 50px; + margin-bottom: 50px; + padding: 50px; + border: solid 1px rgba(0,0,0, 0.1); + box-shadow: 3px 3px 3px rgba(0,0,0,0.1); +} + +/* NB CONNECTOR */ + .nb-connector p{ + margin-bottom: 0.35rem !important; + } + diff --git a/assets/css/menu.css b/assets/css/menu.css index 8cdf3b6a7..b78d34684 100644 --- a/assets/css/menu.css +++ b/assets/css/menu.css @@ -1,147 +1,155 @@ -/********************************************************************************* - * This file is part of Myddleware. - - * @package Myddleware - * @copyright Copyright (C) 2013 - 2015 St�phane Faure - CRMconsult EURL - * @copyright Copyright (C) 2015 - 2016 St�phane Faure - Myddleware ltd - contact@myddleware.com - * @link http://www.myddleware.com - - This file is part of Myddleware. - - Myddleware is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Myddleware is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Myddleware. If not, see . -*********************************************************************************/ - -menu { - margin-top: 40px; -} - -menu ul, menu li { - list-style: none; -} - -menu li { - background: #ffffff; - display: inline-block; - width: 330px; - padding-top: 32px; - height: 95px; - text-align: center; - font-weight: bold; -} - -menu .dropdown-menu > li > a, menu .dropdown-menu > li > a:hover { - background-color:transparent; - background-image:transparent; - background:transparent; -} - - - -menu .dropdown-menu > li > a { - color: #000000; -} - -menu .dropdown-menu > li > a:hover { - color: #198BCA; -} - -menu li li, #myd_top li li { - background: white; - text-align: left; - overflow:hidden; - height: 25px; - padding: 0; - padding-left: 10px; -} - - -/* Dropdown menu */ - - -.dropdown-menu{ - margin-left: -48px!important; - margin-top: 19px!important; -} - -menu li li:hover, #myd_top li li:hover { - background: transparent; -} - -#myd_top .nav-pills > li > a:focus, menu .nav-pills > li > a:focus { - background: transparent; -} - -#myd_top li a { - color: white; - text-decoration: none; -} - -#myd_top li li a { - color: #000000; -} - -menu .nav-pills > li > a, menu .nav-pills > li > a:hover, menu .nav .open > a, menu .nav .open > a:hover, menu .nav .open > a:focus { - color: rgb(0, 0, 0); - background-color:transparent; - border-radius: none; -} - -menu li:hover { - background: #198BCA; - color:white; - cursor:pointer; -} - -menu .logo-nav:hover{ - background: #ffffff; -} - -#myd_top nav { - padding-right: 150px; -} - -#myd_top .nav-pills > li > a, #myd_top .nav-pills > li > a:hover, #myd_top .nav .open > a, #myd_top .nav .open > a:hover, #myd_top .nav .open > a:focus { - color: white; - background-color:transparent; - border-radius: none; -} - -#myd_top .nav-pills > li > a:hover, #myd_top .nav .open > a, #myd_top .nav .open > a:hover, #myd_top .nav .open > a:focus { - color: #198BCA; -} - -#search { - padding-top: 25px; -} - -menu li a { - text-decoration: none; -} - -/* LOGO */ -.logo-nav{ - padding-top: 5px; - margin-right: 120px; - margin-left: 40px; -} - -.flex-nav{ - flex-wrap: initial !important; - width: 87%; - margin-right:auto; -} - -header{ - background-color: #ffffff !important; -} +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 St�phane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 St�phane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Myddleware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Myddleware. If not, see . +*********************************************************************************/ + +/* menu { + margin-top: 40px; +} */ + +menu ul, menu li { + list-style: none; +} + +menu li { + background: #ffffff; + display: inline-block; + width: 233px; + padding-top: 12px; + height: 57px; + text-align: center; + font-weight: bold; +} + +menu .dropdown-menu > li > a, menu .dropdown-menu > li > a:hover { + background-color:transparent; + background-image:transparent; + background:transparent; +} + + + +menu .dropdown-menu > li > a { + color: #000000; +} + +menu .dropdown-menu > li > a:hover { + color: #03c4eb; +} + +menu li li, #myd_top li li { + background: white; + text-align: left; + overflow:hidden; + height: 25px; + padding: 0; + padding-left: 10px; +} + + +/* Dropdown menu */ + + +.dropdown-menu{ + margin-left: -48px!important; + margin-top: 19px!important; +} + +menu li li:hover, #myd_top li li:hover { + background: transparent; +} + +#myd_top .nav-pills > li > a:focus, menu .nav-pills > li > a:focus { + background: transparent; +} + +#myd_top li a { + color: white; + text-decoration: none; +} + +#myd_top li li a { + color: #000000; +} + +menu .nav-pills > li > a, menu .nav .open > a, menu .nav .open > a:hover, menu .nav .open > a:focus { + color: rgb(0, 0, 0); + background-color:transparent; + border-radius: none; +} + +menu li:hover , menu .nav-pills > li > a:hover { + color:#03c4eb !important; + cursor:pointer; +} + +menu .logo-nav:hover{ + background: #ffffff; +} + +#myd_top nav { + padding-right: 150px; +} + +#myd_top .nav-pills > li > a, #myd_top .nav-pills > li > a:hover, #myd_top .nav .open > a, #myd_top .nav .open > a:hover, #myd_top .nav .open > a:focus { + color: white; + background-color:transparent; + border-radius: none; +} + +#myd_top .nav-pills > li > a:hover, #myd_top .nav .open > a, #myd_top .nav .open > a:hover, #myd_top .nav .open > a:focus { + color: #198BCA; +} + +#search { + padding-top: 25px; +} + +menu li a { + text-decoration: none; +} + +/* LOGO */ +.logo-nav{ + padding-top: 5px; + margin-right: 120px; + margin-left: 40px; +} + +.flex-nav{ + flex-wrap: initial !important; + width: 92%; + margin-right:auto; +} + +header{ + background-color: #ffffff !important; +} + + +.dropdown:hover .dropdown-menu { + display: block; +} + +.dropdown-toggle::after { + display: none; +} \ No newline at end of file diff --git a/assets/css/rule.css b/assets/css/rule.css index 39fd3842b..7acc633dc 100755 --- a/assets/css/rule.css +++ b/assets/css/rule.css @@ -486,11 +486,11 @@ } .nav-link.active { -color: #000000!important; +color: #03c4eb!important; } #ruleInfoTabs, #ruleConfigTabs, #connectorCreateTabs, #connectorTabs { - background-color: #ecedee; + box-shadow: rgb(248 249 249) 0px -5px 9px -4px inset, rgba(255, 255, 256, 14.5) 20px 1px 2px 0px inset; } .fancybox { diff --git a/assets/images/template/logo.jpg b/assets/images/template/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0578962515f725ad9c71e9ed16d3760e385b3af6 GIT binary patch literal 15736 zcmeHuXIPU#yJnCkMG&R8D5x|MrASXe1q4I{l&VBPr3r}imPiqh7K(tNf=H7ZL3)ey zqDUw7-UI>(LI|Yo>vz80v*(=My>|D{*&iD+*Ol;2-kE3KXYS{|pP8Xe&=x?a4RrN& zL3DIf9v&WcPCkBKZvHdeJlub8LPrn$hJlfVk&%V_6x%88|Kk_!JBaHfDCtBC zJ)Icn1Q#7W7agqw1O|cV7=hmY?eITebSHp5GBKZIVPyj7KiF&)BPP_Ta)#=^?W$1fmw@sfn3l(eGK zb!8P*wVSteboFlQ8<^dHU~XY)W$p0f=`%+sXBRJTA74NJfWYvG*O5`tZ(@>D-lnFd zzstz{oST&xbj<7bxmzueM4J&M`u@ePjBDw$mrNOW@2(`acOyFb!~m)_a=Vt z&;9}7ka%?b*Dg8`{lA#?-z@ugc5wlAonT;~XJGzo7u^XzpwM$MFrL50bVl2R+0OH< zn0)9-?i)!t6|F4d3Z^(7`41kv z&jo^lDEB_cNP_<7Wp99sIxNb8!;TUPmX>zb_$2Y|Y%jhq#3jZ2&WS906I}Ipg`6O4 ztIif0lO_E*EMyjG_POQ77!BmQp=DR#WU8F|=r-Tcv$}W(LLNRcC$)wo8wNgF7hOKe zB>a9sg%sdT4iLEY-X)b0i5y&i|B=M;1W7Pv1#)IH{TS0eD@p^IeweX+c9Xe2=v>wzb5niF=AsU_xt^AZ4markiE{fVm*KVV^cOHcL}h89 zu-M>hICN~mkQsLB?L7m#OzOBs$4Qloh$iMQpE!#pOHpjZTtj?8_|wu2Owzn_Z4!%J zmUv>H-k&mSkmTo>e^X?-4vZ&}?NgZO>QmNxd%s`LU{%B9c`Q781%xq`77=6waX~|Y zeNJ?6{a1+?Hp?5jNw*E1qCVJK&9?dI;TVznp>`kQ3JGx2kgNTKxmTU*oYOvl=6;|ETw0+BFIrcYPbW@94w zj+BKZzy2?MU$tJbhi5D zQ5+3q{ku3SYLNtaU3iP>x=k0FxKVO&q~q*0`nItKp=Xz~y>UY%8TQIV=id0i4=~5? zZ?H{eDOcCiKYGu9=lNj2GHPGmzbZKr`*t>2Zs@-EBYRAT;DhqqCu@c_?>dqy_%9k8 zG#i+Bx@oq_$1UaadAG$#=H2^Gb|<=>=d^g2I4VP)?EUU+!jAto)Y9#iYMJ zjD{a{&gRY`Enzf}wh}R&q}~-ph`>E@FUHqYoT>cUbFSv94k!?{Z9}qp6nqMQhKP8S zrB4<>$PEYyzn5ZJyR~mNix90J*y2qun%c4sX+DVW@H6-0dSjn?sjnd^Q5*GhcRGcj zjEFi;olhVOIrT3#7O|&~UyGx}+<0k)_?@A#Tru$cKJ@E6HXhm@Z@@|K--gY{HIKZm zY)V|2=rNv^pa#@#gdjoSFN*hl54mP<3J*5)a>ocfx_gtp=iKPqf6Q15RXqI?(;M7C z+hHaZ?OziZ^7V&;sAX16ZapN`uhUj~n9TRd)>W;4#aGQI@0tFy#08K#rx_NOlwB={ z)hwkB&1uAp7@xHv+WztseP2*uXfWBGx!~iwt_=~U|?kvq& z-^wrrqG0{Q!}hwTwK%>gv8?McPe#hdh9X&$1Xl!CTNHyI*AQKE70p*A#&@`5{q+~2 z(;jxe=Nhi=OWV6h-%p)Zsd(3?!g{8|{-RU&G3h5d0WBzoR3D%ce&&}4Dp(9FOs_9(q} zU_f9+t{zpT0bL|@Ey88cd`27b`qIF`0sdoUC{@Z+oH zLp?qRUc2~*bxy(8b}2rH3zj3^(~rcu*kn}BOpSB12%E$)(pLuz#I?|Ev?$^qV|F}H zc8#+_{L4=#d_|d9Q>Mr3!Bam7dBZf2Bbuej94oROE0-(Q^}}xlyM`u-f8Di(lIW@m7qzbNB^xQFGPQZ1zO4zC74_@EY1`jdk`lCp*Tq`Y5-!z-mXdp@2kP!a^^K9($ZXp&i>fu_E7rq|kD4%ePD?Ha%MwyQA8SD^R! zS<6>cJi_G)MEW84*e{j_(&9M~RzLYeBtSL=k#uw@eE*|H&KGqreTm;C6wU$&2MyGF z4Yy7SDBLUWs~ZT<-n$d5N(0%xdGyg=1$`Kkh57?&2cY<6E|s5p8^F|6&C{P_ZhZGq z`Na4+<%Q-+#j1aa9J-#E7^C*ldnbnU>p1dQ|B1i3DqEfB9tR2!{{44gj54Q*7Msqc z#Z4(MEkusk9!?(KM4ft{S8I8EjbFg|9RuT)g|ei@@6!3{$%ex{j*Vb z(a*1V|JE>F5sgy(uVIkR*b=~x2kVrVh!t170wItFN{PY2D3PCOpusJ|h&f|b<5#sK zBXT_aB6WZUYNUMD5X!n%)|pM(c?EUz5xG8XIkIyZb(wWp3z*e)Y(41-wDXkidMli>@hPsX>h%>5vFB{-%*@P9kM|F@w1m;v zB$ACS*$;mqlQU{1bG|iA{_Sgv^QaHP0j1E!?1q*PS#_cZBo=Cyqh935l0?E~8fYX> z^jT{DPxPh*HRv`cUbOw|I$9XDElv^!pH|B5FczFh9nn7HZ0*ywFp!XVB9*0EBg zTTbY2^Py{_y8`ajv9F~He-}>#WRD)7+i`VRuI)ZWzaSUt-gL99PvE$8Y}TKVM& z6Y_wTs?w2;r_VACG;O$iyz!7j#j8r!pGUe6)kjR=0OM@QFQvo(DIpwiK2v~^uu)dOpo<2G<3j+%w0RC5DpQYXpf(!l=AUwP8Yszt z1`N2D{7i*C8%K^N#`;*HQ5vYJ4jOCm8?k+W z=fy06+bIh7YTw>Y(W;bF6&EiY6@{|apidZ^eU8CrPc1Ktk-3QA@~wM);$u50+k!5K zq+73EwNu7sXds+m7%~+7oO8;xOH`4#T5d^{={WwC{>e#l+$B?oT=QEh_LB2^2N3-E zE90l4)zdUkE0ikhkA~rO8{NwMQ_L}99^y`;yHD#T5C7B#2|G>?H@O0?*sfX*IZ4*R z$Im?9`%&;K@izHm?2np|X(aKKFVO1U`6Cgf>{4+5*6lKr%Ufu7V^*>;QFdvA?68Oy zA{GUMi+&2<$ZYar2O zol7bk7+mp1`QZ!0ypho@oM_xmqf>grQ!-e9(zPw3_BQtkeO%+X& zJF;HUJ+x>=#7tRqA{m{h#xq`g)A2m*;iEa4&>gh)Hu|5tPx~-dx*LU*}&?i)q#`Z3*e*2mL%;_a`1Ez;<=GBRekLP z^+E0;dOB!1yX}#^y7Ae5NTJ|DP4*HS_>yV;N)#(9+p8|fVhc)et0m)%`|40JFnaPu zd{En=wM4Yy7x#0i#_3n@T@1yycg;SCsvX3eGP99XsN--!A|-EaTFtlOm( zJDhl_?fEQD9&AJ2YsJQTu9(WU?b1R9k-|(eY{bHfl7?^=sOr5U1W)-AlI&;8*xASx z+1-%p;Q&Ly`SBn;IP^)wsk-VCfHQqJf~3>q=M9L}`1f6+V(E8=$RW!zTjP#TLX}8~ zzTpE_pz=u#D9N-ZC*2(XM`}Ya`VtMaJoXFT_er_?hnBAHbPOeKP?7X(SXTcv%Y=H6 zNq)JAaW4`OidmS$`Kc_3x@tFJ1t0_oM}^Q-qXH@hT*cSgeB)#_39IWAx-`&H!+d7B z{DO2jJg4NKo1`{P^_N(+6qP4BE~3CrermdndDffP`Pgc1Ka1l!5RLE^PcqcfkRUvz z)KqaM#3=sQV0yE38{?fOcWE`z&wo0;ebhnx09DA<5hHL{8`TN44CeIVoKz-6CXX1@ zzioBA92ogt9h_`S(%a{*;meB?(P?XPrysIkS4em& zQnGJMWd%aTrLraH@iEZEa0Kc-bRiR;hbm>h*N%3MRoojZxDdcv*JZR)y@CP9D)UAPLn24L>37JgI$)UmPIbnf)woX|&~lRd&m zu$LzLQ`6`LZkU$3$E4%ZD4GW%^4t33Xv@>l@r@_z#P98#qzysBCV37>jJT2Y_Mlb4 z${J16KQFy8J_Mtv8l3B}Jl5&AT!TYb7LZv8_uW`N7%Re&V(KP;{0M42R)DU)&m(Ix(s~5phQ$gt@1~DrKTfM0({EW;qV&OCpeK{wgl6ZNJjp(-k<(|0Z z5IKW*@^eFt=MnxIIWNbLQ~W@oP3=2IQQft4HAC0crHP)PNsMk$BiiN7^3BRj7kOqS zF!945W143k>bWwc7_^GT8#XFyfqE77t-_${h=n`^xJl;|S$8m4d=bXA83-nXWHhw6 zUD-4AP1ZN=DK&|FQSt%2iy5MUDt5^iTygUTQMRMIK8Dp1`bwRY_5m4natGf_{jL{~ z)T19?TvFge+12Jb#Wl~WMwQI#gjd)s6HbR*mqvD>ZKymOGC4%^$?Mo>_l&v6F&u?Y zVHf^BJqUD`EJgHqx;VhOY4*G2B4(wh?%UgLrRJ(9d_GTAO}kyf)ZI~~YWzZ)!kSfFU3>h8~vUHS1mOi2QJcFXFb%SNI-6TnPlQW z-%11VRDD|l^K4nyqz3Dh;x=plJQVXdm-k9tSa>;UB9r2U+bPW)Px0`MZww4`-6Ihe zC|UF01C?Jlc@|8+E8Q{iCXK~C))9+qo1466K|P`Ax&@waTs}v9gX`;(DXQJ%&E3X; zvi!c#L!(>CE@gvyqdvH7GWYb7L=2fl`Bi$m?WJ)IjVtE61A-68H7)M!mb0s0XX8FN zj$&q0k5&P9cuWH!kx{T6z#&zNnf&#_z0p%<-BX%1MLERA&e|G3mKJ4Ei41*#?wi;3 z3^t&P7SwU@{#Ac84aAxOB_4#pNGG}~gQbU*B|Y}-MiEu6Tbz{ufN5a%N_=mOlHaH3 zQOYzpP;ezV{+YbLQ|Aq8`pF7Vivl}F>YUkts5d$8|^ATZEb1oYQd$OeVvEv zR_Ohp7c%?p3#Z2VyTjZdgZeLGI6^#O>Dk=q6+-m@Nzj-Y$Bo^!RO9n3n_dPw`)|1Z`?fVGFbBF$>^GM;e|%7`rZrp1j;pA3gx34Fsx>K zMfNo@-~*?UeeOqe7r9MLuYQh^JW-QR1DzvfAFs{#V3v%(qmEQOSP&ELaug188IiwcQi3;Y~*hmU3p41$n`^;G!F7?qKmR{$8*u!)CQT&A3we z`vjl;#{o=eQGod(UO~htnecP6-e?JzZ9@ zhO#)DF)3gBa7xE3^}c4PNQlh7``#a2V!+~ypXWyuKSE9?bqc<|rNjT=o@V5=2mJL) z{3SKF)&)Ji-dEZr5lcFbE!xrSy&=B6ZqDwZVsCWiwT?{m7GZ+K*bdQi#O3AaVU;wSO3Av!(9&Q>{PXLCira3rN51A#zv!U;q^vy52$%`NWgV`C!O< zmY@jdCd*Y29Y2*-X+K53W2&$3zA_g+@c^J*+=`qLqNiH$64B-3<6UDf`Jfu*@NFmc z3Jvr0c#WrdWGJpH2eswrIq6(;E#qR^P2?xhye!Zms%Kz|Z8<##>I!``o(a$J@Vt~M zL4qXZ-AqaDrkmg>4GCf;HjEY8<`$XEemi)6T`-L!&gou!uE-gwU;|>*GHhCWnWwt> zV^yvE%#Ri4h9|e))*>~H?Yb{_p^ebbj3;Y#DOtgMB5&D5=9;~84o>NL$U`8T$LF?X zK2VYnPc_vEA-JCAmIQS)-+UKj(l72v>(A=>ZDxe^W{Y~T6Y_gtkaOUZnB$eL>Fo2y zcre0XYEOA9XQ1BXCc0j@XDBeUN3K>K)uX}U+D)p!``+L zo(F%h0pn;n?>x`d1m(@OUoe1JVgLNOMQJ7MqvA9KaJw+#`+$&(5pL>Tg3jd^CD^+^ zI5rBx=!pXsM6;!KPr)W*p(ABq9tsI0wzT#czeZ032hFwf^qy*}u6V5DYpL7p?it54Yna!?D){&l8Mf~7v=v*=BL~m;nBX@w8=9psW=Vv)4irEU1e& z$z0?_6CIwOKs#76py^D&M`{2j>{S5ZekTjI4CD2%bcIAdLco~DixE8q z8!LLJd^G0n!pXNGmdNjdL?}Uu=of@PyTqHjEMn#}x~pE?f37#+!%ix-X_?C4a^L6c zZqEMv8;J0`H{ItHlVp5LXE$5aoPFMQNG&^fyj z-NEL~&+@qsstscSSc5uZ#d{q76`@>mcdxDiT>OFtDmKm!ki#mGAirT%a_#TScsDM# zlz|kS(^p@*t#u0=m~XU?%&;DOL5xWZXIpW@FZ&xVgFB2T-PgNB*<#@z5tm373HDx& z$~P5l>ut3+;O582h1(V+m0AB~+o&$J6?uK&*~|Ao>%Hvtvp=h4{>^?d3}<%RRBlRF zEQY%!%B4SaboZ^}9{nB)4Si5k)j(!+x`tVN_| z>ZXg$wl9)yjb!Am2u0fONglL5g?$lgS)w4x;u5c0M1K2LN}XaX^}Mc!XB41#nb+7m zt}7jqv)ln4kxVrA(RFPhYW@{gt5gaiUNyNx8>0>WYx5_LH^z~_st^ZYX=p2lJOss|wpX9UD<+*Rl@3Yji*BFstRu!Qsw z{b{S(-ilLHd!SEK=yG+urT2N)JEwB#^Di+k8@En+Ku%Cc_BQx_4haPv zd1u6W*11!r;TNPt`(dalj&G+hQT8nXCps!T{3Kq1dXn6CB;XfW%GnFKW{}6(d1DGXz3Va5=XifbEkwQnpq)GQ8oYNwN zMg9cmAwPR~w6(BRjKBz9lp3E$oToerW?&jG9AED}>qw;DDyA|uB8gJ4PCHcZ0+MO! zN3-mLo9S;0Y9Pe)JW&#Gv=cd-DPXgA0PrD#Rn;)jyVaBSeY-Z6`XZ46J;0;}h)@y1 zcWHcF@mUjtbb|7MXo0Vt&>pcsLcL&$>jZ2oBkVZ-3| zz8T22v_B2hl3+Z@iU&_2mUzoZ#_%D*7=O5wl8~Xfo4S6j1#1ESw}v|vzp{0^K3A8;%w#*H9?lskW(~`sD6 z)D91jL^@c5ou=}Wb%+*UCbnASMm!WrKT44DLR;@6KIUayoxD;$uY7aB+F>ePpng`MAZE~cQ2hi&m;0{D2 zmDLUJVO5bi)ivFIEOfh$&php=zI+f;UKiGH?i)I=Qr#NRh%dXq?mb~6@E{Zzi|w4lz;2L7JYTSZXdFo1WTN% zK|;TCPKB)-jI1Z|>0lrO+!5q1rWYVceSD;|%tOveC=(GbcWNWix=VYiDG3%oU*(3S zz9*Rx=e7*5DDnIJww?bGcYE%!M>?x^@4$15ZRPe5yMt`Dss8gbB&|#6i!D-rNN%pV z)Pj`1VF~P{CfMMC)h8MLPILBfEZVyEJ)AYSy#|Zym>&E0Uicm*V0p_1jf?q>__GdRrOb`tE+Kw`wqRFFjFc0@zKPClU}wdrlXB@kAAk|_ zM5n^VgZU`)SeC_?>aIyZuQ<`y@^c30VDrsAN;RQ(^JgSQ`rgol( ztd%DCvQP2L%B0)#=BLjo1d-l-tTj{wqG-H~y|po=-mp;tnNLxze|`X&3u{&;gb>9v z6g>E%m3p+to<17XT828B0j#f@T(WcZU0{F`XSsdqCNvU?S)Lvxr^|O+EXBbH<`1#D zP+<~44!0Z7;tmV2R?A@e#|;IAfoZAu5)ncWSrIS%bf^)cgtu8L$i@xa zbY7YEEHYr7wqD;=E~b_i67*mX(w(tjX^NcQih>>-9~gxg1|o zUIul=bu$FCkNQRxA#0Ang*UjI$5~qLJPS$#YoKPbJzPk|DY@-LgySh!Lx2i}PS+ou z612=x><$XVB=Ei;@&lmwxFz<+Zg{Xrp}&w;?Qqq^truMoN2-)i7~(9$O-m`lCuHM{G?^lrly|f{LvAz6&hy&KIP+4Imh?@W}Vx&n{(ongJVCD zt?g^?T&%{yTkjr_IvXgOL6E{V0b3|n+q?PgpPh4YEdt)Q@Q)Zh6l!V=SPuo*=|xq5 zOZ`K6VH(`?-AXLm{hZ|o+~lM5Pp_PIU4c$zc;c^S+c*KF5+xILHbJ#;k{UJS`NOo2 z`}0nH?s2wmFxNlD*deu`F>?{Ze0}#dTok&$O9Ooq+}UdF0+t1VrNOi0fr>sFXhsqd z22Ey_{9HG1Pi7BBWfcc_9}Eb^w&u{|Q}8N_*kDUa5>=S|7_X1N()?NJS3|F`n{RC` z*Xlhx-OfZgu)$+@G7vYDq~|xFsR71hZkYuP`SN@ipJ4x6QTOV_G-bTo!*8u|cd`$E z2~p`_H~sb6YN%&N&h*Ws2;vm156vrJ*8OgtfJ2_)v!w=k_52EU<&PK*bf+%Kq<%%FM-OpVX5L$q_Y|{n!Z2goxlZ*B z<_tInw)dO{;zDn8D(~^&l*wmnsd`3^Tg-m~DG+>Agf~jmA7+Xp)VdO8B}`tI?YNP< zk%6MQFHg5`o>W>zoI*SqI6)R4muSJ&d^q;Vy5AGIW)(X90a5DPZ8jX;NUvD{KG5GU z9G1r3)0A>yf&<~N^)7T}1=t3&%?PmC+)JoIxyeGReoOj4RgPFO*`AUD7a_ajSzYHY z4q5M7TJla~4Ff#+c1Dv+sxPp$K3f-9YvyZ(oT|dzMe=@hOkyzLqEr1&4|?FuXO{CH zgw(!EKVB}%8<5Zb2$LZtOUPKk=4s99pX!s!UEd=g^=S(*e>pcAN4BB7hbuUv8OV=( zN;b`vE%1hCy2Pud>rPLud-4!IKyeS`7&=+cP0+H zPHmkDHQuHDo%SY{I<7)rb2s4kv5I2dK7wt=M)zvb^JN-HL_spIv*;xho`O8l=mIPiu0tfvqkti0AfPV5G!25@$I5Ty#^gFOoR^Zb>yquRt) z_~Uo8+;^|~Qzoa1Blbz9AtyC3BS4ZDcNY*xHN(u0Sl%7$2fSF`lNa3QWTy7DfN*zW zuVj3(ochV6G4aXdyy)wS6k_J(#?T`gh;$X+2N%O5W6FXJmt0jBUSIsPRNKJZpLS>c zXNpE>){6j%1ByNl8D)=~Lo(($Fj{2R=5JiL*07U(eESu3ql{H(xJP54V?dOc3^X<- zdU3qmKH2|ru)dE%H?Q*5vru{s3k+2NurJ$R?&c77dj*%(EUuelC>0LmRKX}`eBm}2 zm)>Q24n1D3p|OQPsL#ywG`t@CbHVp=KT?_gLKf)6SHvO^{7(WrgjtTteg#2X6}ij| zcpK*foF0XRdX5b6nWarb1W_6h=6*#i+FuW5CeFz4?y9^e<#-$5Pyg=M5WuNmZI(~; z&)#|~pZFcfF+V&26NY2iQ6xYAwCPT8rChX}>|F=f5-})FOpS;nm{9WfVUrY8LPa}CoLIJU=$y%9Dd9n1V^q}7Fdk`gd+h&=~t42$!-qe8E@ zr{#ky_WGc^krLDsGJAW*qApotIF9fIm3BqmM;)~zr@+MVJ$tN z5O$-M*WyLf*|u2`{?mQ%Pnk!+21t6M@#n3yse;r`%^y3f!YsW)yv~`6aERXET-K*U zlVpe-a?C{0w%N069w%-TVn8PlXEP72`*GAL=BWJ*>^c5h{AJQF{T?HK?GmLWUOgEi z>o1_A(^x9ae`R}0CnW0z_vn+AsW)2eakH!QSUQr{#N3>2m(h`PVygEUy}o& zfvCXOWgOZ-NO5y7E14>s-Y0ZJydQterL4}NangjolQSnZrFG%vh*r&(E5HU_p;@Bo zo@t^j7{Um@c5V@a=ZE6X+5!&EXqn+IwAd8$Qb3NA8iUF!puB9vWvL++1_|I0dS6UA!KZAkY@Xye8uANjlYA=)nqU0Y-LUaUj zHH^ne(KQTh2ACmA1JL*p_IrL zo?tCJBnLIW!Qo?c<5^uphR2S@&%^__&v!UKfgUKA4W@NR`rA-h{TBM^^=d0q+5-2> z1hJP3*ui9YHGplKEdVg)Ks&%6z~o0jCKF+ypvQDT;x3#m zo2sfMtVhlKh-!r^9`Z!2*WgQYJ+mSu9Dk z;w*qY@)P1o<%QHZC6b>HuzdnhyA6m`_*M8o%jr?F1L=;FZ|2j+ySPmNlT%;ybqlIq z?jB?XQF6JfSS3NIG#bbm8k?RT7(wM88Fw|jop2@Z@q+^BEEqs}wd3UaY%a1Ypfh5X z3`O)zzv}Ol&S-eVIf~E+#{D5$l%y>=*|`>1FaAuMEBv)SA#Cdq?*IJaUER4B*J5hz z1U`FmeZjxhGS~<;O(l{EzR8@88{5E6@(`X2vFy)xT1|BIC4g*Rb2M>1j zJ4ABOUpBh2z6eAGIU&*+rfZvmaR5C8zoZWr zMSpnBHk=yN!->KnyNB!LCd>$$DP0)z`P0FU&w6vZNc+fQ>K+JXznZHWjD+clN;C7L+J_FG(UM04Z8b3%~^;g@n+Xay;%%-k8 zYAeA)xcR%dgrZ^>Cx+vM9EIC|D))*J5rM3SFPnnpfrnsH(pw_Fq7o%h^ii6dyZ&*0 zK%mVZa!fWWgm;3hIB0tEd$!42dasvym*>IH{Q6 zg<8Z@_B^gc|8tlh=qfp&>)VCzI~v%9N+)(2tK+=5sK>~i<710a@7^54=qy!k4R_YR zCoBjkL%M{bSEa>f)5SYTPL)1MM-|7Y(&~-lLW(;w5q=`r8Nsm0wK3IHIwg#Kom7AQ zCy<|N+%kcWn(2Asc`m*gzdbj6-Qs4VHa&x21^&;%hb*?H6r17mjziC$_X@^QJ|d|< zY_$W59&VoC@>2MDURyZmm+zMinfs!#k^;pQJ; zY3M-gw1;sWoC(n@g9DC`K##e~_v++A5qkzrnm}rulN#aLjQUxD-hF`Rpn(*Q>7@Z> z1qc6g4Xqdx%iPH;dO0`fkLPB%w!}wR;LPo64Yoa+V8{ z`zBebTlfFbPv(#CA?ukI5u(Vl+YJG0me0d$EMgyco(X9)V`O3H5UujTMoXM$h$)?W zE)C5)IY44e@Pgl2z6UQiU()Tqa@`o1PO%Ww+iqPp|0U>@6N*?c>!v8cKEO@@t|rxT zf~nUb?K-(&tN-kE33YpJLm-CnD1c;i@`VW8nI z&oBB3Gtf}Djdb%Y*O$#IcdNpLwDI0);M5Aj3@KW``9z{yVG{-zjpywWt`08$EPo$^Qb_?CNX) literal 0 HcmV?d00001 diff --git a/assets/images/template/logo.png b/assets/images/template/logo1.png old mode 100755 new mode 100644 similarity index 100% rename from assets/images/template/logo.png rename to assets/images/template/logo1.png diff --git a/assets/js/home.js b/assets/js/home.js index d7a2dc5fb..51ffba06a 100755 --- a/assets/js/home.js +++ b/assets/js/home.js @@ -1,138 +1,118 @@ -/********************************************************************************* - * This file is part of Myddleware. - - * @package Myddleware - * @copyright Copyright (C) 2013 - 2015 St�phane Faure - CRMconsult EURL - * @copyright Copyright (C) 2015 - 2016 St�phane Faure - Myddleware ltd - contact@myddleware.com - * @link http://www.myddleware.com - - This file is part of Myddleware. - - Myddleware is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Myddleware is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Myddleware. If not, see . -*********************************************************************************/ - -google.charts.load("visualization", "1", {packages:["corechart"]}); -google.charts.setOnLoadCallback(drawChart); - -function drawChart() { - if ($('#pie_chart_error_doc').length) { - $.ajax({ - type: "POST", - url: 'graph/type/error/doc', - success: function (dataServ) { - var data = google.visualization.arrayToDataTable(dataServ); - var options = { - is3D: false, - }; - - var chart = new google.visualization.PieChart(document.getElementById('pie_chart_error_doc')); - chart.draw(data, options); - } - }); - } - - if ($('#pie_chart_transfer_rule').length) { - $.ajax({ - type: "POST", - url: 'graph/type/transfer/rule', - success: function (dataServ) { - - var data = google.visualization.arrayToDataTable(dataServ); - var options = { - is3D: false, - }; - var chart = new google.visualization.PieChart(document.getElementById('pie_chart_transfer_rule')); - chart.draw(data, options); - } - }); - } - - if ($('#column_chart_histo').length) { - $.ajax({ - type: "POST", - url: 'graph/type/transfer/histo', - success: function (dataServ) { - var data = google.visualization.arrayToDataTable(dataServ); - var options = { - is3D: false, - isStacked: true, - legend: { position: 'bottom'}, - height: 400, - width: 575 - }; - - var chart = new google.visualization.ColumnChart(document.getElementById('column_chart_histo')); - chart.draw(data, options); - } - }); - } - - if ($('#column_chart_job_histo').length) { - $.ajax({ - type: "POST", - url: 'graph/type/job/histo', - success: function (dataServ) { - - var data = google.visualization.arrayToDataTable(dataServ); - - var options = { - is3D: false, - isStacked: true, - legend: { position: 'bottom'}, - height: 400, - width: 600, - }; - - var chart = new google.visualization.ColumnChart(document.getElementById('column_chart_job_histo')); - chart.draw(data, options); - } - }); - } -} - -//-------------- - -$(function() { - if($('#listing-solutions','#panel').length != 0) { - $('#listing-solutions','#panel').scrollbox({ - direction: 'h', - distance: 65 - }); - $('#listing-solutions-backward','#panel').on('click', function () { - $('#listing-solutions','#panel').trigger('backward'); - }); - $('#listing-solutions-forward','#panel').on('click', function () { - $('#listing-solutions','#panel').trigger('forward'); - }); - $('#listing-solutions li','#panel').on('hover', function () { - string = $("img", this).attr('alt') - $("img", this).qtip({ - content: string.charAt(0).toUpperCase() + string.slice(1) + ": " + trans_click, - show: { - delay: 700, - ready: 'true', - }, - position: { - my: 'top center', // Position my top left... - at: 'bottom center', // at the bottom right of... - } - })}, - function () { - $('#listing-solutions img','#panel').each(function(){ - $(this).qtip("hide"); - }); - } - ); - } -}); +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 St�phane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 St�phane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Myddleware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Myddleware. If not, see . +*********************************************************************************/ + +google.charts.load("visualization", "1", {packages:["corechart"]}); + +//-------------- + +// $(function() { +// if($('#listing-solutions','#panel').length != 0) { +// $('#listing-solutions','#panel').scrollbox({ +// direction: 'h', +// distance: 65 +// }); +// $('#listing-solutions-backward','#panel').on('click', function () { +// $('#listing-solutions','#panel').trigger('backward'); +// }); +// $('#listing-solutions-forward','#panel').on('click', function () { +// $('#listing-solutions','#panel').trigger('forward'); +// }); +// $('#listing-solutions li','#panel').on('hover', function () { +// string = $("img", this).attr('alt') +// $("img", this).qtip({ +// content: string.charAt(0).toUpperCase() + string.slice(1) + ": " + trans_click, +// show: { +// delay: 700, +// ready: 'true', +// }, +// position: { +// my: 'top center', // Position my top left... +// at: 'bottom center', // at the bottom right of... +// } +// })}, +// function () { +// $('#listing-solutions img','#panel').each(function(){ +// $(this).qtip("hide"); +// }); +// } +// ); +// } +// }); + +document.addEventListener('DOMContentLoaded', function () { + + if (typeof countNbDocuments !== 'undefined') { + // Fonction for animation of the counter + function animateCounter(id, start, end, duration) { + var obj = document.getElementById(id); + var current = start; + var range = end - start; + var increment = end > start ? 1 : -1; + var stepTime = Math.abs(Math.floor(duration / range)); + + var timer = setInterval(function() { + current += increment; + obj.innerHTML = current; + if (current == end) { + clearInterval(timer); + } + }, stepTime); + } + + animateCounter('countNbDocuments', 0, countNbDocuments, 1000); + } else { + console.error("countNbDocuments not defined"); + } + + const showMoreBtn = document.getElementById('show-more-btn'); + const showLessBtn = document.getElementById('show-less-btn'); + const hiddenItems = document.querySelectorAll('#error-list .list-group-item.d-none'); + const allItems = document.querySelectorAll('#error-list .list-group-item'); + + if (showMoreBtn) { + showMoreBtn.addEventListener('click', function () { + hiddenItems.forEach(item => item.classList.remove('d-none')); + showMoreBtn.classList.add('d-none'); + showLessBtn.classList.remove('d-none'); + }); + } + + if (showLessBtn) { + showLessBtn.addEventListener('click', function () { + allItems.forEach((item, index) => { + if (index >= 5) { + item.classList.add('d-none'); + } + }); + showLessBtn.classList.add('d-none'); + showMoreBtn.classList.remove('d-none'); + }); + } + + document.querySelectorAll('.dropdown-toggle').forEach(function (dropdown) { + dropdown.addEventListener('click', function (e) { + e.preventDefault(); + }); + }); +}); diff --git a/src/Controller/ApiController.php b/src/Controller/ApiController.php index ce100c728..24900b830 100644 --- a/src/Controller/ApiController.php +++ b/src/Controller/ApiController.php @@ -1,477 +1,477 @@ -ruleRepository = $ruleRepository; - $this->jobRepository = $jobRepository; - $this->documentRepository = $documentRepository; - $this->jobManager = $jobManager; - $this->solutionManager = $solutionManager; - $this->formulaManager = $formulaManager; - $this->documentManager = $documentManager; - $this->logger = $logger; - $this->kernel = $kernel; - $this->env = $kernel->getEnvironment(); - $this->parameterBag = $parameterBag; - $this->entityManager = $entityManager; - } - - /** - * @Route("/synchro", name="synchro", methods={"POST"}) - */ - public function synchroAction(Request $request): JsonResponse - { - try { - $return = []; - $return['error'] = ''; - - // Get input data - $data = json_decode($request->getContent(), true); - - // Check parameter - if (empty($data['rule'])) { - throw new Exception('Rule is missing. Please specify a rule id or set ALL to run all active rules. '); - } - - // Prepare command - $application = new Application($this->kernel); - $application->setAutoExit(false); - $arguments = [ - 'command' => 'myddleware:synchro', - 'force' => (empty($data['force']) ? false : true), - 'api' => 1, - '--env' => $this->env, - ]; - - // Prepare input/output parameters - $arguments['rule'] = $data['rule']; - $input = new ArrayInput($arguments); - $output = new BufferedOutput(); - - // Run the command - $application->run($input, $output); - - // Get resut command - $content = $output->fetch(); - if (empty($content)) { - throw new Exception('No response from Myddleware. '); - } - // Log the result - $this->logger->info(print_r($content, true)); - - // Get the job task id, result is 1;..... - $return['jobId'] = substr($content, 2, 23); - $job = $this->jobRepository->find($return['jobId']); - // Get the job statistics - $this->jobManager->setId($this->jobManager->getId()); - $jobData = $this->jobManager->getLogData(); - if (!empty($jobData['jobError'])) { - throw new Exception('Failed to get the job statistics. '.$jobData['jobError']); - } - $return['jobData'] = $jobData; - } catch (Exception $e) { - $this->logger->error($e->getMessage()); - $return['error'] = $e->getMessage(); - } - // Send the response - return $this->json($return); - } - - /** - * @Route("/read_record", name="read_record", methods={"POST"}) - */ - public function readRecordAction(Request $request): JsonResponse - { - try { - $return = []; - $return['error'] = ''; - - // Get input data - //use request content - $rawData = $request->getContent(); - $data = json_decode($rawData, true); - - // Check parameter - if (empty($data['rule'])) { - throw new Exception('Rule is missing. Please specify a ruleId parameter. '); - } - if (empty($data['filterQuery'])) { - throw new Exception('Filter query is missing. filterQuery is a field used to read data in the source application, eg : id. '); - } - if (empty($data['filterValues'])) { - throw new Exception('Filter value is missing. filterValues is the value corresponding to the filterQuery field. '); - } - - // Prepare command - $application = new Application($this->kernel); - $application->setAutoExit(false); - $arguments = [ - 'command' => 'myddleware:readrecord', - 'api' => 1, - '--env' => $this->env, - ]; - - // Prepare input/output parameters - $arguments['ruleId'] = $data['rule']; - $arguments['filterQuery'] = $data['filterQuery']; - $arguments['filterValues'] = $data['filterValues']; - $input = new ArrayInput($arguments); - $output = new BufferedOutput(); - - // Run the command - $application->run($input, $output); - - // Get resut command - $content = $output->fetch(); - if (empty($content)) { - throw new Exception('No response from Myddleware. '); - } - // Log the result - $this->logger->info(print_r($content, true)); - - // Get the job task id, result is ..... - $return['jobId'] = substr($content, 0, 23); - - // Get the job statistics - $job = $this->jobRepository->find($return['jobId']); - $this->jobManager->setId($this->jobManager->getId()); - $jobData = $this->jobManager->getLogData(); - if (!empty($jobData['jobError'])) { - throw new Exception('Failed to get the job statistics. '.$jobData['jobError']); - } - $return['jobData'] = $jobData; - } catch (Exception $e) { - $this->logger->error($e->getMessage()); - $return['error'] = $e->getMessage(); - } - // Send the response - return $this->json($return); - } - - /** - * @Route("/delete_record", name="delete_record", methods={"POST"}) - */ - public function deleteRecordAction(Request $request): JsonResponse - { - try { - $connection = $this->entityManager->getConnection(); - $return = []; - $return['error'] = ''; - - // Get input data - $data = json_decode($request->getContent(), true); - - // Check parameter - if (empty($data['rule'])) { - throw new Exception('Rule is missing. Please specify a ruleId parameter. '); - } - if (empty($data['recordId'])) { - throw new Exception('recordId is missing. recordId is the id of the record you want to delete. '); - } - - // Create job instance - $this->jobManager->setApi(1); - $this->jobManager->initJob('Delete record '.$data['recordId'].' in rule '.$data['rule']); - - $rule = new RuleManager( - $this->logger, - $connection, - $this->entityManager, - $this->parameterBag, - $this->formulaManager, - $this->solutionManager, - $this->documentManager - ); - - $rule->setJobId($this->jobManager->getId()); - $rule->setApi(1); - $rule->setRule($data['rule']); - - // Set the document values - foreach ($data as $key => $value) { - switch ($key) { - case 'recordId': - $docParam['values']['id'] = $value; - break; - case 'reference': - $docParam['values']['date_modified'] = $value; - break; - case 'rule': - break; - default: - $docParam['values'][$key] = $value; - } - } - - // Set all fields not set to empty - $sourceFields = $rule->getSourceFields(); - if (!empty($sourceFields)) { - foreach ($sourceFields as $sourceField) { - if (!array_key_exists($sourceField, $docParam['values'])) { - $docParam['values'][$sourceField] = ''; - } - } - } - - // Add deletion flag - $docParam['values']['myddleware_deletion'] = true; // Force deleted record type - - $document = $rule->generateDocuments($data['recordId'], false, $docParam); - // Stop the process if error during the data transfer creation as we won't be able to manage it in Myddleware - if (!empty($document->error)) { - throw new Exception('Error during data transfer creation (rule '.$data['rule'].') : '.$document->error.'. '); - } - // $connection->commit(); // -- COMMIT TRANSACTION - } catch (Exception $e) { - // $connection->rollBack(); // -- ROLLBACK TRANSACTION - $this->logger->error($e->getMessage()); - $return['error'] .= $e->getMessage(); - // Stop the process if document hasn't been created - return $this->json($return); - } - - // Send the document just created if requested - if (empty($data['asynchronousDeletion'])) { - try { - // db transaction managed into the method actionDocument - $errors = $rule->actionDocument($document[0]->id, 'rerun'); - // Check errors, but in this case the data transfer is created but Myddleware hasn't been able to send it. - // We don't roll back the work here as it will be possible to manage the data transfer in Myddleware - if (!empty($errors)) { - throw new Exception('Document in error (rule '.$data['rule'].') : '.$errors[0].'. '); - } - } catch (Exception $e) { - $this->logger->error($e->getMessage()); - $return['error'] .= $e->getMessage(); - } - } - - // Close job if it has been created - try { - // $connection->beginTransaction(); // -- BEGIN TRANSACTION - if (true === $this->jobManager->createdJob) { - $this->jobManager->closeJob(); - } - // Get the job statistics even if the job has failed - if (!empty($this->jobManager->getId())) { - $return['jobId'] = $this->jobManager->getId(); - $jobData = $this->jobManager->getLogData(); - if (!empty($jobData['jobError'])) { - $return['error'] .= $jobData['jobError']; - } - $return['jobData'] = $jobData; - } - } catch (Exception $e) { - $this->logger->error('Failed to get the job statistics. '.$e->getMessage()); - $return['error'] .= 'Failed to get the job statistics. '.$e->getMessage(); - } - // Send the response - return $this->json($return); - } - - - /** - * @Route("/mass_action", name="mass_action", methods={"POST"}) - */ - public function massActionAction(Request $request): JsonResponse - { - try { - $return = []; - $return['error'] = ''; - - // Get input data - $data = json_decode($request->getContent(), true); - - // Check parameter - if (empty($data['action'])) { - throw new Exception('action is missing. Please specify an action : rerun, cancel, remove, restore or changeStatus. '); - } - if (empty($data['dataType'])) { - throw new Exception('dataType is missing. Please specify a data type : rule or document'); - } - if (empty($data['ids'])) { - throw new Exception('ids is missing. Please specify rule or document ids separated by comma. '); - } - - // Prepare command - $application = new Application($this->kernel); - $application->setAutoExit(false); - $arguments = [ - 'command' => 'myddleware:massaction', - 'api' => 1, - '--env' => $this->env, - ]; - - // Prepare input/output parameters - $arguments['action'] = $data['action']; - $arguments['dataType'] = $data['dataType']; - $arguments['ids'] = $data['ids']; - $arguments['forceAll'] = (!empty($data['forceAll']) ? $data['forceAll'] : ''); - $arguments['fromStatus'] = (!empty($data['fromStatus']) ? $data['fromStatus'] : ''); - $arguments['toStatus'] = (!empty($data['toStatus']) ? $data['toStatus'] : ''); - $input = new ArrayInput($arguments); - $output = new BufferedOutput(); - - // Run the command - $application->run($input, $output); - - // Get resut command - $content = $output->fetch(); - if (empty($content)) { - throw new Exception('No response from Myddleware. '); - } - // Log the result - $this->logger->info(print_r($content, true)); - - // Get the job task id, result is ..... - $return['jobId'] = substr($content, 0, 23); - - // Get the job statistics - $this->jobManager->id = $return['jobId']; - $jobData = $this->jobManager->getLogData(); - if (!empty($jobData['jobError'])) { - throw new Exception('Failed to get the job statistics. '.$jobData['jobError']); - } - $return['jobData'] = $jobData; - } catch (Exception $e) { - $this->logger->error($e->getMessage()); - $return['error'] = $e->getMessage(); - } - // Send the response - return $this->json($return); - } - - /** - * @Route("/rerun_error", name="rerun_error", methods={"POST"}) - */ - public function rerunErrorAction(Request $request): JsonResponse - { - try { - $return = []; - $return['error'] = ''; - - // Get input data - $data = json_decode($request->getContent(), true); - - // Check parameter - if (empty($data['limit'])) { - throw new Exception('limit parameter is missing. Please specify a number to limit the number of data transfer the program has to rerun. '); - } - if (empty($data['attempt'])) { - throw new Exception('attempt parameteris missing. Please specify the maximum number of attempt. If you set 10, the program will rerun only data transfer with attempt <= 10. '); - } - - // Prepare command - $application = new Application($this->kernel); - $application->setAutoExit(false); - $arguments = [ - 'command' => 'myddleware:rerunerror', - 'api' => 1, - '--env' => $this->env, - ]; - - // Prepare input/output parameters - $arguments['limit'] = $data['limit']; - $arguments['attempt'] = $data['attempt']; - $input = new ArrayInput($arguments); - $output = new BufferedOutput(); - - // Run the command - $application->run($input, $output); - - // Get resut command - $content = $output->fetch(); - if (empty($content)) { - throw new Exception('No response from Myddleware. '); - } - // Log the result - $this->logger->info(print_r($content, true)); - - // Get the job task id, result is ..... - $return['jobId'] = substr($content, 0, 23); - - // Get the job statistics - $this->jobManager->id = $return['jobId']; - $jobData = $this->jobManager->getLogData(); - $return['jobData'] = $jobData; - } catch (Exception $e) { - $this->logger->error($e->getMessage()); - $return['error'] = $e->getMessage(); - } - // Send the response - return $this->json($return); - } - - /** - * @Route("/statistics", name="statistics", methods={"POST"}) - */ - public function statisticsAction(Request $request): JsonResponse - { - try { - $return = []; - $home = $this->container->get('myddleware.home'); - - $return['errorByRule'] = $this->ruleRepository->errorByRule(); - $return['countTypeDoc'] = $this->documentRepository->countTypeDoc(); - $return['listJobDetail'] = $this->jobRepository->listJobDetail(); - $return['countTransferHisto'] = $home->countTransferHisto(); - } catch (Exception $e) { - $this->logger->error($e->getMessage()); - $return['error'] = $e->getMessage(); - } - // Send the response - return $this->json($return); - } -} +ruleRepository = $ruleRepository; + $this->jobRepository = $jobRepository; + $this->documentRepository = $documentRepository; + $this->jobManager = $jobManager; + $this->solutionManager = $solutionManager; + $this->formulaManager = $formulaManager; + $this->documentManager = $documentManager; + $this->logger = $logger; + $this->kernel = $kernel; + $this->env = $kernel->getEnvironment(); + $this->parameterBag = $parameterBag; + $this->entityManager = $entityManager; + } + + /** + * @Route("/synchro", name="synchro", methods={"POST"}) + */ + public function synchroAction(Request $request): JsonResponse + { + try { + $return = []; + $return['error'] = ''; + + // Get input data + $data = json_decode($request->getContent(), true); + + // Check parameter + if (empty($data['rule'])) { + throw new Exception('Rule is missing. Please specify a rule id or set ALL to run all active rules. '); + } + + // Prepare command + $application = new Application($this->kernel); + $application->setAutoExit(false); + $arguments = [ + 'command' => 'myddleware:synchro', + 'force' => (empty($data['force']) ? false : true), + 'api' => 1, + '--env' => $this->env, + ]; + + // Prepare input/output parameters + $arguments['rule'] = $data['rule']; + $input = new ArrayInput($arguments); + $output = new BufferedOutput(); + + // Run the command + $application->run($input, $output); + + // Get resut command + $content = $output->fetch(); + if (empty($content)) { + throw new Exception('No response from Myddleware. '); + } + // Log the result + $this->logger->info(print_r($content, true)); + + // Get the job task id, result is 1;..... + $return['jobId'] = substr($content, 2, 23); + $job = $this->jobRepository->find($return['jobId']); + // Get the job statistics + $this->jobManager->setId($this->jobManager->getId()); + $jobData = $this->jobManager->getLogData(); + if (!empty($jobData['jobError'])) { + throw new Exception('Failed to get the job statistics. '.$jobData['jobError']); + } + $return['jobData'] = $jobData; + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + $return['error'] = $e->getMessage(); + } + // Send the response + return $this->json($return); + } + + /** + * @Route("/read_record", name="read_record", methods={"POST"}) + */ + public function readRecordAction(Request $request): JsonResponse + { + try { + $return = []; + $return['error'] = ''; + + // Get input data + //use request content + $rawData = $request->getContent(); + $data = json_decode($rawData, true); + + // Check parameter + if (empty($data['rule'])) { + throw new Exception('Rule is missing. Please specify a ruleId parameter. '); + } + if (empty($data['filterQuery'])) { + throw new Exception('Filter query is missing. filterQuery is a field used to read data in the source application, eg : id. '); + } + if (empty($data['filterValues'])) { + throw new Exception('Filter value is missing. filterValues is the value corresponding to the filterQuery field. '); + } + + // Prepare command + $application = new Application($this->kernel); + $application->setAutoExit(false); + $arguments = [ + 'command' => 'myddleware:readrecord', + 'api' => 1, + '--env' => $this->env, + ]; + + // Prepare input/output parameters + $arguments['ruleId'] = $data['rule']; + $arguments['filterQuery'] = $data['filterQuery']; + $arguments['filterValues'] = $data['filterValues']; + $input = new ArrayInput($arguments); + $output = new BufferedOutput(); + + // Run the command + $application->run($input, $output); + + // Get resut command + $content = $output->fetch(); + if (empty($content)) { + throw new Exception('No response from Myddleware. '); + } + // Log the result + $this->logger->info(print_r($content, true)); + + // Get the job task id, result is ..... + $return['jobId'] = substr($content, 0, 23); + + // Get the job statistics + $job = $this->jobRepository->find($return['jobId']); + $this->jobManager->setId($this->jobManager->getId()); + $jobData = $this->jobManager->getLogData(); + if (!empty($jobData['jobError'])) { + throw new Exception('Failed to get the job statistics. '.$jobData['jobError']); + } + $return['jobData'] = $jobData; + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + $return['error'] = $e->getMessage(); + } + // Send the response + return $this->json($return); + } + + /** + * @Route("/delete_record", name="delete_record", methods={"POST"}) + */ + public function deleteRecordAction(Request $request): JsonResponse + { + try { + $connection = $this->entityManager->getConnection(); + $return = []; + $return['error'] = ''; + + // Get input data + $data = json_decode($request->getContent(), true); + + // Check parameter + if (empty($data['rule'])) { + throw new Exception('Rule is missing. Please specify a ruleId parameter. '); + } + if (empty($data['recordId'])) { + throw new Exception('recordId is missing. recordId is the id of the record you want to delete. '); + } + + // Create job instance + $this->jobManager->setApi(1); + $this->jobManager->initJob('Delete record '.$data['recordId'].' in rule '.$data['rule']); + + $rule = new RuleManager( + $this->logger, + $connection, + $this->entityManager, + $this->parameterBag, + $this->formulaManager, + $this->solutionManager, + $this->documentManager + ); + + $rule->setJobId($this->jobManager->getId()); + $rule->setApi(1); + $rule->setRule($data['rule']); + + // Set the document values + foreach ($data as $key => $value) { + switch ($key) { + case 'recordId': + $docParam['values']['id'] = $value; + break; + case 'reference': + $docParam['values']['date_modified'] = $value; + break; + case 'rule': + break; + default: + $docParam['values'][$key] = $value; + } + } + + // Set all fields not set to empty + $sourceFields = $rule->getSourceFields(); + if (!empty($sourceFields)) { + foreach ($sourceFields as $sourceField) { + if (!array_key_exists($sourceField, $docParam['values'])) { + $docParam['values'][$sourceField] = ''; + } + } + } + + // Add deletion flag + $docParam['values']['myddleware_deletion'] = true; // Force deleted record type + + $document = $rule->generateDocuments($data['recordId'], false, $docParam); + // Stop the process if error during the data transfer creation as we won't be able to manage it in Myddleware + if (!empty($document->error)) { + throw new Exception('Error during data transfer creation (rule '.$data['rule'].') : '.$document->error.'. '); + } + // $connection->commit(); // -- COMMIT TRANSACTION + } catch (Exception $e) { + // $connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->logger->error($e->getMessage()); + $return['error'] .= $e->getMessage(); + // Stop the process if document hasn't been created + return $this->json($return); + } + + // Send the document just created if requested + if (empty($data['asynchronousDeletion'])) { + try { + // db transaction managed into the method actionDocument + $errors = $rule->actionDocument($document[0]->id, 'rerun'); + // Check errors, but in this case the data transfer is created but Myddleware hasn't been able to send it. + // We don't roll back the work here as it will be possible to manage the data transfer in Myddleware + if (!empty($errors)) { + throw new Exception('Document in error (rule '.$data['rule'].') : '.$errors[0].'. '); + } + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + $return['error'] .= $e->getMessage(); + } + } + + // Close job if it has been created + try { + // $connection->beginTransaction(); // -- BEGIN TRANSACTION + if (true === $this->jobManager->createdJob) { + $this->jobManager->closeJob(); + } + // Get the job statistics even if the job has failed + if (!empty($this->jobManager->getId())) { + $return['jobId'] = $this->jobManager->getId(); + $jobData = $this->jobManager->getLogData(); + if (!empty($jobData['jobError'])) { + $return['error'] .= $jobData['jobError']; + } + $return['jobData'] = $jobData; + } + } catch (Exception $e) { + $this->logger->error('Failed to get the job statistics. '.$e->getMessage()); + $return['error'] .= 'Failed to get the job statistics. '.$e->getMessage(); + } + // Send the response + return $this->json($return); + } + + + /** + * @Route("/mass_action", name="mass_action", methods={"POST"}) + */ + public function massActionAction(Request $request): JsonResponse + { + try { + $return = []; + $return['error'] = ''; + + // Get input data + $data = json_decode($request->getContent(), true); + + // Check parameter + if (empty($data['action'])) { + throw new Exception('action is missing. Please specify an action : rerun, cancel, remove, restore or changeStatus. '); + } + if (empty($data['dataType'])) { + throw new Exception('dataType is missing. Please specify a data type : rule or document'); + } + if (empty($data['ids'])) { + throw new Exception('ids is missing. Please specify rule or document ids separated by comma. '); + } + + // Prepare command + $application = new Application($this->kernel); + $application->setAutoExit(false); + $arguments = [ + 'command' => 'myddleware:massaction', + 'api' => 1, + '--env' => $this->env, + ]; + + // Prepare input/output parameters + $arguments['action'] = $data['action']; + $arguments['dataType'] = $data['dataType']; + $arguments['ids'] = $data['ids']; + $arguments['forceAll'] = (!empty($data['forceAll']) ? $data['forceAll'] : ''); + $arguments['fromStatus'] = (!empty($data['fromStatus']) ? $data['fromStatus'] : ''); + $arguments['toStatus'] = (!empty($data['toStatus']) ? $data['toStatus'] : ''); + $input = new ArrayInput($arguments); + $output = new BufferedOutput(); + + // Run the command + $application->run($input, $output); + + // Get resut command + $content = $output->fetch(); + if (empty($content)) { + throw new Exception('No response from Myddleware. '); + } + // Log the result + $this->logger->info(print_r($content, true)); + + // Get the job task id, result is ..... + $return['jobId'] = substr($content, 0, 23); + + // Get the job statistics + $this->jobManager->id = $return['jobId']; + $jobData = $this->jobManager->getLogData(); + if (!empty($jobData['jobError'])) { + throw new Exception('Failed to get the job statistics. '.$jobData['jobError']); + } + $return['jobData'] = $jobData; + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + $return['error'] = $e->getMessage(); + } + // Send the response + return $this->json($return); + } + + /** + * @Route("/rerun_error", name="rerun_error", methods={"POST"}) + */ + public function rerunErrorAction(Request $request): JsonResponse + { + try { + $return = []; + $return['error'] = ''; + + // Get input data + $data = json_decode($request->getContent(), true); + + // Check parameter + if (empty($data['limit'])) { + throw new Exception('limit parameter is missing. Please specify a number to limit the number of data transfer the program has to rerun. '); + } + if (empty($data['attempt'])) { + throw new Exception('attempt parameteris missing. Please specify the maximum number of attempt. If you set 10, the program will rerun only data transfer with attempt <= 10. '); + } + + // Prepare command + $application = new Application($this->kernel); + $application->setAutoExit(false); + $arguments = [ + 'command' => 'myddleware:rerunerror', + 'api' => 1, + '--env' => $this->env, + ]; + + // Prepare input/output parameters + $arguments['limit'] = $data['limit']; + $arguments['attempt'] = $data['attempt']; + $input = new ArrayInput($arguments); + $output = new BufferedOutput(); + + // Run the command + $application->run($input, $output); + + // Get resut command + $content = $output->fetch(); + if (empty($content)) { + throw new Exception('No response from Myddleware. '); + } + // Log the result + $this->logger->info(print_r($content, true)); + + // Get the job task id, result is ..... + $return['jobId'] = substr($content, 0, 23); + + // Get the job statistics + $this->jobManager->id = $return['jobId']; + $jobData = $this->jobManager->getLogData(); + $return['jobData'] = $jobData; + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + $return['error'] = $e->getMessage(); + } + // Send the response + return $this->json($return); + } + + // /** + // * @Route("/statistics", name="statistics", methods={"POST"}) + // */ + // public function statisticsAction(Request $request): JsonResponse + // { + // try { + // $return = []; + // $home = $this->container->get('myddleware.home'); + + // $return['errorByRule'] = $this->ruleRepository->errorByRule(); + // $return['countTypeDoc'] = $this->documentRepository->countTypeDoc(); + // $return['listJobDetail'] = $this->jobRepository->listJobDetail(); + // $return['countTransferHisto'] = $home->countTransferHisto(); + // } catch (Exception $e) { + // $this->logger->error($e->getMessage()); + // $return['error'] = $e->getMessage(); + // } + // // Send the response + // return $this->json($return); + // } +} diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index dfeb114d0..e33c5f8ed 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -1,3166 +1,3070 @@ -. - *********************************************************************************/ - -namespace App\Controller; - -use App\Entity\Connector; -use App\Entity\ConnectorParam; -use App\Entity\Document; -use App\Entity\FuncCat; -use App\Entity\Functions; -use App\Entity\Rule; -use App\Entity\RuleAudit; -use App\Entity\RuleField; -use App\Entity\RuleFilter; -use App\Entity\RuleParam; -use App\Entity\RuleParamAudit; -use App\Entity\RuleRelationShip; -use App\Entity\Solution; -use App\Entity\User; -use App\Form\ConnectorType; -use App\Form\DuplicateRuleFormType; -use App\Manager\DocumentManager; -use App\Manager\FormulaManager; -use App\Manager\HomeManager; -use App\Manager\JobManager; -use App\Manager\RuleManager; -use App\Manager\SolutionManager; -use App\Manager\TemplateManager; -use App\Manager\ToolsManager; -use App\Repository\ConfigRepository; -use App\Repository\DocumentRepository; -use App\Repository\JobRepository; -use App\Repository\RuleRepository; -use App\Service\SessionService; -use Doctrine\DBAL\Connection; -use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\Mapping\Id; -use Exception; -use Illuminate\Encryption\Encrypter; -use Pagerfanta\Adapter\ArrayAdapter; -use Pagerfanta\Doctrine\ORM\QueryAdapter; -use Pagerfanta\Pagerfanta; -use Psr\Log\LoggerInterface; -use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; -use Symfony\Component\Form\Extension\Core\Type\SubmitType; -use Symfony\Component\Form\Extension\Core\Type\TextareaType; -use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\RedirectResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; -use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; -use Symfony\Contracts\Translation\TranslatorInterface; -use App\Form\Type\RelationFilterType; -use App\Entity\Workflow; - - /** - * @Route("/rule") - */ - class DefaultController extends AbstractController - { - private FormulaManager $formuleManager; - private SessionService $sessionService; - private ParameterBagInterface $params; - private EntityManagerInterface $entityManager; - private HomeManager $home; - private ToolsManager $tools; - private TranslatorInterface $translator; - private AuthorizationCheckerInterface $authorizationChecker; - private JobManager $jobManager; - private LoggerInterface $logger; - private TemplateManager $template; - private RuleRepository $ruleRepository; - private JobRepository $jobRepository; - private DocumentRepository $documentRepository; - private SolutionManager $solutionManager; - private RuleManager $ruleManager; - private DocumentManager $documentManager; - protected Connection $connection; - // To allow sending a specific record ID to rule simulation - protected $simulationQueryField; - private ConfigRepository $configRepository; - - public function __construct( - LoggerInterface $logger, - RuleManager $ruleManager, - FormulaManager $formuleManager, - SolutionManager $solutionManager, - DocumentManager $documentManager, - SessionService $sessionService, - EntityManagerInterface $entityManager, - RuleRepository $ruleRepository, - JobRepository $jobRepository, - DocumentRepository $documentRepository, - Connection $connection, - TranslatorInterface $translator, - AuthorizationCheckerInterface $authorizationChecker, - HomeManager $home, - ToolsManager $tools, - JobManager $jobManager, - TemplateManager $template, - ParameterBagInterface $params - ) { - $this->logger = $logger; - $this->ruleManager = $ruleManager; - $this->formuleManager = $formuleManager; - $this->solutionManager = $solutionManager; - $this->documentManager = $documentManager; - $this->sessionService = $sessionService; - $this->entityManager = $entityManager; - $this->ruleRepository = $ruleRepository; - $this->jobRepository = $jobRepository; - $this->documentRepository = $documentRepository; - $this->connection = $connection; - $this->translator = $translator; - $this->authorizationChecker = $authorizationChecker; - $this->home = $home; - $this->tools = $tools; - $this->jobManager = $jobManager; - $this->template = $template; - } - - protected function getInstanceBdd() - { - } - - /* ****************************************************** - * RULE - ****************************************************** */ - - /** - * LISTE DES REGLES. - * - * @return RedirectResponse|Response - * - * @Route("/list", name="regle_list", defaults={"page"=1}) - * @Route("/list/page-{page}", name="regle_list_page", requirements={"page"="\d+"}) - */ - public function ruleListAction(int $page = 1, Request $request) - { - try { - - $ruleName = $request->query->get('rule_name'); - - if ($ruleName) { - - $key = $this->sessionService->getParamRuleLastKey(); - if (null != $key && $this->sessionService->isRuleIdExist($key)) { - $id = $this->sessionService->getRuleId($key); - $this->sessionService->removeRuleId($key); - - return $this->redirect($this->generateUrl('regle_open', ['id' => $id])); - } - - $this->getInstanceBdd(); - $compact['nb'] = 0; - $pager = $this->tools->getParamValue('ruleListPager'); - $compact = $this->nav_pagination([ - 'adapter_em_repository' => $this->entityManager->getRepository(Rule::class)->findListRuleByUser($this->getUser(), $ruleName), - 'maxPerPage' => isset($pager) ? $pager : 20, - 'page' => $page, - ]); - - } else { - - $key = $this->sessionService->getParamRuleLastKey(); - if (null != $key && $this->sessionService->isRuleIdExist($key)) { - $id = $this->sessionService->getRuleId($key); - $this->sessionService->removeRuleId($key); - - return $this->redirect($this->generateUrl('regle_open', ['id' => $id])); - } - - $this->getInstanceBdd(); - - $compact['nb'] = 0; - $pager = $this->tools->getParamValue('ruleListPager'); - $compact = $this->nav_pagination([ - 'adapter_em_repository' => $this->entityManager->getRepository(Rule::class)->findListRuleByUser($this->getUser()), - 'maxPerPage' => isset($pager) ? $pager : 20, - 'page' => $page, - ]); - - } - - // Si tout se passe bien dans la pagination - if ($compact) { - // Si aucune règle - if ($compact['nb'] < 1 && !intval($compact['nb'])) { - $compact['entities'] = ''; - $compact['pager'] = ''; - } - - return $this->render( - 'Rule/list.html.twig', - [ - 'nb_rule' => $compact['nb'], - 'entities' => $compact['entities'], - 'pager' => $compact['pager'], - ] - ); - } - throw $this->createNotFoundException('Error'); - - } catch (Exception $e) { - throw $this->createNotFoundException('Error : ' . $e); - } - } - - - /** - * SUPPRESSION D'UNE REGLE. - * - * @Route("/delete/{id}", name="regle_delete") - */ - public function deleteRule(Request $request, $id): RedirectResponse - { - $session = $request->getSession(); - - // First, checking that the rule has document not deleted - $docClose = $this->getDoctrine() - ->getManager() - ->getRepository(Document::class) - ->findOneBy([ - 'rule' => $id, - 'deleted' => 0, - ] - ); - // Return to the view detail for the rule if we found a document close - if (!empty($docClose)) { - $session->set('error', [$this->translator->trans('error.rule.delete_document')]); - - return $this->redirect($this->generateUrl('regle_open', ['id' => $id])); - } - - // Then, checking that the rule has no document open or in error - $docErrorOpen = $this->getDoctrine() - ->getManager() - ->getRepository(Document::class) - ->findOneBy([ - 'rule' => $id, - 'deleted' => 0, - 'globalStatus' => ['Open', 'Error'], - ] - ); - // Return to the view detail of the rule if we found a document open or in error - if (!empty($docErrorOpen)) { - $session->set('error', [$this->translator->trans('error.rule.delete_document_error_open')]); - - return $this->redirect($this->generateUrl('regle_open', ['id' => $id])); - } - - // Checking if the rule is linked to an other one - $ruleRelationships = $this->getDoctrine() - ->getManager() - ->getRepository(RuleRelationShip::class) - ->findBy(['fieldId' => $id]); - - // Return to the view detail of the rule if a rule relate to this one exists and is not deleted - if (!empty($ruleRelationships)) { - foreach ($ruleRelationships as $ruleRelationship) { - if (empty($ruleRelationship->getDeleted())) { - $session->set('error', [$this->translator->trans('error.rule.delete_relationship_exists').$ruleRelationship->getRule()]); - - return $this->redirect($this->generateUrl('regle_open', ['id' => $id])); - } - } - } - - if ($this->getUser()->isAdmin()) { - $list_fields_sql = - ['id' => $id, - ]; - } else { - $list_fields_sql = - [ - 'id' => $id, - 'createdBy' => $this->getUser()->getId(), - ]; - } - // Detecte si la session est le support --------- - - if (isset($id)) { - // Récupère la règle en fonction de son id - $rule = $this->getDoctrine() - ->getManager() - ->getRepository(Rule::class) - ->findBy($list_fields_sql); - - $rule = $rule[0]; - - // si je supprime une règle qui ne m'appartient pas alors redirection - if (empty($rule)) { - return $this->redirect($this->generateUrl('regle_list')); - } - - // On récupére l'EntityManager - $this->getInstanceBdd(); - - // Remove the rule relationships - $ruleRelationships = $this->getDoctrine() - ->getManager() - ->getRepository(RuleRelationShip::class) - ->findBy(['rule' => $id]); - - if (!empty($ruleRelationships)) { - foreach ($ruleRelationships as $ruleRelationship) { - $ruleRelationship->setDeleted(1); - $this->entityManager->persist($ruleRelationship); - } - } - - $rule->setDeleted(1); - $rule->setActive(0); - $this->entityManager->persist($rule); - $this->entityManager->flush(); - - return $this->redirect($this->generateUrl('regle_list')); - } - return $this->redirect($this->generateUrl('regle_list')); - } - - /** - * @Route("/displayflux/{id}", name="regle_displayflux") - */ - public function displayFlux($id): RedirectResponse - { - $rule = $this->getDoctrine() - ->getManager() - ->getRepository(Rule::class) - ->findOneBy([ - 'id' => $id, - ] - ); - - $this->sessionService->setFluxFilterWhere(['rule' => $rule->getName()]); - $this->sessionService->setFluxFilterRuleName($rule->getName()); - - return $this->redirect($this->generateUrl('document_list_page')); - } - - /** - * @Route("/duplic_rule/{id}", name="duplic_rule") - */ - public function duplicateRule($id, Request $request, TranslatorInterface $translator) - { - try { - $rule = $this->getDoctrine() - ->getManager() - ->getRepository(Rule::class) - ->findOneBy([ - 'id' => $id, - ]); - $newRule = new Rule(); - $connectorSource = $rule->getconnectorSource()->getName(); - $connectorTarget = $rule->getconnectorTarget()->getName(); - - //solution id current rule - $currentRuleSolutionSourceId = $rule->getConnectorSource()->getSolution()->getId(); - $currentRuleSolutionTargetId = $rule->getConnectorTarget()->getSolution()->getId(); - - // Create the form - $form = $this->createForm(DuplicateRuleFormType::class, $newRule, ['solution' => ['source' => $currentRuleSolutionSourceId, 'target' => $currentRuleSolutionTargetId]]); - - $form->handleRequest($request); - //Sends new data if validated and submit - if ($form->isSubmitted() && $form->isValid()) { - $now = new \DateTime(); - $user = $this->getUser(); - $newRuleName = $form->get('name')->getData(); - $newRuleSource = $form->get('connectorSource')->getData(); - $newRuleTarget = $form->get('connectorTarget')->getData(); - - if (isset($newRuleName)) { - // Set the rule header data - $newRule->setName($newRuleName) - ->setCreatedBy($user) - ->setConnectorSource($newRuleSource) - ->setConnectorTarget($newRuleTarget) - ->setDateCreated($now) - ->setDateModified($now) - ->setModifiedBy($user) - ->setModuleSource($rule->getModuleSource()) - ->setModuleTarget($rule->getModuleTarget()) - ->setDeleted(false) - ->setActive(false) - ->setNameSlug($newRuleName); - - // Set the rule parameters - foreach ($rule->getParams() as $param) { - $paramNewRule = new RuleParam(); - $paramNewRule->setRule($newRule); - $paramNewRule->setName($param->getName()); - $paramNewRule->setValue($param->getValue()); - $this->entityManager->persist($paramNewRule); - } - - // Set the rule relationships - foreach ($rule->getRelationsShip() as $relationship) { - $relationsShipNewRule = new RuleRelationShip(); - $relationsShipNewRule->setRule($newRule); - $relationsShipNewRule->setFieldNameSource($relationship->getFieldNameSource()); - $relationsShipNewRule->setFieldNameTarget($relationship->getFieldNameTarget()); - $relationsShipNewRule->setFieldId($relationship->getFieldId()); - $relationsShipNewRule->setParent($relationship->getParent()); - $relationsShipNewRule->setDeleted(0); - $relationsShipNewRule->setErrorEmpty($relationship->getErrorEmpty()); - $relationsShipNewRule->setErrorMissing($relationship->getErrorMissing()); - $this->entityManager->persist($relationsShipNewRule); - } - - // Set the rule filters - foreach ($rule->getFilters() as $filter) { - $filterNewRule = new RuleFilter(); - $filterNewRule->setRule($newRule); - $filterNewRule->setTarget($filter->getTarget()); - $filterNewRule->setType($filter->getType()); - $filterNewRule->setValue($filter->getValue()); - $this->entityManager->persist($filterNewRule); - } - - // Set the rule fields - foreach ($rule->getFields() as $field) { - $fieldNewRule = new RuleField(); - $fieldNewRule->setRule($newRule); - $fieldNewRule->setTarget($field->getTarget()); - $fieldNewRule->setSource($field->getSource()); - $fieldNewRule->setFormula($field->getFormula()); - $this->entityManager->persist($fieldNewRule); - } - - // Save the new rule in the database - $this->entityManager->persist($newRule); - $this->entityManager->flush(); - $success = $translator->trans('duplicate_rule.success_duplicate'); - $this->addFlash('success', $success); - } - - return $this->redirect($this->generateURL('regle_list')); - } - - return $this->render('Rule/create/duplic.html.twig', [ - 'rule' => $rule, - 'connectorSourceUser' => $connectorSource, - 'connectorTarget' => $connectorTarget, - 'form' => $form->createView(), - ]); - } catch (Exception $e) { - return new JsonResponse($e->getMessage()); - } - } - - /** - * ACTIVE UNE REGLE. - * - * @Route("/update/{id}", name="regle_update") - */ - public function ruleUpdActive($id) - { - try { - // On récupére l'EntityManager - $this->getInstanceBdd(); - - $rule = $this->getDoctrine() - ->getManager() - ->getRepository(Rule::class) - ->find($id); - - if ($rule->getActive()) { - $r = 0; - $rule->setActive($r); - } else { - $r = 1; - $rule->setActive($r); - } - - $this->entityManager->persist($rule); - $this->entityManager->flush(); - - return new Response($r); - } catch (Exception $e) { - return new JsonResponse($e->getMessage()); - } - } - - /** - * Executer une règle manuellement. - * - * @Route("/exec/{id}", name="regle_exec") - */ - public function ruleExecAction($id, $documentId = null) - { - // We added a doc id to this function to carry the document ids in case of a run rule by doc id. - // In every case except our mass run by doc id, $documentId will be null so we keep the usual behaviour of the function untouched. - try { - $this->ruleManager->setRule($id); - - if ($documentId !== null) { - $this->ruleManager->actionRule('runRuleByDocId', 'execrunRuleByDocId', $documentId); - - } elseif ('ALL' == $id) { - $this->ruleManager->actionRule('ALL'); - - return $this->redirect($this->generateUrl('regle_list')); - } elseif ('ERROR' == $id) { - $this->ruleManager->actionRule('ERROR'); - - return $this->redirect($this->generateUrl('regle_list')); - } - if ($documentId === null){ - - $this->ruleManager->actionRule('runMyddlewareJob'); - } - - return $this->redirect($this->generateURL('regle_open', ['id' => $id])); - } catch (Exception $e) { - $this->logger->error($e->getMessage().' '.$e->getFile().' '.$e->getLine()); - - return $this->redirect($this->generateUrl('regle_list')); - } - } - - /** - * CANCEL ALL TRANSFERS FOR ONE RULE. - * - * @Route("/view/cancel/documents/{id}", name="rule_cancel_all_transfers") - */ - public function cancelRuleTransfers($id) - { - try { - $this->ruleManager->setRule($id); - $result = $this->ruleManager->actionRule('runMyddlewareJob', 'cancelDocumentJob'); - } catch (\Exception $e) { - return $e->getMessage(); - } - - return $this->redirect($this->generateUrl('regle_open', ['id' => $id])); - } - - /** - * DELETE ALL TRANSFERS FOR ONE RULE. - * - * @Route("/view/delete/documents/{id}", name="rule_delete_all_transfers") - */ - public function deleteRuleTransfers($id) - { - try { - $this->ruleManager->setRule($id); - $result = $this->ruleManager->actionRule('runMyddlewareJob', 'deleteDocumentJob'); - } catch (\Exception $e) { - return $e->getMessage(); - } - - return $this->redirect($this->generateUrl('regle_open', ['id' => $id])); - } - - /** - * MODIFIE LES PARAMETRES D'UNE REGLE. - * @return JsonResponse|Response - * @Route("/update/params/{id}", name="path_fiche_params_update") - */ - public function ruleUpdParams($id) - { - try { - // On récupére l'EntityManager - $this->getInstanceBdd(); - if (isset($_POST['params']) && is_array($_POST['params'])) { - foreach ($_POST['params'] as $p) { - $param = $this->entityManager->getRepository(RuleParam::class) - ->findOneBy([ - 'rule' => $id, - 'name' => $p['name'], - ] - ); - - // In a few case, the parameter could not exist, in this case we create it - if (empty($param)) { - // Create rule entity - $rule = $this->getDoctrine() - ->getManager() - ->getRepository(Rule::class) - ->findOneBy([ - 'id' => $id, - ] - ); - $param = new RuleParam(); - $param->setRule($rule); - $param->setName($p['name']); - $param->setValue($p['value']); - } else { - // Save param modification in the audit table - if ($p['value'] != $param->getValue()) { - $paramAudit = new RuleParamAudit(); - $paramAudit->setRuleParamId($p['id']); - $paramAudit->setDateModified(new \DateTime()); - $paramAudit->setBefore($param->getValue()); - $paramAudit->setAfter($p['value']); - $paramAudit->setByUser($this->getUser()->getId()); - $this->entityManager->persist($paramAudit); - } - $param->setValue($p['value']); - } - $this->entityManager->persist($param); - $this->entityManager->flush(); - } - } - - return new Response(1); - } catch (Exception $e) { - return new JsonResponse($e->getMessage()); - } - } - - /** - * SIMULE LA LECTURE POUR RETOURNER LE NOMBRE DE TRANSFERS POTENTIELS. - * @Route("/simule/{id}", name="path_fiche_params_simulate") - */ - public function ruleSimulateTransfers(Rule $rule): Response - { - try { - // On récupére l'EntityManager - $this->getInstanceBdd(); - - // Get the rule reference - $param['date_ref'] = $rule->getParamByName('datereference')->getValue(); - // Get the rule limit - $limitParam = $rule->getParamByName('limit'); - if ($limitParam) { - $param['limit'] = $limitParam->getValue(); - } - // Get the other rule params - $connectorParams = $rule->getParams(); - foreach ($connectorParams as $connectorParam) { - $param['ruleParams'][$connectorParam->getName()] = $connectorParam->getValue(); - } - - $param['fields'] = []; - // Extraction des champs sources - foreach ($rule->getFields() as $ruleField) { - // It could be several fields in a source when there is a formula - $sources = explode(';', $ruleField->getSource()); - foreach ($sources as $source) { - $param['fields'][] = $source; - } - } - - // Module source - $param['module'] = (string) $rule->getModuleSource(); - - // Solution source - $solution_source_nom = $rule->getConnectorSource()->getSolution()->getName(); - - // Connector source ------------------- - $connectorParamsSource = $this->getDoctrine() - ->getManager() - ->getRepository(ConnectorParam::class) - ->findBy(['connector' => $rule->getConnectorSource()]); - $connectorSource['solution'] = $rule->getConnectorSource()->getSolution()->getName(); - foreach ($connectorParamsSource as $connector) { - $connectorSource[$connector->getName()] = $connector->getValue(); - } - - $solution_source = $this->solutionManager->get($solution_source_nom); - $solution_source->login($connectorSource); - - // Rule Mode - $param['ruleParams']['mode'] = $rule->getParamByName('mode')->getValue(); - - if (empty($param['ruleParams']['mode'])) { - $param['ruleParams']['mode'] = '0'; - } - - $param['offset'] = '0'; - $param['call_type'] = 'read'; - $result = $solution_source->readData($param); - if (!empty($result['error'])) { - throw new Exception('Reading Issue: '.$result['error']); - } - if (isset($result['count'])) { - return new Response($result['count']); - } - - return new Response(0); - } catch (Exception $e) { - $errorMessage = $e->getMessage().' '.$e->getFile().' '.$e->getLine(); - - return new Response(json_encode(['error' => $errorMessage])); - } - } - - /** - * MODE EDITION D'UNE REGLE. - * - * @Route("/edit/{id}", name="regle_edit") - */ - public function ruleEditAction(Request $request, Rule $rule): RedirectResponse - { - $session = $request->getSession(); - - try { - // First, checking that the rule has no document open or in error - $docErrorOpen = $this->getDoctrine() - ->getManager() - ->getRepository(Document::class) - ->findOneBy([ - 'rule' => $rule, - 'deleted' => 0, - 'globalStatus' => ['Open', 'Error'], - ]); - // Return to the view detail fo the rule if we found a document open or in error - if (!empty($docErrorOpen)) { - if ($this->authorizationChecker->isGranted('ROLE_SUPER_ADMIN')) { - $session->set('warning', [$this->translator->trans('error.rule.edit_document_error_open_admin')]); - } else { - $session->set('error', [$this->translator->trans('error.rule.edit_document_error_open')]); - - return $this->redirect($this->generateUrl('regle_open', ['id' => $rule->getId()])); - } - } - - $this->sessionService->setParamRuleLastKey($rule->getId()); - $key = $this->sessionService->getParamRuleLastKey(); - //-- - // si une session existe alors on la supprime - if ($this->sessionService->isParamRuleExist($key)) { - $this->sessionService->removeParamRule($key); - } - - // préparation des sessions - if (!empty($rule->getDeleted())) { - $session->set('error', [$this->translator->trans('error.rule.edit_rule_deleted')]); - - return $this->redirect($this->generateUrl('regle_open', ['id' => $rule->getId()])); - } - - // composition des sessions - $this->sessionService->setParamRuleNameValid($key, true); - $this->sessionService->setParamRuleName($key, $rule->getName()); - $this->sessionService->setParamRuleConnectorSourceId($key, (string) $rule->getConnectorSource()->getId()); - $this->sessionService->setParamRuleConnectorCibleId($key, (string) $rule->getConnectorTarget()->getId()); - $this->sessionService->setParamRuleLastId($key, $rule->getId()); - - // Connector source ------------------- - $connectorParamsSource = $this->getDoctrine() - ->getManager() - ->getRepository(ConnectorParam::class) - ->findByConnector([$rule->getConnectorSource()]); - - $this->sessionService->setParamRuleSourceSolution($key, $rule->getConnectorSource()->getSolution()->getName()); - - foreach ($connectorParamsSource as $connector) { - $this->sessionService->setParamRuleSourceConnector($key, $connector->getName(), $connector->getValue()); - } - // Connector source ------------------- - - // Connector target ------------------- - $connectorParamsTarget = $this->getDoctrine() - ->getManager() - ->getRepository(ConnectorParam::class) - ->findByConnector([$rule->getConnectorTarget()]); - - $this->sessionService->setParamRuleCibleSolution($key, $rule->getConnectorTarget()->getSolution()->getName()); - - foreach ($connectorParamsTarget as $connector) { - $this->sessionService->setParamRuleCibleConnector($key, $connector->getName(), $connector->getValue()); - } - // Connector target ------------------- - - // Paramètre d'une règle - if ($rule->getParams()->count()) { - $params = []; - foreach ($rule->getParams() as $ruleParamsObj) { - $params[] = [ - 'name' => $ruleParamsObj->getName(), - 'value' => $ruleParamsObj->getValue(), - ]; - } - $this->sessionService->setParamRuleReloadParams($key, $params); - } - - // Modules -- - $this->sessionService->setParamRuleSourceModule($key, $rule->getModuleSource()); - $this->sessionService->setParamRuleCibleModule($key, $rule->getModuletarget()); - // Modules -- - - // reload --------------- - $ruleFields = $rule->getFields(); - - // get_modules_fields en source pour avoir l'association fieldid / libellé (ticket 548) - $solution_source_nom = $this->sessionService->getParamRuleSourceSolution($key); - $solution_source = $this->solutionManager->get($solution_source_nom); - - $login = $solution_source->login($this->decrypt_params($this->sessionService->getParamRuleSource($key))); - if (empty($solution_source->connexion_valide)) { - throw new Exception('failed to login to the source application .'.(!empty($login['error']) ? $login['error'] : '')); - } - - // SOURCE ----- Récupère la liste des champs source - // O récupère le module de la règle - $sourceModule = $rule->getModuleSource(); - $sourceFieldsInfo = $solution_source->get_module_fields($sourceModule); - - // Champs et formules d'une règle - if ($ruleFields) { - $fields = array(); - foreach ($ruleFields as $ruleFieldsObj) { - $array = [ - 'target' => $ruleFieldsObj->getTarget(), - 'source' => [], - 'formula' => $ruleFieldsObj->getFormula(), - ]; - $fields_source = explode(';', $ruleFieldsObj->getSource()); - - if (!empty($fields_source)) { - foreach ($fields_source as $field_source) { - if ('my_value' == $field_source) { - $array['source'][$field_source] = 'my_value'; - } elseif (isset($sourceFieldsInfo[$field_source])) { - $array['source'][$field_source] = $sourceFieldsInfo[$field_source]['label']; - } else { - if (!empty($sourceFieldsInfo)) { - foreach ($sourceFieldsInfo as $multiModule) { - if (isset($multiModule[$field_source])) { - $array['source'][$field_source] = $multiModule[$field_source]['label']; - } - } - } - } - if (!isset($array['source'][$field_source])) { - throw new Exception('failed to get the field '.$field_source); - } - } - $fields[] = $array; - } - } - $this->sessionService->setParamRuleReloadFields($key, $fields); - } - - // Relations d'une règle - if ($rule->getRelationsShip()->count()) { - foreach ($rule->getRelationsShip() as $ruleRelationShipsObj) { - $relate[] = [ - 'source' => $ruleRelationShipsObj->getFieldNameSource(), - 'target' => $ruleRelationShipsObj->getFieldNameTarget(), - 'errorMissing' => (!empty($ruleRelationShipsObj->getErrorMissing()) ? '1' : '0'), - 'errorEmpty' => (!empty($ruleRelationShipsObj->getErrorEmpty()) ? '1' : '0'), - 'id' => $ruleRelationShipsObj->getFieldId(), - 'parent' => $ruleRelationShipsObj->getParent(), - ]; - } - $this->sessionService->setParamRuleReloadRelate($key, $relate); - } - - // Filter - if ($rule->getFilters()->count()) { - foreach ($rule->getFilters() as $ruleFilters) { - $filter[] = [ - 'target' => $ruleFilters->getTarget(), - 'type' => $ruleFilters->getType(), - 'value' => $ruleFilters->getValue(), - ]; - } - } - - $this->sessionService->setParamRuleReloadFilter($key, ((isset($filter)) ? $filter : [])); - - // reload --------------- - return $this->redirect($this->generateUrl('regle_stepthree', ['id' => $rule->getId()])); - } catch (Exception $e) { - $this->sessionService->setCreateRuleError($key, $this->translator->trans('error.rule.update').' '.$e->getMessage().' '.$e->getFile().' '.$e->getLine()); - $session->set('error', [$this->translator->trans('error.rule.update').' '.$e->getMessage().' '.$e->getFile().' '.$e->getLine()]); - - return $this->redirect($this->generateUrl('regle_open', ['id' => $rule->getId()])); - } - } - - /** - * FICHE D'UNE REGLE. - * @Route("/view/{id}", name="regle_open") - * @throws Exception - */ - public function ruleOpenAction($id): Response - { - if ($this->getUser()->isAdmin()) { - $list_fields_sql = ['id' => $id]; - } else { - $list_fields_sql = - ['id' => $id, - 'createdBy' => $this->getUser()->getId(), - ]; - } - // Detecte si la session est le support --------- - - // Infos de la regle - /** @var Rule $rule */ - $rule = $this->entityManager->getRepository(Rule::class)->findOneBy($list_fields_sql); - if (!$rule) { - throw $this->createNotFoundException('Couldn\'t find specified rule in database'); - } - - // Liste des relations - $rule_relationships = $rule->getRelationsShip(); - $solution_cible_nom = $rule->getConnectorTarget()->getSolution()->getName(); - $solution_cible = $this->solutionManager->get($solution_cible_nom); - $moduleCible = (string) $rule->getModuleTarget(); - - $tab_rs = []; - $i = 0; - foreach ($rule_relationships as $r) { - $tab_rs[$i]['getFieldId'] = $r->getFieldId(); - $tab_rs[$i]['getFieldNameSource'] = $r->getFieldNameSource(); - $tab_rs[$i]['getFieldNameTarget'] = $r->getFieldNameTarget(); - $tab_rs[$i]['getErrorMissing'] = $r->getErrorMissing(); - $tab_rs[$i]['getErrorEmpty'] = $r->getErrorEmpty(); - $tab_rs[$i]['getParent'] = $r->getParent(); - - $ruleTmp = $this->entityManager->getRepository(Rule::class) - ->findOneBy([ - 'id' => $r->getFieldId(), - ] - ); - - $tab_rs[$i]['getName'] = $ruleTmp->getName(); - ++$i; - } - - // Infos connecteurs & solutions - $ruleRepo = $this->entityManager->getRepository(Rule::class); - $connector = $ruleRepo->infosConnectorByRule($rule->getId()); - - // Changement de référence pour certaines solutions - $autorization_source = $connector[0]['solution_source']; - $autorization_module_trans = mb_strtolower($rule->getModuleSource()); - - $Params = $rule->getParams(); - $Fields = $rule->getFields(); - $Filters = $rule->getFilters(); - $ruleParam = RuleManager::getFieldsParamView(); - $params_suite = []; - if ($Params) { - foreach ($Params as $field) { - $standardField = false; - foreach ($ruleParam as $index => $value) { - // Init the parameter in case it doesn't exist in the database yet - if (!isset($ruleParam[$index]['id_bdd'])) { - $ruleParam[$index]['id_bdd'] = ''; - $ruleParam[$index]['value_bdd'] = ''; - } - if ($field->getName() == $value['name']) { - $ruleParam[$index]['id_bdd'] = $field->getId(); - $ruleParam[$index]['value_bdd'] = $field->getValue(); - $standardField = true; - break; - } - } - if (!$standardField) { - if ('mode' == $field->getName()) { - // We send the translation of the mode to the view - switch ($field->getValue()) { - case '0': - $params_suite['mode'] = $this->tools->getTranslation(['create_rule', 'step3', 'syncdata', 'create_modify']); - break; - case 'C': - $params_suite['mode'] = $this->tools->getTranslation(['create_rule', 'step3', 'syncdata', 'create_only']); - break; - case 'S': - $params_suite['mode'] = $this->tools->getTranslation(['create_rule', 'step3', 'syncdata', 'search_only']); - break; - default: - $params_suite['mode'] = $field->getValue(); - } - } elseif ('bidirectional' == $field->getName()) { - if (!empty($field->getValue())) { - $ruleBidirectional = $this->entityManager->getRepository(Rule::class) - ->findOneBy([ - 'id' => $field->getValue(), - ] - ); - // Send the name and the id of the opposite rule to the view - $params_suite['bidirectional'] = $field->getValue(); - $params_suite['bidirectionalName'] = $ruleBidirectional->getName(); - } - } else { - $params_suite['customParams'][] = ['name' => $field->getName(), 'value' => $field->getValue()]; - } - } - } - } - - // get the workflows of the rule, if there are none then set hasWorkflows to false. If there is at least one then set it to true. to get the workflows we use the entity manager and filter by the rule id - $hasWorkflows = $this->entityManager->getRepository(Workflow::class)->findBy(['rule' => $rule->getId(), 'deleted' => 0]) ? true : false; - - if ($hasWorkflows) { - $workflows = $this->entityManager->getRepository(Workflow::class)->findBy(['rule' => $rule->getId(), 'deleted' => 0]); - - } else { - $workflows = []; - } - - return $this->render('Rule/edit/fiche.html.twig', [ - 'rule' => $rule, - 'connector' => $connector[0], - 'fields' => $Fields, - 'relate' => $tab_rs, - 'parentRelationships' => $solution_cible->allowParentRelationship($moduleCible), - 'params' => $ruleParam, - 'filters' => $Filters, - 'params_suite' => $params_suite, - 'id' => $id, - 'hasWorkflows' => $hasWorkflows, - 'workflows' => $workflows, - ] - ); - } - - /** - * @return JsonResponse|Response - * CREATION - STEP ONE - CONNEXION : jQuery ajax. - * @Route("/inputs", name="regle_inputs", methods={"POST"}, options={"expose"=true}) - */ - public function ruleInputs(Request $request) - { - try { - $ruleKey = $this->sessionService->getParamRuleLastKey(); - // Retourne la liste des inputs pour la connexion - if (1 == $request->request->get('mod')) { - if (is_string($request->request->get('solution')) && is_string($request->request->get('parent'))) { - if (preg_match("#[\w]#", $request->request->get('solution')) && preg_match("#[\w]#", $request->request->get('parent'))) { - $classe = strtolower($request->request->get('solution')); - $parent = $request->request->get('parent'); - $solution = $this->entityManager->getRepository(Solution::class)->findOneBy(['name' => $classe]); - $connector = new Connector(); - $connector->setSolution($solution); - $fieldsLogin = []; - if (null !== $connector->getSolution()) { - $fieldsLogin = $this->solutionManager->get($connector->getSolution()->getName())->getFieldsLogin(); - } - $form = $this->createForm(ConnectorType::class, $connector, [ - 'action' => $this->generateUrl('regle_connector_insert'), - 'attr' => [ - 'fieldsLogin' => $fieldsLogin, - 'secret' => $this->getParameter('secret'), - ], - ]); - - return $this->render('Ajax/result_liste_inputs.html.twig', [ - 'form' => $form->createView(), - 'parent' => $parent, - ] - ); - } - } - } // Vérifie si la connexion peut se faire ou non - elseif (2 == $request->request->get('mod') || 3 == $request->request->get('mod')) { - // Connector - if (2 == $request->request->get('mod')) { - if (preg_match("#[\w]#", $request->request->get('champs')) && preg_match("#[\w]#", $request->request->get('parent')) && preg_match("#[\w]#", $request->request->get('solution'))) { - $classe = strtolower($request->request->get('solution')); - $solution = $this->solutionManager->get($classe); - - // établi un tableau params - $champs = explode(';', $request->request->get('champs')); - - if ($champs) { - foreach ($champs as $key) { - $input = explode('::', $key); - if (!empty($input[0])) { - if (!empty($input[1]) || is_numeric($input[1])) { - $param[$input[0]] = trim($input[1]); - $this->sessionService->setParamConnectorParentType($request->request->get('parent'), $input[0], trim($input[1])); - } - } - } - } - $this->sessionService->setParamConnectorParentType($request->request->get('parent'), 'solution', $classe); - - // Vérification du nombre de champs - if (isset($param) && (count($param) == count($solution->getFieldsLogin()))) { - $result = $solution->login($param); - $r = $solution->connexion_valide; - - if (!empty($r)) { - return new JsonResponse(['success' => true]); // Connexion valide - } - $this->sessionService->removeParamRule($ruleKey); - - return new JsonResponse(['success' => false, 'message' => $this->translator->trans($result['error'])]); // Erreur de connexion - } - - return new JsonResponse(['success' => false, 'message' => $this->translator->trans('Connection error')]); // Erreur pas le même nombre de champs - } else { - // Either parent, solution or champs is empty (from AJAX request sent in verif(div_clock) function in regle.js) - return new JsonResponse(['success' => false, 'message' => $this->translator->trans('create_connector.form_error')]); - } - } // Rule - elseif (3 == $request->request->get('mod')) { - // 0 : solution - // 1 : id connector - $params = explode('_', $request->request->get('solution')); - - // Deux params obligatoires - if (2 == count($params) && intval($params[1]) && is_string($params[0])) { - $this->sessionService->removeParamParentRule($ruleKey, $request->request->get('parent')); - $classe = strtolower($params[0]); - $solution = $this->solutionManager->get($classe); - - $connector = $this->getDoctrine() - ->getManager() - ->getRepository(Connector::class) - ->find($params[1]); - - $connector_params = $this->getDoctrine() - ->getManager() - ->getRepository(ConnectorParam::class) - ->findBy(['connector' => $connector]); - - if ($connector_params) { - foreach ($connector_params as $key) { - $this->sessionService->setParamConnectorParentType($request->request->get('parent'), $key->getName(), $key->getValue()); - } - } - - $this->sessionService->setParamRuleName($ruleKey, $request->request->get('name')); - - // Affectation id connector - $this->sessionService->setParamRuleConnectorParent($ruleKey, $request->request->get('parent'), $params[1]); - - $result = $solution->login($this->decrypt_params($this->sessionService->getParamParentRule($ruleKey, $request->request->get('parent')))); - $this->sessionService->setParamRuleParentName($ruleKey, $request->request->get('parent'), 'solution', $classe); - - $r = $solution->connexion_valide; - if (!empty($r)) { - return new JsonResponse(['success' => true]); // Connexion valide - } - - return new JsonResponse(['success' => false, 'message' => $this->translator->trans($result['error'])]); // Erreur de connexion - - exit; - - return $this->render('Ajax/result_connexion.html.twig', [] - ); - } - - return new JsonResponse(['success' => false, 'message' => $this->translator->trans('Connection error')]); - } - } else { - $this->logger->error("Error: Not Found Exception"); - throw $this->createNotFoundException('Error'); - } - return new JsonResponse(['success' => false]); - } catch (Exception $e) { - return new JsonResponse(['success' => false, 'message' => $e->getMessage().' '.$e->getLine().' '.$e->getFile()]); - } - } - - /** - * CREATION - STEP ONE - VERIF ALIAS RULE. - * - * @Route("/inputs/name_unique/", name="regle_inputs_name_unique", methods={"POST"}, options={"expose"=true}) - */ - public function ruleNameUniq(Request $request): JsonResponse - { - $key = $this->sessionService->getParamRuleLastKey(); - - if ('POST' == $request->getMethod()) { - $this->getInstanceBdd(); - - // Cherche si la règle existe en fonction de son nom - $rule = $this->entityManager->getRepository(Rule::class) - ->findOneBy([ - 'name' => $request->request->get('name'), - ] - ); - - // 0 existe pas 1 existe - if (null == $rule) { - $existRule = 0; - $this->sessionService->setParamRuleNameValid($key, true); - $this->sessionService->setParamRuleName($key, $request->request->get('name')); - } else { - $existRule = 1; - $this->sessionService->setParamRuleNameValid($key, false); - } - - return new JsonResponse($existRule); - } - throw $this->createNotFoundException('Error'); - } - - /** - * CREATION - STEP TWO - CHOIX MODULES. - * - * @return RedirectResponse|Response - * - * @Route("/create/step2/", name="regle_steptwo", methods={"POST"}) - */ - public function ruleStepTwo(Request $request) - { - $session = $request->getSession(); - $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); - // We always add data again in session because these data are removed after the call of the get - $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); - // si le nom de la règle est inferieur à 3 caractères : - if (!isset($myddlewareSession['param']['rule']['source']['solution']) || strlen($myddlewareSession['param']['rule']['rulename']) < 3 || false == $myddlewareSession['param']['rule']['rulename_valide']) { - $myddlewareSession['error']['create_rule'] = $this->translator->trans('error.rule.valid'); - $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); - - return $this->redirect($this->generateUrl('regle_stepone_animation')); - exit; - } - - try { - // ---------------- SOURCE ---------------------------- - $solution_source_nom = $myddlewareSession['param']['rule']['source']['solution']; - $solution_source = $this->solutionManager->get($solution_source_nom); - - $sourceConnection = $solution_source->login($this->decrypt_params($myddlewareSession['param']['rule']['source'])); - - if (empty($solution_source->connexion_valide)) { - $myddlewareSession['error']['create_rule'] = $this->translator->trans('error.rule.source_module_connect').' '.(!empty($sourceConnection['error']) ? $sourceConnection['error'] : 'No message returned by '.$solution_source_nom); - $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); - - return $this->redirect($this->generateUrl('regle_stepone_animation')); - exit; - } - - $liste_modules_source = ToolsManager::composeListHtml($solution_source->get_modules('source'), $this->translator->trans('create_rule.step2.choice_module')); - if (!$liste_modules_source) { - $myddlewareSession['error']['create_rule'] = $this->translator->trans('error.rule.source_module_load_list'); - $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); - - return $this->redirect($this->generateUrl('regle_stepone_animation')); - exit; - } - - // ---------------- /SOURCE ---------------------------- - } catch (Exception $e) { - $myddlewareSession['error']['create_rule'] = $this->translator->trans('error.rule.source_module_all'); - $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); - - return $this->redirect($this->generateUrl('regle_stepone_animation')); - exit; - } - - try { - // ---------------- TARGET ---------------------------- - // Si la solution est la même que la précèdente on récupère les infos - if ($myddlewareSession['param']['rule']['source']['solution'] == $myddlewareSession['param']['rule']['cible']['solution']) { - $solution_cible = $solution_source; - $solution_cible_nom = $solution_source_nom; - } else { - $solution_cible_nom = $myddlewareSession['param']['rule']['cible']['solution']; - $solution_cible = $this->solutionManager->get($solution_cible_nom); - } - $targetConnection = $solution_cible->login($this->decrypt_params($myddlewareSession['param']['rule']['cible'])); - - if (empty($solution_cible->connexion_valide)) { - $myddlewareSession['error']['create_rule'] = $this->translator->trans('error.rule.target_module_connect').' '.(!empty($targetConnection['error']) ? $targetConnection['error'] : 'No message returned by '.$solution_cible_nom); - $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); - - return $this->redirect($this->generateUrl('regle_stepone_animation')); - exit; - } - - $liste_modules_cible = ToolsManager::composeListHtml($solution_cible->get_modules('target'), $this->translator->trans('create_rule.step2.choice_module')); - - if (!$liste_modules_cible) { - $myddlewareSession['error']['create_rule'] = $this->translator->trans('error.rule.target_module_load_list'); - $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); - - return $this->redirect($this->generateUrl('regle_stepone_animation')); - exit; - } - // ---------------- /TARGET ---------------------------- - } catch (Exception $e) { - $myddlewareSession['error']['create_rule'] = $this->translator->trans('error.rule.target_module_all'); - $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); - - return $this->redirect($this->generateUrl('regle_stepone_animation')); - exit; - } - - return $this->render('Rule/create/step2.html.twig', [ - 'solution_source' => $solution_source_nom, - 'solution_cible' => $solution_cible_nom, - 'liste_modules_source' => $liste_modules_source, - 'liste_modules_cible' => $liste_modules_cible, - 'params' => $myddlewareSession['param']['rule'], - ] - ); - } - - /** - * CREATION - STEP THREE - SIMULATION DES DONNEES. - * - * @Route("/create/step3/simulation/", name="regle_simulation", methods={"POST"}) - */ - public function ruleSimulation(Request $request): Response - { - $ruleKey = $this->sessionService->getParamRuleLastKey(); - - if ('POST' == $request->getMethod() && $this->sessionService->isParamRuleExist($ruleKey)) { - // retourne un tableau prêt à l'emploi - $target = $this->createListeParamsRule( - $request->request->get('champs'), // Fields - $request->request->get('formules'), // Formula - '' // Params flux - ); - - $solution_source_nom = $this->sessionService->getParamRuleSourceSolution($ruleKey); - $solution_source = $this->solutionManager->get($solution_source_nom); - $solution_source->login($this->sessionService->getParamRuleSource($ruleKey)); - $tab_simulation = []; - $sourcesfields = []; - - // récupération de tous les champs - if (isset($target['fields']) && count($target['fields']) > 0) { - foreach ($target['fields'] as $f) { - if (isset($f)) { - foreach ($f as $name_fields_target => $k) { - if (isset($k['champs'])) { - $sourcesfields = array_merge($k['champs'], $sourcesfields); - } - } - } - } - } else { - // ici pour les règles avec des relations uniquement - return $this->render('Rule/create/onglets/simulation_tab.html.twig', [ - 'before' => [], // source - 'after' => [], // target - 'data_source' => false, - ] - ); - } - - // Add rule param if exist (the aren't exist in rule creation) - $ruleParams = []; - $ruleParamsResult = $this->getDoctrine()->getManager()->getRepository(RuleParam::class)->findBy(['rule' => $ruleKey]); - if (!empty($ruleParamsResult)) { - foreach ($ruleParamsResult as $ruleParamsObj) { - $ruleParams[$ruleParamsObj->getName()] = $ruleParamsObj->getValue(); - } - } - // The mode is empty when we create the rule, so we set a default value - if (empty($ruleParams['ruleParams']['mode'])) { - $ruleParams['mode'] = '0'; - } - - // Get result from AJAX request in regle.js - $form = $request->request->all(); - if (isset($form['query'])) { - $this->simulationQueryField = $form['query']; - } - - // Avoid sending query on specific record ID if the user didn't actually input something - if (empty($this->simulationQueryField)) { - // Get source data - $source = $solution_source->readData([ - 'module' => $this->sessionService->getParamRuleSourceModule($ruleKey), - 'fields' => $sourcesfields, - 'date_ref' => '1970-01-01 00:00:00', // date_ref is required for some application like Prestashop - 'limit' => 1, - 'ruleParams' => $ruleParams, - 'call_type' => 'simulation', - ]); - } else { - // Get source data - $source = $solution_source->readData([ - 'module' => $this->sessionService->getParamRuleSourceModule($ruleKey), - 'fields' => $sourcesfields, - 'date_ref' => '1970-01-01 00:00:00', // date_ref is required for some application like Prestashop - 'limit' => 1, - 'ruleParams' => $ruleParams, - 'query' => [(!empty($ruleParams['fieldId']) ? $ruleParams['fieldId'] : 'id') => $this->simulationQueryField], - 'call_type' => 'simulation', - ]); - - // In case of wrong record ID input from user - if (!empty($source['error'])) { - return $this->render('Rule/create/onglets/invalidrecord.html.twig'); - } - } - - $before = []; - $after = []; - if (!empty($source['values'])) { - $record = current($source['values']); // Remove a dimension to the array because we need only one record - if (!empty($record)) { - foreach ($target['fields'] as $f) { - foreach ($f as $name_fields_target => $k) { - $r['after'] = []; - // Préparation pour transformation - $name = trim($name_fields_target); - $target_fields = [ - 'target_field_name' => $name, - 'source_field_name' => ((isset($k['champs'])) ? implode(';', $k['champs']) : 'my_value'), - 'formula' => ((isset($k['formule'][0]) ? $k['formule'][0] : '')), - 'related_rule' => '', - ]; - - // Add rule id for simulation purpose when using lookup function - $this->documentManager->setRuleId($ruleKey); - // Transformation - $response = $this->documentManager->getTransformValue($record, $target_fields); - if (!isset($response['message'])) { - $r['after'][$name_fields_target] = $this->documentManager->getTransformValue($record, $target_fields); - } - // If error during transformation, we send back the error - if ( - null == $r['after'][$name_fields_target] - and !empty($response['message']) - ) { - $r['after'][$name_fields_target] = $response['message']; - } - - $k['fields'] = []; - if (empty($k['champs'])) { - $k['fields']['Formula'] = ((isset($k['formule'][0]) ? $k['formule'][0] : '')); - } else { - foreach ($k['champs'] as $fields) { - // Fields couldn't be return. For example Magento return only field not empty - if (!empty($record[$fields])) { - $k['fields'][$fields] = $record[$fields]; - } else { - $k['fields'][$fields] = ''; - } - } - } - - $tab_simulation[] = [ - 'after' => $r['after'], - 'before' => $k['fields'], - ]; - } - } - $after = []; - // Préparation pour tableau template - foreach ($tab_simulation as $key => $value) { - foreach ($value as $k => $v) { - if ('before' == $k) { - $before[] = $v; - } else { - foreach ($v as $key => $value) { - // if value does not contains the substring "mdw_no_send_field" - if (strpos($value, 'mdw_no_send_field') === false) { - $after[] = $v; - } - } - } - } - } - } - } - - return $this->render('Rule/create/onglets/simulation_tab.html.twig', [ - 'before' => $before, // source - 'after' => $after, // target - 'data_source' => (!empty($record) ? true : false), - 'params' => $this->sessionService->getParamRule($ruleKey), - 'simulationQueryField' => $this->simulationQueryField, - ] - ); - } - throw $this->createNotFoundException('Error'); - } - - /** - * CREATION - STEP THREE - CHOIX DES CHAMPS - MAPPING DES CHAMPS. - * - * @return RedirectResponse|Response - * - * @Route("/create/step3/{id}", name="regle_stepthree", defaults={"id"=0}) - */ - public function ruleStepThree(Request $request) - { - $this->getInstanceBdd(); - $ruleKey = $request->get('id'); - - // Test que l'ordre des étapes - if (!$this->sessionService->isParamRuleExist($ruleKey)) { - $this->sessionService->setCreateRuleError($ruleKey, $this->translator->trans('error.rule.order')); - - return $this->redirect($this->generateUrl('regle_stepone_animation')); - exit; - } - - // Contrôle si la nouvelle règle peut-être valide - if ($this->sessionService->isRuleNameLessThanXCharacters($ruleKey, 3)) { - $this->sessionService->setCreateRuleError($ruleKey, $this->translator->trans('error.rule.valid')); - - return $this->redirect($this->generateUrl('regle_stepone_animation')); - exit; - } - - try { - // ---- Mode update ---- - if (!$this->sessionService->isParamRuleSourceModuleExist($ruleKey) && !$this->sessionService->isParamRuleCibleModuleExist($ruleKey)) { - // RELOAD : Chargement des données d'une règle en édition - $this->sessionService->setParamRuleSourceModule($ruleKey, $request->request->get('source_module')); - $this->sessionService->setParamRuleCibleModule($ruleKey, $request->request->get('cible_module')); - } - // ---- Mode update ---- - - // Get all data from the target solution first - $solution_cible = $this->solutionManager->get($this->sessionService->getParamRuleCibleSolution($ruleKey)); - - // TARGET ------------------------------------------------------------------ - // We retriev first all data from the target application and the from the source application - // We can't do both solution in the same time because we could have a bug when these 2 solutions are the same (service are shared by default in Symfony) - $targetConnection = $solution_cible->login($this->decrypt_params($this->sessionService->getParamRuleCible($ruleKey))); - - if (false == $solution_cible->connexion_valide) { - $this->sessionService->setCreateRuleError($ruleKey, $this->translator->trans('error.rule.target_module_connect').' '.(!empty($targetConnection['error']) ? $targetConnection['error'] : 'No message returned by '.$this->sessionService->getParamRuleCibleSolution($ruleKey))); - - return $this->redirect($this->generateUrl('regle_stepone_animation')); - exit; - } - - if ($request->request->get('cible_module')) { - $module['cible'] = $request->request->get('cible_module'); // mode create <<---- - } else { - $module['cible'] = $this->sessionService->getParamRuleCibleModule($ruleKey); // mode update <<---- - } - - // Récupère la liste des paramètres cible - $ruleParamsTarget = $solution_cible->getFieldsParamUpd('target', $module['cible']); - - // Récupère la liste des champs cible - $ruleFieldsTarget = $solution_cible->get_module_fields($module['cible'], 'target'); - - // Récupération de tous les modes de règle possibles pour la cible et la source - $targetMode = $solution_cible->getRuleMode($module['cible'], 'target'); - - $fieldMappingAdd = $solution_cible->getFieldMappingAdd($module['cible']); - - $allowParentRelationship = $solution_cible->allowParentRelationship($this->sessionService->getParamRuleCibleModule($ruleKey)); - - // Champs pour éviter les doublons - $fieldsDuplicateTarget = $solution_cible->getFieldsDuplicate($this->sessionService->getParamRuleCibleModule($ruleKey)); - - // SOURCE ------------------------------------------------------------------ - // Connexion au service de la solution source - $solution_source = $this->solutionManager->get($this->sessionService->getParamRuleSourceSolution($ruleKey)); - $sourceConnection = $solution_source->login($this->decrypt_params($this->sessionService->getParamRuleSource($ruleKey))); - - // Contrôle que la connexion est valide - if (false == $solution_source->connexion_valide) { - $this->sessionService->setCreateRuleError($ruleKey, $this->translator->trans('error.rule.source_module_connect').' '.(!empty($sourceConnection['error']) ? $sourceConnection['error'] : 'No message returned by '.$this->sessionService->getParamRuleSourceSolution($ruleKey))); - - return $this->redirect($this->generateUrl('regle_stepone_animation')); - exit; - } - $modules = $solution_source->get_modules('source'); - if ($request->request->get('source_module')) { - $module['source'] = $request->request->get('source_module'); // mode create <<---- - } else { - $module['source'] = $this->sessionService->getParamRuleSourceModule($ruleKey); // mode update <<---- - } - - // Met en mémoire la façon de traiter la date de référence - $this->sessionService->setParamRuleSourceDateReference($ruleKey, $solution_source->referenceIsDate($module['source'])); - - // Ajoute des champs source pour la validation - $ruleParamsSource = $solution_source->getFieldsParamUpd('source', $module['source']); - - // Add parameters to be able to read rules linked - $param['connectorSourceId'] = $this->sessionService->getParamRuleConnectorSourceId($ruleKey); - $param['connectorTargetId'] = $this->sessionService->getParamRuleConnectorCibleId($ruleKey); - $param['ruleName'] = $this->sessionService->getParamRuleName($ruleKey); - - // Récupère la liste des champs source - $ruleFieldsSource = $solution_source->get_module_fields($module['source'], 'source', $param); - - if ($ruleFieldsSource) { - $this->sessionService->setParamRuleSourceFields($ruleKey, $ruleFieldsSource); - - // Erreur champs, pas de données sources (Exemple: GotoWebinar) - - if ($this->sessionService->isParamRuleSourceFieldsErrorExist($ruleKey) && null != $this->sessionService->getParamRuleSourceFieldsError($ruleKey)) { - $this->sessionService->setCreateRuleError($ruleKey, $this->sessionService->getParamRuleSourceFieldsError($ruleKey)); - - return $this->redirect($this->generateUrl('regle_stepone_animation')); - exit; - } - - foreach ($ruleFieldsSource as $t => $k) { - $source['table'][$module['source']][$t] = $k['label']; - } - // Tri des champs sans tenir compte de la casse - ksort($source['table'][$module['source']], SORT_NATURAL | SORT_FLAG_CASE); - } - - // SOURCE ----- Récupère la liste des champs source - - // Type de synchronisation - // Récupération de tous les modes de règle possibles pour la source - $sourceMode = $solution_source->getRuleMode($module['source'], 'source'); - // Si la target à le type S (search) alors on l'ajoute à la source pour qu'il soit préservé par l'intersection - if (array_key_exists('S', $targetMode)) { - $sourceMode['S'] = 'search_only'; - } - $intersectMode = array_intersect($targetMode, $sourceMode); - // Si jamais l'intersection venait à être vide (ce qui ne devrait jamais arriver) on met par défaut le mode CREATE - if (empty($intersectMode)) { - $intersectMode['C'] = 'create_only'; - } - // If duplicate field exist for the target solution, we allow search rule type - if (!empty($fieldsDuplicateTarget)) { - $intersectMode['S'] = 'search_only'; - } - $this->sessionService->setParamRuleCibleMode($ruleKey, $intersectMode); - - // Préparation des champs cible - $cible['table'] = []; - - if ($ruleFieldsTarget) { - $this->sessionService->setParamRuleTargetFields($ruleKey, $ruleFieldsTarget); - - $tmp = $ruleFieldsTarget; - - $normal = []; - $required = []; - foreach ($ruleFieldsTarget as $t => $k) { - if (isset($k['required']) && true == $k['required']) { - $required[] = $t; - } else { - $normal[] = $t; - } - } - - asort($required); - asort($normal); - - $alpha = array_merge($required, $normal); - $field_target_alpha = []; - foreach ($alpha as $name_fields) { - $field_target_alpha[$name_fields] = $tmp[$name_fields]['required']; - } - - $cible['table'][$module['cible']] = $field_target_alpha; - } else { - $cible['table'][$module['cible']] = []; // rev 1.1.1 - } - - // On ajoute des champs personnalisés à notre mapping - if ($fieldMappingAdd && $this->sessionService->isParamRuleLastVersionIdExist($ruleKey)) { - $ruleFields = $this->getDoctrine() - ->getManager() - ->getRepository(RuleField::class) - ->findBy(['rule' => $this->sessionService->getParamRuleLastId($ruleKey)]); - - $tmp = []; - foreach ($ruleFields as $fields) { - $tmp[$fields->getTarget()] = 0; - } - - foreach ($cible['table'][$module['cible']] as $k => $value) { - $tmp[$k] = $value; - } - - $cible['table'][$module['cible']] = $tmp; - - ksort($cible['table'][$module['cible']]); - } - - // ------------------- TARGET - $lst_relation_target = []; - $lst_relation_target_alpha = []; - if ($ruleFieldsTarget) { - foreach ($ruleFieldsTarget as $key => $value) { - // Only relationship fields - if (empty($value['relate'])) { - continue; - } - $lst_relation_target[] = $key; - } - - asort($lst_relation_target); - - foreach ($lst_relation_target as $name_relate) { - $lst_relation_target_alpha[$name_relate]['required'] = (!empty($ruleFieldsTarget[$name_relate]['required_relationship']) ? 1 : 0); - $lst_relation_target_alpha[$name_relate]['name'] = $name_relate; - $lst_relation_target_alpha[$name_relate]['label'] = (!empty($ruleFieldsTarget[$name_relate]['label']) ? $ruleFieldsTarget[$name_relate]['label'] : $name_relate); - } - } - - // ------------------- SOURCE - // Liste des relations SOURCE - $lst_relation_source = []; - $lst_relation_source_alpha = []; - $choice_source = []; - if ($ruleFieldsSource) { - foreach ($ruleFieldsSource as $key => $value) { - if (empty($value['relate'])) { - continue; // We keep only relationship fields - } - $lst_relation_source[] = $key; - } - - asort($lst_relation_source); - foreach ($lst_relation_source as $name_relate) { - $lst_relation_source_alpha[$name_relate]['label'] = $ruleFieldsSource[$name_relate]['label']; - } - - // préparation de la liste en html - foreach ($lst_relation_source_alpha as $key => $value) { - $choice_source[$key] = (!empty($value['label']) ? $value['label'] : $key); - } - } - - if (!isset($source['table'])) { - $source['table'][$this->sessionService->getParamRuleSourceModule($ruleKey)] = []; - } - - // -- Relation - // Rule list with the same connectors (both directions) to get the relate ones - $ruleRepo = $this->getDoctrine()->getManager()->getRepository(Rule::class); - $ruleListRelation = $ruleRepo->createQueryBuilder('r') - ->select('r.id, r.name, r.moduleSource') - ->where('( - r.connectorSource= ?1 - AND r.connectorTarget= ?2 - AND r.name != ?3 - AND r.deleted = 0 - ) - OR ( - r.connectorTarget= ?1 - AND r.connectorSource= ?2 - AND r.name != ?3 - AND r.deleted = 0 - )') - ->setParameter(1, (int) $this->sessionService->getParamRuleConnectorSourceId($ruleKey)) - ->setParameter(2, (int) $this->sessionService->getParamRuleConnectorCibleId($ruleKey)) - ->setParameter(3, $this->sessionService->getParamRuleName($ruleKey)) - ->getQuery() - ->getResult(); - - //Verson 1.1.1 : possibilité d'ajouter des relations custom en fonction du module source - $ruleListRelationSourceCustom = $solution_source->get_rule_custom_relationship($this->sessionService->getParamRuleSourceModule($ruleKey), 'source'); - if (!empty($ruleListRelationSourceCustom)) { - $ruleListRelation = array_merge($ruleListRelation, $ruleListRelationSourceCustom); - } - - $choice = []; - $control = []; - - foreach ($ruleListRelation as $key => $value) { - if (!in_array($value['name'], $control)) { - $choice[$value['id']] = $value['name']; - $control[] = $value['name']; - } - } - asort($choice); - - // ------------------- Parent relation - // Search if we can send document merged with the target solution - $lstParentFields = []; - if ($allowParentRelationship) { - if (!empty($ruleListRelation)) { - // We get all relate fields from every source module - foreach ($ruleListRelation as $ruleRelation) { - // Get the relate fields from the source module of related rules - $ruleFieldsSource = $solution_source->get_module_fields($ruleRelation['moduleSource'], 'source'); - if (!empty($ruleFieldsSource)) { - foreach ($ruleFieldsSource as $key => $sourceRelateField) { - if (empty($sourceRelateField['relate'])) { - continue; // Only relationship fields - } - $lstParentFields[$key] = $ruleRelation['name'].' - '.$sourceRelateField['label']; - } - } - } - // We allow to search by the id of the module - $lstParentFields['Myddleware_element_id'] = $this->translator->trans('create_rule.step3.relation.record_id'); - } - // No parent relation if no rule to link or no fields related - if (empty($lstParentFields)) { - $allowParentRelationship = false; - } - } - - // On récupére l'EntityManager - $this->getInstanceBdd(); - - // Récupère toutes les catégories - $lstCategory = $this->entityManager->getRepository(FuncCat::class) - ->findAll(); - - // Récupère toutes les functions - $lstFunctions = $this->entityManager->getRepository(Functions::class) - ->findAll(); - - // Les filtres - $lst_filter = [ - $this->translator->trans('filter.content') => 'content', - $this->translator->trans('filter.notcontent') => 'notcontent', - $this->translator->trans('filter.begin') => 'begin', - $this->translator->trans('filter.end') => 'end', - $this->translator->trans('filter.gt') => 'gt', - $this->translator->trans('filter.lt') => 'lt', - $this->translator->trans('filter.equal') => 'equal', - $this->translator->trans('filter.different') => 'different', - $this->translator->trans('filter.gteq') => 'gteq', - $this->translator->trans('filter.lteq') => 'lteq', - $this->translator->trans('filter.in') => 'in', - $this->translator->trans('filter.notin') => 'notin', - ]; - - - //Behavior filters - $lst_errorMissing = [ - '0' => $this->translator->trans('create_rule.step3.relation.no'), - '1' => $this->translator->trans('create_rule.step3.relation.yes'), - ]; - - $lst_errorEmpty = [ - '0' => $this->translator->trans('create_rule.step3.relation.no'), - '1' => $this->translator->trans('create_rule.step3.relation.yes'), - ]; - // paramètres de la règle - $rule_params = array_merge($ruleParamsSource, $ruleParamsTarget); - - // récupération des champs de type liste -------------------------------------------------- - - // -----[ SOURCE ]----- - if ($this->sessionService->isParamRuleSourceFieldsExist($ruleKey)) { - foreach ($this->sessionService->getParamRuleSourceFields($ruleKey) as $field => $fields_tab) { - if (array_key_exists('option', $fields_tab)) { - $formule_list['source'][$field] = $fields_tab; - } - } - } - - if (isset($formule_list['source']) && count($formule_list['source']) > 0) { - foreach ($formule_list['source'] as $field => $fields_tab) { - foreach ($fields_tab['option'] as $field_name => $fields) { - if (!empty($fields)) { - $formule_list['source'][$field]['option'][$field_name] = $field_name.' ( '.$fields.' )'; - } - } - } - } - - $html_list_source = ''; - if (isset($formule_list['source'])) { - foreach ($formule_list['source'] as $field => $fields_tab) { - $html_list_source .= ''; - $html_list_source .= ToolsManager::composeListHtml($fields_tab['option']); - $html_list_source .= ''; - } - } - - // -----[ TARGET ]----- - if ($this->sessionService->isParamRuleTargetFieldsExist($ruleKey)) { - foreach ($this->sessionService->getParamRuleTargetFields($ruleKey) as $field => $fields_tab) { - if (array_key_exists('option', $fields_tab)) { - $formule_list['target'][$field] = $fields_tab; - } - } - } - - if (isset($formule_list['target']) && count($formule_list['target']) > 0) { - foreach ($formule_list['target'] as $field => $fields_tab) { - foreach ($fields_tab['option'] as $field_name => $fields) { - if (!empty($fields)) { - $formule_list['target'][$field]['option'][$field_name] = $field_name.' ( '.$fields.' )'; - } - } - } - } - - $html_list_target = ''; - if (isset($formule_list['target'])) { - foreach ($formule_list['target'] as $field => $fields_tab) { - $html_list_target .= ''; - $html_list_target .= ToolsManager::composeListHtml($fields_tab['option']); - $html_list_target .= ''; - } - } - - // récupération des champs de type liste -------------------------------------------------- - - // Type de synchronisation de données rev 1.06 -------------------------- - if ($this->sessionService->isParamRuleCibleModuleExist($ruleKey)) { - $mode_translate = []; - foreach ($this->sessionService->getParamRuleCibleMode($ruleKey) as $key => $value) { - $mode_translate[$key] = $this->translator->trans('create_rule.step3.syncdata.'.$value); - } - - $mode = - [ - [ - 'id' => 'mode', - 'name' => 'mode', - 'required' => false, - 'type' => 'option', - 'label' => $this->translator->trans('create_rule.step3.syncdata.label'), - 'option' => $mode_translate, - ], - ]; - - $rule_params = array_merge($rule_params, $mode); - } - // Type de synchronisation de données rev 1.06 -------------------------- - - // rev 1.07 -------------------------- - $bidirectional_params['connector']['source'] = $this->sessionService->getParamRuleConnectorSourceId($ruleKey); - $bidirectional_params['connector']['cible'] = $this->sessionService->getParamRuleConnectorCibleId($ruleKey); - $bidirectional_params['module']['source'] = $module['source']; - $bidirectional_params['module']['cible'] = $module['cible']; - - $bidirectional = RuleManager::getBidirectionalRules($this->connection, $bidirectional_params, $solution_source, $solution_cible); - if ($bidirectional) { - $rule_params = array_merge($rule_params, $bidirectional); - } - - // Add param to allow deletion (need source and target application ok to enable deletion) - if ( - true == $solution_source->getReadDeletion($module['source']) - and true == $solution_cible->getSendDeletion($module['cible']) - ) { - $deletion = [ - [ - 'id' => 'deletion', - 'name' => 'deletion', - 'required' => false, - 'type' => 'option', - 'label' => $this->translator->trans('create_rule.step3.deletion.label'), - 'option' => [0 => '', 1 => $this->translator->trans('create_rule.step3.deletion.yes')], - ], - ]; - $rule_params = array_merge($rule_params, $deletion); - } else { - // If the deletion is disable (database in source OK but target application non OK), we remove the deletion list field of database connector - $keyDeletionField = array_search('deletionField', array_column($rule_params, 'id')); - if (!empty($keyDeletionField)) { - unset($rule_params[$keyDeletionField]); - } - } - - // get the array of array $ruleFieldsSource and for each value, get the label only and add it to the array $listOfSourceFieldsLabels - $listOfSourceFieldsLabels = [ - 'Source Fields' => [], - 'Target Fields' => [], - 'Relation Fields' => [], - ]; - foreach ($ruleFieldsSource as $key => $value) { - $listOfSourceFieldsLabels['Source Fields'][$key] = $value['label']; - } - - // get the array of array $ruleFieldsTarget and for each value, get the label only and add it to the array $listOfSourceFieldsLabels - foreach ($ruleFieldsTarget as $key => $value) { - $listOfSourceFieldsLabels['Target Fields'][$key] = $value['label']; - } - - foreach ($lst_relation_source_alpha as $key => $value) { - $listOfSourceFieldsLabels['Relation Fields'][$key] = $value['label']; - } - - - $form_all_related_fields = $this->createForm(RelationFilterType::class, null, [ - 'field_choices' => $listOfSourceFieldsLabels, - 'another_field_choices' => $lst_filter - ]); - - $filters = $this->entityManager->getRepository(RuleFilter::class) - ->findBy(['rule' => $ruleKey]); - - // we want to make a request that fetches all the rule names and ids, so we can display them in the form - $ruleRepo = $this->getDoctrine()->getManager()->getRepository(Rule::class); - $ruleListRelation = $ruleRepo->createQueryBuilder('r') - ->select('r.id, r.name, r.moduleSource') - ->where('( - r.connectorSource= ?1 - AND r.connectorTarget= ?2 - AND r.name != ?3 - AND r.deleted = 0 - ) - OR ( - r.connectorTarget= ?1 - AND r.connectorSource= ?2 - AND r.name != ?3 - AND r.deleted = 0 - )') - ->setParameter(1, (int) $this->sessionService->getParamRuleConnectorSourceId($ruleKey)) - ->setParameter(2, (int) $this->sessionService->getParamRuleConnectorCibleId($ruleKey)) - ->setParameter(3, $this->sessionService->getParamRuleName($ruleKey)) - ->getQuery() - ->getResult(); - - // from the result ruleListRelation we create an array with the rule name as the key and the rule id as the value - $ruleListRelation = array_reduce($ruleListRelation, function ($carry, $item) { - $carry[$item['name']] = $item['id']; - return $carry; - }, []); - - $html_list_rules = ''; - if (!empty($ruleListRelation)) { - foreach ($ruleListRelation as $ruleName => $ruleId) { - $html_list_rules .= ''; - } - } - - // rev 1.07 -------------------------- - $result = [ - 'filters' => $filters, - 'source' => $source['table'], - 'cible' => $cible['table'], - 'rule_params' => $rule_params, - 'lst_relation_target' => $lst_relation_target_alpha, - 'lst_relation_source' => $choice_source, - 'lst_rule' => $choice, - 'lst_category' => $lstCategory, - 'lst_functions' => $lstFunctions, - 'lst_filter' => $lst_filter, - 'form_all_related_fields' => $form_all_related_fields->createView(), - 'lst_errorMissing' => $lst_errorMissing, - 'lst_errorEmpty' => $lst_errorEmpty, - 'params' => $this->sessionService->getParamRule($ruleKey), - 'duplicate_target' => $fieldsDuplicateTarget, - 'opt_target' => $html_list_target, - 'opt_source' => $html_list_source, - 'html_list_rules' => $html_list_rules, - 'fieldMappingAddListType' => $fieldMappingAdd, - 'parentRelationships' => $allowParentRelationship, - 'lst_parent_fields' => $lstParentFields, - 'regleId' => $ruleKey, - 'simulationQueryField' => $this->simulationQueryField, - ]; - - foreach ($result['source'] as $module => $fields) { - foreach ($fields as $fieldNameEncoded => $fieldValue) { - // Decode the field name - $fieldNameDecoded = urldecode($fieldNameEncoded); - - // Optionally, clean up the field name by removing or replacing unwanted characters - $fieldNameCleaned = $fieldNameDecoded; // Adjust as needed - - // Clean the field value - // Example: Trim whitespace and remove special characters - // Adjust the cleaning logic as per your requirements - $fieldValueCleaned = trim($fieldValue); // Trimming whitespace - // For more aggressive cleaning, uncomment and adjust the following line - // $fieldValueCleaned = preg_replace('/[^\x20-\x7E]/', '', $fieldValueCleaned); - - // Check if any cleaning was necessary for the field name - if ($fieldNameCleaned !== $fieldNameEncoded || $fieldValue !== $fieldValueCleaned) { - // Remove the old key - unset($result['source'][$module][$fieldNameEncoded]); - - // Add the cleaned field name with its cleaned value - $result['source'][$module][$fieldNameCleaned] = $fieldValueCleaned; - } - } - } - - $result = $this->tools->beforeRuleEditViewRender($result); - - // Formatage des listes déroulantes : - $result['lst_relation_source'] = ToolsManager::composeListHtml($result['lst_relation_source'], $this->translator->trans('create_rule.step3.relation.fields')); - $result['lst_parent_fields'] = ToolsManager::composeListHtml($result['lst_parent_fields'], ' '); - $result['lst_rule'] = ToolsManager::composeListHtml($result['lst_rule'], $this->translator->trans('create_rule.step3.relation.fields')); - $result['lst_filter'] = ToolsManager::composeListHtml($result['lst_filter'], $this->translator->trans('create_rule.step3.relation.fields')); - $result['lst_errorMissing'] = ToolsManager::composeListHtml($result['lst_errorMissing'], '', '1'); - $result['lst_errorEmpty'] = ToolsManager::composeListHtml($result['lst_errorEmpty'], '', '0'); - - return $this->render('Rule/create/step3.html.twig', $result); - - // ---------------- - } catch (Exception $e) { - $this->logger->error($e->getMessage().' ('.$e->getFile().' line '.$e->getLine()); - $this->sessionService->setCreateRuleError($ruleKey, $this->translator->trans('error.rule.mapping').' : '.$e->getMessage().' ('.$e->getFile().' line '.$e->getLine().')'); - - // return $this->redirect($this->generateUrl('regle_stepone_animation')); - // exit; - dump($e->getMessage().' ('.$e->getFile().' line '.$e->getLine()); - } - } - - /** - * Indique des informations concernant le champ envoyé en paramètre. - * @Route("/info/{type}/{field}/", name="path_info_field", methods={"GET"}) - * @Route("/info", name="path_info_field_not_param") - */ - public function infoField(Request $request, $field, $type): Response - { - $session = $request->getSession(); - $myddlewareSession = $session->get('myddlewareSession'); - // We always add data again in session because these data are removed after the call of the get - $session->set('myddlewareSession', $myddlewareSession); - if (isset($field) && !empty($field) && isset($myddlewareSession['param']['rule']) && 'my_value' != $field) { - if (isset($myddlewareSession['param']['rule'][0][$type]['fields'][$field])) { - return $this->render('Rule/create/onglets/info.html.twig', [ - 'field' => $myddlewareSession['param']['rule'][0][$type]['fields'][$field], - 'name' => htmlentities(trim($field)), - ] - ); - // SuiteCRM connector uses this structure instead - } elseif (isset($myddlewareSession['param']['rule']['key'])) { - $ruleKey = $myddlewareSession['param']['rule']['key']; - - return $this->render('Rule/create/onglets/info.html.twig', [ - 'field' => $myddlewareSession['param']['rule'][$ruleKey][$type]['fields'][$field], - 'name' => htmlentities(trim($field)), - ] - ); - } else { - // Possibilité de Mutlimodules - foreach ($myddlewareSession['param']['rule'][0][$type]['fields'] as $subModule) { // Ce foreach fonctionnera toujours - if (isset($subModule[$field])) { // On teste si ça existe pour éviter une erreur PHP éventuelle - return $this->render('Rule/create/onglets/info.html.twig', [ - 'field' => $subModule[$field], - 'name' => htmlentities(trim($field)), - ] - ); - } - } - } - // On retourne vide si on l'a pas trouvé précédemment - return $this->render('Rule/create/onglets/info.html.twig', [ - 'field' => '', - ] - ); - } - - return $this->render('Rule/create/onglets/info.html.twig', [ - 'field' => '', - ] - ); - } - - /** - * CREATION - STEP THREE - VERIF DES FORMULES. - * - * @Route("/create/step3/formula/", name="regle_formula", methods={"POST"}) - */ - public function ruleVerifFormula(Request $request): JsonResponse - { - if ('POST' == $request->getMethod()) { - // Mise en place des variables - $this->formuleManager->init($request->request->get('formula')); // mise en place de la règle dans la classe - $this->formuleManager->generateFormule(); // Genère la nouvelle formule à la forme PhP - - return new JsonResponse($this->formuleManager->parse['error']); - } - throw $this->createNotFoundException('Error'); - } - - /** - * CREATION - STEP THREE - Validation du formulaire. - * - * @Route("/create/step3/validation/", name="regle_validation", methods={"POST"}) - */ - public function ruleValidation(Request $request): JsonResponse - { - // On récupére l'EntityManager - $this->getInstanceBdd(); - $this->entityManager->getConnection()->beginTransaction(); - try { - /* - * get rule id in the params in regle.js. In creation, regleId = 0 - */ - if (!empty($request->request->get('params'))) { - foreach ($request->request->get('params') as $searchRuleId) { - if ('regleId' == $searchRuleId['name']) { - $ruleKey = $searchRuleId['value']; - break; - } - } - } - - // retourne un tableau prêt à l'emploi - $tab_new_rule = $this->createListeParamsRule( - $request->request->get('champs'), // Fields - $request->request->get('formules'), // Formula - $request->request->get('params') // Params - ); - unset($tab_new_rule['params']['regleId']); // delete id regle for gestion session - - // fields relate - if (!empty($request->request->get('duplicate'))) { - // fix : Put the duplicate fields values in the old $tab_new_rule array - $duplicateArray = implode(';', $request->request->get('duplicate')); - $tab_new_rule['params']['rule']['duplicate_fields'] = $duplicateArray; - $this->sessionService->setParamParentRule($ruleKey, 'duplicate_fields', $duplicateArray); - } - // si le nom de la règle est inferieur à 3 caractères : - if (strlen($this->sessionService->getParamRuleName($ruleKey)) < 3 || false == $this->sessionService->getParamRuleNameValid($ruleKey)) { - return new JsonResponse(0); - } - - //------------ Create rule - $connector_source = $this->getDoctrine() - ->getManager() - ->getRepository(Connector::class) - ->find($this->sessionService->getParamRuleConnectorSourceId($ruleKey)); - - $connector_target = $this->getDoctrine() - ->getManager() - ->getRepository(Connector::class) - ->find($this->sessionService->getParamRuleConnectorCibleId($ruleKey)); - - $param = RuleManager::getFieldsParamDefault(); - - // Get the id of the rule if we edit a rule - // Generate Rule object (create a new one or instanciate the existing one - if (!$this->sessionService->isParamRuleLastVersionIdEmpty($ruleKey)) { - $oneRule = $this->entityManager->getRepository(Rule::class)->find($this->sessionService->getParamRuleLastId($ruleKey)); - $oneRule->setDateModified(new \DateTime()); - $oneRule->setModifiedBy($this->getUser()); - } else { - $oneRule = new Rule(); - $oneRule->setConnectorSource($connector_source); - $oneRule->setConnectorTarget($connector_target); - $oneRule->setDateCreated(new \DateTime()); - $oneRule->setDateModified(new \DateTime()); - $oneRule->setCreatedBy($this->getUser()); - $oneRule->setModifiedBy($this->getUser()); - $oneRule->setModuleSource($this->sessionService->getParamRuleSourceModule($ruleKey)); - $oneRule->setModuleTarget($this->sessionService->getParamRuleCibleModule($ruleKey)); - $oneRule->setDeleted(0); - $oneRule->setActive((int) $param['active']); - $oneRule->setName($this->sessionService->getParamRuleName($ruleKey)); - } - $this->entityManager->persist($oneRule); - // On fait le flush pour obtenir le nameSlug. En cas de problème on fait un remove dans le catch - $this->entityManager->flush(); - $this->sessionService->setRuleId($ruleKey, $oneRule->getId()); - $nameRule = $oneRule->getNameSlug(); - - // BEFORE SAVE rev 1.08 ---------------------- - $relationshipsBeforeSave = $request->request->get('relations'); - $before_save = $this->ruleManager->beforeSave($this->solutionManager, - ['ruleName' => $nameRule, - 'RuleId' => $oneRule->getId(), - 'connector' => $this->sessionService->getParamParentRule($ruleKey, 'connector'), - 'content' => $tab_new_rule, - 'relationships' => $relationshipsBeforeSave, - 'module' => [ - 'source' => [ - 'solution' => $this->sessionService->getParamRuleSourceSolution($ruleKey), - 'name' => $this->sessionService->getParamRuleSourceModule($ruleKey), - ], - 'target' => [ - 'solution' => $this->sessionService->getParamRuleCibleSolution($ruleKey), - 'name' => $this->sessionService->getParamRuleCibleModule($ruleKey), - ], - ], - ] - ); - if (!$before_save['done']) { - throw new Exception($before_save['message']); - } - // Si le retour du beforeSave contient des paramètres alors on les ajoute à la règle avant sauvegarde - if (!empty($before_save['params'])) { - if (empty($tab_new_rule['params'])) { - $tab_new_rule['params'] = $before_save['params']; - } else { - $tab_new_rule['params'] = array_merge($tab_new_rule['params'], $before_save['params']); - } - } - - // Check if search rule then duplicate field shouldn't be empty - if ( - 'S' == $tab_new_rule['params']['mode'] - and empty($tab_new_rule['params']['rule']['duplicate_fields']) - ) { - throw new Exception($this->translator->trans('Failed to save the rule. If you choose to retrieve data with your rule, you have to select at least one duplicate field.')); - } - - // Edit mode - if (!$this->sessionService->isParamRuleLastVersionIdEmpty($ruleKey)) { - foreach ($oneRule->getFields() as $ruleField) { - $this->entityManager->remove($ruleField); - $this->entityManager->flush(); - } - - foreach ($oneRule->getRelationsShip() as $ruleRelationShip) { - $this->entityManager->remove($ruleRelationShip); - $this->entityManager->flush(); - } - - foreach ($oneRule->getFilters() as $ruleFilter) { - $this->entityManager->remove($ruleFilter); - $this->entityManager->flush(); - } - - // Rule Params - foreach ($oneRule->getParams() as $ruleParam) { - // Save reference date - if ('datereference' == $ruleParam->getName()) { - $date_reference = $ruleParam->getValue(); - } - if ('limit' === $ruleParam->getName()) { - $limit = $ruleParam->getValue(); - } - if (in_array($ruleParam->getName(), $this->tools->getRuleParam())) { - $this->entityManager->remove($ruleParam); - $this->entityManager->flush(); - } - } - } // Create mode - else { - if ($this->sessionService->isParamRuleSourceDateReference($ruleKey) && $this->sessionService->getParamRuleSourceDateReference($ruleKey)) { - $date_reference = date('Y-m-d 00:00:00'); - } else { - $date_reference = ''; - } - } - - //------------------------------- Create rule params ------------------- - if (isset($tab_new_rule['params']) || isset($param['RuleParam'])) { - if (!isset($tab_new_rule['params'])) { - $p = $param['RuleParam']; - } else { - $p = array_merge($param['RuleParam'], $tab_new_rule['params']); - } - - $bidirectional = ''; - foreach ($p as $key => $value) { - // Value could be empty, for bidirectional parameter for example (we don't test empty because mode could be equal 0) - if ('' == $value) { - continue; - } - $oneRuleParam = new RuleParam(); - $oneRuleParam->setRule($oneRule); - - // si tableau de doublon - if ('rule' == $key) { - $oneRuleParam->setName('duplicate_fields'); - $oneRuleParam->setValue($value['duplicate_fields']); - } else { - $oneRuleParam->setName($key); - if ('datereference' == $key) { - // date de référence change en fonction create ou update - $oneRuleParam->setValue($date_reference); - // Limit change according to create or update - } elseif ('limit' == $key) { - // Set default value 100 for limit - if (empty($limit)) { - $limit = 100; - } - $oneRuleParam->setValue($limit); - } else { - $oneRuleParam->setValue($value); - } - } - // Save the parameter - if ('bidirectional' == $key) { - $bidirectional = $value; - } - $this->entityManager->persist($oneRuleParam); - $this->entityManager->flush(); - } - - // If a bidirectional parameter exist, we check if the opposite one exists too - if (!empty($bidirectional)) { - // Update the opposite rule if birectional rule - $ruleParamBidirectionalOpposite = $this->entityManager->getRepository(RuleParam::class) - ->findOneBy([ - 'rule' => $bidirectional, - 'name' => 'bidirectional', - 'value' => $oneRule->getId(), - ]); - $bidirectionalRule = $this->ruleRepository->find($bidirectional); - // If the bidirectional parameter doesn't exist on the opposite rule we create it - if (empty($ruleParamBidirectionalOpposite)) { - $ruleParamBidirectionalOpposite = new RuleParam(); - $ruleParamBidirectionalOpposite->setRule($bidirectionalRule); - $ruleParamBidirectionalOpposite->setName('bidirectional'); - $ruleParamBidirectionalOpposite->setValue($oneRule->getId()); - $this->entityManager->persist($ruleParamBidirectionalOpposite); - } - } else { - // If no bidirectional parameter on the rule and if the bidirectional parametr exist on an opposite rule, we delete it - $ruleParamBidirectionalDelete = $this->entityManager->getRepository(RuleParam::class) - ->findOneBy([ - 'value' => $oneRule->getId(), - 'name' => 'bidirectional', - ]); - if (!empty($ruleParamBidirectionalDelete)) { - $this->entityManager->remove($ruleParamBidirectionalDelete); - $this->entityManager->flush(); - } - } - } - - //------------------------------- Create rule fields ------------------- - $debug = []; - - if (isset($tab_new_rule['fields'])) { - foreach ($tab_new_rule['fields']['name'] as $field_target => $c) { - $field_source = ''; - if (isset($c['champs'])) { - foreach ($c['champs'] as $name) { - $field_source .= $name.';'; - } - $field_source = trim($field_source, ';'); - } - - // Formula - $formule = ''; - if (isset($c['formule'])) { - foreach ($c['formule'] as $name) { - $formule .= $name.' '; - $debug[] = $name.' '; - } - } - - // Insert - $oneRuleField = new RuleField(); - $oneRuleField->setRule($oneRule); - $oneRuleField->setTarget(trim($field_target)); - $oneRuleField->setSource(((!empty($field_source)) ? $field_source : 'my_value')); - $oneRuleField->setFormula(((!empty($formule)) ? trim($formule) : null)); - $this->entityManager->persist($oneRuleField); - $this->entityManager->flush(); - } - } - - //------------------------------- RELATIONSHIPS ------------------- - $tabRelationShips = []; - if (!is_null($request->request->get('relations'))) { - foreach ($request->request->get('relations') as $rel) { - if ( - !empty($rel['rule']) - && !empty($rel['source']) - ) { - // Creation dans la table RelationShips - $oneRuleRelationShip = new RuleRelationShip(); - $oneRuleRelationShip->setRule($oneRule); - $oneRuleRelationShip->setFieldNameSource($rel['source']); - $oneRuleRelationShip->setFieldNameTarget($rel['target']); - // No error empty or missing for parent relationship, we set default values - if (!empty($rel['parent'])) { - $rel['errorEmpty'] = '0'; - $rel['errorMissing'] = '1'; - } - $oneRuleRelationShip->setErrorEmpty($rel['errorEmpty']); - $oneRuleRelationShip->setErrorMissing($rel['errorMissing']); - $oneRuleRelationShip->setFieldId($rel['rule']); - $oneRuleRelationShip->setParent($rel['parent']); - $oneRuleRelationShip->setDeleted(0); - // We don't create the field target if the relatiobnship is a parent one - // We only use this field to search in the source application, not to send the data to the target application. - if (empty($rel['parent'])) { - $tabRelationShips['target'][] = $rel['target']; - } - $tabRelationShips['source'][] = $rel['source']; - $this->entityManager->persist($oneRuleRelationShip); - $this->entityManager->flush(); - } - } - } - - // $form = $this->createForm(RelationFilterType::class); - // $form->handleRequest($request); - //------------------------------- RuleFilter ------------------------ - // $form->handleRequest($request); - //------------------------------- RuleFilter ------------------------ - $filters = $request->request->get('filter'); - - if (!empty($filters)) { - foreach ($filters as $filterData) { - // $filterData est un tableau contenant les valeurs des champs pour chaque élément de liste
  • - - // Accès aux valeurs des champs individuels - $fieldInput = $filterData['target']; - $anotherFieldInput = $filterData['filter']; - $textareaFieldInput = $filterData['value']; - - - // Maintenant, vous pouvez utiliser ces valeurs comme vous le souhaitez, par exemple, pour créer un objet RuleFilter - $oneRuleFilter = new RuleFilter(); - $oneRuleFilter->setTarget($fieldInput); - $oneRuleFilter->setRule($oneRule); - - $oneRuleFilter->setType($anotherFieldInput); - $oneRuleFilter->setValue($textareaFieldInput); - - // Enregistrez votre objet RuleFilter dans la base de données - $this->entityManager->persist($oneRuleFilter); - } - - $this->entityManager->flush(); - } - // $this->getDoctrine()->getManager()->flush(); - // } - // if (!empty($request->request->get('filter'))) { - // foreach ($request->request->get('filter') as $filter) { - // $oneRuleFilter = new RuleFilter(); - // $oneRuleFilter->setTarget($filter['target']); - // $oneRuleFilter->setRule($oneRule); - // $oneRuleFilter->setType($filter['filter']); - // $oneRuleFilter->setValue($filter['value']); - // $this->entityManager->persist($oneRuleFilter); - // $this->entityManager->flush(); - // } - // } - - // -------------------------------------------------------------------------------------------------- - // Order all rules - error_log(print_r($request->request->all(), true)); - $this->jobManager->orderRules(); - - // -------------------------------------------------------------------------------------------------- - // Create rule history in order to follow all modifications - // Encode every rule parameters - $ruledata = json_encode( - [ - 'ruleName' => $nameRule, - 'limit' => $limit, - 'datereference' => $date_reference, - 'content' => $tab_new_rule, - 'filters' => $request->request->get('filter'), - 'relationships' => $relationshipsBeforeSave, - ] - ); - // Save the rule audit - $oneRuleAudit = new RuleAudit(); - $oneRuleAudit->setRule($oneRule); - $oneRuleAudit->setDateCreated(new \DateTime()); - $oneRuleAudit->setData($ruledata); - $oneRuleAudit->setCreatedBy($this->getUser()); - $this->entityManager->persist($oneRuleAudit); - $this->entityManager->flush(); - - // notification - $solution_source = $this->solutionManager->get($this->sessionService->getParamRuleSourceSolution($ruleKey)); - $solution_source->setMessageCreateRule($this->sessionService->getParamRuleSourceModule($ruleKey)); - - $solution_target = $this->solutionManager->get($this->sessionService->getParamRuleCibleSolution($ruleKey)); - $solution_target->setMessageCreateRule($this->sessionService->getParamRuleCibleModule($ruleKey)); - // notification - - // -------------------------------------------------------------------------------------------------- - - // Détection règle root ou child rev 1.08 ---------------------- - // On réactualise les paramètres - $tab_new_rule['content']['params'] = $p; - $this->ruleManager->afterSave($this->solutionManager, - [ - 'ruleId' => $oneRule->getId(), - 'ruleName' => $nameRule, - 'oldRule' => ($this->sessionService->isParamRuleLastVersionIdEmpty($ruleKey)) ? '' : $this->sessionService->getParamRuleLastId($ruleKey), - 'datereference' => $date_reference, - 'limit' => $limit, - 'connector' => $this->sessionService->getParamParentRule($ruleKey, 'connector'), - 'content' => $tab_new_rule, - 'relationships' => $relationshipsBeforeSave, - 'module' => [ - 'source' => [ - 'solution' => $this->sessionService->getParamRuleSourceSolution($ruleKey), - 'name' => $this->sessionService->getParamRuleSourceModule($ruleKey), - ], - 'target' => [ - 'solution' => $this->sessionService->getParamRuleCibleSolution($ruleKey), - 'name' => $this->sessionService->getParamRuleCibleModule($ruleKey), - ], - ], - ] - ); - if ($this->sessionService->isParamRuleExist($ruleKey)) { - $this->sessionService->removeParamRule($ruleKey); - } - $this->entityManager->getConnection()->commit(); - - $rule_id = $oneRule->getId(); - $response = ['status' => 1, 'id' => $rule_id]; - //$response = 1; - } catch (Exception $e) { - $this->entityManager->getConnection()->rollBack(); - $this->logger->error('2;'.htmlentities($e->getMessage().' ('.$e->getFile().' line '.$e->getLine().')')); - $response = '2;'.htmlentities($e->getMessage().' ('.$e->getFile().' line '.$e->getLine().')'); - } - - $this->entityManager->close(); - - return new JsonResponse($response); - } - - /** - * TABLEAU DE BORD. - * - * @Route("/panel", name="regle_panel") - */ - public function panel(Request $request): Response - { - $language = $request->getLocale(); - - $this->getInstanceBdd(); - $solution = $this->entityManager->getRepository(Solution::class) - ->solutionActive(); - $lstArray = []; - if ($solution) { - foreach ($solution as $s) { - $lstArray[] = $s->getName(); - } - } - - /** @var User $user */ - $user = $this->getUser(); - $nbFlux = 0; - $listFlux = $this->documentRepository->countTypeDoc($user); - foreach ($listFlux as $field => $value) { - $nbFlux = $nbFlux + (int) $value['nb']; - } - - return $this->render('Home/index.html.twig', [ - 'errorByRule' => $this->ruleRepository->errorByRule($user), - 'listJobDetail' => $this->jobRepository->listJobDetail(), - 'nbFlux' => $nbFlux, - 'solutions' => $lstArray, - 'locale' => $language, - ] - ); - } - - /** - * @Route("/graph/type/error/doc", name="graph_type_error_doc", options={"expose"=true}) - */ - public function graphTypeError(): Response - { - $countTypeDoc = []; - $documents = $this->documentRepository->countTypeDoc($this->getUser()); - if (count($documents)) { - $countTypeDoc[] = ['test', 'test2']; - foreach ($documents as $value) { - $countTypeDoc[] = [$value['global_status'], (int) $value['nb']]; - } - } - - return $this->json($countTypeDoc); - } - - /** - * @Route("/graph/type/transfer/rule", name="graph_type_transfer_rule", options={"expose"=true}) - */ - public function graphTransferRule(): Response - { - $countTransferRule = []; - $values = $this->documentRepository->countTransferRule($this->getUser()); - if (count($values)) { - $countTransferRule[] = ['test', 'test2']; - foreach ($values as $value) { - $countTransferRule[] = [$value['name'], (int) $value['nb']]; - } - } - - return $this->json($countTransferRule); - } - - /** - * @Route("/graph/type/transfer/histo", name="graph_type_transfer_histo", options={"expose"=true}) - */ - public function graphTransferHisto(): Response - { - $countTransferRule = []; - $values = $this->home->countTransferHisto($this->getUser()); - if (count($values)) { - $countTransferRule[] = [ - 'date', - $this->translator->trans('flux.gbl_status.open'), - $this->translator->trans('flux.gbl_status.error'), - $this->translator->trans('flux.gbl_status.cancel'), - $this->translator->trans('flux.gbl_status.close'), - ]; - foreach ($values as $field => $value) { - $countTransferRule[] = [ - $value['date'], - (int) $value['open'], - (int) $value['error'], - (int) $value['cancel'], - (int) $value['close'], - ]; - } - } - - return $this->json($countTransferRule); - } - - /** - * @Route("/graph/type/job/histo", name="graph_type_job_histo", options={"expose"=true}) - */ - public function graphJobHisto(): Response - { - $countTransferRule = []; - $jobs = $this->jobRepository->findBy([], ['begin' => 'ASC'], 5); - - $timeZone = $this->get('session')->get('_timezone', 'UTC'); - $userTimeZone = new \DateTimeZone($timeZone); - - if (count($jobs)) { - $countTransferRule[] = [ - 'date', - $this->translator->trans('flux.gbl_status.open'), - $this->translator->trans('flux.gbl_status.error'), - $this->translator->trans('flux.gbl_status.cancel'), - $this->translator->trans('flux.gbl_status.close'), - ]; - foreach ($jobs as $job) { - $start = clone $job->getBegin(); - $start->setTimezone($userTimeZone); - - $countTransferRule[] = [ - $start->format('d/m/Y H:i:s'), - (int) $job->getOpen(), - (int) $job->getError(), - (int) $job->getCancel(), - (int) $job->getClose(), - ]; - } - } - - return $this->json($countTransferRule); - } - - /** - * ANIMATION - * No more submodule in Myddleware. We return a response 0 for the js (animation.js. - * - * @Route("/submodules", name="regle_submodules", methods={"POST"}) - */ - public function listSubModulesAction(): Response - { - return new Response(0); - } - - /** - * VALIDATION DE L'ANIMATION. - * - * @Route("/validation", name="regle_validation_animation") - */ - public function validationAnimationAction(Request $request): Response - { - $key = $this->sessionService->getParamRuleLastKey(); - - try { - $choiceSelect = $request->get('choice_select', null); - if (null != $choiceSelect) { - if ('module' == $choiceSelect) { - // si le nom de la règle est inferieur à 3 caractères : - if (empty($this->sessionService->getParamRuleSourceSolution($key)) || strlen($this->sessionService->getParamRuleName($key)) < 3) { - $this->sessionService->setParamRuleNameValid($key, false); - } else { - $this->sessionService->setParamRuleNameValid($key, true); - } - $this->sessionService->setParamRuleSourceModule($key, $request->get('module_source')); - $this->sessionService->setParamRuleCibleModule($key, $request->get('module_target')); - - return new Response('module'); - } elseif ('template' == $choiceSelect) { - // Rule creation with the template selected in parameter - $ruleName = $request->get('name'); - $templateName = $request->get('template'); - - $connectorSourceId = (int) $this->sessionService->getParamRuleConnectorSourceId($key); - $connectorTargetId = (int) $this->sessionService->getParamRuleConnectorCibleId($key); - /** @var User $user */ - $user = $this->getUser(); - try { - $this->template->convertTemplate($ruleName, $templateName, $connectorSourceId, $connectorTargetId, $user); - } catch (Exception $e) { - $this->logger->error($e->getMessage().' '.$e->getFile().' '.$e->getLine()); - - return new Response('error'); - } - // Sort the rules - $this->jobManager->orderRules(); - // We return to the list of rule even in case of error (session messages will be displyed in the UI)/: See animation.js function animConfirm - return new Response('template'); - } - - return new Response(0); - } - - return new Response(0); - } catch (Exception $e) { - $this->logger->error($e->getMessage().' '.$e->getFile().' '.$e->getLine()); - - return new Response($e->getMessage()); - } - } - - /** - * LISTE DES TEMPLATES. - * - * @Route("/list/template", name="regle_template") - */ - public function listTemplateAction(): Response - { - $key = $this->sessionService->getParamRuleLastKey(); - $solutionSourceName = $this->sessionService->getParamRuleSourceSolution($key); - $solutionTargetName = $this->sessionService->getParamRuleCibleSolution($key); - $templates = $this->template->getTemplates($solutionSourceName, $solutionTargetName); - if (!empty($templates)) { - $rows = ''; - foreach ($templates as $t) { - $rows .= '
  • - - - - - - - '.$t['name'].''.$t['description'].'
    - - - - - - - - - '.$rows.' - -
    #'.$this->translator->trans('animate.choice.name').''.$this->translator->trans('animate.choice.description').'
    '); - } - - return new Response(''); - } - - /** - * CREATION - STEP ONE - ANIMATION. - * - * @Route("/create", name="regle_stepone_animation") - */ - public function ruleStepOneAnimation(): Response - { - if ($this->sessionService->isConnectorExist()) { - $this->sessionService->removeMyddlewareConnector(); - } - - // New Rule - $this->sessionService->setParamRuleLastKey(0); - - $key = $this->sessionService->getParamRuleLastKey(); - - // Détecte s'il existe des erreurs - if ($this->sessionService->isErrorNotEmpty($key, SessionService::ERROR_CREATE_RULE_INDEX)) { - $error = $this->sessionService->getCreateRuleError($key); - $this->sessionService->removeError($key, SessionService::ERROR_CREATE_RULE_INDEX); - } else { - $error = false; - } - - // Liste source : solution avec au moins 1 connecteur - $this->getInstanceBdd(); - - $solutionSource = $this->entityManager->getRepository(Solution::class) - ->solutionConnector('source', $this->getUser()->isAdmin(), $this->getUser()->getId()); - - if (!empty($solutionSource)) { - foreach ($solutionSource as $s) { - $source[] = $s->getName(); - } - $this->sessionService->setParamConnectorSolutionSource($key, $source); - } - - // Liste target : solution avec au moins 1 connecteur - $solutionTarget = $this->entityManager->getRepository(Solution::class) - ->solutionConnector('target', $this->getUser()->isAdmin(), $this->getUser()->getId()); - - if (!empty($solutionTarget)) { - foreach ($solutionTarget as $t) { - $target[] = $t->getName(); - } - $this->sessionService->setParamConnectorSolutionTarget($key, $target); - } - - return $this->render('Rule/create/step1simply.html.twig', [ - 'source' => $solutionSource, - 'target' => $solutionTarget, - 'error' => $error, - ] - ); - } - - /** - * LISTE DES MODULES POUR ANIMATION. - * @throws Exception - * @Route("/list/module", name="regle_list_module") - */ - public function ruleListModule(Request $request): Response - { - try { - $id_connector = $request->get('id'); - $type = $request->get('type'); - $key = $this->sessionService->getParamRuleLastKey(); // It's a new rule, last key = 0 - - // Control the request - if (!in_array($type, ['source', 'cible']) || !is_numeric($id_connector)) { - throw $this->createAccessDeniedException(); - } - $id_connector = (int) $id_connector; - - $this->getInstanceBdd(); - $connector = $this->entityManager->getRepository(Connector::class) - ->find($id_connector); // infos connector - - $connectorParams = $this->entityManager->getRepository(ConnectorParam::class) - ->findBy(['connector' => $id_connector]); // infos params connector - - foreach ($connectorParams as $p) { - $this->sessionService->setParamRuleParentName($key, $type, $p->getName(), $p->getValue()); // params connector - } - $this->sessionService->setParamRuleConnectorParent($key, $type, $id_connector); // id connector - $this->sessionService->setParamRuleParentName($key, $type, 'solution', $connector->getSolution()->getName()); // nom de la solution - - $solution = $this->solutionManager->get($this->sessionService->getParamRuleParentName($key, $type, 'solution')); - - $params_connexion = $this->decrypt_params($this->sessionService->getParamParentRule($key, $type)); - $params_connexion['idConnector'] = $id_connector; - - $solution->login($params_connexion); - - $t = (('source' == $type) ? 'source' : 'target'); - - $liste_modules = ToolsManager::composeListHtml($solution->get_modules($t), $this->translator->trans('create_rule.step1.choose_module')); - - return new Response($liste_modules); - } catch (Exception $e) { - $error = $e->getMessage().' '.$e->getLine().' '.$e->getFile(); - $this->logger->error($error); - return new Response(''); - } - } - - /* ****************************************************** - * METHODES PRATIQUES - ****************************************************** */ - - // CREATION REGLE - STEP ONE : Liste des connecteurs pour un user - private function connectorsList($type): string - { - $this->getInstanceBdd(); - $solutionRepo = $this->entityManager->getRepository(Connector::class); - $solution = $solutionRepo->findAllConnectorByUser($this->getUser()->getId(), $type); // type = source ou target - $lstArray = []; - if ($solution) { - foreach ($solution as $s) { - $lstArray[$s['name'].'_'.$s['id_connector']] = ucfirst($s['label']); - } - } - - return ToolsManager::composeListHtml($lstArray, $this->translator->trans('create_rule.step1.list_empty')); - } - - // CREATION REGLE - STEP THREE - Retourne les paramètres dans un bon format de tableau - private function createListeParamsRule($fields, $formula, $params): array - { - $phrase_placeholder = $this->translator->trans('rule.step3.placeholder'); - $tab = []; - - // FIELDS ------------------------------------------ - if ($fields) { - $champs = explode(';', $fields); - foreach ($champs as $champ) { - $chp = explode('[=]', $champ); - - if ($chp[0]) { - if ($phrase_placeholder != $chp[1] && 'my_value' != $chp[1]) { - $tab['fields']['name'][$chp[0]]['champs'][] = $chp[1]; - } - } - } - } - - // FORMULA ----------------------------------------- - if ($formula) { - $formules = explode(';', $formula); - - foreach ($formules as $formule) { - $chp = explode('[=]', $formule); - if ($chp[0]) { - if (!empty($chp[1])) { - $tab['fields']['name'][$chp[0]]['formule'][] = $chp[1]; - } - } - } - } - - // PARAMS ----------------------------------------- - if ($params) { - foreach ($params as $k => $p) { - $tab['params'][$p['name']] = $p['value']; - } - } - - return $tab; - } - - // Crée la pagination avec le Bundle Pagerfanta en fonction d'une requete - private function nav_pagination($params, $orm = true) - { - /* - * adapter_em_repository = requete - * maxPerPage = integer - * page = page en cours - */ - - if (is_array($params)) { - /* DOC : - * $pager->setCurrentPage($page); - $pager->getNbResults(); - $pager->getMaxPerPage(); - $pager->getNbPages(); - $pager->haveToPaginate(); - $pager->hasPreviousPage(); - $pager->getPreviousPage(); - $pager->hasNextPage(); - $pager->getNextPage(); - $pager->getCurrentPageResults(); - */ - - $compact = []; - - //On passe l’adapter au bundle qui va s’occuper de la pagination - if ($orm) { - $compact['pager'] = new Pagerfanta(new QueryAdapter($params['adapter_em_repository'])); - } else { - $compact['pager'] = new Pagerfanta(new ArrayAdapter($params['adapter_em_repository'])); - } - - //On définit le nombre d’article à afficher par page (que l’on a biensur définit dans le fichier param) - $compact['pager']->setMaxPerPage($params['maxPerPage']); - try { - $compact['entities'] = $compact['pager'] - //On indique au pager quelle page on veut - ->setCurrentPage($params['page']) - //On récupère les résultats correspondant - ->getCurrentPageResults(); - - $compact['nb'] = $compact['pager']->getNbResults(); - } catch (\Pagerfanta\Exception\NotValidCurrentPageException $e) { - //Si jamais la page n’existe pas on léve une 404 - throw $this->createNotFoundException("Cette page n'existe pas."); - } - - return $compact; - } - - return false; - } - - // Décrypte les paramètres de connexion d'une solution - private function decrypt_params($tab_params) - { - // Instanciate object to decrypte data - $encrypter = new Encrypter(substr($this->getParameter('secret'), -16)); - if (is_array($tab_params)) { - $return_params = []; - foreach ($tab_params as $key => $value) { - if ( - is_string($value) - && !in_array($key, ['solution', 'module']) // Soe data aren't crypted - ) { - $return_params[$key] = $encrypter->decrypt($value); - } - } - - return $return_params; - } - - return $encrypter->decrypt($tab_params); - } - - - // Function to take source document ids optain in a form and reading them and them only. - - /** - * @param $id - * - * @Route("/executebyid/{id}", name="run_by_id") - */ - public function execRuleById($id, Request $request) - { - $form = $this->createFormBuilder() - ->add('id', TextareaType::class) - ->getForm(); - - $form->handleRequest($request); - - if ($form->isSubmitted() && $form->isValid()) { - $documentIdString = $form->get('id')->getData(); - - // We will get to the runrecord commmand using the ids from the form. - $this->ruleExecAction($id, $documentIdString); - } - return $this->render('Rule/byIdForm.html.twig', [ - 'formIdBatch' => $form->createView() - ]); - } - - /** - * @Route("/rule/update_description", name="update_rule_description", methods={"POST"}) - */ - public function updateDescription(Request $request): Response - { - $ruleId = $request->request->get('ruleId'); - $description = $request->request->get('description'); - $entityManager = $this->getDoctrine()->getManager(); - $descriptionOriginal = $entityManager->getRepository(RuleParam::class)->findOneBy([ - 'rule' => $ruleId, - 'name' => 'description' - ]); - // if $description is the same as the previous one or is equal to 0 or is empty - if ($description === '0' || empty($description) || $description === $descriptionOriginal->getValue()) { - return $this->redirect($this->generateUrl('regle_open', ['id' => $ruleId])); - } - - // Retrieve the RuleParam entity using the ruleId - $rule = $entityManager->getRepository(RuleParam::class)->findOneBy(['rule' => $ruleId]); - - if (!$rule) { - throw $this->createNotFoundException('Couldn\'t find specified rule in database'); - } - - // Retrieve the RuleParam with the name "description" and the same rule as the previously retrieved entity - $descriptionRuleParam = $entityManager->getRepository(RuleParam::class)->findOneBy([ - 'rule' => $rule->getRule(), - 'name' => 'description' - ]); - - // Check if the description entity was found - if (!$descriptionRuleParam) { - throw $this->createNotFoundException('Couldn\'t find description rule parameter'); - } - - // Update the value of the description - $descriptionRuleParam->setValue($description); - $entityManager->flush(); - - return new Response('', Response::HTTP_OK); - } -} +. + *********************************************************************************/ + +namespace App\Controller; + +use App\Entity\Connector; +use App\Entity\ConnectorParam; +use App\Entity\Document; +use App\Entity\FuncCat; +use App\Entity\Functions; +use App\Entity\Rule; +use App\Entity\RuleAudit; +use App\Entity\RuleField; +use App\Entity\RuleFilter; +use App\Entity\RuleParam; +use App\Entity\RuleParamAudit; +use App\Entity\RuleRelationShip; +use App\Entity\Solution; +use App\Entity\User; +use App\Form\ConnectorType; +use App\Form\DuplicateRuleFormType; +use App\Manager\DocumentManager; +use App\Manager\FormulaManager; +use App\Manager\HomeManager; +use App\Manager\JobManager; +use App\Manager\RuleManager; +use App\Manager\SolutionManager; +use App\Manager\TemplateManager; +use App\Manager\ToolsManager; +use App\Repository\ConfigRepository; +use App\Repository\DocumentRepository; +use App\Repository\JobRepository; +use App\Repository\RuleRepository; +use App\Service\SessionService; +use Doctrine\DBAL\Connection; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\Id; +use Exception; +use Illuminate\Encryption\Encrypter; +use Pagerfanta\Adapter\ArrayAdapter; +use Pagerfanta\Doctrine\ORM\QueryAdapter; +use Pagerfanta\Pagerfanta; +use Psr\Log\LoggerInterface; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; +use Symfony\Component\Form\Extension\Core\Type\TextareaType; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; +use Symfony\Contracts\Translation\TranslatorInterface; +use App\Form\Type\RelationFilterType; +use App\Entity\Workflow; + + /** + * @Route("/rule") + */ + class DefaultController extends AbstractController + { + private FormulaManager $formuleManager; + private SessionService $sessionService; + private ParameterBagInterface $params; + private EntityManagerInterface $entityManager; + private HomeManager $home; + private ToolsManager $tools; + private TranslatorInterface $translator; + private AuthorizationCheckerInterface $authorizationChecker; + private JobManager $jobManager; + private LoggerInterface $logger; + private TemplateManager $template; + private RuleRepository $ruleRepository; + private JobRepository $jobRepository; + private DocumentRepository $documentRepository; + private SolutionManager $solutionManager; + private RuleManager $ruleManager; + private DocumentManager $documentManager; + protected Connection $connection; + // To allow sending a specific record ID to rule simulation + protected $simulationQueryField; + private ConfigRepository $configRepository; + + public function __construct( + LoggerInterface $logger, + RuleManager $ruleManager, + FormulaManager $formuleManager, + SolutionManager $solutionManager, + DocumentManager $documentManager, + SessionService $sessionService, + EntityManagerInterface $entityManager, + RuleRepository $ruleRepository, + JobRepository $jobRepository, + DocumentRepository $documentRepository, + Connection $connection, + TranslatorInterface $translator, + AuthorizationCheckerInterface $authorizationChecker, + HomeManager $home, + ToolsManager $tools, + JobManager $jobManager, + TemplateManager $template, + ParameterBagInterface $params + ) { + $this->logger = $logger; + $this->ruleManager = $ruleManager; + $this->formuleManager = $formuleManager; + $this->solutionManager = $solutionManager; + $this->documentManager = $documentManager; + $this->sessionService = $sessionService; + $this->entityManager = $entityManager; + $this->ruleRepository = $ruleRepository; + $this->jobRepository = $jobRepository; + $this->documentRepository = $documentRepository; + $this->connection = $connection; + $this->translator = $translator; + $this->authorizationChecker = $authorizationChecker; + $this->home = $home; + $this->tools = $tools; + $this->jobManager = $jobManager; + $this->template = $template; + } + + protected function getInstanceBdd() + { + } + + /* ****************************************************** + * RULE + ****************************************************** */ + + /** + * LISTE DES REGLES. + * + * @return RedirectResponse|Response + * + * @Route("/list", name="regle_list", defaults={"page"=1}) + * @Route("/list/page-{page}", name="regle_list_page", requirements={"page"="\d+"}) + */ + public function ruleListAction(int $page = 1, Request $request) + { + try { + + $ruleName = $request->query->get('rule_name'); + + if ($ruleName) { + + $key = $this->sessionService->getParamRuleLastKey(); + if (null != $key && $this->sessionService->isRuleIdExist($key)) { + $id = $this->sessionService->getRuleId($key); + $this->sessionService->removeRuleId($key); + + return $this->redirect($this->generateUrl('regle_open', ['id' => $id])); + } + + $this->getInstanceBdd(); + $compact['nb'] = 0; + $pager = $this->tools->getParamValue('ruleListPager'); + $compact = $this->nav_pagination([ + 'adapter_em_repository' => $this->entityManager->getRepository(Rule::class)->findListRuleByUser($this->getUser(), $ruleName), + 'maxPerPage' => isset($pager) ? $pager : 20, + 'page' => $page, + ]); + + } else { + + $key = $this->sessionService->getParamRuleLastKey(); + if (null != $key && $this->sessionService->isRuleIdExist($key)) { + $id = $this->sessionService->getRuleId($key); + $this->sessionService->removeRuleId($key); + + return $this->redirect($this->generateUrl('regle_open', ['id' => $id])); + } + + $this->getInstanceBdd(); + + $compact['nb'] = 0; + $pager = $this->tools->getParamValue('ruleListPager'); + $compact = $this->nav_pagination([ + 'adapter_em_repository' => $this->entityManager->getRepository(Rule::class)->findListRuleByUser($this->getUser()), + 'maxPerPage' => isset($pager) ? $pager : 20, + 'page' => $page, + ]); + + } + + // Si tout se passe bien dans la pagination + if ($compact) { + // Si aucune règle + if ($compact['nb'] < 1 && !intval($compact['nb'])) { + $compact['entities'] = ''; + $compact['pager'] = ''; + } + + return $this->render( + 'Rule/list.html.twig', + [ + 'nb_rule' => $compact['nb'], + 'entities' => $compact['entities'], + 'pager' => $compact['pager'], + ] + ); + } + throw $this->createNotFoundException('Error'); + + } catch (Exception $e) { + throw $this->createNotFoundException('Error : ' . $e); + } + } + + + /** + * SUPPRESSION D'UNE REGLE. + * + * @Route("/delete/{id}", name="regle_delete") + */ + public function deleteRule(Request $request, $id): RedirectResponse + { + $session = $request->getSession(); + + // First, checking that the rule has document not deleted + $docClose = $this->getDoctrine() + ->getManager() + ->getRepository(Document::class) + ->findOneBy([ + 'rule' => $id, + 'deleted' => 0, + ] + ); + // Return to the view detail for the rule if we found a document close + if (!empty($docClose)) { + $session->set('error', [$this->translator->trans('error.rule.delete_document')]); + + return $this->redirect($this->generateUrl('regle_open', ['id' => $id])); + } + + // Then, checking that the rule has no document open or in error + $docErrorOpen = $this->getDoctrine() + ->getManager() + ->getRepository(Document::class) + ->findOneBy([ + 'rule' => $id, + 'deleted' => 0, + 'globalStatus' => ['Open', 'Error'], + ] + ); + // Return to the view detail of the rule if we found a document open or in error + if (!empty($docErrorOpen)) { + $session->set('error', [$this->translator->trans('error.rule.delete_document_error_open')]); + + return $this->redirect($this->generateUrl('regle_open', ['id' => $id])); + } + + // Checking if the rule is linked to an other one + $ruleRelationships = $this->getDoctrine() + ->getManager() + ->getRepository(RuleRelationShip::class) + ->findBy(['fieldId' => $id]); + + // Return to the view detail of the rule if a rule relate to this one exists and is not deleted + if (!empty($ruleRelationships)) { + foreach ($ruleRelationships as $ruleRelationship) { + if (empty($ruleRelationship->getDeleted())) { + $session->set('error', [$this->translator->trans('error.rule.delete_relationship_exists').$ruleRelationship->getRule()]); + + return $this->redirect($this->generateUrl('regle_open', ['id' => $id])); + } + } + } + + if ($this->getUser()->isAdmin()) { + $list_fields_sql = + ['id' => $id, + ]; + } else { + $list_fields_sql = + [ + 'id' => $id, + 'createdBy' => $this->getUser()->getId(), + ]; + } + // Detecte si la session est le support --------- + + if (isset($id)) { + // Récupère la règle en fonction de son id + $rule = $this->getDoctrine() + ->getManager() + ->getRepository(Rule::class) + ->findBy($list_fields_sql); + + $rule = $rule[0]; + + // si je supprime une règle qui ne m'appartient pas alors redirection + if (empty($rule)) { + return $this->redirect($this->generateUrl('regle_list')); + } + + // On récupére l'EntityManager + $this->getInstanceBdd(); + + // Remove the rule relationships + $ruleRelationships = $this->getDoctrine() + ->getManager() + ->getRepository(RuleRelationShip::class) + ->findBy(['rule' => $id]); + + if (!empty($ruleRelationships)) { + foreach ($ruleRelationships as $ruleRelationship) { + $ruleRelationship->setDeleted(1); + $this->entityManager->persist($ruleRelationship); + } + } + + $rule->setDeleted(1); + $rule->setActive(0); + $this->entityManager->persist($rule); + $this->entityManager->flush(); + + return $this->redirect($this->generateUrl('regle_list')); + } + return $this->redirect($this->generateUrl('regle_list')); + } + + /** + * @Route("/displayflux/{id}", name="regle_displayflux") + */ + public function displayFlux($id): RedirectResponse + { + $rule = $this->getDoctrine() + ->getManager() + ->getRepository(Rule::class) + ->findOneBy([ + 'id' => $id, + ] + ); + + $this->sessionService->setFluxFilterWhere(['rule' => $rule->getName()]); + $this->sessionService->setFluxFilterRuleName($rule->getName()); + + return $this->redirect($this->generateUrl('document_list_page')); + } + + /** + * @Route("/duplic_rule/{id}", name="duplic_rule") + */ + public function duplicateRule($id, Request $request, TranslatorInterface $translator) + { + try { + $rule = $this->getDoctrine() + ->getManager() + ->getRepository(Rule::class) + ->findOneBy([ + 'id' => $id, + ]); + $newRule = new Rule(); + $connectorSource = $rule->getconnectorSource()->getName(); + $connectorTarget = $rule->getconnectorTarget()->getName(); + + //solution id current rule + $currentRuleSolutionSourceId = $rule->getConnectorSource()->getSolution()->getId(); + $currentRuleSolutionTargetId = $rule->getConnectorTarget()->getSolution()->getId(); + + // Create the form + $form = $this->createForm(DuplicateRuleFormType::class, $newRule, ['solution' => ['source' => $currentRuleSolutionSourceId, 'target' => $currentRuleSolutionTargetId]]); + + $form->handleRequest($request); + //Sends new data if validated and submit + if ($form->isSubmitted() && $form->isValid()) { + $now = new \DateTime(); + $user = $this->getUser(); + $newRuleName = $form->get('name')->getData(); + $newRuleSource = $form->get('connectorSource')->getData(); + $newRuleTarget = $form->get('connectorTarget')->getData(); + + if (isset($newRuleName)) { + // Set the rule header data + $newRule->setName($newRuleName) + ->setCreatedBy($user) + ->setConnectorSource($newRuleSource) + ->setConnectorTarget($newRuleTarget) + ->setDateCreated($now) + ->setDateModified($now) + ->setModifiedBy($user) + ->setModuleSource($rule->getModuleSource()) + ->setModuleTarget($rule->getModuleTarget()) + ->setDeleted(false) + ->setActive(false) + ->setNameSlug($newRuleName); + + // Set the rule parameters + foreach ($rule->getParams() as $param) { + $paramNewRule = new RuleParam(); + $paramNewRule->setRule($newRule); + $paramNewRule->setName($param->getName()); + $paramNewRule->setValue($param->getValue()); + $this->entityManager->persist($paramNewRule); + } + + // Set the rule relationships + foreach ($rule->getRelationsShip() as $relationship) { + $relationsShipNewRule = new RuleRelationShip(); + $relationsShipNewRule->setRule($newRule); + $relationsShipNewRule->setFieldNameSource($relationship->getFieldNameSource()); + $relationsShipNewRule->setFieldNameTarget($relationship->getFieldNameTarget()); + $relationsShipNewRule->setFieldId($relationship->getFieldId()); + $relationsShipNewRule->setParent($relationship->getParent()); + $relationsShipNewRule->setDeleted(0); + $relationsShipNewRule->setErrorEmpty($relationship->getErrorEmpty()); + $relationsShipNewRule->setErrorMissing($relationship->getErrorMissing()); + $this->entityManager->persist($relationsShipNewRule); + } + + // Set the rule filters + foreach ($rule->getFilters() as $filter) { + $filterNewRule = new RuleFilter(); + $filterNewRule->setRule($newRule); + $filterNewRule->setTarget($filter->getTarget()); + $filterNewRule->setType($filter->getType()); + $filterNewRule->setValue($filter->getValue()); + $this->entityManager->persist($filterNewRule); + } + + // Set the rule fields + foreach ($rule->getFields() as $field) { + $fieldNewRule = new RuleField(); + $fieldNewRule->setRule($newRule); + $fieldNewRule->setTarget($field->getTarget()); + $fieldNewRule->setSource($field->getSource()); + $fieldNewRule->setFormula($field->getFormula()); + $this->entityManager->persist($fieldNewRule); + } + + // Save the new rule in the database + $this->entityManager->persist($newRule); + $this->entityManager->flush(); + $success = $translator->trans('duplicate_rule.success_duplicate'); + $this->addFlash('success', $success); + } + + return $this->redirect($this->generateURL('regle_list')); + } + + return $this->render('Rule/create/duplic.html.twig', [ + 'rule' => $rule, + 'connectorSourceUser' => $connectorSource, + 'connectorTarget' => $connectorTarget, + 'form' => $form->createView(), + ]); + } catch (Exception $e) { + return new JsonResponse($e->getMessage()); + } + } + + /** + * ACTIVE UNE REGLE. + * + * @Route("/update/{id}", name="regle_update") + */ + public function ruleUpdActive($id) + { + try { + // On récupére l'EntityManager + $this->getInstanceBdd(); + + $rule = $this->getDoctrine() + ->getManager() + ->getRepository(Rule::class) + ->find($id); + + if ($rule->getActive()) { + $r = 0; + $rule->setActive($r); + } else { + $r = 1; + $rule->setActive($r); + } + + $this->entityManager->persist($rule); + $this->entityManager->flush(); + + return new Response($r); + } catch (Exception $e) { + return new JsonResponse($e->getMessage()); + } + } + + /** + * Executer une règle manuellement. + * + * @Route("/exec/{id}", name="regle_exec") + */ + public function ruleExecAction($id, $documentId = null) + { + // We added a doc id to this function to carry the document ids in case of a run rule by doc id. + // In every case except our mass run by doc id, $documentId will be null so we keep the usual behaviour of the function untouched. + try { + $this->ruleManager->setRule($id); + + if ($documentId !== null) { + $this->ruleManager->actionRule('runRuleByDocId', 'execrunRuleByDocId', $documentId); + + } elseif ('ALL' == $id) { + $this->ruleManager->actionRule('ALL'); + + return $this->redirect($this->generateUrl('regle_list')); + } elseif ('ERROR' == $id) { + $this->ruleManager->actionRule('ERROR'); + + return $this->redirect($this->generateUrl('regle_list')); + } + if ($documentId === null){ + + $this->ruleManager->actionRule('runMyddlewareJob'); + } + + return $this->redirect($this->generateURL('regle_open', ['id' => $id])); + } catch (Exception $e) { + $this->logger->error($e->getMessage().' '.$e->getFile().' '.$e->getLine()); + + return $this->redirect($this->generateUrl('regle_list')); + } + } + + /** + * CANCEL ALL TRANSFERS FOR ONE RULE. + * + * @Route("/view/cancel/documents/{id}", name="rule_cancel_all_transfers") + */ + public function cancelRuleTransfers($id) + { + try { + $this->ruleManager->setRule($id); + $result = $this->ruleManager->actionRule('runMyddlewareJob', 'cancelDocumentJob'); + } catch (\Exception $e) { + return $e->getMessage(); + } + + return $this->redirect($this->generateUrl('regle_open', ['id' => $id])); + } + + /** + * DELETE ALL TRANSFERS FOR ONE RULE. + * + * @Route("/view/delete/documents/{id}", name="rule_delete_all_transfers") + */ + public function deleteRuleTransfers($id) + { + try { + $this->ruleManager->setRule($id); + $result = $this->ruleManager->actionRule('runMyddlewareJob', 'deleteDocumentJob'); + } catch (\Exception $e) { + return $e->getMessage(); + } + + return $this->redirect($this->generateUrl('regle_open', ['id' => $id])); + } + + /** + * MODIFIE LES PARAMETRES D'UNE REGLE. + * @return JsonResponse|Response + * @Route("/update/params/{id}", name="path_fiche_params_update") + */ + public function ruleUpdParams($id) + { + try { + // On récupére l'EntityManager + $this->getInstanceBdd(); + if (isset($_POST['params']) && is_array($_POST['params'])) { + foreach ($_POST['params'] as $p) { + $param = $this->entityManager->getRepository(RuleParam::class) + ->findOneBy([ + 'rule' => $id, + 'name' => $p['name'], + ] + ); + + // In a few case, the parameter could not exist, in this case we create it + if (empty($param)) { + // Create rule entity + $rule = $this->getDoctrine() + ->getManager() + ->getRepository(Rule::class) + ->findOneBy([ + 'id' => $id, + ] + ); + $param = new RuleParam(); + $param->setRule($rule); + $param->setName($p['name']); + $param->setValue($p['value']); + } else { + // Save param modification in the audit table + if ($p['value'] != $param->getValue()) { + $paramAudit = new RuleParamAudit(); + $paramAudit->setRuleParamId($p['id']); + $paramAudit->setDateModified(new \DateTime()); + $paramAudit->setBefore($param->getValue()); + $paramAudit->setAfter($p['value']); + $paramAudit->setByUser($this->getUser()->getId()); + $this->entityManager->persist($paramAudit); + } + $param->setValue($p['value']); + } + $this->entityManager->persist($param); + $this->entityManager->flush(); + } + } + + return new Response(1); + } catch (Exception $e) { + return new JsonResponse($e->getMessage()); + } + } + + /** + * SIMULE LA LECTURE POUR RETOURNER LE NOMBRE DE TRANSFERS POTENTIELS. + * @Route("/simule/{id}", name="path_fiche_params_simulate") + */ + public function ruleSimulateTransfers(Rule $rule): Response + { + try { + // On récupére l'EntityManager + $this->getInstanceBdd(); + + // Get the rule reference + $param['date_ref'] = $rule->getParamByName('datereference')->getValue(); + // Get the rule limit + $limitParam = $rule->getParamByName('limit'); + if ($limitParam) { + $param['limit'] = $limitParam->getValue(); + } + // Get the other rule params + $connectorParams = $rule->getParams(); + foreach ($connectorParams as $connectorParam) { + $param['ruleParams'][$connectorParam->getName()] = $connectorParam->getValue(); + } + + $param['fields'] = []; + // Extraction des champs sources + foreach ($rule->getFields() as $ruleField) { + // It could be several fields in a source when there is a formula + $sources = explode(';', $ruleField->getSource()); + foreach ($sources as $source) { + $param['fields'][] = $source; + } + } + + // Module source + $param['module'] = (string) $rule->getModuleSource(); + + // Solution source + $solution_source_nom = $rule->getConnectorSource()->getSolution()->getName(); + + // Connector source ------------------- + $connectorParamsSource = $this->getDoctrine() + ->getManager() + ->getRepository(ConnectorParam::class) + ->findBy(['connector' => $rule->getConnectorSource()]); + $connectorSource['solution'] = $rule->getConnectorSource()->getSolution()->getName(); + foreach ($connectorParamsSource as $connector) { + $connectorSource[$connector->getName()] = $connector->getValue(); + } + + $solution_source = $this->solutionManager->get($solution_source_nom); + $solution_source->login($connectorSource); + + // Rule Mode + $param['ruleParams']['mode'] = $rule->getParamByName('mode')->getValue(); + + if (empty($param['ruleParams']['mode'])) { + $param['ruleParams']['mode'] = '0'; + } + + $param['offset'] = '0'; + $param['call_type'] = 'read'; + $result = $solution_source->readData($param); + if (!empty($result['error'])) { + throw new Exception('Reading Issue: '.$result['error']); + } + if (isset($result['count'])) { + return new Response($result['count']); + } + + return new Response(0); + } catch (Exception $e) { + $errorMessage = $e->getMessage().' '.$e->getFile().' '.$e->getLine(); + + return new Response(json_encode(['error' => $errorMessage])); + } + } + + /** + * MODE EDITION D'UNE REGLE. + * + * @Route("/edit/{id}", name="regle_edit") + */ + public function ruleEditAction(Request $request, Rule $rule): RedirectResponse + { + $session = $request->getSession(); + + try { + // First, checking that the rule has no document open or in error + $docErrorOpen = $this->getDoctrine() + ->getManager() + ->getRepository(Document::class) + ->findOneBy([ + 'rule' => $rule, + 'deleted' => 0, + 'globalStatus' => ['Open', 'Error'], + ]); + // Return to the view detail fo the rule if we found a document open or in error + if (!empty($docErrorOpen)) { + if ($this->authorizationChecker->isGranted('ROLE_SUPER_ADMIN')) { + $session->set('warning', [$this->translator->trans('error.rule.edit_document_error_open_admin')]); + } else { + $session->set('error', [$this->translator->trans('error.rule.edit_document_error_open')]); + + return $this->redirect($this->generateUrl('regle_open', ['id' => $rule->getId()])); + } + } + + $this->sessionService->setParamRuleLastKey($rule->getId()); + $key = $this->sessionService->getParamRuleLastKey(); + //-- + // si une session existe alors on la supprime + if ($this->sessionService->isParamRuleExist($key)) { + $this->sessionService->removeParamRule($key); + } + + // préparation des sessions + if (!empty($rule->getDeleted())) { + $session->set('error', [$this->translator->trans('error.rule.edit_rule_deleted')]); + + return $this->redirect($this->generateUrl('regle_open', ['id' => $rule->getId()])); + } + + // composition des sessions + $this->sessionService->setParamRuleNameValid($key, true); + $this->sessionService->setParamRuleName($key, $rule->getName()); + $this->sessionService->setParamRuleConnectorSourceId($key, (string) $rule->getConnectorSource()->getId()); + $this->sessionService->setParamRuleConnectorCibleId($key, (string) $rule->getConnectorTarget()->getId()); + $this->sessionService->setParamRuleLastId($key, $rule->getId()); + + // Connector source ------------------- + $connectorParamsSource = $this->getDoctrine() + ->getManager() + ->getRepository(ConnectorParam::class) + ->findByConnector([$rule->getConnectorSource()]); + + $this->sessionService->setParamRuleSourceSolution($key, $rule->getConnectorSource()->getSolution()->getName()); + + foreach ($connectorParamsSource as $connector) { + $this->sessionService->setParamRuleSourceConnector($key, $connector->getName(), $connector->getValue()); + } + // Connector source ------------------- + + // Connector target ------------------- + $connectorParamsTarget = $this->getDoctrine() + ->getManager() + ->getRepository(ConnectorParam::class) + ->findByConnector([$rule->getConnectorTarget()]); + + $this->sessionService->setParamRuleCibleSolution($key, $rule->getConnectorTarget()->getSolution()->getName()); + + foreach ($connectorParamsTarget as $connector) { + $this->sessionService->setParamRuleCibleConnector($key, $connector->getName(), $connector->getValue()); + } + // Connector target ------------------- + + // Paramètre d'une règle + if ($rule->getParams()->count()) { + $params = []; + foreach ($rule->getParams() as $ruleParamsObj) { + $params[] = [ + 'name' => $ruleParamsObj->getName(), + 'value' => $ruleParamsObj->getValue(), + ]; + } + $this->sessionService->setParamRuleReloadParams($key, $params); + } + + // Modules -- + $this->sessionService->setParamRuleSourceModule($key, $rule->getModuleSource()); + $this->sessionService->setParamRuleCibleModule($key, $rule->getModuletarget()); + // Modules -- + + // reload --------------- + $ruleFields = $rule->getFields(); + + // get_modules_fields en source pour avoir l'association fieldid / libellé (ticket 548) + $solution_source_nom = $this->sessionService->getParamRuleSourceSolution($key); + $solution_source = $this->solutionManager->get($solution_source_nom); + + $login = $solution_source->login($this->decrypt_params($this->sessionService->getParamRuleSource($key))); + if (empty($solution_source->connexion_valide)) { + throw new Exception('failed to login to the source application .'.(!empty($login['error']) ? $login['error'] : '')); + } + + // SOURCE ----- Récupère la liste des champs source + // O récupère le module de la règle + $sourceModule = $rule->getModuleSource(); + $sourceFieldsInfo = $solution_source->get_module_fields($sourceModule); + + // Champs et formules d'une règle + if ($ruleFields) { + $fields = array(); + foreach ($ruleFields as $ruleFieldsObj) { + $array = [ + 'target' => $ruleFieldsObj->getTarget(), + 'source' => [], + 'formula' => $ruleFieldsObj->getFormula(), + ]; + $fields_source = explode(';', $ruleFieldsObj->getSource()); + + if (!empty($fields_source)) { + foreach ($fields_source as $field_source) { + if ('my_value' == $field_source) { + $array['source'][$field_source] = 'my_value'; + } elseif (isset($sourceFieldsInfo[$field_source])) { + $array['source'][$field_source] = $sourceFieldsInfo[$field_source]['label']; + } else { + if (!empty($sourceFieldsInfo)) { + foreach ($sourceFieldsInfo as $multiModule) { + if (isset($multiModule[$field_source])) { + $array['source'][$field_source] = $multiModule[$field_source]['label']; + } + } + } + } + if (!isset($array['source'][$field_source])) { + throw new Exception('failed to get the field '.$field_source); + } + } + $fields[] = $array; + } + } + $this->sessionService->setParamRuleReloadFields($key, $fields); + } + + // Relations d'une règle + if ($rule->getRelationsShip()->count()) { + foreach ($rule->getRelationsShip() as $ruleRelationShipsObj) { + $relate[] = [ + 'source' => $ruleRelationShipsObj->getFieldNameSource(), + 'target' => $ruleRelationShipsObj->getFieldNameTarget(), + 'errorMissing' => (!empty($ruleRelationShipsObj->getErrorMissing()) ? '1' : '0'), + 'errorEmpty' => (!empty($ruleRelationShipsObj->getErrorEmpty()) ? '1' : '0'), + 'id' => $ruleRelationShipsObj->getFieldId(), + 'parent' => $ruleRelationShipsObj->getParent(), + ]; + } + $this->sessionService->setParamRuleReloadRelate($key, $relate); + } + + // Filter + if ($rule->getFilters()->count()) { + foreach ($rule->getFilters() as $ruleFilters) { + $filter[] = [ + 'target' => $ruleFilters->getTarget(), + 'type' => $ruleFilters->getType(), + 'value' => $ruleFilters->getValue(), + ]; + } + } + + $this->sessionService->setParamRuleReloadFilter($key, ((isset($filter)) ? $filter : [])); + + // reload --------------- + return $this->redirect($this->generateUrl('regle_stepthree', ['id' => $rule->getId()])); + } catch (Exception $e) { + $this->sessionService->setCreateRuleError($key, $this->translator->trans('error.rule.update').' '.$e->getMessage().' '.$e->getFile().' '.$e->getLine()); + $session->set('error', [$this->translator->trans('error.rule.update').' '.$e->getMessage().' '.$e->getFile().' '.$e->getLine()]); + + return $this->redirect($this->generateUrl('regle_open', ['id' => $rule->getId()])); + } + } + + /** + * FICHE D'UNE REGLE. + * @Route("/view/{id}", name="regle_open") + * @throws Exception + */ + public function ruleOpenAction($id): Response + { + if ($this->getUser()->isAdmin()) { + $list_fields_sql = ['id' => $id]; + } else { + $list_fields_sql = + ['id' => $id, + 'createdBy' => $this->getUser()->getId(), + ]; + } + // Detecte si la session est le support --------- + + // Infos de la regle + /** @var Rule $rule */ + $rule = $this->entityManager->getRepository(Rule::class)->findOneBy($list_fields_sql); + if (!$rule) { + throw $this->createNotFoundException('Couldn\'t find specified rule in database'); + } + + // Liste des relations + $rule_relationships = $rule->getRelationsShip(); + $solution_cible_nom = $rule->getConnectorTarget()->getSolution()->getName(); + $solution_cible = $this->solutionManager->get($solution_cible_nom); + $moduleCible = (string) $rule->getModuleTarget(); + + $tab_rs = []; + $i = 0; + foreach ($rule_relationships as $r) { + $tab_rs[$i]['getFieldId'] = $r->getFieldId(); + $tab_rs[$i]['getFieldNameSource'] = $r->getFieldNameSource(); + $tab_rs[$i]['getFieldNameTarget'] = $r->getFieldNameTarget(); + $tab_rs[$i]['getErrorMissing'] = $r->getErrorMissing(); + $tab_rs[$i]['getErrorEmpty'] = $r->getErrorEmpty(); + $tab_rs[$i]['getParent'] = $r->getParent(); + + $ruleTmp = $this->entityManager->getRepository(Rule::class) + ->findOneBy([ + 'id' => $r->getFieldId(), + ] + ); + + $tab_rs[$i]['getName'] = $ruleTmp->getName(); + ++$i; + } + + // Infos connecteurs & solutions + $ruleRepo = $this->entityManager->getRepository(Rule::class); + $connector = $ruleRepo->infosConnectorByRule($rule->getId()); + + // Changement de référence pour certaines solutions + $autorization_source = $connector[0]['solution_source']; + $autorization_module_trans = mb_strtolower($rule->getModuleSource()); + + $Params = $rule->getParams(); + $Fields = $rule->getFields(); + $Filters = $rule->getFilters(); + $ruleParam = RuleManager::getFieldsParamView(); + $params_suite = []; + if ($Params) { + foreach ($Params as $field) { + $standardField = false; + foreach ($ruleParam as $index => $value) { + // Init the parameter in case it doesn't exist in the database yet + if (!isset($ruleParam[$index]['id_bdd'])) { + $ruleParam[$index]['id_bdd'] = ''; + $ruleParam[$index]['value_bdd'] = ''; + } + if ($field->getName() == $value['name']) { + $ruleParam[$index]['id_bdd'] = $field->getId(); + $ruleParam[$index]['value_bdd'] = $field->getValue(); + $standardField = true; + break; + } + } + if (!$standardField) { + if ('mode' == $field->getName()) { + // We send the translation of the mode to the view + switch ($field->getValue()) { + case '0': + $params_suite['mode'] = $this->tools->getTranslation(['create_rule', 'step3', 'syncdata', 'create_modify']); + break; + case 'C': + $params_suite['mode'] = $this->tools->getTranslation(['create_rule', 'step3', 'syncdata', 'create_only']); + break; + case 'S': + $params_suite['mode'] = $this->tools->getTranslation(['create_rule', 'step3', 'syncdata', 'search_only']); + break; + default: + $params_suite['mode'] = $field->getValue(); + } + } elseif ('bidirectional' == $field->getName()) { + if (!empty($field->getValue())) { + $ruleBidirectional = $this->entityManager->getRepository(Rule::class) + ->findOneBy([ + 'id' => $field->getValue(), + ] + ); + // Send the name and the id of the opposite rule to the view + $params_suite['bidirectional'] = $field->getValue(); + $params_suite['bidirectionalName'] = $ruleBidirectional->getName(); + } + } else { + $params_suite['customParams'][] = ['name' => $field->getName(), 'value' => $field->getValue()]; + } + } + } + } + + // get the workflows of the rule, if there are none then set hasWorkflows to false. If there is at least one then set it to true. to get the workflows we use the entity manager and filter by the rule id + $hasWorkflows = $this->entityManager->getRepository(Workflow::class)->findBy(['rule' => $rule->getId(), 'deleted' => 0]) ? true : false; + + if ($hasWorkflows) { + $workflows = $this->entityManager->getRepository(Workflow::class)->findBy(['rule' => $rule->getId(), 'deleted' => 0]); + + } else { + $workflows = []; + } + + return $this->render('Rule/edit/fiche.html.twig', [ + 'rule' => $rule, + 'connector' => $connector[0], + 'fields' => $Fields, + 'relate' => $tab_rs, + 'parentRelationships' => $solution_cible->allowParentRelationship($moduleCible), + 'params' => $ruleParam, + 'filters' => $Filters, + 'params_suite' => $params_suite, + 'id' => $id, + 'hasWorkflows' => $hasWorkflows, + 'workflows' => $workflows, + ] + ); + } + + /** + * @return JsonResponse|Response + * CREATION - STEP ONE - CONNEXION : jQuery ajax. + * @Route("/inputs", name="regle_inputs", methods={"POST"}, options={"expose"=true}) + */ + public function ruleInputs(Request $request) + { + try { + $ruleKey = $this->sessionService->getParamRuleLastKey(); + // Retourne la liste des inputs pour la connexion + if (1 == $request->request->get('mod')) { + if (is_string($request->request->get('solution')) && is_string($request->request->get('parent'))) { + if (preg_match("#[\w]#", $request->request->get('solution')) && preg_match("#[\w]#", $request->request->get('parent'))) { + $classe = strtolower($request->request->get('solution')); + $parent = $request->request->get('parent'); + $solution = $this->entityManager->getRepository(Solution::class)->findOneBy(['name' => $classe]); + $connector = new Connector(); + $connector->setSolution($solution); + $fieldsLogin = []; + if (null !== $connector->getSolution()) { + $fieldsLogin = $this->solutionManager->get($connector->getSolution()->getName())->getFieldsLogin(); + } + $form = $this->createForm(ConnectorType::class, $connector, [ + 'action' => $this->generateUrl('regle_connector_insert'), + 'attr' => [ + 'fieldsLogin' => $fieldsLogin, + 'secret' => $this->getParameter('secret'), + ], + ]); + + return $this->render('Ajax/result_liste_inputs.html.twig', [ + 'form' => $form->createView(), + 'parent' => $parent, + ] + ); + } + } + } // Vérifie si la connexion peut se faire ou non + elseif (2 == $request->request->get('mod') || 3 == $request->request->get('mod')) { + // Connector + if (2 == $request->request->get('mod')) { + if (preg_match("#[\w]#", $request->request->get('champs')) && preg_match("#[\w]#", $request->request->get('parent')) && preg_match("#[\w]#", $request->request->get('solution'))) { + $classe = strtolower($request->request->get('solution')); + $solution = $this->solutionManager->get($classe); + + // établi un tableau params + $champs = explode(';', $request->request->get('champs')); + + if ($champs) { + foreach ($champs as $key) { + $input = explode('::', $key); + if (!empty($input[0])) { + if (!empty($input[1]) || is_numeric($input[1])) { + $param[$input[0]] = trim($input[1]); + $this->sessionService->setParamConnectorParentType($request->request->get('parent'), $input[0], trim($input[1])); + } + } + } + } + $this->sessionService->setParamConnectorParentType($request->request->get('parent'), 'solution', $classe); + + // Vérification du nombre de champs + if (isset($param) && (count($param) == count($solution->getFieldsLogin()))) { + $result = $solution->login($param); + $r = $solution->connexion_valide; + + if (!empty($r)) { + return new JsonResponse(['success' => true]); // Connexion valide + } + $this->sessionService->removeParamRule($ruleKey); + + return new JsonResponse(['success' => false, 'message' => $this->translator->trans($result['error'])]); // Erreur de connexion + } + + return new JsonResponse(['success' => false, 'message' => $this->translator->trans('Connection error')]); // Erreur pas le même nombre de champs + } else { + // Either parent, solution or champs is empty (from AJAX request sent in verif(div_clock) function in regle.js) + return new JsonResponse(['success' => false, 'message' => $this->translator->trans('create_connector.form_error')]); + } + } // Rule + elseif (3 == $request->request->get('mod')) { + // 0 : solution + // 1 : id connector + $params = explode('_', $request->request->get('solution')); + + // Deux params obligatoires + if (2 == count($params) && intval($params[1]) && is_string($params[0])) { + $this->sessionService->removeParamParentRule($ruleKey, $request->request->get('parent')); + $classe = strtolower($params[0]); + $solution = $this->solutionManager->get($classe); + + $connector = $this->getDoctrine() + ->getManager() + ->getRepository(Connector::class) + ->find($params[1]); + + $connector_params = $this->getDoctrine() + ->getManager() + ->getRepository(ConnectorParam::class) + ->findBy(['connector' => $connector]); + + if ($connector_params) { + foreach ($connector_params as $key) { + $this->sessionService->setParamConnectorParentType($request->request->get('parent'), $key->getName(), $key->getValue()); + } + } + + $this->sessionService->setParamRuleName($ruleKey, $request->request->get('name')); + + // Affectation id connector + $this->sessionService->setParamRuleConnectorParent($ruleKey, $request->request->get('parent'), $params[1]); + + $result = $solution->login($this->decrypt_params($this->sessionService->getParamParentRule($ruleKey, $request->request->get('parent')))); + $this->sessionService->setParamRuleParentName($ruleKey, $request->request->get('parent'), 'solution', $classe); + + $r = $solution->connexion_valide; + if (!empty($r)) { + return new JsonResponse(['success' => true]); // Connexion valide + } + + return new JsonResponse(['success' => false, 'message' => $this->translator->trans($result['error'])]); // Erreur de connexion + + exit; + + return $this->render('Ajax/result_connexion.html.twig', [] + ); + } + + return new JsonResponse(['success' => false, 'message' => $this->translator->trans('Connection error')]); + } + } else { + $this->logger->error("Error: Not Found Exception"); + throw $this->createNotFoundException('Error'); + } + return new JsonResponse(['success' => false]); + } catch (Exception $e) { + return new JsonResponse(['success' => false, 'message' => $e->getMessage().' '.$e->getLine().' '.$e->getFile()]); + } + } + + /** + * CREATION - STEP ONE - VERIF ALIAS RULE. + * + * @Route("/inputs/name_unique/", name="regle_inputs_name_unique", methods={"POST"}, options={"expose"=true}) + */ + public function ruleNameUniq(Request $request): JsonResponse + { + $key = $this->sessionService->getParamRuleLastKey(); + + if ('POST' == $request->getMethod()) { + $this->getInstanceBdd(); + + // Cherche si la règle existe en fonction de son nom + $rule = $this->entityManager->getRepository(Rule::class) + ->findOneBy([ + 'name' => $request->request->get('name'), + ] + ); + + // 0 existe pas 1 existe + if (null == $rule) { + $existRule = 0; + $this->sessionService->setParamRuleNameValid($key, true); + $this->sessionService->setParamRuleName($key, $request->request->get('name')); + } else { + $existRule = 1; + $this->sessionService->setParamRuleNameValid($key, false); + } + + return new JsonResponse($existRule); + } + throw $this->createNotFoundException('Error'); + } + + /** + * CREATION - STEP TWO - CHOIX MODULES. + * + * @return RedirectResponse|Response + * + * @Route("/create/step2/", name="regle_steptwo", methods={"POST"}) + */ + public function ruleStepTwo(Request $request) + { + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + // si le nom de la règle est inferieur à 3 caractères : + if (!isset($myddlewareSession['param']['rule']['source']['solution']) || strlen($myddlewareSession['param']['rule']['rulename']) < 3 || false == $myddlewareSession['param']['rule']['rulename_valide']) { + $myddlewareSession['error']['create_rule'] = $this->translator->trans('error.rule.valid'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + try { + // ---------------- SOURCE ---------------------------- + $solution_source_nom = $myddlewareSession['param']['rule']['source']['solution']; + $solution_source = $this->solutionManager->get($solution_source_nom); + + $sourceConnection = $solution_source->login($this->decrypt_params($myddlewareSession['param']['rule']['source'])); + + if (empty($solution_source->connexion_valide)) { + $myddlewareSession['error']['create_rule'] = $this->translator->trans('error.rule.source_module_connect').' '.(!empty($sourceConnection['error']) ? $sourceConnection['error'] : 'No message returned by '.$solution_source_nom); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + $liste_modules_source = ToolsManager::composeListHtml($solution_source->get_modules('source'), $this->translator->trans('create_rule.step2.choice_module')); + if (!$liste_modules_source) { + $myddlewareSession['error']['create_rule'] = $this->translator->trans('error.rule.source_module_load_list'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + // ---------------- /SOURCE ---------------------------- + } catch (Exception $e) { + $myddlewareSession['error']['create_rule'] = $this->translator->trans('error.rule.source_module_all'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + try { + // ---------------- TARGET ---------------------------- + // Si la solution est la même que la précèdente on récupère les infos + if ($myddlewareSession['param']['rule']['source']['solution'] == $myddlewareSession['param']['rule']['cible']['solution']) { + $solution_cible = $solution_source; + $solution_cible_nom = $solution_source_nom; + } else { + $solution_cible_nom = $myddlewareSession['param']['rule']['cible']['solution']; + $solution_cible = $this->solutionManager->get($solution_cible_nom); + } + $targetConnection = $solution_cible->login($this->decrypt_params($myddlewareSession['param']['rule']['cible'])); + + if (empty($solution_cible->connexion_valide)) { + $myddlewareSession['error']['create_rule'] = $this->translator->trans('error.rule.target_module_connect').' '.(!empty($targetConnection['error']) ? $targetConnection['error'] : 'No message returned by '.$solution_cible_nom); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + $liste_modules_cible = ToolsManager::composeListHtml($solution_cible->get_modules('target'), $this->translator->trans('create_rule.step2.choice_module')); + + if (!$liste_modules_cible) { + $myddlewareSession['error']['create_rule'] = $this->translator->trans('error.rule.target_module_load_list'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + // ---------------- /TARGET ---------------------------- + } catch (Exception $e) { + $myddlewareSession['error']['create_rule'] = $this->translator->trans('error.rule.target_module_all'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + return $this->render('Rule/create/step2.html.twig', [ + 'solution_source' => $solution_source_nom, + 'solution_cible' => $solution_cible_nom, + 'liste_modules_source' => $liste_modules_source, + 'liste_modules_cible' => $liste_modules_cible, + 'params' => $myddlewareSession['param']['rule'], + ] + ); + } + + /** + * CREATION - STEP THREE - SIMULATION DES DONNEES. + * + * @Route("/create/step3/simulation/", name="regle_simulation", methods={"POST"}) + */ + public function ruleSimulation(Request $request): Response + { + $ruleKey = $this->sessionService->getParamRuleLastKey(); + + if ('POST' == $request->getMethod() && $this->sessionService->isParamRuleExist($ruleKey)) { + // retourne un tableau prêt à l'emploi + $target = $this->createListeParamsRule( + $request->request->get('champs'), // Fields + $request->request->get('formules'), // Formula + '' // Params flux + ); + + $solution_source_nom = $this->sessionService->getParamRuleSourceSolution($ruleKey); + $solution_source = $this->solutionManager->get($solution_source_nom); + $solution_source->login($this->sessionService->getParamRuleSource($ruleKey)); + $tab_simulation = []; + $sourcesfields = []; + + // récupération de tous les champs + if (isset($target['fields']) && count($target['fields']) > 0) { + foreach ($target['fields'] as $f) { + if (isset($f)) { + foreach ($f as $name_fields_target => $k) { + if (isset($k['champs'])) { + $sourcesfields = array_merge($k['champs'], $sourcesfields); + } + } + } + } + } else { + // ici pour les règles avec des relations uniquement + return $this->render('Rule/create/onglets/simulation_tab.html.twig', [ + 'before' => [], // source + 'after' => [], // target + 'data_source' => false, + ] + ); + } + + // Add rule param if exist (the aren't exist in rule creation) + $ruleParams = []; + $ruleParamsResult = $this->getDoctrine()->getManager()->getRepository(RuleParam::class)->findBy(['rule' => $ruleKey]); + if (!empty($ruleParamsResult)) { + foreach ($ruleParamsResult as $ruleParamsObj) { + $ruleParams[$ruleParamsObj->getName()] = $ruleParamsObj->getValue(); + } + } + // The mode is empty when we create the rule, so we set a default value + if (empty($ruleParams['ruleParams']['mode'])) { + $ruleParams['mode'] = '0'; + } + + // Get result from AJAX request in regle.js + $form = $request->request->all(); + if (isset($form['query'])) { + $this->simulationQueryField = $form['query']; + } + + // Avoid sending query on specific record ID if the user didn't actually input something + if (empty($this->simulationQueryField)) { + // Get source data + $source = $solution_source->readData([ + 'module' => $this->sessionService->getParamRuleSourceModule($ruleKey), + 'fields' => $sourcesfields, + 'date_ref' => '1970-01-01 00:00:00', // date_ref is required for some application like Prestashop + 'limit' => 1, + 'ruleParams' => $ruleParams, + 'call_type' => 'simulation', + ]); + } else { + // Get source data + $source = $solution_source->readData([ + 'module' => $this->sessionService->getParamRuleSourceModule($ruleKey), + 'fields' => $sourcesfields, + 'date_ref' => '1970-01-01 00:00:00', // date_ref is required for some application like Prestashop + 'limit' => 1, + 'ruleParams' => $ruleParams, + 'query' => [(!empty($ruleParams['fieldId']) ? $ruleParams['fieldId'] : 'id') => $this->simulationQueryField], + 'call_type' => 'simulation', + ]); + + // In case of wrong record ID input from user + if (!empty($source['error'])) { + return $this->render('Rule/create/onglets/invalidrecord.html.twig'); + } + } + + $before = []; + $after = []; + if (!empty($source['values'])) { + $record = current($source['values']); // Remove a dimension to the array because we need only one record + if (!empty($record)) { + foreach ($target['fields'] as $f) { + foreach ($f as $name_fields_target => $k) { + $r['after'] = []; + // Préparation pour transformation + $name = trim($name_fields_target); + $target_fields = [ + 'target_field_name' => $name, + 'source_field_name' => ((isset($k['champs'])) ? implode(';', $k['champs']) : 'my_value'), + 'formula' => ((isset($k['formule'][0]) ? $k['formule'][0] : '')), + 'related_rule' => '', + ]; + + // Add rule id for simulation purpose when using lookup function + $this->documentManager->setRuleId($ruleKey); + // Transformation + $response = $this->documentManager->getTransformValue($record, $target_fields); + if (!isset($response['message'])) { + $r['after'][$name_fields_target] = $this->documentManager->getTransformValue($record, $target_fields); + } + // If error during transformation, we send back the error + if ( + null == $r['after'][$name_fields_target] + and !empty($response['message']) + ) { + $r['after'][$name_fields_target] = $response['message']; + } + + $k['fields'] = []; + if (empty($k['champs'])) { + $k['fields']['Formula'] = ((isset($k['formule'][0]) ? $k['formule'][0] : '')); + } else { + foreach ($k['champs'] as $fields) { + // Fields couldn't be return. For example Magento return only field not empty + if (!empty($record[$fields])) { + $k['fields'][$fields] = $record[$fields]; + } else { + $k['fields'][$fields] = ''; + } + } + } + + $tab_simulation[] = [ + 'after' => $r['after'], + 'before' => $k['fields'], + ]; + } + } + $after = []; + // Préparation pour tableau template + foreach ($tab_simulation as $key => $value) { + foreach ($value as $k => $v) { + if ('before' == $k) { + $before[] = $v; + } else { + foreach ($v as $key => $value) { + // if value does not contains the substring "mdw_no_send_field" + if (strpos($value, 'mdw_no_send_field') === false) { + $after[] = $v; + } + } + } + } + } + } + } + + return $this->render('Rule/create/onglets/simulation_tab.html.twig', [ + 'before' => $before, // source + 'after' => $after, // target + 'data_source' => (!empty($record) ? true : false), + 'params' => $this->sessionService->getParamRule($ruleKey), + 'simulationQueryField' => $this->simulationQueryField, + ] + ); + } + throw $this->createNotFoundException('Error'); + } + + /** + * CREATION - STEP THREE - CHOIX DES CHAMPS - MAPPING DES CHAMPS. + * + * @return RedirectResponse|Response + * + * @Route("/create/step3/{id}", name="regle_stepthree", defaults={"id"=0}) + */ + public function ruleStepThree(Request $request) + { + $this->getInstanceBdd(); + $ruleKey = $request->get('id'); + + // Test que l'ordre des étapes + if (!$this->sessionService->isParamRuleExist($ruleKey)) { + $this->sessionService->setCreateRuleError($ruleKey, $this->translator->trans('error.rule.order')); + + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + // Contrôle si la nouvelle règle peut-être valide + if ($this->sessionService->isRuleNameLessThanXCharacters($ruleKey, 3)) { + $this->sessionService->setCreateRuleError($ruleKey, $this->translator->trans('error.rule.valid')); + + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + try { + // ---- Mode update ---- + if (!$this->sessionService->isParamRuleSourceModuleExist($ruleKey) && !$this->sessionService->isParamRuleCibleModuleExist($ruleKey)) { + // RELOAD : Chargement des données d'une règle en édition + $this->sessionService->setParamRuleSourceModule($ruleKey, $request->request->get('source_module')); + $this->sessionService->setParamRuleCibleModule($ruleKey, $request->request->get('cible_module')); + } + // ---- Mode update ---- + + // Get all data from the target solution first + $solution_cible = $this->solutionManager->get($this->sessionService->getParamRuleCibleSolution($ruleKey)); + + // TARGET ------------------------------------------------------------------ + // We retriev first all data from the target application and the from the source application + // We can't do both solution in the same time because we could have a bug when these 2 solutions are the same (service are shared by default in Symfony) + $targetConnection = $solution_cible->login($this->decrypt_params($this->sessionService->getParamRuleCible($ruleKey))); + + if (false == $solution_cible->connexion_valide) { + $this->sessionService->setCreateRuleError($ruleKey, $this->translator->trans('error.rule.target_module_connect').' '.(!empty($targetConnection['error']) ? $targetConnection['error'] : 'No message returned by '.$this->sessionService->getParamRuleCibleSolution($ruleKey))); + + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + if ($request->request->get('cible_module')) { + $module['cible'] = $request->request->get('cible_module'); // mode create <<---- + } else { + $module['cible'] = $this->sessionService->getParamRuleCibleModule($ruleKey); // mode update <<---- + } + + // Récupère la liste des paramètres cible + $ruleParamsTarget = $solution_cible->getFieldsParamUpd('target', $module['cible']); + + // Récupère la liste des champs cible + $ruleFieldsTarget = $solution_cible->get_module_fields($module['cible'], 'target'); + + // Récupération de tous les modes de règle possibles pour la cible et la source + $targetMode = $solution_cible->getRuleMode($module['cible'], 'target'); + + $fieldMappingAdd = $solution_cible->getFieldMappingAdd($module['cible']); + + $allowParentRelationship = $solution_cible->allowParentRelationship($this->sessionService->getParamRuleCibleModule($ruleKey)); + + // Champs pour éviter les doublons + $fieldsDuplicateTarget = $solution_cible->getFieldsDuplicate($this->sessionService->getParamRuleCibleModule($ruleKey)); + + // SOURCE ------------------------------------------------------------------ + // Connexion au service de la solution source + $solution_source = $this->solutionManager->get($this->sessionService->getParamRuleSourceSolution($ruleKey)); + $sourceConnection = $solution_source->login($this->decrypt_params($this->sessionService->getParamRuleSource($ruleKey))); + + // Contrôle que la connexion est valide + if (false == $solution_source->connexion_valide) { + $this->sessionService->setCreateRuleError($ruleKey, $this->translator->trans('error.rule.source_module_connect').' '.(!empty($sourceConnection['error']) ? $sourceConnection['error'] : 'No message returned by '.$this->sessionService->getParamRuleSourceSolution($ruleKey))); + + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + $modules = $solution_source->get_modules('source'); + if ($request->request->get('source_module')) { + $module['source'] = $request->request->get('source_module'); // mode create <<---- + } else { + $module['source'] = $this->sessionService->getParamRuleSourceModule($ruleKey); // mode update <<---- + } + + // Met en mémoire la façon de traiter la date de référence + $this->sessionService->setParamRuleSourceDateReference($ruleKey, $solution_source->referenceIsDate($module['source'])); + + // Ajoute des champs source pour la validation + $ruleParamsSource = $solution_source->getFieldsParamUpd('source', $module['source']); + + // Add parameters to be able to read rules linked + $param['connectorSourceId'] = $this->sessionService->getParamRuleConnectorSourceId($ruleKey); + $param['connectorTargetId'] = $this->sessionService->getParamRuleConnectorCibleId($ruleKey); + $param['ruleName'] = $this->sessionService->getParamRuleName($ruleKey); + + // Récupère la liste des champs source + $ruleFieldsSource = $solution_source->get_module_fields($module['source'], 'source', $param); + + if ($ruleFieldsSource) { + $this->sessionService->setParamRuleSourceFields($ruleKey, $ruleFieldsSource); + + // Erreur champs, pas de données sources (Exemple: GotoWebinar) + + if ($this->sessionService->isParamRuleSourceFieldsErrorExist($ruleKey) && null != $this->sessionService->getParamRuleSourceFieldsError($ruleKey)) { + $this->sessionService->setCreateRuleError($ruleKey, $this->sessionService->getParamRuleSourceFieldsError($ruleKey)); + + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + foreach ($ruleFieldsSource as $t => $k) { + $source['table'][$module['source']][$t] = $k['label']; + } + // Tri des champs sans tenir compte de la casse + ksort($source['table'][$module['source']], SORT_NATURAL | SORT_FLAG_CASE); + } + + // SOURCE ----- Récupère la liste des champs source + + // Type de synchronisation + // Récupération de tous les modes de règle possibles pour la source + $sourceMode = $solution_source->getRuleMode($module['source'], 'source'); + // Si la target à le type S (search) alors on l'ajoute à la source pour qu'il soit préservé par l'intersection + if (array_key_exists('S', $targetMode)) { + $sourceMode['S'] = 'search_only'; + } + $intersectMode = array_intersect($targetMode, $sourceMode); + // Si jamais l'intersection venait à être vide (ce qui ne devrait jamais arriver) on met par défaut le mode CREATE + if (empty($intersectMode)) { + $intersectMode['C'] = 'create_only'; + } + // If duplicate field exist for the target solution, we allow search rule type + if (!empty($fieldsDuplicateTarget)) { + $intersectMode['S'] = 'search_only'; + } + $this->sessionService->setParamRuleCibleMode($ruleKey, $intersectMode); + + // Préparation des champs cible + $cible['table'] = []; + + if ($ruleFieldsTarget) { + $this->sessionService->setParamRuleTargetFields($ruleKey, $ruleFieldsTarget); + + $tmp = $ruleFieldsTarget; + + $normal = []; + $required = []; + foreach ($ruleFieldsTarget as $t => $k) { + if (isset($k['required']) && true == $k['required']) { + $required[] = $t; + } else { + $normal[] = $t; + } + } + + asort($required); + asort($normal); + + $alpha = array_merge($required, $normal); + $field_target_alpha = []; + foreach ($alpha as $name_fields) { + $field_target_alpha[$name_fields] = $tmp[$name_fields]['required']; + } + + $cible['table'][$module['cible']] = $field_target_alpha; + } else { + $cible['table'][$module['cible']] = []; // rev 1.1.1 + } + + // On ajoute des champs personnalisés à notre mapping + if ($fieldMappingAdd && $this->sessionService->isParamRuleLastVersionIdExist($ruleKey)) { + $ruleFields = $this->getDoctrine() + ->getManager() + ->getRepository(RuleField::class) + ->findBy(['rule' => $this->sessionService->getParamRuleLastId($ruleKey)]); + + $tmp = []; + foreach ($ruleFields as $fields) { + $tmp[$fields->getTarget()] = 0; + } + + foreach ($cible['table'][$module['cible']] as $k => $value) { + $tmp[$k] = $value; + } + + $cible['table'][$module['cible']] = $tmp; + + ksort($cible['table'][$module['cible']]); + } + + // ------------------- TARGET + $lst_relation_target = []; + $lst_relation_target_alpha = []; + if ($ruleFieldsTarget) { + foreach ($ruleFieldsTarget as $key => $value) { + // Only relationship fields + if (empty($value['relate'])) { + continue; + } + $lst_relation_target[] = $key; + } + + asort($lst_relation_target); + + foreach ($lst_relation_target as $name_relate) { + $lst_relation_target_alpha[$name_relate]['required'] = (!empty($ruleFieldsTarget[$name_relate]['required_relationship']) ? 1 : 0); + $lst_relation_target_alpha[$name_relate]['name'] = $name_relate; + $lst_relation_target_alpha[$name_relate]['label'] = (!empty($ruleFieldsTarget[$name_relate]['label']) ? $ruleFieldsTarget[$name_relate]['label'] : $name_relate); + } + } + + // ------------------- SOURCE + // Liste des relations SOURCE + $lst_relation_source = []; + $lst_relation_source_alpha = []; + $choice_source = []; + if ($ruleFieldsSource) { + foreach ($ruleFieldsSource as $key => $value) { + if (empty($value['relate'])) { + continue; // We keep only relationship fields + } + $lst_relation_source[] = $key; + } + + asort($lst_relation_source); + foreach ($lst_relation_source as $name_relate) { + $lst_relation_source_alpha[$name_relate]['label'] = $ruleFieldsSource[$name_relate]['label']; + } + + // préparation de la liste en html + foreach ($lst_relation_source_alpha as $key => $value) { + $choice_source[$key] = (!empty($value['label']) ? $value['label'] : $key); + } + } + + if (!isset($source['table'])) { + $source['table'][$this->sessionService->getParamRuleSourceModule($ruleKey)] = []; + } + + // -- Relation + // Rule list with the same connectors (both directions) to get the relate ones + $ruleRepo = $this->getDoctrine()->getManager()->getRepository(Rule::class); + $ruleListRelation = $ruleRepo->createQueryBuilder('r') + ->select('r.id, r.name, r.moduleSource') + ->where('( + r.connectorSource= ?1 + AND r.connectorTarget= ?2 + AND r.name != ?3 + AND r.deleted = 0 + ) + OR ( + r.connectorTarget= ?1 + AND r.connectorSource= ?2 + AND r.name != ?3 + AND r.deleted = 0 + )') + ->setParameter(1, (int) $this->sessionService->getParamRuleConnectorSourceId($ruleKey)) + ->setParameter(2, (int) $this->sessionService->getParamRuleConnectorCibleId($ruleKey)) + ->setParameter(3, $this->sessionService->getParamRuleName($ruleKey)) + ->getQuery() + ->getResult(); + + //Verson 1.1.1 : possibilité d'ajouter des relations custom en fonction du module source + $ruleListRelationSourceCustom = $solution_source->get_rule_custom_relationship($this->sessionService->getParamRuleSourceModule($ruleKey), 'source'); + if (!empty($ruleListRelationSourceCustom)) { + $ruleListRelation = array_merge($ruleListRelation, $ruleListRelationSourceCustom); + } + + $choice = []; + $control = []; + + foreach ($ruleListRelation as $key => $value) { + if (!in_array($value['name'], $control)) { + $choice[$value['id']] = $value['name']; + $control[] = $value['name']; + } + } + asort($choice); + + // ------------------- Parent relation + // Search if we can send document merged with the target solution + $lstParentFields = []; + if ($allowParentRelationship) { + if (!empty($ruleListRelation)) { + // We get all relate fields from every source module + foreach ($ruleListRelation as $ruleRelation) { + // Get the relate fields from the source module of related rules + $ruleFieldsSource = $solution_source->get_module_fields($ruleRelation['moduleSource'], 'source'); + if (!empty($ruleFieldsSource)) { + foreach ($ruleFieldsSource as $key => $sourceRelateField) { + if (empty($sourceRelateField['relate'])) { + continue; // Only relationship fields + } + $lstParentFields[$key] = $ruleRelation['name'].' - '.$sourceRelateField['label']; + } + } + } + // We allow to search by the id of the module + $lstParentFields['Myddleware_element_id'] = $this->translator->trans('create_rule.step3.relation.record_id'); + } + // No parent relation if no rule to link or no fields related + if (empty($lstParentFields)) { + $allowParentRelationship = false; + } + } + + // On récupére l'EntityManager + $this->getInstanceBdd(); + + // Récupère toutes les catégories + $lstCategory = $this->entityManager->getRepository(FuncCat::class) + ->findAll(); + + // Récupère toutes les functions + $lstFunctions = $this->entityManager->getRepository(Functions::class) + ->findAll(); + + // Les filtres + $lst_filter = [ + $this->translator->trans('filter.content') => 'content', + $this->translator->trans('filter.notcontent') => 'notcontent', + $this->translator->trans('filter.begin') => 'begin', + $this->translator->trans('filter.end') => 'end', + $this->translator->trans('filter.gt') => 'gt', + $this->translator->trans('filter.lt') => 'lt', + $this->translator->trans('filter.equal') => 'equal', + $this->translator->trans('filter.different') => 'different', + $this->translator->trans('filter.gteq') => 'gteq', + $this->translator->trans('filter.lteq') => 'lteq', + $this->translator->trans('filter.in') => 'in', + $this->translator->trans('filter.notin') => 'notin', + ]; + + + //Behavior filters + $lst_errorMissing = [ + '0' => $this->translator->trans('create_rule.step3.relation.no'), + '1' => $this->translator->trans('create_rule.step3.relation.yes'), + ]; + + $lst_errorEmpty = [ + '0' => $this->translator->trans('create_rule.step3.relation.no'), + '1' => $this->translator->trans('create_rule.step3.relation.yes'), + ]; + // paramètres de la règle + $rule_params = array_merge($ruleParamsSource, $ruleParamsTarget); + + // récupération des champs de type liste -------------------------------------------------- + + // -----[ SOURCE ]----- + if ($this->sessionService->isParamRuleSourceFieldsExist($ruleKey)) { + foreach ($this->sessionService->getParamRuleSourceFields($ruleKey) as $field => $fields_tab) { + if (array_key_exists('option', $fields_tab)) { + $formule_list['source'][$field] = $fields_tab; + } + } + } + + if (isset($formule_list['source']) && count($formule_list['source']) > 0) { + foreach ($formule_list['source'] as $field => $fields_tab) { + foreach ($fields_tab['option'] as $field_name => $fields) { + if (!empty($fields)) { + $formule_list['source'][$field]['option'][$field_name] = $field_name.' ( '.$fields.' )'; + } + } + } + } + + $html_list_source = ''; + if (isset($formule_list['source'])) { + foreach ($formule_list['source'] as $field => $fields_tab) { + $html_list_source .= ''; + $html_list_source .= ToolsManager::composeListHtml($fields_tab['option']); + $html_list_source .= ''; + } + } + + // -----[ TARGET ]----- + if ($this->sessionService->isParamRuleTargetFieldsExist($ruleKey)) { + foreach ($this->sessionService->getParamRuleTargetFields($ruleKey) as $field => $fields_tab) { + if (array_key_exists('option', $fields_tab)) { + $formule_list['target'][$field] = $fields_tab; + } + } + } + + if (isset($formule_list['target']) && count($formule_list['target']) > 0) { + foreach ($formule_list['target'] as $field => $fields_tab) { + foreach ($fields_tab['option'] as $field_name => $fields) { + if (!empty($fields)) { + $formule_list['target'][$field]['option'][$field_name] = $field_name.' ( '.$fields.' )'; + } + } + } + } + + $html_list_target = ''; + if (isset($formule_list['target'])) { + foreach ($formule_list['target'] as $field => $fields_tab) { + $html_list_target .= ''; + $html_list_target .= ToolsManager::composeListHtml($fields_tab['option']); + $html_list_target .= ''; + } + } + + // récupération des champs de type liste -------------------------------------------------- + + // Type de synchronisation de données rev 1.06 -------------------------- + if ($this->sessionService->isParamRuleCibleModuleExist($ruleKey)) { + $mode_translate = []; + foreach ($this->sessionService->getParamRuleCibleMode($ruleKey) as $key => $value) { + $mode_translate[$key] = $this->translator->trans('create_rule.step3.syncdata.'.$value); + } + + $mode = + [ + [ + 'id' => 'mode', + 'name' => 'mode', + 'required' => false, + 'type' => 'option', + 'label' => $this->translator->trans('create_rule.step3.syncdata.label'), + 'option' => $mode_translate, + ], + ]; + + $rule_params = array_merge($rule_params, $mode); + } + // Type de synchronisation de données rev 1.06 -------------------------- + + // rev 1.07 -------------------------- + $bidirectional_params['connector']['source'] = $this->sessionService->getParamRuleConnectorSourceId($ruleKey); + $bidirectional_params['connector']['cible'] = $this->sessionService->getParamRuleConnectorCibleId($ruleKey); + $bidirectional_params['module']['source'] = $module['source']; + $bidirectional_params['module']['cible'] = $module['cible']; + + $bidirectional = RuleManager::getBidirectionalRules($this->connection, $bidirectional_params, $solution_source, $solution_cible); + if ($bidirectional) { + $rule_params = array_merge($rule_params, $bidirectional); + } + + // Add param to allow deletion (need source and target application ok to enable deletion) + if ( + true == $solution_source->getReadDeletion($module['source']) + and true == $solution_cible->getSendDeletion($module['cible']) + ) { + $deletion = [ + [ + 'id' => 'deletion', + 'name' => 'deletion', + 'required' => false, + 'type' => 'option', + 'label' => $this->translator->trans('create_rule.step3.deletion.label'), + 'option' => [0 => '', 1 => $this->translator->trans('create_rule.step3.deletion.yes')], + ], + ]; + $rule_params = array_merge($rule_params, $deletion); + } else { + // If the deletion is disable (database in source OK but target application non OK), we remove the deletion list field of database connector + $keyDeletionField = array_search('deletionField', array_column($rule_params, 'id')); + if (!empty($keyDeletionField)) { + unset($rule_params[$keyDeletionField]); + } + } + + // get the array of array $ruleFieldsSource and for each value, get the label only and add it to the array $listOfSourceFieldsLabels + $listOfSourceFieldsLabels = [ + 'Source Fields' => [], + 'Target Fields' => [], + 'Relation Fields' => [], + ]; + foreach ($ruleFieldsSource as $key => $value) { + $listOfSourceFieldsLabels['Source Fields'][$key] = $value['label']; + } + + // get the array of array $ruleFieldsTarget and for each value, get the label only and add it to the array $listOfSourceFieldsLabels + foreach ($ruleFieldsTarget as $key => $value) { + $listOfSourceFieldsLabels['Target Fields'][$key] = $value['label']; + } + + foreach ($lst_relation_source_alpha as $key => $value) { + $listOfSourceFieldsLabels['Relation Fields'][$key] = $value['label']; + } + + + $form_all_related_fields = $this->createForm(RelationFilterType::class, null, [ + 'field_choices' => $listOfSourceFieldsLabels, + 'another_field_choices' => $lst_filter + ]); + + $filters = $this->entityManager->getRepository(RuleFilter::class) + ->findBy(['rule' => $ruleKey]); + + // we want to make a request that fetches all the rule names and ids, so we can display them in the form + $ruleRepo = $this->getDoctrine()->getManager()->getRepository(Rule::class); + $ruleListRelation = $ruleRepo->createQueryBuilder('r') + ->select('r.id, r.name, r.moduleSource') + ->where('( + r.connectorSource= ?1 + AND r.connectorTarget= ?2 + AND r.name != ?3 + AND r.deleted = 0 + ) + OR ( + r.connectorTarget= ?1 + AND r.connectorSource= ?2 + AND r.name != ?3 + AND r.deleted = 0 + )') + ->setParameter(1, (int) $this->sessionService->getParamRuleConnectorSourceId($ruleKey)) + ->setParameter(2, (int) $this->sessionService->getParamRuleConnectorCibleId($ruleKey)) + ->setParameter(3, $this->sessionService->getParamRuleName($ruleKey)) + ->getQuery() + ->getResult(); + + // from the result ruleListRelation we create an array with the rule name as the key and the rule id as the value + $ruleListRelation = array_reduce($ruleListRelation, function ($carry, $item) { + $carry[$item['name']] = $item['id']; + return $carry; + }, []); + + $html_list_rules = ''; + if (!empty($ruleListRelation)) { + foreach ($ruleListRelation as $ruleName => $ruleId) { + $html_list_rules .= ''; + } + } + + // rev 1.07 -------------------------- + $result = [ + 'filters' => $filters, + 'source' => $source['table'], + 'cible' => $cible['table'], + 'rule_params' => $rule_params, + 'lst_relation_target' => $lst_relation_target_alpha, + 'lst_relation_source' => $choice_source, + 'lst_rule' => $choice, + 'lst_category' => $lstCategory, + 'lst_functions' => $lstFunctions, + 'lst_filter' => $lst_filter, + 'form_all_related_fields' => $form_all_related_fields->createView(), + 'lst_errorMissing' => $lst_errorMissing, + 'lst_errorEmpty' => $lst_errorEmpty, + 'params' => $this->sessionService->getParamRule($ruleKey), + 'duplicate_target' => $fieldsDuplicateTarget, + 'opt_target' => $html_list_target, + 'opt_source' => $html_list_source, + 'html_list_rules' => $html_list_rules, + 'fieldMappingAddListType' => $fieldMappingAdd, + 'parentRelationships' => $allowParentRelationship, + 'lst_parent_fields' => $lstParentFields, + 'regleId' => $ruleKey, + 'simulationQueryField' => $this->simulationQueryField, + ]; + + foreach ($result['source'] as $module => $fields) { + foreach ($fields as $fieldNameEncoded => $fieldValue) { + // Decode the field name + $fieldNameDecoded = urldecode($fieldNameEncoded); + + // Optionally, clean up the field name by removing or replacing unwanted characters + $fieldNameCleaned = $fieldNameDecoded; // Adjust as needed + + // Clean the field value + // Example: Trim whitespace and remove special characters + // Adjust the cleaning logic as per your requirements + $fieldValueCleaned = trim($fieldValue); // Trimming whitespace + // For more aggressive cleaning, uncomment and adjust the following line + // $fieldValueCleaned = preg_replace('/[^\x20-\x7E]/', '', $fieldValueCleaned); + + // Check if any cleaning was necessary for the field name + if ($fieldNameCleaned !== $fieldNameEncoded || $fieldValue !== $fieldValueCleaned) { + // Remove the old key + unset($result['source'][$module][$fieldNameEncoded]); + + // Add the cleaned field name with its cleaned value + $result['source'][$module][$fieldNameCleaned] = $fieldValueCleaned; + } + } + } + + $result = $this->tools->beforeRuleEditViewRender($result); + + // Formatage des listes déroulantes : + $result['lst_relation_source'] = ToolsManager::composeListHtml($result['lst_relation_source'], $this->translator->trans('create_rule.step3.relation.fields')); + $result['lst_parent_fields'] = ToolsManager::composeListHtml($result['lst_parent_fields'], ' '); + $result['lst_rule'] = ToolsManager::composeListHtml($result['lst_rule'], $this->translator->trans('create_rule.step3.relation.fields')); + $result['lst_filter'] = ToolsManager::composeListHtml($result['lst_filter'], $this->translator->trans('create_rule.step3.relation.fields')); + $result['lst_errorMissing'] = ToolsManager::composeListHtml($result['lst_errorMissing'], '', '1'); + $result['lst_errorEmpty'] = ToolsManager::composeListHtml($result['lst_errorEmpty'], '', '0'); + + return $this->render('Rule/create/step3.html.twig', $result); + + // ---------------- + } catch (Exception $e) { + $this->logger->error($e->getMessage().' ('.$e->getFile().' line '.$e->getLine()); + $this->sessionService->setCreateRuleError($ruleKey, $this->translator->trans('error.rule.mapping').' : '.$e->getMessage().' ('.$e->getFile().' line '.$e->getLine().')'); + + // return $this->redirect($this->generateUrl('regle_stepone_animation')); + // exit; + dump($e->getMessage().' ('.$e->getFile().' line '.$e->getLine()); + } + } + + /** + * Indique des informations concernant le champ envoyé en paramètre. + * @Route("/info/{type}/{field}/", name="path_info_field", methods={"GET"}) + * @Route("/info", name="path_info_field_not_param") + */ + public function infoField(Request $request, $field, $type): Response + { + $session = $request->getSession(); + $myddlewareSession = $session->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->set('myddlewareSession', $myddlewareSession); + if (isset($field) && !empty($field) && isset($myddlewareSession['param']['rule']) && 'my_value' != $field) { + if (isset($myddlewareSession['param']['rule'][0][$type]['fields'][$field])) { + return $this->render('Rule/create/onglets/info.html.twig', [ + 'field' => $myddlewareSession['param']['rule'][0][$type]['fields'][$field], + 'name' => htmlentities(trim($field)), + ] + ); + // SuiteCRM connector uses this structure instead + } elseif (isset($myddlewareSession['param']['rule']['key'])) { + $ruleKey = $myddlewareSession['param']['rule']['key']; + + return $this->render('Rule/create/onglets/info.html.twig', [ + 'field' => $myddlewareSession['param']['rule'][$ruleKey][$type]['fields'][$field], + 'name' => htmlentities(trim($field)), + ] + ); + } else { + // Possibilité de Mutlimodules + foreach ($myddlewareSession['param']['rule'][0][$type]['fields'] as $subModule) { // Ce foreach fonctionnera toujours + if (isset($subModule[$field])) { // On teste si ça existe pour éviter une erreur PHP éventuelle + return $this->render('Rule/create/onglets/info.html.twig', [ + 'field' => $subModule[$field], + 'name' => htmlentities(trim($field)), + ] + ); + } + } + } + // On retourne vide si on l'a pas trouvé précédemment + return $this->render('Rule/create/onglets/info.html.twig', [ + 'field' => '', + ] + ); + } + + return $this->render('Rule/create/onglets/info.html.twig', [ + 'field' => '', + ] + ); + } + + /** + * CREATION - STEP THREE - VERIF DES FORMULES. + * + * @Route("/create/step3/formula/", name="regle_formula", methods={"POST"}) + */ + public function ruleVerifFormula(Request $request): JsonResponse + { + if ('POST' == $request->getMethod()) { + // Mise en place des variables + $this->formuleManager->init($request->request->get('formula')); // mise en place de la règle dans la classe + $this->formuleManager->generateFormule(); // Genère la nouvelle formule à la forme PhP + + return new JsonResponse($this->formuleManager->parse['error']); + } + throw $this->createNotFoundException('Error'); + } + + /** + * CREATION - STEP THREE - Validation du formulaire. + * + * @Route("/create/step3/validation/", name="regle_validation", methods={"POST"}) + */ + public function ruleValidation(Request $request): JsonResponse + { + // On récupére l'EntityManager + $this->getInstanceBdd(); + $this->entityManager->getConnection()->beginTransaction(); + try { + /* + * get rule id in the params in regle.js. In creation, regleId = 0 + */ + if (!empty($request->request->get('params'))) { + foreach ($request->request->get('params') as $searchRuleId) { + if ('regleId' == $searchRuleId['name']) { + $ruleKey = $searchRuleId['value']; + break; + } + } + } + + // retourne un tableau prêt à l'emploi + $tab_new_rule = $this->createListeParamsRule( + $request->request->get('champs'), // Fields + $request->request->get('formules'), // Formula + $request->request->get('params') // Params + ); + unset($tab_new_rule['params']['regleId']); // delete id regle for gestion session + + // fields relate + if (!empty($request->request->get('duplicate'))) { + // fix : Put the duplicate fields values in the old $tab_new_rule array + $duplicateArray = implode(';', $request->request->get('duplicate')); + $tab_new_rule['params']['rule']['duplicate_fields'] = $duplicateArray; + $this->sessionService->setParamParentRule($ruleKey, 'duplicate_fields', $duplicateArray); + } + // si le nom de la règle est inferieur à 3 caractères : + if (strlen($this->sessionService->getParamRuleName($ruleKey)) < 3 || false == $this->sessionService->getParamRuleNameValid($ruleKey)) { + return new JsonResponse(0); + } + + //------------ Create rule + $connector_source = $this->getDoctrine() + ->getManager() + ->getRepository(Connector::class) + ->find($this->sessionService->getParamRuleConnectorSourceId($ruleKey)); + + $connector_target = $this->getDoctrine() + ->getManager() + ->getRepository(Connector::class) + ->find($this->sessionService->getParamRuleConnectorCibleId($ruleKey)); + + $param = RuleManager::getFieldsParamDefault(); + + // Get the id of the rule if we edit a rule + // Generate Rule object (create a new one or instanciate the existing one + if (!$this->sessionService->isParamRuleLastVersionIdEmpty($ruleKey)) { + $oneRule = $this->entityManager->getRepository(Rule::class)->find($this->sessionService->getParamRuleLastId($ruleKey)); + $oneRule->setDateModified(new \DateTime()); + $oneRule->setModifiedBy($this->getUser()); + } else { + $oneRule = new Rule(); + $oneRule->setConnectorSource($connector_source); + $oneRule->setConnectorTarget($connector_target); + $oneRule->setDateCreated(new \DateTime()); + $oneRule->setDateModified(new \DateTime()); + $oneRule->setCreatedBy($this->getUser()); + $oneRule->setModifiedBy($this->getUser()); + $oneRule->setModuleSource($this->sessionService->getParamRuleSourceModule($ruleKey)); + $oneRule->setModuleTarget($this->sessionService->getParamRuleCibleModule($ruleKey)); + $oneRule->setDeleted(0); + $oneRule->setActive((int) $param['active']); + $oneRule->setName($this->sessionService->getParamRuleName($ruleKey)); + } + $this->entityManager->persist($oneRule); + // On fait le flush pour obtenir le nameSlug. En cas de problème on fait un remove dans le catch + $this->entityManager->flush(); + $this->sessionService->setRuleId($ruleKey, $oneRule->getId()); + $nameRule = $oneRule->getNameSlug(); + + // BEFORE SAVE rev 1.08 ---------------------- + $relationshipsBeforeSave = $request->request->get('relations'); + $before_save = $this->ruleManager->beforeSave($this->solutionManager, + ['ruleName' => $nameRule, + 'RuleId' => $oneRule->getId(), + 'connector' => $this->sessionService->getParamParentRule($ruleKey, 'connector'), + 'content' => $tab_new_rule, + 'relationships' => $relationshipsBeforeSave, + 'module' => [ + 'source' => [ + 'solution' => $this->sessionService->getParamRuleSourceSolution($ruleKey), + 'name' => $this->sessionService->getParamRuleSourceModule($ruleKey), + ], + 'target' => [ + 'solution' => $this->sessionService->getParamRuleCibleSolution($ruleKey), + 'name' => $this->sessionService->getParamRuleCibleModule($ruleKey), + ], + ], + ] + ); + if (!$before_save['done']) { + throw new Exception($before_save['message']); + } + // Si le retour du beforeSave contient des paramètres alors on les ajoute à la règle avant sauvegarde + if (!empty($before_save['params'])) { + if (empty($tab_new_rule['params'])) { + $tab_new_rule['params'] = $before_save['params']; + } else { + $tab_new_rule['params'] = array_merge($tab_new_rule['params'], $before_save['params']); + } + } + + // Check if search rule then duplicate field shouldn't be empty + if ( + 'S' == $tab_new_rule['params']['mode'] + and empty($tab_new_rule['params']['rule']['duplicate_fields']) + ) { + throw new Exception($this->translator->trans('Failed to save the rule. If you choose to retrieve data with your rule, you have to select at least one duplicate field.')); + } + + // Edit mode + if (!$this->sessionService->isParamRuleLastVersionIdEmpty($ruleKey)) { + foreach ($oneRule->getFields() as $ruleField) { + $this->entityManager->remove($ruleField); + $this->entityManager->flush(); + } + + foreach ($oneRule->getRelationsShip() as $ruleRelationShip) { + $this->entityManager->remove($ruleRelationShip); + $this->entityManager->flush(); + } + + foreach ($oneRule->getFilters() as $ruleFilter) { + $this->entityManager->remove($ruleFilter); + $this->entityManager->flush(); + } + + // Rule Params + foreach ($oneRule->getParams() as $ruleParam) { + // Save reference date + if ('datereference' == $ruleParam->getName()) { + $date_reference = $ruleParam->getValue(); + } + if ('limit' === $ruleParam->getName()) { + $limit = $ruleParam->getValue(); + } + if (in_array($ruleParam->getName(), $this->tools->getRuleParam())) { + $this->entityManager->remove($ruleParam); + $this->entityManager->flush(); + } + } + } // Create mode + else { + if ($this->sessionService->isParamRuleSourceDateReference($ruleKey) && $this->sessionService->getParamRuleSourceDateReference($ruleKey)) { + $date_reference = date('Y-m-d 00:00:00'); + } else { + $date_reference = ''; + } + } + + //------------------------------- Create rule params ------------------- + if (isset($tab_new_rule['params']) || isset($param['RuleParam'])) { + if (!isset($tab_new_rule['params'])) { + $p = $param['RuleParam']; + } else { + $p = array_merge($param['RuleParam'], $tab_new_rule['params']); + } + + $bidirectional = ''; + foreach ($p as $key => $value) { + // Value could be empty, for bidirectional parameter for example (we don't test empty because mode could be equal 0) + if ('' == $value) { + continue; + } + $oneRuleParam = new RuleParam(); + $oneRuleParam->setRule($oneRule); + + // si tableau de doublon + if ('rule' == $key) { + $oneRuleParam->setName('duplicate_fields'); + $oneRuleParam->setValue($value['duplicate_fields']); + } else { + $oneRuleParam->setName($key); + if ('datereference' == $key) { + // date de référence change en fonction create ou update + $oneRuleParam->setValue($date_reference); + // Limit change according to create or update + } elseif ('limit' == $key) { + // Set default value 100 for limit + if (empty($limit)) { + $limit = 100; + } + $oneRuleParam->setValue($limit); + } else { + $oneRuleParam->setValue($value); + } + } + // Save the parameter + if ('bidirectional' == $key) { + $bidirectional = $value; + } + $this->entityManager->persist($oneRuleParam); + $this->entityManager->flush(); + } + + // If a bidirectional parameter exist, we check if the opposite one exists too + if (!empty($bidirectional)) { + // Update the opposite rule if birectional rule + $ruleParamBidirectionalOpposite = $this->entityManager->getRepository(RuleParam::class) + ->findOneBy([ + 'rule' => $bidirectional, + 'name' => 'bidirectional', + 'value' => $oneRule->getId(), + ]); + $bidirectionalRule = $this->ruleRepository->find($bidirectional); + // If the bidirectional parameter doesn't exist on the opposite rule we create it + if (empty($ruleParamBidirectionalOpposite)) { + $ruleParamBidirectionalOpposite = new RuleParam(); + $ruleParamBidirectionalOpposite->setRule($bidirectionalRule); + $ruleParamBidirectionalOpposite->setName('bidirectional'); + $ruleParamBidirectionalOpposite->setValue($oneRule->getId()); + $this->entityManager->persist($ruleParamBidirectionalOpposite); + } + } else { + // If no bidirectional parameter on the rule and if the bidirectional parametr exist on an opposite rule, we delete it + $ruleParamBidirectionalDelete = $this->entityManager->getRepository(RuleParam::class) + ->findOneBy([ + 'value' => $oneRule->getId(), + 'name' => 'bidirectional', + ]); + if (!empty($ruleParamBidirectionalDelete)) { + $this->entityManager->remove($ruleParamBidirectionalDelete); + $this->entityManager->flush(); + } + } + } + + //------------------------------- Create rule fields ------------------- + $debug = []; + + if (isset($tab_new_rule['fields'])) { + foreach ($tab_new_rule['fields']['name'] as $field_target => $c) { + $field_source = ''; + if (isset($c['champs'])) { + foreach ($c['champs'] as $name) { + $field_source .= $name.';'; + } + $field_source = trim($field_source, ';'); + } + + // Formula + $formule = ''; + if (isset($c['formule'])) { + foreach ($c['formule'] as $name) { + $formule .= $name.' '; + $debug[] = $name.' '; + } + } + + // Insert + $oneRuleField = new RuleField(); + $oneRuleField->setRule($oneRule); + $oneRuleField->setTarget(trim($field_target)); + $oneRuleField->setSource(((!empty($field_source)) ? $field_source : 'my_value')); + $oneRuleField->setFormula(((!empty($formule)) ? trim($formule) : null)); + $this->entityManager->persist($oneRuleField); + $this->entityManager->flush(); + } + } + + //------------------------------- RELATIONSHIPS ------------------- + $tabRelationShips = []; + if (!is_null($request->request->get('relations'))) { + foreach ($request->request->get('relations') as $rel) { + if ( + !empty($rel['rule']) + && !empty($rel['source']) + ) { + // Creation dans la table RelationShips + $oneRuleRelationShip = new RuleRelationShip(); + $oneRuleRelationShip->setRule($oneRule); + $oneRuleRelationShip->setFieldNameSource($rel['source']); + $oneRuleRelationShip->setFieldNameTarget($rel['target']); + // No error empty or missing for parent relationship, we set default values + if (!empty($rel['parent'])) { + $rel['errorEmpty'] = '0'; + $rel['errorMissing'] = '1'; + } + $oneRuleRelationShip->setErrorEmpty($rel['errorEmpty']); + $oneRuleRelationShip->setErrorMissing($rel['errorMissing']); + $oneRuleRelationShip->setFieldId($rel['rule']); + $oneRuleRelationShip->setParent($rel['parent']); + $oneRuleRelationShip->setDeleted(0); + // We don't create the field target if the relatiobnship is a parent one + // We only use this field to search in the source application, not to send the data to the target application. + if (empty($rel['parent'])) { + $tabRelationShips['target'][] = $rel['target']; + } + $tabRelationShips['source'][] = $rel['source']; + $this->entityManager->persist($oneRuleRelationShip); + $this->entityManager->flush(); + } + } + } + + // $form = $this->createForm(RelationFilterType::class); + // $form->handleRequest($request); + //------------------------------- RuleFilter ------------------------ + // $form->handleRequest($request); + //------------------------------- RuleFilter ------------------------ + $filters = $request->request->get('filter'); + + if (!empty($filters)) { + foreach ($filters as $filterData) { + // $filterData est un tableau contenant les valeurs des champs pour chaque élément de liste
  • + + // Accès aux valeurs des champs individuels + $fieldInput = $filterData['target']; + $anotherFieldInput = $filterData['filter']; + $textareaFieldInput = $filterData['value']; + + + // Maintenant, vous pouvez utiliser ces valeurs comme vous le souhaitez, par exemple, pour créer un objet RuleFilter + $oneRuleFilter = new RuleFilter(); + $oneRuleFilter->setTarget($fieldInput); + $oneRuleFilter->setRule($oneRule); + + $oneRuleFilter->setType($anotherFieldInput); + $oneRuleFilter->setValue($textareaFieldInput); + + // Enregistrez votre objet RuleFilter dans la base de données + $this->entityManager->persist($oneRuleFilter); + } + + $this->entityManager->flush(); + } + // $this->getDoctrine()->getManager()->flush(); + // } + // if (!empty($request->request->get('filter'))) { + // foreach ($request->request->get('filter') as $filter) { + // $oneRuleFilter = new RuleFilter(); + // $oneRuleFilter->setTarget($filter['target']); + // $oneRuleFilter->setRule($oneRule); + // $oneRuleFilter->setType($filter['filter']); + // $oneRuleFilter->setValue($filter['value']); + // $this->entityManager->persist($oneRuleFilter); + // $this->entityManager->flush(); + // } + // } + + // -------------------------------------------------------------------------------------------------- + // Order all rules + error_log(print_r($request->request->all(), true)); + $this->jobManager->orderRules(); + + // -------------------------------------------------------------------------------------------------- + // Create rule history in order to follow all modifications + // Encode every rule parameters + $ruledata = json_encode( + [ + 'ruleName' => $nameRule, + 'limit' => $limit, + 'datereference' => $date_reference, + 'content' => $tab_new_rule, + 'filters' => $request->request->get('filter'), + 'relationships' => $relationshipsBeforeSave, + ] + ); + // Save the rule audit + $oneRuleAudit = new RuleAudit(); + $oneRuleAudit->setRule($oneRule); + $oneRuleAudit->setDateCreated(new \DateTime()); + $oneRuleAudit->setData($ruledata); + $oneRuleAudit->setCreatedBy($this->getUser()); + $this->entityManager->persist($oneRuleAudit); + $this->entityManager->flush(); + + // notification + $solution_source = $this->solutionManager->get($this->sessionService->getParamRuleSourceSolution($ruleKey)); + $solution_source->setMessageCreateRule($this->sessionService->getParamRuleSourceModule($ruleKey)); + + $solution_target = $this->solutionManager->get($this->sessionService->getParamRuleCibleSolution($ruleKey)); + $solution_target->setMessageCreateRule($this->sessionService->getParamRuleCibleModule($ruleKey)); + // notification + + // -------------------------------------------------------------------------------------------------- + + // Détection règle root ou child rev 1.08 ---------------------- + // On réactualise les paramètres + $tab_new_rule['content']['params'] = $p; + $this->ruleManager->afterSave($this->solutionManager, + [ + 'ruleId' => $oneRule->getId(), + 'ruleName' => $nameRule, + 'oldRule' => ($this->sessionService->isParamRuleLastVersionIdEmpty($ruleKey)) ? '' : $this->sessionService->getParamRuleLastId($ruleKey), + 'datereference' => $date_reference, + 'limit' => $limit, + 'connector' => $this->sessionService->getParamParentRule($ruleKey, 'connector'), + 'content' => $tab_new_rule, + 'relationships' => $relationshipsBeforeSave, + 'module' => [ + 'source' => [ + 'solution' => $this->sessionService->getParamRuleSourceSolution($ruleKey), + 'name' => $this->sessionService->getParamRuleSourceModule($ruleKey), + ], + 'target' => [ + 'solution' => $this->sessionService->getParamRuleCibleSolution($ruleKey), + 'name' => $this->sessionService->getParamRuleCibleModule($ruleKey), + ], + ], + ] + ); + if ($this->sessionService->isParamRuleExist($ruleKey)) { + $this->sessionService->removeParamRule($ruleKey); + } + $this->entityManager->getConnection()->commit(); + + $rule_id = $oneRule->getId(); + $response = ['status' => 1, 'id' => $rule_id]; + //$response = 1; + } catch (Exception $e) { + $this->entityManager->getConnection()->rollBack(); + $this->logger->error('2;'.htmlentities($e->getMessage().' ('.$e->getFile().' line '.$e->getLine().')')); + $response = '2;'.htmlentities($e->getMessage().' ('.$e->getFile().' line '.$e->getLine().')'); + } + + $this->entityManager->close(); + + return new JsonResponse($response); + } + + /** + * TABLEAU DE BORD. + * + * @Route("/panel", name="regle_panel") + */ + public function panel(Request $request): Response + { + $language = $request->getLocale(); + + $this->getInstanceBdd(); + $solution = $this->entityManager->getRepository(Solution::class) + ->solutionActive(); + $lstArray = []; + if ($solution) { + foreach ($solution as $s) { + $lstArray[] = $s->getName(); + } + } + + /** @var User $user */ + $user = $this->getUser(); + $nbFlux = 0; + $listFlux = $this->documentRepository->countTypeDoc($user); + foreach ($listFlux as $field => $value) { + $nbFlux = $nbFlux + (int) $value['nb']; + } + + $countNbDocuments = $this->documentRepository->countNbDocuments(); + + return $this->render('Home/index.html.twig', [ + 'errorByRule' => $this->ruleRepository->errorByRule($user), + 'listJobDetail' => $this->jobRepository->listJobDetail(), + 'nbFlux' => $nbFlux, + 'solutions' => $lstArray, + 'locale' => $language, + 'countNbDocuments' => $countNbDocuments, + ] + ); + } + + /** + * ANIMATION + * No more submodule in Myddleware. We return a response 0 for the js (animation.js. + * + * @Route("/submodules", name="regle_submodules", methods={"POST"}) + */ + public function listSubModulesAction(): Response + { + return new Response(0); + } + + /** + * VALIDATION DE L'ANIMATION. + * + * @Route("/validation", name="regle_validation_animation") + */ + public function validationAnimationAction(Request $request): Response + { + $key = $this->sessionService->getParamRuleLastKey(); + + try { + $choiceSelect = $request->get('choice_select', null); + if (null != $choiceSelect) { + if ('module' == $choiceSelect) { + // si le nom de la règle est inferieur à 3 caractères : + if (empty($this->sessionService->getParamRuleSourceSolution($key)) || strlen($this->sessionService->getParamRuleName($key)) < 3) { + $this->sessionService->setParamRuleNameValid($key, false); + } else { + $this->sessionService->setParamRuleNameValid($key, true); + } + $this->sessionService->setParamRuleSourceModule($key, $request->get('module_source')); + $this->sessionService->setParamRuleCibleModule($key, $request->get('module_target')); + + return new Response('module'); + } elseif ('template' == $choiceSelect) { + // Rule creation with the template selected in parameter + $ruleName = $request->get('name'); + $templateName = $request->get('template'); + + $connectorSourceId = (int) $this->sessionService->getParamRuleConnectorSourceId($key); + $connectorTargetId = (int) $this->sessionService->getParamRuleConnectorCibleId($key); + /** @var User $user */ + $user = $this->getUser(); + try { + $this->template->convertTemplate($ruleName, $templateName, $connectorSourceId, $connectorTargetId, $user); + } catch (Exception $e) { + $this->logger->error($e->getMessage().' '.$e->getFile().' '.$e->getLine()); + + return new Response('error'); + } + // Sort the rules + $this->jobManager->orderRules(); + // We return to the list of rule even in case of error (session messages will be displyed in the UI)/: See animation.js function animConfirm + return new Response('template'); + } + + return new Response(0); + } + + return new Response(0); + } catch (Exception $e) { + $this->logger->error($e->getMessage().' '.$e->getFile().' '.$e->getLine()); + + return new Response($e->getMessage()); + } + } + + /** + * LISTE DES TEMPLATES. + * + * @Route("/list/template", name="regle_template") + */ + public function listTemplateAction(): Response + { + $key = $this->sessionService->getParamRuleLastKey(); + $solutionSourceName = $this->sessionService->getParamRuleSourceSolution($key); + $solutionTargetName = $this->sessionService->getParamRuleCibleSolution($key); + $templates = $this->template->getTemplates($solutionSourceName, $solutionTargetName); + if (!empty($templates)) { + $rows = ''; + foreach ($templates as $t) { + $rows .= ' + + + + + + + + + '.$t['name'].' + '.$t['description'].' + '; + } + + return new Response(' + + + + + + + + + '.$rows.' + +
    #'.$this->translator->trans('animate.choice.name').''.$this->translator->trans('animate.choice.description').'
    '); + } + + return new Response(''); + } + + /** + * CREATION - STEP ONE - ANIMATION. + * + * @Route("/create", name="regle_stepone_animation") + */ + public function ruleStepOneAnimation(): Response + { + if ($this->sessionService->isConnectorExist()) { + $this->sessionService->removeMyddlewareConnector(); + } + + // New Rule + $this->sessionService->setParamRuleLastKey(0); + + $key = $this->sessionService->getParamRuleLastKey(); + + // Détecte s'il existe des erreurs + if ($this->sessionService->isErrorNotEmpty($key, SessionService::ERROR_CREATE_RULE_INDEX)) { + $error = $this->sessionService->getCreateRuleError($key); + $this->sessionService->removeError($key, SessionService::ERROR_CREATE_RULE_INDEX); + } else { + $error = false; + } + + // Liste source : solution avec au moins 1 connecteur + $this->getInstanceBdd(); + + $solutionSource = $this->entityManager->getRepository(Solution::class) + ->solutionConnector('source', $this->getUser()->isAdmin(), $this->getUser()->getId()); + + if (!empty($solutionSource)) { + foreach ($solutionSource as $s) { + $source[] = $s->getName(); + } + $this->sessionService->setParamConnectorSolutionSource($key, $source); + } + + // Liste target : solution avec au moins 1 connecteur + $solutionTarget = $this->entityManager->getRepository(Solution::class) + ->solutionConnector('target', $this->getUser()->isAdmin(), $this->getUser()->getId()); + + if (!empty($solutionTarget)) { + foreach ($solutionTarget as $t) { + $target[] = $t->getName(); + } + $this->sessionService->setParamConnectorSolutionTarget($key, $target); + } + + return $this->render('Rule/create/step1simply.html.twig', [ + 'source' => $solutionSource, + 'target' => $solutionTarget, + 'error' => $error, + ] + ); + } + + /** + * LISTE DES MODULES POUR ANIMATION. + * @throws Exception + * @Route("/list/module", name="regle_list_module") + */ + public function ruleListModule(Request $request): Response + { + try { + $id_connector = $request->get('id'); + $type = $request->get('type'); + $key = $this->sessionService->getParamRuleLastKey(); // It's a new rule, last key = 0 + + // Control the request + if (!in_array($type, ['source', 'cible']) || !is_numeric($id_connector)) { + throw $this->createAccessDeniedException(); + } + $id_connector = (int) $id_connector; + + $this->getInstanceBdd(); + $connector = $this->entityManager->getRepository(Connector::class) + ->find($id_connector); // infos connector + + $connectorParams = $this->entityManager->getRepository(ConnectorParam::class) + ->findBy(['connector' => $id_connector]); // infos params connector + + foreach ($connectorParams as $p) { + $this->sessionService->setParamRuleParentName($key, $type, $p->getName(), $p->getValue()); // params connector + } + $this->sessionService->setParamRuleConnectorParent($key, $type, $id_connector); // id connector + $this->sessionService->setParamRuleParentName($key, $type, 'solution', $connector->getSolution()->getName()); // nom de la solution + + $solution = $this->solutionManager->get($this->sessionService->getParamRuleParentName($key, $type, 'solution')); + + $params_connexion = $this->decrypt_params($this->sessionService->getParamParentRule($key, $type)); + $params_connexion['idConnector'] = $id_connector; + + $solution->login($params_connexion); + + $t = (('source' == $type) ? 'source' : 'target'); + + $liste_modules = ToolsManager::composeListHtml($solution->get_modules($t), $this->translator->trans('create_rule.step1.choose_module')); + + return new Response($liste_modules); + } catch (Exception $e) { + $error = $e->getMessage().' '.$e->getLine().' '.$e->getFile(); + $this->logger->error($error); + return new Response(''); + } + } + + /* ****************************************************** + * METHODES PRATIQUES + ****************************************************** */ + + // CREATION REGLE - STEP ONE : Liste des connecteurs pour un user + private function connectorsList($type): string + { + $this->getInstanceBdd(); + $solutionRepo = $this->entityManager->getRepository(Connector::class); + $solution = $solutionRepo->findAllConnectorByUser($this->getUser()->getId(), $type); // type = source ou target + $lstArray = []; + if ($solution) { + foreach ($solution as $s) { + $lstArray[$s['name'].'_'.$s['id_connector']] = ucfirst($s['label']); + } + } + + return ToolsManager::composeListHtml($lstArray, $this->translator->trans('create_rule.step1.list_empty')); + } + + // CREATION REGLE - STEP THREE - Retourne les paramètres dans un bon format de tableau + private function createListeParamsRule($fields, $formula, $params): array + { + $phrase_placeholder = $this->translator->trans('rule.step3.placeholder'); + $tab = []; + + // FIELDS ------------------------------------------ + if ($fields) { + $champs = explode(';', $fields); + foreach ($champs as $champ) { + $chp = explode('[=]', $champ); + + if ($chp[0]) { + if ($phrase_placeholder != $chp[1] && 'my_value' != $chp[1]) { + $tab['fields']['name'][$chp[0]]['champs'][] = $chp[1]; + } + } + } + } + + // FORMULA ----------------------------------------- + if ($formula) { + $formules = explode(';', $formula); + + foreach ($formules as $formule) { + $chp = explode('[=]', $formule); + if ($chp[0]) { + if (!empty($chp[1])) { + $tab['fields']['name'][$chp[0]]['formule'][] = $chp[1]; + } + } + } + } + + // PARAMS ----------------------------------------- + if ($params) { + foreach ($params as $k => $p) { + $tab['params'][$p['name']] = $p['value']; + } + } + + return $tab; + } + + // Crée la pagination avec le Bundle Pagerfanta en fonction d'une requete + private function nav_pagination($params, $orm = true) + { + /* + * adapter_em_repository = requete + * maxPerPage = integer + * page = page en cours + */ + + if (is_array($params)) { + /* DOC : + * $pager->setCurrentPage($page); + $pager->getNbResults(); + $pager->getMaxPerPage(); + $pager->getNbPages(); + $pager->haveToPaginate(); + $pager->hasPreviousPage(); + $pager->getPreviousPage(); + $pager->hasNextPage(); + $pager->getNextPage(); + $pager->getCurrentPageResults(); + */ + + $compact = []; + + //On passe l’adapter au bundle qui va s’occuper de la pagination + if ($orm) { + $compact['pager'] = new Pagerfanta(new QueryAdapter($params['adapter_em_repository'])); + } else { + $compact['pager'] = new Pagerfanta(new ArrayAdapter($params['adapter_em_repository'])); + } + + //On définit le nombre d’article à afficher par page (que l’on a biensur définit dans le fichier param) + $compact['pager']->setMaxPerPage($params['maxPerPage']); + try { + $compact['entities'] = $compact['pager'] + //On indique au pager quelle page on veut + ->setCurrentPage($params['page']) + //On récupère les résultats correspondant + ->getCurrentPageResults(); + + $compact['nb'] = $compact['pager']->getNbResults(); + } catch (\Pagerfanta\Exception\NotValidCurrentPageException $e) { + //Si jamais la page n’existe pas on léve une 404 + throw $this->createNotFoundException("Cette page n'existe pas."); + } + + return $compact; + } + + return false; + } + + // Décrypte les paramètres de connexion d'une solution + private function decrypt_params($tab_params) + { + // Instanciate object to decrypte data + $encrypter = new Encrypter(substr($this->getParameter('secret'), -16)); + if (is_array($tab_params)) { + $return_params = []; + foreach ($tab_params as $key => $value) { + if ( + is_string($value) + && !in_array($key, ['solution', 'module']) // Soe data aren't crypted + ) { + $return_params[$key] = $encrypter->decrypt($value); + } + } + + return $return_params; + } + + return $encrypter->decrypt($tab_params); + } + + + // Function to take source document ids optain in a form and reading them and them only. + + /** + * @param $id + * + * @Route("/executebyid/{id}", name="run_by_id") + */ + public function execRuleById($id, Request $request) + { + $form = $this->createFormBuilder() + ->add('id', TextareaType::class) + ->getForm(); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $documentIdString = $form->get('id')->getData(); + + // We will get to the runrecord commmand using the ids from the form. + $this->ruleExecAction($id, $documentIdString); + } + return $this->render('Rule/byIdForm.html.twig', [ + 'formIdBatch' => $form->createView() + ]); + } + + /** + * @Route("/rule/update_description", name="update_rule_description", methods={"POST"}) + */ + public function updateDescription(Request $request): Response + { + $ruleId = $request->request->get('ruleId'); + $description = $request->request->get('description'); + $entityManager = $this->getDoctrine()->getManager(); + $descriptionOriginal = $entityManager->getRepository(RuleParam::class)->findOneBy([ + 'rule' => $ruleId, + 'name' => 'description' + ]); + // if $description is the same as the previous one or is equal to 0 or is empty + if ($description === '0' || empty($description) || $description === $descriptionOriginal->getValue()) { + return $this->redirect($this->generateUrl('regle_open', ['id' => $ruleId])); + } + + // Retrieve the RuleParam entity using the ruleId + $rule = $entityManager->getRepository(RuleParam::class)->findOneBy(['rule' => $ruleId]); + + if (!$rule) { + throw $this->createNotFoundException('Couldn\'t find specified rule in database'); + } + + // Retrieve the RuleParam with the name "description" and the same rule as the previously retrieved entity + $descriptionRuleParam = $entityManager->getRepository(RuleParam::class)->findOneBy([ + 'rule' => $rule->getRule(), + 'name' => 'description' + ]); + + // Check if the description entity was found + if (!$descriptionRuleParam) { + throw $this->createNotFoundException('Couldn\'t find description rule parameter'); + } + + // Update the value of the description + $descriptionRuleParam->setValue($description); + $entityManager->flush(); + + return new Response('', Response::HTTP_OK); + } +} diff --git a/src/Manager/HomeManager.php b/src/Manager/HomeManager.php index b5f572eac..f816354bd 100644 --- a/src/Manager/HomeManager.php +++ b/src/Manager/HomeManager.php @@ -1,85 +1,85 @@ -. -*********************************************************************************/ - -namespace App\Manager; - -use App\Entity\User; -use App\Repository\DocumentRepository; -use App\Repository\JobRepository; -use Doctrine\DBAL\Connection; -use Psr\Log\LoggerInterface; - -class HomeManager -{ - protected Connection $connection; - protected LoggerInterface $logger; - - const historicDays = 7; - const nbHistoricJobs = 5; - protected string $historicDateFormat = 'M-d'; - private JobRepository $jobRepository; - private DocumentRepository $documentRepository; - - public function __construct( - LoggerInterface $logger, - Connection $connection, - JobRepository $jobRepository, - DocumentRepository $documentRepository - ) { - $this->logger = $logger; - $this->connection = $connection; - $this->jobRepository = $jobRepository; - $this->documentRepository = $documentRepository; - } - - public function countTransferHisto(User $user = null): array - { - try { - $historic = []; - // Start date - $startDate = date('Y-m-d', strtotime('-'.self::historicDays.' days')); - // End date - $endDate = date('Y-m-d'); - // Init array - while (strtotime($startDate) < strtotime($endDate)) { - $startDateFormat = date($this->historicDateFormat, strtotime('+1 day', strtotime($startDate))); - $startDate = date('Y-m-d', strtotime('+1 day', strtotime($startDate))); - $historic[$startDate] = ['date' => $startDateFormat, 'open' => 0, 'error' => 0, 'cancel' => 0, 'close' => 0]; - } - - // Select the number of transfers per day - $result = $this->documentRepository->countTransferHisto($user); - if (!empty($result)) { - foreach ($result as $row) { - $historic[$row['date']][strtolower($row['globalStatus'])] = $row['nb']; - } - } - } catch (\Exception $e) { - $this->logger->error('Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); - } - - return $historic; - } -} +. +*********************************************************************************/ + +namespace App\Manager; + +use App\Entity\User; +use App\Repository\DocumentRepository; +use App\Repository\JobRepository; +use Doctrine\DBAL\Connection; +use Psr\Log\LoggerInterface; + +class HomeManager +{ + protected Connection $connection; + protected LoggerInterface $logger; + + const historicDays = 7; + const nbHistoricJobs = 5; + protected string $historicDateFormat = 'M-d'; + private JobRepository $jobRepository; + private DocumentRepository $documentRepository; + + public function __construct( + LoggerInterface $logger, + Connection $connection, + JobRepository $jobRepository, + DocumentRepository $documentRepository + ) { + $this->logger = $logger; + $this->connection = $connection; + $this->jobRepository = $jobRepository; + $this->documentRepository = $documentRepository; + } + + // public function countTransferHisto(User $user = null): array + // { + // try { + // $historic = []; + // // Start date + // $startDate = date('Y-m-d', strtotime('-'.self::historicDays.' days')); + // // End date + // $endDate = date('Y-m-d'); + // // Init array + // while (strtotime($startDate) < strtotime($endDate)) { + // $startDateFormat = date($this->historicDateFormat, strtotime('+1 day', strtotime($startDate))); + // $startDate = date('Y-m-d', strtotime('+1 day', strtotime($startDate))); + // $historic[$startDate] = ['date' => $startDateFormat, 'open' => 0, 'error' => 0, 'cancel' => 0, 'close' => 0]; + // } + + // // Select the number of transfers per day + // $result = $this->documentRepository->countTransferHisto($user); + // if (!empty($result)) { + // foreach ($result as $row) { + // $historic[$row['date']][strtolower($row['globalStatus'])] = $row['nb']; + // } + // } + // } catch (\Exception $e) { + // $this->logger->error('Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); + // } + + // return $historic; + // } +} diff --git a/src/Repository/DocumentRepository.php b/src/Repository/DocumentRepository.php index c13581f2f..531a2e7b7 100644 --- a/src/Repository/DocumentRepository.php +++ b/src/Repository/DocumentRepository.php @@ -170,20 +170,6 @@ public function countTypeDoc(User $user = null) return $qb->getQuery()->getResult(); } - public function countTransferRule(User $user = null) - { - $qb = $this->createQueryBuilder('d') - ->select('COUNT(d) as nb, rule.name') - ->join('d.rule', 'rule') - ->andWhere('d.deleted = 0') - ->andWhere('d.status = :send') - ->setParameter('send', 'Send') - ->groupBy('rule.name') - ->orderBy('nb', 'DESC'); - - return $qb->getQuery()->getResult(); - } - public function countTransferHisto(User $user = null) { $qb = $this->createQueryBuilder('d') @@ -436,4 +422,14 @@ public function removeLock($jobId) { ->getQuery(); $q->execute(); } + + public function countNbDocuments(): int + { + return $this->createQueryBuilder('d') + ->select('COUNT(d.id)') + ->where('d.deleted = :deleted') + ->setParameter('deleted', 0) + ->getQuery() + ->getSingleScalarResult(); + } } diff --git a/templates/Home/index.html.twig b/templates/Home/index.html.twig index 3fac28f76..2738960d1 100644 --- a/templates/Home/index.html.twig +++ b/templates/Home/index.html.twig @@ -1,126 +1,141 @@ -{#/********************************************************************************* - * This file is part of Myddleware. - - * @package Myddleware - * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL - * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com - * @link http://www.myddleware.com - - This file is part of Myddleware. - - Myddleware is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Myddleware is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Myddleware. If not, see . -*********************************************************************************/ #} - -{% extends 'base.html.twig' %} -{% block titlesm %}{{'title.index.panel'|trans}}{% endblock titlesm %} -{% block body %} -
    -
    -
    - {% if solutions is not empty %} -
    -

    {{'panel.solutions.title'|trans}}

    -
    -
    - -
    -
      - {% for solution in solutions %} -
    • {{solution}}
    • - {% endfor %} -
    -
    - -
    - {% endif %} -
    -
    -
    -
    -
    -
    -
    -

    {{'panel.graph_tranfer_rule.desc'|trans}}

    -
    -
    -
    -
    -
    -

    {{'panel.graph_error.title'|trans}} ({{ nbFlux }})

    -
    -
    -
    -
    -
    -
    -
    -

    {{ 'panel.graph_job_histo.title'|trans }}

    -
    -
    -
    -
    -
    -

    {{ 'panel.graph_histo.title'|trans }}

    -
    -
    -
    -
    -
    -
    -
    -

    {{'panel.list_job.title'|trans}}

    -
      - {% for job in listJobDetail %} -
    • - {{ job.begin|date('d/m/Y') }} - | {{ job.duration }} {{'panel.list_job.duration'|trans}} - | {{ job.status }} - | {{ job.message|sensor }} -
    • - {% endfor %} -
    -
    -
    -
    -
    - {% if errorByRule %} -

    {{'panel.list_job.nb'|trans}}

    -
      - {% for rule in errorByRule %} -
    • - {{ rule.cpt }} - {{ rule.name }} -
    • - {% endfor %} -
    - {% endif %} -
    -
    -
    -
    -
    -
    -
    -
    -
    - -{% endblock %} \ No newline at end of file +{#/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Myddleware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Myddleware. If not, see . +*********************************************************************************/ #} + +{% extends 'base.html.twig' %} +{% block titlesm %} +
    + {{'title.index.panel'|trans }} +

    {{'title.index.text'|trans }}

    +

    {{'title.index.text2'|trans }}

    + +
    +{% endblock titlesm %} +{% block body %} +
    +
    +
    +
    +

    {{'panel.data'|trans}}

    +
    +
    +
    +
    +
    +
    +
    +
    + + + + + + +
    +

    0

    +

    {{'panel.documents.title'|trans}}

    +
    +
    +
    +
    +
    +
    +
    + {% if errorByRule %} +
    +

    {{'panel.list_job.nb'|trans}}

    +
    +
      + {% for rule in errorByRule %} +
    • + {{ rule.cpt }} + {{ rule.name }} +
    • + {% endfor %} +
    + {% if errorByRule|length > 5 %} +
    + + +
    + {% endif %} + {% endif %} +
    +
    +
    +
    + {# SOLUTION #} +
    +
    +
    +

    {{'panel.solutions.title'|trans}}

    +
    +
    + {% if solutions is not empty %} +
      + {% for solution in solutions %} +
    • + + {{ solution }} + +
    • + {% endfor %} +
    + {% endif %} +
    + {# FEATURES #} +
    +
    +
    +

    {{'panel.features.title'|trans}}

    +
    +
    + {# EN ATTENTE DE LA LISTE DES FEATURES #} +
    + {# CONTACT US #} +
    +
    +
    +

    {{'panel.contact.title'|trans}}

    +
    +
    +
    +
    +

    {{'panel.contact.title2'|trans}}

    +

    {{'panel.contact.text1'|trans}}

    +

    {{'panel.contact.text2'|trans}}

    +

    {{'panel.contact.text3'|trans}}

    +
    +
    +

    {{'panel.contact.title3'|trans}}

    +

    {{'panel.contact.text4'|trans}}

    +

    {{'panel.contact.text5'|trans}}

    +
    +
    +
    +
    +
    +
  • {% endblock %} diff --git a/templates/base.html.twig b/templates/base.html.twig index d70efbd26..31cfbf22a 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -1,4 +1,4 @@ - {#/********************************************************************************* +{#/********************************************************************************* * This file is part of Myddleware. * @package Myddleware @@ -20,206 +20,194 @@ You should have received a copy of the GNU General Public License along with Myddleware. If not, see . -*********************************************************************************/ #} +*********************************************************************************/ #} - - - {% block title %}Myddleware{% endblock %} - - {% block stylesheets %} - {{ encore_entry_link_tags('app') }} - {% endblock %} - - - -
    -
    - -
    - -
    - -
    - {% include "Layout/notification.html.twig" %} -
    -
    - - {% block titlesm %}{% endblock titlesm %} -
    -
    - {% block body %} - {% block arianne %}Myddleware{% endblock %} - {% endblock %} -
    -
    - {% include 'footer.html.twig' %} -
    + + + + {% block title %}Myddleware + {% endblock %} + + + {% block stylesheets %} + {{ encore_entry_link_tags('app') }} + {% endblock %} + + + +
    + {% for v in entityBanner.getEntityBanner %} + {% if v.name == "cron_enabled"%} +
    + +
    + {% endif %} + {% endfor %} +
    + + + +
    +
    + +
    +{% include "Layout/notification.html.twig" %}
    {% block titlesm %}{% endblock titlesm %} +
    +
    + {% block body %} + {% block arianne %}Myddleware + {% endblock %} + {% endblock %} +
    +
    + {% include 'footer.html.twig' %} +
    +{% block base_js %} + + {{ encore_entry_script_tags('app') }} +{% endblock base_js %} +{% block js %}{% endblock js %} diff --git a/translations/messages.en.yml b/translations/messages.en.yml index 4b5756564..15f628f6b 100644 --- a/translations/messages.en.yml +++ b/translations/messages.en.yml @@ -23,8 +23,22 @@ # *********************************************************************************/ panel: + data: Your data + features: + title: Unlock new featues with Myddleware Premium + contact: + title: Contact us + title2: Need technical assistance + text1: For any technical issues, bugs, or development-related inquiries, feel free to contact us through the following channels + text2: Technical contact + text3: GitHub Repository, view or report issues directly on our GitHub + title3: New projects or buisiness opportunities ? + text4: To discuss a new project, collaboration, or business opportunities, please reach out to us at the following address + text5: Business contact + documents: + title: Documents envoyés par Myddleware solutions: - title: Start by connecting these applications + title: Connect your data click: Click to see how to create this connector. graph_tranfer_rule: desc: Documents sent by rule @@ -676,7 +690,10 @@ title: account: view: My account index: - panel: Control panel + panel: Welcome to Myddleware ! + text: We're excited to have you on board ! + text2: Start your project now by giving a look to our documentation. + btn: Start now ! rule: create_name: Creation of the rule create: Creation of a rule diff --git a/translations/messages.fr.yml b/translations/messages.fr.yml index 285699432..e859aea3a 100644 --- a/translations/messages.fr.yml +++ b/translations/messages.fr.yml @@ -23,8 +23,22 @@ # *********************************************************************************/ panel: + data: Vos données + features: + title: Débloquer de nouvelles fonctionnalités avec Myddleware Premium + contact: + title: Contactez-nous + title2 : Besoin d'assistance technique + text1 : Pour tout problème technique, bogue ou question relative au développement, n'hésitez pas à nous contacter par les canaux suivants + text2 : Contact technique + text3 : Dépôt GitHub, voir ou signaler des problèmes directement sur notre GitHub + title3 : Nouveaux projets ou opportunités d'affaires ? + text4 : Pour discuter d'un nouveau projet, d'une collaboration ou d'une opportunité commerciale, veuillez nous contacter à l'adresse suivante + text5 : Contact professionnel + documents: + title: Documents sent by Myddleware solutions: - title: Connectez dès à présent ces différentes solutions + title: Connectez vos données click: Cliquez pour savoir comment créer ce connecteur. graph_tranfer_rule: desc: Documents envoyés par règle @@ -673,7 +687,10 @@ title: account: view: Mon compte index: - panel: Tableau de bord + panel: Bienvenue sur Myddleware ! + text: Nous sommes ravis de vous compter parmi nous ! + text2: Démarrez votre projet dès maintenant en consultant notre documentation. + btn: Commencer maintenant rule: create_name: Création de la règle create: Création d'une règle From ba74400963c35ee857712c913b065b6968962d89 Mon Sep 17 00:00:00 2001 From: myddleware Date: Sun, 13 Oct 2024 18:07:03 +0200 Subject: [PATCH 156/452] Add premium declaration Signed-off-by: myddleware --- config/services_custom.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/config/services_custom.yaml b/config/services_custom.yaml index 3bbbfc84c..d30de229e 100644 --- a/config/services_custom.yaml +++ b/config/services_custom.yaml @@ -23,3 +23,23 @@ services: # decorates: App\Solutions\wordpress # decoration_on_invalid: ignore # decoration_priority: 1 + + App\Custom\Premium\Solutions\mysqlpremium: + decorates: App\Solutions\mysql + decoration_on_invalid: ignore + decoration_priority: 1 + + App\Custom\Solutions\mysqlcustom: + decorates: App\Custom\Premium\Solutions\mysqlpremium + decoration_on_invalid: ignore + decoration_priority: 1 + + App\Custom\Premium\Manager\DocumentManagerPremium: + decorates: App\Manager\DocumentManager + decoration_on_invalid: ignore + decoration_priority: 1 + + App\Custom\Premium\Manager\ToolsManagerPremium: + decorates: App\Manager\ToolsManager + decoration_on_invalid: ignore + decoration_priority: 1 \ No newline at end of file From d19ba50fc71da8345091c612f576690fd632d0ef Mon Sep 17 00:00:00 2001 From: myddleware Date: Sun, 13 Oct 2024 19:06:33 +0200 Subject: [PATCH 157/452] Manage premium code Signed-off-by: myddleware --- src/Manager/DocumentManager.php | 301 +------------------------------- src/Manager/ToolsManager.php | 3 + 2 files changed, 5 insertions(+), 299 deletions(-) diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index 9c95345b3..5535b3bda 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -324,12 +324,6 @@ public function setParam($param, $clear = false, $clearRule = true) if (!empty($param['ruleFilters'])) { $this->ruleFilters = $param['ruleFilters']; } - if (!empty($param['ruleWorkflows'])) { - $this->ruleWorkflows = $param['ruleWorkflows']; - } - if (!empty($param['variables'])) { - $this->variables = $param['variables']; - } // Init type error for each new document $this->typeError = 'S'; } catch (\Exception $e) { @@ -353,7 +347,6 @@ protected function clearAttributes($clearRule = true) $this->ruleFields = []; $this->ruleRelationships = []; $this->ruleFilters = []; - $this->ruleWorkflows = []; $this->ruleParams = []; } $this->id = ''; @@ -1641,10 +1634,6 @@ public function getTransformValue($source, $ruleField) if (empty($source)) { throw new \Exception('Source data are empty. Failed to transform data.' ); } - // Include custom variables that could be used in the formula // TO BE REMOVED - if (file_exists( __DIR__.'/../Custom/Utils/myddlewareVariables.php')) { - include __DIR__.'/../Custom/Utils/myddlewareVariables.php'; - } // Include variable in the database if (!empty($this->variables)) { foreach($this->variables as $key => $value) { @@ -2660,295 +2649,9 @@ public function getStatus() return $this->status; } + // Premium method public function runWorkflow($rerun=false) { - try { - // Check if at least on workflow exist for the rule - if (!empty($this->ruleWorkflows)) { - $targetFields = false; - $historyFields = false; - // Can be empty depending on the context of the workflow call - if (empty($this->sourceData)) { - $this->sourceData = $this->getDocumentData('S'); - } - // Add all source data in variables - if (!empty($this->sourceData)) { - foreach($this->sourceData as $key => $value) { - $fieldName = 'source_'.$key; - $$fieldName = $value; - } - } - - // Include variables used in the formula - $status = $this->status; - $documentType = $this->documentType; - $attempt = $this->attempt; - $message = $this->message; - $typeError = $this->typeError; - - // Execute every workflow of the rule - foreach ($this->ruleWorkflows as $ruleWorkflow) { - // Add target fields if requested and if not already calculated - if ( - strpos($ruleWorkflow['condition'], 'target_') !== false - AND !$targetFields - ) { - $targetFields = true; - $target = $this->getDocumentData('T'); - // Add all source data in variables - if (!empty($target)) { - foreach($target as $key => $value) { - $fieldName = 'target_'.$key; - $$fieldName = $value; - } - } - } - // Add history fields if requested and if not already calculated - if ( - strpos($ruleWorkflow['condition'], 'history_') !== false - AND !$historyFields - ) { - $historyFields = true; - $history = $this->getDocumentData('H'); - // Add all source data in variables - if (!empty($history)) { - foreach($history as $key => $value) { - $fieldName = 'history_'.$key; - $$fieldName = $value; - } - } - } - - // Check the condition - $this->formulaManager->init($ruleWorkflow['condition']); // mise en place de la règle dans la classe - $this->formulaManager->generateFormule(); // Genère la nouvelle formule à la forme PhP - $f = $this->formulaManager->execFormule(); - eval('$condition = ('.$f.'?1:0);'); - // Execute the action if the condition is met - if ($condition == 1) { - try { - // Execute all actions - if (!empty($ruleWorkflow['actions'])) { - // Call each actions - foreach($ruleWorkflow['actions'] as $action) { - // Check if the action has already been executed for the current document - // Only if attempt > 0, if it is the first attempt then the action has never been executed - if ( - $this->attempt > 0 - OR $rerun - ) { - // Search action for the current document - $workflowLogEntity = $this->entityManager->getRepository(WorkflowLog::class) - ->findOneBy([ - 'triggerDocument' => $this->id, - 'action' => $action['id'], - ] - ); - // If the current action has been found for the current document, we don't execute the current action - if ( - !empty($workflowLogEntity) - AND $workflowLogEntity->getStatus() == 'Success' - ) { - // GenerateDocument can be empty depending the action - if (!empty($workflowLogEntity->getGenerateDocument())) { - $this->docIdRefError = $workflowLogEntity->getGenerateDocument()->getId(); - } - $this->generateDocLog('W','Action ' . $action['id'] . ' already executed for this document. '); - continue; - } - } - - // Execute action depending of the function in the workflow - $arguments = $this->setWorkflowNotificationArguments($action); - switch ($action['action']) { - case 'generateDocument': - // Set default value if empty - $searchField = (!empty($arguments['searchField']) ? $arguments['searchField'] : 'id'); - $searchValue = ((!empty($arguments['searchValue']) AND !empty($this->sourceData[$arguments['searchValue']])) ? $this->sourceData[$arguments['searchValue']] : ''); - $this->generateDocument($arguments['ruleId'],$searchValue, $searchField ,$arguments['rerun'], $action); - break; - case 'sendNotification': - $workflowStatus = 'Success'; - $error = ''; - // Method sendMessage throws an exception if it fails - $this->tools->sendMessage($arguments['to'],$arguments['subject'],$arguments['message']); - $this->createWorkflowLog($action, $workflowStatus, $error); - break; - case 'updateStatus': - $workflowStatus = 'Success'; - $error = ''; - $this->typeError = 'W'; - $this->message = 'Status change using workflow. '; - $this->updateStatus($arguments['status'], true); - $this->createWorkflowLog($action, $workflowStatus, $error); - break; - case 'changeData': - $workflowStatus = 'Success'; - $error = ''; - $this->typeError = 'W'; - $this->message = 'Change fields values. '; - if (!empty($arguments['fields'])) { - $this->updateDocumentData($this->id, $arguments['fields'], 'T'); - } - $this->createWorkflowLog($action, $workflowStatus, $error); - break; - case 'transformDocument': - $workflowStatus = 'Success'; - $error = ''; - $this->typeError = 'W'; - // Avoid infinite loop by skipping the call to the workflow after changing the status - $this->workflowAction = true; - // Set back the parameter $this->transformError to false - // because it could have been set to true by the current action - $this->transformError = false; - $this->transformDocument(); - $this->message = 'Document transformed using workflow. '; - $this->createWorkflowLog($action, $workflowStatus, $error); - break; - default: - throw new \Exception('Function '.key($action).' unknown.'); - } - } - } - } catch (\Exception $e) { - $this->logger->error($this->id.' - Failed to run the workflow '.$ruleWorkflow['name'].' : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); - $this->generateDocLog('E','Failed to run the workflow '.$ruleWorkflow['name'].' : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); - $this->setWorkflowError(true); - } - } - } - } - } catch (\Exception $e) { - $this->logger->error($this->id.' - Failed to run all workflows : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); - $this->generateDocLog('E','Failed to run all workflows : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); - } - } - - protected function setWorkflowNotificationArguments($action) { - $arguments = unserialize($action['arguments']); - // In case of notification, we add informations on the message - if ($action['action'] == 'sendNotification') { - $arguments['message'] .= '

    Document ID : '.$this->id.'

    '; - if (!empty($this->sourceData)) { - $arguments['message'] .= 'Detail of the document :
    '; - foreach($this->sourceData as $key => $value) { - $arguments['message'] .= $key.' : '.$value.'
    '; - } - } - $arguments['message'] .= '
    Best regards
    Myddleware'; - // We transform the string to array in case there are several recipients - $arguments['to'] = explode(',',$arguments['to']); - } - return $arguments; - } - - // Generate a document using the rule id and search parameters - protected function generateDocument($ruleId, $searchValue = null, $searchField = 'id', $rerun = true, $action = null) - { - try { - // Instantiate the rule - $rule = new RuleManager($this->logger, $this->connection, $this->entityManager, $this->parameterBagInterface, $this->formulaManager, $this->solutionManager, clone $this); - $rule->setRule($ruleId); - $rule->setJobId($this->jobId); - - if (empty($searchValue)) { - $searchValue = $this->sourceId; - } - - // Generate the documents depending on the search parameter - $documents = $rule->generateDocuments($searchValue, true, '', $searchField); - if (!empty($documents->error)) { - throw new \Exception($documents->error); - } - // Run documents - if ( - !empty($documents) - and $rerun - ) { - foreach ($documents as $doc) { - $errors = $rule->actionDocument($doc->id, 'rerun'); - // Check errors - if (!empty($errors)) { - $this->message .= 'Document ' . $doc->id . ' in error (rule ' . $ruleId . ' : ' . $errors[0] . '. '; - } - // Generate the workflow log for each document if it has been generated by a workflow - if (!empty($action['id'])) { - $error = ''; - if (!empty($errors)) { - $error = $this->message; - $status = 'Error'; - } else { - $status = 'Success'; - } - $this->createWorkflowLog($action, $status, $error, $doc->id); - } - } - } - } catch (\Exception $e) { - $this->logger->error($this->id.' - Error : ' . $e->getMessage() . ' ' . $e->getFile() . ' Line : ( ' . $e->getLine() . ' )'); - $this->generateDocLog('E',$this->message); - } - } - - // Create a workflow log - protected function createWorkflowLog($action, $status, $error=null, $generateDocumentId=null) { - try { - // Generate the workflow log - $workflowLog = new WorkflowLog(); - // Set the current document - $triggerDocumentEntity = $this->entityManager->getRepository(Document::class)->find($this->id); - $workflowLog->setTriggerDocument($triggerDocumentEntity); - // Set the current action - $workflowActionEntity = $this->entityManager->getRepository(WorkflowAction::class)->find($action['id']); - $workflowLog->setAction($workflowActionEntity); - // Set the generated document if the action has generated a document - if (!empty($generateDocumentId)) { - $generateDocumentEntity = $this->entityManager->getRepository(Document::class)->find($generateDocumentId); - $this->docIdRefError = $generateDocumentId; - $workflowLog->setGenerateDocument($generateDocumentEntity); - } - // Set the workflow - $workflowEntity = $this->entityManager->getRepository(Workflow::class)->find($action['workflow_id']); - $workflowLog->setWorkflow($workflowEntity); - // Set the job - $jobEntity = $this->entityManager->getRepository(Job::class)->find($this->jobId); - $workflowLog->setJob($jobEntity); - // Set the creation date - $workflowLog->setDateCreated(new \DateTime()); - // Set the status depending on the error message - if (!empty($errors)) { - $workflowLog->setMessage($error); - $workflowLog->setStatus($status); - } else { - $workflowLog->setStatus('Success');; - } - $this->entityManager->persist($workflowLog); - $this->entityManager->flush(); - // Generate a document log. - $this->generateDocLog('S','Action '.$action['action'].' : '.$action['name'].' executed. '.(!empty($generateDocumentId) ? 'The document '.$generateDocumentId.' has been generated. ' : '')); - } catch (\Exception $e) { - $this->logger->error($this->id.' - Error : ' . $e->getMessage() . ' ' . $e->getFile() . ' Line : ( ' . $e->getLine() . ' )'); - $this->generateDocLog('E','Error : ' . $e->getMessage() . ' ' . $e->getFile() . ' Line : ( ' . $e->getLine() . ' )'); - } - } - - // Unset the lock on the rule - protected function setWorkflowError($flag) { - try { - // Get the rule details - $documentEntity = $this->entityManager->getRepository(Document::class)->find($this->id); - // If read lock empty, we set the lock with the job id - if (!empty($documentEntity)) { - $documentEntity->setWorkflowError($flag); - $this->entityManager->persist($documentEntity); - $this->entityManager->flush(); - $this->workflowError = true; - return true; - } - } catch (Exception $e) { - $this->logger->error('Failed set the flag workflow error to '.$flag.' to the document '.$this->id.' : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'); - } - return false; - } + } // Check the document before an action is executed protected function checkDocumentBeforeAction() { diff --git a/src/Manager/ToolsManager.php b/src/Manager/ToolsManager.php index 8d29d0dc3..e0495bc49 100644 --- a/src/Manager/ToolsManager.php +++ b/src/Manager/ToolsManager.php @@ -253,6 +253,9 @@ public function sendMessage($to, $subject, $message) { } } } + + public function isPremium() { + } } class ToolsManager extends toolscore From 9f310b8d1f334828ee2a6a570f9f85f898a7834c Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 16 Oct 2024 09:47:14 +0200 Subject: [PATCH 158/452] PREMIUM: redirect to home if not premium --- src/Controller/WorkflowController.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Controller/WorkflowController.php b/src/Controller/WorkflowController.php index 06d0f834f..a49063404 100644 --- a/src/Controller/WorkflowController.php +++ b/src/Controller/WorkflowController.php @@ -172,6 +172,10 @@ protected function getInstanceBdd() {} public function WorkflowListAction(int $page = 1, Request $request) { try { + + if (!($this->tools->isPremium())) { + return $this->redirectToRoute('regle_panel'); + } // Récupérer les filtres depuis la requête $workflowName = $request->query->get('workflow_name'); From a53d8f5c847883c1e8fe1c0ea75ef34789ac799d Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 16 Oct 2024 09:47:34 +0200 Subject: [PATCH 159/452] PREMIUM: locked link if not premium --- templates/base.html.twig | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/templates/base.html.twig b/templates/base.html.twig index 31cfbf22a..aac5a5736 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -76,10 +76,17 @@
  • - - - {{'menu.workflow.list'|trans}} - + {% if isPremium is defined and isPremium %} + + + {{ 'menu.workflow.list'|trans }} + + {% else %} + + + Premium Required + + {% endif %}
  • From 6096072ca71d4acb351088265c1fd1aa8ebf49b6 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 16 Oct 2024 09:49:24 +0200 Subject: [PATCH 160/452] PREMIUM: variable isPremium for homepage --- src/Controller/DefaultController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index e33c5f8ed..fff790a72 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -2638,6 +2638,7 @@ public function panel(Request $request): Response 'solutions' => $lstArray, 'locale' => $language, 'countNbDocuments' => $countNbDocuments, + 'isPremium' => $this->tools->isPremium() ? true : false, ] ); } From a90c6ab5600b7ded51cfb89945c75bdfbdd01ff4 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 16 Oct 2024 09:54:18 +0200 Subject: [PATCH 161/452] Revert "PREMIUM: locked link if not premium" This reverts commit a53d8f5c847883c1e8fe1c0ea75ef34789ac799d. --- templates/base.html.twig | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/templates/base.html.twig b/templates/base.html.twig index aac5a5736..31cfbf22a 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -76,17 +76,10 @@
  • - {% if isPremium is defined and isPremium %} - - - {{ 'menu.workflow.list'|trans }} - - {% else %} - - - Premium Required - - {% endif %} + + + {{'menu.workflow.list'|trans}} +
  • From 68f3b541362135687392a7974a1d72f6a58d762a Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 16 Oct 2024 09:56:20 +0200 Subject: [PATCH 162/452] Revert "PREMIUM: redirect to home if not premium" This reverts commit 9f310b8d1f334828ee2a6a570f9f85f898a7834c. --- src/Controller/WorkflowController.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Controller/WorkflowController.php b/src/Controller/WorkflowController.php index a49063404..06d0f834f 100644 --- a/src/Controller/WorkflowController.php +++ b/src/Controller/WorkflowController.php @@ -172,10 +172,6 @@ protected function getInstanceBdd() {} public function WorkflowListAction(int $page = 1, Request $request) { try { - - if (!($this->tools->isPremium())) { - return $this->redirectToRoute('regle_panel'); - } // Récupérer les filtres depuis la requête $workflowName = $request->query->get('workflow_name'); From ed3e4734b0cbe1e5f9eb878ce5369ddf5463eeb8 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 16 Oct 2024 09:56:31 +0200 Subject: [PATCH 163/452] Revert "PREMIUM: variable isPremium for homepage" This reverts commit 6096072ca71d4acb351088265c1fd1aa8ebf49b6. --- src/Controller/DefaultController.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index fff790a72..e33c5f8ed 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -2638,7 +2638,6 @@ public function panel(Request $request): Response 'solutions' => $lstArray, 'locale' => $language, 'countNbDocuments' => $countNbDocuments, - 'isPremium' => $this->tools->isPremium() ? true : false, ] ); } From 8feec29c8ba06ee4e686a99b87e1e0bfa36c7f02 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 16 Oct 2024 10:25:37 +0200 Subject: [PATCH 164/452] basic premium controller --- src/Controller/PremiumController.php | 191 +++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 src/Controller/PremiumController.php diff --git a/src/Controller/PremiumController.php b/src/Controller/PremiumController.php new file mode 100644 index 000000000..6b89f1719 --- /dev/null +++ b/src/Controller/PremiumController.php @@ -0,0 +1,191 @@ +. + *********************************************************************************/ + +namespace App\Controller; + +use Exception; +use App\Entity\Rule; +use App\Entity\User; +use App\Entity\FuncCat; +use App\Entity\Document; +use App\Entity\Solution; +use App\Entity\Workflow; +use App\Entity\Connector; +use App\Entity\Functions; +use App\Entity\RuleAudit; +use App\Entity\RuleField; +use App\Entity\RuleParam; +use App\Entity\RuleFilter; +use Pagerfanta\Pagerfanta; +use App\Entity\WorkflowLog; +use App\Form\ConnectorType; +use App\Manager\JobManager; +use App\Manager\HomeManager; +use App\Manager\RuleManager; +use Doctrine\ORM\Mapping\Id; +use Psr\Log\LoggerInterface; +use App\Entity\WorkflowAudit; +use App\Manager\ToolsManager; +use Doctrine\DBAL\Connection; +use App\Entity\ConnectorParam; +use App\Entity\RuleParamAudit; +use App\Entity\WorkflowAction; +use App\Form\Type\WorkflowType; +use App\Manager\FormulaManager; +use App\Service\SessionService; +use App\Entity\RuleRelationShip; +use App\Manager\DocumentManager; +use App\Manager\SolutionManager; +use App\Manager\TemplateManager; +use App\Repository\JobRepository; +use App\Repository\RuleRepository; +use App\Form\DuplicateRuleFormType; +use App\Repository\ConfigRepository; +use Illuminate\Encryption\Encrypter; +use Pagerfanta\Adapter\ArrayAdapter; +use App\Form\Type\RelationFilterType; +use App\Repository\DocumentRepository; +use App\Repository\WorkflowRepository; +use App\Repository\WorkflowLogRepository; +use Doctrine\ORM\EntityManagerInterface; +use Pagerfanta\Doctrine\ORM\QueryAdapter; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Contracts\Translation\TranslatorInterface; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; +use Symfony\Component\Form\Extension\Core\Type\TextareaType; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; +use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; + +/** + * @Route("/premium") + */ +class PremiumController extends AbstractController +{ + private FormulaManager $formuleManager; + private SessionService $sessionService; + private ParameterBagInterface $params; + private EntityManagerInterface $entityManager; + private HomeManager $home; + private ToolsManager $tools; + private TranslatorInterface $translator; + private AuthorizationCheckerInterface $authorizationChecker; + private JobManager $jobManager; + private LoggerInterface $logger; + private TemplateManager $template; + private RuleRepository $ruleRepository; + private JobRepository $jobRepository; + private DocumentRepository $documentRepository; + private SolutionManager $solutionManager; + private RuleManager $ruleManager; + private DocumentManager $documentManager; + private WorkflowLogRepository $workflowLogRepository; + + + protected Connection $connection; + // To allow sending a specific record ID to rule simulation + protected $simulationQueryField; + private ConfigRepository $configRepository; + + public function __construct( + LoggerInterface $logger, + RuleManager $ruleManager, + FormulaManager $formuleManager, + SolutionManager $solutionManager, + DocumentManager $documentManager, + SessionService $sessionService, + EntityManagerInterface $entityManager, + RuleRepository $ruleRepository, + JobRepository $jobRepository, + DocumentRepository $documentRepository, + Connection $connection, + TranslatorInterface $translator, + AuthorizationCheckerInterface $authorizationChecker, + HomeManager $home, + ToolsManager $tools, + JobManager $jobManager, + TemplateManager $template, + WorkflowLogRepository $workflowLogRepository, + ParameterBagInterface $params + ) { + $this->logger = $logger; + $this->ruleManager = $ruleManager; + $this->formuleManager = $formuleManager; + $this->solutionManager = $solutionManager; + $this->documentManager = $documentManager; + $this->sessionService = $sessionService; + $this->entityManager = $entityManager; + $this->ruleRepository = $ruleRepository; + $this->jobRepository = $jobRepository; + $this->documentRepository = $documentRepository; + $this->connection = $connection; + $this->translator = $translator; + $this->authorizationChecker = $authorizationChecker; + $this->home = $home; + $this->tools = $tools; + $this->jobManager = $jobManager; + $this->template = $template; + $this->workflowLogRepository = $workflowLogRepository; + } + + protected function getInstanceBdd() {} + + + +/** + * PAGE ACHAT PREMIUM. + * + * @return RedirectResponse|Response + * + * @Route("/list", name="premium_list", defaults={"page"=1}) + * @Route("/list/page-{page}", name="premium_list_page", requirements={"page"="\d+"}) + */ + public function PremiumListAction(int $page = 1, Request $request) + { + try { + + + + + // Si ce n'est pas une requête AJAX, rendre la page complète + return $this->render( + 'premium/list.html.twig', + [ + 'isPremium' => true, + ] + ); + } catch (Exception $e) { + throw $this->createNotFoundException('Erreur : ' . $e->getMessage()); + } + } + + + + +} From 133172f63659ab675fcdd9e18cadf14485fba992 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 16 Oct 2024 10:25:45 +0200 Subject: [PATCH 165/452] basic premium page --- templates/Premium/list.html.twig | 41 ++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 templates/Premium/list.html.twig diff --git a/templates/Premium/list.html.twig b/templates/Premium/list.html.twig new file mode 100644 index 000000000..f135ffbd4 --- /dev/null +++ b/templates/Premium/list.html.twig @@ -0,0 +1,41 @@ +{% extends 'base.html.twig' %} + +{% block title %} + {{ parent() }} | {{ 'premium.upgrade.title_main'|trans }} +{% endblock %} + +{% block titlesm %} + {{ 'premium.upgrade.title_main'|trans }} +{% endblock titlesm %} + +{% block body %} +
    + + + +
    + {% for message in app.flashes('success') %} + + {% endfor %} +
    + +
    +

    {{ 'premium.upgrade.benefits.title'|trans }}

    +
      +
    • {{ 'premium.upgrade.benefit1'|trans }}
    • +
    • {{ 'premium.upgrade.benefit2'|trans }}
    • +
    • {{ 'premium.upgrade.benefit3'|trans }}
    • +
    +
    + +
    + +{% endblock %} + +{% block cssin %}{% endblock cssin %} \ No newline at end of file From 96eb15cca7c8c0e630396fad2e8c9112b5370fa8 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 16 Oct 2024 10:25:51 +0200 Subject: [PATCH 166/452] premium messages --- translations/messages.en.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/translations/messages.en.yml b/translations/messages.en.yml index 15f628f6b..2fb719a9f 100644 --- a/translations/messages.en.yml +++ b/translations/messages.en.yml @@ -1041,4 +1041,16 @@ variable: table_headers: name: Name value: Value - actions: Actions \ No newline at end of file + actions: Actions + +premium: + upgrade: + title_main: "Upgrade to Premium" + title: "Unlock Premium Features" + description: "Get access to exclusive features and support by upgrading to the premium version of Myddleware." + button: "Buy Premium" + benefits: + title: "Premium Benefits" + benefit1: "Benefit 1 description" + benefit2: "Benefit 2 description" + benefit3: "Benefit 3 description" \ No newline at end of file From dbde9e61af5a56dff612fef4420a85f6c6a8368f Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 16 Oct 2024 10:27:42 +0200 Subject: [PATCH 167/452] remove unused components and use statement (for reference they were workflow) --- src/Controller/PremiumController.php | 106 --------------------------- 1 file changed, 106 deletions(-) diff --git a/src/Controller/PremiumController.php b/src/Controller/PremiumController.php index 6b89f1719..894425a7d 100644 --- a/src/Controller/PremiumController.php +++ b/src/Controller/PremiumController.php @@ -26,132 +26,26 @@ namespace App\Controller; use Exception; -use App\Entity\Rule; -use App\Entity\User; -use App\Entity\FuncCat; -use App\Entity\Document; -use App\Entity\Solution; -use App\Entity\Workflow; -use App\Entity\Connector; -use App\Entity\Functions; -use App\Entity\RuleAudit; -use App\Entity\RuleField; -use App\Entity\RuleParam; -use App\Entity\RuleFilter; -use Pagerfanta\Pagerfanta; -use App\Entity\WorkflowLog; -use App\Form\ConnectorType; -use App\Manager\JobManager; -use App\Manager\HomeManager; -use App\Manager\RuleManager; -use Doctrine\ORM\Mapping\Id; -use Psr\Log\LoggerInterface; -use App\Entity\WorkflowAudit; -use App\Manager\ToolsManager; use Doctrine\DBAL\Connection; -use App\Entity\ConnectorParam; -use App\Entity\RuleParamAudit; -use App\Entity\WorkflowAction; -use App\Form\Type\WorkflowType; -use App\Manager\FormulaManager; -use App\Service\SessionService; -use App\Entity\RuleRelationShip; -use App\Manager\DocumentManager; -use App\Manager\SolutionManager; -use App\Manager\TemplateManager; -use App\Repository\JobRepository; -use App\Repository\RuleRepository; -use App\Form\DuplicateRuleFormType; -use App\Repository\ConfigRepository; -use Illuminate\Encryption\Encrypter; -use Pagerfanta\Adapter\ArrayAdapter; -use App\Form\Type\RelationFilterType; -use App\Repository\DocumentRepository; -use App\Repository\WorkflowRepository; -use App\Repository\WorkflowLogRepository; -use Doctrine\ORM\EntityManagerInterface; -use Pagerfanta\Doctrine\ORM\QueryAdapter; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; -use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\RedirectResponse; -use Symfony\Contracts\Translation\TranslatorInterface; -use Symfony\Component\Form\Extension\Core\Type\SubmitType; -use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; -use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; /** * @Route("/premium") */ class PremiumController extends AbstractController { - private FormulaManager $formuleManager; - private SessionService $sessionService; - private ParameterBagInterface $params; - private EntityManagerInterface $entityManager; - private HomeManager $home; - private ToolsManager $tools; - private TranslatorInterface $translator; - private AuthorizationCheckerInterface $authorizationChecker; - private JobManager $jobManager; - private LoggerInterface $logger; - private TemplateManager $template; - private RuleRepository $ruleRepository; - private JobRepository $jobRepository; - private DocumentRepository $documentRepository; - private SolutionManager $solutionManager; - private RuleManager $ruleManager; - private DocumentManager $documentManager; - private WorkflowLogRepository $workflowLogRepository; protected Connection $connection; // To allow sending a specific record ID to rule simulation protected $simulationQueryField; - private ConfigRepository $configRepository; public function __construct( - LoggerInterface $logger, - RuleManager $ruleManager, - FormulaManager $formuleManager, - SolutionManager $solutionManager, - DocumentManager $documentManager, - SessionService $sessionService, - EntityManagerInterface $entityManager, - RuleRepository $ruleRepository, - JobRepository $jobRepository, - DocumentRepository $documentRepository, - Connection $connection, - TranslatorInterface $translator, - AuthorizationCheckerInterface $authorizationChecker, - HomeManager $home, - ToolsManager $tools, - JobManager $jobManager, - TemplateManager $template, - WorkflowLogRepository $workflowLogRepository, - ParameterBagInterface $params ) { - $this->logger = $logger; - $this->ruleManager = $ruleManager; - $this->formuleManager = $formuleManager; - $this->solutionManager = $solutionManager; - $this->documentManager = $documentManager; - $this->sessionService = $sessionService; - $this->entityManager = $entityManager; - $this->ruleRepository = $ruleRepository; - $this->jobRepository = $jobRepository; - $this->documentRepository = $documentRepository; - $this->connection = $connection; - $this->translator = $translator; - $this->authorizationChecker = $authorizationChecker; - $this->home = $home; - $this->tools = $tools; - $this->jobManager = $jobManager; - $this->template = $template; - $this->workflowLogRepository = $workflowLogRepository; } protected function getInstanceBdd() {} From ee40e13906b5d3209a45619908ed4d0c8e2c7803 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 16 Oct 2024 14:23:59 +0200 Subject: [PATCH 168/452] remove extra route --- src/Controller/PremiumController.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Controller/PremiumController.php b/src/Controller/PremiumController.php index 894425a7d..809dbcd1e 100644 --- a/src/Controller/PremiumController.php +++ b/src/Controller/PremiumController.php @@ -58,15 +58,10 @@ protected function getInstanceBdd() {} * @return RedirectResponse|Response * * @Route("/list", name="premium_list", defaults={"page"=1}) - * @Route("/list/page-{page}", name="premium_list_page", requirements={"page"="\d+"}) */ public function PremiumListAction(int $page = 1, Request $request) { try { - - - - // Si ce n'est pas une requête AJAX, rendre la page complète return $this->render( 'premium/list.html.twig', From 05d4069f6f4c8b47d4504fa72aa1f9cc5a918489 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 16 Oct 2024 14:26:38 +0200 Subject: [PATCH 169/452] add check with is premium() --- src/Controller/WorkflowController.php | 48 +++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/Controller/WorkflowController.php b/src/Controller/WorkflowController.php index 06d0f834f..5babd25d0 100644 --- a/src/Controller/WorkflowController.php +++ b/src/Controller/WorkflowController.php @@ -172,6 +172,9 @@ protected function getInstanceBdd() {} public function WorkflowListAction(int $page = 1, Request $request) { try { + if (($this->tools->isPremium())) { + return $this->redirectToRoute('premium_list'); + } // Récupérer les filtres depuis la requête $workflowName = $request->query->get('workflow_name'); @@ -229,6 +232,11 @@ public function WorkflowListAction(int $page = 1, Request $request) public function WorkflowListByRuleAction(string $ruleId, int $page = 1, Request $request) { try { + + if (($this->tools->isPremium())) { + return $this->redirectToRoute('premium_list'); + } + // Récupération des workflows par règle $workflows = $this->entityManager->getRepository(Workflow::class)->findBy( ['rule' => $ruleId, 'deleted' => 0], @@ -260,6 +268,11 @@ public function WorkflowListByRuleAction(string $ruleId, int $page = 1, Request public function WorkflowDeleteAction(string $id, Request $request) { try { + + if (($this->tools->isPremium())) { + return $this->redirectToRoute('premium_list'); + } + $em = $this->getDoctrine()->getManager(); $workflowSearchResult = $em->getRepository(Workflow::class)->findBy(['id' => $id, 'deleted' => 0]); $workflow = $workflowSearchResult[0]; @@ -285,6 +298,11 @@ public function WorkflowDeleteAction(string $id, Request $request) public function saveWorkflowAudit($workflowId) { + if (($this->tools->isPremium())) { + return $this->redirectToRoute('premium_list'); + } + + $em = $this->getDoctrine()->getManager(); $workflowArray = $em->getRepository(Workflow::class)->findBy(['id' => $workflowId, 'deleted' => 0]); $workflow = $workflowArray[0]; @@ -340,6 +358,11 @@ public function saveWorkflowAudit($workflowId) public function WorkflowActiveAction(string $id, Request $request) { try { + + if (($this->tools->isPremium())) { + return $this->redirectToRoute('premium_list'); + } + $em = $this->getDoctrine()->getManager(); $workflowResult = $em->getRepository(Workflow::class)->findBy(['id' => $id, 'deleted' => 0]); $workflow = $workflowResult[0]; @@ -366,6 +389,11 @@ public function WorkflowActiveAction(string $id, Request $request) public function WorkflowActiveShowAction(string $id, Request $request) { try { + + if (($this->tools->isPremium())) { + return $this->redirectToRoute('premium_list'); + } + $em = $this->getDoctrine()->getManager(); $workflowResult = $em->getRepository(Workflow::class)->findBy(['id' => $id, 'deleted' => 0]); $workflow = $workflowResult[0]; @@ -389,6 +417,11 @@ public function WorkflowActiveShowAction(string $id, Request $request) #[Route('/workflow/toggle/{id}', name: 'workflow_toggle', methods: ['POST'])] public function toggleWorkflow(Request $request, EntityManagerInterface $em, WorkflowRepository $workflowRepository, string $id): JsonResponse { + + if (($this->tools->isPremium())) { + return $this->redirectToRoute('premium_list'); + } + $workflow = $workflowRepository->find($id); if (!$workflow) { @@ -416,6 +449,11 @@ public function WorkflowCreateAction(Request $request) { try { + if (($this->tools->isPremium())) { + return $this->redirectToRoute('premium_list'); + } + + $rules = RuleRepository::findActiveRulesNames($this->entityManager); $em = $this->getDoctrine()->getManager(); @@ -458,6 +496,11 @@ public function WorkflowCreateAction(Request $request) public function WorkflowShowAction(string $id, Request $request, int $page): Response { try { + + if (($this->tools->isPremium())) { + return $this->redirectToRoute('premium_list'); + } + $em = $this->getDoctrine()->getManager(); $workflow = $em->getRepository(Workflow::class)->findBy(['id' => $id, 'deleted' => 0]); @@ -500,6 +543,11 @@ public function WorkflowShowAction(string $id, Request $request, int $page): Res public function WorkflowEditAction(string $id, Request $request) { try { + + if (($this->tools->isPremium())) { + return $this->redirectToRoute('premium_list'); + } + $em = $this->getDoctrine()->getManager(); $workflowArray = $em->getRepository(Workflow::class)->findBy(['id' => $id, 'deleted' => 0]); $workflow = $workflowArray[0]; From 2a5fd11bbcb91dee82420d7d5f061c1fde172ebe Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 16 Oct 2024 16:10:22 +0200 Subject: [PATCH 170/452] reverse condition --- src/Controller/WorkflowController.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Controller/WorkflowController.php b/src/Controller/WorkflowController.php index 5babd25d0..8c633313c 100644 --- a/src/Controller/WorkflowController.php +++ b/src/Controller/WorkflowController.php @@ -172,7 +172,7 @@ protected function getInstanceBdd() {} public function WorkflowListAction(int $page = 1, Request $request) { try { - if (($this->tools->isPremium())) { + if (!($this->tools->isPremium())) { return $this->redirectToRoute('premium_list'); } @@ -233,7 +233,7 @@ public function WorkflowListByRuleAction(string $ruleId, int $page = 1, Request { try { - if (($this->tools->isPremium())) { + if (!($this->tools->isPremium())) { return $this->redirectToRoute('premium_list'); } @@ -269,7 +269,7 @@ public function WorkflowDeleteAction(string $id, Request $request) { try { - if (($this->tools->isPremium())) { + if (!($this->tools->isPremium())) { return $this->redirectToRoute('premium_list'); } @@ -298,7 +298,7 @@ public function WorkflowDeleteAction(string $id, Request $request) public function saveWorkflowAudit($workflowId) { - if (($this->tools->isPremium())) { + if (!($this->tools->isPremium())) { return $this->redirectToRoute('premium_list'); } @@ -359,7 +359,7 @@ public function WorkflowActiveAction(string $id, Request $request) { try { - if (($this->tools->isPremium())) { + if (!($this->tools->isPremium())) { return $this->redirectToRoute('premium_list'); } @@ -390,7 +390,7 @@ public function WorkflowActiveShowAction(string $id, Request $request) { try { - if (($this->tools->isPremium())) { + if (!($this->tools->isPremium())) { return $this->redirectToRoute('premium_list'); } @@ -418,7 +418,7 @@ public function WorkflowActiveShowAction(string $id, Request $request) public function toggleWorkflow(Request $request, EntityManagerInterface $em, WorkflowRepository $workflowRepository, string $id): JsonResponse { - if (($this->tools->isPremium())) { + if (!($this->tools->isPremium())) { return $this->redirectToRoute('premium_list'); } @@ -449,7 +449,7 @@ public function WorkflowCreateAction(Request $request) { try { - if (($this->tools->isPremium())) { + if (!($this->tools->isPremium())) { return $this->redirectToRoute('premium_list'); } @@ -497,7 +497,7 @@ public function WorkflowShowAction(string $id, Request $request, int $page): Res { try { - if (($this->tools->isPremium())) { + if (!($this->tools->isPremium())) { return $this->redirectToRoute('premium_list'); } @@ -544,7 +544,7 @@ public function WorkflowEditAction(string $id, Request $request) { try { - if (($this->tools->isPremium())) { + if (!($this->tools->isPremium())) { return $this->redirectToRoute('premium_list'); } From 49c1cde6309729569e9f8085d3f710040e129657 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 16 Oct 2024 18:14:58 +0200 Subject: [PATCH 171/452] remove echo toto --- src/Controller/FilterController.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Controller/FilterController.php b/src/Controller/FilterController.php index 85262f87b..519ac0559 100644 --- a/src/Controller/FilterController.php +++ b/src/Controller/FilterController.php @@ -189,8 +189,6 @@ public function removeFilter(Request $request): JsonResponse */ public function testFilterAction(Request $request, int $page = 1, int $search = 1): Response { - - $toto = 'toto'; $formFilter = $this->createForm(FilterType::class, null); $form = $this->createForm(CombinedFilterType::class, null, [ 'entityManager' => $this->entityManager, From 54bec7aeec06bd48dd35471bc12ab13fa6d8170c Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 16 Oct 2024 18:15:31 +0200 Subject: [PATCH 172/452] add premium check --- src/Controller/JobSchedulerController.php | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Controller/JobSchedulerController.php b/src/Controller/JobSchedulerController.php index 0faef5b64..fb3388440 100644 --- a/src/Controller/JobSchedulerController.php +++ b/src/Controller/JobSchedulerController.php @@ -24,6 +24,8 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Pagerfanta\Doctrine\ORM\QueryAdapter; use Pagerfanta\Pagerfanta; +use App\Manager\ToolsManager; + /** * @Route("/rule/jobscheduler") @@ -32,11 +34,13 @@ class JobSchedulerController extends AbstractController { private JobSchedulerManager $jobSchedulerManager; private EntityManagerInterface $entityManager; + private ToolsManager $tools; - public function __construct(EntityManagerInterface $entityManager, JobSchedulerManager $jobSchedulerManager) + public function __construct(EntityManagerInterface $entityManager, JobSchedulerManager $jobSchedulerManager,ToolsManager $tools) { $this->entityManager = $entityManager; $this->jobSchedulerManager = $jobSchedulerManager; + $this->tools = $tools; } /** @@ -44,6 +48,12 @@ public function __construct(EntityManagerInterface $entityManager, JobSchedulerM */ public function index(): Response { + + if (!($this->tools->isPremium())) { + return $this->redirectToRoute('premium_list'); + } + + $entities = $this->entityManager->getRepository(JobScheduler::class)->findBy([], ['jobOrder' => 'ASC']); return $this->render('JobScheduler/index.html.twig', [ @@ -151,7 +161,6 @@ public function show($id): Response public function edit($id): Response { $entity = $this->entityManager->getRepository(JobScheduler::class)->find($id); - if (!$entity) { throw $this->createNotFoundException('Unable to find JobScheduler entity.'); } @@ -313,6 +322,13 @@ public function createActionWithCron(Request $request, TranslatorInterface $tran */ public function crontabList(int $page): Response { + + if (!($this->tools->isPremium())) { + return $this->redirectToRoute('premium_list'); + } + + + //Check if crontab is enabled $entitiesCron = $this->entityManager->getRepository(Config::class)->findBy(['name' => 'cron_enabled']); $searchLimit = $this->entityManager->getRepository(Config::class)->findOneBy(['name' => 'search_limit'])->getValue(); From ff3da7e1977a8243bc7084ebbb376fe7995792a0 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 16 Oct 2024 18:19:17 +0200 Subject: [PATCH 173/452] use pagerfanta() to generate pagination --- templates/Workflow/show.html.twig | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/templates/Workflow/show.html.twig b/templates/Workflow/show.html.twig index 17f681462..4aa4c7e29 100644 --- a/templates/Workflow/show.html.twig +++ b/templates/Workflow/show.html.twig @@ -246,34 +246,10 @@ {% endfor %} + {% if pager.haveToPaginate %} - - {% endif %} -
  • -
    -
    + {{ pagerfanta(pager, 'twitter_bootstrap4', {'routeName': 'workflow_show_page', 'routeParams': {'id': workflow.id}}) }} + {% endif %} {% block js %} From cfee9734007ca7bce717ce906c7067ebf81698ab Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Thu, 17 Oct 2024 09:40:32 +0200 Subject: [PATCH 174/452] modify task template, set value from translations --- templates/base.html.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/base.html.twig b/templates/base.html.twig index 31cfbf22a..28578a3b9 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -133,14 +133,14 @@
  • - {{'menu.flux.list'|trans}} + {{'menu.task.title'|trans}}
  • - {{'Stop all the Tasks'}} + {{'menu.task.stopall' |trans}}
  • From 1c596afcb2ec442b92931f6b3ba71abc5e68b487 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Thu, 17 Oct 2024 09:40:48 +0200 Subject: [PATCH 175/452] change translations, ticket 375 https://myddleware.atlassian.net/browse/PROJ-375 --- translations/messages.en.yml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/translations/messages.en.yml b/translations/messages.en.yml index 2fb719a9f..78ad3b6a8 100644 --- a/translations/messages.en.yml +++ b/translations/messages.en.yml @@ -722,24 +722,27 @@ menu: rule: title: Rules createv1: Creation (v1) - createv2: Creation - list: List + createv2: Create Rule + list: Rules connector: title: Connectors - create: Creation - list: List + create: Create Connector + list: Connectors flux: title: Documents list: List variable: title: Documents - list: List of variables + list: Variables job: title: Tasks list: List support: Support workflow: - list: List of Workflows + list: Workflows + task: + title: Tasks + stopall: Stop all tasks breadcrumb: rule_list: Rules list From 575212c0a7feacef5e68d5ece32799617445c046 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Thu, 17 Oct 2024 09:41:59 +0200 Subject: [PATCH 176/452] taches fr --- translations/messages.fr.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/translations/messages.fr.yml b/translations/messages.fr.yml index e859aea3a..c9e6bb9b7 100644 --- a/translations/messages.fr.yml +++ b/translations/messages.fr.yml @@ -739,6 +739,9 @@ menu: workflow: list: Liste des workflows + task: + title: Tâches + stopall: Arrêter toutes les tâches breadcrumb: rule_list: Liste des règles transfert_list: Liste des documents From af8058efd74012b31896e244e6f36675b5d5c4ed Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Thu, 17 Oct 2024 09:44:23 +0200 Subject: [PATCH 177/452] 375 traductions francaises --- translations/messages.fr.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/translations/messages.fr.yml b/translations/messages.fr.yml index c9e6bb9b7..9528ddf84 100644 --- a/translations/messages.fr.yml +++ b/translations/messages.fr.yml @@ -720,25 +720,25 @@ menu: rule: title: Règles createv1: Création (v1) - createv2: Création - list: Liste + createv2: Créer une règle + list: Règles connector: title: Connecteurs - create: Création - list: Liste + create: Créer un connecteur + list: Connecteurs flux: title: Documents list: Liste variable: title: Documents - list: Liste des variables + list: Variables job: title: Tâches list: Liste support: Support workflow: - list: Liste des workflows + list: Workflows task: title: Tâches stopall: Arrêter toutes les tâches From 5b244ea91391e93f6156267279696572dcd0d2f6 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Thu, 17 Oct 2024 11:38:45 +0200 Subject: [PATCH 178/452] fix form for crontab https://myddleware.atlassian.net/browse/PROJ-320 --- src/Controller/JobSchedulerController.php | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/Controller/JobSchedulerController.php b/src/Controller/JobSchedulerController.php index fb3388440..cfa10455d 100644 --- a/src/Controller/JobSchedulerController.php +++ b/src/Controller/JobSchedulerController.php @@ -435,12 +435,37 @@ public function updateCrontab(Request $request, $id) if (!$entity) { throw $this->createNotFoundException('Unable to find Crontab entity.'); } + + // Get the current running instances from the $entity + $currentRunningInstances = $entity->getRunningInstances(); + $currentMaxInstances = $entity->getMaxInstances(); + $currentMaxInstancesString = (string) $currentMaxInstances; + + $currentRunningInstancesString = (string) $currentRunningInstances; + + // get the new value of the running instances from the request + $newRunningInstances = $request->request->get('job_scheduler_cron')['runningInstances']; + + $newRunningInstancesInteger = (int) $newRunningInstances; + + // if the new value is different from the current value, update the request to not update the running instances + if ($currentRunningInstances !== $newRunningInstancesInteger) { + $request->request->set('job_scheduler_cron', ['runningInstances' => $currentRunningInstancesString, 'maxInstances' => $currentMaxInstancesString, "period" => $entity->getPeriod(), "command" => $entity->getCommand(), "description" => $entity->getDescription()]); + } + $editForm = $this->createEditFormCrontab($entity); $editForm->handleRequest($request); if ($editForm->isSubmitted() && $editForm->isValid()) { $this->entityManager->flush(); + return $this->redirect($this->generateUrl('jobscheduler_cron_list')); + } else if ($editForm->isSubmitted() && !($editForm->isValid())) { + // do an sql statement to update the running instances + $this->entityManager->getConnection()->executeQuery('UPDATE cron_job SET running_instances = :running_instances WHERE id = :id', ['running_instances' => $newRunningInstances, 'id' => $id]); + $this->entityManager->flush(); + + // redirect to the show view return $this->redirect($this->generateUrl('jobscheduler_cron_list')); } From ab2a675819343bfcf64118b19abc46771af4205f Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Thu, 17 Oct 2024 11:47:08 +0200 Subject: [PATCH 179/452] refactor current running instances --- src/Controller/JobSchedulerController.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Controller/JobSchedulerController.php b/src/Controller/JobSchedulerController.php index cfa10455d..881abbd13 100644 --- a/src/Controller/JobSchedulerController.php +++ b/src/Controller/JobSchedulerController.php @@ -437,11 +437,10 @@ public function updateCrontab(Request $request, $id) } // Get the current running instances from the $entity - $currentRunningInstances = $entity->getRunningInstances(); $currentMaxInstances = $entity->getMaxInstances(); $currentMaxInstancesString = (string) $currentMaxInstances; - $currentRunningInstancesString = (string) $currentRunningInstances; + $currentRunningInstancesString = (string) $entity->getRunningInstances(); // get the new value of the running instances from the request $newRunningInstances = $request->request->get('job_scheduler_cron')['runningInstances']; @@ -449,7 +448,7 @@ public function updateCrontab(Request $request, $id) $newRunningInstancesInteger = (int) $newRunningInstances; // if the new value is different from the current value, update the request to not update the running instances - if ($currentRunningInstances !== $newRunningInstancesInteger) { + if ($entity->getRunningInstances() !== $newRunningInstancesInteger) { $request->request->set('job_scheduler_cron', ['runningInstances' => $currentRunningInstancesString, 'maxInstances' => $currentMaxInstancesString, "period" => $entity->getPeriod(), "command" => $entity->getCommand(), "description" => $entity->getDescription()]); } From 039ab946ee3b928814a237c8c80b8ef6315aa44c Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Thu, 17 Oct 2024 11:47:56 +0200 Subject: [PATCH 180/452] refactor current max intstances --- src/Controller/JobSchedulerController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Controller/JobSchedulerController.php b/src/Controller/JobSchedulerController.php index 881abbd13..35e05b2ff 100644 --- a/src/Controller/JobSchedulerController.php +++ b/src/Controller/JobSchedulerController.php @@ -437,8 +437,7 @@ public function updateCrontab(Request $request, $id) } // Get the current running instances from the $entity - $currentMaxInstances = $entity->getMaxInstances(); - $currentMaxInstancesString = (string) $currentMaxInstances; + $currentMaxInstancesString = (string) $entity->getMaxInstances(); $currentRunningInstancesString = (string) $entity->getRunningInstances(); From 0bb7e45283c279d0ff11d098cb34c59e474ab7a9 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Thu, 17 Oct 2024 12:01:11 +0200 Subject: [PATCH 181/452] get other variables because not updated if changing several --- src/Controller/JobSchedulerController.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Controller/JobSchedulerController.php b/src/Controller/JobSchedulerController.php index 35e05b2ff..897c6b1f5 100644 --- a/src/Controller/JobSchedulerController.php +++ b/src/Controller/JobSchedulerController.php @@ -446,6 +446,11 @@ public function updateCrontab(Request $request, $id) $newRunningInstancesInteger = (int) $newRunningInstances; + $newDescription = $request->request->get('job_scheduler_cron')['description']; + $newPeriod = $request->request->get('job_scheduler_cron')['period']; + $newMaxInstances = $request->request->get('job_scheduler_cron')['maxInstances']; + + // if the new value is different from the current value, update the request to not update the running instances if ($entity->getRunningInstances() !== $newRunningInstancesInteger) { $request->request->set('job_scheduler_cron', ['runningInstances' => $currentRunningInstancesString, 'maxInstances' => $currentMaxInstancesString, "period" => $entity->getPeriod(), "command" => $entity->getCommand(), "description" => $entity->getDescription()]); @@ -460,7 +465,18 @@ public function updateCrontab(Request $request, $id) return $this->redirect($this->generateUrl('jobscheduler_cron_list')); } else if ($editForm->isSubmitted() && !($editForm->isValid())) { // do an sql statement to update the running instances - $this->entityManager->getConnection()->executeQuery('UPDATE cron_job SET running_instances = :running_instances WHERE id = :id', ['running_instances' => $newRunningInstances, 'id' => $id]); + $this->entityManager->getConnection()->executeQuery('UPDATE cron_job SET running_instances = :running_instances, + max_instances = :max_instances, + period = :period, + description = :description + + WHERE id = :id', + ['running_instances' => $newRunningInstances, + 'id' => $id, + 'max_instances' => $newMaxInstances, + 'period' => $newPeriod, + 'description' => $newDescription + ]); $this->entityManager->flush(); // redirect to the show view From 30acd9e3b1fbedc5a4a4ceeb3a33fc5be1074506 Mon Sep 17 00:00:00 2001 From: myddleware Date: Thu, 17 Oct 2024 13:42:47 +0200 Subject: [PATCH 182/452] New feature : rule group Signed-off-by: myddleware --- config/services_custom.yaml | 32 +++--- src/Command/CronRunCommand.php | 11 +- src/Command/MassActionCommand.php | 4 +- src/Command/SynchroCommand.php | 21 +++- src/Entity/Rule.php | 18 ++++ src/Entity/RuleGroup.php | 173 ++++++++++++++++++++++++++++++ src/Manager/JobManager.php | 30 ++++-- src/Manager/ToolsManager.php | 4 + 8 files changed, 263 insertions(+), 30 deletions(-) create mode 100644 src/Entity/RuleGroup.php diff --git a/config/services_custom.yaml b/config/services_custom.yaml index d30de229e..5a8bda1f3 100644 --- a/config/services_custom.yaml +++ b/config/services_custom.yaml @@ -24,22 +24,22 @@ services: # decoration_on_invalid: ignore # decoration_priority: 1 - App\Custom\Premium\Solutions\mysqlpremium: - decorates: App\Solutions\mysql - decoration_on_invalid: ignore - decoration_priority: 1 + # App\Custom\Premium\Solutions\mysqlpremium: + # decorates: App\Solutions\mysql + # decoration_on_invalid: ignore + # decoration_priority: 1 - App\Custom\Solutions\mysqlcustom: - decorates: App\Custom\Premium\Solutions\mysqlpremium - decoration_on_invalid: ignore - decoration_priority: 1 + # App\Custom\Solutions\mysqlcustom: + # decorates: App\Custom\Premium\Solutions\mysqlpremium + # decoration_on_invalid: ignore + # decoration_priority: 1 - App\Custom\Premium\Manager\DocumentManagerPremium: - decorates: App\Manager\DocumentManager - decoration_on_invalid: ignore - decoration_priority: 1 + # App\Custom\Premium\Manager\DocumentManagerPremium: + # decorates: App\Manager\DocumentManager + # decoration_on_invalid: ignore + # decoration_priority: 1 - App\Custom\Premium\Manager\ToolsManagerPremium: - decorates: App\Manager\ToolsManager - decoration_on_invalid: ignore - decoration_priority: 1 \ No newline at end of file + # App\Custom\Premium\Manager\ToolsManagerPremium: + # decorates: App\Manager\ToolsManager + # decoration_on_invalid: ignore + # decoration_priority: 1 \ No newline at end of file diff --git a/src/Command/CronRunCommand.php b/src/Command/CronRunCommand.php index 3fd171cb2..ba1ff4b0b 100644 --- a/src/Command/CronRunCommand.php +++ b/src/Command/CronRunCommand.php @@ -23,21 +23,27 @@ use App\Entity\Config; use Doctrine\ORM\EntityManagerInterface; +use App\Manager\ToolsManager; + final class CronRunCommand extends BaseCommand { private CommandHelper $commandHelper; //protected $configParams; protected EntityManagerInterface $entityManager; + + private ToolsManager $toolsManager; public function __construct( CommandHelper $commandHelper, ManagerRegistry $registry, - EntityManagerInterface $entityManager + EntityManagerInterface $entityManager, + ToolsManager $toolsManager, ) { parent::__construct($registry); $this->commandHelper = $commandHelper; $this->entityManager = $entityManager; + $this->toolsManager = $toolsManager; } protected function configure(): void @@ -49,6 +55,9 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { + if (!$this->toolsManager->isPremium()) { + throw new Exception("Command cronrun only available with the entreprise package. "); + } $jobRepo = $this->getCronJobRepository(); $style = new CronStyle($input, $output); //Check if crontab is enabled diff --git a/src/Command/MassActionCommand.php b/src/Command/MassActionCommand.php index 6a5295b5f..41a2365a5 100644 --- a/src/Command/MassActionCommand.php +++ b/src/Command/MassActionCommand.php @@ -133,8 +133,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (!in_array($action, ['rerun', 'cancel', 'remove', 'restore', 'changeStatus', 'unlock', 'rerunWorkflow'])) { throw new Exception('Action '.$action.' unknown. Please use action rerun, cancel, remove, restore, changeStatus, unlock or rerunWorkflow.'); } - if (!in_array($dataType, ['document', 'rule'])) { - throw new Exception('Data type '.$dataType.' unknown. Please use data type document or rule.'); + if (!in_array($dataType, ['document', 'rule', 'group'])) { + throw new Exception('Data type '.$dataType.' unknown. Please use data type document, group or rule.'); } if (empty($ids)) { throw new Exception('No ids in the command parameters. Please add ids to run this action.'); diff --git a/src/Command/SynchroCommand.php b/src/Command/SynchroCommand.php index b141366a0..faa1bb605 100644 --- a/src/Command/SynchroCommand.php +++ b/src/Command/SynchroCommand.php @@ -28,6 +28,7 @@ use App\Manager\DocumentManager; use App\Manager\JobManager; use App\Manager\RuleManager; +use App\Manager\ToolsManager; use App\Repository\DocumentRepository; use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; @@ -45,6 +46,7 @@ class SynchroCommand extends Command private DocumentManager $documentManager; private RuleManager $ruleManager; private DocumentRepository $documentRepository; + private ToolsManager $toolsManager; // the name of the command (the part after "bin/console") protected static $defaultName = 'myddleware:synchro'; @@ -56,7 +58,8 @@ public function __construct( RuleManager $ruleManager, EntityManagerInterface $entityManager, DocumentRepository $documentRepository, - ManagerRegistry $registry + ManagerRegistry $registry, + ToolsManager $toolsManager, ) { parent::__construct(); $this->logger = $logger; @@ -66,6 +69,7 @@ public function __construct( $this->documentManager = $documentManager; $this->documentRepository = $documentRepository; $this->registry = $registry; + $this->toolsManager = $toolsManager; } protected function configure() @@ -110,7 +114,20 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ('ALL' == $rule) { $rules = $this->jobManager->getRules($force); } else { - $rules = explode(',',$rule); + //Check if the parameter is a rule group + if ($this->toolsManager->isPremium()) { + // Get the rules from the group + $rulesGroup = $this->toolsManager->getRulesFromGroup($rule, $force); + if (!empty($rulesGroup)) { + foreach($rulesGroup as $ruleGroup) { + $rules[] = $ruleGroup['name_slug']; + } + } + } + // If the parameter isn't a group + if (empty($rules)) { + $rules = explode(',',$rule); + } } if (!empty($rules)) { foreach ($rules as $key => $value) { diff --git a/src/Entity/Rule.php b/src/Entity/Rule.php index a012cae37..78a024ae1 100755 --- a/src/Entity/Rule.php +++ b/src/Entity/Rule.php @@ -169,6 +169,12 @@ class Rule * @ORM\OrderBy({"sourceDateModified" : "ASC"}) */ private $documents; + + /** + * @ORM\ManyToOne(targetEntity="RuleGroup") + * @ORM\JoinColumn(name="group_id", referencedColumnName="id") + */ + private RuleGroup $group; /** * @ORM\Column(name="read_job_lock", type="string", length=23, nullable=true, options={"default":NULL}) @@ -733,5 +739,17 @@ public function isModuleTargetSet(): bool { return isset($this->moduleTarget); } + + public function setGroup(RuleGroup $group): self + { + $this->group = $group; + + return $this; + } + + public function getGroup(): RuleGroup + { + return $this->group; + } } diff --git a/src/Entity/RuleGroup.php b/src/Entity/RuleGroup.php new file mode 100644 index 000000000..4bf08fba6 --- /dev/null +++ b/src/Entity/RuleGroup.php @@ -0,0 +1,173 @@ +. +*********************************************************************************/ + +namespace App\Entity; + +use DateTime; +use Doctrine\ORM\Mapping as ORM; +use Exception; + +/** + * @ORM\Table() + * @ORM\Entity(repositoryClass="App\Repository\RuleGroupRepository") + * @ORM\Table(name="rulegroup") + */ +class RuleGroup +{ + + /** + * @ORM\Id + * @ORM\Column(type="string") + */ + private $id; + + /** + * @ORM\Column(name="name", type="text", nullable=false) + */ + private string $name; + /** + * @ORM\Column(name="date_created", type="datetime", nullable=false) + */ + + private $dateCreated; + + /** + * @ORM\Column(name="date_modified", type="datetime", nullable=false) + */ + private DateTime $dateModified; + + /** + * @ORM\ManyToOne(targetEntity="User") + * @ORM\JoinColumn(name="created_by", referencedColumnName="id", nullable=false) + */ + private User $createdBy; + + /** + * @ORM\ManyToOne(targetEntity="User") + * @ORM\JoinColumn(name="modified_by", referencedColumnName="id", nullable=false) + */ + private User $modifiedBy; + + /** + * @ORM\Column(name="description", type="text", nullable=true) + */ + private string $description; + + /** + * @ORM\Column(name="deleted", type="boolean", options={"default":0}) + */ + private int $deleted; + + + public function getId(): string + { + return $this->id; + } + + public function setId(string $id): self + { + $this->id = $id; + + return $this; + } + + public function setDateCreated($dateCreated): self + { + $this->dateCreated = $dateCreated; + return $this; + } + + public function getDateCreated(): DateTime + { + return $this->dateCreated; + } + + public function setDateModified($dateModified): self + { + $this->dateModified = $dateModified; + return $this; + } + + public function getDateModified(): DateTime + { + return $this->dateModified; + } + + public function getCreatedBy(): ?User + { + return $this->createdBy; + } + + public function setCreatedBy(?User $createdBy): self + { + $this->createdBy = $createdBy; + return $this; + } + + public function getModifiedBy(): ?User + { + return $this->modifiedBy; + } + + public function setModifiedBy(?User $modifiedBy): self + { + $this->modifiedBy = $modifiedBy; + return $this; + } + + public function setName($name): self + { + $this->name = $name; + return $this; + } + + public function getName(): string + { + return $this->name; + } + + public function setDescription($description): self + { + $this->description = $description; + return $this; + } + + public function getDescription(): string + { + return $this->description; + } + + public function setDeleted($deleted): self + { + $this->deleted = $deleted; + return $this; + } + + public function getDeleted(): int + { + return $this->deleted; + } + +} diff --git a/src/Manager/JobManager.php b/src/Manager/JobManager.php index 56186d2c1..ec3c45c41 100644 --- a/src/Manager/JobManager.php +++ b/src/Manager/JobManager.php @@ -55,7 +55,7 @@ class jobcore protected $container; protected DriverConnection $connection; protected LoggerInterface $logger; - protected ToolsManager $tools; + protected ToolsManager $toolsManager; protected RuleManager $ruleManager; protected $ruleId; @@ -116,7 +116,7 @@ public function __construct( LogRepository $logRepository, RouterInterface $router, SessionInterface $session, - ToolsManager $tools, + ToolsManager $toolsManager, RuleManager $ruleManager, TemplateManager $templateManager, UpgradeManager $upgrade @@ -130,7 +130,7 @@ public function __construct( $this->logRepository = $logRepository; $this->router = $router; $this->session = $session; - $this->tools = $tools; + $this->toolsManager = $toolsManager; $this->ruleManager = $ruleManager; $this->upgrade = $upgrade; $this->templateManager = $templateManager; @@ -399,7 +399,7 @@ public function runBackgroundJob($job, $param) } } // Get the php executable - $php = $this->tools->getPhpVersion(); + $php = $this->toolsManager->getPhpVersion(); //Create your own folder in the cache directory $fileTmp = $this->parameterBagInterface->get('kernel.cache_dir').'/myddleware/job/'.$guid.'.txt'; @@ -438,7 +438,7 @@ public function runBackgroundJob($job, $param) // Renvoie du message en session $session = new Session(); - $session->set('info', [''.$this->tools->getTranslation(['session', 'task', 'msglink']).'. '.$this->tools->getTranslation(['session', 'task', 'msginfo'])]); + $session->set('info', [''.$this->toolsManager->getTranslation(['session', 'task', 'msglink']).'. '.$this->toolsManager->getTranslation(['session', 'task', 'msginfo'])]); return $idJob; } catch (Exception $e) { @@ -457,11 +457,23 @@ public function massAction($action, $dataType, $ids, $forceAll, $fromStatus, $to if (empty($ids)) { throw new Exception('No ids in the input parameter of the function massAction.'); } + //Check if the parameter is a rule group + if ($this->toolsManager->isPremium()) { + // Get the rules from the group + $rulesGroup = $this->toolsManager->getRulesFromGroup(implode(',',$ids)); + if (!empty($rulesGroup)) { + // Recalculate the array ids + $ids = array(); + foreach($rulesGroup as $ruleGroup) { + $ids[] = $ruleGroup['id']; + } + } + } + // Build IN parameter - // $idsDocArray = explode(',',$ids); $queryIn = '('; - foreach ($ids as $idDoc) { - $queryIn .= "'".$idDoc."',"; + foreach ($ids as $recordId) { + $queryIn .= "'".$recordId."',"; } $queryIn = rtrim($queryIn, ','); $queryIn .= ')'; @@ -469,7 +481,7 @@ public function massAction($action, $dataType, $ids, $forceAll, $fromStatus, $to // Buid WHERE section // Filter on rule or docuement depending on the data type $where = ' WHERE '; - if ('rule' == $dataType) { + if (in_array($dataType, array('rule','group'))) { $where .= " rule.id IN $queryIn "; } elseif ('document' == $dataType) { $where .= " document.id IN $queryIn "; diff --git a/src/Manager/ToolsManager.php b/src/Manager/ToolsManager.php index e0495bc49..862e45c01 100644 --- a/src/Manager/ToolsManager.php +++ b/src/Manager/ToolsManager.php @@ -256,6 +256,10 @@ public function sendMessage($to, $subject, $message) { public function isPremium() { } + + public function getRulesFromGroup($ruleGroup, $force = false) { + return array(); + } } class ToolsManager extends toolscore From 91d3d614ae8134b3fb1812f5c97d4073eae1cf78 Mon Sep 17 00:00:00 2001 From: myddleware Date: Thu, 17 Oct 2024 19:59:01 +0200 Subject: [PATCH 183/452] Fix : Manage reference date when it has been changed without any result Signed-off-by: myddleware --- src/Manager/RuleManager.php | 8 +++++++- src/Solutions/sendinblue.php | 1 - 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Manager/RuleManager.php b/src/Manager/RuleManager.php index 1894676a9..67206750f 100644 --- a/src/Manager/RuleManager.php +++ b/src/Manager/RuleManager.php @@ -446,7 +446,13 @@ public function createDocuments() } // Mise à jour de la date de référence si des documents ont été créés $this->updateReferenceDate(); - } + // In case there is no result but the reference date has changed (e.g. stat reding from Brevo) + } elseif ( + $readSource['count'] == 0 + AND $readSource['date_ref'] != $this->ruleParams['datereference'] + ) { + $this->updateReferenceDate(); + } // If params has been added in the output of the rule we saved it $this->updateParams(); diff --git a/src/Solutions/sendinblue.php b/src/Solutions/sendinblue.php index ffa4b5e02..1757a364d 100644 --- a/src/Solutions/sendinblue.php +++ b/src/Solutions/sendinblue.php @@ -538,7 +538,6 @@ public function EmailTransactional($param): \SendinBlue\Client\Model\GetEmailEve try { $result = $apiInstance->getEmailEventReport($limit, $offset, $startDate, $endDate, $messageId, $templateId); - print_r($result); } catch (\Exception $e) { echo 'Exception when calling TransactionalEmailsApi->getEmailEventReport: ', $e->getMessage(); } From d2dfdfe6060e83f78577cbe2eec5498fa40ba7c2 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Fri, 18 Oct 2024 11:45:22 +0200 Subject: [PATCH 184/452] add a working link --- templates/Workflow/show.html.twig | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/templates/Workflow/show.html.twig b/templates/Workflow/show.html.twig index 4aa4c7e29..a44fdca4f 100644 --- a/templates/Workflow/show.html.twig +++ b/templates/Workflow/show.html.twig @@ -247,9 +247,29 @@ - {% if pager.haveToPaginate %} - {{ pagerfanta(pager, 'twitter_bootstrap4', {'routeName': 'workflow_show_page', 'routeParams': {'id': workflow.id}}) }} - {% endif %} + {% if pager.haveToPaginate %} + {{ pagerfanta(pager, 'twitter_bootstrap4', {'routeName': 'workflow_show_page', 'routeParams': {'id': workflow.id, 'page': app.request.get('page', 1)}}) }} + +
    +
    + + +
    + +
    + + + {% endif %} {% block js %} From 032be703d9ccb820f5059ae4481db7a2f467eabc Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Fri, 18 Oct 2024 12:01:58 +0200 Subject: [PATCH 185/452] add styling --- templates/Workflow/show.html.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/Workflow/show.html.twig b/templates/Workflow/show.html.twig index a44fdca4f..1b7f62a43 100644 --- a/templates/Workflow/show.html.twig +++ b/templates/Workflow/show.html.twig @@ -251,11 +251,11 @@ {{ pagerfanta(pager, 'twitter_bootstrap4', {'routeName': 'workflow_show_page', 'routeParams': {'id': workflow.id, 'page': app.request.get('page', 1)}}) }}
    -
    +
    - + + {% endif %}
    From 5e636c8448b8b20edc273339dfe0382f92f5087f Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Fri, 18 Oct 2024 13:36:31 +0200 Subject: [PATCH 187/452] pagination with page selection for crontab --- templates/JobScheduler/crontab_list.html.twig | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/templates/JobScheduler/crontab_list.html.twig b/templates/JobScheduler/crontab_list.html.twig index d637741fd..77f130820 100644 --- a/templates/JobScheduler/crontab_list.html.twig +++ b/templates/JobScheduler/crontab_list.html.twig @@ -196,25 +196,29 @@
    From f86ff15aaca39811e2961707dda1ab6fac01b1f7 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Fri, 18 Oct 2024 13:39:06 +0200 Subject: [PATCH 188/452] add security to prevent the user from going to a higher number than the max page --- templates/JobScheduler/crontab_list.html.twig | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/templates/JobScheduler/crontab_list.html.twig b/templates/JobScheduler/crontab_list.html.twig index 77f130820..b8b8cef66 100644 --- a/templates/JobScheduler/crontab_list.html.twig +++ b/templates/JobScheduler/crontab_list.html.twig @@ -196,29 +196,35 @@
    From dce170a6af5d906c4e9b34333d09373893a6999e Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Fri, 18 Oct 2024 13:40:36 +0200 Subject: [PATCH 189/452] add max page to workflow and action --- templates/Workflow/show.html.twig | 2 ++ templates/WorkflowAction/show.html.twig | 2 ++ 2 files changed, 4 insertions(+) diff --git a/templates/Workflow/show.html.twig b/templates/Workflow/show.html.twig index 1b7f62a43..058fbabe8 100644 --- a/templates/Workflow/show.html.twig +++ b/templates/Workflow/show.html.twig @@ -259,6 +259,8 @@ - {% block javascripts %} - {{ encore_entry_script_tags('rulelist') }} - {% endblock %} -{% endblock %} - - +{#/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Myddleware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Myddleware. If not, see . +*********************************************************************************/ #} + +{% extends 'base.html.twig' %} +{% block title %} + {{parent()}} + | + {{'list_rule.title'|trans}} +{% endblock %} +{% block titlesm %} +
    + {{'list_rule.title'|trans}} + {% if nb_rule > 0 %} + ({{ nb_rule }}) + {% endif %} +
    +
    + +
    +
    +{% endblock titlesm %} +{% block body %} + {# success message if the rule has been duplicated #} + {% for label, flashes in app.session.flashbag.all %} + {% for flash in flashes %} + {% if ( label == 'success' ) %} +
    + {{ flash }} +
    + {% endif %} + {% endfor %} + {% endfor %} + +
    + {% if nb_rule > 0 %} + +

    +
    +
    + + {% if entities is not empty %} + + + + + + + + + + + + {% for rule in entities %} + + + + + + + + + {% endfor %} + + + + + + + + + + {% endif %} +
    {{'rule.source'|trans}}{{'rule.target'|trans}}{{'list_rule.th.name'|trans}}{{'list_rule.th.active'|trans}}{{'list_rule.th.date_created'|trans}}{{'list_rule.th.option'|trans}}
    +

    {{rule.solution_source }}

    + {{rule.lbl_source }} +
    +

    {{rule.solution_target }}

    + {{rule.lbl_target }} +
    + {{rule.name }} + +
    + + +
    +
    + {{rule.dateCreated|date("d/m/Y") }} + {% if app.user.getUsername == 'support' %} +

    + {{ rule.createdBy.username }} +

    + {% endif %} +
    + + + + + + + + + + + + + + + +
    {{'rule.source'|trans}}{{'rule.target'|trans}}{{'list_rule.th.name'|trans}}{{'list_rule.th.active'|trans}}{{'list_rule.th.date_created'|trans}}{{'list_rule.th.option'|trans}}
    +
    +
    +
    + {% if pager.haveToPaginate %} + {{ pagerfanta(pager, 'twitter_bootstrap4', {'routeName': 'regle_list_page'}) }} + {% endif %} +
    + {% else %} +
    +

    {{'list_rule.empty'|trans}}

    + {{'list_rule.create_first_rule'|trans}} +
    + {% endif %} + +
    + + {% block javascripts %} + {{ encore_entry_script_tags('rulelist') }} + {% endblock %} +{% endblock %} diff --git a/templates/WorkflowAction/edit.html.twig b/templates/WorkflowAction/edit.html.twig index b1e83476b..3774b06df 100644 --- a/templates/WorkflowAction/edit.html.twig +++ b/templates/WorkflowAction/edit.html.twig @@ -10,7 +10,7 @@ {% block body %}
    {{ form_start(form) }} -
    +
    {{ 'view_workflow.edit'|trans }}
    @@ -91,5 +91,6 @@ {% endblock %} diff --git a/templates/WorkflowAction/new.html.twig b/templates/WorkflowAction/new.html.twig index 0f6919efa..6ae2c8258 100644 --- a/templates/WorkflowAction/new.html.twig +++ b/templates/WorkflowAction/new.html.twig @@ -10,7 +10,7 @@ {% block body %}
    {{ form_start(form) }} -
    +
    {{ 'view_workflow.edit'|trans }}
    diff --git a/templates/base.html.twig b/templates/base.html.twig index 28578a3b9..9521181b2 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -59,10 +59,13 @@
    +
    +
    + {{ form_row(form.description) }} +
    +
    {{ form_end(form) }}
    diff --git a/templates/variable/edit.html.twig b/templates/variable/edit.html.twig index 553c1e6df..3fad26611 100644 --- a/templates/variable/edit.html.twig +++ b/templates/variable/edit.html.twig @@ -22,6 +22,10 @@
    {{ form_row(form.value) }}
    +
    +
    + {{ form_row(form.description) }} +
    {{ form_end(form) }} diff --git a/templates/variable/list.html.twig b/templates/variable/list.html.twig index 93cf7a1c4..75d4eaf4b 100644 --- a/templates/variable/list.html.twig +++ b/templates/variable/list.html.twig @@ -26,6 +26,7 @@ {{ 'variable.table_headers.name'|trans }} {{ 'variable.table_headers.value'|trans }} + {{ 'variable.table_headers.description'|trans }} {{ 'variable.table_headers.actions'|trans }} @@ -34,6 +35,7 @@ {{ variable.name }} {{ variable.value }} + {{ variable.description }} diff --git a/templates/variable/show.html.twig b/templates/variable/show.html.twig index 2423e70f1..1c49be956 100644 --- a/templates/variable/show.html.twig +++ b/templates/variable/show.html.twig @@ -38,6 +38,12 @@
    {{ variable.value }}
    +
    +
    + {{ 'variable.table_headers.description'|trans|upper }} +
    {{ variable.description }}
    +
    +
    {% endblock %} \ No newline at end of file diff --git a/translations/messages.en.yml b/translations/messages.en.yml index 78ad3b6a8..ac7559fa9 100644 --- a/translations/messages.en.yml +++ b/translations/messages.en.yml @@ -235,7 +235,7 @@ rule: help: What should I do ? list_rule: - title: List of rules + title: Rules total: Number of rules empty: You do not have any rules yet ! delete: Are you sure you want to delete this rule? @@ -1045,7 +1045,7 @@ variable: name: Name value: Value actions: Actions - + description: Description premium: upgrade: title_main: "Upgrade to Premium" @@ -1056,4 +1056,4 @@ premium: title: "Premium Benefits" benefit1: "Benefit 1 description" benefit2: "Benefit 2 description" - benefit3: "Benefit 3 description" \ No newline at end of file + benefit3: "Benefit 3 description" diff --git a/translations/messages.fr.yml b/translations/messages.fr.yml index 9528ddf84..5346da0b9 100644 --- a/translations/messages.fr.yml +++ b/translations/messages.fr.yml @@ -234,7 +234,7 @@ rule: help: Que dois-je faire ? list_rule: - title: Liste des règles + title: Règles total: Nombre de règles empty: Vous n'avez pas encore de règle ! delete: Etes-vous sûr de vouloir supprimer cette règle ? @@ -1024,4 +1024,5 @@ variable: table_headers: name: Nom value: Valeur - actions: Actions \ No newline at end of file + actions: Actions + description: Description \ No newline at end of file From 840e4f97770cf02f01d4606730b384f08c46b844 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Tue, 22 Oct 2024 15:18:19 +0200 Subject: [PATCH 196/452] 334 restrictions for https://myddleware.atlassian.net/browse/PROJ-334 --- assets/app.js | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/assets/app.js b/assets/app.js index 7b03b2851..e0f91dccb 100755 --- a/assets/app.js +++ b/assets/app.js @@ -30,22 +30,39 @@ require('./js/lib/jquery_fancybox/jquery.fancybox.pack.js') require('./js/lib/jquery_scrollbox/jquery.scrollbox.min.js') require('./js/lib/jquery_qtip/jquery.qtip.min.js') require('./js/lib/jquery_myddleware/function.js') -require('./js/regle.js') require('./js/account.js') require('./js/lib/d3.v2.js') -require('./js/home.js') -require('./js/jobscheduler.js') require('./js/jcarousel.ajax.js') require('./js/animation.js') require('./js/task.js') require('./js/connector.js') -require('./js/smtp.js') -require('./js/filter.js') -require('./js/crontab.js') require('./js/rule_relation_filter.js') -require('./js/editAction.js') require('./js/connector_detail.js') -require("./js/workflows.js"); +require('./js/regle.js') + + +if (window.location.href.includes('rule/document/list')) { + require('./js/filter.js'); +} + +if (window.location.href.includes('workflowAction') || window.location.href.includes('workflow')) { + require('./js/workflows.js') + require('./js/editAction.js') +} + +if (window.location.href.includes('rule/panel')) { + require('./js/home.js') +} + +if (window.location.href.includes('rule/jobscheduler')) { +require('./js/crontab.js') +require('./js/jobscheduler.js') +} + +if (window.location.href.includes('rule/managementsmtp')) { + require('./js/smtp.js') +} + From c56240da453554129c4372989a384c1e9545f4b5 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Tue, 22 Oct 2024 16:30:55 +0200 Subject: [PATCH 197/452] Update custom.css --- assets/css/custom.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/css/custom.css b/assets/css/custom.css index a988729fd..7f8acf9da 100755 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -1,7 +1,7 @@ #logo { background: url('./../images/template/logo.jpg') no-repeat; background-size: 209px; - height: 185px; + height: 52px; width: 240px; display: inline-block; } From 2aed091face6f72ab33884dfa8164872a9998de0 Mon Sep 17 00:00:00 2001 From: Stephane Faure Date: Tue, 22 Oct 2024 19:13:43 +0200 Subject: [PATCH 198/452] Change the way to create a documentManager object Signed-off-by: Stephane Faure --- src/Solutions/solution.php | 12 +++++++++--- src/Solutions/suitecrm.php | 6 +++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Solutions/solution.php b/src/Solutions/solution.php index dd825eaed..29a125d1f 100644 --- a/src/Solutions/solution.php +++ b/src/Solutions/solution.php @@ -154,7 +154,10 @@ protected function updateDocumentStatus($idDoc, $value, $param, $forceStatus = n try { $param['id_doc_myddleware'] = $idDoc; $param['api'] = $this->api; - $documentManager = new DocumentManager($this->logger, $this->connection, $this->entityManager, $this->formulaManager, null, $this->parameterBagInterface); + // Create new documentManager with a clean entityManager + $chlidEntityManager = clone $this->entityManager; + $chlidEntityManager->clear(); + $documentManager = new DocumentManager($this->logger, $this->connection, $chlidEntityManager, $this->formulaManager, null, $this->parameterBagInterface); $documentManager->setParam($param); // If a message exist, we add it to the document logs if (!empty($value['error'])) { @@ -656,8 +659,11 @@ public function sourceActionBeforeSend($send) { // Calculation of all target fields where the source field exists if(array_search($field, $fieldsArray) !== false) { $param['id_doc_myddleware'] = $docId; - $param['api'] = $this->api; - $documentManager = new DocumentManager($this->logger, $this->connection, $this->entityManager, $this->formulaManager); + $param['api'] = $this->api; + // Create new documentManager with a clean entityManager + $chlidEntityManager = clone $this->entityManager; + $chlidEntityManager->clear(); + $documentManager = new DocumentManager($this->logger, $this->connection, $chlidEntityManager, $this->formulaManager); $documentManager->setParam($param); $send['data'][$docId][$ruleField['target_field_name']] = $documentManager->getTransformValue($send['source'][$docId], $ruleField); } diff --git a/src/Solutions/suitecrm.php b/src/Solutions/suitecrm.php index 495932c16..6ec655e19 100644 --- a/src/Solutions/suitecrm.php +++ b/src/Solutions/suitecrm.php @@ -645,7 +645,7 @@ public function createData($param): array $setEntriesListParameters = [ 'session' => $this->session, 'module_name' => $param['module'], - 'name_value_lists' => $dataSugar, + 'name_value_list' => $dataSugar, ]; $get_entry_list_result = $this->call('set_entry', $setEntriesListParameters); @@ -784,7 +784,7 @@ public function updateData($param): array $setEntriesListParameters = [ 'session' => $this->session, 'module_name' => $param['module'], - 'name_value_lists' => $dataSugar, + 'name_value_list' => $dataSugar, ]; $get_entry_list_result = $this->call('set_entry', $setEntriesListParameters); @@ -880,7 +880,7 @@ protected function generateQuery($param, $method): string } elseif ('Employees' == $param['module']) { $query .= 'users.'.$key." = '".$value."' "; } else { - $query .= strtolower($param['module']).'.'.$key." = '".$value."' "; + $query .= (substr($key,-2) == '_c' ? strtolower($param['module']).'_cstm' : strtolower($param['module'])).'.'.$key." = '".$value."' "; } } } From 9c441b58e5c5cc2d18b417427dd37ffcc6525d5d Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 23 Oct 2024 08:53:30 +0200 Subject: [PATCH 199/452] add rerun action in controller --- src/Controller/WorkflowActionController.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Controller/WorkflowActionController.php b/src/Controller/WorkflowActionController.php index db6f01e37..24c8104d1 100644 --- a/src/Controller/WorkflowActionController.php +++ b/src/Controller/WorkflowActionController.php @@ -351,6 +351,7 @@ public function WorkflowCreateActionWithWorkflow(string $workflowId, Request $re 'sendNotification' => 'sendNotification', 'generateDocument' => 'generateDocument', 'transformDocument' => 'transformDocument', + 'rerun' => 'rerun', 'changeData' => 'changeData', ], ]) @@ -694,6 +695,7 @@ public function WorkflowActionEditAction(string $id, Request $request) 'sendNotification' => 'sendNotification', 'generateDocument' => 'generateDocument', 'transformDocument' => 'transformDocument', + 'rerun' => 'rerun', 'changeData' => 'changeData', ], ]) @@ -912,6 +914,8 @@ public function emptyArgumentsBasedOnAction($id) unset($arguments['status'], $arguments['searchField'], $arguments['searchValue']); } elseif ($action == 'transformDocument') { unset($arguments['to'], $arguments['subject'], $arguments['message'], $arguments['status'], $arguments['searchField'], $arguments['searchValue']); + } elseif ($action == 'rerun') { + unset($arguments['to'], $arguments['subject'], $arguments['message'], $arguments['status'], $arguments['searchField'], $arguments['searchValue']); } $workflowAction->setArguments($arguments); From bdfd8bec4c9393913d2913481e6ee6c1f0722519 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 23 Oct 2024 08:53:38 +0200 Subject: [PATCH 200/452] add rerun action in action type --- src/Form/Type/WorkflowActionType.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Form/Type/WorkflowActionType.php b/src/Form/Type/WorkflowActionType.php index 5c67ee475..a838499cc 100644 --- a/src/Form/Type/WorkflowActionType.php +++ b/src/Form/Type/WorkflowActionType.php @@ -66,6 +66,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) 'generateDocument' => 'generateDocument', 'transformDocument' => 'transformDocument', 'changeData' => 'changeData', + 'rerun' => 'rerun' ], 'attr' => [ 'class' => 'form-control', From b1b097689a20b7b78ea264c8dfae8391ca4bc2c1 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 23 Oct 2024 08:54:11 +0200 Subject: [PATCH 201/452] add rerun action in workflow.js --- assets/js/workflows.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/js/workflows.js b/assets/js/workflows.js index dbcb51335..f0d490f35 100644 --- a/assets/js/workflows.js +++ b/assets/js/workflows.js @@ -85,6 +85,7 @@ $(document).ready(function () { if ( actionValue === "transformDocument" || + actionValue === "rerun" || actionValue === "sendNotification" || actionValue === "updateStatus" ) { From 9b6c9d607562be914636e77c9b52a2f143ee1097 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 23 Oct 2024 09:36:35 +0200 Subject: [PATCH 202/452] restore class to button in workflow --- templates/Workflow/show.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/Workflow/show.html.twig b/templates/Workflow/show.html.twig index 07a382120..de0aae405 100644 --- a/templates/Workflow/show.html.twig +++ b/templates/Workflow/show.html.twig @@ -65,7 +65,7 @@
    {{ 'view_workflow.status'| trans | upper }}
    - +
    From e7192f37a20487a0e538f35acd59d8b52dbceedb Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 23 Oct 2024 09:38:24 +0200 Subject: [PATCH 203/452] add undefined typeof check for targetsFieldsData --- assets/js/workflows.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/workflows.js b/assets/js/workflows.js index f0d490f35..f02b6040d 100644 --- a/assets/js/workflows.js +++ b/assets/js/workflows.js @@ -5,7 +5,7 @@ $(document).ready(function () { $('fieldset:has(#form_targetFieldValues)').hide(); - if (isEditMode && targetFieldsData && targetFieldsData.length > 0) { + if (isEditMode && typeof targetFieldsData !== 'undefined' && targetFieldsData && targetFieldsData.length > 0) { targetFieldsData.forEach(function (fieldData) { addNewTargetFieldWithValue(fieldData.field, fieldData.value); }); From 9165c1ef1789b1644d78285cc26fead2b1f6828a Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 23 Oct 2024 09:38:37 +0200 Subject: [PATCH 204/452] remove duplicate on change function --- assets/js/workflows.js | 43 ------------------------------------------ 1 file changed, 43 deletions(-) diff --git a/assets/js/workflows.js b/assets/js/workflows.js index f02b6040d..4e7d813c0 100644 --- a/assets/js/workflows.js +++ b/assets/js/workflows.js @@ -129,49 +129,6 @@ $(document).ready(function () { fetchFilteredData(); }); - $(".workflow-check-input").on("change", function () { - var $input = $(this); - var entityId = $input.data("id"); - var entityType = $input.data("type"); - var newState = $input.is(":checked") ? 1 : 0; - - var pathArray = window.location.pathname.split("/"); - var basePath = - window.location.origin + "/" + pathArray[1] + "/" + pathArray[2]; - var currentUrl = `${basePath}/${entityType}/${entityType}/toggle/${entityId}`; - - $.ajax({ - url: currentUrl, - type: "POST", - data: { newState: newState }, - beforeSend: function () { - console.log( - `Before sending the request for ${entityType} ID: ${entityId}` - ); - }, - success: function (response) { - console.log( - `Success response received for ${entityType} ID: ${entityId}`, - response - ); - }, - error: function (xhr, status, error) { - console.error( - `Error received for ${entityType} ID: ${entityId}`, - xhr, - status, - error - ); - alert("Erreur lors de la bascule"); - }, - complete: function (xhr, status) { - console.log( - `Request completed for ${entityType} ID: ${entityId}`, - status - ); - }, - }); - }); $(".workflow-check-input").on("change", function () { var $input = $(this); From 3d29c79b6afcfc1d9b6f4da07458444e586114e6 Mon Sep 17 00:00:00 2001 From: AlexMyddleware Date: Wed, 23 Oct 2024 09:53:21 +0200 Subject: [PATCH 205/452] 363 put rule first https://myddleware.atlassian.net/browse/PROJ-363 --- templates/testFilter.html.twig | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/templates/testFilter.html.twig b/templates/testFilter.html.twig index 818fbce66..b21d9b510 100644 --- a/templates/testFilter.html.twig +++ b/templates/testFilter.html.twig @@ -86,6 +86,13 @@
    + +
    - +
    {% endblock %} diff --git a/templates/Home/index.html.twig b/templates/Home/index.html.twig index 03b07dfdc..f8ea2a332 100644 --- a/templates/Home/index.html.twig +++ b/templates/Home/index.html.twig @@ -113,7 +113,65 @@

    {{'panel.features.title'|trans}}

    - {# EN ATTENTE DE LA LISTE DES FEATURES #} + +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    {# CONTACT US #}
    @@ -126,13 +184,16 @@

    {{'panel.contact.title2'|trans}}

    {{'panel.contact.text1'|trans}}

    -

    {{'panel.contact.text2'|trans}}

    +

    {{'panel.contact.text2'|trans}} + : support@myddleware.com +

    {{'panel.contact.text3'|trans}}

    {{'panel.contact.title3'|trans}}

    {{'panel.contact.text4'|trans}}

    -

    {{'panel.contact.text5'|trans}}

    +

    {{'panel.contact.text5'|trans}} + : romanfaure@myddleware.com

    diff --git a/templates/JobScheduler/crontab_list.html.twig b/templates/JobScheduler/crontab_list.html.twig index b8b8cef66..10775f961 100644 --- a/templates/JobScheduler/crontab_list.html.twig +++ b/templates/JobScheduler/crontab_list.html.twig @@ -1,16 +1,11 @@ {% extends 'base.html.twig' %} {% block titlesm %} {{ 'crontab.title'|trans }} + ({{ entity | length }}) {% endblock %} {% block body %} -
    -

    {{ 'jobscheduler.total'|trans }}

    -

    - {{ entity | length }} -

    -
    -
    -
    + +

    {{ 'crontab.title_cron'|trans }}

    {% for entityCron in entitiesCron %} {% if entityCron.value == true %} @@ -77,14 +72,14 @@ {% for value in entity %} + {% if value.runningInstances == value.maxInstances %} + bg-danger + {% elseif value.runningInstances == value.maxInstances - 1 and value.maxInstances > 1 %} + bg-warning + {% else %} + bg-none + {% endif %} + ">
    {{ value.command|slice(0, 20) ~ '...' }} @@ -196,9 +191,9 @@ diff --git a/templates/Rule/edit/onglets/mapping.html.twig b/templates/Rule/edit/onglets/mapping.html.twig index 89ee41ad4..2239fd67f 100644 --- a/templates/Rule/edit/onglets/mapping.html.twig +++ b/templates/Rule/edit/onglets/mapping.html.twig @@ -1,65 +1,79 @@ -{#/********************************************************************************* - * This file is part of Myddleware. - - * @package Myddleware - * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL - * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com - * @link http://www.myddleware.com - - This file is part of Myddleware. - - Myddleware is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Myddleware is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Myddleware. If not, see . -*********************************************************************************/ #} - - - -
    -

    {{'view_rule.mapping.module_target'|trans}} : {{ rule.getModuleTarget }}

    -

    {{'view_rule.mapping.module_source'|trans}} : {{ rule.getModuleSource }}

    -

    -
    -
    - {% if fields %} - {% for c, f in fields %} -
    -
    -

    {{ f.getTarget }}

    -

    -

    {{ f.getSource }}

    - {% if f.getFormula %} -

    {{'view_rule.mapping.formula'|trans}} : {{ f.getFormula }}

    - {% else %} -

    {{'view_rule.mapping.formula_empty'|trans}}

    - {% endif %} -

    -
    -
    - {% endfor %} -
    - - {% else %} -
    {{'view_rule.mapping.empty'|trans}}
    - {% endif %} -
    -
    -

    {{'help.title'|trans}} - -

    -

    {{'help.viewrule.mapping'|trans|raw}}

    -
    +{#/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Myddleware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Myddleware. If not, see . +*********************************************************************************/ #} + + + +
    +

    {{'view_rule.mapping.module_target'|trans}} + :{{ rule.getModuleTarget }} +

    +

    +

    {{'view_rule.mapping.module_source'|trans}} + :{{ rule.getModuleSource }} +

    +

    +
    +
    +
    + {% if fields %} + {% for c, f in fields %} +
    +
    +

    {{ f.getTarget }}

    +

    +

    {{ f.getSource }} +

    + {% if f.getFormula %} +

    {{'view_rule.mapping.formula'|trans}} + : + {{ f.getFormula }} +

    + {% else %} +

    {{'view_rule.mapping.formula_empty'|trans}}

    + {% endif %} +

    + +
    +

    Votre com

    + +
    +
    +
    + {% endfor %} +
    + +{% else %} +
    {{'view_rule.mapping.empty'|trans}}
    +{% endif %}
    +

    {{'help.title'|trans}} + +

    +

    {{'help.viewrule.mapping'|trans|raw}}

    diff --git a/templates/Task/list.html.twig b/templates/Task/list.html.twig index 6b6d51fd5..60ac6d88e 100644 --- a/templates/Task/list.html.twig +++ b/templates/Task/list.html.twig @@ -24,7 +24,12 @@ {% extends 'base.html.twig' %} {% block title %}{{parent()}} | {{'title.task.list'|trans}}{% endblock %} -{% block titlesm %}{{'title.task.list'|trans}}{% endblock titlesm %} +{% block titlesm %} + {{'title.task.list'|trans}} + {% if nb > 0 %} + ({{ nb }}) + {% endif %} +{% endblock titlesm %} {% block body %} {% endfor %} - - {% if nb > 0 %} -
    -

    {{'list_task.total'|trans}} :

    {{ nb }} -

    - - -
    {% if entities is not empty %} diff --git a/templates/Workflow/list.html.twig b/templates/Workflow/list.html.twig index 37d034f01..919c2a0f3 100644 --- a/templates/Workflow/list.html.twig +++ b/templates/Workflow/list.html.twig @@ -25,79 +25,75 @@ {% extends 'base.html.twig' %} {% block title %} - {{ parent() }} | {{ 'title.workflow.list'|trans }} + {{ parent() }} + | + {{ 'title.workflow.list'|trans }} {% endblock %} {% block titlesm %} - {{ 'title.workflow.list'|trans }} + {{ 'title.workflow.list'|trans }} + {% if nb_workflow > 0 %} + ({{ nb_workflow }}) + {% endif %} {% endblock titlesm %} {% block body %} -
    +
    - + -
    - {% for message in app.flashes('success') %} - - {% endfor %} +
    + {% for message in app.flashes('success') %} + + {% endfor %} + {% if nb_workflow > 0 %} +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    + +
    +
    - {% if nb_workflow > 0 %} -
    -

    {{ 'list_workflow.total'|trans }}:

    -

    - {{ nb_workflow }} -

    -
    -
    +
    +
    + {% include 'Workflow/_workflow_table.html.twig' %} +
    +
    + {% if pager_workflow_list.haveToPaginate %} + {{ pagerfanta(pager_workflow_list, 'twitter_bootstrap4', {'routeName': 'workflow_list_page'}) }} + {% endif %} +
    + {% else %} +

    {{ 'list_workflow.empty'|trans }}

    + {% endif %} +
    +
    -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    - -
    -
    - -
    -
    - {% include 'Workflow/_workflow_table.html.twig' %} -
    -
    - {% if pager_workflow_list.haveToPaginate %} - {{ pagerfanta(pager_workflow_list, 'twitter_bootstrap4', {'routeName': 'workflow_list_page'}) }} - {% endif %} -
    - {% else %} -

    {{ 'list_workflow.empty'|trans }}

    - {% endif %} -
    -
    - -{# ------------- PARAMETRES JQUERY ------------- #} - -{# ------------- PARAMETRES JQUERY ------------- #} + {# ------------- PARAMETRES JQUERY ------------- #} + + {# ------------- PARAMETRES JQUERY ------------- #} {% endblock %} diff --git a/templates/WorkflowAction/list.html.twig b/templates/WorkflowAction/list.html.twig index b91f51566..bf342dca7 100644 --- a/templates/WorkflowAction/list.html.twig +++ b/templates/WorkflowAction/list.html.twig @@ -20,41 +20,43 @@ You should have received a copy of the GNU General Public License along with Myddleware. If not, see . -*********************************************************************************/ #} +*********************************************************************************/ #} {% extends 'base.html.twig' %} -{% block title %}{{parent()}} | {{'title.workflow.list'|trans}}{% endblock %} -{% block titlesm %}{{'title.workflow.list'|trans}}{% endblock titlesm %} -{% block body %} -{% block js %} - - - - -{% endblock js %} -
    +{% block title %} + {{parent()}} + | + {{'title.workflow.list'|trans}} +{% endblock %} +{% block titlesm %} + {{'title.workflow.list'|trans}} - + {% if nb_workflow > 0 %} + {{ nb_workflow }} + {% endif %} +{% endblock titlesm %} +{% block body %} + {% block js %} + + + + + {% endblock js %} +
    -
    - {% for message in app.flashes('success') %} -
    - {% if entities is not empty %} + {% if entities is not empty %} @@ -63,12 +65,17 @@ - + {% for workflow in entities %} - - + + - + + - - + + - - - - - {% endfor %} + {% endfor %} {% endif %}
    {{'list_workflow.th.id'|trans}} {{'list_workflow.th.ruleName'|trans}}{{'list_workflow.th.workflowCondition'|trans}} {{'list_workflow.th.active'|trans}} {{'list_workflow.th.actions'|trans}}
    {{ workflow.id }} {{ workflow.rule.name }}{{ workflow.name }} + + {{ workflow.rule.name }} + + {{ workflow.name }} + {{ workflow.description }} {{ workflow.condition }} @@ -76,39 +83,50 @@ {{ workflow.active ? 'Active' : 'Inactive' }} + +
    - +
    {% if pager.haveToPaginate %} @@ -137,15 +155,14 @@

    {{ 'list_workflow.empty'|trans }}

    {% endif %}
    -
    +
    - {# ------------- PARAMETRES JQUERY ------------- #} - - {# ------------- PARAMETRES JQUERY ------------- #} +var lang = "{{ app.request.locale }}"; + + {# ------------- PARAMETRES JQUERY ------------- #} {% endblock %} {% block cssin %}{% endblock cssin %} - diff --git a/templates/base.html.twig b/templates/base.html.twig index 9521181b2..f17e771ce 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -156,9 +156,8 @@ -
    -
    - {{ form_row(form.ruleId) }} -
    {{ form_row(form.to) }}
    diff --git a/templates/WorkflowAction/new.html.twig b/templates/WorkflowAction/new.html.twig index 6ae2c8258..831dfccde 100644 --- a/templates/WorkflowAction/new.html.twig +++ b/templates/WorkflowAction/new.html.twig @@ -51,6 +51,9 @@
    {{ form_row(form.message) }}
    +
    + {{ form_row(form.ruleId) }} +
    {{ form_row(form.searchField) }}
    @@ -64,9 +67,6 @@
    -
    - {{ form_row(form.ruleId) }} -
    -
    -