From 310a9e8c4e294e93b528b552cad0a61ea31594d7 Mon Sep 17 00:00:00 2001 From: Camille Pichaud <95077335+CamillePMyddleware@users.noreply.github.com> Date: Mon, 6 Mar 2023 14:38:36 +0100 Subject: [PATCH 1/5] fix: link moodle (#966) * fix: link solution in the homepage --- .env | 2 +- translations/messages.en.yml | 2 +- translations/messages.fr.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.env b/.env index 7a97ca772..118aa7c2b 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -MYDDLEWARE_VERSION=3.3.1a +MYDDLEWARE_VERSION=3.3.1b APP_SECRET=Thissecretisnotsosecretchangeit APP_ENV=prod diff --git a/translations/messages.en.yml b/translations/messages.en.yml index 3b6e27886..15375f80b 100644 --- a/translations/messages.en.yml +++ b/translations/messages.en.yml @@ -296,7 +296,7 @@ view_connector: help: Assistance link_help: Click here to have more information on this connector help: - link: https://myddleware.github.io/myddleware/#/connectors_credentials + link: https://myddleware.github.io/myddleware/#/connectors_credentials?id= list_flux: start: Start diff --git a/translations/messages.fr.yml b/translations/messages.fr.yml index c62e95f68..c5470e233 100644 --- a/translations/messages.fr.yml +++ b/translations/messages.fr.yml @@ -295,7 +295,7 @@ view_connector: help: Aide link_help: Cliquez-ici pour avoir plus d'informations sur ce connecteur help: - link: https://myddleware.github.io/myddleware/#/connectors_credentials + link: https://myddleware.github.io/myddleware/#/connectors_credentials?id= list_flux: start: Début From 3aa3c2b22934ed658ac179c81798f2963208bd4d Mon Sep 17 00:00:00 2001 From: Myddleware Date: Mon, 27 Mar 2023 13:50:14 +0200 Subject: [PATCH 2/5] Release 3.3.1c (#989) * feat : add parameter to change the number of result of the rule list view * Connector : File adapted to Myddleware3 * Connector : Database, fix error handling row (#986) * Connector : Moodle manage custom fields for users and courses (#987) --- .env | 2 +- src/Controller/DefaultController.php | 5 +- src/Manager/ToolsManager.php | 15 +++ src/Solutions/database.php | 16 ++- src/Solutions/file.php | 23 +++-- src/Solutions/moodle.php | 143 +++++++++++++++++++++++++-- src/Solutions/solution.php | 2 + translations/messages.en.yml | 2 + translations/messages.fr.yml | 4 +- 9 files changed, 187 insertions(+), 25 deletions(-) diff --git a/.env b/.env index 118aa7c2b..533f90cbf 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -MYDDLEWARE_VERSION=3.3.1b +MYDDLEWARE_VERSION=3.3.1c APP_SECRET=Thissecretisnotsosecretchangeit APP_ENV=prod diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index a60302fc9..a990fd11a 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -169,13 +169,14 @@ public function ruleListAction(int $page = 1) $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($this->params['pager']) ? $this->params['pager'] : 20, + 'maxPerPage' => isset($pager) ? $pager : 20, 'page' => $page, ]); + // Si tout se passe bien dans la pagination if ($compact) { // Si aucune règle diff --git a/src/Manager/ToolsManager.php b/src/Manager/ToolsManager.php index 80e5b863f..47acd3559 100644 --- a/src/Manager/ToolsManager.php +++ b/src/Manager/ToolsManager.php @@ -200,6 +200,21 @@ public function getPhpVersion() return $php; } + + + public function getParamValue($paramName) + { + // Get the custom php version first + $select = "SELECT * FROM config WHERE name = :param_name"; + $stmt = $this->connection->prepare($select); + $stmt->bindValue(':param_name', $paramName); + $result = $stmt->executeQuery(); + $config = $result->fetchAssociative(); + if (!empty($config['value'])) { + return $config['value']; + } + return null; + } } class ToolsManager extends toolscore diff --git a/src/Solutions/database.php b/src/Solutions/database.php index 9dcc79e2d..fec2f3320 100644 --- a/src/Solutions/database.php +++ b/src/Solutions/database.php @@ -387,6 +387,7 @@ public function readData($param) */ protected function create($param, $record, $idDoc = null) { + // Get the target reference field if (!isset($param['ruleParams']['targetFieldId'])) { throw new Exception('targetFieldId has to be specified for the data creation.'); @@ -423,6 +424,11 @@ protected function create($param, $record, $idDoc = null) throw new Exception('Create: '.$errorInfo[2].' . Query : '.$sql); } + // Check if the row has been created + if ($q->rowCount() === 0) { + throw new Exception('No row was created for the id : ' . $idTarget); + } + // If the target reference field isn't in data sent if (!isset($idTarget)) { // If the target reference field is a primary key auto increment, we retrive the value here @@ -430,6 +436,7 @@ protected function create($param, $record, $idDoc = null) } return $idTarget; + } /** @@ -464,8 +471,8 @@ protected function update($param, $record, $idDoc = null) throw new Exception('Update: '.$errorInfo[2].' . Query : '.$sql); } // No modification - if (0 == $q->rowCount()) { - $this->message = 'There is no error but the query hasn\'t modified any record.'; + if ($q->rowCount() === 0) { + throw new Exception('No row was updated for the id : ' . $record['target_id']); } // Several modifications if ( @@ -502,6 +509,11 @@ public function delete($param, $record) throw new Exception('Delete: '.$errorInfo[2].' . Query : '.$sql); } + // No deletion + if ($q->rowCount() === 0) { + throw new Exception('No row was deleted for the id : ' . $record['target_id']); + } + return $record['target_id']; } diff --git a/src/Solutions/file.php b/src/Solutions/file.php index 03f568c2a..fec0340dc 100644 --- a/src/Solutions/file.php +++ b/src/Solutions/file.php @@ -34,7 +34,7 @@ class filecore extends solution protected $baseUrl; protected array $messages = []; protected array $duplicateDoc = []; - protected \Doctrine\DBAL\Connection $connection; + protected $sshconnection; protected string $delimiter = ';'; protected string $enclosure = '"'; protected string $escape = ''; @@ -60,14 +60,14 @@ public function login($paramConnexion): void throw new \Exception('Please enable extension ssh2. Help here : http://php.net/manual/fr/ssh2.installation.php'); } // Connect to the server - $this->connection = ssh2_connect($this->paramConnexion['host'], $this->paramConnexion['port']); - ssh2_auth_password($this->connection, $this->paramConnexion['login'], $this->paramConnexion['password']); + $this->sshconnection = ssh2_connect($this->paramConnexion['host'], $this->paramConnexion['port']); + ssh2_auth_password($this->sshconnection, $this->paramConnexion['login'], $this->paramConnexion['password']); // Check if the directory exist - $stream = ssh2_exec($this->connection, 'cd '.$this->paramConnexion['directory'].';pwd'); + $stream = ssh2_exec($this->sshconnection, 'cd '.$this->paramConnexion['directory'].';pwd'); stream_set_blocking($stream, true); $output = stream_get_contents($stream); - if (trim($this->paramConnexion['directory']) != trim($output)) { + if (strpos(trim($output), trim($this->paramConnexion['directory'])) === false) { throw new \Exception('Failed to access to the directory'.$this->paramConnexion['directory'].'. Could you check if this directory exists and if the user has the right to read it. '); } @@ -115,7 +115,7 @@ public function get_modules($type = 'source'): array { try { // Get the subfolders of the current directory - $stream = ssh2_exec($this->connection, 'cd '.$this->paramConnexion['directory'].';ls -d */'); + $stream = ssh2_exec($this->sshconnection, 'cd '.$this->paramConnexion['directory'].';ls -d */'); stream_set_blocking($stream, true); $output = stream_get_contents($stream); // Transform the directory list in an array @@ -148,7 +148,7 @@ public function get_module_fields($module, $type = 'source', $param = null): arr $file = $this->get_last_file($this->paramConnexion['directory'].'/'.$module, '1970-01-01 00:00:00'); $fileName = trim($this->paramConnexion['directory'].'/'.$module.$file); // Open the file - $sftp = ssh2_sftp($this->connection); + $sftp = ssh2_sftp($this->sshconnection); $stream = fopen('ssh2.sftp://'.intval($sftp).$fileName, 'r'); $headerString = trim(fgets($stream)); // Close the file @@ -290,9 +290,9 @@ public function readData($param) } $fileName = $this->paramConnexion['directory'].'/'.$param['module'].$file; - + // Open the file - $sftp = ssh2_sftp($this->connection); + $sftp = ssh2_sftp($this->sshconnection); $stream = fopen('ssh2.sftp://'.intval($sftp).$fileName, 'r'); $header = $this->getFileHeader($stream, $param); @@ -303,7 +303,7 @@ public function readData($param) $allRuleField[] = $param['ruleParams']['fieldId']; // Get the date of modification of the file - $new_date_ref = ssh2_exec($this->connection, 'cd '.$this->paramConnexion['directory'].'/'.$param['module'].';stat -c %y '.$file); + $new_date_ref = ssh2_exec($this->sshconnection, 'cd '.$this->paramConnexion['directory'].'/'.$param['module'].';stat -c %y '.$file); stream_set_blocking($new_date_ref, true); $new_date_ref = stream_get_contents($new_date_ref); $new_date_ref = trim($new_date_ref); @@ -433,7 +433,6 @@ public function readData($param) if (!empty($stream)) { fclose($stream); } - return $result; } @@ -577,7 +576,7 @@ protected function validateRow($row, $idRow, $rowNumber): bool protected function get_last_file($directory, $date_ref): string { - $stream = ssh2_exec($this->connection, 'cd '.$directory.';find . -newermt "'.$date_ref.'" -type f | sort | head -n 1'); + $stream = ssh2_exec($this->sshconnection, 'cd '.$directory.';find . -newermt "'.$date_ref.'" -type f | sort | head -n 1'); stream_set_blocking($stream, true); $file = stream_get_contents($stream); $file = ltrim($file, './'); // The filename can have ./ at the beginning diff --git a/src/Solutions/moodle.php b/src/Solutions/moodle.php index 68dcf86a4..c35875f95 100644 --- a/src/Solutions/moodle.php +++ b/src/Solutions/moodle.php @@ -87,6 +87,16 @@ public function getFieldsLogin(): array 'name' => 'token', 'type' => PasswordType::class, 'label' => 'solution.fields.token', + ], + [ + 'name' => 'user_custom_fields', + 'type' => TextType::class, + 'label' => 'solution.fields.user_custom_fields', + ], + [ + 'name' => 'course_custom_fields', + 'type' => TextType::class, + 'label' => 'solution.fields.course_custom_fields', ], ]; } @@ -154,6 +164,8 @@ public function get_module_fields($module, $type = 'source', $param = null): arr } } + // Add user custom fields + $this->addCustomFields($module, $type, $param); return $this->moduleFields; } catch (\Exception $e) { $error = $e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; @@ -169,11 +181,15 @@ public function read($param): array { try { $result = []; - // Set parameters to call Moodle $parameters = $this->setParameters($param); // Get function to call Moodle $functionName = $this->getFunctionName($param); + // Get the custom fields set in the connector + $customFieldList = $this->getCustomFields($param); + // Init the attribute name and value for custom fields + $attributeName = ($param['module'] == 'courses' ? 'shortname' : 'name'); + $attributeValue = ($param['module'] == 'courses' ? 'valueraw' : 'value'); // Call to Moodle $serverurl = $this->paramConnexion['url'].'/webservice/rest/server.php'.'?wstoken='.$this->paramConnexion['token'].'&wsfunction='.$functionName; @@ -182,15 +198,48 @@ public function read($param): array if (!empty($xml->ERRORCODE)) { throw new \Exception("Error $xml->ERRORCODE : $xml->MESSAGE"); } - // Transform the data to Myddleware format if (!empty($xml->MULTIPLE->SINGLE)) { foreach ($xml->MULTIPLE->SINGLE as $data) { + $row = array(); + // Init custom fields to empty because Moodle returns custom field only if they exist for the current record + if (!empty($customFieldList)) { + foreach($customFieldList as $custom) { + $row[$custom] = ''; + } + } foreach ($data as $field) { // Get all the requested fields - if (false !== array_search($field->attributes()->__toString(), $param['fields'])) { + if (array_search($field->attributes()->__toString(), $param['fields']) !== false) { $row[$field->attributes()->__toString()] = $field->VALUE->__toString(); } + // Manage custom field + elseif ( + $field->attributes()->__toString() == 'customfields' + AND !empty($customFieldList) + ) { + // Get the curstom field values + // Loop on each custom field returns by Moodle + foreach($field->MULTIPLE->SINGLE as $customField) { + // Get the name and the value of each field + $customFieldValue = ''; + $customFieldName = ''; + foreach($customField->KEY as $customFieldValues) { + if ($customFieldValues->attributes()->__toString() == $attributeName) { + $customFieldName = $customFieldValues->VALUE->__toString(); + } elseif ($customFieldValues->attributes()->__toString() == $attributeValue) { + $customFieldValue = $customFieldValues->VALUE->__toString(); + } + } + // Set the custom value to the output result + if ( + !empty($customFieldName) + AND in_array($customFieldName, $customFieldList) + ) { + $row[$customFieldName] = $customFieldValue; + } + } + } } $result[] = $row; } @@ -199,7 +248,6 @@ public function read($param): array $result['error'] = 'Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->logger->error($result['error']); } - return $result; } @@ -213,17 +261,34 @@ public function createData($param): array // Transformation du tableau d'entrée pour être compatible webservice Sugar foreach ($param['data'] as $idDoc => $data) { try { + // Get the custom fields set in the connector + $customFieldList = $this->getCustomFields($param); + // Check control before create $data = $this->checkDataBeforeCreate($param, $data, $idDoc); $dataSugar = []; $obj = new \stdClass(); foreach ($data as $key => $value) { // We don't send Myddleware_element_id field to Moodle - if (in_array($key, array('Myddleware_element_id','source_date_modified','id_doc_myddleware'))) { + if (in_array($key, array('Myddleware_element_id', 'source_date_modified', 'id_doc_myddleware'))) { continue; } if (!empty($value)) { - $obj->$key = $value; + // if $value belongs to $this->paramConnexion[user_custom_fields] then we add it to $obj->customfields + if (in_array($key, $customFieldList)) { + $customField = new \stdClass(); + // Param names are differents depending on the module + if($param['module'] == 'users') { + $customField->type = $key; + } elseif($param['module'] == 'courses') { + $customField->shortname = $key; + } + $customField->value = $value; + $obj->customfields[] = $customField; + + } else { + $obj->$key = $value; + } } } switch ($param['module']) { @@ -328,6 +393,8 @@ public function updateData($param): array try { // Check control before update $data = $this->checkDataBeforeUpdate($param, $data, $idDoc); + // Get the custom fields set in the connector + $customFieldList = $this->getCustomFields($param); $dataSugar = []; $obj = new \stdClass(); foreach ($data as $key => $value) { @@ -338,7 +405,20 @@ public function updateData($param): array continue; } if (!empty($value)) { - $obj->$key = $value; + // if $value belongs to $this->paramConnexion[user_custom_fields] then we add it to $obj->customfields + if (in_array($key, $customFieldList)) { + $customField = new \stdClass(); + // Param names are differents depending on the module + if($param['module'] == 'users') { + $customField->type = $key; + } elseif($param['module'] == 'courses') { + $customField->shortname = $key; + } + $customField->value = $value; + $obj->customfields[] = $customField; + } else { + $obj->$key = $value; + } } } @@ -563,6 +643,55 @@ public function getRefFieldName($param): string break; } } + + // Get the custom fields depending on the module + protected function getCustomFields ($param) { + // User and course Moodle fields aren't stored in the same parameter + if ( + $param['module'] == 'users' + AND !empty($this->paramConnexion['user_custom_fields']) + ) { + return explode(',',$this->paramConnexion['user_custom_fields']); + } + if ( + $param['module'] == 'courses' + AND !empty($this->paramConnexion['course_custom_fields']) + ) { + return explode(',',$this->paramConnexion['course_custom_fields']); + } + return null; + } + + // Function to add custom fields for course and user modules. + // The custom fields are stored into the connector parameters + protected function addCustomFields($module, $type, $param) { + $customFields = array(); + // Check if custom fields exist + if ( + $module == 'users' + AND !empty($this->paramConnexion['user_custom_fields']) + ) { + $customFields = explode(',',$this->paramConnexion['user_custom_fields']); + } elseif ( + $module == 'courses' + AND !empty($this->paramConnexion['course_custom_fields']) + ) { + $customFields = explode(',',$this->paramConnexion['course_custom_fields']); + } + // Add the custom fields in the attribute $moduleFields + if (!empty($customFields)) { + foreach ($customFields as $customField) { + $this->moduleFields[$customField] = [ + 'label' => $customField, + 'type' => 'varchar(255)', + 'type_bdd' => 'varchar(255)', + 'required' => 0, + 'required_relationship' => 0, + 'relate' => false, + ]; + } + } + } } class moodle extends moodlecore diff --git a/src/Solutions/solution.php b/src/Solutions/solution.php index 3dd4447ac..e3ac41687 100644 --- a/src/Solutions/solution.php +++ b/src/Solutions/solution.php @@ -412,6 +412,7 @@ public function createData($param): array if (empty($recordId)) { throw new \Exception('No Id returned. '); } + // Format result $result[$idDoc] = [ 'id' => $recordId, @@ -498,6 +499,7 @@ public function updateData($param): array if (empty($recordId)) { throw new \Exception('No Id returned. '); } + // Format result $result[$idDoc] = [ 'id' => $recordId, diff --git a/translations/messages.en.yml b/translations/messages.en.yml index 15375f80b..6cdc22b8d 100644 --- a/translations/messages.en.yml +++ b/translations/messages.en.yml @@ -533,6 +533,8 @@ solution: applicationName: Application name hashkey: Hash key id_auth: Auth ID + user_custom_fields: User custom fields (shortname separated by coma) + course_custom_fields: Course custom fields (shortname separated by coma) params: dateref: Reference (data will be read from this parameter) limit: Limit (read limit for each call) diff --git a/translations/messages.fr.yml b/translations/messages.fr.yml index c5470e233..36803ca11 100644 --- a/translations/messages.fr.yml +++ b/translations/messages.fr.yml @@ -531,6 +531,8 @@ solution: applicationName: Nom de l'application hashkey: Clé de hachage authid: Id auth + user_custom_fields: User - champs personalisés (shortname séparés par des virgules) + course_custom_fields: Course - champs personalisés (shortname séparés par des virgules) params: dateref: Référence (Les données seront lues à partir de ce paramètre) limit: Limite (Nombre de données lues maximum par appel) @@ -553,7 +555,7 @@ solution: 30_day: 30 jours 60_day: 60 jours 90_day: 90 jours - + error: connexion: Connexion impossible general: Erreur inattendue From e17766c9ba2b6c05b11d7704aee3bf8231246716 Mon Sep 17 00:00:00 2001 From: AlexMyddleware <106162060+AlexMyddleware@users.noreply.github.com> Date: Thu, 30 Mar 2023 23:24:43 +0200 Subject: [PATCH 3/5] feat: update documentation (#988) * feat: update documentation for role --- docs/admin_tasks.md | 26 ++++++++++++++++++ docs/images/admin_tasks/cancel_document.png | Bin 0 -> 28066 bytes .../images/admin_tasks/delete_logs_button.png | Bin 0 -> 58820 bytes docs/images/admin_tasks/rule_tasks.png | Bin 0 -> 32650 bytes 4 files changed, 26 insertions(+) create mode 100644 docs/images/admin_tasks/cancel_document.png create mode 100644 docs/images/admin_tasks/delete_logs_button.png create mode 100644 docs/images/admin_tasks/rule_tasks.png diff --git a/docs/admin_tasks.md b/docs/admin_tasks.md index 5f0c97f3d..79cf25718 100644 --- a/docs/admin_tasks.md +++ b/docs/admin_tasks.md @@ -210,3 +210,29 @@ Once that's done, you now need to build your Myddleware instance for production yarn build ``` +## Admin Rights + +| Action | Definition | User | Admin | Super Admin | +|-----------------------|--------------------------------------------------------------------------------------------------|------|-------|-------------| +| Delete the logs | Delete the logs of your current environment, to make debugging easier | | | ✔ | +| Cancel a document | In the document view section, a super admin can force a document to be canceled. The button is hidden if the user doesn't have the required rights. | | | ✔ | +| Rule detail commands | In the rule detail view section, a super admin can cancel all documents at once, delete all documents, or delete the rule itself. Note that all documents must be deleted before deleting the rule. The buttons are hidden if the user doesn't have the required rights. | | | ✔ | +| Open a document | Open a document for viewing | ✔ | ✔ | ✔ | +| Delete a document | Delete the selected document | ✔ | ✔ | ✔ | +| Cancel a document | Cancel a document and change its status to "Cancel" | ✔ | ✔ | ✔ | +| View a rule | Open a rule and see its information | ✔ | ✔ | ✔ | +| Edit a rule | Change important properties of a rule such as the fields or the relationships | ✔ | ✔ | ✔ | +| Run a rule | Launch the rule for all the documents based on the reference | ✔ | ✔ | ✔ | + + + + + +### Delete the logs +![Delete the logs button](images/admin_tasks/delete_logs_button.png) + +### Cancel a Document +![Cancel a document](images/admin_tasks/cancel_document.png) + +### Rule detail commands +![Rule detail commands](images/admin_tasks/rule_tasks.png) \ No newline at end of file diff --git a/docs/images/admin_tasks/cancel_document.png b/docs/images/admin_tasks/cancel_document.png new file mode 100644 index 0000000000000000000000000000000000000000..4abe5c3aad96511c7d96540ea518645649bf0f01 GIT binary patch literal 28066 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

&o6ke~zsck#z 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 h_B`@B7{2$65gpmLM literal 0 HcmV?d00001 diff --git a/docs/images/admin_tasks/delete_logs_button.png b/docs/images/admin_tasks/delete_logs_button.png new file mode 100644 index 0000000000000000000000000000000000000000..b552785253844fafe346c9e6a41ef941e0a64ed2 GIT binary patch literal 58820 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 literal 0 HcmV?d00001 diff --git a/docs/images/admin_tasks/rule_tasks.png b/docs/images/admin_tasks/rule_tasks.png new file mode 100644 index 0000000000000000000000000000000000000000..323e9c7e3eab3633eda4f36263c67d87627fb09d GIT binary patch literal 32650 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 literal 0 HcmV?d00001 From 771d278783ef6076a8964a9be94df8e5a35068f5 Mon Sep 17 00:00:00 2001 From: Myddleware Date: Sat, 1 Apr 2023 22:56:01 +0200 Subject: [PATCH 4/5] Release 3.3.1d (#992) Connector : * Airtable : manage field id with read action * Airtable : manage array fields * SugarCRM : fix bug with deleted record * Moodle : add groups and group_members modules Bugfix : fix order data with the read limit is reached --- .env | 2 +- src/Manager/RuleManager.php | 4 +- src/Solutions/airtable.php | 70 +++++++++++++++------------ src/Solutions/lib/moodle/metadata.php | 4 +- src/Solutions/moodle.php | 11 +++++ src/Solutions/sugarcrm.php | 16 ++++-- 6 files changed, 68 insertions(+), 39 deletions(-) diff --git a/.env b/.env index 533f90cbf..cbaecb321 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -MYDDLEWARE_VERSION=3.3.1c +MYDDLEWARE_VERSION=3.3.1d APP_SECRET=Thissecretisnotsosecretchangeit APP_ENV=prod diff --git a/src/Manager/RuleManager.php b/src/Manager/RuleManager.php index 80acc2fa1..22a282b49 100644 --- a/src/Manager/RuleManager.php +++ b/src/Manager/RuleManager.php @@ -560,7 +560,7 @@ protected function validateReadDataSource() // Order data in the date_modified order $modified = array_column($dataSourceValues, 'date_modified'); - array_multisort($modified, SORT_DESC, $dataSourceValues); + array_multisort($modified, SORT_ASC, $dataSourceValues); foreach ($dataSourceValues as $value) { // Check if the previous record has the same date_modified than the current record // Check only if offset isn't managed into the source application connector @@ -1413,7 +1413,7 @@ protected function massIdRerun(string $documentIds) } } if (in_array($status, ['Predecessor_OK', 'Relate_KO'])) { - $response = $this->ckeckParentDocuments($arrayIdDocument); + $response = $this->checkParentDocument($arrayIdDocument); if (true === $this->verifyMultiIdResponse($response)) { // Update status if an action has been executed $status = 'Relate_OK'; diff --git a/src/Solutions/airtable.php b/src/Solutions/airtable.php index 271995744..54a77b0cd 100644 --- a/src/Solutions/airtable.php +++ b/src/Solutions/airtable.php @@ -43,7 +43,6 @@ class airtablecore extends solution protected string $airtableURL = 'https://api.airtable.com/v0/'; protected string $metadataApiEndpoint = 'https://api.airtable.com/v0/meta/bases/'; - protected array $required_fields = ['default' => ['createdTime', 'Last Modified']]; /** * Airtable base. */ @@ -188,20 +187,15 @@ public function readData($param): array $param['fields'] = $this->cleanMyddlewareElementId($param['fields']); // Add required fields $param['fields'] = $this->addRequiredField($param['fields'], $param['module']); - // There is a bug on the parameter returnFieldsByFieldId soit can't be used - // In case we use fieldsId, we need to get the label to compare with Airtable result (only field label are returned) - include 'lib/airtable/metadata.php'; - foreach ($param['fields'] as $field) { - if ('fld' == substr($field, 0, 3)) { - if (!empty($moduleFields[$baseID][$param['module']][$field]['label'])) { - $fields[$field] = $moduleFields[$baseID][$param['module']][$field]['label']; - continue; - } - } - $fields[$field] = $field; - } - // Get the reference date field name - $dateRefField = $this->getDateRefName($param['module'], $param['ruleParams']['mode']); + + // Get the reference date field name only when we read using reference date + if (empty($param['query'])) { + $dateRefField = $this->getDateRefName($param['module'], $param['ruleParams']['mode']); + // Add the dateRefField in teh field list + if (array_search($dateRefField, $param['fields']) === false) { + $param['fields'][] = $dateRefField; + } + } $stop = false; $page = 1; $offset = ''; @@ -213,7 +207,7 @@ public function readData($param): array if (!empty($param['query'])) { if (!empty($param['query']['id'])) { $id = $param['query']['id']; - $response = $client->request('GET', $this->airtableURL.$baseID.'/'.$param['module'].'/'.$id, $options); + $response = $client->request('GET', $this->airtableURL.$baseID.'/'.$param['module'].'/'.$id.'?returnFieldsByFieldId=true', $options); $statusCode = $response->getStatusCode(); $contentType = $response->getHeaders()['content-type'][0]; $content2 = $response->getContent(); @@ -253,7 +247,7 @@ public function readData($param): array $filterByFormula .= ')'; } // Get all records corresponding to the filters - $response = $client->request('GET', $this->airtableURL.$baseID.'/'.$param['module'].'?'.$filterByFormula, $options); + $response = $client->request('GET', $this->airtableURL.$baseID.'/'.$param['module'].'?returnFieldsByFieldId=true&'.$filterByFormula, $options); $statusCode = $response->getStatusCode(); $contentType = $response->getHeaders()['content-type'][0]; $content = $response->getContent(); @@ -262,7 +256,7 @@ public function readData($param): array } else { // all records $dateRef = $this->dateTimeFromMyddleware($param['date_ref']); - $response = $client->request('GET', $this->airtableURL.$baseID.'/'.$param['module']."?sort[0][field]=Last Modified&filterByFormula=IS_AFTER({Last Modified},'$dateRef')&pageSize=".$this->defaultLimit.'&maxRecords='.$param['limit'].$offset, $options); + $response = $client->request('GET', $this->airtableURL.$baseID.'/'.$param['module']."?sort[0][field]=Last Modified&filterByFormula=IS_AFTER({Last Modified},'$dateRef')&returnFieldsByFieldId=true&pageSize=".$this->defaultLimit.'&maxRecords='.$param['limit'].$offset, $options); $statusCode = $response->getStatusCode(); $contentType = $response->getHeaders()['content-type'][0]; $content = $response->getContent(); @@ -277,19 +271,18 @@ public function readData($param): array $content = $this->convertResponse($param, $content['records']); foreach ($content as $record) { ++$currentCount; - foreach ($fields as $key => $field) { - $fieldWithSpace = str_replace('___', ' ', $field); - if (isset($record['fields'][$fieldWithSpace])) { - // Depending on the field type, the result can be an array, in this case we take the first result - if (is_array($record['fields'][$fieldWithSpace])) { - $result['values'][$record['id']][$key] = current($record['fields'][$fieldWithSpace]); - } else { - $result['values'][$record['id']][$key] = $record['fields'][$fieldWithSpace]; - } - } else { - $result['values'][$record['id']][$key] = ''; + foreach ($param['fields'] as $field) { + if (!empty($record['fields'][$field])) { + // If teh 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]; + } else { + $result['values'][$record['id']][$field] = $record['fields'][$field]; + } + } else { + $result['values'][$record['id']][$field] = ''; } - } + } // Get the reference date if (!empty($record['fields'][$dateRefField])) { @@ -328,7 +321,6 @@ public function readData($param): array $result['error'] = 'Error : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->logger->error($e->getMessage().' '.$e->getFile().' '.$e->getLine()); } - return $result; } @@ -524,7 +516,21 @@ protected function convertResponse($param, $response) public function getDateRefName($moduleSource, $ruleMode): string { - return 'Last Modified'; + // Search the field id + include 'lib/airtable/metadata.php'; + $found_key = array_search('Last Modified', array_column($moduleFields[$this->paramConnexion['projectid']][$moduleSource], 'label'), true); + // Error if not found + if ($found_key === false) { + throw new Exception('Failed to found the date reference field name in the metadata'); + } + + // Get the field id + $fieldId = key(array_slice($moduleFields[$this->paramConnexion['projectid']][$moduleSource], $found_key, $found_key)); + if (empty($fieldId)) { + throw new Exception('Failed to found the id corresponding to the date reference field.'); + } + + return $fieldId; } /** diff --git a/src/Solutions/lib/moodle/metadata.php b/src/Solutions/lib/moodle/metadata.php index 22b8496c5..22cb1a77a 100644 --- a/src/Solutions/lib/moodle/metadata.php +++ b/src/Solutions/lib/moodle/metadata.php @@ -319,7 +319,9 @@ ], 'group_members' => [ - 'groupid' => ['label' => 'Group ID', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0, 'required_relationship' => 1, 'relate' => true], + 'id' => ['label' => 'ID', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0], + 'groupid' => ['label' => 'Group ID', 'type' => 'varchar(255)', 'type_bdd' => 'varchar(255)', 'required' => 0, 'required_relationship' => 1, 'relate' => true], '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], ], ]; diff --git a/src/Solutions/moodle.php b/src/Solutions/moodle.php index c35875f95..aa2fa2d6b 100644 --- a/src/Solutions/moodle.php +++ b/src/Solutions/moodle.php @@ -39,6 +39,8 @@ class moodlecore extends solution 'get_users_last_access' => ['id', 'lastaccess'], 'get_course_completion_by_date' => ['id', 'timecompleted'], 'get_user_grades' => ['id', 'timemodified'], + 'groups' => ['id', 'timemodified'], + 'group_members' => ['id', 'timeadded'], ]; protected array $FieldsDuplicate = [ @@ -116,6 +118,8 @@ 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', + 'groups' => 'Groups', + 'group_members' => 'Group members', ]; } @@ -584,6 +588,10 @@ protected function getFunctionName($param): string return 'local_myddleware_get_users_by_date'; } elseif ('courses' == $param['module']) { return 'local_myddleware_get_courses_by_date'; + } elseif ('groups' == $param['module']) { + return 'local_myddleware_get_groups_by_date'; + } elseif ('group_members' == $param['module']) { + return 'local_myddleware_get_group_members_by_date'; } } // In all other cases @@ -638,6 +646,9 @@ public function getRefFieldName($param): string return 'timemodified'; } break; + case 'group_members': + return 'timeadded'; + break; default: return 'timemodified'; break; diff --git a/src/Solutions/sugarcrm.php b/src/Solutions/sugarcrm.php index 4d18b8105..3be93e029 100644 --- a/src/Solutions/sugarcrm.php +++ b/src/Solutions/sugarcrm.php @@ -286,6 +286,8 @@ public function read($param) $deleted = true; $param['fields'][] = 'deleted'; } + // Flag to know if Myddleware has read only deleted records + $onlyDeletion = true; // Init search parameters $filterArgs = [ @@ -336,7 +338,11 @@ public function read($param) and !empty($record->deleted) ) { $result[$record->id]['myddleware_deletion'] = true; - } + } else { + // At least one non deleted record read + $onlyDeletion = false; + } + foreach ($param['fields'] as $field) { // Sugar returns multilist value as array if ( @@ -352,11 +358,15 @@ public function read($param) } $result[$record->id][$field] = (!empty($record->$field) ? $record->$field : ''); } - // No data returned if record deleted, we set a default date (date ref to have no impact on the final date ref) + // 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'] = $param['date_ref']; + $result[$record->id]['date_modified'] = end($records)->date_modified; } } + // Error if only deletion records read + if ($onlyDeletion) { + 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.'); + } } return $result; From f1294a1adcf3e495efe6fc794001bb71c4fba11a Mon Sep 17 00:00:00 2001 From: Myddleware Date: Fri, 12 May 2023 11:41:17 +0200 Subject: [PATCH 5/5] Release 3.3.1e (#1015) ** Bugfix * Bugfix : no filter on delete document * Bugfix : change order to validate values * Bugfix : DocIdRef was not returned * Bugfix : add missing setter method for manual parameter * Bugfix : change value formula to manage parentheses * Bugfix : We don't send output for the API and Myddleware UI * Bugfix : change the way to get the environment * Bugfix : enhance function changeValue and changeMultiValue * Bugfix : dateref value * Bugfix : Manage space into formula changeValue and changeMultiValue * Bugfix : Rename function checkParentDocument to checkParentDocuments for rule and job classes * Bugfix : Add http-client library * Bugfix : Myddleware 3.3 : Change dockerfile * Bugfix : clearSendData (manage fields 'source_date_modified', 'id_doc_myddleware', 'Myddleware_element_id' ) * Bugfix : output management * Bugfix : manage return null value for check methods * Bugfix : change the type attribute from private to protected * Bugfix : init $emailAddresses attribute ** Connector * SugarCRM : deletion management * SugarCRM : a duplicate field on email address module * Moodle : imporve error management * Moodle : manage create only fields * Moodle : fix issue on new custom field function * Salesforce : add rule mode Update only (U) * Airtable : fix bug on reference date * Airtable : fix ref date field name --- .env | 2 +- Dockerfile | 85 +++---------- composer.json | 1 + composer.lock | 169 ++++++++++++++++++++++++- src/Command/SynchroCommand.php | 2 +- src/Manager/DocumentManager.php | 25 +++- src/Manager/FormulaFunctionManager.php | 10 +- src/Manager/JobManager.php | 7 +- src/Manager/NotificationManager.php | 2 +- src/Manager/RuleManager.php | 18 +-- src/Solutions/airtable.php | 10 +- src/Solutions/erpnext.php | 14 +- src/Solutions/moodle.php | 48 +++++-- src/Solutions/salesforce.php | 16 +-- src/Solutions/solution.php | 21 ++- src/Solutions/sugarcrm.php | 19 ++- src/Solutions/vtigercrm.php | 2 +- templates/Flux/view/view.html.twig | 4 +- 18 files changed, 314 insertions(+), 141 deletions(-) diff --git a/.env b/.env index cbaecb321..734ceeaef 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -MYDDLEWARE_VERSION=3.3.1d +MYDDLEWARE_VERSION=3.3.2a APP_SECRET=Thissecretisnotsosecretchangeit APP_ENV=prod diff --git a/Dockerfile b/Dockerfile index 28c171333..e21386d7a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,95 +1,42 @@ -FROM --platform=linux/amd64 php:7.4.26-apache -LABEL maintainer="Francesco Bianco " +FROM --platform=linux/amd64 php:8.1.17-apache ## Configure PHP RUN apt-get update && apt-get upgrade -y && \ - apt-get -y install -qq --force-yes rsync mariadb-client libzip-dev libicu-dev git zlib1g-dev libc-client-dev libkrb5-dev cron rsyslog unzip libssh2-1-dev gnupg2 alien libaio1 nano vim net-tools iputils-ping telnet && \ + apt-get -y install -qq --force-yes mariadb-client libzip-dev libicu-dev zlib1g-dev libc-client-dev libkrb5-dev gnupg2 libaio1 && \ docker-php-ext-configure intl && docker-php-ext-configure imap --with-kerberos --with-imap-ssl && \ docker-php-ext-install imap exif mysqli pdo pdo_mysql zip intl && \ echo "short_open_tag=off" >> /usr/local/etc/php/conf.d/syntax.ini && \ echo "memory_limit=-1" >> /usr/local/etc/php/conf.d/memory_limit.ini && \ echo "display_errors=0" >> /usr/local/etc/php/conf.d/errors.ini && \ sed -e 's!DocumentRoot /var/www/html!DocumentRoot /var/www/html/public!' -ri /etc/apache2/sites-available/000-default.conf && \ - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer && \ apt-get clean && rm -rf /tmp/* /var/tmp/* /var/lib/apt/lists/* + +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer #RUN pecl install -f ssh2-1.1.2 && docker-php-ext-enable ssh2 -## Install PHP Accelerators -RUN pecl install apcu \ - && pecl install apcu_bc-1.0.3 \ - && docker-php-ext-enable apcu --ini-name 10-docker-php-ext-apcu.ini \ - && docker-php-ext-enable apc --ini-name 20-docker-php-ext-apc.ini +COPY composer.json ./composer.json +COPY composer.lock ./composer.lock +RUN composer install ## Intall NodeJS RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - && \ apt-get update && apt-get install -y nodejs build-essential && npm install -g npm yarn && \ apt-get clean && rm -rf /tmp/* /var/tmp/* /var/lib/apt/lists/* -## Install Xdebug -RUN pecl install -f xdebug && \ - docker-php-ext-enable xdebug && \ - echo "xdebug.remote_enable = 1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \ - echo "xdebug.remote_host=host.docker.internal" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \ - echo "xdebug.remote_autostart = 0" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \ - echo "xdebug.remote_connect_back = 0" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \ - echo "xdebug.remote_port = 9000" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \ - echo "xdebug.remote_handler = 'dbgp'" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \ - echo "xdebug.remote_mode = req" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini - -## Install MS Database -#RUN curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - && \ -# curl https://packages.microsoft.com/config/debian/10/prod.list > /etc/apt/sources.list.d/mssql-release.list && \ -# apt-get update && \ -# apt-get install --no-install-recommends -y msodbcsql17 unixodbc-dev && \ -# pecl install -f sqlsrv pdo_sqlsrv && \ -# docker-php-ext-enable sqlsrv pdo_sqlsrv && \ -# sed -i 's,^\(MinProtocol[ ]*=\).*,\1'TLSv1.0',g' /etc/ssl/openssl.cnf && \ -# sed -i 's,^\(CipherString[ ]*=\).*,\1'DEFAULT@SECLEVEL=1',g' /etc/ssl/openssl.cnf - -## Install Oracle Database -RUN curl "https://download.oracle.com/otn_software/linux/instantclient/195000/oracle-instantclient19.5-sqlplus-19.5.0.0.0-1.x86_64.rpm" -o "/mnt/oracle-instant-sqlplus.rpm" && \ - curl "https://download.oracle.com/otn_software/linux/instantclient/195000/oracle-instantclient19.5-basic-19.5.0.0.0-1.x86_64.rpm" -o "/mnt/oracle-instant-basic.rpm" && \ - curl "https://download.oracle.com/otn_software/linux/instantclient/195000/oracle-instantclient19.5-devel-19.5.0.0.0-1.x86_64.rpm" -o "/mnt/oracle-instant-devel.rpm" && \ - curl "https://download.oracle.com/otn_software/linux/instantclient/195000/oracle-instantclient19.5-odbc-19.5.0.0.0-1.x86_64.rpm" -o "/mnt/oracle-instant-odbc.rpm" && \ - curl "https://download.oracle.com/otn_software/linux/instantclient/195000/oracle-instantclient19.5-tools-19.5.0.0.0-1.x86_64.rpm" -o "/mnt/oracle-instant-tools.rpm" && \ - alien -i /mnt/oracle-instant-sqlplus.rpm && \ - alien -i /mnt/oracle-instant-basic.rpm && \ - alien -i /mnt/oracle-instant-devel.rpm && \ - alien -i /mnt/oracle-instant-odbc.rpm && \ - alien -i /mnt/oracle-instant-tools.rpm && \ - ln -s /usr/lib/oracle/19.5/client64/lib/libsqora.so.19.1 /usr/lib/libsqora.so && \ - rm /mnt/* && \ - export LD_LIBRARY_PATH=/usr/lib/oracle/19.5/client64/lib && \ - export ORACLE_HOME=/usr/lib/oracle/19.5/client64 && \ - export C_INCLUDE_PATH=/usr/include/oracle/19.5/client64 && \ - docker-php-ext-install oci8 pdo_oci +COPY --chown=www-data:www-data . . -## Install Platform tool -RUN curl -sS https://platform.sh/cli/installer | php && \ - ln -s /root/.platformsh/bin/platform /usr/local/bin/platform +# Build packages with yarn +RUN yarn install +RUN yarn run build ## Setup Cronjob -RUN echo "cron.* /var/log/cron.log" >> /etc/rsyslog.conf && rm -fr /etc/cron.* && mkdir /etc/cron.d -COPY docker/etc/crontab /etc/ -RUN chmod 600 /etc/crontab - -## Install DBLIB -#RUN apt-get update && \ -# apt-get install -y freetds-bin freetds-dev freetds-common libct4 libsybdb5 tdsodbc libfreetype6-dev libjpeg62-turbo-dev libmcrypt-dev zlib1g-dev libicu-dev g++ libc-client-dev && \ -# docker-php-ext-configure pdo_dblib --with-libdir=/lib/x86_64-linux-gnu && \ -# docker-php-ext-configure intl && \ -# docker-php-ext-install pdo_dblib && \ -# docker-php-ext-install intl && \ -# docker-php-ext-install mbstring && \ -# docker-php-ext-enable intl mbstring pdo_dblib - -## Sysadmin tools -RUN apt-get update && apt-get upgrade -y && \ - apt-get -y install -qq --force-yes nano vim net-tools iputils-ping telnet +# RUN echo "cron.* /var/log/cron.log" >> /etc/rsyslog.conf && rm -fr /etc/cron.* && mkdir /etc/cron.d +# COPY docker/etc/crontab /etc/ +# RUN chmod 600 /etc/crontab ## Entrypoint and scripts -COPY ./docker/script/myddleware-cron.sh /usr/local/bin/myddleware-cron.sh COPY ./docker/script/myddleware-foreground.sh /usr/local/bin/myddleware-foreground.sh -COPY ./docker/script/myddleware-monitor.sh /usr/local/bin/myddleware-monitor.sh +COPY ./docker/script/myddleware-cron.sh /usr/local/bin/myddleware-cron.sh + RUN chmod +x /usr/local/bin/myddleware-*.sh CMD ["myddleware-foreground.sh"] diff --git a/composer.json b/composer.json index e3ac11f8c..0443660aa 100644 --- a/composer.json +++ b/composer.json @@ -65,6 +65,7 @@ "symfony/flex": "^1.3.1", "symfony/form": "5.4.*", "symfony/framework-bundle": "5.4.*", + "symfony/http-client": "5.4.*", "symfony/http-foundation": "5.4.*", "symfony/mailer": "5.4.*", "symfony/monolog-bundle": "3.8.*", diff --git a/composer.lock b/composer.lock index 081bc91d1..a7d45d190 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2364746f914f74c3813b0187e13c5393", + "content-hash": "b5f2f49aa6d7eaf23a64023c53ae99a6", "packages": [ { "name": "api-platform/core", @@ -7855,6 +7855,171 @@ ], "time": "2023-01-10T17:40:25+00:00" }, + { + "name": "symfony/http-client", + "version": "v5.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "0dabec4e3898d3e00451dd47b5ef839168f9bbf5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/0dabec4e3898d3e00451dd47b5ef839168f9bbf5", + "reference": "0dabec4e3898d3e00451dd47b5ef839168f9bbf5", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/http-client-contracts": "^2.4", + "symfony/polyfill-php73": "^1.11", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.0|^2|^3" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "2.4" + }, + "require-dev": { + "amphp/amp": "^2.5", + "amphp/http-client": "^4.2.1", + "amphp/http-tunnel": "^1.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/http-kernel": "^4.4.13|^5.1.5|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/stopwatch": "^4.4|^5.0|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-client/tree/v5.4.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-04-12T16:02:29+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v2.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "1a4f708e4e87f335d1b1be6148060739152f0bd5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/1a4f708e4e87f335d1b1be6148060739152f0bd5", + "reference": "1a4f708e4e87f335d1b1be6148060739152f0bd5", + "shasum": "" + }, + "require": { + "php": ">=7.2.5" + }, + "suggest": { + "symfony/http-client-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v2.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-03-13T20:07:29+00:00" + }, { "name": "symfony/http-foundation", "version": "v5.4.20", @@ -14338,5 +14503,5 @@ "ext-simplexml": "*" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.1.0" } diff --git a/src/Command/SynchroCommand.php b/src/Command/SynchroCommand.php index 5c976bc60..2650b2f39 100644 --- a/src/Command/SynchroCommand.php +++ b/src/Command/SynchroCommand.php @@ -129,7 +129,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->jobManager->checkPredecessorDocuments(); // Permet de valider qu'au moins un document parent(relation père) est existant - $this->jobManager->checkParentDocument(); + $this->jobManager->checkParentDocuments(); // Permet de transformer les docuement avant d'être envoyés à la cible $this->jobManager->transformDocuments(); diff --git a/src/Manager/DocumentManager.php b/src/Manager/DocumentManager.php index cb46ceaab..29206c67a 100644 --- a/src/Manager/DocumentManager.php +++ b/src/Manager/DocumentManager.php @@ -68,6 +68,7 @@ class documentcore protected $jobId; protected $key; protected $docIdRefError; + protected $env; protected bool $transformError = false; protected ?ToolsManager $tools; protected $api; // Specify if the class is called by the API @@ -122,6 +123,7 @@ public function __construct( $this->tools = $tools; $this->formulaManager = $formulaManager; $this->solutionManager = $solutionManager; + $this->env = $_SERVER['APP_ENV']; } public static function lstGblStatus(): array @@ -361,8 +363,12 @@ public function filterDocument($ruleFilters) } try { $filterOK = true; - // Si des filtres sont présents - if (!empty($ruleFilters)) { + // Only if there is a least one filter + // No filter on delete document as they will be filter after is Myddleware never sent the data + if ( + !empty($ruleFilters) + AND $this->documentType != 'D' + ) { // Boucle sur les filtres foreach ($ruleFilters as $ruleFilter) { if (!$this->checkFilter($this->sourceData[$ruleFilter['target']], $ruleFilter['type'], $ruleFilter['value'])) { @@ -689,7 +695,7 @@ public function checkPredecessorDocument(): bool return true; } catch (\Exception $e) { // Reference document id is used to show which document is blocking the current document in Myddleware - $this->docIdRefError = (is_array($result) and !empty($result['id']) ? $result['id'] : ''); + $this->docIdRefError = ((is_array($result) and !empty($result['id'])) ? $result['id'] : ''); $this->message .= 'Failed to check document predecessor : '.$e->getMessage().' '.$e->getFile().' Line : ( '.$e->getLine().' )'; $this->typeError = 'E'; $this->updateStatus('Predecessor_KO'); @@ -1922,7 +1928,11 @@ public function updateStatus($new_status) WHERE id = :id '; - if (!$this->api) { + // We don't send output for the API and Myddleware UI + if ( + !$this->api + AND $this->env == 'background' + ) { echo 'status '.$new_status.' id = '.$this->id.' '.$now.chr(10); } // Suppression de la dernière virgule @@ -1962,7 +1972,11 @@ public function updateDeleteFlag($deleted) WHERE id = :id '; - if (!$this->api) { + // We don't send output for the API and Myddleware UI + if ( + !$this->api + AND $this->env != 'prod' + ) { echo(!empty($deleted) ? 'Remove' : 'Restore').' document id = '.$this->id.' '.$now.chr(10); } $stmt = $this->connection->prepare($query); @@ -2180,6 +2194,7 @@ protected function getTargetId($ruleRelationship, $record_id) { try { $direction = $this->getRelationshipDirection($ruleRelationship); + // En fonction du sens de la relation, la recherche du parent id peut-être inversée (recherchée en source ou en cible) // Search all documents with target ID not empty in status close or no_send (document canceled but it is a real document) if ('-1' == $direction) { diff --git a/src/Manager/FormulaFunctionManager.php b/src/Manager/FormulaFunctionManager.php index 74c08dfd1..699a06ead 100644 --- a/src/Manager/FormulaFunctionManager.php +++ b/src/Manager/FormulaFunctionManager.php @@ -81,7 +81,9 @@ public static function changeFormatDate($dateToChange, $oldFormat, $newFormat) public static function changeValue($var, $arrayKeyToValue, $acceptNull = null) { // Transform string into an array - $arrayKeyToValue = json_decode(str_replace(['(', ')', '\''], ['{', '}', '"'], $arrayKeyToValue), true); + // Change first and last characters (parentheses) by accolades + // Replace ' before and after , and : by " (manage space before , and :) + $arrayKeyToValue = json_decode('{"'.str_replace([ ':\'', '\':', ': \'', '\' :', ',\'', '\',', ', \'', '\' ,'], [ ':"', '":', ':"', '":', ',"', '",', ',"', '",'], substr($arrayKeyToValue,2,-2)).'"}', true); if (in_array($var, array_keys($arrayKeyToValue))) { return $arrayKeyToValue[$var]; } @@ -92,11 +94,13 @@ public static function changeValue($var, $arrayKeyToValue, $acceptNull = null) public static function changeMultiValue($var, $arrayKeyToValue, $delimiter) { - // Transform $var into array + // Transform string into an array + // Change first and last characters (parentheses) by accolades + // Replace ' before and after , and : by " (manage space before , and :) $return = ''; $arrayVar = explode($delimiter, $var); if (!empty($arrayVar)) { - $arrayKeyToValue = json_decode(str_replace(['(', ')', '\''], ['{', '}', '"'], $arrayKeyToValue), true); + $arrayKeyToValue = json_decode('{"'.str_replace([ ':\'', '\':', ': \'', '\' :', ',\'', '\',', ', \'', '\' ,'], [ ':"', '":', ':"', '":', ',"', '",', ',"', '",'], substr($arrayKeyToValue,2,-2)).'"}', true); foreach ($arrayVar as $varValue) { // Transform string into an array if (!empty($arrayKeyToValue[$varValue])) { diff --git a/src/Manager/JobManager.php b/src/Manager/JobManager.php index 6780c1f1a..76c0637e7 100644 --- a/src/Manager/JobManager.php +++ b/src/Manager/JobManager.php @@ -234,9 +234,9 @@ public function filterDocuments() } // Permet de contrôler si un docuement a une relation mais n'a pas de correspondance d'ID pour cette relation dans Myddleware - public function checkParentDocument() + public function checkParentDocuments() { - $this->ruleManager->checkParentDocument(); + $this->ruleManager->checkParentDocuments(); } // Permet de trasformer les documents @@ -287,6 +287,7 @@ public function runError($limit, $attempt) 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); $errorActionDocument = $this->ruleManager->actionDocument($documentError['id'], 'rerun'); if (!empty($errorActionDocument)) { @@ -501,6 +502,7 @@ public function massAction($action, $dataType, $ids, $forceAll, $fromStatus, $to if ($param['ruleId'] != $document['rule_id']) { $this->ruleManager->setApi($this->api); $this->ruleManager->setJobId($this->id); + $this->ruleManager->setManual($this->manual); $this->ruleManager->setRule($document['rule_id']); } $this->ruleManager->actionDocument($document['id'], $action, $toStatus); @@ -544,6 +546,7 @@ public function readRecord($ruleId, $filterQuery, $filterValues, $usesDocumentId // We instanciate the rule $this->ruleManager->setRule($ruleId); $this->ruleManager->setJobId($this->id); + $this->ruleManager->setManual($this->manual); $this->ruleManager->setApi($this->api); // We create an array that will match the initial structure of the function diff --git a/src/Manager/NotificationManager.php b/src/Manager/NotificationManager.php index ea4791b2c..e87341944 100644 --- a/src/Manager/NotificationManager.php +++ b/src/Manager/NotificationManager.php @@ -51,7 +51,7 @@ class NotificationManager { protected EntityManagerInterface $entityManager; - protected $emailAddresses; + protected $emailAddresses = array(); protected $configParams; protected ToolsManager $tools; private LoggerInterface $logger; diff --git a/src/Manager/RuleManager.php b/src/Manager/RuleManager.php index 22a282b49..8b26cba60 100644 --- a/src/Manager/RuleManager.php +++ b/src/Manager/RuleManager.php @@ -560,7 +560,7 @@ protected function validateReadDataSource() // Order data in the date_modified order $modified = array_column($dataSourceValues, 'date_modified'); - array_multisort($modified, SORT_ASC, $dataSourceValues); + array_multisort($modified, SORT_DESC, $dataSourceValues); foreach ($dataSourceValues as $value) { // Check if the previous record has the same date_modified than the current record // Check only if offset isn't managed into the source application connector @@ -698,7 +698,7 @@ public function checkPredecessorDocuments($documents = null): array * * @throws \Doctrine\DBAL\Exception */ - public function checkParentDocument($documents = null): array + public function checkParentDocuments($documents = null): array { // include_once 'document.php'; // Permet de charger dans la classe toutes les relations de la règle @@ -1270,7 +1270,7 @@ protected function rerun($id_document): array $status = $this->documentManager->getStatus(); } if (in_array($status, ['Predecessor_OK', 'Relate_KO'])) { - $response = $this->checkParentDocument([['id' => $id_document]]); + $response = $this->checkParentDocuments([['id' => $id_document]]); if (true === $response[$id_document]) { $msg_success[] = 'Transfer id '.$id_document.' : Status change => Relate_OK'; } else { @@ -1413,7 +1413,7 @@ protected function massIdRerun(string $documentIds) } } if (in_array($status, ['Predecessor_OK', 'Relate_KO'])) { - $response = $this->checkParentDocument($arrayIdDocument); + $response = $this->checkParentDocuments($arrayIdDocument); if (true === $this->verifyMultiIdResponse($response)) { // Update status if an action has been executed $status = 'Relate_OK'; @@ -1510,15 +1510,15 @@ protected function clearSendData($sendData) if (!empty($sendData)) { foreach ($sendData as $key => $value) { if (isset($value['source_date_modified'])) { - unset($sendData->{$key}['source_date_modified']); + unset($sendData[$key]['source_date_modified']); } if (isset($value['id_doc_myddleware'])) { - unset($sendData->{$key}['id_doc_myddleware']); + unset($sendData[$key]['id_doc_myddleware']); } - + if (isset($value['Myddleware_element_id'])) { + unset($sendData[$key]['Myddleware_element_id']); + } } - - return $sendData; } } diff --git a/src/Solutions/airtable.php b/src/Solutions/airtable.php index 54a77b0cd..57c3d7d31 100644 --- a/src/Solutions/airtable.php +++ b/src/Solutions/airtable.php @@ -196,6 +196,7 @@ public function readData($param): array $param['fields'][] = $dateRefField; } } + $stop = false; $page = 1; $offset = ''; @@ -285,7 +286,10 @@ public function readData($param): array } // Get the reference date - if (!empty($record['fields'][$dateRefField])) { + if ( + !empty($dateRefField) + AND !empty($record['fields'][$dateRefField]) + ) { $dateModified = $record['fields'][$dateRefField]; // createdTime not allowed for reading action, only to get an history or a duplicate field } elseif ( @@ -399,10 +403,6 @@ public function upsert(string $method, array $param): array ++$i; continue; } - // Myddleware_element_id is a field only used by Myddleware. Not sent to the target application - if (!empty($data['Myddleware_element_id'])) { - unset($data['Myddleware_element_id']); - } $body['records'][$i]['fields'] = $data; diff --git a/src/Solutions/erpnext.php b/src/Solutions/erpnext.php index ff6ebf327..67259cffe 100644 --- a/src/Solutions/erpnext.php +++ b/src/Solutions/erpnext.php @@ -327,14 +327,12 @@ public function createUpdate($method, $param): array try { foreach ($data as $key => $value) { // We don't send Myddleware fields - if (in_array($key, ['target_id', 'Myddleware_element_id'])) { - if ( - 'target_id' == $key - and !empty($value) - ) { - $url = $this->paramConnexion['url'].'/api/resource/'.rawurlencode($param['module']).'/'.rawurlencode($value); - } - unset($data[$key]); + if ( + 'target_id' == $key + and !empty($value) + ) { + $url = $this->paramConnexion['url'].'/api/resource/'.rawurlencode($param['module']).'/'.rawurlencode($value); + unset($data[$key]); // if the data is a link } elseif ('link_doctype' == $key) { $data['links'] = [['link_doctype' => $data[$key], 'link_name' => $data['link_name']]]; diff --git a/src/Solutions/moodle.php b/src/Solutions/moodle.php index aa2fa2d6b..9615b99d4 100644 --- a/src/Solutions/moodle.php +++ b/src/Solutions/moodle.php @@ -48,6 +48,10 @@ class moodlecore extends solution 'courses' => ['shortname', 'idnumber'], ]; + protected array $createOnlyFields = [ + 'courses' => ['lang'], + ]; + protected string $delaySearch = '-1 year'; public function login($paramConnexion) @@ -191,14 +195,14 @@ public function read($param): array $functionName = $this->getFunctionName($param); // Get the custom fields set in the connector $customFieldList = $this->getCustomFields($param); - // Init the attribute name and value for custom fields - $attributeName = ($param['module'] == 'courses' ? 'shortname' : 'name'); + // Init the attribute value for custom fields depending on the module $attributeValue = ($param['module'] == 'courses' ? 'valueraw' : 'value'); // Call to Moodle $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 $xml->ERRORCODE : $xml->MESSAGE"); } @@ -229,7 +233,7 @@ public function read($param): array $customFieldValue = ''; $customFieldName = ''; foreach($customField->KEY as $customFieldValues) { - if ($customFieldValues->attributes()->__toString() == $attributeName) { + if ($customFieldValues->attributes()->__toString() == 'shortname') { $customFieldName = $customFieldValues->VALUE->__toString(); } elseif ($customFieldValues->attributes()->__toString() == $attributeValue) { $customFieldValue = $customFieldValues->VALUE->__toString(); @@ -273,10 +277,6 @@ public function createData($param): array $dataSugar = []; $obj = new \stdClass(); foreach ($data as $key => $value) { - // We don't send Myddleware_element_id field to Moodle - if (in_array($key, array('Myddleware_element_id', 'source_date_modified', 'id_doc_myddleware'))) { - continue; - } if (!empty($value)) { // if $value belongs to $this->paramConnexion[user_custom_fields] then we add it to $obj->customfields if (in_array($key, $customFieldList)) { @@ -336,7 +336,17 @@ public function createData($param): array $serverurl = $this->paramConnexion['url'].'/webservice/rest/server.php'.'?wstoken='.$this->paramConnexion['token'].'&wsfunction='.$functionname; $response = $this->moodleClient->post($serverurl, $params); $xml = simplexml_load_string($response); + + // Check if there is a warning + if ( + !empty($xml->SINGLE) + AND $xml->SINGLE->KEY->attributes()->__toString() == 'warnings' + AND !empty($xml->SINGLE->KEY->MULTIPLE->SINGLE->KEY[3]) + ) { + throw new \Exception('ERROR : '.$xml->SINGLE->KEY->MULTIPLE->SINGLE->KEY[3]->VALUE.chr(10)); + } + // Réponse standard pour les modules avec retours if ( !empty($xml->MULTIPLE->SINGLE->KEY->VALUE) @@ -404,10 +414,7 @@ public function updateData($param): array foreach ($data as $key => $value) { if ('target_id' == $key) { continue; - // We don't send Myddleware_element_id field to Moodle - } elseif (in_array($key, array('Myddleware_element_id','source_date_modified','id_doc_myddleware'))) { - continue; - } + } if (!empty($value)) { // if $value belongs to $this->paramConnexion[user_custom_fields] then we add it to $obj->customfields if (in_array($key, $customFieldList)) { @@ -466,6 +473,15 @@ public function updateData($param): array $serverurl = $this->paramConnexion['url'].'/webservice/rest/server.php'.'?wstoken='.$this->paramConnexion['token'].'&wsfunction='.$functionname; $response = $this->moodleClient->post($serverurl, $params); $xml = simplexml_load_string($response); + + // Check if there is a warning + if ( + !empty($xml) + AND $xml->SINGLE->KEY->attributes()->__toString() == 'warnings' + AND !empty($xml->SINGLE->KEY->MULTIPLE->SINGLE->KEY[3]) + ) { + throw new \Exception('ERROR : '.$xml->SINGLE->KEY->MULTIPLE->SINGLE->KEY[3]->VALUE.chr(10)); + } // Réponse standard pour les modules avec retours if (!empty($xml->ERRORCODE)) { @@ -513,6 +529,14 @@ protected function checkDataBeforeUpdate($param, $data, $idDoc=null) ) { unset($data['createpassword']); } + // Rempove create only field + if (!empty($this->createOnlyFields[$param['module']])) { + foreach($this->createOnlyFields[$param['module']] as $createOnlyField) { + if (isset($data[$createOnlyField])) { + unset($data[$createOnlyField]); + } + } + } return parent::checkDataBeforeUpdate($param, $data, $idDoc); } @@ -670,7 +694,7 @@ protected function getCustomFields ($param) { ) { return explode(',',$this->paramConnexion['course_custom_fields']); } - return null; + return array(); } // Function to add custom fields for course and user modules. diff --git a/src/Solutions/salesforce.php b/src/Solutions/salesforce.php index f7abad779..bc0181cf4 100644 --- a/src/Solutions/salesforce.php +++ b/src/Solutions/salesforce.php @@ -489,11 +489,7 @@ public function createData($param): array $parameter = array(); $parameter['attributes'] = array('type' => $param['module'], 'referenceId' => 'Ref'.$i); foreach ($data as $key => $value) { - // On n'envoie jamais le champ Myddleware_element_id à Salesforce - if (in_array($key, array('Myddleware_element_id','id_doc_myddleware','source_date_modified'))) { - continue; - } - elseif ($key == 'target_id') { + if ($key == 'target_id') { continue; } elseif($key == 'Birthdate') { @@ -604,11 +600,7 @@ public function updateData($param): array // Instanciation de l'URL d'appel $query_url = $this->instance_url."/services/data/".$this->versionApi."/sobjects/" . $param['module'] . '/'; foreach ($data as $key => $value) { - // On n'envoie jamais le champ Myddleware_element_id à Salesforce - if (in_array($key, array('Myddleware_element_id','id_doc_myddleware','source_date_modified'))) { - continue; - } - elseif ($key == 'target_id') { + if ($key == 'target_id') { $target_id = $value; // Ajout de l'ID à l'URL pour la modification $query_url .= $value . '/'; @@ -775,12 +767,12 @@ public function getRuleMode($module,$type): array */ public function getRefFieldName($param): string { - if(in_array($param['ruleParams']['mode'],array("0","S"))) { + if(in_array($param['ruleParams']['mode'],array("0","S","U"))) { return "LastModifiedDate"; } else if ($param['ruleParams']['mode'] == "C"){ return "CreatedDate"; } else { - throw new \Exception ("$param[ruleParams][mode] is not a correct Rule mode."); + throw new \Exception ($param['ruleParams']['mode']." is not a correct Rule mode."); } return ""; } diff --git a/src/Solutions/solution.php b/src/Solutions/solution.php index e3ac41687..145ba7670 100644 --- a/src/Solutions/solution.php +++ b/src/Solutions/solution.php @@ -78,9 +78,9 @@ class solutioncore protected Connection $connection; protected ParameterBagInterface $parameterBagInterface; protected EntityManagerInterface $entityManager; - private DocumentRepository $documentRepository; - private RuleRelationShipRepository $ruleRelationshipsRepository; - private FormulaManager $formulaManager; + protected DocumentRepository $documentRepository; + protected RuleRelationShipRepository $ruleRelationshipsRepository; + protected FormulaManager $formulaManager; public function __construct( LoggerInterface $logger, @@ -397,6 +397,7 @@ public function read($param) public function createData($param): array { try { + $result = array(); // For every document foreach ($param['data'] as $idDoc => $record) { try { @@ -405,6 +406,10 @@ public function createData($param): array // Check control before create $record = $this->checkDataBeforeCreate($param, $record, $idDoc); + // No action if null is returned + if ($record === null) { + continue; + } // Call create method $recordId = $this->create($param, $record, $idDoc); @@ -481,6 +486,7 @@ protected function create($param, $record, $idDoc = null) public function updateData($param): array { try { + $result = array(); // For every document foreach ($param['data'] as $idDoc => $record) { try { @@ -492,6 +498,10 @@ public function updateData($param): array } // Check control before create $record = $this->checkDataBeforeUpdate($param, $record, $idDoc); + // No action if null is returned + if ($record === null) { + continue; + } // Call create methode $recordId = $this->update($param, $record, $idDoc); @@ -535,6 +545,7 @@ protected function update($param, $data, $idDoc = null) public function deleteData($param): array { try { + $result = array(); // For every document foreach ($param['data'] as $idDoc => $record) { try { @@ -543,6 +554,10 @@ public function deleteData($param): array } // Check control before delete $record = $this->checkDataBeforeDelete($param, $record); + // No action if null is returned + if ($record === null) { + continue; + } // Call delete methode $recordId = $this->delete($param, $record); diff --git a/src/Solutions/sugarcrm.php b/src/Solutions/sugarcrm.php index 3be93e029..534da49ac 100644 --- a/src/Solutions/sugarcrm.php +++ b/src/Solutions/sugarcrm.php @@ -43,14 +43,17 @@ class sugarcrmcore extends solution protected array $required_fields = ['default' => ['id', 'date_modified']]; - protected array $FieldsDuplicate = ['Contacts' => ['email1', 'last_name'], + protected array $FieldsDuplicate = + [ + 'default' => ['name'], + 'Contacts' => ['email1', 'last_name'], 'Accounts' => ['email1', 'name'], 'Users' => ['email1', 'last_name'], 'Leads' => ['email1', 'last_name'], 'Prospects' => ['email1', 'name'], - 'default' => ['name'], + 'EmailAddresses' => ['email_address'], ]; - + public function getFieldsLogin(): array { return [ @@ -363,12 +366,18 @@ public function read($param) $result[$record->id]['date_modified'] = end($records)->date_modified; } } + // Error if only deletion records read if ($onlyDeletion) { - 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.'); + 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/src/Solutions/vtigercrm.php b/src/Solutions/vtigercrm.php index a2689ec02..d60b3a732 100644 --- a/src/Solutions/vtigercrm.php +++ b/src/Solutions/vtigercrm.php @@ -701,7 +701,7 @@ public function deleteData($param): array // Clean a record by removing all Myddleware fields protected function cleanRecord($param, $data) { - $myddlewareFields = ['target_id', 'source_date_modified', 'id_doc_myddleware', 'Myddleware_element_id']; + $myddlewareFields = ['target_id']; foreach ($myddlewareFields as $myddlewareField) { if (array_key_exists($myddlewareField, $data)) { unset($data[$myddlewareField]); diff --git a/templates/Flux/view/view.html.twig b/templates/Flux/view/view.html.twig index 014513c30..4b00709dd 100644 --- a/templates/Flux/view/view.html.twig +++ b/templates/Flux/view/view.html.twig @@ -340,7 +340,7 @@ {{'list_flux.tab.name'|trans}} {{'list_flux.tab.source_id'|trans}} {{'list_flux.tab.target_id'|trans}} - {{'list_flux.tab.date_modified'|trans}} + {{'list_flux.tab.dateref'|trans}} {{'list_flux.tab.type'|trans}} {{'list_flux.tab.statut'|trans}} @@ -363,7 +363,7 @@ {{ flux.source }} {{ flux.target }} - {{ flux.dateModified|date("d/m/Y H:i:s", timezone) }} + {{ flux.sourceDateModified|date("d/m/Y H:i:s", timezone) }} {{ flux.type }}