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