From 5a786a0229ff4ef17de205d56543615d49a43983 Mon Sep 17 00:00:00 2001 From: tomolimo Date: Tue, 21 May 2019 10:39:45 +0200 Subject: [PATCH 01/15] Set version to 3.4.9 Reset EOL to CRLF instead of LF Fixed some typos and CS Fixed HTTPS support when certificates are not OK Fixed ['MESSAGE_AFTER_REDIRECT'][WARNING] instead of ERROR Fixed upload of files when in grid Fixed get_pdfdata to return correct task content Fixed solution vs status priority Fixed random pb of blank page when viewing case tasks Fixed two issues on update: default DB version when updating from unknown DB version, and id column for users when updating to 3.2.8 Added "under maintenance" messages and images to processes Added default search for process and case lists Added max cases per item management Added GLPI_ITEM_GLOBAL_VALIDATION, GLPI_TICKET_TYPE, GLPI_ITEM_DUE_DATE, GLPI_ITEM_STATUS, GLPI_ITEM_TASK_REMINDER Added traces for the case link actions Review domain.js management Deleted old empty-database files fixes #81 --- README.md | 2 +- ajax/dropdownProcesses.php | 13 +- front/case.form.php | 9 +- front/case.php | 13 +- front/process_profile.form.php | 2 +- front/processmaker.form.php | 22 +- front/processmaker.helpdesk.form.php | 1 + hook.php | 177 ++----- inc/case.class.php | 230 ++++----- inc/casechangelog.class.php | 9 +- inc/casedynaform.class.php | 3 +- inc/casehistory.class.php | 9 +- inc/caselinkaction.class.php | 2 +- inc/casemap.class.php | 9 +- inc/config.class.php | 21 +- inc/process.class.php | 59 ++- inc/processmaker.class.php | 439 +++++++++--------- inc/selfservicedraft.class.php | 2 +- inc/task.class.php | 1 + inc/taskcategory.class.php | 14 +- install/install.php | 23 +- install/mysql/3.2.8-empty.sql | 167 ------- install/mysql/3.2.9-empty.sql | 168 ------- install/mysql/3.3.0-empty.sql | 179 ------- install/mysql/3.3.1-empty.sql | 186 -------- ...3.3.8-empty.sql => processmaker-empty.sql} | 7 +- install/update.php | 5 + install/update_3_3_1_to_3_3_8.php | 2 +- install/update_3_3_8_to_3_4_9.php | 30 ++ install/update_to_3_2_8.php | 2 +- js/cases.helpdesk.js | 10 +- js/cases.js | 2 +- js/central.js | 11 +- js/domain.js.php | 19 - js/helpdesk.public.js.php | 2 +- pics/small-under_maintenance.png | Bin 0 -> 39576 bytes pics/verysmall-under_maintenance.png | Bin 0 -> 13147 bytes processmaker.xml | 2 +- setup.php | 25 +- 39 files changed, 559 insertions(+), 1318 deletions(-) delete mode 100644 install/mysql/3.2.8-empty.sql delete mode 100644 install/mysql/3.2.9-empty.sql delete mode 100644 install/mysql/3.3.0-empty.sql delete mode 100644 install/mysql/3.3.1-empty.sql rename install/mysql/{3.3.8-empty.sql => processmaker-empty.sql} (96%) create mode 100644 install/update_3_3_8_to_3_4_9.php delete mode 100644 js/domain.js.php create mode 100644 pics/small-under_maintenance.png create mode 100644 pics/verysmall-under_maintenance.png diff --git a/README.md b/README.md index b7d1bad..d554ca2 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ GLPI plugin that provides an interface with a customized ProcessMaker server (https://github.com/tomolimo/processmaker-server). -version 3.4.5 is compatible with GLPI 9.2 and needs ProcessMaker either 3.0.1.8-RE-1.12 (https://github.com/tomolimo/processmaker-server/releases/tag/3.0.1.8-RE-1.12) or 3.3.0-RE-1.0 (https://github.com/tomolimo/processmaker-server/releases/tag/3.3.0-RE-1.0) +version 3.4.x is compatible with GLPI 9.2 and needs ProcessMaker either 3.0.1.8-RE-1.12 (https://github.com/tomolimo/processmaker-server/releases/tag/3.0.1.8-RE-1.12) or 3.3.0-RE-1.x (https://github.com/tomolimo/processmaker-server/releases/tag/3.3.0-RE-1.0) This plugin can run classic and bpmn processes diff --git a/ajax/dropdownProcesses.php b/ajax/dropdownProcesses.php index af506a7..1fa8719 100644 --- a/ajax/dropdownProcesses.php +++ b/ajax/dropdownProcesses.php @@ -61,15 +61,22 @@ } $processall = (isset($_REQUEST['specific_tags']['process_restrict']) && !$_REQUEST['specific_tags']['process_restrict']); +$count_cases_per_item = isset($_REQUEST['specific_tags']['count_cases_per_item']) ? $_REQUEST['specific_tags']['count_cases_per_item'] : []; $result = PluginProcessmakerProcess::getSqlSearchResult(false, $search); if ($DB->numrows($result)) { while ($data = $DB->fetch_array($result)) { $process_entities = PluginProcessmakerProcess::getEntitiesForProfileByProcess($data["id"], $_SESSION['glpiactiveprofile']['id'], true); - if ($processall || in_array( $_REQUEST["entity_restrict"], $process_entities)) { - array_push( $processes, [ 'id' => $data["id"], - 'text' => $data["name"] ]); + $can_add = $data['max_cases_per_item'] == 0 || !isset($count_cases_per_item[$data["id"]]) || $count_cases_per_item[$data["id"]] < $data['max_cases_per_item']; + if ($processall + || ($data['maintenance'] != 1 + && in_array( $_REQUEST["entity_restrict"], $process_entities) + && $can_add) ) { + + array_push( $processes, ['id' => $data["id"], + 'text' => $data["name"] + ]); $count++; } } diff --git a/front/case.form.php b/front/case.form.php index 6ed607f..f3f63ac 100644 --- a/front/case.form.php +++ b/front/case.form.php @@ -22,16 +22,13 @@ function glpi_processmaker_case_reload_page() { if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'route' && isset( $_REQUEST['UID'] ) && isset( $_REQUEST['APP_UID'] ) && isset( $_REQUEST['__DynaformName__'] )) { // then get item id from DB if ($locCase->getFromGUID($_REQUEST['APP_UID'])) { - - if (isset( $_REQUEST['form'] )) { - $PM_SOAP->derivateCase($locCase, $_REQUEST); - } + $PM_SOAP->derivateCase($locCase, $_REQUEST); } glpi_processmaker_case_reload_page(); -} else if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'delete') { +} else if (isset($_REQUEST['purge'])) { // delete case from case table, this will also delete the tasks - if ($locCase->getFromDB($_POST['cases_id']) && $locCase->deleteCase()) { + if ($locCase->getFromDB($_REQUEST['id']) && $locCase->deleteCase()) { Session::addMessageAfterRedirect(__('Case has been deleted!', 'processmaker'), true, INFO); } else { Session::addMessageAfterRedirect(__('Unable to delete case!', 'processmaker'), true, ERROR); diff --git a/front/case.php b/front/case.php index 8cb05e5..45bb2ac 100644 --- a/front/case.php +++ b/front/case.php @@ -5,19 +5,8 @@ Html::header(__('ProcessMaker', 'processmaker'), $_SERVER['PHP_SELF'], "helpdesk", "PluginProcessmakerCase", "cases"); if (!$PM_SOAP->config->fields['maintenance']) { - if (Session::haveRightsOr("plugin_processmaker_config", [READ, UPDATE])) { + if (Session::haveRightsOr("plugin_processmaker_case", [READ, UPDATE])) { - // force default sort to column id / DESC - if (empty($_SESSION['glpisearch']['PluginProcessmakerCase']) - || isset($_GET["reset"]) - || !isset($_GET["sort"]) - ) { - $_SESSION['glpisearch']['PluginProcessmakerCase']['order'] = 'DESC'; - $_SESSION['glpisearch']['PluginProcessmakerCase']['sort'] = '1'; - if (isset($_GET["reset"])) { - unset($_GET['reset']); - } - } Search::show('PluginProcessmakerCase'); } else { Html::displayRightError(); diff --git a/front/process_profile.form.php b/front/process_profile.form.php index c273793..1de5fda 100644 --- a/front/process_profile.form.php +++ b/front/process_profile.form.php @@ -12,6 +12,6 @@ if ($right->add($_POST)) { } Html::back(); -} +} Html::displayErrorAndDie("lost"); diff --git a/front/processmaker.form.php b/front/processmaker.form.php index a2a0b2f..5345afa 100644 --- a/front/processmaker.form.php +++ b/front/processmaker.form.php @@ -3,12 +3,9 @@ switch ($_POST["action"]) { case 'newcase': - if (isset($_POST['items_id']) && $_POST['items_id'] > 0) { // then this case will be bound to an item - // TODO: we must check if a case is not already existing - // to manage the problem of F5 (Refresh) + if (isset($_POST['items_id']) && $_POST['items_id'] > 0) { - //$hasCase = PluginProcessmakerCase::getIDFromItem($_POST['itemtype'], $_POST['items_id']); - //if ($hasCase === false && $_POST['plugin_processmaker_processes_id'] > 0) { + // then this case will be bound to an item if ($_POST['plugin_processmaker_processes_id'] > 0) { $resultCase = $PM_SOAP->startNewCase($_POST['plugin_processmaker_processes_id'], $_POST['itemtype'], $_POST['items_id'], Session::getLoginUserID()); @@ -39,11 +36,18 @@ Html::back(); } } else { // the case is created before the ticket (used for post-only case creation before ticket creation) + $pm_user_guid = PluginProcessmakerUser::getPMUserId( Session::getLoginUserID() ); $resultCase = $PM_SOAP->newCase( $_POST['plugin_processmaker_processes_id'], - [ 'GLPI_ITEM_CAN_BE_SOLVED' => 0, - 'GLPI_SELFSERVICE_CREATED' => '1', - 'GLPI_ITEM_TYPE' => 'Ticket', - 'GLPI_URL' => $CFG_GLPI['url_base']] ); + ['GLPI_ITEM_CAN_BE_SOLVED' => 0, + 'GLPI_SELFSERVICE_CREATED' => '1', + 'GLPI_ITEM_TYPE' => 'Ticket', + 'GLPI_URL' => $CFG_GLPI['url_base'], + // Specific to Tickets + // GLPI_TICKET_TYPE will contains 1 (= incident) or 2 (= request) + 'GLPI_TICKET_TYPE' => $_POST['type'], + 'GLPI_ITEM_REQUESTER_GLPI_ID' => Session::getLoginUserID(), + 'GLPI_ITEM_REQUESTER_PM_ID' => $pm_user_guid + ] ); if ($resultCase->status_code == 0) { // case is created // Must show it... diff --git a/front/processmaker.helpdesk.form.php b/front/processmaker.helpdesk.form.php index 212a910..5a7175a 100644 --- a/front/processmaker.helpdesk.form.php +++ b/front/processmaker.helpdesk.form.php @@ -54,6 +54,7 @@ function processMakerShowCase($ID, $from_helpdesk) { $rand = rand(); + $PM_SOAP->echoDomain(); echo ""; $tkt = new Ticket; diff --git a/hook.php b/hook.php index 130e12b..85cad3f 100644 --- a/hook.php +++ b/hook.php @@ -24,66 +24,6 @@ function plugin_processmaker_MassiveActions($type) { } -//function plugin_processmaker_MassiveActionsDisplay($options) { - -// switch ($options['itemtype']) { -// case 'PluginProcessmakerProcess' : -// //case 'PluginProcessmakerCase' : -// switch ($options['action']) { -// case "plugin_processmaker_taskrefresh" : -// //case "plugin_processmaker_purgecase" : -// echo ""; -// break; - -// } -// break; -// } -// return ""; -//} - - -//function plugin_processmaker_MassiveActionsProcess($data) { - -// switch ($data['action']) { - -// case "plugin_processmaker_taskrefresh" : -// if ($data['itemtype'] == 'PluginProcessmakerProcess') { -// foreach ($data["item"] as $key => $val) { -// if ($val == 1) { -// $process = new PluginProcessmakerProcess; -// $process->refreshTasks( array( 'id' => $key ) ); - -// } -// } -// } -// break; -// //case "plugin_processmaker_purgecase": -// // if ($data['itemtype'] == 'PluginProcessmakerCase') { -// // foreach ($data["item"] as $key => $val) { -// // if ($val == 1) { -// // $locCase= new PluginProcessmakerCase; -// // //$locCase->( array( 'id' => $key ) ); - -// // } -// // } -// // } -// // break; -// //case 'plugin_processmaker_process_profile_delete' : -// // if ($data['itemtype'] == 'PluginProcessmakerProcess_Profile') { -// // foreach ($data["item"] as $key => $val) { -// // if ($val == 1) { -// // $process_profile = new PluginProcessmakerProcess_Profile; -// // $process_profile->delete( array( 'id' => $key ), true ); - -// // } -// // } -// // } -// // break; - -// } -//} - /** * Summary of plugin_processmaker_install * Creates tables and initializes tasks, "GLPI Requesters" group @@ -192,6 +132,7 @@ function plugin_pre_item_update_processmaker(CommonITILObject $parm) { switch ($key) { case 'global_validation' : $locVar[ 'GLPI_TICKET_GLOBAL_VALIDATION' ] = $val; + $locVar[ 'GLPI_ITEM_GLOBAL_VALIDATION' ] = $val; break; case 'itilcategories_id' : $locVar[ 'GLPI_ITEM_ITIL_CATEGORY_ID' ] = $val; @@ -250,73 +191,6 @@ function plugin_item_update_processmaker_satisfaction($parm) { } } -///** -// * Summary of plugin_pre_item_purge_processmaker -// * @param mixed $parm is the object -// */ -//function plugin_pre_item_purge_processmaker ( $parm ) { - -// if ($parm->getType() == 'Ticket_User' && is_array( $parm->fields ) && isset( $parm->fields['type'] ) && $parm->fields['type'] == 2) { -// $itemId = $parm->fields['tickets_id']; -// $itemType = 'Ticket'; -// $technicians = PluginProcessmakerProcessmaker::getItemUsers( $itemType, $itemId, 2 ); // 2 for technicians - -// if (PluginProcessmakerCase::getIDFromItem($itemType, $itemId) && count($technicians) == 1) { -// $parm->input = null; // to cancel deletion of the last tech in the ticket -// } -// } -//} - -///** -// * Summary of plugin_item_purge_processmaker -// * @param mixed $parm is the object -// */ -//function plugin_item_purge_processmaker($parm) { -// global $DB, $PM_SOAP; - -// //$objects = ['Ticket', 'Change', 'Problem']; -// $object_users = ['Ticket_User', 'Change_User', 'Problem_User']; - -// if (in_array($parm->getType(), $object_users) && is_array( $parm->fields ) && isset( $parm->fields['type'] ) && $parm->fields['type'] == 2) { - -// // We just deleted a tech from this ticket then we must if needed "de-assign" the tasks assigned to this tech -// // and re-assign them to the first tech in the list !!!! - -// $itemType = strtolower(explode('_', $parm->getType())[0]); // $parm->getType() returns 'Ticket_User'; -// $itemId = $parm->fields[$itemType.'s_id']; -// $cases = PluginProcessmakerCase::getIDsFromItem($itemType, $itemId); -// foreach ($cases as $cases_id) { -// // cases are existing for this item -// $locCase = new PluginProcessmakerCase; -// if ($locCase->getFromDB($cases_id)) { -// $technicians = PluginProcessmakerProcessmaker::getItemUsers($itemType, $itemId, CommonITILActor::ASSIGN); - -// $locVars = array( 'GLPI_TICKET_TECHNICIAN_GLPI_ID' => $technicians[0]['glpi_id'], -// 'GLPI_ITEM_TECHNICIAN_GLPI_ID' => $technicians[0]['glpi_id'], -// 'GLPI_TICKET_TECHNICIAN_PM_ID' => $technicians[0]['pm_id'], -// 'GLPI_ITEM_TECHNICIAN_PM_ID' => $technicians[0]['pm_id'] -// ); - -// // and we must find all tasks assigned to this former user and re-assigned them to new user (if any :))! -// $caseInfo = $locCase->getCaseInfo( ); -// if ($caseInfo !== false) { -// $locCase->sendVariables( $locVars); -// // need to get info on the thread of the GLPI current user -// // we must retreive currentGLPI user from this array -// $GLPICurrentPMUserId = PluginProcessmakerUser::getPMUserId( $parm->fields['users_id'] ); -// if (property_exists($caseInfo, 'currentUsers') && is_array( $caseInfo->currentUsers )) { -// foreach ($caseInfo->currentUsers as $caseUser) { -// if ($caseUser->userId == $GLPICurrentPMUserId && in_array( $caseUser->delThreadStatus, array('DRAFT', 'OPEN', 'PAUSE' ) )) { -// $locCase->reassignCase($caseUser->delIndex, $caseUser->taskId, $caseUser->delThread, $parm->fields['users_id'], $technicians[0]['pm_id'] ); -// } -// } -// } -// } - -// } -// } -// } -//} function plugin_processmaker_post_init() { global $PM_DB, $PM_SOAP; @@ -352,22 +226,26 @@ function plugin_processmaker_change_profile() { function plugin_item_update_processmaker_tasks($parm) { global $DB, $CFG_GLPI, $PM_SOAP; - // we need to test if a specific case is completed, and if so - // we should complete the linked cases (via linked tickets) $pmTaskCat = new PluginProcessmakerTaskCategory; if ($pmTaskCat->getFromDBbyCategory( $parm->fields['taskcategories_id'] ) && in_array( 'state', $parm->updates ) - && $parm->input['state'] == 2) { // the task has just been set to DONE state + && $parm->input['state'] == Planning::DONE + && $parm->oldvalues['state'] == Planning::TODO) { // the task has just been set to DONE state - $itemtype = str_replace( 'Task', '', $parm->getType() ); + //$itemtype = str_replace( 'Task', '', $parm->getType() ); $pmTask = new PluginProcessmakerTask($parm->getType()); $pmTask->getFromDB($parm->fields['id']); $locCase = new PluginProcessmakerCase; - $locCase->getFromDB($pmTask->fields['plugin_processmaker_cases_id']); //Item($itemtype, $parm->fields['tickets_id']); + $locCase->getFromDB($pmTask->fields['plugin_processmaker_cases_id']); $srccase_guid = $locCase->fields['case_guid']; + $msg = ' $locCase: '.str_replace("\n", "\n ", print_r($locCase, true))."\n"; + $msg .= ' $task: '.str_replace("\n", "\n ", print_r($parm, true))."\n"; + $msg .= ' $pmTask: '.str_replace("\n", "\n ", print_r($pmTask, true))."\n"; + $msg .= "\n"; + foreach ($DB->request( 'glpi_plugin_processmaker_caselinks', "is_active = 1 AND sourcetask_guid='".$pmTaskCat->fields['pm_task_guid']."'") as $targetTask) { // Must check the condition @@ -390,7 +268,7 @@ function plugin_item_update_processmaker_tasks($parm) { // must read some values $externalapplication = json_decode( $targetTask['externalapplication'], true ); // must be of the form - // {"method":"POST","url":"http://arsupd201.ar.ray.group:8000/search_by_userid/","params":{"user":"@@USER_ID","system":"GPP","list":"@@ROLE_LIST"}} + // {"method":"POST","url":"urloftheservice","params":json_object} // Where method is the POST, GET, ... method // url is the URL to be called // params is a list of parameters to get from running case @@ -410,9 +288,20 @@ function plugin_item_update_processmaker_tasks($parm) { $infoForTasks[ "@@$casevar" ] = "'$varval'"; unset( $infoForTasks[ $casevar ] ); } + + $msg .= " ***********\n"; + $msg .= ' $targetTask: '.str_replace("\n", "\n ", print_r($targetTask, true))."\n"; + $targetTask['sourcecondition'] = str_replace( array_keys($infoForTasks), $infoForTasks, $targetTask['sourcecondition'] ); - if (eval( "return ".$targetTask['sourcecondition'].";" )) { + $eval = eval( "return (".$targetTask['sourcecondition']." ? 1 : 0);" ); + + $msg .= ' $infoForTasks: '.str_replace("\n", "\n ", print_r($infoForTasks, true))."\n"; + $msg .= ' $targetTask[\'sourcecondition\']: '.str_replace("\n", "\n ", print_r($targetTask['sourcecondition'], true))."\n"; + $msg .= ' $result: '."$eval\n"; + $msg .= "\n"; + + if ($eval) { // look at each linked ticket if a case is attached and then if a task like $val is TO_DO // then will try to routeCase for each tasks in $val @@ -436,9 +325,13 @@ function plugin_item_update_processmaker_tasks($parm) { $ch = curl_init(); $externalapplication['url'] = eval( "return '".str_replace( array_keys($infoForTasks), $infoForTasks, $externalapplication['url'])."' ;" ); // '??? curl_setopt($ch, CURLOPT_URL, $externalapplication['url'] ); + if (isset($externalapplication['method']) && $externalapplication['method'] == 'POST') { + curl_setopt($ch, CURLOPT_POST, 1); + } } if ($targetTask['is_self']) { + $PM_SOAP->login(true); $taskCase = $PM_SOAP->taskCase( $srccase_guid ); foreach ($taskCase as $task) { // search for target task guid @@ -446,6 +339,7 @@ function plugin_item_update_processmaker_tasks($parm) { break; } } + $PM_SOAP->login(); $postdata['APP_UID'] = $srccase_guid; $postdata['DEL_INDEX'] = $task->delegate; @@ -470,20 +364,22 @@ function plugin_item_update_processmaker_tasks($parm) { if ($externalapplication) { // must call external application in order to get the needed data asynchroneously // must be of the form - // {"url":"http://arsupd201.ar.ray.group:8000/search_by_userid/","params":{"user":"@@USER_ID","system":"GPP","list":"@@ROLE_LIST"}} + // {"url":"urloftheservice","params":{"user":"@@USER_ID","system":"GPP","list":"@@ROLE_LIST"}} // url is the URL to be called $externalapplicationparams['id'] = $cronaction->getID(); $externalapplicationparams = json_encode( $externalapplicationparams, JSON_HEX_APOS | JSON_HEX_QUOT); - curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $externalapplicationparams); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Content-Length: ' . strlen($externalapplicationparams), 'Expect:']); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 ); + curl_setopt($ch, CURLOPT_VERBOSE, 1); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $pmconfig->fields['ssl_verify']); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $pmconfig->fields['ssl_verify']); - //curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1 ) ; - //curl_setopt($ch, CURLOPT_PROXY, "localhost:8889"); + //curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1 ) ; + //curl_setopt($ch, CURLOPT_PROXY, "localhost:8889"); $response = curl_exec ($ch); @@ -493,6 +389,7 @@ function plugin_item_update_processmaker_tasks($parm) { } // } } else { + // TODO to review this part of code as it is no longer usable like this !!! foreach (Ticket_Ticket::getLinkedTicketsTo( $parm->fields['tickets_id'] ) as $tlink) { if ($tlink['link'] == Ticket_Ticket::LINK_TO) { $query = "SELECT glpi_plugin_processmaker_cases.id, MAX(glpi_plugin_processmaker_tasks.del_index) AS del_index FROM glpi_tickettasks @@ -531,5 +428,9 @@ function plugin_item_update_processmaker_tasks($parm) { } } + + $msg .= "================\n"; + Toolbox::logInFile("processmaker", $msg); + } } diff --git a/inc/case.class.php b/inc/case.class.php index 3dbfe2e..1b03635 100644 --- a/inc/case.class.php +++ b/inc/case.class.php @@ -28,13 +28,13 @@ static function getTypeName($nb = 0) { //} - //static function canView() { - // return Session::haveRightsOr('plugin_processmaker_case', [READ, UPDATE]); - //} + static function canView() { + return Session::haveRightsOr('plugin_processmaker_case', [READ, UPDATE]); + } - //function canViewItem() { - // return Session::haveRightsOr('plugin_processmaker_case', READ); - //} + function canViewItem() { + return Session::haveRight('plugin_processmaker_case', READ); + } //static function canUpdate( ) { // return Session::haveRight('plugin_processmaker_config', UPDATE); @@ -44,25 +44,31 @@ static function getTypeName($nb = 0) { // return Session::haveRight('plugin_processmaker_config', UPDATE); //} + //function canEdit($ID) { + // return parent::canDelete(); + //} function maybeDeleted() { return false; } - static function canDelete() { - return parent::canDelete(); - } + //static function canDelete() { + // return parent::canDelete(); + //} - function canDeleteItem() { - return parent::canDeleteItem(); - } + //function canDeleteItem() { + // return parent::canDeleteItem(); + //} static function canPurge() { - return self::canDelete(); + return true; //self::canDelete(); } function canPurgeItem() { - return $this->canDeleteItem(); + return $_SESSION['glpiactiveprofile']['interface'] == 'central' + && $this->fields['plugin_processmaker_cases_id'] == 0 + && $this->canDeleteItem() + && (self::canDelete() || $this->fields['case_status'] == PluginProcessmakerCase::DRAFT); } static function canCancel() { @@ -83,7 +89,7 @@ function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) { // case is a sub-case $tabname = __('Sub-case', 'processmaker'); } - return [ __CLASS__ => $tabname." ".self::getStatus($item->fields['case_status']).""]; + return [ 'main' => $tabname." ".self::getStatus($item->fields['case_status']).""]; } else { $items_id = $item->getID(); $itemtype = $item->getType(); @@ -477,9 +483,6 @@ public function sortTasks($tasks, $GLPICurrentPMUserId) { */ static function showCaseInfoTab(CommonGLPI $case, $tabnum = 1, $withtemplate = 0) { // echo 'The idea is to show here the GLPI ITIL item to which it is linked, and to give a resume of the current case status, and to give possibility to delete or cancel the case.'; - - echo ""; - $itemtype = $case->fields['itemtype']; $maintitle = __('Case is linked to a %1s', 'processmaker'); @@ -487,7 +490,7 @@ static function showCaseInfoTab(CommonGLPI $case, $tabnum = 1, $withtemplate = 0 $maintitle = __('Sub-case is linked to a %1s', 'processmaker'); } - echo ""; + echo ""; Ticket::commonListHeader(Search::HTML_OUTPUT); @@ -502,12 +505,8 @@ static function showCaseInfoTab(CommonGLPI $case, $tabnum = 1, $withtemplate = 0 // it's a main case, not a sub-case // and we have the rights to cancel cases - // show a form to be able to cancel the case - $rand = rand(); echo "

"; - echo ""; - echo "
"; echo "
".sprintf($maintitle, $itemtype::getTypeName(1))."
".sprintf($maintitle, $itemtype::getTypeName(1))."
"; echo ""; echo ""; @@ -517,33 +516,27 @@ static function showCaseInfoTab(CommonGLPI $case, $tabnum = 1, $withtemplate = 0 echo ""; echo "
".__('Case cancellation', 'processmaker')."
".__('Cancel case', 'processmaker')."
"; - Html::closeForm(); - } // will not show delete button if case is a sub-process // and will show it only if it is a draft or if current glpi user has the right to delete cases and session is central - if ($case->fields['plugin_processmaker_cases_id'] == 0 - && ($case->fields['case_status'] == self::DRAFT - || (plugin_processmaker_haveRight("case", DELETE) - && $_SESSION['glpiactiveprofile']['interface'] == 'central'))) { + if ($case->canPurgeItem($case->getID())) { // then propose a button to delete case - $rand = rand(); + // the button will be effectively shown by the showFormButtons() echo "

"; - echo ""; - echo "
"; echo ""; - echo ""; - echo ""; - echo "
".__('Case deletion', 'processmaker')."
".__('Delete case', 'processmaker').""; - echo ""; - echo ""; - echo ""; - echo "
"; + echo "".__('Case deletion', 'processmaker').""; + // echo "".__('Delete case', 'processmaker').""; + // echo ""; + // echo ""; + // echo ""; + // echo ""; + // echo ""; + echo ""; + echo "

"; - Html::closeForm(); } return; @@ -556,7 +549,7 @@ static function showCaseInfoTab(CommonGLPI $case, $tabnum = 1, $withtemplate = 0 * @param CommonITILObject $item */ static function showForItem(CommonITILObject $item) { - global $DB, $CFG_GLPI; + global $DB, $CFG_GLPI, $PM_SOAP; $items_id = $item->getField('id'); $itemtype = $item->getType(); @@ -570,7 +563,7 @@ static function showForItem(CommonITILObject $item) { $rand = mt_rand(); - $query = "SELECT gppc.`id` AS assocID, gppc.`id` as id, gppp.name as pname, gppc.`case_status`, gppc.`plugin_processmaker_cases_id` + $query = "SELECT gppc.`id` AS assocID, gppc.`id` as id, gppp.id as pid, gppp.name as pname, gppc.`case_status`, gppc.`plugin_processmaker_cases_id` FROM `glpi_plugin_processmaker_cases` as gppc LEFT JOIN `glpi_plugin_processmaker_processes` AS gppp ON gppp.`id`=gppc.`plugin_processmaker_processes_id` WHERE gppc.`itemtype` = '$itemtype' @@ -579,25 +572,33 @@ static function showForItem(CommonITILObject $item) { $result = $DB->query($query); $cases = []; - $used = []; + $used = []; + $pid = []; if ($numrows = $DB->numrows($result)) { while ($data = $DB->fetch_assoc($result)) { $cases[$data['id']] = $data; - $used[$data['id']] = $data['id']; + $used[$data['id']] = $data['id']; + if (isset($pid[$data['pid']])) { + $pid[$data['pid']] += 1; + } else { + $pid[$data['pid']] = 1; + } } } $columns = ['pname' => __('Process', 'processmaker'), - 'name' => __('Title', 'processmaker'), - 'status' => __('Status', 'processmaker'), - 'sub' => __('Sub-case of', 'processmaker') + 'name' => __('Title', 'processmaker'), + 'status' => __('Status', 'processmaker'), + 'sub' => __('Sub-case of', 'processmaker') ]; // check if item is not solved nor closed if ($canupdate && $item->fields['status'] != CommonITILObject::SOLVED && $item->fields['status'] != CommonITILObject::CLOSED - && $_SESSION['glpiactiveprofile']['interface'] != 'helpdesk') { + && $_SESSION['glpiactiveprofile']['interface'] != 'helpdesk' + && ($numrows < $PM_SOAP->config->fields['max_cases_per_item'] + || $PM_SOAP->config->fields['max_cases_per_item'] == 0)) { echo "
"; echo ""; echo ""; @@ -621,7 +622,8 @@ static function showForItem(CommonITILObject $item) { PluginProcessmakerProcess::dropdown(['value' => 0, 'entity' => $item->fields['entities_id'], 'name' => 'plugin_processmaker_processes_id', - 'condition' => "is_active=1 $is_itemtype" + 'condition' => "is_active=1 $is_itemtype", + 'specific_tags' => ['count_cases_per_item' => $pid] ]); echo ""; echo ""; @@ -728,7 +730,7 @@ static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtem if ($item->getType() == __CLASS__) { // we are in a case viewing the main tab // the 'Case infos' tab - self::showCaseInfoTab($item, $tabnum, $withtemplate); + //self::showCaseInfoTab($item, $tabnum, $withtemplate); } else { @@ -1098,6 +1100,18 @@ static function getItemtype($value) { } + /** + * Get default values to search engine to override + **/ + static function getDefaultSearchRequest() { + + $search = ['sort' => 1, + 'order' => 'DESC']; + + return $search; + } + + /** * Summary of getSearchOptions * @return mixed @@ -1186,81 +1200,28 @@ function getSearchOptions() { function showForm ($ID, $options = ['candel'=>false]) { - //global $DB, $CFG_GLPI, $LANG; + $options['colspan'] = 6; + $options['formtitle'] = sprintf( __('Case status is \'%s\'', 'processmaker'), self::getStatus($this->fields['case_status'])); - $options['candel'] = true; - - $this->initForm($ID, $options); $this->showFormHeader($options); - //echo ""; - //echo "".__("Name").""; - //echo ""; - //echo ""; - - //echo ""; - //echo "".__("Active").""; - //Dropdown::showYesNo("is_active", $this->fields["is_active"]); - //echo ""; - - //echo ""; - //echo "".__("External data").""; - //Dropdown::showYesNo("is_externaldata", $this->fields["is_externaldata"]); - //echo ""; - - //echo ""; - //echo "".__("Self").""; - //Dropdown::showYesNo("is_self", $this->fields["is_self"]); - //echo ""; - - //echo ""; - //echo "".__("Source task GUID").""; - ////PluginProcessmakerTaskCategory::dropdown(array('name' => 'plugin_processmaker_taskcategories_id_source', - //// 'display_emptychoice' => false, - //// 'value' => $this->fields['plugin_processmaker_taskcategories_id_source'])); - //echo ""; - //echo ""; - - //echo ""; - //echo "".__("Target task GUID").""; - ////PluginProcessmakerTaskCategory::dropdown(array('name' => 'plugin_processmaker_taskcategories_id_target', - //// 'display_emptychoice' => false, - //// 'value' => $this->fields['plugin_processmaker_taskcategories_id_target'])); - //echo ""; - //echo ""; - - //echo ""; - //echo "".__("Target process GUID").""; - ////Dropdown::show( 'PluginProcessmakerProcess', array('name' => 'plugin_processmaker_processes_id', - //// 'display_emptychoice' => true, - //// 'value' => $this->fields['plugin_processmaker_processes_id'], - //// 'condition' => 'is_active = 1')); - //echo ""; - //echo ""; - - //echo ""; - //echo "".__("Target dynaform GUID").""; - //echo ""; - //echo ""; - - //echo ""; - //echo "".__("Source condition").""; - ////echo ""; - //echo ""; - //echo ""; - - //echo ""; - //echo "".__("Claim target task").""; - //Dropdown::showYesNo("is_targettoclaim", $this->fields["is_targettoclaim"]); - //echo ""; - - //echo ""; - //echo "".__("External application JSON config").""; - //echo ""; - //echo ""; - - $this->showFormButtons($options ); + $process = new PluginProcessmakerProcess; + $process->getFromDB($this->fields['plugin_processmaker_processes_id']); + + if ($process->fields['maintenance']) { + PluginProcessmakerProcess::showUnderMaintenance($process->fields['name'], 'small'); + } + self::showCaseInfoTab($this); + + //echo '
' ; + $options['candel'] = true; + $this->showFormButtons($options); + + echo Html::scriptBlock(" + $('#tabsbody th').css('text-align', 'center'); + $('#tabsbody td').css('text-align', 'center'); + "); } @@ -1271,21 +1232,31 @@ function showForm ($ID, $options = ['candel'=>false]) { */ function defineTabs($options = []) { - // $ong = array('empty' => $this->getTypeName(1)); + $process = new PluginProcessmakerProcess; + $process->getFromDB($this->fields['plugin_processmaker_processes_id']); + $ong = []; - //$this->addDefaultFormTab($ong); + if (self::isLayoutWithMain()) { + $this->addDefaultFormTab($ong); + } - $this->addStandardTab('PluginProcessmakerTask', $ong, $options); + if (!$process->fields['maintenance']) { + $this->addStandardTab('PluginProcessmakerTask', $ong, $options); + } - $this->addStandardTab(__CLASS__, $ong, $options); + if (!self::isLayoutWithMain()) { + $this->addStandardTab(__CLASS__, $ong, $options); + } - $this->addStandardTab('PluginProcessmakerCasemap', $ong, $options); + if (!$process->fields['maintenance']) { + $this->addStandardTab('PluginProcessmakerCasemap', $ong, $options); - $this->addStandardTab('PluginProcessmakerCasehistory', $ong, $options); + $this->addStandardTab('PluginProcessmakerCasehistory', $ong, $options); - $this->addStandardTab('PluginProcessmakerCasechangelog', $ong, $options); + $this->addStandardTab('PluginProcessmakerCasechangelog', $ong, $options); - $this->addStandardTab('PluginProcessmakerCasedynaform', $ong, $options); + $this->addStandardTab('PluginProcessmakerCasedynaform', $ong, $options); + } return $ong; } @@ -1332,4 +1303,5 @@ function deleteCronTaskActions() { return $DB->query($query); } + } diff --git a/inc/casechangelog.class.php b/inc/casechangelog.class.php index 9d10ce7..34c73e9 100644 --- a/inc/casechangelog.class.php +++ b/inc/casechangelog.class.php @@ -10,13 +10,14 @@ */ class PluginProcessmakerCasechangelog extends CommonDBTM { - static function displayTabContentForItem(CommonGLPI $case, $tabnum=1, $withtemplate=0) { + static function displayTabContentForItem(CommonGLPI $case, $tabnum = 1, $withtemplate = 0) { global $CFG_GLPI, $PM_SOAP; $rand = rand(); $caseHistoryURL = $PM_SOAP->serverURL."/cases/ajaxListener?action=changeLogHistory&rand=$rand"; + $PM_SOAP->echoDomain(); echo ""; //?rand=$rand' $iframe = ""; - $PM_SOAP->initCaseAndShowTab(['APP_UID' => $case->fields['case_guid'], 'DEL_INDEX' => 1], $iframe, $rand) ; + $PM_SOAP->initCaseAndShowTab(['APP_UID' => $case->fields['case_guid'], 'DEL_INDEX' => 1], $iframe, $rand); } - function getTabNameForItem(CommonGLPI $case, $withtemplate = 0){ + function getTabNameForItem(CommonGLPI $case, $withtemplate = 0) { return __('Change log', 'processmaker'); } -} \ No newline at end of file +} diff --git a/inc/casedynaform.class.php b/inc/casedynaform.class.php index e6d85f7..29d5a24 100644 --- a/inc/casedynaform.class.php +++ b/inc/casedynaform.class.php @@ -19,6 +19,7 @@ static function displayTabContentForItem(CommonGLPI $case, $tabnum = 1, $withtem $proj = new PluginProcessmakerProcess; $proj->getFromDB($case->fields['plugin_processmaker_processes_id']); + $PM_SOAP->echoDomain(); echo ""; //?rand=$rand' echo ""; $iframe = ""; - $PM_SOAP->initCaseAndShowTab(['APP_UID' => $case->fields['case_guid'], 'DEL_INDEX' => 1], $iframe, $rand) ; + $PM_SOAP->initCaseAndShowTab(['APP_UID' => $case->fields['case_guid'], 'DEL_INDEX' => 1], $iframe, $rand); } - function getTabNameForItem(CommonGLPI $case, $withtemplate = 0){ + function getTabNameForItem(CommonGLPI $case, $withtemplate = 0) { return __('History', 'processmaker'); } -} \ No newline at end of file +} diff --git a/inc/caselinkaction.class.php b/inc/caselinkaction.class.php index bce380d..60f0eb8 100644 --- a/inc/caselinkaction.class.php +++ b/inc/caselinkaction.class.php @@ -9,4 +9,4 @@ * @author MoronO */ class PluginProcessmakerCaselinkaction extends CommonDBTM { -} \ No newline at end of file +} diff --git a/inc/casemap.class.php b/inc/casemap.class.php index 2b2c53a..4c268c0 100644 --- a/inc/casemap.class.php +++ b/inc/casemap.class.php @@ -10,7 +10,7 @@ */ class PluginProcessmakerCasemap extends CommonDBTM { - static function displayTabContentForItem(CommonGLPI $case, $tabnum=1, $withtemplate=0) { + static function displayTabContentForItem(CommonGLPI $case, $tabnum = 1, $withtemplate = 0) { global $CFG_GLPI, $PM_SOAP; $rand = rand(); @@ -26,6 +26,7 @@ static function displayTabContentForItem(CommonGLPI $case, $tabnum=1, $withtempl "/cases/ajaxListener?action=processMap" )."&rand=$rand"; + $PM_SOAP->echoDomain(); echo ""; //?rand=$rand' $iframe = ""; - $PM_SOAP->initCaseAndShowTab(['APP_UID' => $case->fields['case_guid'], 'DEL_INDEX' => 1], $iframe, $rand) ; + $PM_SOAP->initCaseAndShowTab(['APP_UID' => $case->fields['case_guid'], 'DEL_INDEX' => 1], $iframe, $rand); } - function getTabNameForItem(CommonGLPI $case, $withtemplate = 0){ + function getTabNameForItem(CommonGLPI $case, $withtemplate = 0) { return __('Map', 'processmaker'); } -} \ No newline at end of file +} diff --git a/inc/config.class.php b/inc/config.class.php index 9520523..262b0f0 100644 --- a/inc/config.class.php +++ b/inc/config.class.php @@ -315,24 +315,29 @@ function parseUrl( url ) { } else { echo "".__('Not connected'); } + echo "\n"; + echo ""; + echo "" . __('Max cases per item (0=unlimited)', 'processmaker') . ""; + echo ""; echo "\n"; + } else { echo ""; PluginProcessmakerProcessmaker::showUnderMaintenance(); echo ""; } - echo "".__('Maintenance').""; + echo "".__('Maintenance').""; - echo ""; - echo "".__('Maintenance mode').""; - Dropdown::showYesNo("maintenance", $config->fields['maintenance']); - echo ""; + echo ""; + echo "".__('Maintenance mode').""; + Dropdown::showYesNo("maintenance", $config->fields['maintenance']); + echo ""; - echo ""; + echo ""; - echo "".__('Processmaker system information', 'processmaker').""; + echo "".__('Processmaker system information', 'processmaker').""; if ($setup_ok) { $info = $PM_SOAP->systemInformation( ); echo ''.__('Version', 'processmaker').''.$info->version.''; @@ -347,7 +352,7 @@ function parseUrl( url ) { } else { echo ''.__('Version', 'processmaker').''.__('Not yet!', 'processmaker').''; } - $config->showFormButtons(['candel'=>false]); + $config->showFormButtons(['candel' => false]); return false; } diff --git a/inc/process.class.php b/inc/process.class.php index 8d8d3c6..30bc049 100644 --- a/inc/process.class.php +++ b/inc/process.class.php @@ -44,6 +44,19 @@ function maybeDeleted() { return false; } + + /** + * Get default values to search engine to override + **/ + static function getDefaultSearchRequest() { + + $search = ['sort' => 1, + 'order' => 'ASC']; + + return $search; + } + + /** * Summary of refreshTasks * will refresh (re-synch) all process task list @@ -486,6 +499,18 @@ function getSearchOptions() { $tab[19]['massiveaction'] = true; $tab[19]['datatype'] = 'bool'; + $tab[20]['table'] = 'glpi_plugin_processmaker_processes'; + $tab[20]['field'] = 'maintenance'; + $tab[20]['name'] = __('Maintenance', 'processmaker'); + $tab[20]['massiveaction'] = true; + $tab[20]['datatype'] = 'bool'; + + $tab[21]['table'] = 'glpi_plugin_processmaker_processes'; + $tab[21]['field'] = 'max_cases_per_item'; + $tab[21]['name'] = __('Max cases per item (0=unlimited)', 'processmaker'); + $tab[21]['massiveaction'] = true; + $tab[21]['datatype'] = 'number'; + return $tab; } @@ -594,7 +619,7 @@ function showForm ($ID, $options = ['candel'=>false]) { echo ""; echo ""; - echo "".__("Active").""; + echo "".__('Active').""; Dropdown::showYesNo("is_active", $this->fields["is_active"]); echo ""; @@ -608,6 +633,11 @@ function showForm ($ID, $options = ['candel'=>false]) { Dropdown::showYesNo("insert_task_comment", $this->fields["insert_task_comment"]); echo ""; + echo ""; + echo "" . __('Max cases per item (0=unlimited)', 'processmaker') . ""; + echo ""; + echo "\n"; + echo ""; echo "".__('Visible in Incident for Central interface', 'processmaker').""; Dropdown::showYesNo("is_incident", $this->fields["is_incident"]); @@ -680,6 +710,15 @@ function showForm ($ID, $options = ['candel'=>false]) { Dropdown::showFromArray( 'project_type', self::getAllTypeArray(), [ 'value' => $this->fields["project_type"] ] ); echo ""; + echo ""; + echo "".__('Maintenance mode').""; + Dropdown::showYesNo("maintenance", $this->fields["maintenance"]); + if ($this->fields["maintenance"]) { + echo ""; + echo "Synchronize Task List"; + } + echo ""; + $this->showFormButtons($options); } @@ -801,5 +840,23 @@ static function dropdown($options = []) { } + + /** + * Summary of underMaintenance + * Shows a nice(?) under maintenance message + */ + static function showUnderMaintenance($ptitle, $size = '') { + global $CFG_GLPI; + if ($size != '') { + $size .= '-'; + } + echo "
"; + + echo Html::image($CFG_GLPI['root_doc']."/plugins/processmaker/pics/{$size}under_maintenance.png"); + echo "

"; + echo sprintf(__('Process \'%s\' is under maintenance, please retry later, thank you.', 'processmaker'), $ptitle); + echo "

"; + echo "
"; + } } diff --git a/inc/processmaker.class.php b/inc/processmaker.class.php index aaa3d22..3c981ed 100644 --- a/inc/processmaker.class.php +++ b/inc/processmaker.class.php @@ -180,16 +180,22 @@ private function openSoap() { $this->taskWriter = $this->config->fields['users_id']; $this->pm_group_guid = $this->config->fields['pm_group_guid']; + //$cipher_list = openssl_get_cipher_methods(true); + $wsdl = $this->serverURL."/services/wsdl2"; $context['ssl'] = ['verify_peer_name' => $this->config->fields['ssl_verify'], // Verification of peer name 'verify_peer' => $this->config->fields['ssl_verify'], // Verification of SSL certificate used + //'ciphers' => 'RSA', ]; - $options = [ 'stream_context' => stream_context_create($context), - 'soap_version' => SOAP_1_2, - 'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP, - //'proxy_host' => 'localhost', - //'proxy_port' => 8888 + $options = ['stream_context' => stream_context_create($context), + 'soap_version' => SOAP_1_2, + 'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP, + 'keep_alive' => false, + //'trace' => true, + //'exceptions' => false, + //'proxy_host' => 'localhost', + //'proxy_port' => 8889 ]; $this->pmSoapClient = new SoapClient($wsdl, $options); @@ -246,7 +252,7 @@ function login($admin_or_user = false, $case_guid = 'default') { $gusr = new User; if (is_numeric($admin_or_user)) { $gusr->getFromDB($admin_or_user); - } elseif ($admin_or_user !== false) { + } else if ($admin_or_user !== false) { $gusr->getFromDBbyName($admin_or_user); } else { $gusr->getFromDB(Session::getLoginUserID()); @@ -1440,18 +1446,35 @@ public static function plugin_pre_item_add_processmaker($parm) { $caseInfo = $myProcessMaker->getCaseInfo( $parm->input['processmaker_caseguid'], $parm->input['processmaker_delindex']); $parm->input['name'] = $PM_DB->escape($caseInfo->caseName ); - $caseInitialDueDate = $myProcessMaker->getVariables( $parm->input['processmaker_caseguid'], [ 'GLPI_ITEM_TITLE', 'GLPI_ITEM_INITIAL_DUE_DATE' ]); - if (array_key_exists( 'GLPI_ITEM_INITIAL_DUE_DATE', $caseInitialDueDate )) { - $parm->input['time_to_resolve'] = $caseInitialDueDate[ 'GLPI_ITEM_INITIAL_DUE_DATE' ]." 23:59:59"; + $casegetvariables = ['GLPI_ITEM_TITLE', 'GLPI_ITEM_INITIAL_DUE_DATE', 'GLPI_ITEM_DUE_DATE']; + $caseresetvariables = []; + + $caseDueDate = $myProcessMaker->getVariables( $parm->input['processmaker_caseguid'], $casegetvariables); + if (array_key_exists('GLPI_ITEM_INITIAL_DUE_DATE', $caseDueDate)) { + $parm->input['time_to_resolve'] = $caseDueDate['GLPI_ITEM_INITIAL_DUE_DATE']; + $caseresetvariables['GLPI_ITEM_INITIAL_DUE_DATE'] = ''; + } + if (array_key_exists( 'GLPI_ITEM_DUE_DATE', $caseDueDate )) { + $parm->input['time_to_resolve'] = $caseDueDate['GLPI_ITEM_DUE_DATE']; + $caseresetvariables['GLPI_ITEM_DUE_DATE'] = ''; } + $re = '/^(?\'date\'[0-9]{4}-[0-1][0-9]-[0-3][0-9])( (?\'time\'[0-2][0-9]:[0-5][0-9]:[0-5][0-9]))*$/'; + if (preg_match($re, $parm->input['time_to_resolve'], $matches) && !array_key_exists('time', $matches)) { + $parm->input['time_to_resolve'] .= " 23:59:59"; + } + $txtItemTitle = $caseInfo->caseName; - if (array_key_exists( 'GLPI_ITEM_TITLE', $caseInitialDueDate )) { - $txtItemTitle = $caseInitialDueDate[ 'GLPI_ITEM_TITLE' ]; - // reset those variables - $resultSave = $myProcessMaker->sendVariables( $parm->input['processmaker_caseguid'], [ "GLPI_ITEM_TITLE" => ''] ); + if (array_key_exists( 'GLPI_ITEM_TITLE', $caseDueDate )) { + $txtItemTitle = $caseDueDate['GLPI_ITEM_TITLE']; + // reset item title case variable + $caseresetvariables['GLPI_ITEM_TITLE'] = ''; } $parm->input['name'] = $PM_DB->escape($txtItemTitle ); + if (count($caseresetvariables)) { + $resultSave = $myProcessMaker->sendVariables( $parm->input['processmaker_caseguid'], $caseresetvariables ); + } + $procDef = new PluginProcessmakerProcess; $procDef->getFromGUID( $caseInfo->processId ); if (isset($parm->input['type'])) { @@ -1465,6 +1488,12 @@ public static function plugin_pre_item_add_processmaker($parm) { } } + + /** + * Summary of plugin_item_add_processmaker + * @param mixed $parm + * @return void + */ public static function plugin_item_add_processmaker($parm) { global $DB, $GLOBALS, $PM_SOAP; @@ -1493,74 +1522,12 @@ public static function plugin_item_add_processmaker($parm) { // before routing, send items_id and itemtype // as this information was not available at case creation - $myCase->sendVariables( [ "GLPI_TICKET_ID" => $items_id ] ); - - // route case - $pmRouteCaseResponse = $PM_SOAP->routeCase($case_guid, $parm->input['processmaker_delindex'] ); - - // gets new case status - $caseInfo = $myCase->getCaseInfo();//$parm->input['processmaker_delindex']); - // now manage tasks associated with item - // create new tasks - if (property_exists( $pmRouteCaseResponse, 'routing' )) { - // now tries to get some variables to setup content for new task and to append text to solved task - $txtForTasks = $myCase->getVariables( [ "GLPI_ITEM_APPEND_TO_TASK", - "GLPI_ITEM_SET_STATUS", - "GLPI_TICKET_FOLLOWUP_CONTENT", - "GLPI_TICKET_FOLLOWUP_IS_PRIVATE", - "GLPI_TICKET_FOLLOWUP_REQUESTTYPES_ID" ] ); - $itemSetStatus = ''; - if (array_key_exists( 'GLPI_ITEM_SET_STATUS', $txtForTasks )) { - $itemSetStatus = $txtForTasks[ 'GLPI_ITEM_SET_STATUS' ]; - } - if (array_key_exists( 'GLPI_ITEM_APPEND_TO_TASK', $txtForTasks )) { - $txtToAppendToTask = $txtForTasks[ 'GLPI_ITEM_APPEND_TO_TASK' ]; - } else { - $txtToAppendToTask = ''; - } - $createFollowup = false; // by default - if (array_key_exists( 'GLPI_TICKET_FOLLOWUP_CONTENT', $txtForTasks ) && $txtForTasks[ 'GLPI_TICKET_FOLLOWUP_CONTENT' ] != '') { - $createFollowup = true; - } - - // reset those variables - $resultSave = $myCase->sendVariables( [ "GLPI_ITEM_APPEND_TO_TASK" => '', - "GLPI_ITEM_SET_STATUS" => '', - "GLPI_TICKET_FOLLOWUP_CONTENT" => '', - "GLPI_TICKET_FOLLOWUP_IS_PRIVATE" => '', - "GLPI_TICKET_FOLLOWUP_REQUESTTYPES_ID" => '' ] ); - - // routing has been done, then solve 1st task - $PM_SOAP->solveTask($myCase->getID(), $parm->input['processmaker_delindex'], [ 'txtToAppend' => $txtToAppendToTask, 'notif' => false] ); - - // create a followup if requested - if ($createFollowup && $itemtype == 'Ticket') { - $PM_SOAP->addTicketFollowup( $items_id, $txtForTasks ); - } - - // and create GLPI tasks for the newly created PM tasks. - foreach ($pmRouteCaseResponse->routing as $route) { - $PM_SOAP->addTask( $myCase->getID(), - $myCase->fields['itemtype'], - $myCase->fields['items_id'], - $caseInfo, - $route->delIndex, - PluginProcessmakerUser::getGLPIUserId( $route->userId ), - 0, - $route->taskId, - $route->delThread ); - } + $myCase->sendVariables( [ "GLPI_TICKET_ID" => $items_id, + "GLPI_ITEM_ID" => $items_id, + "GLPI_ITEM_TYPE" => $itemtype, + ] ); - if ($itemSetStatus != '') { - $PM_SOAP->setItemStatus($myCase->fields['itemtype'], $myCase->fields['items_id'], $itemSetStatus ); - } - } - - // evolution of case status: DRAFT, TO_DO, COMPLETED, CANCELLED - $myCase->update( ['id' => $myCase->getID(), - 'case_status' => $caseInfo->caseStatus, - 'name' => $caseInfo->caseName - ]); + $PM_SOAP->derivateCase($myCase, ['DEL_INDEX' => $parm->input['processmaker_delindex']]); } else {//if( array_key_exists('_head', $parm->input) ) { // this ticket have been created via email receiver. $ptnProcessToStart = "/##TicketProcess:\s*#([0-9a-f]{32})\s*##/i"; @@ -1657,13 +1624,17 @@ public static function addWatcher($itemType, $itemId, $techId) { */ public function addTask($cases_id, $itemtype, $items_id, $caseInfo, $delIndex, $techId, $groupId, $pmTaskId, $delThread, $options = []) { global $DB, $PM_DB, $_SESSION; + $dbu = new DbUtils; + $default_options = [ 'txtTaskContent' => '', 'start_date' => '', 'end_date' => '', + 'reminder' => '', 'notif' => true ]; + foreach ($default_options as $key => $opt) { if (!isset($options[$key])) { $options[$key] = $opt; @@ -1710,7 +1681,7 @@ public function addTask($cases_id, $itemtype, $items_id, $caseInfo, $delIndex, $ $input['users_id'] = $this->taskWriter; // manage groups - if ($techId == 0) { // then we must look-up DB to get the pseudo-group that will be assigned to the task + if ($techId == 0) { // then we must look-up DB to get the group that will be assigned to the task $groupname=''; if ($groupId == 0) { $query = "SELECT CONTENT.CON_VALUE FROM TASK_USER @@ -1766,13 +1737,21 @@ public function addTask($cases_id, $itemtype, $items_id, $caseInfo, $delIndex, $ $input['groups_id_tech'] = $groups_id_tech; } + if ($options['reminder'] != '' && $techId) { + $input['_planningrecall'] = ['before_time' => $options['reminder'], + 'itemtype' => get_class($glpi_task), + 'items_id' => '', + 'users_id' => $techId, + 'field' => 'begin']; + } + $donotif = self::saveNotification(false); // do not send notification yet as the PluginProcessmakerTask is not yet added to DB $glpi_task->add( Toolbox::addslashes_deep( $input ) ); self::restoreNotification($donotif); // to prevent error message for overlapping planning - if (isset($_SESSION["MESSAGE_AFTER_REDIRECT"][ERROR])) { - unset($_SESSION["MESSAGE_AFTER_REDIRECT"][ERROR]); + if (isset($_SESSION["MESSAGE_AFTER_REDIRECT"][WARNING])) { + unset($_SESSION["MESSAGE_AFTER_REDIRECT"][WARNING]); } if ($glpi_task->getId() > 0) { @@ -1892,6 +1871,21 @@ public function setItemTitle($itemtype, $itemId, $newtitle) { } + /** + * Summary of setItemDuedate + * @param mixed $itemtype + * @param mixed $itemId + * @param mixed $duedate + */ + public function setItemDuedate($itemtype, $itemId, $duedate) { + $dbu = new DbUtils; + $item = $dbu->getItemForItemtype( $itemtype ); + if ($item->getFromDB( $itemId )) { + $item->update( ['id' => $itemId, 'time_to_resolve' => $duedate] ); + } + } + + /** * Summary of setItemSolution * @param mixed $itemType @@ -2384,25 +2378,12 @@ public static function getItemUsers($itemtype, $itemId, $userType) { /** * Summary of saveForm * This function posts dynaform variables to PM, using the CURL module. - * @param mixed $request: is the $_REQUEST server array + * @param array $request: is the $_REQUEST server array * //@param string $cookie: is the $_SERVER['HTTP_COOKIE'] string * @return mixed: returns false if request failed, otherwise, returns true */ public function saveForm($request) { - //, $cookie ) { - - //if (!function_exists( 'HandleHeaderLine' )) { - // function HandleHeaderLine( $curl, $header_line ) { - // //global $cookies; - // $temp = explode( ": ", $header_line ); - // if (is_array( $temp ) && $temp[0] == 'Set-Cookie') { - // $temp2 = explode( "; ", $temp[1]); - // //$cookies .= $temp2[0].'; ' ; - // curl_setopt($curl, CURLOPT_COOKIE, $temp2[0]."; " ); - // } - // return strlen($header_line); - // } - //} + $loggable = false; $request = stripcslashes_deep( $request ); @@ -2411,7 +2392,6 @@ public function saveForm($request) { //to be able to trace network traffic with a local proxy // curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1 ); // curl_setopt($ch, CURLOPT_PROXY, "localhost:8888"); - // curl_setopt($ch, CURLOPT_PROXY, "fry07689.fr.ray.group:8889"); //curl_setopt($ch, CURLINFO_HEADER_OUT, 1); //curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); @@ -2423,7 +2403,7 @@ public function saveForm($request) { curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $this->config->fields['ssl_verify']); //curl_setopt($ch, CURLOPT_HEADERFUNCTION, "HandleHeaderLine"); - // to stock cookies in memory + // to store cookies in memory // ( replace HandleHeaderLine function ) curl_setopt($ch, CURLOPT_COOKIEFILE, ""); @@ -2440,7 +2420,8 @@ public function saveForm($request) { $data = http_formdata_flat_hierarchy( $request ); // check if any files are in the $_FILES global array // and add them to the curl POST - if (isset($_FILES['form']['name'])) { + $fileForm = $_FILES['form']['name']; + if (!empty($fileForm[array_keys($fileForm)[0]][1][array_keys($fileForm[array_keys($fileForm)[0]][1])[0]])) { foreach ($_FILES['form']['name'] as $key => $file) { if (is_array($file)) { // it's a grid which contains documents @@ -2457,7 +2438,7 @@ public function saveForm($request) { } } // to get all cookies in one variable - $cookies = curl_getinfo($ch, CURLINFO_COOKIELIST); + //$cookies = curl_getinfo($ch, CURLINFO_COOKIELIST); curl_setopt($ch, CURLOPT_HTTPHEADER, ["Expect:"]); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); // inject POST values // add agent and referer params @@ -2476,10 +2457,6 @@ public function saveForm($request) { } return ($response ? true : false); - - //$n = preg_match("/HTTP\/1.1 302 /", $response, $matches); - - //return ($n < 1 ? false : true) ; } /** @@ -2597,7 +2574,8 @@ public static function plugin_item_get_pdfdata_processmaker($item) { if (in_array( $config->fields['taskcategories_id'], $ancestors)) { $loc_completename = DropdownTranslation::getTranslatedValue( $taskCat->getID(), 'TaskCategory', 'completename', $_SESSION['glpilanguage'], $taskCat->fields['completename'] ); $loc_comment = DropdownTranslation::getTranslatedValue( $taskCat->getID(), 'TaskCategory', 'comment', $_SESSION['glpilanguage'], $taskCat->fields['comment'] ); - $item->datas['content'] = $loc_completename."\n\n".str_replace( "##processmaker.taskcomment##\n##processmakercase.url##", $loc_comment, $item->datas['content']); + $item->datas['content'] = $loc_completename."\n\n".str_replace( "##processmaker.taskcomment##", $loc_comment, $item->datas['content']); + $item->datas['content'] = str_replace( "##processmakercase.url##", '', $item->datas['content']); } // restore default translations if (isset( $trans )) { @@ -2641,15 +2619,15 @@ public static function getProcessesWithCategoryAndProfile($category, $type, $pro /** * Summary of startNewCase * @param mixed $processes_id integer: GLPI process id - * @param mixed $itemType string: item type 'Ticket', 'Change' or 'Problem' + * @param mixed $itemtype string: item type 'Ticket', 'Change' or 'Problem' * @param mixed $items_id integer: id to thte item * @param mixed $users_id integer: GLPI user id * @return mixed */ - public function startNewCase($processes_id, $itemType, $items_id, $users_id = null) { + public function startNewCase($processes_id, $itemtype, $items_id, $users_id = null) { global $DB, $CFG_GLPI; - $requesters = PluginProcessmakerProcessmaker::getItemUsers( $itemType, $items_id, CommonITILActor::REQUESTER); // 1 for requesters + $requesters = PluginProcessmakerProcessmaker::getItemUsers( $itemtype, $items_id, CommonITILActor::REQUESTER); // 1 for requesters if (!key_exists( 0, $requesters )) { $requesters[0]['glpi_id'] = 0; $requesters[0]['pm_id'] = 0; @@ -2663,65 +2641,72 @@ public function startNewCase($processes_id, $itemType, $items_id, $users_id = nu //} // get item info to retreive title, description and duedate - $locItem = new $itemType; // $_POST['itemtype'] ; //Ticket(); - $locItem->getFromDB( $items_id ); // $_POST['id'] ) ; + $item = new $itemtype; + $item->getFromDB( $items_id ); - if ($locItem->countUsers(CommonITILActor::ASSIGN) == 0 - || !$locItem->isUser(CommonITILActor::ASSIGN, $users_id) ) { - $locItem->update( [ 'id' => $items_id, '_itil_assign' => [ '_type' => 'user', 'users_id' => $users_id ] ] ); + if ($item->countUsers(CommonITILActor::ASSIGN) == 0 + || !$item->isUser(CommonITILActor::ASSIGN, $users_id) ) { + $item->update( [ 'id' => $items_id, '_itil_assign' => [ '_type' => 'user', 'users_id' => $users_id ] ] ); } - if (!isset($locItem->fields['time_to_resolve']) || $locItem->fields['time_to_resolve'] == null) { - $locItem->fields['time_to_resolve'] = ""; + if (!isset($item->fields['time_to_resolve']) || $item->fields['time_to_resolve'] == null) { + $item->fields['time_to_resolve'] = ""; } $resultCase = $this->newCase( $processes_id, - ['GLPI_ITEM_CAN_BE_SOLVED' => 0, - 'GLPI_TICKET_ID' => $items_id, - 'GLPI_ITEM_ID' => $items_id, - 'GLPI_ITEM_TYPE' => $itemType, - 'GLPI_TICKET_REQUESTER_GLPI_ID' => $requesters[0]['glpi_id'], - 'GLPI_ITEM_REQUESTER_GLPI_ID' => $requesters[0]['glpi_id'], - 'GLPI_TICKET_REQUESTER_PM_ID' => $requesters[0]['pm_id'], - 'GLPI_ITEM_REQUESTER_PM_ID' => $requesters[0]['pm_id'], - 'GLPI_TICKET_TITLE' => $locItem->fields['name'], - 'GLPI_ITEM_TITLE' => $locItem->fields['name'], - 'GLPI_TICKET_DESCRIPTION' => $locItem->fields['content'], - 'GLPI_ITEM_DESCRIPTION' => $locItem->fields['content'], - 'GLPI_TICKET_DUE_DATE' => $locItem->fields['time_to_resolve'], - 'GLPI_ITEM_OPENING_DATE' => $locItem->fields['date'], - 'GLPI_ITEM_DUE_DATE' => $locItem->fields['time_to_resolve'], - 'GLPI_ITEM_ITIL_CATEGORY_ID' => $locItem->fields['itilcategories_id'], - 'GLPI_TICKET_URGENCY' => $locItem->fields['urgency'], - 'GLPI_ITEM_URGENCY' => $locItem->fields['urgency'], - 'GLPI_ITEM_IMPACT' => $locItem->fields['impact'], - 'GLPI_ITEM_PRIORITY' => $locItem->fields['priority'], - 'GLPI_TICKET_GLOBAL_VALIDATION' => $locItem->fields['global_validation'] , - 'GLPI_TICKET_TECHNICIAN_GLPI_ID' => $users_id, - 'GLPI_ITEM_TECHNICIAN_GLPI_ID' => $users_id, - 'GLPI_TICKET_TECHNICIAN_PM_ID' => PluginProcessmakerUser::getPMUserId( $users_id ), - 'GLPI_ITEM_TECHNICIAN_PM_ID' => PluginProcessmakerUser::getPMUserId( $users_id ), - 'GLPI_URL' => $CFG_GLPI['url_base'] - ] ); + ['GLPI_ITEM_CAN_BE_SOLVED' => 0, + 'GLPI_TICKET_ID' => $items_id, + 'GLPI_ITEM_ID' => $items_id, + 'GLPI_ITEM_TYPE' => $itemtype, + 'GLPI_ITEM_STATUS' => $item->fields['status'], + 'GLPI_TICKET_REQUESTER_GLPI_ID' => $requesters[0]['glpi_id'], + 'GLPI_ITEM_REQUESTER_GLPI_ID' => $requesters[0]['glpi_id'], + 'GLPI_TICKET_REQUESTER_PM_ID' => $requesters[0]['pm_id'], + 'GLPI_ITEM_REQUESTER_PM_ID' => $requesters[0]['pm_id'], + 'GLPI_TICKET_TITLE' => $item->fields['name'], + 'GLPI_ITEM_TITLE' => $item->fields['name'], + 'GLPI_TICKET_DESCRIPTION' => $item->fields['content'], + 'GLPI_ITEM_DESCRIPTION' => $item->fields['content'], + 'GLPI_ITEM_OPENING_DATE' => $item->fields['date'], + 'GLPI_TICKET_DUE_DATE' => $item->fields['time_to_resolve'], + 'GLPI_ITEM_DUE_DATE' => $item->fields['time_to_resolve'], + 'GLPI_ITEM_ITIL_CATEGORY_ID' => $item->fields['itilcategories_id'], + 'GLPI_TICKET_URGENCY' => $item->fields['urgency'], + 'GLPI_ITEM_URGENCY' => $item->fields['urgency'], + 'GLPI_ITEM_IMPACT' => $item->fields['impact'], + 'GLPI_ITEM_PRIORITY' => $item->fields['priority'], + // Specific to Tickets and Changes + // GLPI_ITEM_GLOBAL_VALIDATION will be '' when Problem, else it will be the global_validation field + 'GLPI_TICKET_GLOBAL_VALIDATION' => $itemtype == 'Problem' ? '' : $item->fields['global_validation'], + 'GLPI_ITEM_GLOBAL_VALIDATION' => $itemtype == 'Problem' ? '' : $item->fields['global_validation'], + 'GLPI_TICKET_TECHNICIAN_GLPI_ID' => $users_id, + 'GLPI_ITEM_TECHNICIAN_GLPI_ID' => $users_id, + 'GLPI_TICKET_TECHNICIAN_PM_ID' => PluginProcessmakerUser::getPMUserId( $users_id ), + 'GLPI_ITEM_TECHNICIAN_PM_ID' => PluginProcessmakerUser::getPMUserId( $users_id ), + 'GLPI_URL' => $CFG_GLPI['url_base'], + // Specific to Tickets + // GLPI_TICKET_TYPE will contains 1 (= incident) or 2 (= request), or '' if itemtype is not Ticket + 'GLPI_TICKET_TYPE' => $itemtype == 'Ticket' ? $item->fields['type'] : '' + ]); if ($resultCase->status_code === 0) { $caseInfo = $this->getCaseInfo( $resultCase->caseId ); // save info to DB $locCase = new PluginProcessmakerCase; - $locCase->add( ['id' => $resultCase->caseNumber, - 'itemtype' => $itemType, - 'items_id' => $items_id, - 'case_guid' => $resultCase->caseId, - 'case_status' => $caseInfo->caseStatus, - 'name' => $caseInfo->caseName, - 'entities_id' => $locItem->fields['entities_id'], + $locCase->add( ['id' => $resultCase->caseNumber, + 'itemtype' => $itemtype, + 'items_id' => $items_id, + 'case_guid' => $resultCase->caseId, + 'case_status' => $caseInfo->caseStatus, + 'name' => $caseInfo->caseName, + 'entities_id' => $item->fields['entities_id'], 'plugin_processmaker_processes_id' => $processes_id, - 'plugin_processmaker_cases_id' => 0 + 'plugin_processmaker_cases_id' => 0 ], [], true ); - $this->add1stTask($locCase->getID(), $itemType, $items_id, $caseInfo, ['userId' => $users_id] ); + $this->add1stTask($locCase->getID(), $itemtype, $items_id, $caseInfo, ['userId' => $users_id] ); } return $resultCase; @@ -2747,64 +2732,13 @@ public function derivateCase($myCase, $request, $users_id = null) { $item->getFromDB($items_id); // save the dynaform variables into the current case - $resultSave = $this->saveForm( $request ); + if (isset($request['UID']) && isset($request['APP_UID']) && isset($request['__DynaformName__'])) { + $resultSave = $this->saveForm( $request ); + } // now derivate the case !!! $pmRouteCaseResponse = $this->routeCase($myCase->fields['case_guid'], $request['DEL_INDEX']); - //if (property_exists($pmRouteCaseResponse, 'routing')) { - // // must check if case has started a sub-process - // // we may have several new cases for this case - // // must check if all child cases are existing in GLPI - // $locTaskCat = new PluginProcessmakerTaskCategory; - // foreach($pmRouteCaseResponse->routing as $route) { - // if ($locTaskCat->getFromGUID($route->taskId) && $locTaskCat->fields['is_subprocess']) { - // // look for APP_UID - // foreach($PM_DB->request("SELECT APP_UID FROM SUB_APPLICATION WHERE APP_PARENT='{$myCase->fields['case_guid']}' AND DEL_INDEX_PARENT={$route->delIndex} AND DEL_THREAD_PARENT={$route->delThread} AND SA_STATUS='ACTIVE'") as $subCase) { - // // normally only one case for this delIndex and delThread - // // now need to get the PRO_UID - // $sub_caseInfo = self::getCaseInfo($subCase['APP_UID']); - - // $locProc = new PluginProcessmakerProcess; - // $locProc->getFromGUID($sub_caseInfo->processId); - // $locCase = new PluginProcessmakerCase; - // $locCase->add(['id' => $sub_caseInfo->caseNumber, - // 'case_guid'=> $sub_caseInfo->caseId, - // 'itemtype' => $itemtype, - // 'items_id' => $items_id, - // 'name' => $sub_caseInfo->caseName, - // 'entities_id' => $item->fields['entities_id'], - // 'case_status' => $sub_caseInfo->caseStatus, - // 'plugin_processmaker_processes_id' => $locProc->getID(), - // 'plugin_processmaker_cases_id' => $myCase->getID()]); - - // // then create associated task - // if (property_exists( $sub_caseInfo, 'currentUsers' )) { - // foreach ($sub_caseInfo->currentUsers as $sub_route) { - // $this->addTask($locCase->getID(), $itemtype, - // $items_id, - // $sub_caseInfo, - // $sub_route->delIndex, - // PluginProcessmakerUser::getGLPIUserId($sub_route->userId), - // 0, - // $sub_route->taskId, - // $sub_route->delThread, - // [] - // //array( 'txtTaskContent' => $txtTaskContent, - // // 'start_date' => $taskStartDate, - // // 'end_date' => $taskEndDate) - // ); - - // } - // } - - // } - - // } - // } - - //} - $casevariables = ["GLPI_ITEM_TASK_CONTENT", "GLPI_ITEM_APPEND_TO_TASK", "GLPI_NEXT_GROUP_TO_BE_ASSIGNED", @@ -2814,13 +2748,17 @@ public function derivateCase($myCase, $request, $users_id = null) { "GLPI_TICKET_FOLLOWUP_REQUESTTYPES_ID", "GLPI_ITEM_TASK_ENDDATE", "GLPI_ITEM_TASK_STARTDATE", + "GLPI_ITEM_TASK_REMINDER", "GLPI_ITEM_SOLVED_TASK_ENDDATE", "GLPI_ITEM_SOLVED_TASK_STARTDATE", "GLPI_ITEM_SOLVED_TASK_SETINFO", "GLPI_ITEM_SET_STATUS", + "GLPI_ITEM_STATUS", "GLPI_ITEM_SET_SOLUTION_TEMPLATE_ID", "GLPI_ITEM_SET_SOLUTION_TYPE_ID", - "GLPI_ITEM_APPEND_TO_SOLUTION_DESCRIPTION" + "GLPI_ITEM_APPEND_TO_SOLUTION_DESCRIPTION", + "GLPI_ITEM_INITIAL_DUE_DATE", + "GLPI_ITEM_DUE_DATE" ]; // now tries to get some variables to setup content for new task and to append text to solved task @@ -2830,6 +2768,9 @@ public function derivateCase($myCase, $request, $users_id = null) { if (array_key_exists( 'GLPI_ITEM_SET_STATUS', $casevariablevalues )) { $itemSetStatus = $casevariablevalues[ 'GLPI_ITEM_SET_STATUS' ]; } + if (array_key_exists( 'GLPI_ITEM_STATUS', $casevariablevalues )) { + $itemSetStatus = $casevariablevalues[ 'GLPI_ITEM_STATUS' ]; + } $txtItemTitle = ''; if (array_key_exists( 'GLPI_ITEM_TITLE', $casevariablevalues )) { @@ -2864,6 +2805,11 @@ public function derivateCase($myCase, $request, $users_id = null) { } } + $taskReminder = ''; + if (array_key_exists( 'GLPI_ITEM_TASK_REMINDER', $casevariablevalues )) { + $taskReminder = $casevariablevalues[ 'GLPI_ITEM_TASK_REMINDER' ]; + } + $solvedTaskStartDate = ''; $solvedTaskEndDate = ''; if (array_key_exists( 'GLPI_ITEM_SOLVED_TASK_ENDDATE', $casevariablevalues )) { @@ -2889,6 +2835,18 @@ public function derivateCase($myCase, $request, $users_id = null) { $createFollowup = true; } + $item_duedate = ''; + if (array_key_exists('GLPI_ITEM_INITIAL_DUE_DATE', $casevariablevalues)) { + $item_duedate = $casevariablevalues['GLPI_ITEM_INITIAL_DUE_DATE']; + } + if (array_key_exists( 'GLPI_ITEM_DUE_DATE', $casevariablevalues )) { + $item_duedate = $casevariablevalues['GLPI_ITEM_DUE_DATE']; + } + $re = '/^(?\'date\'[0-9]{4}-[0-1][0-9]-[0-3][0-9])( (?\'time\'[0-2][0-9]:[0-5][0-9]:[0-5][0-9]))*$/'; + if (preg_match($re, $item_duedate, $matches) && !array_key_exists('time', $matches)) { + $item_duedate .= " 23:59:59"; + } + // reset those variables $resetcasevariables = []; foreach ($casevariables as $val) { @@ -2920,8 +2878,9 @@ public function derivateCase($myCase, $request, $users_id = null) { $this->setItemTitle($itemtype, $items_id, $txtItemTitle); } - if ($itemSetStatus != '') { - $this->setItemStatus($itemtype, $items_id, $itemSetStatus ); + if ($item_duedate != '') { + // we are going to change the due date (time to resolve) of current GLPI Item + $this->setItemDuedate($itemtype, $items_id, $item_duedate); } if (array_key_exists( 'GLPI_ITEM_SET_SOLUTION_TEMPLATE_ID', $casevariablevalues ) @@ -2931,6 +2890,10 @@ public function derivateCase($myCase, $request, $users_id = null) { $this->setItemSolution($itemtype, $items_id, $casevariablevalues); } + if ($itemSetStatus != '') { + $this->setItemStatus($itemtype, $items_id, $itemSetStatus ); + } + // get the new case info $caseInfo = $myCase->getCaseInfo($request['DEL_INDEX']); // not sure that it should passed this @@ -2978,8 +2941,9 @@ public function derivateCase($myCase, $request, $users_id = null) { $sub_route->taskId, $sub_route->delThread, [ 'txtTaskContent' => $txtTaskContent, - 'start_date' => $taskStartDate, - 'end_date' => $taskEndDate] + 'start_date' => $taskStartDate, + 'end_date' => $taskEndDate, + 'reminder' => $taskReminder] ); // if end date was specified, then must change due date of the PM task @@ -3002,6 +2966,7 @@ public function derivateCase($myCase, $request, $users_id = null) { 'GLPI_TICKET_ID' => $items_id, 'GLPI_ITEM_ID' => $items_id, 'GLPI_ITEM_TYPE' => $itemtype, + 'GLPI_ITEM_STATUS' => $item->fields['status'], 'GLPI_TICKET_REQUESTER_GLPI_ID' => $requesters[0]['glpi_id'], 'GLPI_ITEM_REQUESTER_GLPI_ID' => $requesters[0]['glpi_id'], 'GLPI_TICKET_REQUESTER_PM_ID' => $requesters[0]['pm_id'], @@ -3010,20 +2975,26 @@ public function derivateCase($myCase, $request, $users_id = null) { 'GLPI_ITEM_TITLE' => $item->fields['name'], 'GLPI_TICKET_DESCRIPTION' => $item->fields['content'], 'GLPI_ITEM_DESCRIPTION' => $item->fields['content'], - 'GLPI_TICKET_DUE_DATE' => $item->fields['time_to_resolve'], 'GLPI_ITEM_OPENING_DATE' => $item->fields['date'], + 'GLPI_TICKET_DUE_DATE' => $item->fields['time_to_resolve'], 'GLPI_ITEM_DUE_DATE' => $item->fields['time_to_resolve'], 'GLPI_ITEM_ITIL_CATEGORY_ID' => $item->fields['itilcategories_id'], 'GLPI_TICKET_URGENCY' => $item->fields['urgency'], 'GLPI_ITEM_URGENCY' => $item->fields['urgency'], 'GLPI_ITEM_IMPACT' => $item->fields['impact'], 'GLPI_ITEM_PRIORITY' => $item->fields['priority'], - 'GLPI_TICKET_GLOBAL_VALIDATION' => $item->fields['global_validation'] , + // Specific to Tickets and Changes + // GLPI_ITEM_GLOBAL_VALIDATION will be '' when Problem, else it will be the global_validation field + 'GLPI_TICKET_GLOBAL_VALIDATION' => $itemtype == 'Problem' ? '' : $item->fields['global_validation'], + 'GLPI_ITEM_GLOBAL_VALIDATION' => $itemtype == 'Problem' ? '' : $item->fields['global_validation'], 'GLPI_TICKET_TECHNICIAN_GLPI_ID' => $users_id, 'GLPI_ITEM_TECHNICIAN_GLPI_ID' => $users_id, 'GLPI_TICKET_TECHNICIAN_PM_ID' => PluginProcessmakerUser::getPMUserId( $users_id ), 'GLPI_ITEM_TECHNICIAN_PM_ID' => PluginProcessmakerUser::getPMUserId( $users_id ), - 'GLPI_URL' => $CFG_GLPI['url_base'] + 'GLPI_URL' => $CFG_GLPI['url_base'], + // Specific to Tickets + // GLPI_TICKET_TYPE will contains 1 (= incident) or 2 (= request) + 'GLPI_TICKET_TYPE' => $itemtype == 'Ticket' ? $item->fields['type'] : '' ]; $subCase->sendVariables($glpi_variables); @@ -3042,8 +3013,9 @@ public function derivateCase($myCase, $request, $users_id = null) { $route->taskId, $route->delThread, [ 'txtTaskContent' => $txtTaskContent, - 'start_date' => $taskStartDate, - 'end_date' => $taskEndDate] + 'start_date' => $taskStartDate, + 'end_date' => $taskEndDate, + 'reminder' => $taskReminder] ); // if end date was specified, then must change due date of the PM task if ($taskEndDate != '') { @@ -3079,8 +3051,9 @@ public function derivateCase($myCase, $request, $users_id = null) { $open_task->taskId, $open_task->delThread, [ 'txtTaskContent' => $txtTaskContent, - 'start_date' => $taskStartDate, - 'end_date' => $taskEndDate] + 'start_date' => $taskStartDate, + 'end_date' => $taskEndDate, + 'reminder' => $taskReminder] ); // if end date was specified, then must change due date of the PM task @@ -3210,8 +3183,32 @@ static function showUnderMaintenance() { echo "
"; echo Html::image($CFG_GLPI['root_doc'].'/plugins/processmaker/pics/under_maintenance.png'); echo "

"; - __('ProcessMaker plugin is under maintenance, please retry later, thank you.', 'processmaker'); + echo __('ProcessMaker plugin is under maintenance, please retry later, thank you.', 'processmaker'); echo "

"; echo "
"; } -} + + + /** + * Summary of echoDomain + */ + function echoDomain() { + if (isset($this->config->fields['domain']) && $this->config->fields['domain'] != '') { + $script = " + (function() { + var id = 'commonDomainGlpiPmScript_Wjd4uWisWHLt9I'; + //debugger; + if ($('#' + id).length == 0) { + //debugger; + var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0]; + g.type = 'text/javascript'; + g.id = id; + g.text = 'try { document.domain = \'".$this->config->fields['domain']."\'; } catch(ev) { /*console.log(ev);*/ }'; + s.parentNode.insertBefore(g, s); + } + })();"; + + echo Html::scriptBlock($script); + } + } +} \ No newline at end of file diff --git a/inc/selfservicedraft.class.php b/inc/selfservicedraft.class.php index 705d4bc..df45378 100644 --- a/inc/selfservicedraft.class.php +++ b/inc/selfservicedraft.class.php @@ -10,4 +10,4 @@ */ class PluginProcessmakerSelfservicedraft extends CommonDBTM { -} \ No newline at end of file +} diff --git a/inc/task.class.php b/inc/task.class.php index c6e02d6..f87fe89 100644 --- a/inc/task.class.php +++ b/inc/task.class.php @@ -347,6 +347,7 @@ static function displayTabContentForItem(CommonGLPI $case, $tabnum = 1, $withtem } } + $PM_SOAP->echoDomain(); echo ""; $csrf = Session::getNewCSRFToken(); diff --git a/inc/taskcategory.class.php b/inc/taskcategory.class.php index d0952e1..c73da85 100644 --- a/inc/taskcategory.class.php +++ b/inc/taskcategory.class.php @@ -94,14 +94,16 @@ static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtem static function title(CommonGLPI $item) { global $CFG_GLPI; - $buttons = []; - $title = __('Synchronize Task List', 'processmaker'); - if (Session::haveRight('plugin_processmaker_config', UPDATE)) { - $buttons["process.form.php?refreshtask=1&id=".$item->getID()] = $title; - Html::displayTitle($CFG_GLPI["root_doc"] . "/plugins/processmaker/pics/gears.png", $title, "", - $buttons); + $title = __('Synchronize Task List', 'processmaker'); + $buttons = ["process.form.php?refreshtask=1&id=".$item->getID() => $title]; + $pic = $CFG_GLPI["root_doc"] . "/plugins/processmaker/pics/gears.png"; + if ($item->fields['maintenance']) { + $pic = $CFG_GLPI["root_doc"] . "/plugins/processmaker/pics/verysmall-under_maintenance.png"; + } + Html::displayTitle($pic, $title, "", $buttons); } + } //function getLinkItemFromExternalID($extId) { diff --git a/install/install.php b/install/install.php index ec0b43d..0f1825d 100644 --- a/install/install.php +++ b/install/install.php @@ -4,29 +4,10 @@ function processmaker_install() { global $DB; // installation from scratch - include_once(GLPI_ROOT."/plugins/processmaker/setup.php"); - $info = plugin_version_processmaker(); - switch ($info['version']) { - case '3.3.0' : - $version = '3.3.0'; - break; - case '3.3.1' : - case '3.3.2' : - case '3.3.3' : - case '3.3.4' : - case '3.3.5' : - case '3.3.6' : - case '3.3.7' : - $version = '3.3.1'; - break; - case '3.3.8' : - default : - $version = '3.3.8'; - break; - } - $DB->runFile(GLPI_ROOT . "/plugins/processmaker/install/mysql/$version-empty.sql"); + $DB->runFile(GLPI_ROOT . "/plugins/processmaker/install/mysql/processmaker-empty.sql"); // add configuration singleton $query = "INSERT INTO `glpi_plugin_processmaker_configs` (`id`) VALUES (1);"; $DB->query( $query ) or die("error creating default record in glpi_plugin_processmaker_configs" . $DB->error()); + } diff --git a/install/mysql/3.2.8-empty.sql b/install/mysql/3.2.8-empty.sql deleted file mode 100644 index 6a217a1..0000000 --- a/install/mysql/3.2.8-empty.sql +++ /dev/null @@ -1,167 +0,0 @@ -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET NAMES utf8 */; -/*!50503 SET NAMES utf8mb4 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; - --- Dumping structure for table glpi.glpi_plugin_processmaker_caselinkactions -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_caselinkactions` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `plugin_processmaker_caselinks_id` int(11) DEFAULT NULL, - `name` varchar(255) NOT NULL, - `value` text, - PRIMARY KEY (`id`), - UNIQUE KEY `caselinks_id_name` (`plugin_processmaker_caselinks_id`,`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_caselinks -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_caselinks` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(255) NOT NULL, - `is_active` tinyint(1) NOT NULL DEFAULT '0', - `is_externaldata` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0:insert data from case,1:wait for external application to set datas', - `is_self` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0:use linked tickets, 1:use self', - `sourcetask_guid` varchar(32) DEFAULT NULL, - `targettask_guid` varchar(32) DEFAULT NULL, - `targetprocess_guid` varchar(32) DEFAULT NULL, - `targetdynaform_guid` varchar(32) DEFAULT NULL, - `sourcecondition` text, - `is_targettoclaim` tinyint(1) NOT NULL DEFAULT '0', - `externalapplication` text, - `date_mod` timestamp NULL DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `is_active` (`is_active`), - KEY `is_externaldata` (`is_externaldata`), - KEY `is_self` (`is_self`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_cases -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_cases` ( - `id` varchar(32) NOT NULL, - `items_id` int(11) NOT NULL, - `itemtype` varchar(10) NOT NULL DEFAULT 'Ticket', - `case_num` int(11) NOT NULL, - `case_status` varchar(20) NOT NULL DEFAULT 'DRAFT', - `processes_id` int(11) DEFAULT NULL, - UNIQUE KEY `items` (`itemtype`,`items_id`), - KEY `case_status` (`case_status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_configs -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_configs` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(50) NOT NULL DEFAULT 'ProcessMaker', - `pm_server_URL` varchar(250) NOT NULL DEFAULT 'http://localhost/', - `pm_workspace` varchar(50) NOT NULL DEFAULT 'workflow', - `pm_admin_user` varchar(255) DEFAULT NULL, - `pm_admin_passwd` varchar(255) DEFAULT NULL, - `pm_theme` varchar(50) NOT NULL DEFAULT 'glpi_classic', - `date_mod` timestamp NULL DEFAULT NULL, - `taskcategories_id` int(11) DEFAULT NULL, - `users_id` int(11) DEFAULT NULL, - `pm_group_guid` varchar(32) DEFAULT NULL, - `comment` text, - `pm_dbserver_name` varchar(255) DEFAULT 'localhost', - `pm_dbname` varchar(50) DEFAULT 'wf_workflow', - `pm_dbserver_user` varchar(255) DEFAULT NULL, - `pm_dbserver_passwd` varchar(255) DEFAULT NULL, - `domain` varchar(50) DEFAULT '', - `maintenance` tinyint(1) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_crontaskactions -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_crontaskactions` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `plugin_processmaker_caselinks_id` int(11) DEFAULT NULL, - `itemtype` varchar(100) NOT NULL, - `items_id` int(11) NOT NULL DEFAULT '0', - `users_id` int(11) NOT NULL DEFAULT '0', - `is_targettoclaim` tinyint(1) NOT NULL DEFAULT '0', - `postdata` mediumtext, - `logs_out` mediumtext, - `state` int(11) NOT NULL, - `date_mod` timestamp NULL DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_processes -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_processes` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `process_guid` varchar(32) NOT NULL, - `name` varchar(255) NOT NULL, - `is_active` tinyint(1) NOT NULL DEFAULT '0', - `hide_case_num_title` tinyint(1) NOT NULL DEFAULT '0', - `insert_task_comment` tinyint(1) NOT NULL DEFAULT '0', - `comment` text, - `taskcategories_id` int(11) DEFAULT NULL, - `itilcategories_id` int(11) NOT NULL DEFAULT '0', - `type` int(11) NOT NULL DEFAULT '1' COMMENT 'Only used for Tickets', - `date_mod` timestamp NULL DEFAULT NULL, - `project_type` varchar(50) NOT NULL DEFAULT 'classic', - PRIMARY KEY (`id`), - UNIQUE KEY `process_guid` (`process_guid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_processes_profiles -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_processes_profiles` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `processes_id` int(11) NOT NULL DEFAULT '0', - `profiles_id` int(11) NOT NULL DEFAULT '0', - `entities_id` int(11) NOT NULL DEFAULT '0', - `is_recursive` tinyint(1) NOT NULL DEFAULT '1', - PRIMARY KEY (`id`), - KEY `entities_id` (`entities_id`), - KEY `profiles_id` (`profiles_id`), - KEY `processes_id` (`processes_id`), - KEY `is_recursive` (`is_recursive`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_taskcategories -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_taskcategories` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `processes_id` int(11) NOT NULL, - `pm_task_guid` varchar(32) NOT NULL, - `taskcategories_id` int(11) NOT NULL, - `start` bit(1) NOT NULL DEFAULT b'0', - `is_active` tinyint(1) NOT NULL DEFAULT '1', - PRIMARY KEY (`id`), - UNIQUE KEY `pm_task_guid` (`pm_task_guid`), - UNIQUE KEY `items` (`taskcategories_id`), - KEY `processes_id` (`processes_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_tasks -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_tasks` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `items_id` int(11) NOT NULL, - `itemtype` varchar(32) NOT NULL, - `case_id` varchar(32) NOT NULL, - `del_index` int(11) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `case_id` (`case_id`,`del_index`), - UNIQUE KEY `items` (`itemtype`,`items_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_users -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_users` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `pm_users_id` varchar(32) NOT NULL, - `password` varchar(32) DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `pm_users_id` (`pm_users_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - -/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; -/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; diff --git a/install/mysql/3.2.9-empty.sql b/install/mysql/3.2.9-empty.sql deleted file mode 100644 index 3ee30cd..0000000 --- a/install/mysql/3.2.9-empty.sql +++ /dev/null @@ -1,168 +0,0 @@ -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET NAMES utf8 */; -/*!50503 SET NAMES utf8mb4 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; - --- Dumping structure for table glpi.glpi_plugin_processmaker_caselinkactions -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_caselinkactions` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `plugin_processmaker_caselinks_id` int(11) DEFAULT NULL, - `name` varchar(255) NOT NULL, - `value` text, - PRIMARY KEY (`id`), - UNIQUE KEY `caselinks_id_name` (`plugin_processmaker_caselinks_id`,`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_caselinks -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_caselinks` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(255) NOT NULL, - `is_active` tinyint(1) NOT NULL DEFAULT '0', - `is_externaldata` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0:insert data from case,1:wait for external application to set datas', - `is_self` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0:use linked tickets, 1:use self', - `sourcetask_guid` varchar(32) DEFAULT NULL, - `targettask_guid` varchar(32) DEFAULT NULL, - `targetprocess_guid` varchar(32) DEFAULT NULL, - `targetdynaform_guid` varchar(32) DEFAULT NULL, - `sourcecondition` text, - `is_targettoclaim` tinyint(1) NOT NULL DEFAULT '0', - `externalapplication` text, - `date_mod` timestamp NULL DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `is_active` (`is_active`), - KEY `is_externaldata` (`is_externaldata`), - KEY `is_self` (`is_self`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_cases -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_cases` ( - `id` varchar(32) NOT NULL, - `items_id` int(11) NOT NULL, - `itemtype` varchar(10) NOT NULL DEFAULT 'Ticket', - `case_num` int(11) NOT NULL, - `case_status` varchar(20) NOT NULL DEFAULT 'DRAFT', - `processes_id` int(11) DEFAULT NULL, - UNIQUE KEY `items` (`itemtype`,`items_id`), - KEY `case_status` (`case_status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_configs -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_configs` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(50) NOT NULL DEFAULT 'ProcessMaker', - `pm_server_URL` varchar(250) NOT NULL DEFAULT 'http://localhost/', - `pm_workspace` varchar(50) NOT NULL DEFAULT 'workflow', - `pm_admin_user` varchar(255) DEFAULT NULL, - `pm_admin_passwd` varchar(255) DEFAULT NULL, - `pm_theme` varchar(50) NOT NULL DEFAULT 'glpi_classic', - `date_mod` timestamp NULL DEFAULT NULL, - `taskcategories_id` int(11) DEFAULT NULL, - `users_id` int(11) DEFAULT NULL, - `pm_group_guid` varchar(32) DEFAULT NULL, - `comment` text, - `pm_dbserver_name` varchar(255) DEFAULT 'localhost', - `pm_dbname` varchar(50) DEFAULT 'wf_workflow', - `pm_dbserver_user` varchar(255) DEFAULT NULL, - `pm_dbserver_passwd` varchar(255) DEFAULT NULL, - `domain` varchar(50) DEFAULT '', - `maintenance` tinyint(1) NOT NULL DEFAULT '0', - `db_version` varchar(10) NOT NULL DEFAULT '3.2.9', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_crontaskactions -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_crontaskactions` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `plugin_processmaker_caselinks_id` int(11) DEFAULT NULL, - `itemtype` varchar(100) NOT NULL, - `items_id` int(11) NOT NULL DEFAULT '0', - `users_id` int(11) NOT NULL DEFAULT '0', - `is_targettoclaim` tinyint(1) NOT NULL DEFAULT '0', - `postdata` mediumtext, - `logs_out` mediumtext, - `state` int(11) NOT NULL, - `date_mod` timestamp NULL DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_processes -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_processes` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `process_guid` varchar(32) NOT NULL, - `name` varchar(255) NOT NULL, - `is_active` tinyint(1) NOT NULL DEFAULT '0', - `hide_case_num_title` tinyint(1) NOT NULL DEFAULT '0', - `insert_task_comment` tinyint(1) NOT NULL DEFAULT '0', - `comment` text, - `taskcategories_id` int(11) DEFAULT NULL, - `itilcategories_id` int(11) NOT NULL DEFAULT '0', - `type` int(11) NOT NULL DEFAULT '1' COMMENT 'Only used for Tickets', - `date_mod` timestamp NULL DEFAULT NULL, - `project_type` varchar(50) NOT NULL DEFAULT 'classic', - PRIMARY KEY (`id`), - UNIQUE KEY `process_guid` (`process_guid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_processes_profiles -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_processes_profiles` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `processes_id` int(11) NOT NULL DEFAULT '0', - `profiles_id` int(11) NOT NULL DEFAULT '0', - `entities_id` int(11) NOT NULL DEFAULT '0', - `is_recursive` tinyint(1) NOT NULL DEFAULT '1', - PRIMARY KEY (`id`), - KEY `entities_id` (`entities_id`), - KEY `profiles_id` (`profiles_id`), - KEY `processes_id` (`processes_id`), - KEY `is_recursive` (`is_recursive`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_taskcategories -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_taskcategories` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `processes_id` int(11) NOT NULL, - `pm_task_guid` varchar(32) NOT NULL, - `taskcategories_id` int(11) NOT NULL, - `start` bit(1) NOT NULL DEFAULT b'0', - `is_active` tinyint(1) NOT NULL DEFAULT '1', - PRIMARY KEY (`id`), - UNIQUE KEY `pm_task_guid` (`pm_task_guid`), - UNIQUE KEY `items` (`taskcategories_id`), - KEY `processes_id` (`processes_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_tasks -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_tasks` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `items_id` int(11) NOT NULL, - `itemtype` varchar(32) NOT NULL, - `case_id` varchar(32) NOT NULL, - `del_index` int(11) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `case_id` (`case_id`,`del_index`), - UNIQUE KEY `items` (`itemtype`,`items_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_users -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_users` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `pm_users_id` varchar(32) NOT NULL, - `password` varchar(32) DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `pm_users_id` (`pm_users_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - -/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; -/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; diff --git a/install/mysql/3.3.0-empty.sql b/install/mysql/3.3.0-empty.sql deleted file mode 100644 index 3049ca9..0000000 --- a/install/mysql/3.3.0-empty.sql +++ /dev/null @@ -1,179 +0,0 @@ -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET NAMES utf8 */; -/*!50503 SET NAMES utf8mb4 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; - --- Dumping structure for table glpi.glpi_plugin_processmaker_caselinkactions -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_caselinkactions` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `plugin_processmaker_caselinks_id` int(11) DEFAULT NULL, - `name` varchar(255) NOT NULL, - `value` text, - PRIMARY KEY (`id`), - UNIQUE KEY `caselinks_id_name` (`plugin_processmaker_caselinks_id`,`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_caselinks -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_caselinks` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(255) NOT NULL, - `is_active` tinyint(1) NOT NULL DEFAULT '0', - `is_externaldata` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0:insert data from case,1:wait for external application to set datas', - `is_self` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0:use linked tickets, 1:use self', - `sourcetask_guid` varchar(32) DEFAULT NULL, - `targettask_guid` varchar(32) DEFAULT NULL, - `targetprocess_guid` varchar(32) DEFAULT NULL, - `targetdynaform_guid` varchar(32) DEFAULT NULL, - `sourcecondition` text, - `is_targettoclaim` tinyint(1) NOT NULL DEFAULT '0', - `externalapplication` text, - `date_mod` timestamp NULL DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `is_active` (`is_active`), - KEY `is_externaldata` (`is_externaldata`), - KEY `is_self` (`is_self`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_cases -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_cases` ( - `id` INT(11) NOT NULL, - `itemtype` VARCHAR(10) NOT NULL DEFAULT 'Ticket', - `items_id` INT(11) NOT NULL, - `entities_id` INT(11) NOT NULL DEFAULT '0', - `name` MEDIUMTEXT NOT NULL DEFAULT '', - `case_guid` VARCHAR(32) NOT NULL, - `case_status` VARCHAR(20) NOT NULL DEFAULT 'DRAFT', - `plugin_processmaker_processes_id` INT(11) NULL DEFAULT NULL, - `plugin_processmaker_cases_id` INT(11) NULL DEFAULT NULL, - INDEX `items` (`itemtype`, `items_id`), - INDEX `case_status` (`case_status`), - PRIMARY KEY (`id`), - UNIQUE INDEX `case_guid` (`case_guid`), - INDEX `plugin_processmaker_processes_id` (`plugin_processmaker_processes_id`), - INDEX `plugin_processmaker_cases_id` (`plugin_processmaker_cases_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_configs -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_configs` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(50) NOT NULL DEFAULT 'ProcessMaker', - `pm_server_URL` varchar(250) NOT NULL DEFAULT 'http://localhost/', - `pm_workspace` varchar(50) NOT NULL DEFAULT 'workflow', - `pm_admin_user` varchar(255) DEFAULT NULL, - `pm_admin_passwd` varchar(255) DEFAULT NULL, - `pm_theme` varchar(50) NOT NULL DEFAULT 'glpi_classic', - `date_mod` timestamp NULL DEFAULT NULL, - `taskcategories_id` int(11) DEFAULT NULL, - `users_id` int(11) DEFAULT NULL, - `pm_group_guid` varchar(32) DEFAULT NULL, - `comment` text, - `pm_dbserver_name` varchar(255) DEFAULT 'localhost', - `pm_dbname` varchar(50) DEFAULT 'wf_workflow', - `pm_dbserver_user` varchar(255) DEFAULT NULL, - `pm_dbserver_passwd` varchar(255) DEFAULT NULL, - `domain` varchar(50) DEFAULT '', - `maintenance` tinyint(1) NOT NULL DEFAULT '0', - `db_version` varchar(10) NOT NULL DEFAULT '3.3.0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_crontaskactions -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_crontaskactions` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `plugin_processmaker_caselinks_id` int(11) DEFAULT NULL, - `plugin_processmaker_cases_id` int(11) DEFAULT '0', - `users_id` int(11) NOT NULL DEFAULT '0', - `is_targettoclaim` tinyint(1) NOT NULL DEFAULT '0', - `postdata` mediumtext, - `logs_out` mediumtext, - `state` int(11) NOT NULL, - `date_mod` timestamp NULL DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_processes -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_processes` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `process_guid` varchar(32) NOT NULL, - `name` varchar(255) NOT NULL, - `is_active` tinyint(1) NOT NULL DEFAULT '0', - `hide_case_num_title` tinyint(1) NOT NULL DEFAULT '0', - `insert_task_comment` tinyint(1) NOT NULL DEFAULT '0', - `comment` text, - `taskcategories_id` int(11) DEFAULT NULL, - `itilcategories_id` int(11) NOT NULL DEFAULT '0', - `type` int(11) NOT NULL DEFAULT '1' COMMENT 'Only used for Tickets', - `date_mod` timestamp NULL DEFAULT NULL, - `project_type` varchar(50) NOT NULL DEFAULT 'classic', - PRIMARY KEY (`id`), - UNIQUE KEY `process_guid` (`process_guid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_processes_profiles -CREATE TABLE `glpi_plugin_processmaker_processes_profiles` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `plugin_processmaker_processes_id` int(11) NOT NULL, - `profiles_id` int(11) NOT NULL, - `entities_id` int(11) NOT NULL, - `is_recursive` tinyint(1) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE INDEX `plugin_processmaker_processes_id_profiles_id_entities_id` (`plugin_processmaker_processes_id`, `profiles_id`, `entities_id`), - KEY `entities_id` (`entities_id`), - KEY `profiles_id` (`profiles_id`), - KEY `plugin_processmaker_processes_id` (`plugin_processmaker_processes_id`), - KEY `is_recursive` (`is_recursive`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 ; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_taskcategories -CREATE TABLE `glpi_plugin_processmaker_taskcategories` ( - `id` INT(11) NOT NULL AUTO_INCREMENT, - `plugin_processmaker_processes_id` INT(11) NOT NULL, - `pm_task_guid` VARCHAR(32) NOT NULL, - `taskcategories_id` INT(11) NOT NULL, - `is_start` TINYINT(1) NOT NULL DEFAULT '0', - `is_active` TINYINT(1) NOT NULL DEFAULT '1', - `is_subprocess` TINYINT(1) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE INDEX `pm_task_guid` (`pm_task_guid`), - UNIQUE INDEX `items` (`taskcategories_id`), - INDEX `plugin_processmaker_processes_id` (`plugin_processmaker_processes_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_tasks -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_tasks` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `items_id` int(11) NOT NULL, - `itemtype` varchar(32) NOT NULL, - `plugin_processmaker_cases_id` int(11) NOT NULL, - `plugin_processmaker_taskcategories_id` int(11) NOT NULL, - `del_index` int(11) NOT NULL, - `del_thread` INT(11) NOT NULL, - `del_thread_status` varchar(32) NOT NULL DEFAULT 'OPEN', - PRIMARY KEY (`id`), - UNIQUE KEY `tasks` (`plugin_processmaker_cases_id`,`del_index`), - UNIQUE KEY `items` (`itemtype`,`items_id`), - KEY `del_thread_status` (`del_thread_status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_users -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_users` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `pm_users_id` varchar(32) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `pm_users_id` (`pm_users_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - -/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; -/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; diff --git a/install/mysql/3.3.1-empty.sql b/install/mysql/3.3.1-empty.sql deleted file mode 100644 index cb7d897..0000000 --- a/install/mysql/3.3.1-empty.sql +++ /dev/null @@ -1,186 +0,0 @@ -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET NAMES utf8 */; -/*!50503 SET NAMES utf8mb4 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; - --- Dumping structure for table glpi.glpi_plugin_processmaker_caselinkactions -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_caselinkactions` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `plugin_processmaker_caselinks_id` int(11) DEFAULT NULL, - `name` varchar(255) NOT NULL, - `value` text, - PRIMARY KEY (`id`), - UNIQUE KEY `caselinks_id_name` (`plugin_processmaker_caselinks_id`,`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_caselinks -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_caselinks` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(255) NOT NULL, - `is_active` tinyint(1) NOT NULL DEFAULT '0', - `is_externaldata` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0:insert data from case,1:wait for external application to set datas', - `is_self` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0:use linked tickets, 1:use self', - `sourcetask_guid` varchar(32) DEFAULT NULL, - `targettask_guid` varchar(32) DEFAULT NULL, - `targetprocess_guid` varchar(32) DEFAULT NULL, - `targetdynaform_guid` varchar(32) DEFAULT NULL, - `sourcecondition` text, - `is_targettoclaim` tinyint(1) NOT NULL DEFAULT '0', - `is_targettoreassign` TINYINT(1) NOT NULL DEFAULT '0', - `is_targettoimpersonate` TINYINT(1) NOT NULL DEFAULT '0', - `externalapplication` TEXT NULL, - `is_synchronous` TINYINT(1) NOT NULL DEFAULT '0', - `date_mod` timestamp NULL DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `is_active` (`is_active`), - KEY `is_externaldata` (`is_externaldata`), - KEY `is_self` (`is_self`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_cases -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_cases` ( - `id` INT(11) NOT NULL, - `itemtype` VARCHAR(10) NOT NULL DEFAULT 'Ticket', - `items_id` INT(11) NOT NULL, - `entities_id` INT(11) NOT NULL DEFAULT '0', - `name` MEDIUMTEXT NOT NULL DEFAULT '', - `case_guid` VARCHAR(32) NOT NULL, - `case_status` VARCHAR(20) NOT NULL DEFAULT 'DRAFT', - `plugin_processmaker_processes_id` INT(11) NULL DEFAULT NULL, - `plugin_processmaker_cases_id` INT(11) NULL DEFAULT NULL, - INDEX `items` (`itemtype`, `items_id`), - INDEX `case_status` (`case_status`), - PRIMARY KEY (`id`), - UNIQUE INDEX `case_guid` (`case_guid`), - INDEX `plugin_processmaker_processes_id` (`plugin_processmaker_processes_id`), - INDEX `plugin_processmaker_cases_id` (`plugin_processmaker_cases_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_configs -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_configs` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(50) NOT NULL DEFAULT 'ProcessMaker', - `pm_server_URL` varchar(250) NOT NULL DEFAULT 'http://localhost/', - `pm_workspace` varchar(50) NOT NULL DEFAULT 'workflow', - `pm_admin_user` varchar(255) DEFAULT NULL, - `pm_admin_passwd` varchar(255) DEFAULT NULL, - `pm_theme` varchar(50) NOT NULL DEFAULT 'glpi_classic', - `date_mod` timestamp NULL DEFAULT NULL, - `taskcategories_id` int(11) DEFAULT NULL, - `users_id` int(11) DEFAULT NULL, - `pm_group_guid` varchar(32) DEFAULT NULL, - `comment` text, - `pm_dbserver_name` varchar(255) DEFAULT 'localhost', - `pm_dbname` varchar(50) DEFAULT 'wf_workflow', - `pm_dbserver_user` varchar(255) DEFAULT NULL, - `pm_dbserver_passwd` varchar(255) DEFAULT NULL, - `domain` varchar(50) DEFAULT '', - `maintenance` tinyint(1) NOT NULL DEFAULT '0', - `db_version` varchar(10) NOT NULL DEFAULT '3.3.0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_crontaskactions -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_crontaskactions` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `plugin_processmaker_caselinks_id` int(11) DEFAULT NULL, - `plugin_processmaker_cases_id` int(11) DEFAULT '0', - `users_id` int(11) NOT NULL DEFAULT '0', - `is_targettoclaim` tinyint(1) NOT NULL DEFAULT '0', - `postdata` mediumtext, - `logs_out` mediumtext, - `state` int(11) NOT NULL, - `date_mod` timestamp NULL DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_processes -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_processes` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `process_guid` varchar(32) NOT NULL, - `name` varchar(255) NOT NULL, - `is_active` tinyint(1) NOT NULL DEFAULT '0', - `hide_case_num_title` tinyint(1) NOT NULL DEFAULT '0', - `insert_task_comment` tinyint(1) NOT NULL DEFAULT '0', - `comment` text, - `taskcategories_id` int(11) DEFAULT NULL, - `itilcategories_id` int(11) NOT NULL DEFAULT '0', - `type` int(11) NOT NULL DEFAULT '1' COMMENT 'Only used for self-service Tickets', - `date_mod` timestamp NULL DEFAULT NULL, - `project_type` varchar(50) NOT NULL DEFAULT 'classic', - `is_change` tinyint(1) NOT NULL DEFAULT '0', - `is_problem` tinyint(1) NOT NULL DEFAULT '0', - `is_incident` tinyint(1) NOT NULL DEFAULT '0', - `is_request` tinyint(1) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `process_guid` (`process_guid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_processes_profiles -CREATE TABLE `glpi_plugin_processmaker_processes_profiles` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `plugin_processmaker_processes_id` int(11) NOT NULL, - `profiles_id` int(11) NOT NULL, - `entities_id` int(11) NOT NULL, - `is_recursive` tinyint(1) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE INDEX `plugin_processmaker_processes_id_profiles_id_entities_id` (`plugin_processmaker_processes_id`, `profiles_id`, `entities_id`), - KEY `entities_id` (`entities_id`), - KEY `profiles_id` (`profiles_id`), - KEY `plugin_processmaker_processes_id` (`plugin_processmaker_processes_id`), - KEY `is_recursive` (`is_recursive`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 ; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_taskcategories -CREATE TABLE `glpi_plugin_processmaker_taskcategories` ( - `id` INT(11) NOT NULL AUTO_INCREMENT, - `plugin_processmaker_processes_id` INT(11) NOT NULL, - `pm_task_guid` VARCHAR(32) NOT NULL, - `taskcategories_id` INT(11) NOT NULL, - `is_start` TINYINT(1) NOT NULL DEFAULT '0', - `is_active` TINYINT(1) NOT NULL DEFAULT '1', - `is_subprocess` TINYINT(1) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE INDEX `pm_task_guid` (`pm_task_guid`), - UNIQUE INDEX `items` (`taskcategories_id`), - INDEX `plugin_processmaker_processes_id` (`plugin_processmaker_processes_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_tasks -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_tasks` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `items_id` int(11) NOT NULL, - `itemtype` varchar(32) NOT NULL, - `plugin_processmaker_cases_id` int(11) NOT NULL, - `plugin_processmaker_taskcategories_id` int(11) NOT NULL, - `del_index` int(11) NOT NULL, - `del_thread` INT(11) NOT NULL, - `del_thread_status` varchar(32) NOT NULL DEFAULT 'OPEN', - PRIMARY KEY (`id`), - UNIQUE KEY `tasks` (`plugin_processmaker_cases_id`,`del_index`), - UNIQUE KEY `items` (`itemtype`,`items_id`), - KEY `del_thread_status` (`del_thread_status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - --- Dumping structure for table glpi.glpi_plugin_processmaker_users -CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_users` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `pm_users_id` varchar(32) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `pm_users_id` (`pm_users_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - -/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; -/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; diff --git a/install/mysql/3.3.8-empty.sql b/install/mysql/processmaker-empty.sql similarity index 96% rename from install/mysql/3.3.8-empty.sql rename to install/mysql/processmaker-empty.sql index a5be338..3ad90bb 100644 --- a/install/mysql/3.3.8-empty.sql +++ b/install/mysql/processmaker-empty.sql @@ -64,7 +64,7 @@ CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_cases` ( CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_configs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL DEFAULT 'ProcessMaker', - `pm_server_URL` varchar(250) NOT NULL DEFAULT 'http://localhost/', + `pm_server_URL` varchar(250) NOT NULL DEFAULT 'http://itsm-pm.acme.com/', `pm_workspace` varchar(50) NOT NULL DEFAULT 'workflow', `pm_admin_user` varchar(255) DEFAULT NULL, `pm_admin_passwd` varchar(255) DEFAULT NULL, @@ -81,7 +81,8 @@ CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_configs` ( `domain` varchar(50) DEFAULT '', `maintenance` tinyint(1) NOT NULL DEFAULT '0', `ssl_verify` tinyint(1) NOT NULL DEFAULT '0', - `db_version` varchar(10) NOT NULL DEFAULT '3.3.8', + `db_version` varchar(10) NOT NULL DEFAULT '3.4.9', + `max_cases_per_item` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -119,6 +120,8 @@ CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_processes` ( `is_problem` tinyint(1) NOT NULL DEFAULT '0', `is_incident` tinyint(1) NOT NULL DEFAULT '0', `is_request` tinyint(1) NOT NULL DEFAULT '0', + `maintenance` TINYINT(1) NOT NULL DEFAULT '0', + `max_cases_per_item` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), UNIQUE KEY `process_guid` (`process_guid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/install/update.php b/install/update.php index 06f713a..a68e8df 100644 --- a/install/update.php +++ b/install/update.php @@ -11,6 +11,7 @@ function processmaker_update() { include_once(GLPI_ROOT."/plugins/processmaker/inc/config.class.php"); $config = PluginProcessmakerConfig::getInstance(); $current_version = $config->fields['db_version']; + if (empty($current_version)) $current_version = '2.4.1'; } switch ($current_version) { @@ -37,6 +38,10 @@ function processmaker_update() { // will upgrade 3.3.1 to 3.3.8 include_once(GLPI_ROOT."/plugins/processmaker/install/update_3_3_1_to_3_3_8.php"); $new_version = update_3_3_1_to_3_3_8(); + case '3.3.8' : + // will upgrade 3.3.8 to 3.4.9 + include_once(GLPI_ROOT."/plugins/processmaker/install/update_3_3_8_to_3_4_9.php"); + $new_version = update_3_3_8_to_3_4_9(); } if (isset($new_version)) { diff --git a/install/update_3_3_1_to_3_3_8.php b/install/update_3_3_1_to_3_3_8.php index ebac746..1a4e4f7 100644 --- a/install/update_3_3_1_to_3_3_8.php +++ b/install/update_3_3_1_to_3_3_8.php @@ -1,6 +1,6 @@ fieldExists("glpi_plugin_processmaker_configs", "max_cases_per_item" )) { + $query = "ALTER TABLE `glpi_plugin_processmaker_configs` + ADD COLUMN `max_cases_per_item` INT(11) NOT NULL DEFAULT '0' AFTER `db_version`;"; + + $DB->query($query) or die("error adding max_cases_per_item to glpi_plugin_processmaker_configs table" . $DB->error()); + } + + // Alter table glpi_plugin_processmaker_processes + if (!$DB->fieldExists("glpi_plugin_processmaker_processes", "maintenance" )) { + $query = "ALTER TABLE `glpi_plugin_processmaker_processes` + ADD COLUMN `maintenance` TINYINT(1) NOT NULL DEFAULT '0' AFTER `is_request`;"; + + $DB->query($query) or die("error adding maintenance to glpi_plugin_processmaker_processes table" . $DB->error()); + } + // Alter table glpi_plugin_processmaker_processes + if (!$DB->fieldExists("glpi_plugin_processmaker_processes", "max_cases_per_item" )) { + $query = "ALTER TABLE `glpi_plugin_processmaker_processes` + ADD COLUMN `max_cases_per_item` INT(11) NOT NULL DEFAULT '0' AFTER `maintenance`;"; + + $DB->query($query) or die("error adding max_cases_per_item to glpi_plugin_processmaker_processes table" . $DB->error()); + } + + return '3.4.9'; +} \ No newline at end of file diff --git a/install/update_to_3_2_8.php b/install/update_to_3_2_8.php index 47f48df..d9784b3 100644 --- a/install/update_to_3_2_8.php +++ b/install/update_to_3_2_8.php @@ -71,7 +71,7 @@ function update_to_3_2_8() { } } - if (!$DB->fieldExists('glpi_plugin_processmaker_users', 'password')) { + if (!$DB->fieldExists('glpi_plugin_processmaker_users', 'password') && !$DB->fieldExists('glpi_plugin_processmaker_users', 'id')) { $query = "ALTER TABLE `glpi_plugin_processmaker_users` ADD COLUMN `id` INT NOT NULL AUTO_INCREMENT FIRST, ADD COLUMN `password` VARCHAR(32) NULL DEFAULT NULL AFTER `pm_users_id`, diff --git a/js/cases.helpdesk.js b/js/cases.helpdesk.js index b846e59..fb1c062 100644 --- a/js/cases.helpdesk.js +++ b/js/cases.helpdesk.js @@ -21,7 +21,7 @@ function onClickContinue(obj) { // hide the iFrame caseIFrame.style.visibility = 'hidden'; - // trigger a click on the 'add' button of the ticket + // trigger a click on the 'add' button of the ticket submitButton.click(); } @@ -59,17 +59,17 @@ function onLoadFrame( evt, caseId, delIndex, caseNumber, processName ) { if (caseIFrame != undefined && contentDocument) { var buttonContinue = contentDocument.getElementById('form[btnGLPISendRequest]'); var linkList = contentDocument.getElementsByTagName('a'); - + if (!bButtonContinue && buttonContinue != undefined && linkList != undefined && linkList.length > 0) { bButtonContinue = true; //window.clearInterval(caseTimer); // to be sure that it will be done only one time // change action for the attached form and add some parameters //debugger; bGLPIHideElement(linkList, 'href', 'cases_Step?TYPE=ASSIGN_TASK&UID=-1&POSITION=10000&ACTION=ASSIGN'); - + oldHandler = buttonContinue.onclick; buttonContinue.onclick = onClickContinue; - + submitButton = $("input[name='add'][type=submit]")[0]; submitButton.insertAdjacentHTML('beforebegin', ""); submitButton.insertAdjacentHTML('beforebegin', ""); @@ -116,4 +116,4 @@ function redimTaskFrame(taskFrame) { taskFrame.height = newHeight; } catch (e) { } -} \ No newline at end of file +} diff --git a/js/cases.js b/js/cases.js index d4b93ef..841dc8a 100644 --- a/js/cases.js +++ b/js/cases.js @@ -1,4 +1,4 @@ -//debugger; +//debugger; // To manage submits to case.front.php var GLPI_HTTP_CASE_FORM = window.location.href.replace(window.location.search, ''); //window.location.href.split('/', loc_split.length - 2).join('/') + '/plugins/processmaker/front/case.front.php'; // http://hostname/glpi/... // to manage reloads diff --git a/js/central.js b/js/central.js index 593e425..fe4b7eb 100644 --- a/js/central.js +++ b/js/central.js @@ -1,19 +1,18 @@ -$(function () { +$(function () { $(document).ajaxComplete(function (event, jqXHR, ajaxOptions) { //debugger; var pattern = /##processmaker.*(##|...)/g; - + $('tr.tab_bg_2 td a').each(function (index) { - + var textToChange = $(this).text(); var matches = textToChange.match(pattern); if (matches) { textToChange = textToChange.replace(pattern, ''); - if (!textToChange.trim().length>0) - { + if (!textToChange.trim().length>0) { var title = $(this).parent().prev().text(); textToChange = title; - } + } $(this).text(textToChange); } }); diff --git a/js/domain.js.php b/js/domain.js.php deleted file mode 100644 index 3d10868..0000000 --- a/js/domain.js.php +++ /dev/null @@ -1,19 +0,0 @@ -isActivated('processmaker')) { - $config = PluginProcessmakerConfig::getInstance(); - if (isset($config->fields['domain']) && $config->fields['domain'] != '') { - echo " - //debugger; - var d = document, - g = d.createElement('script'), - s = d.getElementsByTagName('script')[0]; - g.type = 'text/javascript'; - g.text = 'try { document.domain = \'".$config->fields['domain']."\'; } catch(ev) { /*console.log(ev);*/ }'; - s.parentNode.insertBefore(g, s); - "; - } -} \ No newline at end of file diff --git a/js/helpdesk.public.js.php b/js/helpdesk.public.js.php index c93b8d4..6d2bd5d 100644 --- a/js/helpdesk.public.js.php +++ b/js/helpdesk.public.js.php @@ -14,7 +14,7 @@ $config = PluginProcessmakerConfig::getInstance(); if (!$config->fields['maintenance']) { - echo "$(function () { + echo "$(function () { // look if name='helpdeskform' is present. If yes replace the form.location var ahrefTI = '".$CFG_GLPI["root_doc"]."/plugins/processmaker/front/tracking.injector.php'; var formLink = $(\"form[name='helpdeskform']\")[0]; diff --git a/pics/small-under_maintenance.png b/pics/small-under_maintenance.png new file mode 100644 index 0000000000000000000000000000000000000000..0f4749a61541aa8dfafb363311299657e993def5 GIT binary patch literal 39576 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

-3^P!SlJb!VJ%j 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 M07*qoM6N<$f^)-*y#N3J literal 0 HcmV?d00001 diff --git a/pics/verysmall-under_maintenance.png b/pics/verysmall-under_maintenance.png new file mode 100644 index 0000000000000000000000000000000000000000..b65974958c815979985a540901564fd5bdf5772f GIT binary patch literal 13147 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 Bczgf= literal 0 HcmV?d00001 diff --git a/processmaker.xml b/processmaker.xml index 607189d..b607018 100644 --- a/processmaker.xml +++ b/processmaker.xml @@ -27,7 +27,7 @@ 9.1 - 3.4.5 + 3.4.9 9.2 diff --git a/setup.php b/setup.php index f894ae2..e51b2c8 100644 --- a/setup.php +++ b/setup.php @@ -2,7 +2,7 @@ // used for case cancellation define("CANCEL", 256); -define('PROCESSMAKER_VERSION', '3.4.5'); +define('PROCESSMAKER_VERSION', '3.4.9'); // Init the hooks of the plugins -Needed function plugin_init_processmaker() { @@ -45,8 +45,13 @@ function plugin_init_processmaker() { // Display a menu entry ? if (Session::haveRightsOr('plugin_processmaker_config', [READ, UPDATE])) { - // tools and helpdesk - $PLUGIN_HOOKS['menu_toadd']['processmaker'] = ['tools' => 'PluginProcessmakerMenu', 'helpdesk' => 'PluginProcessmakerCase']; + // tools + $PLUGIN_HOOKS['menu_toadd']['processmaker']['tools'] = 'PluginProcessmakerMenu'; + } + + if (Session::haveRightsOr('plugin_processmaker_case', [READ, UPDATE])) { + // helpdesk + $PLUGIN_HOOKS['menu_toadd']['processmaker']['helpdesk'] = 'PluginProcessmakerCase'; } Plugin::registerClass('PluginProcessmakerProcess', [ 'massiveaction_nodelete_types' => true] ); @@ -83,7 +88,7 @@ function plugin_init_processmaker() { $hooks = []; foreach ($objects as $obj) { - $hooks["PluginPdf'.$obj.'Task"] = ['PluginProcessmakerProcessmaker', 'plugin_item_get_pdfdata_processmaker']; + $hooks["PluginPdf".$obj."Task"] = ['PluginProcessmakerProcessmaker', 'plugin_item_get_pdfdata_processmaker']; } $PLUGIN_HOOKS['item_get_pdfdatas']['processmaker'] = $hooks; @@ -102,24 +107,24 @@ function plugin_init_processmaker() { $plugin = new Plugin(); if ($plugin->isActivated('processmaker') && Session::getLoginUserID() ) { - + $url = explode("/", $_SERVER['PHP_SELF']); $pageName = explode("?", array_pop($url)); switch ($pageName[0]) { case "tracking.injector.php": case "helpdesk.public.php": - $PLUGIN_HOOKS['add_javascript']['processmaker'][] = "js/helpdesk.public.js.php"; + $PLUGIN_HOOKS['add_javascript']['processmaker'] = "js/helpdesk.public.js.php"; break; case "planning.php": - $PLUGIN_HOOKS['add_javascript']['processmaker'][] = "js/planning.js"; + $PLUGIN_HOOKS['add_javascript']['processmaker'] = "js/planning.js"; break; case "central.php": - $PLUGIN_HOOKS['add_javascript']['processmaker'][] = "js/central.js"; + $PLUGIN_HOOKS['add_javascript']['processmaker'] = "js/central.js"; break; case "case.form.php": case "processmaker.helpdesk.form.php" : - $PLUGIN_HOOKS['add_javascript']['processmaker'] = ["js/domain.js.php"]; - break; + //$PLUGIN_HOOKS['add_javascript']['processmaker'] = "js/domain.js.php"; + break; } } From a39394454e5974ef2f8b07d470424e7623a6388d Mon Sep 17 00:00:00 2001 From: tomolimo Date: Tue, 18 Jun 2019 09:57:41 +0200 Subject: [PATCH 02/15] Added a test to be sure that $_FILES['form']['name'] is existing Set version to 3.4.10 --- inc/processmaker.class.php | 4 ++-- processmaker.xml | 2 +- setup.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/inc/processmaker.class.php b/inc/processmaker.class.php index 3c981ed..27618ff 100644 --- a/inc/processmaker.class.php +++ b/inc/processmaker.class.php @@ -2420,8 +2420,8 @@ public function saveForm($request) { $data = http_formdata_flat_hierarchy( $request ); // check if any files are in the $_FILES global array // and add them to the curl POST - $fileForm = $_FILES['form']['name']; - if (!empty($fileForm[array_keys($fileForm)[0]][1][array_keys($fileForm[array_keys($fileForm)[0]][1])[0]])) { + $fileForm = isset($_FILES['form']['name']) ? $_FILES['form']['name'] : null; + if (isset($fileForm) && !empty($fileForm[array_keys($fileForm)[0]][1][array_keys($fileForm[array_keys($fileForm)[0]][1])[0]])) { foreach ($_FILES['form']['name'] as $key => $file) { if (is_array($file)) { // it's a grid which contains documents diff --git a/processmaker.xml b/processmaker.xml index b607018..46902bc 100644 --- a/processmaker.xml +++ b/processmaker.xml @@ -27,7 +27,7 @@ 9.1 - 3.4.9 + 3.4.10 9.2 diff --git a/setup.php b/setup.php index e51b2c8..5790057 100644 --- a/setup.php +++ b/setup.php @@ -2,7 +2,7 @@ // used for case cancellation define("CANCEL", 256); -define('PROCESSMAKER_VERSION', '3.4.9'); +define('PROCESSMAKER_VERSION', '3.4.10'); // Init the hooks of the plugins -Needed function plugin_init_processmaker() { From 39f86c6733dca56cfc42d1104ec9e619598b83d5 Mon Sep 17 00:00:00 2001 From: tomolimo Date: Fri, 26 Jul 2019 16:28:40 +0200 Subject: [PATCH 03/15] Added possibility to set notification for tasks (and of course notification templates) Added two new events per tasks (new and update) Added possibility to send spontaneous emails Fixed various CS typos Set version to 3.4.12 fixes #77 --- ajax/dropdownProcesses.php | 4 +- front/processmaker.form.php | 2 +- inc/case.class.php | 50 ++- inc/notificationtargetcase.class.php | 120 +++++++ inc/notificationtargetprocessmaker.class.php | 168 ++++++++++ inc/notificationtargettask.class.php | 320 +++++++++++++++++++ inc/process.class.php | 7 +- inc/processmaker.class.php | 92 ++++-- inc/taskcategory.class.php | 121 ++++++- install/update.php | 4 +- processmaker.xml | 2 +- setup.php | 8 +- 12 files changed, 841 insertions(+), 57 deletions(-) create mode 100644 inc/notificationtargetcase.class.php create mode 100644 inc/notificationtargetprocessmaker.class.php create mode 100644 inc/notificationtargettask.class.php diff --git a/ajax/dropdownProcesses.php b/ajax/dropdownProcesses.php index 1fa8719..ba543e3 100644 --- a/ajax/dropdownProcesses.php +++ b/ajax/dropdownProcesses.php @@ -69,9 +69,9 @@ while ($data = $DB->fetch_array($result)) { $process_entities = PluginProcessmakerProcess::getEntitiesForProfileByProcess($data["id"], $_SESSION['glpiactiveprofile']['id'], true); $can_add = $data['max_cases_per_item'] == 0 || !isset($count_cases_per_item[$data["id"]]) || $count_cases_per_item[$data["id"]] < $data['max_cases_per_item']; - if ($processall + if ($processall || ($data['maintenance'] != 1 - && in_array( $_REQUEST["entity_restrict"], $process_entities) + && in_array( $_REQUEST["entity_restrict"], $process_entities) && $can_add) ) { array_push( $processes, ['id' => $data["id"], diff --git a/front/processmaker.form.php b/front/processmaker.form.php index 5345afa..aaa4410 100644 --- a/front/processmaker.form.php +++ b/front/processmaker.form.php @@ -14,7 +14,7 @@ $case = new PluginProcessmakerCase; if ($case->getFromGUID($resultCase->caseId)) { $link = $case->getLinkURL(); - $task = new PluginProcessmakerTask(); + $task = new PluginProcessmakerTask($_POST['itemtype'].'Task'); $task->getFromDBByQuery(" WHERE `plugin_processmaker_cases_id`=".$case->getID()); // normally there is only one and only one first task //$link .= '&forcetab=PluginProcessmakerTask$'.$task->getID(); Session::setActiveTab('PluginProcessmakerCase', 'PluginProcessmakerTask$'.$task->getID()); diff --git a/inc/case.class.php b/inc/case.class.php index 1b03635..5c86a01 100644 --- a/inc/case.class.php +++ b/inc/case.class.php @@ -256,22 +256,52 @@ function reassignCase($delIndex, $taskGuid, $delThread, $users_id_source, $users public function reassignTask ($delIndex, $newDelIndex, $delThread, $newDelThread, $newTech) { global $DB; - $query = "SELECT * FROM glpi_plugin_processmaker_tasks WHERE plugin_processmaker_cases_id={$this->getID()} AND del_index=$delIndex AND del_thread=$delThread; "; - $res = $DB->query($query); - if ($DB->numrows($res) > 0) { - $row = $DB->fetch_array( $res ); - $glpi_task = new $row['itemtype']; - $glpi_task->getFromDB( $row['items_id'] ); - - $itilobject_itemtype = $this->fields['itemtype']; //str_replace( 'Task', '', $row['itemtype'] ); + $dbu = new DbUtils; + $pm_task_row = $dbu->getAllDataFromTable(PluginProcessmakerTask::getTable(), ['plugin_processmaker_cases_id' => $this->getID(), 'del_index' => $delIndex, 'del_thread' => $delThread]); + if ($pm_task_row && count($pm_task_row) == 1) { + $pm_task_row = array_shift($pm_task_row); + $glpi_task = new $pm_task_row['itemtype']; + $glpi_task->getFromDB( $pm_task_row['items_id'] ); + + $itilobject_itemtype = $this->fields['itemtype']; $foreignkey = getForeignKeyFieldForItemType( $itilobject_itemtype ); PluginProcessmakerProcessmaker::addWatcher( $itilobject_itemtype, $glpi_task->fields[ $foreignkey ], $newTech ); - $glpi_task->update( [ 'id' => $row['items_id'], $foreignkey => $glpi_task->fields[ $foreignkey ], 'users_id_tech' => $newTech ]); + $donotif = PluginProcessmakerNotificationTargetProcessmaker::saveNotificationState(false); // do not send notification yet + $glpi_task->update( ['id' => $glpi_task->getID(), $foreignkey => $glpi_task->fields[$foreignkey], 'users_id_tech' => $newTech, 'update' => true] ); + PluginProcessmakerNotificationTargetProcessmaker::restoreNotificationState($donotif); + + // Notification management + // search if at least one active notification is existing for that pm task with that event 'task_update_'.$glpi_task->fields['taskcategories_id'] + $res = PluginProcessmakerNotificationTargetTask::getNotifications('task_update', $glpi_task->fields['taskcategories_id'], $this->fields['entities_id']); + if ($res['notifications'] && count($res['notifications']) > 0) { + $pm_task = new PluginProcessmakerTask($pm_task_row['itemtype']); + $pm_task->getFromDB($pm_task_row['items_id']); + NotificationEvent::raiseEvent($res['event'], + $pm_task, + ['plugin_processmaker_cases_id' => $this->getID(), + 'itemtype' => $pm_task_row['itemtype'], + 'task_id' => $glpi_task->getID(), + 'old_users_id_tech' => $glpi_task->oldvalues['users_id_tech'], + 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0, + 'entities_id' => $this->fields['entities_id'], + 'case' => $this + ]); + } else { + $item = new $itilobject_itemtype; + $item->getFromDB($glpi_task->fields[$foreignkey]); + NotificationEvent::raiseEvent('update_task', + $item, + ['plugin_processmaker_cases_id' => $this->getID(), + 'itemtype' => $pm_task_row['itemtype'], + 'task_id' => $glpi_task->getID(), + 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0 + ]); + } // then update the delIndex and delThread - $query = "UPDATE glpi_plugin_processmaker_tasks SET del_index = $newDelIndex, del_thread = $newDelThread WHERE id={$row['id']}; "; + $query = "UPDATE glpi_plugin_processmaker_tasks SET del_index = $newDelIndex, del_thread = $newDelThread WHERE id={$pm_task_row['id']}; "; $res = $DB->query($query); } } diff --git a/inc/notificationtargetcase.class.php b/inc/notificationtargetcase.class.php new file mode 100644 index 0000000..7e74e32 --- /dev/null +++ b/inc/notificationtargetcase.class.php @@ -0,0 +1,120 @@ + __('Send email', 'processmaker')]; + } + + + /** + * Summary of addAdditionalTargets + * @param mixed $event + */ + function addAdditionalTargets($event = '') { + $this->notification_targets = []; + $this->notification_targets_labels = []; + $this->addTarget(self::RECIPIENTS, __('eMail recipients', 'processmaker'), self::EMAIL_RECIPIENTS); + } + + + /** + * Summary of addSpecificTargets + * @param mixed $data + * @param mixed $options + */ + function addSpecificTargets($data, $options) { + + // test if we are in the good notification + // then in this case add the targets from the ['recipients'] + if (isset($options['glpi_send_email'])) { + // normalize $options['glpi_send_email'] to an array of email parameters + $options['glpi_send_email'] = isset($options['glpi_send_email']['notifications_id']) ? [$options['glpi_send_email']] : $options['glpi_send_email']; + + foreach($options['glpi_send_email'] as $params) { + if (isset($params['notifications_id']) + && $params['notifications_id'] == $data['notifications_id']) { + //Look for all targets whose type is Notification::ITEM_USER + switch ($data['type']) { + case self::EMAIL_RECIPIENTS: + + switch ($data['items_id']) { + + case self::RECIPIENTS : + $this->addUsers($params); + break; + } + } + } + } + } + + // if no target is added to $this, then the notification will not be sent. + + } + + + /** + * Add users from $options['glpi_send_email']['to'] + * + * @param array $email_param should contain 'recipients' + * + * @return void + */ + function addUsers($email_param = []) { + global $DB, $CFG_GLPI; + + if (isset($email_param['recipients'])) { + $id_list = []; // for users with ids + $email_list = []; // for standalone emails + + // normalize into array the recipient list + $email_param['recipients'] = is_array($email_param['recipients']) ? $email_param['recipients'] : [$email_param['recipients']]; + foreach ($email_param['recipients'] as $user) { + if (is_numeric($user)) { + $id_list[] = intval($user); + } else { + $email_list[] = $user; + } + } + + $query = $this->getDistinctUserSql()." + FROM `glpi_users` ". + $this->getProfileJoinSql()." + WHERE `glpi_users`.`id` IN (".implode(',', $id_list).")"; + + foreach ($DB->request($query) as $data) { + $this->addToRecipientsList($data); + } + + foreach($email_list as $email){ + $this->addToRecipientsList([ + 'email' => $email, + 'language' => $CFG_GLPI["language"], + 'users_id' => -1 + ]); + } + } + } + +} diff --git a/inc/notificationtargetprocessmaker.class.php b/inc/notificationtargetprocessmaker.class.php new file mode 100644 index 0000000..8179c1a --- /dev/null +++ b/inc/notificationtargetprocessmaker.class.php @@ -0,0 +1,168 @@ + __('Process category', 'processmaker'), + 'process.categoryid' => __('Process category id', 'processmaker'), + 'process.categorycomment' => __('Process category comment', 'processmaker'), + 'case.id' => __('Case id', 'processmaker'), + 'case.title' => __('Case title', 'processmaker'), + 'case.description' => __('Case description', 'processmaker'), + 'case.url' => __('URL'), + 'var.XXX' => __('Case variable \'XXX\'', 'processmaker'), + 'array.YYY' => __('List of values in \'YYY\' array', 'processmaker'), + 'array.numberofYYY' => __('Number of rows in \'YYY\' array', 'processmaker'), + 'array.YYY.colname' => __('Value for colname in case array \'YYY\'', 'processamker') + ]; + + foreach ($tags as $tag => $label) { + $elt= ['tag' => $tag, + 'label' => $label, + 'value' => true]; + if ($tag == 'var.XXX') { + $elt['allowed_values'] = [__('XXX is to be replaced by any case variable names', 'processmaker')]; + } + if ($tag == 'array.YYY') { + $elt['allowed_values'] = [__('YYY is to be replaced by any array variables', 'processmaker')]; + $elt['foreach'] = true; + } + $this->addTagToList($elt); + } + + asort($this->tag_descriptions); + } + + + /** + * Get all data needed for template processing + **/ + public function addDataForTemplate($event, $options = []) { + global $PM_DB, $CFG_GLPI; + + $excluded = ['_VAR_CHANGED_', + 'PIN', + 'APPLICATION', + 'PROCESS', + 'TASK', + 'INDEX', + 'USER_LOGGED', + 'USR_USERNAME', + 'APP_NUMBER', + 'GLPI_.*', + 'SYS_.*' + ]; + + $process = new PluginProcessmakerProcess; + + $process->getFromDB($options['case']->fields['plugin_processmaker_processes_id']); + $taskcat_id = $process->fields['taskcategories_id']; + + // set defaults to all + foreach ($this->tags as $key => $val) { + $this->data["##$key##"] = "-"; + } + + // get case variable values + $res = $PM_DB->query("SELECT APP_DATA, APP_TITLE, APP_DESCRIPTION FROM APPLICATION WHERE APP_NUMBER = ".$options['case']->fields['id']); + if ($res && $PM_DB->numrows($res) == 1) { + // get all the case variables from $PM_DB + $caserow = $PM_DB->fetch_assoc($res); + $case_variables = unserialize($caserow['APP_DATA']); + $excluded_re = '/^(' . implode('|', $excluded) . ')$/u'; + foreach ($case_variables as $key => $val) { + if (!preg_match($excluded_re, $key)) { + if (is_array($val)) { + // add numberof for count of rows + $this->data["##array.numberof$key##"] = count($val); + // get the keys/vals of the sub-array + foreach ($val as $row) { + foreach ($row as $col_name => $col_val) { + $this->data["array.$key"][]["##array.$key.$col_name##"] = $col_val; + $this->data["##lang.array.$key.$col_name##"] = $col_name; + } + } + } else { + $this->data["##var.$key##"] = $val; + $this->data["##lang.var.$key##"] = $key; + } + } + } + $this->data['##case.title##'] = $caserow['APP_TITLE']; + $this->data['##case.description##'] = $caserow['APP_DESCRIPTION']; + } + + // case id + $this->data['##case.id##'] = $options['case']->fields['id']; + + // case URL + $this->data['##case.url##'] = $CFG_GLPI["url_base"]."/index.php?redirect=".urlencode("/plugins/processmaker/front/case.form.php?id=".$options['case']->fields['id']); + + // parent task information: meta data on process + // will get parent of task which is the process task category + $tmp_taskcatinfo['name'] = DropdownTranslation::getTranslatedValue( $taskcat_id, 'TaskCategory', 'name'); + $tmp_taskcatinfo['comment'] = DropdownTranslation::getTranslatedValue( $taskcat_id, 'TaskCategory', 'comment'); + // process title + $this->data['##process.categoryid##'] = $taskcat_id; + $this->data['##process.category##'] = $tmp_taskcatinfo['name']; + $this->data['##process.categorycomment##'] = $tmp_taskcatinfo['comment']; + + // add labels + $this->getTags(); + foreach ($this->tag_descriptions[NotificationTarget::TAG_LANGUAGE] as $tag => $values) { + if (!isset($this->data[$tag])) { + $this->data[$tag] = $values['label']; + } + } + } + +} \ No newline at end of file diff --git a/inc/notificationtargettask.class.php b/inc/notificationtargettask.class.php new file mode 100644 index 0000000..948be21 --- /dev/null +++ b/inc/notificationtargettask.class.php @@ -0,0 +1,320 @@ + ['event' => 'task_add_', 'label' => __('New task')], + 'task_update' => ['event' => 'task_update_', 'label' => __('Update of a task')] + ]; + } + + + /** + * Summary of getNotification + * @param mixed $evt + * @param mixed $taskcat + * @param mixed $entity + * @return array + */ + static function getNotifications($evt, $taskcat, $entity) { + // search if at least one active notification is existing for that pm task with that event 'task_update_'.$glpi_task->fields['taskcategories_id'] + $defaultEvents = self::getDefaultEvents(); + $event = $defaultEvents[$evt]['event'].$taskcat; + $dbu = new DbUtils; + $crit = $dbu->getEntitiesRestrictCriteria(Notification::getTable(), 'entities_id', $entity, true); + return ['event' => $event, 'notifications' => $dbu->getAllDataFromTable(Notification::getTable(), ['itemtype' => 'PluginProcessmakerTask', 'event' => $event, 'is_active' => 1, $crit])]; + } + + + /** + * Summary of getEvents + * @return string[] + */ + public function getEvents() { + global $DB; + $actions = []; + + $defaultEvents = self::getDefaultEvents(); + + $table = PluginProcessmakerTaskCategory::getTable(); + $ptable = PluginProcessmakerProcess::getTable(); + $query = "SELECT $table.taskcategories_id AS taskcat, $ptable.taskcategories_id AS ptaskcat FROM $table + LEFT JOIN $ptable ON $ptable.id=$table.plugin_processmaker_processes_id + WHERE $table.is_active = 1 + AND $ptable.is_active = 1"; + + $ptaskcats = []; + $temp = new TaskCategory; + foreach ($DB->request($query) as $row) { + if (!isset($ptaskcats[$row['ptaskcat']])) { + $temp->getFromDB($row['ptaskcat']); + $ptaskcats[$row['ptaskcat']] = $temp->fields['name']; + } + $temp->getFromDB($row['taskcat']); + + $actions[$defaultEvents['task_add']['event'].$row['taskcat']] = $ptaskcats[$row['ptaskcat']]." > ".$temp->fields['name'].": " . $defaultEvents['task_add']['label']; + $actions[$defaultEvents['task_update']['event'].$row['taskcat']] = $ptaskcats[$row['ptaskcat']]." > ".$temp->fields['name'].": " . $defaultEvents['task_update']['label']; + } + + return $actions; + } + + + /** + * Get all data needed for template processing + **/ + public function addDataForTemplate($event, $options = []) { + global $PM_DB, $CFG_GLPI; + + if (!isset($options['case'])) { + $mycase = new PluginProcessmakerCase; + $mycase->getFromDB($options['plugin_processmaker_cases_id']); + $options['case'] = $mycase; + } + parent::addDataForTemplate($event, $options); + + $events = self::getDefaultEvents(); + $locevent = explode('_', $event); + $baseevent = $locevent[0].'_'.$locevent[1]; + $taskcat_id = $locevent[2]; + + // task action: add or update + $this->data['##task.action##'] = $events[$baseevent]['label']; + + // task category information: meta data on task + $tmp_taskcatinfo['name'] = DropdownTranslation::getTranslatedValue( $taskcat_id, 'TaskCategory', 'name'); + $tmp_taskcatinfo['comment'] = DropdownTranslation::getTranslatedValue( $taskcat_id, 'TaskCategory', 'comment'); + $this->data['##task.categoryid##'] = $taskcat_id; + $this->data['##task.category##'] = $tmp_taskcatinfo['name']; + $this->data['##task.categorycomment##'] = $tmp_taskcatinfo['comment']; + + // task information + $taskobj = $this->obj; + + // is private? + $this->data['##task.isprivate##'] = Dropdown::getYesNo(false); + if ($taskobj->maybePrivate()) { + $this->data['##task.isprivate##'] = Dropdown::getYesNo($taskobj->fields['is_private']); + } + // status + $this->data['##task.status##'] = Planning::getState($taskobj->fields['state']); + // creation date + $this->data['##task.date##'] = Html::convDateTime($taskobj->fields['date_creation']); + // update date + $this->data['##task.update##'] = Html::convDateTime($taskobj->fields['date_mod']); + // content: don't know if this could be interesting + $this->data['##task.description##'] = $taskobj->fields['content']; + + // task creator + // should always be Process Maker user + $dbu = new DbUtils(); + $this->data['##task.author##'] = Html::clean($dbu->getUserName($taskobj->fields['users_id'])); + + // task editor + $this->data['##task.lastupdater##'] = Html::clean($dbu->getUserName($taskobj->fields['users_id_editor'])); + + // task technician + $this->data['##task.user##'] = Html::clean($dbu->getUserName($taskobj->fields['users_id_tech'])); + + // task group technician + $this->data['##task.group##'] = Html::clean(Toolbox::clean_cross_side_scripting_deep(Dropdown::getDropdownName("glpi_groups", $taskobj->fields['groups_id_tech'])), true, 2, false); + + // task planning + $this->data['##task.begin##'] = ''; + $this->data['##task.end##'] = ''; + if (!is_null($taskobj->fields['begin'])) { + $this->data['##task.begin##'] = Html::convDateTime($taskobj->fields['begin']); + $this->data['##task.end##'] = Html::convDateTime($taskobj->fields['end']); + } + // task duration + $this->data['##task.time##'] = Html::timestampToString($taskobj->fields['actiontime'], false); + + // add labels + $this->getTags(); + foreach ($this->tag_descriptions[NotificationTarget::TAG_LANGUAGE] as $tag => $values) { + if (!isset($this->data[$tag])) { + $this->data[$tag] = $values['label']; + } + } + } + + + /** + * Summary of getTags + */ + public function getTags() { + + parent::getTags(); + + $tags = ['task.action' => __('Task action', 'processmaker'), + 'task.author' => __('Writer'), + 'task.isprivate' => __('Private'), + 'task.date' => __('Opening date'), + 'task.description' => __('Description'), + 'task.categoryid' => __('Category id'), + 'task.category' => __('Category'), + 'task.categorycomment' => __('Category comment'), + 'task.time' => __('Total duration'), + 'task.user' => __('User assigned to task'), + 'task.group' => __('Group assigned to task'), + 'task.begin' => __('Start date'), + 'task.end' => __('End date'), + 'task.status' => __('Status'), + 'task.lastupdater' => __('Last updater'), + 'task.update' => __('Last update') + ]; + foreach ($tags as $tag => $label) { + $elt= ['tag' => $tag, + 'label' => $label, + 'value' => true]; + + $this->addTagToList($elt); + } + asort($this->tag_descriptions); + } + + + /** + * Summary of addAdditionalTargets + * @param mixed $event + */ + function addAdditionalTargets($event = '') { + + $this->addTarget(Notification::TASK_ASSIGN_TECH, __('Technician in charge of the task')); + $this->addTarget(Notification::TASK_ASSIGN_GROUP, __('Group in charge of the task')); + + if (strpos($event, 'task_update_') === 0) { + $this->addTarget(Notification::OLD_TECH_IN_CHARGE, + __('Former technician in charge of the task')); + } + + } + + + /** + * Summary of addSpecificTargets + * @param mixed $data + * @param mixed $options + */ + function addSpecificTargets($data, $options) { + + //Look for all targets whose type is Notification::ITEM_USER + switch ($data['type']) { + case Notification::USER_TYPE : + + switch ($data['items_id']) { + + //Send to the ITIL object followup author + case Notification::TASK_ASSIGN_TECH : + $this->addTaskAssignUser($options); + break; + + //Send to the ITIL object task group assigned + case Notification::TASK_ASSIGN_GROUP : + $this->addTaskAssignGroup($options); + break; + + //Send to the technician previously in charge of the task (before re-assignment) + case Notification::OLD_TECH_IN_CHARGE : + $this->addOldAssignTechnician($options); + break; + + } + } + } + + + /** + * Add user assigned to task + * + * @param array $options Options + * + * @return void + */ + function addTaskAssignUser($options = []) { + global $DB; + + // In case of delete task pass user id + if (isset($options['task_users_id_tech'])) { + $query = $this->getDistinctUserSql()." + FROM `glpi_users` ". + $this->getProfileJoinSql()." + WHERE `glpi_users`.`id` = '".$options['task_users_id_tech']."'"; + + foreach ($DB->request($query) as $data) { + $this->addToRecipientsList($data); + } + } else if (isset($options['task_id'])) { + $dbu = new DbUtils; + $tasktable = $dbu->getTableForItemType($options['itemtype']); //getTableForItemType($this->obj->getType().'Task'); + + $query = $this->getDistinctUserSql()." + FROM `$tasktable` + INNER JOIN `glpi_users` + ON (`glpi_users`.`id` = `$tasktable`.`users_id_tech`)". + $this->getProfileJoinSql()." + WHERE `$tasktable`.`id` = '".$options['task_id']."'"; + + foreach ($DB->request($query) as $data) { + $this->addToRecipientsList($data); + } + } + } + + + /** + * Add group assigned to the task + * + * @param array $options Options + * + * @return void + */ + function addTaskAssignGroup($options = []) { + global $DB; + + // In case of delete task pass user id + if (isset($options['task_groups_id_tech'])) { + $this->addForGroup(0, $options['task_groups_id_tech']); + + } else if (isset($options['task_id'])) { + $dbu = new DbUtils; + $tasktable = $dbu->getTableForItemType($options['itemtype']); //getTableForItemType($this->obj->getType().'Task'); + foreach ($DB->request([$tasktable, 'glpi_groups'], "`glpi_groups`.`id` = `$tasktable`.`groups_id_tech` + AND `$tasktable`.`id` = '".$options['task_id']."'") as $data) { + $this->addForGroup(0, $data['groups_id_tech']); + } + } + } + + + function addOldAssignTechnician($options = []) { + global $DB; + + // In case of delete task pass user id + if (isset($options['old_users_id_tech'])) { + $query = $this->getDistinctUserSql()." + FROM `glpi_users` ". + $this->getProfileJoinSql()." + WHERE `glpi_users`.`id` = '".$options['old_users_id_tech']."'"; + + foreach ($DB->request($query) as $data) { + $this->addToRecipientsList($data); + } + } + } + +} diff --git a/inc/process.class.php b/inc/process.class.php index 30bc049..af4defa 100644 --- a/inc/process.class.php +++ b/inc/process.class.php @@ -752,7 +752,12 @@ static function getSqlSearchResult ($count = true, $search = '') { } if (strlen($search)>0 && $search!=$CFG_GLPI["ajax_wildcard"]) { - $where .= " AND (glpi_plugin_processmaker_processes.name $search + if ($where == '') { + $where = ' WHERE '; + } else { + $where .= ' AND '; + } + $where .= " (glpi_plugin_processmaker_processes.name $search OR glpi_plugin_processmaker_processes.comment $search) "; } diff --git a/inc/processmaker.class.php b/inc/processmaker.class.php index 27618ff..7d510dc 100644 --- a/inc/processmaker.class.php +++ b/inc/processmaker.class.php @@ -114,8 +114,8 @@ static function getTable($classname = null) { static function getAllPMErrorArray() { $tab = [self::ERROR_CREATING_CASE => _x('errors', 'Error creating case!', 'processmaker'), - self::ERROR_NO_RIGHTS => _x('errors', 'Can\'t create case: no rights for it!', 'processmaker'), - self::ERROR_CREATING_CASE2 => _x('errors', 'Error creating case!', 'processmaker')]; + self::ERROR_NO_RIGHTS => _x('errors', 'Can\'t create case: no rights for it!', 'processmaker'), + self::ERROR_CREATING_CASE2 => _x('errors', 'Error creating case!', 'processmaker')]; return $tab; } @@ -1111,12 +1111,12 @@ static function cronPMTaskActions($crontask = null) { } // do not send notifications - $donotif = self::saveNotification(false); + $donotif = PluginProcessmakerNotificationTargetProcessmaker::saveNotificationState(false); // now manage tasks associated with item $PM_SOAP->claimTask( $postdata['APP_UID'], $postdata['DEL_INDEX'], $users_id ); - self::restoreNotification($donotif); + PluginProcessmakerNotificationTargetProcessmaker::restoreNotificationState($donotif); } @@ -1594,9 +1594,9 @@ public static function addWatcher($itemType, $itemId, $techId) { $glpi_item_user = $dbu->getItemForItemtype( $glpi_item->getType() . "_User" ); // do not send notifications - $donotif = self::saveNotification(false); + $donotif = PluginProcessmakerNotificationTargetProcessmaker::saveNotificationState(false); $glpi_item_user->add( [ $glpi_item::getForeignKeyField() => $glpi_item->getId(), 'users_id' => $techId, 'type' => CommonITILActor::OBSERVER, '_disablenotif' => true ] ); - self::restoreNotification($donotif); + PluginProcessmakerNotificationTargetProcessmaker::restoreNotificationState($donotif); return true; } } @@ -1745,9 +1745,9 @@ public function addTask($cases_id, $itemtype, $items_id, $caseInfo, $delIndex, $ 'field' => 'begin']; } - $donotif = self::saveNotification(false); // do not send notification yet as the PluginProcessmakerTask is not yet added to DB + $donotif = PluginProcessmakerNotificationTargetProcessmaker::saveNotificationState(false); // do not send notification yet as the PluginProcessmakerTask is not yet added to DB $glpi_task->add( Toolbox::addslashes_deep( $input ) ); - self::restoreNotification($donotif); + PluginProcessmakerNotificationTargetProcessmaker::restoreNotificationState($donotif); // to prevent error message for overlapping planning if (isset($_SESSION["MESSAGE_AFTER_REDIRECT"][WARNING])) { @@ -1768,32 +1768,38 @@ public function addTask($cases_id, $itemtype, $items_id, $caseInfo, $delIndex, $ } // send notification if needed for new task as now we have the PluginProcessmakerTask in the DB - $donotif = self::saveNotification($options['notif']); + $donotif = PluginProcessmakerNotificationTargetProcessmaker::saveNotificationState($options['notif']); + // Notification management $item = new $itemtype; $item->getFromDB($items_id); - NotificationEvent::raiseEvent('add_task', $item, ['task_id' => $glpi_task->getID(), 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0]); - self::restoreNotification($donotif); - - } - - - private static function saveNotification($donotif) { - global $CFG_GLPI; - // $CFG_GLPI["use_notifications"] is available since 9.2 - $savenotif = isset($CFG_GLPI["use_notifications"]) ? $CFG_GLPI["use_notifications"] : $CFG_GLPI["use_mailing"]; - if (!$donotif) { - isset($CFG_GLPI["use_notifications"]) ? $CFG_GLPI["use_notifications"] = false : $CFG_GLPI["use_mailing"] = false; + // search if at least one active notification is existing for that pm task with that event 'task_update_'.$glpi_task->fields['taskcategories_id'] + $res = PluginProcessmakerNotificationTargetTask::getNotifications('task_add', $glpi_task->fields['taskcategories_id'], $item->fields['entities_id']); + if ($res['notifications'] && count($res['notifications']) > 0) { + $pm_task = new PluginProcessmakerTask($glpi_task->getType()); + $pm_task->getFromDB($glpi_task->getId()); + NotificationEvent::raiseEvent($res['event'], + $pm_task, + ['plugin_processmaker_cases_id' => $cases_id, + 'itemtype' => $glpi_task->getType(), + 'task_id' => $glpi_task->getID(), + 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0, + 'entities_id' => $item->fields['entities_id'] + ]); + } else { + NotificationEvent::raiseEvent('add_task', + $item, + ['plugin_processmaker_cases_id' => $cases_id, + 'itemtype' => $itemtype, + 'task_id' => $glpi_task->getID(), + 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0 + ]); } - return $savenotif; - } + PluginProcessmakerNotificationTargetProcessmaker::restoreNotificationState($donotif); - private static function restoreNotification($savenotif) { - global $CFG_GLPI; - // $CFG_GLPI["use_notifications"] is available since 9.2 - isset($CFG_GLPI["use_notifications"]) ? $CFG_GLPI["use_notifications"] = $savenotif : $CFG_GLPI["use_mailing"] = $savenotif; } + /** * Summary of add1stTask * adds a GLPI task to ticket @@ -2035,9 +2041,9 @@ public function solveTask($cases_id, $delIndex, $options = []) { //'groups_id_tech' => 0, 'content' => $DB->escape($glpi_task->fields[ 'content' ].$options['txtToAppend']) ]; - $donotif = self::saveNotification($options['notif']); + $donotif = PluginProcessmakerNotificationTargetProcessmaker::saveNotificationState($options['notif']); $glpi_task->update($params); - self::restoreNotification($donotif); + PluginProcessmakerNotificationTargetProcessmaker::restoreNotificationState($donotif); // Close the task $DB->query("UPDATE glpi_plugin_processmaker_tasks SET del_thread_status = '".PluginProcessmakerTask::CLOSED."' WHERE id = {$row['id']}"); @@ -2072,10 +2078,11 @@ public function claimTask($cases_id, $delIndex, $users_id_tech = null) { $glpi_item = $dbu->getItemForItemtype( $itemType ); $glpi_item->getFromDB( $glpi_task->fields[ getForeignKeyFieldForItemType( $itemType ) ] ); - $glpi_task->update( [ 'id' => $row['items_id'], - $glpi_item->getForeignKeyField() => $glpi_item->getId(), - 'users_id_tech' => (isset($users_id_tech)?$users_id_tech: Session::getLoginUserID()), - 'groups_id_tech' => 0 ]); + $glpi_task->update( [ 'id' => $row['items_id'], + $glpi_item->getForeignKeyField() => $glpi_item->getId(), + 'users_id_tech' => (isset($users_id_tech)?$users_id_tech: Session::getLoginUserID()), + 'groups_id_tech' => 0, + 'update' => true]); } } @@ -2758,12 +2765,18 @@ public function derivateCase($myCase, $request, $users_id = null) { "GLPI_ITEM_SET_SOLUTION_TYPE_ID", "GLPI_ITEM_APPEND_TO_SOLUTION_DESCRIPTION", "GLPI_ITEM_INITIAL_DUE_DATE", - "GLPI_ITEM_DUE_DATE" + "GLPI_ITEM_DUE_DATE", + "GLPI_SEND_EMAIL" ]; // now tries to get some variables to setup content for new task and to append text to solved task $casevariablevalues = $myCase->getVariables($casevariables); + $sendemail = ''; + if (array_key_exists( 'GLPI_SEND_EMAIL', $casevariablevalues ) && $casevariablevalues[ 'GLPI_SEND_EMAIL' ] != '') { + $sendemail = json_decode($casevariablevalues[ 'GLPI_SEND_EMAIL' ], true); + } + $itemSetStatus = ''; if (array_key_exists( 'GLPI_ITEM_SET_STATUS', $casevariablevalues )) { $itemSetStatus = $casevariablevalues[ 'GLPI_ITEM_SET_STATUS' ]; @@ -3038,7 +3051,7 @@ public function derivateCase($myCase, $request, $users_id = null) { foreach ($parentCaseInfo->currentUsers as $open_task) { // must check if $open_task is not is_subprocess and is not already existing in the item $locTaskCat = new PluginProcessmakerTaskCategory; - $locTask = new PluginProcessmakerTask(); + $locTask = new PluginProcessmakerTask($itemtype.'Task'); if ($locTaskCat->getFromGUID($open_task->taskId) && !$locTaskCat->fields['is_subprocess'] && !$locTask->getFromDBByQuery("WHERE `plugin_processmaker_cases_id`={$parentCase->getID()} AND `plugin_processmaker_taskcategories_id`={$locTaskCat->getID()} AND `del_index`={$open_task->delIndex}")) { @@ -3071,6 +3084,15 @@ public function derivateCase($myCase, $request, $users_id = null) { // evolution of case status: DRAFT, TO_DO, COMPLETED, CANCELLED $myCase->update( [ 'id' => $myCase->getID(), 'case_status' => $caseInfo->caseStatus, 'name' => $caseInfo->caseName ] ); + // send email if requested + if (is_array($sendemail)) { + NotificationEvent::raiseEvent('send_email', + $myCase, + ['glpi_send_email' => $sendemail, + 'case' => $myCase + ]); + } + } diff --git a/inc/taskcategory.class.php b/inc/taskcategory.class.php index c73da85..46e773d 100644 --- a/inc/taskcategory.class.php +++ b/inc/taskcategory.class.php @@ -18,11 +18,19 @@ class PluginProcessmakerTaskCategory extends CommonDBTM { function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) { - return __('Task List', 'processmaker'); + if ($item->getType() == 'TaskCategory') { + $pmtaskcat = new PluginProcessmakerTaskCategory; + if ($pmtaskcat->getFromDBbyCategory($item->fields['id'])) { + return __('Process task', 'processmaker'); + } else { + return ''; // means no tab + } + } + return __('Task list', 'processmaker'); } - static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) { + static function displayTabContentForProcess(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) { global $DB, $CFG_GLPI; self::title($item); @@ -86,6 +94,114 @@ static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtem return true; } + + /** + * Summary of displayTabContentForTaskCategory + * @param CommonGLPI $item + * @param mixed $tabnum + * @param mixed $withtemplate + * @return boolean + */ + static function displayTabContentForTaskCategory(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) { + global $DB, $CFG_GLPI; + + $is_taskcat = false; + $processes_id = 0; + $pmtaskcat = new PluginProcessmakerTaskCategory; + $is_taskcat = $pmtaskcat->getFromDBbyCategory($item->fields['id']); + $processes_id = $pmtaskcat->fields['plugin_processmaker_processes_id']; + + echo "

"; + + echo ""; + echo ""; + echo ""; + + echo "" . + "" . + "" . + "" . + "" . + "" . + ""; + + $query = "SELECT pm.pm_task_guid, pm.taskcategories_id, pm.`is_start`, glp.name as 'pname', gl.name, gl.completename, gl.`comment`, pm.is_active, pm.is_subprocess FROM glpi_plugin_processmaker_taskcategories AS pm + LEFT JOIN glpi_taskcategories AS gl ON pm.taskcategories_id=gl.id + LEFT JOIN glpi_taskcategories AS glp ON glp.id=gl.taskcategories_id + WHERE pm.taskcategories_id=".$item->getID().";"; + + foreach ($DB->request($query) as $taskCat) { + echo ""; + + echo ""; + echo ""; + + echo ""; + + echo ""; + + echo ""; + + echo ""; + + echo ""; + + echo ""; + + echo ""; + } + echo "
".__('Process task', 'processmaker')."
".__('Process name', 'processmaker')."".__('Task name', 'processmaker')."".__('Complete name')."".__('Start', 'processmaker')."".__('Task GUID', 'processmaker')."".__('Comments')."".__('Active')."".__('Sub-process', 'processmaker')."
" . $taskCat['pname']; + if ($_SESSION["glpiis_ids_visible"]) { + echo " (" . $processes_id . ")"; + } + echo ""; + echo $taskCat['name']; + + if ($_SESSION["glpiis_ids_visible"]) { + echo " (" . $taskCat['taskcategories_id'] . ")"; + } + echo "" . $taskCat['completename'] . ""; + if ($taskCat['is_start']) { + echo "\""."; + } + echo "".$taskCat['pm_task_guid']."".$taskCat['comment'].""; + if ($taskCat['is_active']) { + echo "\""."; + } + echo ""; + if ($taskCat['is_subprocess']) { + echo "\""."; + } + echo "
"; + + return true; + } + + + /** + * Summary of displayTabContentForItem + * @param CommonGLPI $item + * @param mixed $tabnum + * @param mixed $withtemplate + * @return boolean + */ + static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) { + $ret = false; + switch ($item->getType()) { + case 'PluginProcessmakerProcess': + $ret = self::displayTabContentForProcess($item, $tabnum, $withtemplate); + break; + case 'TaskCategory': + $ret = self::displayTabContentForTaskCategory($item, $tabnum, $withtemplate); + break; + } + return $ret; + } + /** * Print a good title for task categories tab * add button for re-synchro of taskcategory list (only if rigths are w) @@ -103,7 +219,6 @@ static function title(CommonGLPI $item) { } Html::displayTitle($pic, $title, "", $buttons); } - } //function getLinkItemFromExternalID($extId) { diff --git a/install/update.php b/install/update.php index a68e8df..febe0e5 100644 --- a/install/update.php +++ b/install/update.php @@ -11,7 +11,9 @@ function processmaker_update() { include_once(GLPI_ROOT."/plugins/processmaker/inc/config.class.php"); $config = PluginProcessmakerConfig::getInstance(); $current_version = $config->fields['db_version']; - if (empty($current_version)) $current_version = '2.4.1'; + if (empty($current_version)) { + $current_version = '2.4.1'; + } } switch ($current_version) { diff --git a/processmaker.xml b/processmaker.xml index 46902bc..93743f6 100644 --- a/processmaker.xml +++ b/processmaker.xml @@ -27,7 +27,7 @@ 9.1 - 3.4.10 + 3.4.12 9.2 diff --git a/setup.php b/setup.php index 5790057..84b41b8 100644 --- a/setup.php +++ b/setup.php @@ -2,7 +2,7 @@ // used for case cancellation define("CANCEL", 256); -define('PROCESSMAKER_VERSION', '3.4.10'); +define('PROCESSMAKER_VERSION', '3.4.12'); // Init the hooks of the plugins -Needed function plugin_init_processmaker() { @@ -15,9 +15,11 @@ function plugin_init_processmaker() { Plugin::registerClass('PluginProcessmakerProcessmaker'); - Plugin::registerClass('PluginProcessmakerCase', ['addtabon' => $objects]); + Plugin::registerClass('PluginProcessmakerCase', ['addtabon' => $objects, 'notificationtemplates_types' => true]); - Plugin::registerClass('PluginProcessmakerTaskCategory'); + Plugin::registerClass('PluginProcessmakerTask', ['notificationtemplates_types' => true]); + + Plugin::registerClass('PluginProcessmakerTaskCategory', ['addtabon' => 'TaskCategory']); if (Session::haveRightsOr("config", [READ, UPDATE])) { Plugin::registerClass('PluginProcessmakerConfig', ['addtabon' => 'Config']); From d7512b89a36f4b8c1b5425dc1e85be88d14de83e Mon Sep 17 00:00:00 2001 From: tomolimo Date: Tue, 6 Aug 2019 11:28:18 +0200 Subject: [PATCH 04/15] Added adhoc users re-assign rights Added possibility to assign a task to an adhoc user Added task del_index tag to notification template Added missing GLPI_ITEM_TASK_GROUP --- front/processmaker.form.php | 10 ++++++++-- inc/case.class.php | 13 ++++++++++--- inc/notificationtargettask.class.php | 6 +++++- inc/processmaker.class.php | 8 ++++++-- inc/profile.class.php | 4 ++-- inc/task.class.php | 18 +++++++++++++++--- inc/user.class.php | 9 +++++---- processmaker.xml | 2 +- setup.php | 5 ++++- 9 files changed, 56 insertions(+), 19 deletions(-) diff --git a/front/processmaker.form.php b/front/processmaker.form.php index aaa4410..730e27c 100644 --- a/front/processmaker.form.php +++ b/front/processmaker.form.php @@ -76,7 +76,7 @@ } else if (isset($_POST['reassign'])) { // here we should re-assign the current task to $_POST['users_id_recipient'] //$GLPINewPMUserId = PluginProcessmakerUser::getPMUserId( $_POST['users_id_recipient'] ); - if ($_POST['users_id'] != $_POST['users_id_recipient']) { // normally should be different as of the dropdown prevents already used + if ($_POST['users_id'] != $_POST['users_id_recipient'] && $_POST['users_id_recipient'] != 0) { // normally should be different as of the dropdown prevents already used $locCase = new PluginProcessmakerCase; $locCase->getFromDB($_POST['cases_id']); @@ -91,7 +91,13 @@ Session::addMessageAfterRedirect(__('Error re-assigning task: ', 'processmaker').$pmResponse->message, true, ERROR); } } else { - Session::addMessageAfterRedirect(__('Task already assigned to this person!', 'processmaker'), true, ERROR); + if ($_POST['users_id_recipient'] == 0) { + Session::addMessageAfterRedirect(__('Can\'t un-assign Task!', 'processmaker'), true, ERROR); + } else { + if ($_POST['users_id'] === $_POST['users_id_recipient'] ) { // normally should be different as of the dropdown prevents already used + Session::addMessageAfterRedirect(__('Task already assigned to this person!', 'processmaker'), true, ERROR); + } + } } //} else if (isset($_POST['delete'])) { // // delete case from case table, this will also delete the tasks diff --git a/inc/case.class.php b/inc/case.class.php index 5c86a01..b07b23d 100644 --- a/inc/case.class.php +++ b/inc/case.class.php @@ -222,10 +222,13 @@ function unpauseCase($delIndex, $userGuid) { */ function reassignCase($delIndex, $taskGuid, $delThread, $users_id_source, $users_id_target) { global $PM_SOAP; - $users_guid_source = PluginProcessmakerUser::getPMUserId($users_id_source); + $users_guid_source = ''; // by default + if ($users_id_source !== 0) { // when task is not 'to be claimed' + $users_guid_source = PluginProcessmakerUser::getPMUserId($users_id_source); + } $users_guid_target = PluginProcessmakerUser::getPMUserId($users_id_target); $pmResponse = $PM_SOAP->reassignCase($this->fields['case_guid'], $delIndex, $users_guid_source, $users_guid_target); - // now should managed GLPI Tasks previously assigned to the $users_guid_source + // now should manage GLPI Tasks previously assigned to the $users_guid_source if ($pmResponse->status_code == 0) { // we need to change the delindex of the glpi task and the assigned tech to prevent creation of new tasks // we need the delindex of the current glpi task, and the delindex of the new one @@ -269,7 +272,11 @@ public function reassignTask ($delIndex, $newDelIndex, $delThread, $newDelThread PluginProcessmakerProcessmaker::addWatcher( $itilobject_itemtype, $glpi_task->fields[ $foreignkey ], $newTech ); $donotif = PluginProcessmakerNotificationTargetProcessmaker::saveNotificationState(false); // do not send notification yet - $glpi_task->update( ['id' => $glpi_task->getID(), $foreignkey => $glpi_task->fields[$foreignkey], 'users_id_tech' => $newTech, 'update' => true] ); + $glpi_task->update( ['id' => $glpi_task->getID(), + $foreignkey => $glpi_task->fields[$foreignkey], + 'users_id_tech' => $newTech, + 'groups_id_tech' => 0, + 'update' => true] ); PluginProcessmakerNotificationTargetProcessmaker::restoreNotificationState($donotif); // Notification management diff --git a/inc/notificationtargettask.class.php b/inc/notificationtargettask.class.php index 948be21..3932b1c 100644 --- a/inc/notificationtargettask.class.php +++ b/inc/notificationtargettask.class.php @@ -105,6 +105,9 @@ public function addDataForTemplate($event, $options = []) { // task information $taskobj = $this->obj; + // del index + $this->data['##task.delindex##'] = $taskobj->fields['del_index']; + // is private? $this->data['##task.isprivate##'] = Dropdown::getYesNo(false); if ($taskobj->maybePrivate()) { @@ -175,7 +178,8 @@ public function getTags() { 'task.end' => __('End date'), 'task.status' => __('Status'), 'task.lastupdater' => __('Last updater'), - 'task.update' => __('Last update') + 'task.update' => __('Last update'), + 'task.delindex' => __('Delegation index') ]; foreach ($tags as $tag => $label) { $elt= ['tag' => $tag, diff --git a/inc/processmaker.class.php b/inc/processmaker.class.php index 7d510dc..4b76c0d 100644 --- a/inc/processmaker.class.php +++ b/inc/processmaker.class.php @@ -2749,6 +2749,7 @@ public function derivateCase($myCase, $request, $users_id = null) { $casevariables = ["GLPI_ITEM_TASK_CONTENT", "GLPI_ITEM_APPEND_TO_TASK", "GLPI_NEXT_GROUP_TO_BE_ASSIGNED", + "GLPI_ITEM_TASK_GROUP", "GLPI_ITEM_TITLE", "GLPI_TICKET_FOLLOWUP_CONTENT", "GLPI_TICKET_FOLLOWUP_IS_PRIVATE", @@ -2804,6 +2805,9 @@ public function derivateCase($myCase, $request, $users_id = null) { if (array_key_exists( 'GLPI_NEXT_GROUP_TO_BE_ASSIGNED', $casevariablevalues )) { $groupId = $casevariablevalues[ 'GLPI_NEXT_GROUP_TO_BE_ASSIGNED' ]; } + if (array_key_exists( 'GLPI_ITEM_TASK_GROUP', $casevariablevalues )) { + $groupId = $casevariablevalues[ 'GLPI_ITEM_TASK_GROUP' ]; + } $taskStartDate = ''; $taskEndDate = ''; @@ -3060,7 +3064,7 @@ public function derivateCase($myCase, $request, $users_id = null) { $parentCaseInfo, $open_task->delIndex, PluginProcessmakerUser::getGLPIUserId($open_task->userId), - 0, + $groupId, $open_task->taskId, $open_task->delThread, [ 'txtTaskContent' => $txtTaskContent, @@ -3071,7 +3075,7 @@ public function derivateCase($myCase, $request, $users_id = null) { // if end date was specified, then must change due date of the PM task if ($taskEndDate != '') { - $PM_DB->query( "UPDATE APP_DELEGATION SET DEL_TASK_DUE_DATE='$taskEndDate' WHERE APP_UID='".$sub_caseInfo->caseId."' AND DEL_INDEX=".$open_task->delIndex); + $PM_DB->query( "UPDATE APP_DELEGATION SET DEL_TASK_DUE_DATE='$taskEndDate' WHERE APP_UID='".$parentCaseInfo->caseId."' AND DEL_INDEX=".$open_task->delIndex); } } } diff --git a/inc/profile.class.php b/inc/profile.class.php index fa6690d..c83cdbe 100644 --- a/inc/profile.class.php +++ b/inc/profile.class.php @@ -20,7 +20,7 @@ static function getAllRights() { ['itemtype' => 'PluginProcessmakerConfig', 'label' => __('Cases', 'processmaker'), 'field' => 'plugin_processmaker_case', - 'rights' => [READ => __('Read'), CANCEL => __('Cancel', 'processmaker'), DELETE => __('Delete')]] + 'rights' => [READ => __('Read'), CANCEL => __('Cancel', 'processmaker'), DELETE => __('Delete'), ADHOC_REASSIGN => __('Ad Hoc user re-assign', 'processmaker')]] ]; return $rights; @@ -69,7 +69,7 @@ function showForm($ID = 0, $openform = true, $closeform = true) { * @param mixed $ID */ static function createAdminAccess($ID) { - self::addDefaultProfileInfos($ID, ['plugin_processmaker_config' => READ + UPDATE, 'plugin_processmaker_case' => READ + DELETE + CANCEL], true); + self::addDefaultProfileInfos($ID, ['plugin_processmaker_config' => READ + UPDATE, 'plugin_processmaker_case' => READ + DELETE + CANCEL + ADHOC_REASSIGN], true); } /** diff --git a/inc/task.class.php b/inc/task.class.php index f87fe89..1970d44 100644 --- a/inc/task.class.php +++ b/inc/task.class.php @@ -319,8 +319,8 @@ static function displayTabContentForItem(CommonGLPI $case, $tabnum = 1, $withtem } if (isset($currentUser)) { - if ($currentUser->userId && $task[$tabnum]['del_index']) { - // to load users for task re-assign only when task is not to be 'claimed' and if task is not a sub-case + if (/*$currentUser->userId &&*/ $task[$tabnum]['del_index']) { + // to load users for task re-assign only when task is not a sub-case echo "
".__('Loading...')."
"; echo ""; - } else { + //} else { + // // manages the claim + // // current task is to be claimed + // // get the assigned group to the item task + // $itemtask = $dbu->getItemForItemtype( $task[$tabnum]['itemtype'] ); + // $itemtask->getFromDB( $task[$tabnum]['items_id'] ); + // // check if this group can be found in the current user's groups + // if (!isset($_SESSION['glpigroups']) || !in_array( $itemtask->fields['groups_id_tech'], $_SESSION['glpigroups'] )) { + // $hide_claim_button=true; + // } + } + + if (!$currentUser->userId || !$task[$tabnum]['del_index']) { // manages the claim // current task is to be claimed // get the assigned group to the item task diff --git a/inc/user.class.php b/inc/user.class.php index 9d3a007..bd864a9 100644 --- a/inc/user.class.php +++ b/inc/user.class.php @@ -30,13 +30,14 @@ static function getSqlSearchResult ($taskId, $count = true, $right = "all", $ent global $DB, $PM_DB, $CFG_GLPI; // first need to get all users from $taskId - //$db_pm = PluginProcessmakerConfig::getInstance()->getProcessMakerDB(); + $adhoc_users = Session::haveRight('plugin_processmaker_case', ADHOC_REASSIGN) ? 2 : -1; + // TU_TYPE in (1, 2) means 1 is normal user, 2 is for adhoc users $pmQuery = "SELECT GROUP_USER.USR_UID AS pm_user_id FROM TASK_USER - JOIN GROUP_USER ON GROUP_USER.GRP_UID=TASK_USER.USR_UID AND TASK_USER.TU_RELATION = 2 AND TASK_USER.TU_TYPE=1 + JOIN GROUP_USER ON GROUP_USER.GRP_UID=TASK_USER.USR_UID AND TASK_USER.TU_RELATION = 2 AND TASK_USER.TU_TYPE IN (1, $adhoc_users) WHERE TAS_UID = '$taskId' UNION SELECT TASK_USER.USR_UID AS pm_user_id FROM TASK_USER - WHERE TAS_UID = '$taskId' AND TASK_USER.TU_RELATION = 1 AND TASK_USER.TU_TYPE=1 ; "; + WHERE TAS_UID = '$taskId' AND TASK_USER.TU_RELATION = 1 AND TASK_USER.TU_TYPE IN (1, $adhoc_users); "; $pmUsers = [ ]; foreach ($PM_DB->request( $pmQuery ) as $pmUser) { $pmUsers[ ] = $pmUser[ 'pm_user_id' ]; @@ -50,7 +51,7 @@ static function getSqlSearchResult ($taskId, $count = true, $right = "all", $ent break; case "all" : - $where = " `glpi_users`.`id` > '1' "; + $where = " `glpi_users`.`id` > '0' "; break; } diff --git a/processmaker.xml b/processmaker.xml index 93743f6..a7ffd0d 100644 --- a/processmaker.xml +++ b/processmaker.xml @@ -27,7 +27,7 @@ 9.1 - 3.4.12 + 3.4.13 9.2 diff --git a/setup.php b/setup.php index 84b41b8..b4c3c63 100644 --- a/setup.php +++ b/setup.php @@ -1,8 +1,11 @@ Date: Mon, 2 Sep 2019 16:36:52 +0200 Subject: [PATCH 05/15] Added a filter on TASK_USER.TU_TYPE to be sure that it searches for normal assignment (not adhoc) Added default groups_id_tech (set to 0) when clearing a task Set new version to 3.4.14 --- inc/case.class.php | 2 +- inc/processmaker.class.php | 3 ++- processmaker.xml | 2 +- setup.php | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/inc/case.class.php b/inc/case.class.php index b07b23d..7d79e5a 100644 --- a/inc/case.class.php +++ b/inc/case.class.php @@ -822,7 +822,7 @@ private function cancelTasks() { $ret = false; if (isset($this->fields['case_status']) && $this->fields['case_status'] == "TO_DO") { - $query = "UPDATE glpi_".$this->fields['itemtype']."tasks SET state=0,users_id_tech=0,begin=NULL,end=NULL WHERE state=1 AND id in (select items_id from glpi_plugin_processmaker_tasks where plugin_processmaker_cases_id='".$this->fields['id']."')"; + $query = "UPDATE glpi_".$this->fields['itemtype']."tasks SET state=0,users_id_tech=0,groups_id_tech=0,begin=NULL,end=NULL WHERE state=1 AND id in (select items_id from glpi_plugin_processmaker_tasks where plugin_processmaker_cases_id='".$this->fields['id']."')"; if ($DB->query( $query )) { $ret = true; } diff --git a/inc/processmaker.class.php b/inc/processmaker.class.php index 4b76c0d..0379ef2 100644 --- a/inc/processmaker.class.php +++ b/inc/processmaker.class.php @@ -1684,9 +1684,10 @@ public function addTask($cases_id, $itemtype, $items_id, $caseInfo, $delIndex, $ if ($techId == 0) { // then we must look-up DB to get the group that will be assigned to the task $groupname=''; if ($groupId == 0) { + // TU_RELATION=2 is groups and TU_TYPE=1 means normal (= not adhoc) $query = "SELECT CONTENT.CON_VALUE FROM TASK_USER JOIN CONTENT ON CONTENT.CON_ID=TASK_USER.USR_UID AND CONTENT.CON_CATEGORY='GRP_TITLE' AND CONTENT.CON_LANG = 'en' - WHERE TASK_USER.TAS_UID='$pmTaskId' AND TASK_USER.TU_RELATION=2 LIMIT 1;"; + WHERE TASK_USER.TAS_UID='$pmTaskId' AND TASK_USER.TU_RELATION=2 AND TASK_USER.TU_TYPE=1 LIMIT 1;"; } else { $query = "SELECT CON_VALUE FROM CONTENT WHERE CONTENT.CON_ID='$groupId' AND CONTENT.CON_CATEGORY='GRP_TITLE' AND CONTENT.CON_LANG='en' ;"; diff --git a/processmaker.xml b/processmaker.xml index a7ffd0d..7fc5444 100644 --- a/processmaker.xml +++ b/processmaker.xml @@ -27,7 +27,7 @@ 9.1 - 3.4.13 + 3.4.14 9.2 diff --git a/setup.php b/setup.php index b4c3c63..f5a1839 100644 --- a/setup.php +++ b/setup.php @@ -1,6 +1,6 @@ Date: Mon, 28 Oct 2019 15:36:58 +0100 Subject: [PATCH 06/15] Fixed bugs for URL in notifications Set version to 3.4.17 Added new tags in notification templates Fixed bugs on tag data Added 1 dim assoc array management in notification templates Fixed issue on the 2 dim arrays Added missing notification template tags to get info on item hosting the case (Ticket, Change, Problem) Added quote to be sure that SQL request will not throw an error Added a test for the return code from routeCase() to prevent closing of current task if routing is not done --- hook.php | 25 +++-- inc/case.class.php | 8 +- inc/notificationtargetprocessmaker.class.php | 87 ++++++++++++--- inc/notificationtargettask.class.php | 22 +++- inc/processmaker.class.php | 110 ++++++++++++------- inc/task.class.php | 2 +- processmaker.xml | 2 +- setup.php | 2 +- 8 files changed, 181 insertions(+), 77 deletions(-) diff --git a/hook.php b/hook.php index 85cad3f..c03179c 100644 --- a/hook.php +++ b/hook.php @@ -241,10 +241,11 @@ function plugin_item_update_processmaker_tasks($parm) { $locCase->getFromDB($pmTask->fields['plugin_processmaker_cases_id']); $srccase_guid = $locCase->fields['case_guid']; - $msg = ' $locCase: '.str_replace("\n", "\n ", print_r($locCase, true))."\n"; - $msg .= ' $task: '.str_replace("\n", "\n ", print_r($parm, true))."\n"; - $msg .= ' $pmTask: '.str_replace("\n", "\n ", print_r($pmTask, true))."\n"; - $msg .= "\n"; + //$msg = Toolbox::backtrace(false); + //$msg .= ' $locCase: '.str_replace("\n", "\n ", print_r($locCase, true))."\n"; + //$msg .= ' $task: '.str_replace("\n", "\n ", print_r($parm, true))."\n"; + //$msg .= ' $pmTask: '.str_replace("\n", "\n ", print_r($pmTask, true))."\n"; + //$msg .= "\n"; foreach ($DB->request( 'glpi_plugin_processmaker_caselinks', "is_active = 1 AND sourcetask_guid='".$pmTaskCat->fields['pm_task_guid']."'") as $targetTask) { @@ -289,17 +290,17 @@ function plugin_item_update_processmaker_tasks($parm) { unset( $infoForTasks[ $casevar ] ); } - $msg .= " ***********\n"; - $msg .= ' $targetTask: '.str_replace("\n", "\n ", print_r($targetTask, true))."\n"; + //$msg .= " ***********\n"; + //$msg .= ' $targetTask: '.str_replace("\n", "\n ", print_r($targetTask, true))."\n"; $targetTask['sourcecondition'] = str_replace( array_keys($infoForTasks), $infoForTasks, $targetTask['sourcecondition'] ); $eval = eval( "return (".$targetTask['sourcecondition']." ? 1 : 0);" ); - $msg .= ' $infoForTasks: '.str_replace("\n", "\n ", print_r($infoForTasks, true))."\n"; - $msg .= ' $targetTask[\'sourcecondition\']: '.str_replace("\n", "\n ", print_r($targetTask['sourcecondition'], true))."\n"; - $msg .= ' $result: '."$eval\n"; - $msg .= "\n"; + //$msg .= ' $infoForTasks: '.str_replace("\n", "\n ", print_r($infoForTasks, true))."\n"; + //$msg .= ' $targetTask[\'sourcecondition\']: '.str_replace("\n", "\n ", print_r($targetTask['sourcecondition'], true))."\n"; + //$msg .= ' $result: '."$eval\n"; + //$msg .= "\n"; if ($eval) { // look at each linked ticket if a case is attached and then if a task like $val is TO_DO @@ -429,8 +430,8 @@ function plugin_item_update_processmaker_tasks($parm) { } - $msg .= "================\n"; - Toolbox::logInFile("processmaker", $msg); + //$msg .= "================\n"; + //Toolbox::logInFile("processmaker", $msg); } } diff --git a/inc/case.class.php b/inc/case.class.php index 7d79e5a..91813b7 100644 --- a/inc/case.class.php +++ b/inc/case.class.php @@ -279,6 +279,10 @@ public function reassignTask ($delIndex, $newDelIndex, $delThread, $newDelThread 'update' => true] ); PluginProcessmakerNotificationTargetProcessmaker::restoreNotificationState($donotif); + // then update the delIndex and delThread + $query = "UPDATE glpi_plugin_processmaker_tasks SET del_index = $newDelIndex, del_thread = $newDelThread WHERE id={$pm_task_row['id']}; "; + $res = $DB->query($query); + // Notification management // search if at least one active notification is existing for that pm task with that event 'task_update_'.$glpi_task->fields['taskcategories_id'] $res = PluginProcessmakerNotificationTargetTask::getNotifications('task_update', $glpi_task->fields['taskcategories_id'], $this->fields['entities_id']); @@ -306,10 +310,6 @@ public function reassignTask ($delIndex, $newDelIndex, $delThread, $newDelThread 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0 ]); } - - // then update the delIndex and delThread - $query = "UPDATE glpi_plugin_processmaker_tasks SET del_index = $newDelIndex, del_thread = $newDelThread WHERE id={$pm_task_row['id']}; "; - $res = $DB->query($query); } } diff --git a/inc/notificationtargetprocessmaker.class.php b/inc/notificationtargetprocessmaker.class.php index 8179c1a..bd2f023 100644 --- a/inc/notificationtargetprocessmaker.class.php +++ b/inc/notificationtargetprocessmaker.class.php @@ -4,7 +4,7 @@ * PluginProcessmakerNotificationTargetProcessmaker short summary. * * PluginProcessmakerNotificationTargetProcessmaker description. - * + * * Common notificationtarget class for cases and tasks * * @version 1.0 @@ -58,11 +58,16 @@ public function getTags() { 'case.id' => __('Case id', 'processmaker'), 'case.title' => __('Case title', 'processmaker'), 'case.description' => __('Case description', 'processmaker'), - 'case.url' => __('URL'), + 'case.url' => __('Case URL'), 'var.XXX' => __('Case variable \'XXX\'', 'processmaker'), 'array.YYY' => __('List of values in \'YYY\' array', 'processmaker'), 'array.numberofYYY' => __('Number of rows in \'YYY\' array', 'processmaker'), - 'array.YYY.colname' => __('Value for colname in case array \'YYY\'', 'processamker') + 'array.YYY.colname' => __('Value for colname in \'YYY\' array', 'processmaker'), + '1darray.ZZZ.key' => __('Value for key in \'ZZZ\' assoc array (1-dimension array)', 'processmaker'), + 'item.type' => __('Item type', 'processmaker'), + 'item.id' => __('Item id', 'processmaker'), + 'item.url' => __('Item URL', 'processmaker'), + 'item.title' => __('Item title', 'processmaker') ]; foreach ($tags as $tag => $label) { @@ -76,6 +81,9 @@ public function getTags() { $elt['allowed_values'] = [__('YYY is to be replaced by any array variables', 'processmaker')]; $elt['foreach'] = true; } + if ($tag == '1darray.ZZZ.key') { + $elt['allowed_values'] = [__('ZZZ is to be replaced by any assoc array variables (1-dimension array with key/value pairs)', 'processmaker')]; + } $this->addTagToList($elt); } @@ -87,7 +95,7 @@ public function getTags() { * Get all data needed for template processing **/ public function addDataForTemplate($event, $options = []) { - global $PM_DB, $CFG_GLPI; + global $CFG_GLPI, $PM_DB; $excluded = ['_VAR_CHANGED_', 'PIN', @@ -107,11 +115,6 @@ public function addDataForTemplate($event, $options = []) { $process->getFromDB($options['case']->fields['plugin_processmaker_processes_id']); $taskcat_id = $process->fields['taskcategories_id']; - // set defaults to all - foreach ($this->tags as $key => $val) { - $this->data["##$key##"] = "-"; - } - // get case variable values $res = $PM_DB->query("SELECT APP_DATA, APP_TITLE, APP_DESCRIPTION FROM APPLICATION WHERE APP_NUMBER = ".$options['case']->fields['id']); if ($res && $PM_DB->numrows($res) == 1) { @@ -125,10 +128,16 @@ public function addDataForTemplate($event, $options = []) { // add numberof for count of rows $this->data["##array.numberof$key##"] = count($val); // get the keys/vals of the sub-array - foreach ($val as $row) { - foreach ($row as $col_name => $col_val) { - $this->data["array.$key"][]["##array.$key.$col_name##"] = $col_val; - $this->data["##lang.array.$key.$col_name##"] = $col_name; + foreach ($val as $attribute => $row) { + if (is_array($row)) { + $index = isset($this->data["array.$key"]) ? count($this->data["array.$key"]) : 0; + foreach ($row as $col_name => $col_val) { + $this->data["array.$key"][$index]["##array.$key.$col_name##"] = $col_val; + $this->data["##lang.array.$key.$col_name##"] = $col_name; + } + } else { + $this->data["1darray.$key"]["##array.$key.$attribute##"] = $row; + $this->data["##lang.1darray.$key.$attribute##"] = $attribute; } } } else { @@ -145,18 +154,30 @@ public function addDataForTemplate($event, $options = []) { $this->data['##case.id##'] = $options['case']->fields['id']; // case URL - $this->data['##case.url##'] = $CFG_GLPI["url_base"]."/index.php?redirect=".urlencode("/plugins/processmaker/front/case.form.php?id=".$options['case']->fields['id']); + $this->data['##case.url##'] = $this->formatURL($options['additionnaloption']['usertype'], + urlencode(urlencode($CFG_GLPI["url_base"] . + PluginProcessmakerCase::getFormURLWithID($options['case']->fields['id'], false)))); // parent task information: meta data on process // will get parent of task which is the process task category - $tmp_taskcatinfo['name'] = DropdownTranslation::getTranslatedValue( $taskcat_id, 'TaskCategory', 'name'); - $tmp_taskcatinfo['comment'] = DropdownTranslation::getTranslatedValue( $taskcat_id, 'TaskCategory', 'comment'); + $tmp_taskcatinfo['name'] = DropdownTranslation::getTranslatedValue($taskcat_id, 'TaskCategory', 'name'); + $tmp_taskcatinfo['comment'] = DropdownTranslation::getTranslatedValue($taskcat_id, 'TaskCategory', 'comment'); // process title $this->data['##process.categoryid##'] = $taskcat_id; $this->data['##process.category##'] = $tmp_taskcatinfo['name']; $this->data['##process.categorycomment##'] = $tmp_taskcatinfo['comment']; - // add labels + // add information about item that hosts the case + $item = new $options['case']->fields['itemtype']; + $item->getFromDB($options['case']->fields['items_id']); + $this->data['##item.type##'] = $item->getTypeName(1); + $this->data['##item.id##'] = sprintf("%07d", $options['case']->fields['items_id']); // to have items_id with 7 digits with leading 0 + $this->data['##item.url##'] = $this->formatURL($options['additionnaloption']['usertype'], + urlencode(urlencode($CFG_GLPI["url_base"] . + $item::getFormURLWithID($options['case']->fields['items_id'], false)))); + $this->data['##item.title##'] = HTML::entities_deep($item->fields['name']); + + // add labels to tags that are not set $this->getTags(); foreach ($this->tag_descriptions[NotificationTarget::TAG_LANGUAGE] as $tag => $values) { if (!isset($this->data[$tag])) { @@ -165,4 +186,36 @@ public function addDataForTemplate($event, $options = []) { } } + + /** + * Get header to add to content + **/ + function getContentHeader() { + + if ($this->getMode() == \Notification_NotificationTemplate::MODE_MAIL + && MailCollector::getNumberOfActiveMailCollectors() + ) { + return NotificationTargetTicket::HEADERTAG.' '.__('To answer by email, write above this line').' '. + NotificationTargetTicket::HEADERTAG; + } + + return ''; + } + + + /** + * Get footer to add to content + **/ + function getContentFooter() { + + if ($this->getMode() == \Notification_NotificationTemplate::MODE_MAIL + && MailCollector::getNumberOfActiveMailCollectors() + ) { + return NotificationTargetTicket::FOOTERTAG.' '.__('To answer by email, write under this line').' '. + NotificationTargetTicket::FOOTERTAG; + } + + return ''; + } + } \ No newline at end of file diff --git a/inc/notificationtargettask.class.php b/inc/notificationtargettask.class.php index 3932b1c..3e1868f 100644 --- a/inc/notificationtargettask.class.php +++ b/inc/notificationtargettask.class.php @@ -18,7 +18,8 @@ class PluginProcessmakerNotificationTargetTask extends PluginProcessmakerNotific */ private static function getDefaultEvents() { return ['task_add' => ['event' => 'task_add_', 'label' => __('New task')], - 'task_update' => ['event' => 'task_update_', 'label' => __('Update of a task')] + 'task_update' => ['event' => 'task_update_', 'label' => __('Update of a task')], + 'task_done' => ['event' => 'task_done_', 'label' => __('Task done')] ]; } @@ -66,8 +67,9 @@ public function getEvents() { } $temp->getFromDB($row['taskcat']); - $actions[$defaultEvents['task_add']['event'].$row['taskcat']] = $ptaskcats[$row['ptaskcat']]." > ".$temp->fields['name'].": " . $defaultEvents['task_add']['label']; + $actions[$defaultEvents['task_add']['event'].$row['taskcat']] = $ptaskcats[$row['ptaskcat']]." > ".$temp->fields['name'].": " . $defaultEvents['task_add']['label']; $actions[$defaultEvents['task_update']['event'].$row['taskcat']] = $ptaskcats[$row['ptaskcat']]." > ".$temp->fields['name'].": " . $defaultEvents['task_update']['label']; + $actions[$defaultEvents['task_done']['event'].$row['taskcat']] = $ptaskcats[$row['ptaskcat']]." > ".$temp->fields['name'].": " . $defaultEvents['task_done']['label']; } return $actions; @@ -92,7 +94,7 @@ public function addDataForTemplate($event, $options = []) { $baseevent = $locevent[0].'_'.$locevent[1]; $taskcat_id = $locevent[2]; - // task action: add or update + // task action: add, update or done $this->data['##task.action##'] = $events[$baseevent]['label']; // task category information: meta data on task @@ -132,6 +134,15 @@ public function addDataForTemplate($event, $options = []) { // task technician $this->data['##task.user##'] = Html::clean($dbu->getUserName($taskobj->fields['users_id_tech'])); + $tech = new User; + $tech->getFromDB($taskobj->fields['users_id_tech']); + $this->data['##task.user.login##'] = $tech->fields['name']; + if (isset($options['old_users_id_tech'])) { + $oldtech = new User; + $oldtech->getFromDB($options['old_users_id_tech']); + $this->data['##task.former.user##'] = Html::clean($dbu->getUserName($options['old_users_id_tech'])); + $this->data['##task.former.user.login##'] = $oldtech->fields['name']; + } // task group technician $this->data['##task.group##'] = Html::clean(Toolbox::clean_cross_side_scripting_deep(Dropdown::getDropdownName("glpi_groups", $taskobj->fields['groups_id_tech'])), true, 2, false); @@ -146,7 +157,7 @@ public function addDataForTemplate($event, $options = []) { // task duration $this->data['##task.time##'] = Html::timestampToString($taskobj->fields['actiontime'], false); - // add labels + // add labels to tags that are not set $this->getTags(); foreach ($this->tag_descriptions[NotificationTarget::TAG_LANGUAGE] as $tag => $values) { if (!isset($this->data[$tag])) { @@ -173,6 +184,9 @@ public function getTags() { 'task.categorycomment' => __('Category comment'), 'task.time' => __('Total duration'), 'task.user' => __('User assigned to task'), + 'task.user.login' => __('User login assigned to task'), + 'task.former.user' => __('Former user assigned to task'), + 'task.former.user.login' => __('Former user login assigned to task'), 'task.group' => __('Group assigned to task'), 'task.begin' => __('Start date'), 'task.end' => __('End date'), diff --git a/inc/processmaker.class.php b/inc/processmaker.class.php index 0379ef2..d1dfef5 100644 --- a/inc/processmaker.class.php +++ b/inc/processmaker.class.php @@ -373,9 +373,9 @@ function getCaseInfo($caseGuid, $delIndex = '') { * routes a case (i.e., moves the case to the next task in the process according to its routing rules). * Embedded routeCase() PM web service call (definition: http://wiki.processmaker.com/index.php/2.0/ProcessMaker_WSDL_Web_Services#routeCase.28.29) * A session must be open before with login() - * @param $case_guid The case ID, which can be obtained with the caseList() function - * @param $delIndex The delegation index, which is a positive integer to identify the current task of the case. If empty then use current delIndex. - * @return a routeCaseResponse object, or false exception occured. routing is normalized to be always an array of routeListStruct + * @param $case_guid string The case GUID, which can be obtained with the caseList() function + * @param $delIndex integer The delegation index, which is a positive integer to identify the current task of the case. If empty then use current delIndex. + * @return stdClass|boolean routeCaseResponse object or false when exception occured. routing is normalized to be always an array of routeListStruct */ function routeCase($case_guid, $delIndex) { try { @@ -1766,37 +1766,37 @@ public function addTask($cases_id, $itemtype, $items_id, $caseInfo, $delIndex, $ $query = "INSERT INTO glpi_plugin_processmaker_tasks (items_id, itemtype, plugin_processmaker_cases_id, plugin_processmaker_taskcategories_id, del_index, del_thread) VALUES ({$glpi_task->getId()}, '{$glpi_task->getType()}', $cases_id, {$pmtaskcat->fields['id']}, $delIndex, $delThread);"; $DB->query( $query ); - } - // send notification if needed for new task as now we have the PluginProcessmakerTask in the DB - $donotif = PluginProcessmakerNotificationTargetProcessmaker::saveNotificationState($options['notif']); - // Notification management - $item = new $itemtype; - $item->getFromDB($items_id); - // search if at least one active notification is existing for that pm task with that event 'task_update_'.$glpi_task->fields['taskcategories_id'] - $res = PluginProcessmakerNotificationTargetTask::getNotifications('task_add', $glpi_task->fields['taskcategories_id'], $item->fields['entities_id']); - if ($res['notifications'] && count($res['notifications']) > 0) { - $pm_task = new PluginProcessmakerTask($glpi_task->getType()); - $pm_task->getFromDB($glpi_task->getId()); - NotificationEvent::raiseEvent($res['event'], - $pm_task, - ['plugin_processmaker_cases_id' => $cases_id, - 'itemtype' => $glpi_task->getType(), - 'task_id' => $glpi_task->getID(), - 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0, - 'entities_id' => $item->fields['entities_id'] - ]); - } else { - NotificationEvent::raiseEvent('add_task', - $item, - ['plugin_processmaker_cases_id' => $cases_id, - 'itemtype' => $itemtype, - 'task_id' => $glpi_task->getID(), - 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0 - ]); - } + // send notification if needed for new task as now we have the PluginProcessmakerTask in the DB + $donotif = PluginProcessmakerNotificationTargetProcessmaker::saveNotificationState($options['notif']); + // Notification management + $item = new $itemtype; + $item->getFromDB($items_id); + // search if at least one active notification is existing for that pm task with that event 'task_update_'.$glpi_task->fields['taskcategories_id'] + $res = PluginProcessmakerNotificationTargetTask::getNotifications('task_add', $glpi_task->fields['taskcategories_id'], $item->fields['entities_id']); + if ($res['notifications'] && count($res['notifications']) > 0) { + $pm_task = new PluginProcessmakerTask($glpi_task->getType()); + $pm_task->getFromDB($glpi_task->getId()); + NotificationEvent::raiseEvent($res['event'], + $pm_task, + ['plugin_processmaker_cases_id' => $cases_id, + 'itemtype' => $glpi_task->getType(), + 'task_id' => $glpi_task->getID(), + 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0, + 'entities_id' => $item->fields['entities_id'] + ]); + } else { + NotificationEvent::raiseEvent('add_task', + $item, + ['plugin_processmaker_cases_id' => $cases_id, + 'itemtype' => $itemtype, + 'task_id' => $glpi_task->getID(), + 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0 + ]); + } - PluginProcessmakerNotificationTargetProcessmaker::restoreNotificationState($donotif); + PluginProcessmakerNotificationTargetProcessmaker::restoreNotificationState($donotif); + } } @@ -2008,7 +2008,6 @@ public function solveTask($cases_id, $delIndex, $options = []) { $hostItem->getFromDB( $glpi_task->fields[ $itemFKField ] ); // change current glpi_currenttime to be sure that date_mode for solved task will not be identical than date_mode of the newly started task - //$end_date = new DateTime( $_SESSION["glpi_currenttime"] ); $saved_date_time = $_SESSION["glpi_currenttime"]; $_SESSION["glpi_currenttime"] = (new DateTime($saved_date_time))->sub(new DateInterval("PT1S"))->format("Y-m-d H:i:s"); @@ -2042,13 +2041,41 @@ public function solveTask($cases_id, $delIndex, $options = []) { //'groups_id_tech' => 0, 'content' => $DB->escape($glpi_task->fields[ 'content' ].$options['txtToAppend']) ]; - $donotif = PluginProcessmakerNotificationTargetProcessmaker::saveNotificationState($options['notif']); + + $donotif = PluginProcessmakerNotificationTargetProcessmaker::saveNotificationState(false); // do not send notification yet $glpi_task->update($params); PluginProcessmakerNotificationTargetProcessmaker::restoreNotificationState($donotif); // Close the task $DB->query("UPDATE glpi_plugin_processmaker_tasks SET del_thread_status = '".PluginProcessmakerTask::CLOSED."' WHERE id = {$row['id']}"); + // send notification if needed for new task as now the PluginProcessmakerTask has been updated in the DB + $donotif = PluginProcessmakerNotificationTargetProcessmaker::saveNotificationState($options['notif']); + // Notification management + // search if at least one active notification is existing for that pm task with that event 'task_update_'.$glpi_task->fields['taskcategories_id'] + $res = PluginProcessmakerNotificationTargetTask::getNotifications('task_done', $glpi_task->fields['taskcategories_id'], $hostItem->fields['entities_id']); + if ($res['notifications'] && count($res['notifications']) > 0) { + $pm_task = new PluginProcessmakerTask($glpi_task->getType()); + $pm_task->getFromDB($glpi_task->getId()); + NotificationEvent::raiseEvent($res['event'], + $pm_task, + ['plugin_processmaker_cases_id' => $cases_id, + 'itemtype' => $glpi_task->getType(), + 'task_id' => $glpi_task->getID(), + 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0, + 'entities_id' => $hostItem->fields['entities_id'] + ]); + } else { + NotificationEvent::raiseEvent('update_task', + $hostItem, + ['plugin_processmaker_cases_id' => $cases_id, + 'itemtype' => $hostItem->getType(), + 'task_id' => $glpi_task->getID(), + 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0 + ]); + } + PluginProcessmakerNotificationTargetProcessmaker::restoreNotificationState($donotif); + // restore current glpi time $_SESSION["glpi_currenttime"] = $saved_date_time; @@ -2739,6 +2766,8 @@ public function derivateCase($myCase, $request, $users_id = null) { $item = new $itemtype; $item->getFromDB($items_id); + //Toolbox::logInFile("processmaker_derivate", Toolbox::backtrace(false) . "derivateCase: myCase = " . print_r($myCase, true) . "\nrequest = " . print_r($request, true) . "\nusers_id = " . print_r($users_id, true) ); + // save the dynaform variables into the current case if (isset($request['UID']) && isset($request['APP_UID']) && isset($request['__DynaformName__'])) { $resultSave = $this->saveForm( $request ); @@ -2746,6 +2775,16 @@ public function derivateCase($myCase, $request, $users_id = null) { // now derivate the case !!! $pmRouteCaseResponse = $this->routeCase($myCase->fields['case_guid'], $request['DEL_INDEX']); + //Toolbox::logInFile("processmaker_derivate", "routeCase: pmRouteCaseResponse = " . print_r($pmRouteCaseResponse, true)); + + if ($pmRouteCaseResponse->status_code != 0) { + Session::addMessageAfterRedirect(sprintf(__('Unable to derivate case! Retry in a moment, or ask your administrator.
Error code: %u
Error message: %s
', 'processmaker'), + $pmRouteCaseResponse->status_code, + $pmRouteCaseResponse->message), + true, + ERROR); + return; + } $casevariables = ["GLPI_ITEM_TASK_CONTENT", "GLPI_ITEM_APPEND_TO_TASK", @@ -2872,9 +2911,6 @@ public function derivateCase($myCase, $request, $users_id = null) { } $resultSave = $myCase->sendVariables($resetcasevariables); - // print_r( $pmRouteCaseResponse ) ; - // die() ; - // now manage tasks associated with item // switch own task to 'done' and create a new one $this->solveTask($myCase->getID(), diff --git a/inc/task.class.php b/inc/task.class.php index 1970d44..3700533 100644 --- a/inc/task.class.php +++ b/inc/task.class.php @@ -41,7 +41,7 @@ function getItilObjectItemType() { function getFromDB($items_id) { global $DB; - if ($this->getFromDBByQuery(" WHERE itemtype='".$this->itemtype."' AND items_id=$items_id;" )) { + if ($this->getFromDBByQuery(" WHERE itemtype='".$this->itemtype."' AND items_id='$items_id';" )) { $task = new $this->itemtype; if ($task->getFromDB( $items_id )) { // then we should add our own fields diff --git a/processmaker.xml b/processmaker.xml index 7fc5444..eb2dcc7 100644 --- a/processmaker.xml +++ b/processmaker.xml @@ -27,7 +27,7 @@ 9.1 - 3.4.14 + 3.4.17 9.2 diff --git a/setup.php b/setup.php index f5a1839..a37a3a0 100644 --- a/setup.php +++ b/setup.php @@ -1,6 +1,6 @@ Date: Wed, 7 Oct 2020 11:23:23 +0200 Subject: [PATCH 07/15] Added condition to set user = admin when script is run in crontask Set version to 3.4.18 --- hook.php | 5 ++++- setup.php | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/hook.php b/hook.php index c03179c..27be219 100644 --- a/hook.php +++ b/hook.php @@ -180,7 +180,10 @@ function plugin_pre_item_update_processmaker(CommonITILObject $parm) { * @param mixed $parm is the object */ function plugin_item_update_processmaker_satisfaction($parm) { - + global $PM_SOAP; + if (Session::isCron()) { // Task cron log with user admin + $PM_SOAP->login(true); + } $cases = PluginProcessmakerCase::getIDsFromItem('Ticket', $parm->fields['tickets_id']); foreach ($cases as $cases_id) { $locCase = new PluginProcessmakerCase; diff --git a/setup.php b/setup.php index a37a3a0..44ee314 100644 --- a/setup.php +++ b/setup.php @@ -1,6 +1,6 @@ Date: Wed, 7 Oct 2020 11:25:11 +0200 Subject: [PATCH 08/15] Added a hook to manage the change of user login Cleanning of unused code Set version to 3.4.19 --- hook.php | 27 +++++++++++++++++++++++++++ inc/config.class.php | 2 +- inc/processmaker.class.php | 4 ++-- setup.php | 7 +++++-- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/hook.php b/hook.php index 27be219..8aa662a 100644 --- a/hook.php +++ b/hook.php @@ -195,6 +195,33 @@ function plugin_item_update_processmaker_satisfaction($parm) { } +/** + * Summary of plugin_item_update_processmaker_user + * When a user login is changed, then must change it in the PM tables + * @param User $param is the user being changed + */ +function plugin_item_update_processmaker_user(User $param) { + // Must test if user login has been changed + // if yes, must change the login in the PM tables: + // USERS and RBAC_USERS, othewise the link in the processmaker_users table will be invalid + if (in_array('name', $param->updates)) { + // check if user is in the processmaker_user table + $pm_user = PluginProcessmakerUser::getPMUserId($param->getID()); + if ($pm_user) { + // must update the user in PM tables + global $PM_SOAP; + if ($param->fields['is_active'] == 0 || $param->fields['is_deleted'] == 1) { + $status = "INACTIVE"; + } else { + $status = "ACTIVE"; + } + $PM_SOAP->login(true); + $pmResult = $PM_SOAP->updateUser( $pm_user, $param->fields['name'], $param->fields['firstname'], $param->fields['realname'], $status ); + } + } +} + + function plugin_processmaker_post_init() { global $PM_DB, $PM_SOAP; if (!isset($PM_DB)) { diff --git a/inc/config.class.php b/inc/config.class.php index 262b0f0..ea5f898 100644 --- a/inc/config.class.php +++ b/inc/config.class.php @@ -156,7 +156,7 @@ static function showConfigForm($item) { if (!$config->fields['maintenance']) { echo ""; - echo "".__('Server URL (must be in same domain than GLPI)', 'processmaker').""; + echo "".__('Server URL (must be in same domain than GLPI, if GLPI is using HTTPS, PM server must also use HTTPS)', 'processmaker').""; echo ""; echo ""; diff --git a/inc/processmaker.class.php b/inc/processmaker.class.php index d1dfef5..ecdfb89 100644 --- a/inc/processmaker.class.php +++ b/inc/processmaker.class.php @@ -1296,9 +1296,9 @@ static function cronPMUsers($task) { // insert into DB the link between glpi users and pm user $pmuser = new PluginProcessmakerUser; if ($pmuser->getFromDB( $user['id'] )) { - $pmuser->update( [ 'id' => $user['id'], 'pm_users_id' => $pmResult->userUID, 'password' => md5( $pass ) ] ); + $pmuser->update( [ 'id' => $user['id'], 'pm_users_id' => $pmResult->userUID ] ); } else { - $pmuser->add( [ 'id' => $user['id'], 'pm_users_id' => $pmResult->userUID, 'password' => md5( $pass ) ] ); + $pmuser->add( [ 'id' => $user['id'], 'pm_users_id' => $pmResult->userUID ] ); } $actionCode = 1; diff --git a/setup.php b/setup.php index 44ee314..220dd33 100644 --- a/setup.php +++ b/setup.php @@ -1,6 +1,6 @@ 'plugin_item_update_processmaker_satisfaction']; + $hooks = ['TicketSatisfaction' => 'plugin_item_update_processmaker_satisfaction', + 'User' => 'plugin_item_update_processmaker_user']; foreach ($objects as $obj) { $hooks[$obj.'Task'] = 'plugin_item_update_processmaker_tasks'; } @@ -109,6 +110,8 @@ function plugin_init_processmaker() { //} //$PLUGIN_HOOKS['item_purge']['processmaker'] = $hooks; + + // Javascript $plugin = new Plugin(); if ($plugin->isActivated('processmaker') && Session::getLoginUserID() ) { From 66b87db5fc923c286c052a53862cd07402904038 Mon Sep 17 00:00:00 2001 From: tomolimo Date: Wed, 7 Oct 2020 11:28:33 +0200 Subject: [PATCH 09/15] Added Send reminder Added unclaim task Rewrite some code for clearer maintenance Set version to 3.4.20 --- ajax/task_users.php | 125 +++++++++-- front/processmaker.form.php | 135 ++++-------- inc/case.class.php | 220 +++++++++++++++---- inc/notificationtargetprocessmaker.class.php | 7 +- inc/notificationtargettask.class.php | 147 ++++++------- inc/processmaker.class.php | 117 +++++----- inc/task.class.php | 91 +++++--- install/mysql/processmaker-empty.sql | 2 +- install/update.php | 4 + install/update_3_4_9_to_3_4_10.php | 12 + processmaker.xml | 2 +- setup.php | 2 +- 12 files changed, 542 insertions(+), 322 deletions(-) create mode 100644 install/update_3_4_9_to_3_4_10.php diff --git a/ajax/task_users.php b/ajax/task_users.php index e9e5351..d0df073 100644 --- a/ajax/task_users.php +++ b/ajax/task_users.php @@ -18,31 +18,120 @@ Session::checkLoginUser(); +$commoninputs = "". + "". + "". + "". + "". + "". + "". + "". + ""; + $PM_SOAP = new PluginProcessmakerProcessmaker; // not used in this context, just here to define the type of $PM_SOAP $PM_DB = new PluginProcessmakerDB; $rand = rand(); echo ""; echo __('Re-assign task to', 'processmaker')." "; -echo ""; -echo ""; -echo ""; -echo ""; -echo ""; -echo ""; -echo ""; -echo ""; +echo ""; +echo ""; +echo $commoninputs; + +$can_unclaim = false; // by default +$query = "SELECT TAS_GROUP_VARIABLE FROM TASK WHERE TAS_UID='".$_REQUEST['taskGuid']."' AND TAS_ASSIGN_TYPE='SELF_SERVICE';"; +$res = $PM_DB->query($query); +if ($PM_DB->numrows($res) > 0) { + $can_unclaim = true; +} PluginProcessmakerUser::dropdown( ['name' => 'users_id_recipient', - 'value' => $_REQUEST['users_id'], - 'used' => [$_REQUEST['users_id']], - 'entity' => 0, //$item->fields["entities_id"], // not used, as any user can be assigned to any tasks - 'entity_sons' => false, // not used, as any user can be assigned to any tasks - 'right' => 'all', - 'rand' => $rand, - 'width' => '', - 'specific_tags' => ['taskGuid' => $_REQUEST['taskGuid']]]); + 'value' => $_REQUEST['users_id'], + 'used' => [$_REQUEST['users_id']], + 'entity' => 0, //$item->fields["entities_id"], // not used, as any user can be assigned to any tasks + 'entity_sons' => false, // not used, as any user can be assigned to any tasks + 'right' => 'all', + 'rand' => $rand, + 'width' => '', + 'specific_tags' => ['taskGuid' => $_REQUEST['taskGuid']] + ]); + echo "  "; -echo ""; -Html::closeForm(true); +echo ""; +echo ""; + +echo HTML::scriptBlock(" + $(function () { + // Dialog helpers + // Create the dialog with \"Re-assign\" button + function showCommentDlg(title, content, alttext) { + + var dlgContents = { + title: title, + modal: true, + width: 'auto', + height: 'auto', + resizable: false, + close: function (event, ui) { + $(this).dialog('destroy').remove(); + }, + buttons: [{ + text: alttext, + id: 'submit$rand', + disabled: 'disabled', + click: function() { + $('#processmaker_form_task$rand-".$_REQUEST['delIndex']." input[name=comment]').val($('#comment$rand').val()); + //$('#processmaker_form_task$rand-".$_REQUEST['delIndex']."').submit(); + $('input[name=reassign').click(); + $('#submit$rand').button('disable'); + } + }], + show: true, + hide: true + } + $('
').appendTo($('#processmaker_form_task$rand-".$_REQUEST['delIndex']."')); + var locDlg = $('#reassign$rand').html(content + '

Input at least 10 words in English to justify.').dialog(dlgContents); + $('#comment$rand').focus(); + $('#comment$rand').on('keydown keyup', function(e) { + if ($('#comment$rand').val().split(/\W+/).length > 10) { + $('#submit$rand').button('enable'); + } else { + $('#submit$rand').button('disable'); + } + }); + return locDlg; + }; + + $('input[name=reassign$rand]').click(function (e) { + e.preventDefault(); + if ($('input[name=users_id]').val() == $('input[name=users_id_recipient]').val()) { + // task is already assigned to this user + alert('".__('Task is already assigned to this user or group!', 'processmaker')."', '".__('Re-assign task', 'processmaker')."'); + } else if ($('input[name=users_id_recipient]').val() == 0) { + // un-claim + if (".($can_unclaim ? 1 : 0)." && $('input[name=users_id]').val() != 0) { + showCommentDlg('".__('Un-claim task', 'processmaker')."', + '".__('Please input reason to un-claim
(task will be re-assigned to former group):', 'processmaker')."', + '".__('Un-claim', 'processmaker')."'); + } else { + // task can't be unclaim because it isn't SELF_SERVICE + alert('".__('Can\\\'t un-assign Task!', 'processmaker')."', '".__('Un-claim task', 'processmaker')."'); + } + } else { + showCommentDlg('".__('Re-assign task', 'processmaker')."', + '".__('Please input reason to re-assign:', 'processmaker')."', + '".__('Re-assign', 'processmaker')."'); + } + return false; + }); + }) + "); + + +if (Session::getLoginUserID() != $_REQUEST['users_id']) { + echo "  "; + echo ""; +} + +Html::closeForm(true); diff --git a/front/processmaker.form.php b/front/processmaker.form.php index 730e27c..bf506a3 100644 --- a/front/processmaker.form.php +++ b/front/processmaker.form.php @@ -1,31 +1,31 @@ 0) { + if (isset($_REQUEST['items_id']) && $_REQUEST['items_id'] > 0) { // then this case will be bound to an item - if ($_POST['plugin_processmaker_processes_id'] > 0) { + if ($_REQUEST['plugin_processmaker_processes_id'] > 0) { - $resultCase = $PM_SOAP->startNewCase($_POST['plugin_processmaker_processes_id'], $_POST['itemtype'], $_POST['items_id'], Session::getLoginUserID()); + $resultCase = $PM_SOAP->startNewCase($_REQUEST['plugin_processmaker_processes_id'], $_REQUEST['itemtype'], $_REQUEST['items_id'], Session::getLoginUserID()); if ($resultCase->status_code == 0) { $case = new PluginProcessmakerCase; if ($case->getFromGUID($resultCase->caseId)) { $link = $case->getLinkURL(); - $task = new PluginProcessmakerTask($_POST['itemtype'].'Task'); + $task = new PluginProcessmakerTask($_REQUEST['itemtype'].'Task'); $task->getFromDBByQuery(" WHERE `plugin_processmaker_cases_id`=".$case->getID()); // normally there is only one and only one first task //$link .= '&forcetab=PluginProcessmakerTask$'.$task->getID(); Session::setActiveTab('PluginProcessmakerCase', 'PluginProcessmakerTask$'.$task->getID()); - $item = new $_POST['itemtype']; - $item->getFromDB($_POST['items_id']); + $item = new $_REQUEST['itemtype']; + $item->getFromDB($_REQUEST['items_id']); unset($_SERVER['REQUEST_URI']); // to prevent use of processmaker.form.php in NavigateList Session::initNavigateListItems('PluginProcessmakerCase', //TRANS : %1$s is the itemtype name, // %2$s is the name of the item (used for headings of a list) sprintf('%1$s = %2$s', - $_POST['itemtype']::getTypeName(1), $item->fields["name"])); + $_REQUEST['itemtype']::getTypeName(1), $item->fields["name"])); Html::redirect($link); } Html::back(); @@ -37,14 +37,14 @@ } } else { // the case is created before the ticket (used for post-only case creation before ticket creation) $pm_user_guid = PluginProcessmakerUser::getPMUserId( Session::getLoginUserID() ); - $resultCase = $PM_SOAP->newCase( $_POST['plugin_processmaker_processes_id'], + $resultCase = $PM_SOAP->newCase( $_REQUEST['plugin_processmaker_processes_id'], ['GLPI_ITEM_CAN_BE_SOLVED' => 0, 'GLPI_SELFSERVICE_CREATED' => '1', 'GLPI_ITEM_TYPE' => 'Ticket', 'GLPI_URL' => $CFG_GLPI['url_base'], // Specific to Tickets // GLPI_TICKET_TYPE will contains 1 (= incident) or 2 (= request) - 'GLPI_TICKET_TYPE' => $_POST['type'], + 'GLPI_TICKET_TYPE' => $_REQUEST['type'], 'GLPI_ITEM_REQUESTER_GLPI_ID' => Session::getLoginUserID(), 'GLPI_ITEM_REQUESTER_PM_ID' => $pm_user_guid ] ); @@ -53,7 +53,7 @@ // Must show it... // $rand = rand( ); - Html::redirect($CFG_GLPI['root_doc']."/plugins/processmaker/front/processmaker.helpdesk.form.php?processes_id=".$_POST['plugin_processmaker_processes_id']."&case_guid=".$resultCase->caseId."&rand=$rand&itilcategories_id=".$_POST["itilcategories_id"]."&type=".$_REQUEST["type"]."&entities_id=".$_REQUEST['entities_id']); + Html::redirect($CFG_GLPI['root_doc']."/plugins/processmaker/front/processmaker.helpdesk.form.php?processes_id=".$_REQUEST['plugin_processmaker_processes_id']."&case_guid=".$resultCase->caseId."&rand=$rand&itilcategories_id=".$_REQUEST["itilcategories_id"]."&type=".$_REQUEST["type"]."&entities_id=".$_REQUEST['entities_id']); } else { Session::addMessageAfterRedirect( PluginProcessmakerProcessmaker::getPMErrorMessage($resultCase->status_code)."
$resultCase->message ($resultCase->status_code)", true, ERROR); @@ -61,94 +61,55 @@ } } - break; + break; - case 'unpausecase_or_reassign_or_delete' : - if (isset($_POST['unpause'])) { + case 'reassign_reminder' : + if (isset($_REQUEST['reassign'])) { + // here we should re-assign the current task to $_REQUEST['users_id_recipient'] $locCase = new PluginProcessmakerCase; - $locCase->getFromDB($_POST['cases_id']); - $pmResultUnpause = $locCase->unpauseCase($_POST['delIndex'], $_POST['users_id']); - if ($pmResultUnpause->status_code == 0) { - Html::back(); - } else { - echo "Error unpausing case: ".$pmResultUnpause->message." \n"; - } - } else if (isset($_POST['reassign'])) { - // here we should re-assign the current task to $_POST['users_id_recipient'] - //$GLPINewPMUserId = PluginProcessmakerUser::getPMUserId( $_POST['users_id_recipient'] ); - if ($_POST['users_id'] != $_POST['users_id_recipient'] && $_POST['users_id_recipient'] != 0) { // normally should be different as of the dropdown prevents already used - $locCase = new PluginProcessmakerCase; - $locCase->getFromDB($_POST['cases_id']); - - $pmResponse = $locCase->reassignCase($_POST['delIndex'], - $_POST['taskGuid'], - $_POST['delThread'], - $_POST['users_id'], - $_POST['users_id_recipient']); + $locCase->getFromDB($_REQUEST['cases_id']); + if ($_REQUEST['users_id_recipient'] != 0) { + // we are assigning a new tech to a task + $pmResponse = $locCase->reassignCase($_REQUEST['delIndex'], + $_REQUEST['taskGuid'], + $_REQUEST['delThread'], + $_REQUEST['users_id'], + $_REQUEST['users_id_recipient'], + ['comment' => $_REQUEST['comment']]); if ($pmResponse) { Session::addMessageAfterRedirect(__('Task re-assigned!', 'processmaker'), true, INFO); } else { Session::addMessageAfterRedirect(__('Error re-assigning task: ', 'processmaker').$pmResponse->message, true, ERROR); } - } else { - if ($_POST['users_id_recipient'] == 0) { - Session::addMessageAfterRedirect(__('Can\'t un-assign Task!', 'processmaker'), true, ERROR); + } elseif ($_REQUEST['users_id_recipient'] == 0) { + // we are unassigning a task, i.e.: task un-claim + $pmResponse = $locCase->unassignCase($_REQUEST['delIndex'], + $_REQUEST['taskGuid'], + $_REQUEST['tasktype'], + $_REQUEST['tasks_id'], + $_REQUEST['itemtype'], + ['comment' => $_REQUEST['comment']]); + if ($pmResponse) { + Session::addMessageAfterRedirect(__('Task un-claimed!', 'processmaker'), true, INFO); } else { - if ($_POST['users_id'] === $_POST['users_id_recipient'] ) { // normally should be different as of the dropdown prevents already used - Session::addMessageAfterRedirect(__('Task already assigned to this person!', 'processmaker'), true, ERROR); - } + Session::addMessageAfterRedirect(__("Can't un-claim task! Verify 'Assignement Rules' in the process definition.", 'processmaker'), true, ERROR); } } - //} else if (isset($_POST['delete'])) { - // // delete case from case table, this will also delete the tasks - // $locCase = new PluginProcessmakerCase; - // if ($locCase->getFromDB($_POST['cases_id']) && $locCase->deleteCase()) { - // // request delete from pm itself - // $PM_SOAP->login(true); + } elseif (isset($_REQUEST['reminder'])) { + // send notification remider as requested for this task - // $resultPM = $PM_SOAP->deleteCase($locCase->fields['case_guid']); - - // if ($resultPM->status_code == 0) { - // Session::addMessageAfterRedirect($LANG['processmaker']['item']['case']['deleted'], true, INFO); - // } else { - // Session::addMessageAfterRedirect($LANG['processmaker']['item']['case']['errordeleted'], true, ERROR); - // } - // } else { - // Session::addMessageAfterRedirect($LANG['processmaker']['item']['case']['errordeleted'], true, ERROR); - // } - //} else if (isset($_POST['cancel'])) { - // // cancel case from PM - // $locCase = new PluginProcessmakerCase; - // $locCase->getFromDB($_POST['cases_id']); - // $resultPM = $PM_SOAP->cancelCase($locCase->fields['case_guid']); //, $_POST['plugin_processmaker_del_index'], $_POST['plugin_processmaker_users_id'] ) ; - // if ($resultPM->status_code === 0) { - // //$locCase = new PluginProcessmakerCase; - // //$locCase->getFromDB($_POST['cases_id']); - // if ($locCase->cancelCase()) { - // Session::addMessageAfterRedirect($LANG['processmaker']['item']['case']['cancelled'], true, INFO); - // } else { - // Session::addMessageAfterRedirect($LANG['processmaker']['item']['case']['errorcancelled'], true, ERROR); - // } - // } else { - // if ($resultPM->status_code == 100 && $locCase->deleteCase()) { // case is draft then delete it - // // request delete from pm itself - // $PM_SOAP->login(true); - - // $resultPM = $PM_SOAP->deleteCase($locCase->fields['case_guid']); - - // if ($resultPM->status_code == 0) { - // Session::addMessageAfterRedirect($LANG['processmaker']['item']['case']['deleted'], true, INFO); - // } else { - // Session::addMessageAfterRedirect($LANG['processmaker']['item']['case']['errordeleted'], true, ERROR); - // } - // } else { - // Session::addMessageAfterRedirect($LANG['processmaker']['item']['case']['errorcancelled']. " " . $resultPM->message, true, ERROR); - // } - // } + $locCase = new PluginProcessmakerCase; + $locCase->getFromDB($_REQUEST['cases_id']); + $glpi_item = new $_REQUEST['itemtype']; + $glpi_item->getFromDB($_REQUEST['items_id']); + $pm_task = new PluginProcessmakerTask($_REQUEST['tasktype']); + $pm_task->getFromDB($_REQUEST['tasks_id']); + $glpi_task = new $_REQUEST['tasktype']; + $glpi_task->getFromDB($_REQUEST['tasks_id']); + + // send notification now! + $pm_task->sendNotification('task_reminder', $glpi_task, $glpi_item, $locCase); } - - break; - } // to return to item diff --git a/inc/case.class.php b/inc/case.class.php index 91813b7..9ddf760 100644 --- a/inc/case.class.php +++ b/inc/case.class.php @@ -14,6 +14,8 @@ class PluginProcessmakerCase extends CommonDBTM { //static public $items_id = 'items_id'; // Field name static $rightname = 'plugin_processmaker_case'; + private $process = null; + const DRAFT = 'DRAFT'; const TO_DO = 'TO_DO'; const COMPLETED = 'COMPLETED'; @@ -211,6 +213,121 @@ function unpauseCase($delIndex, $userGuid) { } + /** + * Summary of unassignCase + * Will unassign the delIndex task, restoring the assigned group + * @param $delIndex int the delegation index + * @param $taskGuid string the GUID of the task + * @param $tasktype string the type of task (TicketTask, ChangeTask, ProblemTask) + * @param $tasks_id int the id of the task + * @param $itemtype string the type of the ITIL object (Ticket, Change, Problem) + * @return bool + */ + function unassignCase($delIndex, $taskGuid, $tasktype, $tasks_id, $itemtype, $options) { + global $PM_DB, $PM_SOAP; + + // un-claim task + // will unclaim the task + // to unclaim a task, we must un-assign the task in the APP_DELEGATION table + // and un-assign the task in glpi_itemtypeTask table + $groupname = ''; + + // should get the group that is assigned to the task in SELF_SERVICE assign type + $query = "SELECT TAS_GROUP_VARIABLE FROM TASK WHERE TAS_UID='".$taskGuid."' AND TAS_ASSIGN_TYPE='SELF_SERVICE' LIMIT 1;"; + foreach($PM_DB->request($query) as $pmGroup) { + // should have only one record + if ($pmGroup['TAS_GROUP_VARIABLE'] == '') { + // then we are in the self-service with a group in the TASK_USER table + // TU_RELATION=2 is groups and TU_TYPE=1 means normal (= not adhoc) + // then get the group name from the CONTENT table + $query = "SELECT CONTENT.CON_VALUE FROM TASK_USER + JOIN CONTENT ON CONTENT.CON_ID=TASK_USER.USR_UID AND CONTENT.CON_CATEGORY='GRP_TITLE' AND CONTENT.CON_LANG = 'en' + WHERE TASK_USER.TAS_UID='".$taskGuid."' AND TASK_USER.TU_RELATION=2 AND TASK_USER.TU_TYPE=1 LIMIT 1;"; + + foreach ($PM_DB->request($query) as $onlyrec) { + $groupname = $onlyrec['CON_VALUE']; + } + + + } else { + // then we are in the self-service with a case variable that contains either a group either an array of users + // array of users is not yet supported by PM plugin, as GLPI tasks have one and only one users_id_tech. + // group guid (in the case variable) must be retrieved from APP DATA + + // TODO + //////////////////////////////////////////////////////////////// + // Currently this case is not manageable by GLPI + // as GLPI needs at least a user or a group to be assigned to a task + // and when using the self-service value based assignment, there + // is a case variable that contains a list of users. This list of users cannot be mapped to a group in GLI + // OR may be we may create an artificial group in GLPI that would contains the list of users + //////////////////////////////////////////////////////////////// + + // and then we get the name of the group from the CONTENT table + //$query = "SELECT CON_VALUE FROM CONTENT + // WHERE CONTENT.CON_ID='$groupId' AND CONTENT.CON_CATEGORY='GRP_TITLE' AND CONTENT.CON_LANG='en' ;"; + + } + } + + $groups_id_tech = PluginProcessmakerProcessmaker::getGLPIGroupId($groupname); + + if ($groups_id_tech) { + // unclaim the case only when a GLPI group can be found + + $query = "UPDATE APP_DELEGATION SET USR_UID='', DEL_INIT_DATE=NULL, USR_ID=0 WHERE APP_NUMBER=".$this->getID()." AND DEL_INDEX=$delIndex;"; + $PM_DB->query($query); + + $glpi_task = new $tasktype; + $glpi_task->getFromDB($tasks_id); + $foreignkey = getForeignKeyFieldForItemType( $itemtype ); + + $donotif = PluginProcessmakerNotificationTargetProcessmaker::saveNotificationState(false); // do not send notification yet + $glpi_task->update( ['id' => $glpi_task->getID(), + $foreignkey => $glpi_task->fields[$foreignkey], + 'users_id_tech' => 0, + 'groups_id_tech' => $groups_id_tech, + 'update' => true] ); + PluginProcessmakerNotificationTargetProcessmaker::restoreNotificationState($donotif); + + // send notification now! + $pm_task = new PluginProcessmakerTask($tasktype); + $pm_task->getFromDB($tasks_id); + $glpi_item = new $itemtype; + $glpi_item->getFromDB($glpi_task->fields[$foreignkey]); + $pm_task->sendNotification('task_unclaim', $glpi_task, $glpi_item, $this); + + // create an information task and add comment + $pm_process = $this->getProcess(); + $dbu = new DbUtils; + $info = __('Task un-claimed!
Case: %s
Task: "%s" has been un-assigned from "%s" and assigned to "%s" group.
Reason: %s', 'processmaker'); + $info .= "getID()."\'>getID()."\'>"; + $taskCat = new TaskCategory; + $taskCat->getFromDB( $glpi_task->fields['taskcategories_id'] ); + $info = sprintf($info, + $this->getNameID(['forceid' => true]), + DropdownTranslation::getTranslatedValue($glpi_task->fields['taskcategories_id'], 'TaskCategory', 'name', $_SESSION['glpilanguage'], $taskCat->fields['name']), + Html::clean($dbu->getUserName(isset($glpi_task->oldvalues['users_id_tech']) ? $glpi_task->oldvalues['users_id_tech'] : 0)), + Html::clean($groupname), + $options['comment'] + ); + + $glpi_task->add([$foreignkey => $glpi_task->fields[$foreignkey], + 'is_private' => 1, + 'taskcategories_id' => $pm_process->fields['taskcategories_id'], + 'content' => $info, + 'users_id' => $PM_SOAP->taskWriter, + 'state' => Planning::INFO, + 'users_id_tech' => Session::getLoginUserID(), + ]); + + return true; + } + + return false; + } + + /** * Summary of reassignCase * @param mixed $delIndex @@ -220,7 +337,7 @@ function unpauseCase($delIndex, $userGuid) { * @param mixed $users_id_target * @return mixed */ - function reassignCase($delIndex, $taskGuid, $delThread, $users_id_source, $users_id_target) { + function reassignCase($delIndex, $taskGuid, $delThread, $users_id_source, $users_id_target, $options) { global $PM_SOAP; $users_guid_source = ''; // by default if ($users_id_source !== 0) { // when task is not 'to be claimed' @@ -243,7 +360,7 @@ function reassignCase($delIndex, $taskGuid, $delThread, $users_id_source, $users break; } } - $this->reassignTask($delIndex, $newDelIndex, $delThread, $newDelThread, $users_id_target ); + $this->reassignTask($delIndex, $newDelIndex, $delThread, $newDelThread, $users_id_target, $options); return true; } return false; @@ -256,15 +373,15 @@ function reassignCase($delIndex, $taskGuid, $delThread, $users_id_source, $users * @param mixed $newDelIndex * @param mixed $newTech */ - public function reassignTask ($delIndex, $newDelIndex, $delThread, $newDelThread, $newTech) { - global $DB; + public function reassignTask ($delIndex, $newDelIndex, $delThread, $newDelThread, $newTech, $options) { + global $DB, $PM_SOAP; $dbu = new DbUtils; $pm_task_row = $dbu->getAllDataFromTable(PluginProcessmakerTask::getTable(), ['plugin_processmaker_cases_id' => $this->getID(), 'del_index' => $delIndex, 'del_thread' => $delThread]); if ($pm_task_row && count($pm_task_row) == 1) { $pm_task_row = array_shift($pm_task_row); $glpi_task = new $pm_task_row['itemtype']; - $glpi_task->getFromDB( $pm_task_row['items_id'] ); + $glpi_task->getFromDB($pm_task_row['items_id']); $itilobject_itemtype = $this->fields['itemtype']; $foreignkey = getForeignKeyFieldForItemType( $itilobject_itemtype ); @@ -281,39 +398,67 @@ public function reassignTask ($delIndex, $newDelIndex, $delThread, $newDelThread // then update the delIndex and delThread $query = "UPDATE glpi_plugin_processmaker_tasks SET del_index = $newDelIndex, del_thread = $newDelThread WHERE id={$pm_task_row['id']}; "; - $res = $DB->query($query); - - // Notification management - // search if at least one active notification is existing for that pm task with that event 'task_update_'.$glpi_task->fields['taskcategories_id'] - $res = PluginProcessmakerNotificationTargetTask::getNotifications('task_update', $glpi_task->fields['taskcategories_id'], $this->fields['entities_id']); - if ($res['notifications'] && count($res['notifications']) > 0) { - $pm_task = new PluginProcessmakerTask($pm_task_row['itemtype']); - $pm_task->getFromDB($pm_task_row['items_id']); - NotificationEvent::raiseEvent($res['event'], - $pm_task, - ['plugin_processmaker_cases_id' => $this->getID(), - 'itemtype' => $pm_task_row['itemtype'], - 'task_id' => $glpi_task->getID(), - 'old_users_id_tech' => $glpi_task->oldvalues['users_id_tech'], - 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0, - 'entities_id' => $this->fields['entities_id'], - 'case' => $this - ]); + $DB->query($query); + + // send notification now! + $pm_task = new PluginProcessmakerTask($pm_task_row['itemtype']); + $pm_task->getFromDB($pm_task_row['items_id']); + $glpi_item = new $itilobject_itemtype; + $glpi_item->getFromDB($glpi_task->fields[$foreignkey]); + $pm_task->sendNotification('task_reassign', $glpi_task, $glpi_item, $this); + + // create an information task and add comment + $pm_process = $this->getProcess(); + $old_users_tech_id = isset($glpi_task->oldvalues['users_id_tech']) ? $glpi_task->oldvalues['users_id_tech'] : 0; + $taskCat = new TaskCategory; + $taskCat->getFromDB( $glpi_task->fields['taskcategories_id'] ); + $task_name = DropdownTranslation::getTranslatedValue($glpi_task->fields['taskcategories_id'], 'TaskCategory', 'name', $_SESSION['glpilanguage'], $taskCat->fields['name']); + $new_tech_name = Html::clean($dbu->getUserName($newTech)); + if ($old_users_tech_id) { + $info = __('Task re-assigned!
Case: %s
Task: "%s" has been re-assigned from "%s" to "%s".
Reason: %s', 'processmaker'); + $info = sprintf($info, + $this->getNameID(['forceid' => true]), + $task_name, + Html::clean($dbu->getUserName(isset($glpi_task->oldvalues['users_id_tech']) ? $glpi_task->oldvalues['users_id_tech'] : 0)), + $new_tech_name, + $options['comment'] + ); } else { - $item = new $itilobject_itemtype; - $item->getFromDB($glpi_task->fields[$foreignkey]); - NotificationEvent::raiseEvent('update_task', - $item, - ['plugin_processmaker_cases_id' => $this->getID(), - 'itemtype' => $pm_task_row['itemtype'], - 'task_id' => $glpi_task->getID(), - 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0 - ]); + $info = __('Task assigned!
Case: %s
Task: "%s" has been assigned to "%s".
Reason: %s', 'processmaker'); + $info = sprintf($info, + $this->getNameID(['forceid' => true]), + $task_name, + $new_tech_name, + $options['comment'] + ); } + $info .= "getID()."\'>getID()."\'>"; + + $glpi_task->add([$foreignkey => $glpi_task->fields[$foreignkey], + 'is_private' => 1, + 'taskcategories_id' => $pm_process->fields['taskcategories_id'], + 'content' => $info, + 'users_id' => $PM_SOAP->taskWriter, + 'state' => Planning::INFO, + 'users_id_tech' => Session::getLoginUserID(), + ]); } } + /** + * Summary of getProcess + * Returns process object + * @return bool|PluginProcessmakerProcess + */ + function getProcess() { + $pm_process = new PluginProcessmakerProcess; + if (!$this->process && $pm_process->getFromDB($this->fields['plugin_processmaker_processes_id'])) { + $this->process = $pm_process; + } + return $this->process; + } + /** * Summary of showCaseProperties */ @@ -791,12 +936,13 @@ private function deleteTasks() { $ret = false; $query = "DELETE FROM glpi_".strtolower($this->fields['itemtype'])."tasks WHERE id IN (SELECT items_id FROM glpi_plugin_processmaker_tasks WHERE plugin_processmaker_cases_id='".$this->fields['id']."')"; - if ($DB->query( $query )) { - $query = "DELETE FROM glpi_plugin_processmaker_tasks WHERE plugin_processmaker_cases_id='".$this->fields['id']."'"; - if ($DB->query( $query )) { - $ret = true; - } + // also delete all information tasks that are linked to this case + $query2 = "DELETE FROM glpi_".strtolower($this->fields['itemtype'])."tasks WHERE content REGEXP 'fields['id']."\'>'"; + $query3 = "DELETE FROM glpi_plugin_processmaker_tasks WHERE plugin_processmaker_cases_id='".$this->fields['id']."'"; + if ($DB->query($query) && $DB->query($query2) && $DB->query($query3)) { + $ret = true; } + return $ret; } diff --git a/inc/notificationtargetprocessmaker.class.php b/inc/notificationtargetprocessmaker.class.php index bd2f023..6076fb9 100644 --- a/inc/notificationtargetprocessmaker.class.php +++ b/inc/notificationtargetprocessmaker.class.php @@ -10,7 +10,12 @@ * @version 1.0 * @author MoronO */ -class PluginProcessmakerNotificationTargetProcessmaker extends NotificationTarget { +class PluginProcessmakerNotificationTargetProcessmaker extends NotificationTargetCommonITILObject { + + const PM_USER_TYPE = 1000; + + const OLD_TASK_TECH_IN_CHARGE = 1; + /** * Summary of saveNotificationState diff --git a/inc/notificationtargettask.class.php b/inc/notificationtargettask.class.php index 3e1868f..1deff80 100644 --- a/inc/notificationtargettask.class.php +++ b/inc/notificationtargettask.class.php @@ -12,18 +12,33 @@ // Class NotificationTarget class PluginProcessmakerNotificationTargetTask extends PluginProcessmakerNotificationTargetProcessmaker { + /** * Summary of getDefaultEvents * @return array[] */ private static function getDefaultEvents() { - return ['task_add' => ['event' => 'task_add_', 'label' => __('New task')], - 'task_update' => ['event' => 'task_update_', 'label' => __('Update of a task')], - 'task_done' => ['event' => 'task_done_', 'label' => __('Task done')] + return ['task_add' => ['event' => 'task_add_', 'label' => __('Task add'), 'glpi' => 'add_task'], + 'task_reassign' => ['event' => 'task_reassign_', 'label' => __('Task re-assign'), 'glpi' => 'update_task'], + 'task_unclaim' => ['event' => 'task_unclaim_', 'label' => __('Task un-claim'), 'glpi' => 'update_task'], + 'task_done' => ['event' => 'task_done_', 'label' => __('Task done'), 'glpi' => 'update_task'], + 'task_reminder' => ['event' => 'task_reminder_', 'label' => __('Task reminder'), 'glpi' => 'update_task'] ]; } + /** + * Summary of getDefaultGLPIEvents + * Will return the GLPI default task event matching a self type + * @param string $type + * @return string + */ + static function getDefaultGLPIEvents($type) { + $events = self::getDefaultEvents(); + return $events[$type]['glpi']; + } + + /** * Summary of getNotification * @param mixed $evt @@ -54,9 +69,7 @@ public function getEvents() { $table = PluginProcessmakerTaskCategory::getTable(); $ptable = PluginProcessmakerProcess::getTable(); $query = "SELECT $table.taskcategories_id AS taskcat, $ptable.taskcategories_id AS ptaskcat FROM $table - LEFT JOIN $ptable ON $ptable.id=$table.plugin_processmaker_processes_id - WHERE $table.is_active = 1 - AND $ptable.is_active = 1"; + LEFT JOIN $ptable ON $ptable.id=$table.plugin_processmaker_processes_id"; $ptaskcats = []; $temp = new TaskCategory; @@ -67,9 +80,9 @@ public function getEvents() { } $temp->getFromDB($row['taskcat']); - $actions[$defaultEvents['task_add']['event'].$row['taskcat']] = $ptaskcats[$row['ptaskcat']]." > ".$temp->fields['name'].": " . $defaultEvents['task_add']['label']; - $actions[$defaultEvents['task_update']['event'].$row['taskcat']] = $ptaskcats[$row['ptaskcat']]." > ".$temp->fields['name'].": " . $defaultEvents['task_update']['label']; - $actions[$defaultEvents['task_done']['event'].$row['taskcat']] = $ptaskcats[$row['ptaskcat']]." > ".$temp->fields['name'].": " . $defaultEvents['task_done']['label']; + foreach($defaultEvents as $events) { + $actions[$events['event'].$row['taskcat']] = $ptaskcats[$row['ptaskcat']]." > ".$temp->fields['name'].": " . $events['label']; + } } return $actions; @@ -82,7 +95,7 @@ public function getEvents() { public function addDataForTemplate($event, $options = []) { global $PM_DB, $CFG_GLPI; - if (!isset($options['case'])) { + if (!isset($options['case']) || $options['case'] == null) { $mycase = new PluginProcessmakerCase; $mycase->getFromDB($options['plugin_processmaker_cases_id']); $options['case'] = $mycase; @@ -98,8 +111,8 @@ public function addDataForTemplate($event, $options = []) { $this->data['##task.action##'] = $events[$baseevent]['label']; // task category information: meta data on task - $tmp_taskcatinfo['name'] = DropdownTranslation::getTranslatedValue( $taskcat_id, 'TaskCategory', 'name'); - $tmp_taskcatinfo['comment'] = DropdownTranslation::getTranslatedValue( $taskcat_id, 'TaskCategory', 'comment'); + $tmp_taskcatinfo['name'] = DropdownTranslation::getTranslatedValue($taskcat_id, 'TaskCategory', 'name'); + $tmp_taskcatinfo['comment'] = DropdownTranslation::getTranslatedValue($taskcat_id, 'TaskCategory', 'comment'); $this->data['##task.categoryid##'] = $taskcat_id; $this->data['##task.category##'] = $tmp_taskcatinfo['name']; $this->data['##task.categorycomment##'] = $tmp_taskcatinfo['comment']; @@ -133,13 +146,18 @@ public function addDataForTemplate($event, $options = []) { $this->data['##task.lastupdater##'] = Html::clean($dbu->getUserName($taskobj->fields['users_id_editor'])); // task technician - $this->data['##task.user##'] = Html::clean($dbu->getUserName($taskobj->fields['users_id_tech'])); + $this->data['##task.user##'] = ''; + $this->data['##task.user.login##'] = ''; // by default $tech = new User; - $tech->getFromDB($taskobj->fields['users_id_tech']); - $this->data['##task.user.login##'] = $tech->fields['name']; - if (isset($options['old_users_id_tech'])) { - $oldtech = new User; - $oldtech->getFromDB($options['old_users_id_tech']); + if ($taskobj->fields['users_id_tech'] > 0 + && $tech->getFromDB($taskobj->fields['users_id_tech'])) { + $this->data['##task.user##'] = Html::clean($dbu->getUserName($taskobj->fields['users_id_tech'])); + $this->data['##task.user.login##'] = $tech->fields['name']; + } + $oldtech = new User; + if (isset($options['old_users_id_tech']) + && $options['old_users_id_tech'] > 0 + && $oldtech->getFromDB($options['old_users_id_tech'])) { $this->data['##task.former.user##'] = Html::clean($dbu->getUserName($options['old_users_id_tech'])); $this->data['##task.former.user.login##'] = $oldtech->fields['name']; } @@ -215,6 +233,11 @@ function addAdditionalTargets($event = '') { $this->addTarget(Notification::TASK_ASSIGN_TECH, __('Technician in charge of the task')); $this->addTarget(Notification::TASK_ASSIGN_GROUP, __('Group in charge of the task')); + $this->addTarget(Notification::OBSERVER_GROUP, __('Watcher group')); + $this->addTarget(Notification::OBSERVER, __('Watcher')); + + $this->addTarget(Notification::AUTHOR, __('Requester'), PluginProcessmakerNotificationTargetProcessmaker::PM_USER_TYPE); + if (strpos($event, 'task_update_') === 0) { $this->addTarget(Notification::OLD_TECH_IN_CHARGE, __('Former technician in charge of the task')); @@ -230,12 +253,35 @@ function addAdditionalTargets($event = '') { */ function addSpecificTargets($data, $options) { - //Look for all targets whose type is Notification::ITEM_USER + $obj = $this->obj; + $this->obj = $options['obj']; + switch ($data['type']) { + case PluginProcessmakerNotificationTargetProcessmaker::PM_USER_TYPE : + + switch ($data['items_id']) { + + //Send to the requester of the ITIL object + case Notification::AUTHOR : + $this->addItemAuthor(); + break; + } + break; + case Notification::USER_TYPE : switch ($data['items_id']) { + //Notification to the ITIL object's observer group + case Notification::OBSERVER_GROUP : + $this->addLinkedGroupByType(CommonITILActor::OBSERVER); + break; + + //Notification to the ITIL object's observer user + case Notification::OBSERVER : + $this->addLinkedUserByType(CommonITILActor::OBSERVER); + break; + //Send to the ITIL object followup author case Notification::TASK_ASSIGN_TECH : $this->addTaskAssignUser($options); @@ -253,72 +299,15 @@ function addSpecificTargets($data, $options) { } } - } - - - /** - * Add user assigned to task - * - * @param array $options Options - * - * @return void - */ - function addTaskAssignUser($options = []) { - global $DB; - - // In case of delete task pass user id - if (isset($options['task_users_id_tech'])) { - $query = $this->getDistinctUserSql()." - FROM `glpi_users` ". - $this->getProfileJoinSql()." - WHERE `glpi_users`.`id` = '".$options['task_users_id_tech']."'"; - - foreach ($DB->request($query) as $data) { - $this->addToRecipientsList($data); - } - } else if (isset($options['task_id'])) { - $dbu = new DbUtils; - $tasktable = $dbu->getTableForItemType($options['itemtype']); //getTableForItemType($this->obj->getType().'Task'); - - $query = $this->getDistinctUserSql()." - FROM `$tasktable` - INNER JOIN `glpi_users` - ON (`glpi_users`.`id` = `$tasktable`.`users_id_tech`)". - $this->getProfileJoinSql()." - WHERE `$tasktable`.`id` = '".$options['task_id']."'"; - foreach ($DB->request($query) as $data) { - $this->addToRecipientsList($data); - } - } + $this->obj = $obj; } /** - * Add group assigned to the task - * - * @param array $options Options - * - * @return void + * Summary of addOldAssignTechnician + * @param $options */ - function addTaskAssignGroup($options = []) { - global $DB; - - // In case of delete task pass user id - if (isset($options['task_groups_id_tech'])) { - $this->addForGroup(0, $options['task_groups_id_tech']); - - } else if (isset($options['task_id'])) { - $dbu = new DbUtils; - $tasktable = $dbu->getTableForItemType($options['itemtype']); //getTableForItemType($this->obj->getType().'Task'); - foreach ($DB->request([$tasktable, 'glpi_groups'], "`glpi_groups`.`id` = `$tasktable`.`groups_id_tech` - AND `$tasktable`.`id` = '".$options['task_id']."'") as $data) { - $this->addForGroup(0, $data['groups_id_tech']); - } - } - } - - function addOldAssignTechnician($options = []) { global $DB; diff --git a/inc/processmaker.class.php b/inc/processmaker.class.php index ecdfb89..4348c71 100644 --- a/inc/processmaker.class.php +++ b/inc/processmaker.class.php @@ -84,7 +84,7 @@ class PluginProcessmakerProcessmaker extends CommonDBTM { private $pmWorkspace = ""; private $pmAdminSession = false; - private $taskWriter = 0; + var $taskWriter = 0; private $pm_group_guid = ''; // guid for default user group in Process Maker is used for all GLPI user synchronization into ProcessMaker var $lasterror; var $lang; @@ -1700,13 +1700,7 @@ public function addTask($cases_id, $itemtype, $items_id, $caseInfo, $delIndex, $ $groupname = $onlyrec['CON_VALUE']; } - $groups_id_tech = 0; - $query = "SELECT id AS glpi_group_id FROM glpi_groups WHERE name LIKE '$groupname';"; - $res = $DB->query($query); - if ($DB->numrows($res) > 0) { - $row = $DB->fetch_array( $res ); - $groups_id_tech = $row['glpi_group_id']; - } + $groups_id_tech = self::getGLPIGroupId($groupname); } else { // adds the user tech to ticket watcher if neccessary @@ -1730,7 +1724,7 @@ public function addTask($cases_id, $itemtype, $items_id, $caseInfo, $delIndex, $ $input['is_private'] = 0; $input['actiontime'] = 0; - $input['state'] = 1; // == TO_DO + $input['state'] = Planning::TODO; // == TO_DO $input['users_id_tech'] = 0; // by default as it can't be empty if ($techId) { $input['users_id_tech'] = $techId; @@ -1769,31 +1763,13 @@ public function addTask($cases_id, $itemtype, $items_id, $caseInfo, $delIndex, $ // send notification if needed for new task as now we have the PluginProcessmakerTask in the DB $donotif = PluginProcessmakerNotificationTargetProcessmaker::saveNotificationState($options['notif']); - // Notification management - $item = new $itemtype; - $item->getFromDB($items_id); - // search if at least one active notification is existing for that pm task with that event 'task_update_'.$glpi_task->fields['taskcategories_id'] - $res = PluginProcessmakerNotificationTargetTask::getNotifications('task_add', $glpi_task->fields['taskcategories_id'], $item->fields['entities_id']); - if ($res['notifications'] && count($res['notifications']) > 0) { - $pm_task = new PluginProcessmakerTask($glpi_task->getType()); - $pm_task->getFromDB($glpi_task->getId()); - NotificationEvent::raiseEvent($res['event'], - $pm_task, - ['plugin_processmaker_cases_id' => $cases_id, - 'itemtype' => $glpi_task->getType(), - 'task_id' => $glpi_task->getID(), - 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0, - 'entities_id' => $item->fields['entities_id'] - ]); - } else { - NotificationEvent::raiseEvent('add_task', - $item, - ['plugin_processmaker_cases_id' => $cases_id, - 'itemtype' => $itemtype, - 'task_id' => $glpi_task->getID(), - 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0 - ]); - } + + // send notification now! + $pm_task = new PluginProcessmakerTask($glpi_task->getType()); + $pm_task->getFromDB($glpi_task->getId()); + $glpi_item = new $itemtype; + $glpi_item->getFromDB($items_id); + $pm_task->sendNotification('task_add', $glpi_task, $glpi_item); PluginProcessmakerNotificationTargetProcessmaker::restoreNotificationState($donotif); } @@ -1801,6 +1777,24 @@ public function addTask($cases_id, $itemtype, $items_id, $caseInfo, $delIndex, $ } + /** + * Summary of getGLPIGroupId + * returns GLPI group id from pm group name + * returns false when not found + * @param string $pmGroupName + * @return bool|integer + */ + static function getGLPIGroupId(string $pmGroupName) { + global $DB; + $query = "SELECT id AS glpi_group_id FROM glpi_groups WHERE name LIKE '$pmGroupName';"; + $res = $DB->query($query); + if ($DB->numrows($res) > 0) { + $row = $DB->fetch_array($res); + return $row['glpi_group_id']; + } + return false; + } + /** * Summary of add1stTask * adds a GLPI task to ticket @@ -2003,9 +1997,9 @@ public function solveTask($cases_id, $delIndex, $options = []) { $glpi_task = new $row['itemtype']; $glpi_task->getFromDB( $row['items_id'] ); - $hostItem = $dbu->getItemForItemtype( $glpi_task->getItilObjectItemType() ); + $glpi_item = $dbu->getItemForItemtype( $glpi_task->getItilObjectItemType() ); $itemFKField = getForeignKeyFieldForItemType( $glpi_task->getItilObjectItemType() ); - $hostItem->getFromDB( $glpi_task->fields[ $itemFKField ] ); + $glpi_item->getFromDB( $glpi_task->fields[ $itemFKField ] ); // change current glpi_currenttime to be sure that date_mode for solved task will not be identical than date_mode of the newly started task $saved_date_time = $_SESSION["glpi_currenttime"]; @@ -2027,7 +2021,7 @@ public function solveTask($cases_id, $delIndex, $options = []) { } } - $duration = $this->computeTaskDuration($options['begin'], $options['end'], $hostItem->fields['entities_id']); + $duration = $this->computeTaskDuration($options['begin'], $options['end'], $glpi_item->fields['entities_id']); if ($options['txtToAppend'] != "") { $options['txtToAppend'] = "\n
".$options['txtToAppend']; } @@ -2035,7 +2029,7 @@ public function solveTask($cases_id, $delIndex, $options = []) { 'state' => $options['toInformation'] ? Planning::INFO : Planning::DONE, 'begin' => $options['begin'], 'end' => $options['end'], - $itemFKField => $hostItem->getID(), + $itemFKField => $glpi_item->getID(), 'actiontime' => $duration, 'users_id_tech' => (isset($options['users_id_tech']) ? $options['users_id_tech'] : Session::getLoginUserID()), //'groups_id_tech' => 0, @@ -2051,29 +2045,12 @@ public function solveTask($cases_id, $delIndex, $options = []) { // send notification if needed for new task as now the PluginProcessmakerTask has been updated in the DB $donotif = PluginProcessmakerNotificationTargetProcessmaker::saveNotificationState($options['notif']); - // Notification management - // search if at least one active notification is existing for that pm task with that event 'task_update_'.$glpi_task->fields['taskcategories_id'] - $res = PluginProcessmakerNotificationTargetTask::getNotifications('task_done', $glpi_task->fields['taskcategories_id'], $hostItem->fields['entities_id']); - if ($res['notifications'] && count($res['notifications']) > 0) { - $pm_task = new PluginProcessmakerTask($glpi_task->getType()); - $pm_task->getFromDB($glpi_task->getId()); - NotificationEvent::raiseEvent($res['event'], - $pm_task, - ['plugin_processmaker_cases_id' => $cases_id, - 'itemtype' => $glpi_task->getType(), - 'task_id' => $glpi_task->getID(), - 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0, - 'entities_id' => $hostItem->fields['entities_id'] - ]); - } else { - NotificationEvent::raiseEvent('update_task', - $hostItem, - ['plugin_processmaker_cases_id' => $cases_id, - 'itemtype' => $hostItem->getType(), - 'task_id' => $glpi_task->getID(), - 'is_private' => isset($glpi_task->fields['is_private']) ? $glpi_task->fields['is_private'] : 0 - ]); - } + + // send notification now! + $pm_task = new PluginProcessmakerTask($glpi_task->getType()); + $pm_task->getFromDB($glpi_task->getId()); + $pm_task->sendNotification('task_done', $glpi_task, $glpi_item); + PluginProcessmakerNotificationTargetProcessmaker::restoreNotificationState($donotif); // restore current glpi time @@ -2203,10 +2180,24 @@ static function multiexplode($delimiters, $string) { */ public static function pre_show_item_processmaker($params) { - if (!is_array($params['item']) && is_subclass_of( $params['item'], 'CommonITILTask')) { + if (!is_array($params['item']) && is_subclass_of($params['item'], 'CommonITILTask')) { // must check if Task is bound to a PM task $pmTask = new PluginProcessmakerTask($params['item']->getType()); - if ($pmTask->getFromDB($params['item']->getId())) {//$pmTask->getFromDBByQuery("WHERE itemtype='".$params['item']->getType()."' and items_id=".$params['item']->getId())) { + $is_pmtask = $pmTask->getFromDB($params['item']->getId()); + if (!$is_pmtask && $params['item']->fields['state'] == Planning::INFO) { + // look if it is a meta task for this process + // means a re-assign or an un-claim task info + $pm_process = new PluginProcessmakerProcess; + if ($pm_process->getFromDBByQuery( " WHERE `taskcategories_id` = ".$params['item']->fields['taskcategories_id'])) { + // then look into content to get case id + $re = '//'; + if (preg_match($re, $params['item']->fields['content'], $matches)) { + // here we get the case id and the task id + $is_pmtask = $pmTask->getFromDB($matches['taskid']); + } + } + } + if ($is_pmtask) { $params['item']->fields['can_edit'] = false; // to prevent task edition // replace ##ticket.url##_PluginProcessmakerCase$processmakercases by a setActiveTab to the Case panel diff --git a/inc/task.class.php b/inc/task.class.php index 3700533..d254674 100644 --- a/inc/task.class.php +++ b/inc/task.class.php @@ -61,6 +61,15 @@ function getFromDB($items_id) { return false; } + + /** + * Summary of getPMTaskID + * @return mixed + */ + function getPMTaskID() { + return $this->fields['items_id']; + } + /** * Summary of getToDoTasks * returns all 'to do' tasks associated with this case @@ -319,31 +328,21 @@ static function displayTabContentForItem(CommonGLPI $case, $tabnum = 1, $withtem } if (isset($currentUser)) { - if (/*$currentUser->userId &&*/ $task[$tabnum]['del_index']) { + if ($task[$tabnum]['del_index']) { // to load users for task re-assign only when task is not a sub-case echo "
".__('Loading...')."
"; - echo ""; - //} else { - // // manages the claim - // // current task is to be claimed - // // get the assigned group to the item task - // $itemtask = $dbu->getItemForItemtype( $task[$tabnum]['itemtype'] ); - // $itemtask->getFromDB( $task[$tabnum]['items_id'] ); - // // check if this group can be found in the current user's groups - // if (!isset($_SESSION['glpigroups']) || !in_array( $itemtask->fields['groups_id_tech'], $_SESSION['glpigroups'] )) { - // $hide_claim_button=true; - // } + echo ""; } if (!$currentUser->userId || !$task[$tabnum]['del_index']) { @@ -364,18 +363,6 @@ static function displayTabContentForItem(CommonGLPI $case, $tabnum = 1, $withtem $csrf = Session::getNewCSRFToken(); - //echo "
"; $url = $PM_SOAP->serverURL ."/cases/cases_Open?sid=".$PM_SOAP->getPMSessionID() ."&APP_UID=".$case->fields['case_guid'] @@ -413,6 +400,42 @@ function urldecode(url) { } }); "); + } + + + /** + * Summary of sendNotification + * Will send either dedicated notification, or standard one + * @param string $type is 'task_add', 'task_reassign', 'task_done', 'task_reminder' + * @param CommonITILTask $task is the task (TicketTask,...) + * @param CommonITILObject $item is the ITIL item (Ticket,...) + * @param PluginProcessmakerCase $case is the case + */ + function sendNotification(string $type, CommonITILTask $task, CommonITILObject $item, PluginProcessmakerCase $case = null) { + // Notification management + // search if at least one active notification is existing for that pm task with that event 'task_update_'.$glpi_task->fields['taskcategories_id'] + $res = PluginProcessmakerNotificationTargetTask::getNotifications($type, $task->fields['taskcategories_id'], $item->fields['entities_id']); + if ($res['notifications'] && count($res['notifications']) > 0) { + NotificationEvent::raiseEvent($res['event'], + $this, + ['plugin_processmaker_cases_id' => $this->fields['plugin_processmaker_cases_id'], + 'itemtype' => $item->getType(), + 'task_id' => $task->getID(), + 'old_users_id_tech' => isset($task->oldvalues['users_id_tech']) ? $task->oldvalues['users_id_tech'] : 0, + 'is_private' => isset($task->fields['is_private']) ? $task->fields['is_private'] : 0, + 'entities_id' => $item->fields['entities_id'], + 'case' => $case, + 'obj' => $item + ]); + } else { + NotificationEvent::raiseEvent(PluginProcessmakerNotificationTargetTask::getDefaultGLPIEvents($type), + $item, + ['plugin_processmaker_cases_id' => $this->fields['plugin_processmaker_cases_id'], + 'itemtype' => $item->getType(), + 'task_id' => $task->getID(), + 'is_private' => isset($task->fields['is_private']) ? $task->fields['is_private'] : 0 + ]); + } } diff --git a/install/mysql/processmaker-empty.sql b/install/mysql/processmaker-empty.sql index 3ad90bb..3e8dd8d 100644 --- a/install/mysql/processmaker-empty.sql +++ b/install/mysql/processmaker-empty.sql @@ -81,7 +81,7 @@ CREATE TABLE IF NOT EXISTS `glpi_plugin_processmaker_configs` ( `domain` varchar(50) DEFAULT '', `maintenance` tinyint(1) NOT NULL DEFAULT '0', `ssl_verify` tinyint(1) NOT NULL DEFAULT '0', - `db_version` varchar(10) NOT NULL DEFAULT '3.4.9', + `db_version` varchar(10) NOT NULL DEFAULT '3.4.10', `max_cases_per_item` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/install/update.php b/install/update.php index febe0e5..78b4c60 100644 --- a/install/update.php +++ b/install/update.php @@ -44,6 +44,10 @@ function processmaker_update() { // will upgrade 3.3.8 to 3.4.9 include_once(GLPI_ROOT."/plugins/processmaker/install/update_3_3_8_to_3_4_9.php"); $new_version = update_3_3_8_to_3_4_9(); + case '3.4.9' : + // will upgrade 3.4.9 to 3.4.10 + include_once(GLPI_ROOT."/plugins/processmaker/install/update_3_4_9_to_3_4_10.php"); + $new_version = update_3_4_9_to_3_4_10(); } if (isset($new_version)) { diff --git a/install/update_3_4_9_to_3_4_10.php b/install/update_3_4_9_to_3_4_10.php new file mode 100644 index 0000000..365cbc1 --- /dev/null +++ b/install/update_3_4_9_to_3_4_10.php @@ -0,0 +1,12 @@ +query($query) or die("error when updating event field in glpi_notifications" . $DB->error()); + + return '3.4.10'; +} \ No newline at end of file diff --git a/processmaker.xml b/processmaker.xml index eb2dcc7..08e6123 100644 --- a/processmaker.xml +++ b/processmaker.xml @@ -27,7 +27,7 @@ 9.1 - 3.4.17 + 3.4.20 9.2 diff --git a/setup.php b/setup.php index 220dd33..94b4512 100644 --- a/setup.php +++ b/setup.php @@ -1,6 +1,6 @@ Date: Wed, 7 Oct 2020 11:29:36 +0200 Subject: [PATCH 10/15] Revert back $_REQUEST into $_POST Set version to 3.4.21 --- front/processmaker.form.php | 72 ++++++++++++++++++------------------- processmaker.xml | 2 +- setup.php | 2 +- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/front/processmaker.form.php b/front/processmaker.form.php index bf506a3..a038065 100644 --- a/front/processmaker.form.php +++ b/front/processmaker.form.php @@ -1,31 +1,31 @@ 0) { + if (isset($_POST['items_id']) && $_POST['items_id'] > 0) { // then this case will be bound to an item - if ($_REQUEST['plugin_processmaker_processes_id'] > 0) { + if ($_POST['plugin_processmaker_processes_id'] > 0) { - $resultCase = $PM_SOAP->startNewCase($_REQUEST['plugin_processmaker_processes_id'], $_REQUEST['itemtype'], $_REQUEST['items_id'], Session::getLoginUserID()); + $resultCase = $PM_SOAP->startNewCase($_POST['plugin_processmaker_processes_id'], $_POST['itemtype'], $_POST['items_id'], Session::getLoginUserID()); if ($resultCase->status_code == 0) { $case = new PluginProcessmakerCase; if ($case->getFromGUID($resultCase->caseId)) { $link = $case->getLinkURL(); - $task = new PluginProcessmakerTask($_REQUEST['itemtype'].'Task'); + $task = new PluginProcessmakerTask($_POST['itemtype'].'Task'); $task->getFromDBByQuery(" WHERE `plugin_processmaker_cases_id`=".$case->getID()); // normally there is only one and only one first task //$link .= '&forcetab=PluginProcessmakerTask$'.$task->getID(); Session::setActiveTab('PluginProcessmakerCase', 'PluginProcessmakerTask$'.$task->getID()); - $item = new $_REQUEST['itemtype']; - $item->getFromDB($_REQUEST['items_id']); + $item = new $_POST['itemtype']; + $item->getFromDB($_POST['items_id']); unset($_SERVER['REQUEST_URI']); // to prevent use of processmaker.form.php in NavigateList Session::initNavigateListItems('PluginProcessmakerCase', //TRANS : %1$s is the itemtype name, // %2$s is the name of the item (used for headings of a list) sprintf('%1$s = %2$s', - $_REQUEST['itemtype']::getTypeName(1), $item->fields["name"])); + $_POST['itemtype']::getTypeName(1), $item->fields["name"])); Html::redirect($link); } Html::back(); @@ -37,14 +37,14 @@ } } else { // the case is created before the ticket (used for post-only case creation before ticket creation) $pm_user_guid = PluginProcessmakerUser::getPMUserId( Session::getLoginUserID() ); - $resultCase = $PM_SOAP->newCase( $_REQUEST['plugin_processmaker_processes_id'], + $resultCase = $PM_SOAP->newCase( $_POST['plugin_processmaker_processes_id'], ['GLPI_ITEM_CAN_BE_SOLVED' => 0, 'GLPI_SELFSERVICE_CREATED' => '1', 'GLPI_ITEM_TYPE' => 'Ticket', 'GLPI_URL' => $CFG_GLPI['url_base'], // Specific to Tickets // GLPI_TICKET_TYPE will contains 1 (= incident) or 2 (= request) - 'GLPI_TICKET_TYPE' => $_REQUEST['type'], + 'GLPI_TICKET_TYPE' => $_POST['type'], 'GLPI_ITEM_REQUESTER_GLPI_ID' => Session::getLoginUserID(), 'GLPI_ITEM_REQUESTER_PM_ID' => $pm_user_guid ] ); @@ -53,7 +53,7 @@ // Must show it... // $rand = rand( ); - Html::redirect($CFG_GLPI['root_doc']."/plugins/processmaker/front/processmaker.helpdesk.form.php?processes_id=".$_REQUEST['plugin_processmaker_processes_id']."&case_guid=".$resultCase->caseId."&rand=$rand&itilcategories_id=".$_REQUEST["itilcategories_id"]."&type=".$_REQUEST["type"]."&entities_id=".$_REQUEST['entities_id']); + Html::redirect($CFG_GLPI['root_doc']."/plugins/processmaker/front/processmaker.helpdesk.form.php?processes_id=".$_POST['plugin_processmaker_processes_id']."&case_guid=".$resultCase->caseId."&rand=$rand&itilcategories_id=".$_POST["itilcategories_id"]."&type=".$_POST["type"]."&entities_id=".$_POST['entities_id']); } else { Session::addMessageAfterRedirect( PluginProcessmakerProcessmaker::getPMErrorMessage($resultCase->status_code)."
$resultCase->message ($resultCase->status_code)", true, ERROR); @@ -64,48 +64,48 @@ break; case 'reassign_reminder' : - if (isset($_REQUEST['reassign'])) { - // here we should re-assign the current task to $_REQUEST['users_id_recipient'] + if (isset($_POST['reassign'])) { + // here we should re-assign the current task to $_POST['users_id_recipient'] $locCase = new PluginProcessmakerCase; - $locCase->getFromDB($_REQUEST['cases_id']); - if ($_REQUEST['users_id_recipient'] != 0) { + $locCase->getFromDB($_POST['cases_id']); + if ($_POST['users_id_recipient'] != 0) { // we are assigning a new tech to a task - $pmResponse = $locCase->reassignCase($_REQUEST['delIndex'], - $_REQUEST['taskGuid'], - $_REQUEST['delThread'], - $_REQUEST['users_id'], - $_REQUEST['users_id_recipient'], - ['comment' => $_REQUEST['comment']]); + $pmResponse = $locCase->reassignCase($_POST['delIndex'], + $_POST['taskGuid'], + $_POST['delThread'], + $_POST['users_id'], + $_POST['users_id_recipient'], + ['comment' => $_POST['comment']]); if ($pmResponse) { Session::addMessageAfterRedirect(__('Task re-assigned!', 'processmaker'), true, INFO); } else { Session::addMessageAfterRedirect(__('Error re-assigning task: ', 'processmaker').$pmResponse->message, true, ERROR); } - } elseif ($_REQUEST['users_id_recipient'] == 0) { + } elseif ($_POST['users_id_recipient'] == 0) { // we are unassigning a task, i.e.: task un-claim - $pmResponse = $locCase->unassignCase($_REQUEST['delIndex'], - $_REQUEST['taskGuid'], - $_REQUEST['tasktype'], - $_REQUEST['tasks_id'], - $_REQUEST['itemtype'], - ['comment' => $_REQUEST['comment']]); + $pmResponse = $locCase->unassignCase($_POST['delIndex'], + $_POST['taskGuid'], + $_POST['tasktype'], + $_POST['tasks_id'], + $_POST['itemtype'], + ['comment' => $_POST['comment']]); if ($pmResponse) { Session::addMessageAfterRedirect(__('Task un-claimed!', 'processmaker'), true, INFO); } else { Session::addMessageAfterRedirect(__("Can't un-claim task! Verify 'Assignement Rules' in the process definition.", 'processmaker'), true, ERROR); } } - } elseif (isset($_REQUEST['reminder'])) { + } elseif (isset($_POST['reminder'])) { // send notification remider as requested for this task $locCase = new PluginProcessmakerCase; - $locCase->getFromDB($_REQUEST['cases_id']); - $glpi_item = new $_REQUEST['itemtype']; - $glpi_item->getFromDB($_REQUEST['items_id']); - $pm_task = new PluginProcessmakerTask($_REQUEST['tasktype']); - $pm_task->getFromDB($_REQUEST['tasks_id']); - $glpi_task = new $_REQUEST['tasktype']; - $glpi_task->getFromDB($_REQUEST['tasks_id']); + $locCase->getFromDB($_POST['cases_id']); + $glpi_item = new $_POST['itemtype']; + $glpi_item->getFromDB($_POST['items_id']); + $pm_task = new PluginProcessmakerTask($_POST['tasktype']); + $pm_task->getFromDB($_POST['tasks_id']); + $glpi_task = new $_POST['tasktype']; + $glpi_task->getFromDB($_POST['tasks_id']); // send notification now! $pm_task->sendNotification('task_reminder', $glpi_task, $glpi_item, $locCase); diff --git a/processmaker.xml b/processmaker.xml index 08e6123..2587759 100644 --- a/processmaker.xml +++ b/processmaker.xml @@ -27,7 +27,7 @@ 9.1 - 3.4.20 + 3.4.21 9.2 diff --git a/setup.php b/setup.php index 94b4512..ca7f34b 100644 --- a/setup.php +++ b/setup.php @@ -1,6 +1,6 @@ Date: Wed, 7 Oct 2020 11:32:17 +0200 Subject: [PATCH 11/15] Added a new case variable to be able to create information tasks GLPI_ITEM_INFORMATION_TASK Set version to 3.4.22 --- inc/case.class.php | 15 ++++++--- inc/processmaker.class.php | 63 +++++++++++++++++++++++++++++++------- processmaker.xml | 2 +- setup.php | 2 +- 4 files changed, 64 insertions(+), 18 deletions(-) diff --git a/inc/case.class.php b/inc/case.class.php index 9ddf760..05e3a24 100644 --- a/inc/case.class.php +++ b/inc/case.class.php @@ -224,7 +224,7 @@ function unpauseCase($delIndex, $userGuid) { * @return bool */ function unassignCase($delIndex, $taskGuid, $tasktype, $tasks_id, $itemtype, $options) { - global $PM_DB, $PM_SOAP; + global $PM_DB, $PM_SOAP, $DB; // un-claim task // will unclaim the task @@ -301,7 +301,7 @@ function unassignCase($delIndex, $taskGuid, $tasktype, $tasks_id, $itemtype, $op $pm_process = $this->getProcess(); $dbu = new DbUtils; $info = __('Task un-claimed!
Case: %s
Task: "%s" has been un-assigned from "%s" and assigned to "%s" group.
Reason: %s', 'processmaker'); - $info .= "getID()."\'>getID()."\'>"; + $info .= ""; $taskCat = new TaskCategory; $taskCat->getFromDB( $glpi_task->fields['taskcategories_id'] ); $info = sprintf($info, @@ -311,11 +311,13 @@ function unassignCase($delIndex, $taskGuid, $tasktype, $tasks_id, $itemtype, $op Html::clean($groupname), $options['comment'] ); + // unescape some chars and replace CRLF, CR or LF by
+ $info = str_replace(["\\'", '\\"', '\r\n', '\r', '\n'], ["'", '"', '
', '
', '
'], $info); $glpi_task->add([$foreignkey => $glpi_task->fields[$foreignkey], 'is_private' => 1, 'taskcategories_id' => $pm_process->fields['taskcategories_id'], - 'content' => $info, + 'content' => $DB->escape($info), 'users_id' => $PM_SOAP->taskWriter, 'state' => Planning::INFO, 'users_id_tech' => Session::getLoginUserID(), @@ -432,12 +434,15 @@ public function reassignTask ($delIndex, $newDelIndex, $delThread, $newDelThread $options['comment'] ); } - $info .= "getID()."\'>getID()."\'>"; + $info .= ""; + + // unescape some chars and replace CRLF, CR or LF by
+ $info = str_replace(["\\'", '\\"', '\r\n', '\r', '\n'], ["'", '"', '
', '
', '
'], $info); $glpi_task->add([$foreignkey => $glpi_task->fields[$foreignkey], 'is_private' => 1, 'taskcategories_id' => $pm_process->fields['taskcategories_id'], - 'content' => $info, + 'content' => $DB->escape($info), 'users_id' => $PM_SOAP->taskWriter, 'state' => Planning::INFO, 'users_id_tech' => Session::getLoginUserID(), diff --git a/inc/processmaker.class.php b/inc/processmaker.class.php index 4348c71..875511b 100644 --- a/inc/processmaker.class.php +++ b/inc/processmaker.class.php @@ -1984,7 +1984,7 @@ function computeTaskDuration($begin, $end, $entity) { * 'begin' => is the new begin date of the task * 'end' => is the new end date of the task * 'toInformation' => is the new status of the task (usually set to INFORMATION) - * + * @return false|object the solved task, when found */ public function solveTask($cases_id, $delIndex, $options = []) { global $DB; @@ -2056,8 +2056,10 @@ public function solveTask($cases_id, $delIndex, $options = []) { // restore current glpi time $_SESSION["glpi_currenttime"] = $saved_date_time; + return $glpi_task; } + return false; } /** @@ -2750,7 +2752,7 @@ public function startNewCase($processes_id, $itemtype, $items_id, $users_id = nu */ public function derivateCase($myCase, $request, $users_id = null) { //$cookies, - global $PM_DB, $CFG_GLPI; + global $PM_DB, $CFG_GLPI, $DB; $itemtype = $myCase->getField('itemtype'); $items_id = $myCase->getField('items_id'); @@ -2798,12 +2800,18 @@ public function derivateCase($myCase, $request, $users_id = null) { "GLPI_ITEM_APPEND_TO_SOLUTION_DESCRIPTION", "GLPI_ITEM_INITIAL_DUE_DATE", "GLPI_ITEM_DUE_DATE", - "GLPI_SEND_EMAIL" + "GLPI_SEND_EMAIL", + "GLPI_ITEM_INFORMATION_TASK" ]; // now tries to get some variables to setup content for new task and to append text to solved task $casevariablevalues = $myCase->getVariables($casevariables); + $information_task = ''; + if (array_key_exists( 'GLPI_ITEM_INFORMATION_TASK', $casevariablevalues ) && $casevariablevalues[ 'GLPI_ITEM_INFORMATION_TASK' ] != '') { + $information_task = $casevariablevalues[ 'GLPI_ITEM_INFORMATION_TASK' ]; + } + $sendemail = ''; if (array_key_exists( 'GLPI_SEND_EMAIL', $casevariablevalues ) && $casevariablevalues[ 'GLPI_SEND_EMAIL' ] != '') { $sendemail = json_decode($casevariablevalues[ 'GLPI_SEND_EMAIL' ], true); @@ -2904,14 +2912,15 @@ public function derivateCase($myCase, $request, $users_id = null) { // now manage tasks associated with item // switch own task to 'done' and create a new one - $this->solveTask($myCase->getID(), - $request['DEL_INDEX'], - ['txtToAppend' => $txtToAppendToTask, - 'users_id_tech' => $users_id, - 'begin' => $solvedTaskStartDate, - 'end' => $solvedTaskEndDate, - 'toInformation' => $solvedTaskSetToInformation - ] ); + $glpi_task = $this->solveTask($myCase->getID(), + $request['DEL_INDEX'], + ['txtToAppend' => $txtToAppendToTask, + 'users_id_tech' => $users_id, + 'begin' => $solvedTaskStartDate, + 'end' => $solvedTaskEndDate, + 'toInformation' => $solvedTaskSetToInformation + ] + ); // create a followup if requested if ($createFollowup && $itemtype == 'Ticket') { @@ -2942,6 +2951,38 @@ public function derivateCase($myCase, $request, $users_id = null) { // get the new case info $caseInfo = $myCase->getCaseInfo($request['DEL_INDEX']); // not sure that it should passed this + // create an information task if requested + if ($information_task != '' && $glpi_task) { + // create an information task and add comment + // $information_task is the content of the task + $pm_process = $myCase->getProcess(); + $taskCat = new TaskCategory; + $taskCat->getFromDB($glpi_task->fields['taskcategories_id']); + // we may replace ##casename## by the name of the case, and ##taskname## by the task name + $search = ['##casename##', + '##taskname##' + ]; + $replace = [$caseInfo->caseName." (".$myCase->getID().")", + DropdownTranslation::getTranslatedValue($glpi_task->fields['taskcategories_id'], 'TaskCategory', 'name', $_SESSION['glpilanguage'], $taskCat->fields['name']) + ]; + $info = str_replace($search, $replace, $information_task); + + $info .= ""; + + // unescape some chars and replace CRLF, CR or LF by
+ $info = str_replace(["\\'", '\\"', '\r\n', '\r', '\n'], ["'", '"', '
', '
', '
'], $info); + + $foreignkey = getForeignKeyFieldForItemType($glpi_task->getItilObjectItemType()); + $glpi_task->add([$foreignkey => $glpi_task->fields[$foreignkey], + 'is_private' => 1, + 'taskcategories_id' => $pm_process->fields['taskcategories_id'], + 'content' => $DB->escape($info), + 'users_id' => $this->taskWriter, + 'state' => Planning::INFO, + 'users_id_tech' => Session::getLoginUserID(), + ]); + } + // now create the new tasks if any if (property_exists( $pmRouteCaseResponse, 'routing' )) { diff --git a/processmaker.xml b/processmaker.xml index 2587759..21d3f63 100644 --- a/processmaker.xml +++ b/processmaker.xml @@ -27,7 +27,7 @@ 9.1 - 3.4.21 + 3.4.22 9.2 diff --git a/setup.php b/setup.php index ca7f34b..70591c4 100644 --- a/setup.php +++ b/setup.php @@ -1,6 +1,6 @@ Date: Wed, 7 Oct 2020 11:38:11 +0200 Subject: [PATCH 12/15] Added a proxy and SSL mangement to external application calls Added an escape DB for ticket name and ticket solution description Updated translation Set version to 3.4.23 --- hook.php | 19 +- inc/processmaker.class.php | 8 +- locales/fr_FR.mo | Bin 9870 -> 14622 bytes locales/fr_FR.po | 508 +++++++++++++++++++++++++----------- locales/source.po | 516 +++++++++++++++++++++++++++---------- processmaker.xml | 2 +- setup.php | 2 +- 7 files changed, 761 insertions(+), 294 deletions(-) diff --git a/hook.php b/hook.php index 8aa662a..5ffb08b 100644 --- a/hook.php +++ b/hook.php @@ -406,15 +406,26 @@ function plugin_item_update_processmaker_tasks($parm) { curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Content-Length: ' . strlen($externalapplicationparams), 'Expect:']); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 ); curl_setopt($ch, CURLOPT_VERBOSE, 1); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $pmconfig->fields['ssl_verify']); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $pmconfig->fields['ssl_verify']); + + //curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $pmconfig->fields['ssl_verify']); + //curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $pmconfig->fields['ssl_verify']); + if (isset($externalapplication['ssl_verify'])) { + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $externalapplication['ssl_verify']); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $externalapplication['ssl_verify']); + } //curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1 ) ; - //curl_setopt($ch, CURLOPT_PROXY, "localhost:8889"); + //curl_setopt($ch, CURLOPT_PROXY, "localhost:8888"); + if (isset($externalapplication['proxy'])) { + curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1 ) ; + curl_setopt($ch, CURLOPT_PROXY, $externalapplication['proxy']); + } $response = curl_exec ($ch); - //Toolbox::logDebug( $response ) ; + if (!$response) { + Toolbox::logDebug($response); + } curl_close ($ch); } diff --git a/inc/processmaker.class.php b/inc/processmaker.class.php index 875511b..c049a74 100644 --- a/inc/processmaker.class.php +++ b/inc/processmaker.class.php @@ -1864,10 +1864,12 @@ public function setItemStatus($itemtype, $itemId, $newstatus) { * @param mixed $newtitle */ public function setItemTitle($itemtype, $itemId, $newtitle) { + global $DB; + $dbu = new DbUtils; $item = $dbu->getItemForItemtype( $itemtype ); if ($item->getFromDB( $itemId )) { - $item->update( ['id' => $itemId, 'name' => $newtitle] ); + $item->update( ['id' => $itemId, 'name' => $DB->escape($newtitle)] ); } } @@ -1894,6 +1896,8 @@ public function setItemDuedate($itemtype, $itemId, $duedate) { * @param mixed $casevariablevalues */ public function setItemSolution($itemType, $itemId, $casevariablevalues) { + global $DB; + $dbu = new DbUtils; $item = $dbu->getItemForItemtype( $itemType ); if ($item->getFromDB( $itemId )) { @@ -1941,7 +1945,7 @@ public function setItemSolution($itemType, $itemId, $casevariablevalues) { if ($solution != '') { $solution .= "\n"; } - $solution .= $casevariablevalues[ 'GLPI_ITEM_APPEND_TO_SOLUTION_DESCRIPTION' ]; + $solution .= $DB->escape($casevariablevalues[ 'GLPI_ITEM_APPEND_TO_SOLUTION_DESCRIPTION' ]); $to_update = true; } diff --git a/locales/fr_FR.mo b/locales/fr_FR.mo index 9299adb384a3ff982f23e3e750803bb6acc8462d..a72879c76d0b1aa022fc544db053320a77fab2f7 100644 GIT binary patch literal 14622 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 1);\n" -"X-Poedit-KeywordsList: ;__:1,2t;_e:1,2t;_x:1c,2,3t;_n:1,2,4t;__s:1,2t;" -"_ex:1c,2,3t;_sx:1c,2,3t;_nx:1c,2,3,5t\n" +"X-Poedit-KeywordsList: ;__:1,2t;_e:1,2t;_x:1c,2,3t;_n:1,2,4t;__s:1,2t;_ex:1c,2,3t;_sx:1c,2,3t;_nx:1c,2,3,5t\n" "X-Crowdin-Project: processmaker-plugin\n" +"X-Crowdin-Project-ID: 310131\n" "X-Crowdin-Language: fr\n" -"X-Crowdin-File: /3.3.0/source.po\n" +"X-Crowdin-File: /3.4 - GLPI 9.2/source.po\n" +"X-Crowdin-File-ID: 20\n" "X-Poedit-SearchPath-0: ajax\n" "X-Poedit-SearchPath-1: front\n" "X-Poedit-SearchPath-2: inc\n" @@ -25,239 +26,306 @@ msgstr "" "X-Poedit-SearchPath-5: hook.php\n" "X-Poedit-SearchPath-6: setup.php\n" -#: ajax/task_users.php:25 +#: ajax/task_users.php:36 msgid "Re-assign task to" msgstr "Ré-affecter tâche à" -#: ajax/task_users.php:45 +#: ajax/task_users.php:60 ajax/task_users.php:61 ajax/task_users.php:124 msgid "Re-assign" msgstr "Ré-affecter" -#: front/case.form.php:35 +#: ajax/task_users.php:110 +#| msgid "Task already assigned to this person!" +msgid "Task is already assigned to this user or group!" +msgstr "La tâche est déjà affectée à cet utilisateur (ou ce groupe) !" + +#: ajax/task_users.php:110 ajax/task_users.php:122 +#| msgid "Re-assign task to" +msgid "Re-assign task" +msgstr "Ré-affecter tâche" + +#: ajax/task_users.php:114 ajax/task_users.php:119 +#| msgid "Claim target task" +msgid "Un-claim task" +msgstr "Annuler affectation de la tâche" + +#: ajax/task_users.php:115 +msgid "Please input reason to un-claim
(task will be re-assigned to former group):" +msgstr "Saisir la raison pour l'annulation de l'affectation
(la tâche sera ré-affectée au groupe précédent) :" + +#: ajax/task_users.php:116 +msgid "Un-claim" +msgstr "Annulation de l'affectation" + +#: ajax/task_users.php:119 +msgid "Can\\'t un-assign Task!" +msgstr "Annulation de l'affectation impossible !" + +#: ajax/task_users.php:123 +msgid "Please input reason to re-assign:" +msgstr "Saisir la raison pour la ré-affectation :" + +#: ajax/task_users.php:134 +msgid "Send reminder" +msgstr "Envoyer un rappel" + +#: front/case.form.php:32 msgid "Case has been deleted!" msgstr "Le cas a été effacé !" -#: front/case.form.php:37 +#: front/case.form.php:34 msgid "Unable to delete case!" msgstr "Impossible d'effacer le cas !" -#: front/case.form.php:50 +#: front/case.form.php:46 msgid "Case has been cancelled!" msgstr "Le cas a été annulé !" -#: front/case.form.php:52 front/case.form.php:55 +#: front/case.form.php:48 front/case.form.php:51 msgid "Unable to cancel case!" msgstr "Impossible d'annuler le cas !" -#: front/case.form.php:76 front/case.form.php:78 inc/case.class.php:1018 +#: front/case.form.php:70 front/case.form.php:72 inc/case.class.php:1310 msgid "Process cases" msgstr "Cas des processus" -#: front/case.php:5 front/caselink.form.php:27 front/caselink.php:5 -#: front/process.form.php:25 front/process.php:5 inc/caselink.class.php:143 -#: inc/config.class.php:50 inc/config.class.php:354 inc/process.class.php:393 +#: front/case.php:5 front/caselink.form.php:29 front/caselink.php:5 +#: front/process.form.php:27 front/process.php:5 inc/caselink.class.php:157 +#: inc/config.class.php:50 inc/config.class.php:363 inc/process.class.php:409 #: inc/profile.class.php:52 inc/profile.class.php:83 msgid "ProcessMaker" msgstr "ProcessMaker" -#: front/processmaker.form.php:85 +#: front/processmaker.form.php:80 msgid "Task re-assigned!" msgstr "Tâche ré-affectée !" -#: front/processmaker.form.php:87 +#: front/processmaker.form.php:82 msgid "Error re-assigning task: " -msgstr "Impossible de re-affecter cette tâche : " +msgstr "Impossible de ré-affecter cette tâche :" + +#: front/processmaker.form.php:93 +#| msgid "Task name" +msgid "Task un-claimed!" +msgstr "Annulation de l'affectation de la tâche effectuée !" -#: front/processmaker.form.php:90 -msgid "Task already assigned to this person!" -msgstr "Tâche déjà affectée à cette personne !" +#: front/processmaker.form.php:95 +msgid "Can't un-claim task! Verify 'Assignement Rules' in the process definition." +msgstr "Annulation de l'affectation impossible ! Vérifiez les règles d'assignation dans la définition du Processus." #: front/processmaker.helpdesk.form.php:21 msgid "Process - Case" msgstr "Processus - Cas" -#: front/processmaker.helpdesk.form.php:24 inc/case.class.php:605 +#: front/processmaker.helpdesk.form.php:24 inc/case.class.php:799 msgid "Select the process you want to add" msgstr "Choisir le processus à démarrer" -#: hook.php:31 inc/taskcategory.class.php:98 +#: hook.php:9 inc/taskcategory.class.php:214 msgid "Synchronize Task List" msgstr "Synchroniser la liste des Tâches" -#: hook.php:158 inc/case.class.php:77 +#: hook.php:76 inc/case.class.php:89 msgid "Case" msgstr "Cas" -#: hook.php:158 inc/case.class.php:586 inc/case.class.php:1062 +#: hook.php:76 inc/case.class.php:778 inc/case.class.php:1355 msgid "Status" msgstr "Statut" -#: inc/case.class.php:23 inc/case.class.php:90 inc/case.class.php:92 +#: inc/case.class.php:25 inc/case.class.php:102 inc/case.class.php:104 msgid "Process case" msgid_plural "Process cases" msgstr[0] "Cas du processus" msgstr[1] "Cas des processus" -#: inc/case.class.php:80 +#: inc/case.class.php:92 msgid "Sub-case" msgstr "Sous-cas" -#: inc/case.class.php:312 +#: inc/case.class.php:303 +#, php-format +msgid "Task un-claimed!
Case: %s
Task: \"%s\" has been un-assigned from \"%s\" and assigned to \"%s\" group.
Reason: %s" +msgstr "Annulation de l'affection de la tâche effectuée !
Cas : %s
Tâche : \"%s\" a été libérée de \"%s\" et assignée au groupe \"%s\".
Motif : %s" + +#: inc/case.class.php:420 +#, php-format +msgid "Task re-assigned!
Case: %s
Task: \"%s\" has been re-assigned from \"%s\" to \"%s\".
Reason: %s" +msgstr "Tâche ré-assignée !
Cas : %s
Tâche : \"%s\" a été ré-assignée de \"%s\" à \"%s\".
Motif : %s" + +#: inc/case.class.php:429 +#, php-format +msgid "Task assigned!
Case: %s
Task: \"%s\" has been assigned to \"%s\".
Reason: %s" +msgstr "Tâche assignée !
Cas : %s
Tâche : \"%s\" a été asignée à \"%s\".
Motif : %s" + +#: inc/case.class.php:510 msgid "Current task(s) properties" msgstr "Propriétés des tâches en cours" -#: inc/case.class.php:316 inc/task.class.php:263 +#: inc/case.class.php:514 inc/task.class.php:283 msgid "Task" msgstr "Tâche" -#: inc/case.class.php:317 inc/task.class.php:264 +#: inc/case.class.php:515 inc/task.class.php:284 msgid "Task guid" msgstr "Guid de la tâche" -#: inc/case.class.php:318 inc/task.class.php:265 +#: inc/case.class.php:516 inc/task.class.php:285 msgid "Current user" msgstr "Utilisateur actuel" -#: inc/case.class.php:319 inc/task.class.php:266 +#: inc/case.class.php:517 inc/task.class.php:286 msgid "Task delegation date" msgstr "Date de délégation de tâche" -#: inc/case.class.php:341 inc/task.class.php:275 +#: inc/case.class.php:539 inc/task.class.php:295 msgid "To be claimed" msgstr "A réclamer" -#: inc/case.class.php:377 +#: inc/case.class.php:575 msgid "Sub-case properties" msgstr "Propriétés du sous-cas" -#: inc/case.class.php:380 +#: inc/case.class.php:578 msgid "Parent case properties" msgstr "Propriétés du cas parent" -#: inc/case.class.php:382 +#: inc/case.class.php:580 msgid "Case properties" msgstr "Propriétés du cas" -#: inc/case.class.php:387 inc/case.class.php:584 inc/case.class.php:1036 -#: inc/process.class.php:548 +#: inc/case.class.php:585 inc/case.class.php:776 inc/case.class.php:1328 +#: inc/process.class.php:576 msgid "Process" msgstr "Processus" -#: inc/case.class.php:388 +#: inc/case.class.php:586 inc/notificationtargetprocessmaker.class.php:64 msgid "Case title" msgstr "Titre du cas" -#: inc/case.class.php:389 +#: inc/case.class.php:587 msgid "Case number" msgstr "Numéro du cas" -#: inc/case.class.php:390 +#: inc/case.class.php:588 msgid "Case status" msgstr "Statut du cas" -#: inc/case.class.php:391 +#: inc/case.class.php:589 msgid "Case guid" msgstr "Guid du cas" -#: inc/case.class.php:392 +#: inc/case.class.php:590 msgid "Creator" msgstr "Créateur" -#: inc/case.class.php:393 +#: inc/case.class.php:591 inc/case.class.php:1370 msgid "Creation date" msgstr "Date de création" -#: inc/case.class.php:394 +#: inc/case.class.php:592 msgid "Last update" msgstr "Dernière mise à jour" -#: inc/case.class.php:479 +#: inc/case.class.php:675 #, php-format msgid "Case is linked to a %1s" msgstr "Le cas est lié à un %1s" -#: inc/case.class.php:481 +#: inc/case.class.php:677 #, php-format msgid "Sub-case is linked to a %1s" msgstr "Le sous-cas est lié à un %1s" -#: inc/case.class.php:506 +#: inc/case.class.php:698 msgid "Case cancellation" msgstr "Annulation du cas" -#: inc/case.class.php:507 +#: inc/case.class.php:699 msgid "Cancel case" msgstr "Annuler cas" -#: inc/case.class.php:511 +#: inc/case.class.php:703 msgid "Confirm cancellation?" msgstr "Confirmer l’annulation ?" -#: inc/case.class.php:511 inc/profile.class.php:23 +#: inc/case.class.php:703 inc/profile.class.php:23 msgid "Cancel" msgstr "Annuler" -#: inc/case.class.php:532 +#: inc/case.class.php:717 msgid "Case deletion" msgstr "Suppression du cas" -#: inc/case.class.php:533 -msgid "Delete case" -msgstr "Supprimer le cas" - -#: inc/case.class.php:585 inc/case.class.php:1029 +#: inc/case.class.php:777 inc/case.class.php:1321 msgid "Title" msgstr "Titre" -#: inc/case.class.php:587 inc/case.class.php:1070 +#: inc/case.class.php:779 inc/case.class.php:1362 msgid "Sub-case of" msgstr "Sous-cas de" -#: inc/case.class.php:602 +#: inc/case.class.php:796 msgid "Add a new case" msgstr "Ajouter un nouveau cas" -#: inc/case.class.php:995 +#: inc/case.class.php:1227 msgctxt "case_status" msgid "Draft" msgstr "Brouillon" -#: inc/case.class.php:996 +#: inc/case.class.php:1228 msgctxt "case_status" msgid "To do" msgstr "A faire" -#: inc/case.class.php:997 +#: inc/case.class.php:1229 msgctxt "case_status" msgid "Completed" msgstr "Terminé" -#: inc/case.class.php:998 +#: inc/case.class.php:1230 msgctxt "case_status" msgid "Cancelled" msgstr "Annulé" -#: inc/case.class.php:1022 +#: inc/case.class.php:1314 msgid "ID" msgstr "ID" -#: inc/case.class.php:1049 +#: inc/case.class.php:1335 inc/notificationtargetprocessmaker.class.php:72 +#| msgid "Item entity" +msgid "Item type" +msgstr "Type de l'item" + +#: inc/case.class.php:1342 msgid "Item" msgstr "Item" -#: inc/case.class.php:1056 +#: inc/case.class.php:1349 msgid "Item entity" msgstr "Entité de l'item" -#: inc/casechangelog.class.php:35 +#: inc/case.class.php:1379 +#| msgid "Last update" +msgid "Last update date" +msgstr "Date de dernière mise à jour" + +#: inc/case.class.php:1392 +#, php-format +#| msgid "Case status" +msgid "Case status is '%s'" +msgstr "L'état du cas est '%s'" + +#: inc/casechangelog.class.php:36 msgid "Change log" msgstr "Evolution" -#: inc/casedynaform.class.php:80 +#: inc/casedynaform.class.php:83 msgid "Dynaforms" msgstr "DynaForms" -#: inc/casehistory.class.php:35 +#: inc/casehistory.class.php:36 msgid "History" msgstr "Historique" @@ -269,43 +337,51 @@ msgstr "Liens-cas" msgid "Case-link" msgstr "Lien-cas" -#: inc/caselink.class.php:75 inc/caselink.class.php:165 +#: inc/caselink.class.php:75 +msgid "Synchronous" +msgstr "Synchrone" + +#: inc/caselink.class.php:80 inc/caselink.class.php:179 msgid "External data" msgstr "Données externes" -#: inc/caselink.class.php:80 inc/caselink.class.php:171 +#: inc/caselink.class.php:85 inc/caselink.class.php:185 msgid "Self" msgstr "Self" -#: inc/caselink.class.php:85 inc/caselink.class.php:189 +#: inc/caselink.class.php:90 inc/caselink.class.php:203 msgid "Source task GUID" msgstr "GUID de la tâche source" -#: inc/caselink.class.php:93 inc/caselink.class.php:195 +#: inc/caselink.class.php:98 inc/caselink.class.php:209 msgid "Target task GUID" msgstr "GUID de la tâche cible" -#: inc/caselink.class.php:101 inc/caselink.class.php:207 +#: inc/caselink.class.php:106 inc/caselink.class.php:221 msgid "Target process GUID" msgstr "GUID du Processus cible" -#: inc/caselink.class.php:110 inc/caselink.class.php:201 +#: inc/caselink.class.php:115 inc/caselink.class.php:215 msgid "Target dynaform GUID" msgstr "GUID du dynaform cible" -#: inc/caselink.class.php:115 inc/caselink.class.php:213 +#: inc/caselink.class.php:120 inc/caselink.class.php:227 msgid "Source condition" msgstr "Condition d'activation du lien" -#: inc/caselink.class.php:121 inc/caselink.class.php:177 +#: inc/caselink.class.php:126 inc/caselink.class.php:191 msgid "Claim target task" msgstr "Tâche cible 'à réclamer'" -#: inc/caselink.class.php:126 inc/caselink.class.php:183 +#: inc/caselink.class.php:136 +msgid "Impersonate target task user" +msgstr "Personnifier l'utilisateur de la tâche cible" + +#: inc/caselink.class.php:141 inc/caselink.class.php:197 msgid "External application JSON config" msgstr "Configuration JSON de l'application externe" -#: inc/casemap.class.php:43 +#: inc/casemap.class.php:44 msgid "Map" msgstr "Carte" @@ -314,190 +390,294 @@ msgid "ProcessMaker setup" msgstr "Configuration du serveur ProcessMaker" #: inc/config.class.php:159 -msgid "Server URL (must be in same domain than GLPI)" -msgstr "URL du serveur (doit être dans le même domaine que GLPI)" +#| msgid "Server URL (must be in same domain than GLPI)" +msgid "Server URL (must be in same domain than GLPI, if GLPI is using HTTPS, PM server must also use HTTPS)" +msgstr "URL du serveur (doit être dans le même domaine que GLPI, si GLPI utilise HTTPS, alors PM doit aussi utiliser HTTPS)" -#: inc/config.class.php:164 +#: inc/config.class.php:163 msgid "Common domain with GLPI" msgstr "Domaine commun avec GLPI" -#: inc/config.class.php:193 +#: inc/config.class.php:192 msgid "None!" msgstr "Aucun !" -#: inc/config.class.php:202 +#: inc/config.class.php:201 +msgid "Verify SSL certificate" +msgstr "Verification du certificat SSL" + +#: inc/config.class.php:206 msgid "Workspace Name" msgstr "Nom du Workspace" -#: inc/config.class.php:207 +#: inc/config.class.php:211 msgid "Server administrator name" msgstr "Non de l'administreur du server ProcessMaker" -#: inc/config.class.php:212 +#: inc/config.class.php:216 msgid "Server administrator password" msgstr "Mot de passe de l'administrateur du serveur ProcessMaker" -#: inc/config.class.php:218 inc/config.class.php:258 +#: inc/config.class.php:222 inc/config.class.php:262 msgid "Connection status" msgstr "Status de la connexion" -#: inc/config.class.php:234 +#: inc/config.class.php:238 msgid "SQL server setup" msgstr "Configuration du serveur SQL" -#: inc/config.class.php:237 +#: inc/config.class.php:241 msgid "SQL server (MariaDB or MySQL)" msgstr "SQL server (MariaDB ou MySQL)" -#: inc/config.class.php:242 +#: inc/config.class.php:246 msgid "Database name" msgstr "Nom de la base de données" -#: inc/config.class.php:247 +#: inc/config.class.php:251 msgid "SQL user" msgstr "Utilisateur SQL" -#: inc/config.class.php:252 +#: inc/config.class.php:256 msgid "SQL password" msgstr "Mot de passe SQL" -#: inc/config.class.php:269 +#: inc/config.class.php:273 msgid "Theme Name" msgstr "Nom du thème" -#: inc/config.class.php:275 +#: inc/config.class.php:279 msgid "Main Task Category (edit to change name)" msgstr "Catégorie principale des tâches (éditer pour changer le nom)" -#: inc/config.class.php:282 +#: inc/config.class.php:286 msgid "Task Writer (edit to change name)" msgstr "Auteur des tâches (éditer pour changer le nom)" -#: inc/config.class.php:302 +#: inc/config.class.php:306 msgid "Group in ProcessMaker which will contain all GLPI users" msgstr "Groupe dans ProcessMaker qui contiendra les utilisateurs de GLPI" -#: inc/config.class.php:331 +#: inc/config.class.php:321 inc/process.class.php:510 inc/process.class.php:637 +msgid "Max cases per item (0=unlimited)" +msgstr "Nombre de cas maxi par item (0 = pas de limite)" + +#: inc/config.class.php:340 msgid "Processmaker system information" msgstr "Informations système du serveur ProcessMaker" -#: inc/config.class.php:334 inc/config.class.php:344 +#: inc/config.class.php:343 inc/config.class.php:353 msgid "Version" msgstr "Version" -#: inc/config.class.php:335 +#: inc/config.class.php:344 msgid "Web server" msgstr "Serveur web" -#: inc/config.class.php:336 +#: inc/config.class.php:345 msgid "Server name" msgstr "Nom du serveur" -#: inc/config.class.php:337 +#: inc/config.class.php:346 msgid "PHP version" msgstr "Version de PHP" -#: inc/config.class.php:338 +#: inc/config.class.php:347 msgid "DB version" msgstr "Version de la Db" -#: inc/config.class.php:339 +#: inc/config.class.php:348 msgid "DB server IP" msgstr "IP du serveur DB" -#: inc/config.class.php:340 +#: inc/config.class.php:349 msgid "DB name" msgstr "Nom de la BD" -#: inc/config.class.php:341 +#: inc/config.class.php:350 msgid "User browser" msgstr "Navigateur de l’utilisateur" -#: inc/config.class.php:342 +#: inc/config.class.php:351 msgid "User IP" msgstr "IP de l'utilisateur" -#: inc/config.class.php:344 +#: inc/config.class.php:353 msgid "Not yet!" msgstr "Pas encore !" -#: inc/process.class.php:350 +#: inc/notificationtargetcase.class.php:26 +msgid "Send email" +msgstr "Envoi d'email" + +#: inc/notificationtargetcase.class.php:37 +msgid "eMail recipients" +msgstr "Destinataires des emails" + +#: inc/notificationtargetprocessmaker.class.php:60 +#| msgid "Process case" +#| msgid_plural "Process cases" +msgid "Process category" +msgstr "Catégorie du processus" + +#: inc/notificationtargetprocessmaker.class.php:61 +#| msgid "Process case" +#| msgid_plural "Process cases" +msgid "Process category id" +msgstr "ID de la catégorie du processus" + +#: inc/notificationtargetprocessmaker.class.php:62 +#| msgid "Process case task" +#| msgid_plural "Process case tasks" +msgid "Process category comment" +msgstr "Commentaire de la catégorie du processus" + +#: inc/notificationtargetprocessmaker.class.php:63 +#| msgid "Case guid" +msgid "Case id" +msgstr "ID du cas" + +#: inc/notificationtargetprocessmaker.class.php:65 +#| msgid "Case deletion" +msgid "Case description" +msgstr "Description du cas" + +#: inc/notificationtargetprocessmaker.class.php:67 +msgid "Case variable 'XXX'" +msgstr "Variable de cas 'XXX'" + +#: inc/notificationtargetprocessmaker.class.php:68 +msgid "List of values in 'YYY' array" +msgstr "Liste des valeurs du tableau 'YYY'" + +#: inc/notificationtargetprocessmaker.class.php:69 +msgid "Number of rows in 'YYY' array" +msgstr "Nombre de lignes du tableau 'YYY'" + +#: inc/notificationtargetprocessmaker.class.php:70 +msgid "Value for colname in 'YYY' array" +msgstr "Valeur de la colonne 'colname' du tableau 'YYY'" + +#: inc/notificationtargetprocessmaker.class.php:71 +msgid "Value for key in 'ZZZ' assoc array (1-dimension array)" +msgstr "Valeur pour la clé 'key' du tableau associatif à une dimension 'ZZZ'" + +#: inc/notificationtargetprocessmaker.class.php:73 +#| msgid "Item" +msgid "Item id" +msgstr "ID de l'item" + +#: inc/notificationtargetprocessmaker.class.php:74 +#| msgid "Item" +msgid "Item URL" +msgstr "URL de l'item" + +#: inc/notificationtargetprocessmaker.class.php:75 +#| msgid "Item entity" +msgid "Item title" +msgstr "Titre de l'item" + +#: inc/notificationtargetprocessmaker.class.php:83 +msgid "XXX is to be replaced by any case variable names" +msgstr "XXX doit être remplacé par n'importe quel nom de variable de cas" + +#: inc/notificationtargetprocessmaker.class.php:86 +msgid "YYY is to be replaced by any array variables" +msgstr "YYY doit être remplacé par n'importe quel nom de tableau" + +#: inc/notificationtargetprocessmaker.class.php:90 +msgid "ZZZ is to be replaced by any assoc array variables (1-dimension array with key/value pairs)" +msgstr "ZZZ doit être replacé par n'importe quel nom de tableau associatif à une dimension (paire clé/valeur)" + +#: inc/notificationtargettask.class.php:195 +#| msgid "Task delegation date" +msgid "Task action" +msgstr "Action de la tâche" + +#: inc/process.class.php:366 msgid "Synchronize Process List" msgstr "Synchroniser la liste des processus" -#: inc/process.class.php:427 inc/process.class.php:589 +#: inc/process.class.php:443 inc/process.class.php:617 msgid "Process GUID" msgstr "GUID du Processus" -#: inc/process.class.php:433 +#: inc/process.class.php:449 msgid "Process type" msgstr "Type de processus" -#: inc/process.class.php:439 +#: inc/process.class.php:455 msgid "Hide case num. & title" msgstr "Cacher num. et titre du cas" -#: inc/process.class.php:445 +#: inc/process.class.php:461 msgid "Insert Task Category" msgstr "Insérer la catégorie des tâches" -#: inc/process.class.php:457 +#: inc/process.class.php:473 msgid "Ticket type (self-service)" msgstr "Type de ticket (self-service)" -#: inc/process.class.php:464 inc/process.class.php:609 +#: inc/process.class.php:480 inc/process.class.php:642 msgid "Visible in Incident for Central interface" msgstr "Visible pour un Incident sur l'interface Centrale" -#: inc/process.class.php:470 inc/process.class.php:614 +#: inc/process.class.php:486 inc/process.class.php:647 msgid "Visible in Request for Central interface" msgstr "Visible pour une demande sur l'interface Centrale" -#: inc/process.class.php:476 inc/process.class.php:666 +#: inc/process.class.php:492 inc/process.class.php:699 msgid "Visible in Change" msgstr "Visible dans un Changement" -#: inc/process.class.php:482 inc/process.class.php:671 +#: inc/process.class.php:498 inc/process.class.php:704 msgid "Visible in Problem" msgstr "Visible dans un Problème" -#: inc/process.class.php:519 +#: inc/process.class.php:504 +msgid "Maintenance" +msgstr "Maintenance" + +#: inc/process.class.php:547 msgctxt "process_type" msgid "Classic" msgstr "Classique" -#: inc/process.class.php:520 +#: inc/process.class.php:548 msgctxt "process_type" msgid "BPMN" msgstr "BPMN" -#: inc/process.class.php:546 +#: inc/process.class.php:574 msgid "Processes" msgstr "Processus" -#: inc/process.class.php:599 +#: inc/process.class.php:627 msgid "Hide case number and title in task descriptions" msgstr "Masquer numéro et titre des cas dans les descriptions des tâches" -#: inc/process.class.php:604 +#: inc/process.class.php:632 msgid "Insert Task Category comments in Task Description" -msgstr "" -"Insérer les commentaires des Catégories de Tâches dans les descriptions des " -"Tâches" +msgstr "Insérer les commentaires des Catégories de Tâches dans les descriptions des Tâches" -#: inc/process.class.php:619 +#: inc/process.class.php:652 msgid "ITIL Category for Self-service interface (left empty to disable)" -msgstr "" -"Catégorie ITIL pour l'interface libre-service (laissez vide pour désactiver)" +msgstr "Catégorie ITIL pour l'interface libre-service (laissez vide pour désactiver)" -#: inc/process.class.php:646 +#: inc/process.class.php:679 msgid "Type for Self-service interface" msgstr "Type de ticket pour l'interface libre-service" -#: inc/process.class.php:676 +#: inc/process.class.php:709 msgid "Process type (to be changed only if not up-to-date)" msgstr "Type de processus (à modifier uniquement si pas à jour)" +#: inc/process.class.php:862 +#, php-format +#| msgid "ProcessMaker plugin is under maintenance, please retry later, thank you." +msgid "Process '%s' is under maintenance, please retry later, thank you." +msgstr "Le processus '%s' est en maintenance, ré-essayez plus tard, merci." + #: inc/process_profile.class.php:26 inc/process_profile.class.php:45 msgid "Authorizations" msgstr "Autorisations" @@ -516,40 +696,42 @@ msgctxt "errors" msgid "Can't create case: no rights for it!" msgstr "Impossible de créer le cas : pas de droits pour cela !" -#: inc/processmaker.class.php:984 +#: inc/processmaker.class.php:1012 msgid "Syncs GLPI users and groups into ProcessMaker." msgstr "Synchronisation des utilisateurs GLPI et des groups avec ProcessMaker." -#: inc/processmaker.class.php:986 +#: inc/processmaker.class.php:1014 msgid "Cleaning of orphan cases." msgstr "Nettoyage des cas orphelins." -#: inc/processmaker.class.php:986 +#: inc/processmaker.class.php:1014 msgid "Number of days to keep orphan cases" msgstr "Nombre de jours avant nettoyage des cas orphelins" -#: inc/processmaker.class.php:988 +#: inc/processmaker.class.php:1016 msgid "To apply task actions between cases." msgstr "Pour appliquer des actions de tâches entre cas." -#: inc/processmaker.class.php:1696 +#: inc/processmaker.class.php:1720 msgid "Case title: " msgstr "Titre du cas : " -#: inc/processmaker.class.php:2178 +#: inc/processmaker.class.php:2275 msgid "A 'Case' is running!" msgstr "Un 'Cas' est en cours !" -#: inc/processmaker.class.php:2179 +#: inc/processmaker.class.php:2276 msgid "You must manage it first (see 'Process - Case' tab)!" msgstr "Vous devez d'abord le terminer (voir onglet 'Processus - Cas') !" -#: inc/processmaker.class.php:3038 -msgid "" -"ProcessMaker plugin is under maintenance, please retry later, thank you." -msgstr "" -"Le plugin ProcessMaker est en maintenance, veuillez ré-essayer plus tard, " -"merci." +#: inc/processmaker.class.php:2774 +#, php-format +msgid "Unable to derivate case! Retry in a moment, or ask your administrator.
Error code: %u
Error message: %s
" +msgstr "Impossible de dériver le cas ! Ré-essayez dans un moment, ou demandez à votre administrateur.
Code d'erreur : %u
Message d'erreur : %s
" + +#: inc/processmaker.class.php:3281 +msgid "ProcessMaker plugin is under maintenance, please retry later, thank you." +msgstr "Le plugin ProcessMaker est en maintenance, veuillez ré-essayer plus tard, merci." #: inc/profile.class.php:17 msgid "Process configuration" @@ -559,36 +741,62 @@ msgstr "Configuration des Processus" msgid "Cases" msgstr "Cas" +#: inc/profile.class.php:23 +msgid "Ad Hoc user re-assign" +msgstr "Ré-assignation d'un utilisateur Ad Hoc" + #: inc/task.class.php:29 msgid "Process case task" msgid_plural "Process case tasks" msgstr[0] "Tâche d'un cas" msgstr[1] "Tâches d'un cas" -#: inc/task.class.php:258 +#: inc/task.class.php:278 msgid "Sub-case task(s)" msgstr "Tache(s) du sous-cas" -#: inc/taskcategory.class.php:21 inc/taskcategory.class.php:31 +#: inc/taskcategory.class.php:24 inc/taskcategory.class.php:116 +#| msgid "Process case" +#| msgid_plural "Process cases" +msgid "Process task" +msgstr "Tâche du processus" + +#: inc/taskcategory.class.php:29 +#| msgid "Task List" +msgid "Task list" +msgstr "List des tâches" + +#: inc/taskcategory.class.php:39 msgid "Task List" msgstr "Liste des tâches" -#: inc/taskcategory.class.php:32 +#: inc/taskcategory.class.php:40 inc/taskcategory.class.php:118 msgid "Task name" msgstr "Nom de la Tâche" -#: inc/taskcategory.class.php:34 inc/taskcategory.class.php:60 +#: inc/taskcategory.class.php:42 inc/taskcategory.class.php:68 +#: inc/taskcategory.class.php:121 inc/taskcategory.class.php:155 msgid "Start" msgstr "Démarrer" -#: inc/taskcategory.class.php:35 +#: inc/taskcategory.class.php:43 inc/taskcategory.class.php:122 msgid "Task GUID" msgstr "GUID de la Tâche" -#: inc/taskcategory.class.php:38 inc/taskcategory.class.php:78 +#: inc/taskcategory.class.php:46 inc/taskcategory.class.php:86 +#: inc/taskcategory.class.php:125 inc/taskcategory.class.php:173 msgid "Sub-process" msgstr "Sous-processus" +#: inc/taskcategory.class.php:117 +#| msgid "Process case" +#| msgid_plural "Process cases" +msgid "Process name" +msgstr "Nom du procesus" + +#~ msgid "Delete case" +#~ msgstr "Delete case" + #~ msgid "Case item" #~ msgstr "Case item" diff --git a/locales/source.po b/locales/source.po index 17ae5a9..ea91229 100644 --- a/locales/source.po +++ b/locales/source.po @@ -1,15 +1,15 @@ msgid "" msgstr "" "Project-Id-Version: Processmaker plugin 3.3.0\n" -"POT-Creation-Date: 2018-08-06 14:32+0200\n" -"PO-Revision-Date: 2018-08-06 14:33+0200\n" +"POT-Creation-Date: 2020-07-13 09:41+0200\n" +"PO-Revision-Date: 2020-07-13 11:03+0200\n" "Last-Translator: tomolimo\n" "Language-Team: en_GB\n" "Language: en_GB\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 2.1.1\n" +"X-Generator: Poedit 2.3.1\n" "X-Poedit-Basepath: ..\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Poedit-KeywordsList: ;__:1,2t;_e:1,2t;_x:1c,2,3t;_n:1,2,4t;__s:1,2t;" @@ -22,239 +22,324 @@ msgstr "" "X-Poedit-SearchPath-5: hook.php\n" "X-Poedit-SearchPath-6: setup.php\n" -#: ajax/task_users.php:25 +#: ajax/task_users.php:36 msgid "Re-assign task to" msgstr "Re-assign task to" -#: ajax/task_users.php:45 +#: ajax/task_users.php:60 ajax/task_users.php:61 ajax/task_users.php:124 msgid "Re-assign" msgstr "Re-assign" -#: front/case.form.php:35 +#: ajax/task_users.php:110 +#| msgid "Task already assigned to this person!" +msgid "Task is already assigned to this user or group!" +msgstr "Task is already assigned to this user or group!" + +#: ajax/task_users.php:110 ajax/task_users.php:122 +#| msgid "Re-assign task to" +msgid "Re-assign task" +msgstr "Re-assign task" + +#: ajax/task_users.php:114 ajax/task_users.php:119 +#| msgid "Claim target task" +msgid "Un-claim task" +msgstr "Un-claim task" + +#: ajax/task_users.php:115 +msgid "" +"Please input reason to un-claim
(task will be re-assigned to former " +"group):" +msgstr "" +"Please input reason to un-claim
(task will be re-assigned to former " +"group):" + +#: ajax/task_users.php:116 +msgid "Un-claim" +msgstr "Un-claim" + +#: ajax/task_users.php:119 +msgid "Can\\'t un-assign Task!" +msgstr "Can\\'t un-assign Task!" + +#: ajax/task_users.php:123 +msgid "Please input reason to re-assign:" +msgstr "Please input reason to re-assign:" + +#: ajax/task_users.php:134 +msgid "Send reminder" +msgstr "Send reminder" + +#: front/case.form.php:32 msgid "Case has been deleted!" msgstr "Case has been deleted!" -#: front/case.form.php:37 +#: front/case.form.php:34 msgid "Unable to delete case!" msgstr "Unable to delete case!" -#: front/case.form.php:50 +#: front/case.form.php:46 msgid "Case has been cancelled!" msgstr "Case has been cancelled!" -#: front/case.form.php:52 front/case.form.php:55 +#: front/case.form.php:48 front/case.form.php:51 msgid "Unable to cancel case!" msgstr "Unable to cancel case!" -#: front/case.form.php:76 front/case.form.php:78 inc/case.class.php:1018 +#: front/case.form.php:70 front/case.form.php:72 inc/case.class.php:1310 msgid "Process cases" msgstr "Process cases" -#: front/case.php:5 front/caselink.form.php:27 front/caselink.php:5 -#: front/process.form.php:25 front/process.php:5 inc/caselink.class.php:143 -#: inc/config.class.php:50 inc/config.class.php:354 inc/process.class.php:393 +#: front/case.php:5 front/caselink.form.php:29 front/caselink.php:5 +#: front/process.form.php:27 front/process.php:5 inc/caselink.class.php:157 +#: inc/config.class.php:50 inc/config.class.php:363 inc/process.class.php:409 #: inc/profile.class.php:52 inc/profile.class.php:83 msgid "ProcessMaker" msgstr "ProcessMaker" -#: front/processmaker.form.php:85 +#: front/processmaker.form.php:80 msgid "Task re-assigned!" msgstr "Task re-assigned!" -#: front/processmaker.form.php:87 +#: front/processmaker.form.php:82 msgid "Error re-assigning task: " msgstr "Error re-assigning task: " -#: front/processmaker.form.php:90 -msgid "Task already assigned to this person!" -msgstr "Task already assigned to this person!" +#: front/processmaker.form.php:93 +#| msgid "Task name" +msgid "Task un-claimed!" +msgstr "Task un-claimed!" + +#: front/processmaker.form.php:95 +msgid "" +"Can't un-claim task! Verify 'Assignement Rules' in the process definition." +msgstr "" +"Can't un-claim task! Verify 'Assignement Rules' in the process definition." #: front/processmaker.helpdesk.form.php:21 msgid "Process - Case" msgstr "Process - Case" -#: front/processmaker.helpdesk.form.php:24 inc/case.class.php:605 +#: front/processmaker.helpdesk.form.php:24 inc/case.class.php:799 msgid "Select the process you want to add" msgstr "Select the process you want to add" -#: hook.php:31 inc/taskcategory.class.php:98 +#: hook.php:9 inc/taskcategory.class.php:214 msgid "Synchronize Task List" msgstr "Synchronize Task List" -#: hook.php:158 inc/case.class.php:77 +#: hook.php:76 inc/case.class.php:89 msgid "Case" msgstr "Case" -#: hook.php:158 inc/case.class.php:586 inc/case.class.php:1062 +#: hook.php:76 inc/case.class.php:778 inc/case.class.php:1355 msgid "Status" msgstr "Status" -#: inc/case.class.php:23 inc/case.class.php:90 inc/case.class.php:92 +#: inc/case.class.php:25 inc/case.class.php:102 inc/case.class.php:104 msgid "Process case" msgid_plural "Process cases" msgstr[0] "Process case" msgstr[1] "Process cases" -#: inc/case.class.php:80 +#: inc/case.class.php:92 msgid "Sub-case" msgstr "Sub-case" -#: inc/case.class.php:312 +#: inc/case.class.php:303 +#, php-format +msgid "" +"Task un-claimed!
Case: %s
Task: \"%s\" has been " +"un-assigned from \"%s\" and assigned to \"%s\" group.
Reason: %s" +msgstr "" +"Task un-claimed!
Case: %s
Task: \"%s\" has been " +"un-assigned from \"%s\" and assigned to \"%s\" group.
Reason: %s" + +#: inc/case.class.php:420 +#, php-format +msgid "" +"Task re-assigned!
Case: %s
Task: \"%s\" has " +"been re-assigned from \"%s\" to \"%s\".
Reason: %s" +msgstr "" +"Task re-assigned!
Case: %s
Task: \"%s\" has " +"been re-assigned from \"%s\" to \"%s\".
Reason: %s" + +#: inc/case.class.php:429 +#, php-format +msgid "" +"Task assigned!
Case: %s
Task: \"%s\" has been " +"assigned to \"%s\".
Reason: %s" +msgstr "" +"Task assigned!
Case: %s
Task: \"%s\" has been " +"assigned to \"%s\".
Reason: %s" + +#: inc/case.class.php:510 msgid "Current task(s) properties" msgstr "Current task(s) properties" -#: inc/case.class.php:316 inc/task.class.php:263 +#: inc/case.class.php:514 inc/task.class.php:283 msgid "Task" msgstr "Task" -#: inc/case.class.php:317 inc/task.class.php:264 +#: inc/case.class.php:515 inc/task.class.php:284 msgid "Task guid" msgstr "Task guid" -#: inc/case.class.php:318 inc/task.class.php:265 +#: inc/case.class.php:516 inc/task.class.php:285 msgid "Current user" msgstr "Current user" -#: inc/case.class.php:319 inc/task.class.php:266 +#: inc/case.class.php:517 inc/task.class.php:286 msgid "Task delegation date" msgstr "Task delegation date" -#: inc/case.class.php:341 inc/task.class.php:275 +#: inc/case.class.php:539 inc/task.class.php:295 msgid "To be claimed" msgstr "To be claimed" -#: inc/case.class.php:377 +#: inc/case.class.php:575 msgid "Sub-case properties" msgstr "Sub-case properties" -#: inc/case.class.php:380 +#: inc/case.class.php:578 msgid "Parent case properties" msgstr "Parent case properties" -#: inc/case.class.php:382 +#: inc/case.class.php:580 msgid "Case properties" msgstr "Case properties" -#: inc/case.class.php:387 inc/case.class.php:584 inc/case.class.php:1036 -#: inc/process.class.php:548 +#: inc/case.class.php:585 inc/case.class.php:776 inc/case.class.php:1328 +#: inc/process.class.php:576 msgid "Process" msgstr "Process" -#: inc/case.class.php:388 +#: inc/case.class.php:586 inc/notificationtargetprocessmaker.class.php:64 msgid "Case title" msgstr "Case title" -#: inc/case.class.php:389 +#: inc/case.class.php:587 msgid "Case number" msgstr "Case number" -#: inc/case.class.php:390 +#: inc/case.class.php:588 msgid "Case status" msgstr "Case status" -#: inc/case.class.php:391 +#: inc/case.class.php:589 msgid "Case guid" msgstr "Case guid" -#: inc/case.class.php:392 +#: inc/case.class.php:590 msgid "Creator" msgstr "Creator" -#: inc/case.class.php:393 +#: inc/case.class.php:591 inc/case.class.php:1370 msgid "Creation date" msgstr "Creation date" -#: inc/case.class.php:394 +#: inc/case.class.php:592 msgid "Last update" msgstr "Last update" -#: inc/case.class.php:479 +#: inc/case.class.php:675 #, php-format msgid "Case is linked to a %1s" msgstr "Case is linked to a %1s" -#: inc/case.class.php:481 +#: inc/case.class.php:677 #, php-format msgid "Sub-case is linked to a %1s" msgstr "Sub-case is linked to a %1s" -#: inc/case.class.php:506 +#: inc/case.class.php:698 msgid "Case cancellation" msgstr "Case cancellation" -#: inc/case.class.php:507 +#: inc/case.class.php:699 msgid "Cancel case" msgstr "Cancel case" -#: inc/case.class.php:511 +#: inc/case.class.php:703 msgid "Confirm cancellation?" msgstr "Confirm cancellation?" -#: inc/case.class.php:511 inc/profile.class.php:23 +#: inc/case.class.php:703 inc/profile.class.php:23 msgid "Cancel" msgstr "Cancel" -#: inc/case.class.php:532 +#: inc/case.class.php:717 msgid "Case deletion" msgstr "Case deletion" -#: inc/case.class.php:533 -msgid "Delete case" -msgstr "Delete case" - -#: inc/case.class.php:585 inc/case.class.php:1029 +#: inc/case.class.php:777 inc/case.class.php:1321 msgid "Title" msgstr "Title" -#: inc/case.class.php:587 inc/case.class.php:1070 +#: inc/case.class.php:779 inc/case.class.php:1362 msgid "Sub-case of" msgstr "Sub-case of" -#: inc/case.class.php:602 +#: inc/case.class.php:796 msgid "Add a new case" msgstr "Add a new case" -#: inc/case.class.php:995 +#: inc/case.class.php:1227 msgctxt "case_status" msgid "Draft" msgstr "Draft" -#: inc/case.class.php:996 +#: inc/case.class.php:1228 msgctxt "case_status" msgid "To do" msgstr "To do" -#: inc/case.class.php:997 +#: inc/case.class.php:1229 msgctxt "case_status" msgid "Completed" msgstr "Completed" -#: inc/case.class.php:998 +#: inc/case.class.php:1230 msgctxt "case_status" msgid "Cancelled" msgstr "Cancelled" -#: inc/case.class.php:1022 +#: inc/case.class.php:1314 msgid "ID" msgstr "ID" -#: inc/case.class.php:1049 +#: inc/case.class.php:1335 inc/notificationtargetprocessmaker.class.php:72 +#| msgid "Item entity" +msgid "Item type" +msgstr "Item type" + +#: inc/case.class.php:1342 msgid "Item" msgstr "Item" -#: inc/case.class.php:1056 +#: inc/case.class.php:1349 msgid "Item entity" msgstr "Item entity" -#: inc/casechangelog.class.php:35 +#: inc/case.class.php:1379 +#| msgid "Last update" +msgid "Last update date" +msgstr "Last update date" + +#: inc/case.class.php:1392 +#, php-format +#| msgid "Case status" +msgid "Case status is '%s'" +msgstr "Case status is '%s'" + +#: inc/casechangelog.class.php:36 msgid "Change log" msgstr "Change log" -#: inc/casedynaform.class.php:80 +#: inc/casedynaform.class.php:83 msgid "Dynaforms" msgstr "Dynaforms" -#: inc/casehistory.class.php:35 +#: inc/casehistory.class.php:36 msgid "History" msgstr "History" @@ -266,43 +351,51 @@ msgstr "Case-links" msgid "Case-link" msgstr "Case-link" -#: inc/caselink.class.php:75 inc/caselink.class.php:165 +#: inc/caselink.class.php:75 +msgid "Synchronous" +msgstr "Synchronous" + +#: inc/caselink.class.php:80 inc/caselink.class.php:179 msgid "External data" msgstr "External data" -#: inc/caselink.class.php:80 inc/caselink.class.php:171 +#: inc/caselink.class.php:85 inc/caselink.class.php:185 msgid "Self" msgstr "Self" -#: inc/caselink.class.php:85 inc/caselink.class.php:189 +#: inc/caselink.class.php:90 inc/caselink.class.php:203 msgid "Source task GUID" msgstr "Source task GUID" -#: inc/caselink.class.php:93 inc/caselink.class.php:195 +#: inc/caselink.class.php:98 inc/caselink.class.php:209 msgid "Target task GUID" msgstr "Target task GUID" -#: inc/caselink.class.php:101 inc/caselink.class.php:207 +#: inc/caselink.class.php:106 inc/caselink.class.php:221 msgid "Target process GUID" msgstr "Target process GUID" -#: inc/caselink.class.php:110 inc/caselink.class.php:201 +#: inc/caselink.class.php:115 inc/caselink.class.php:215 msgid "Target dynaform GUID" msgstr "Target dynaform GUID" -#: inc/caselink.class.php:115 inc/caselink.class.php:213 +#: inc/caselink.class.php:120 inc/caselink.class.php:227 msgid "Source condition" msgstr "Source condition" -#: inc/caselink.class.php:121 inc/caselink.class.php:177 +#: inc/caselink.class.php:126 inc/caselink.class.php:191 msgid "Claim target task" msgstr "Claim target task" -#: inc/caselink.class.php:126 inc/caselink.class.php:183 +#: inc/caselink.class.php:136 +msgid "Impersonate target task user" +msgstr "Impersonate target task user" + +#: inc/caselink.class.php:141 inc/caselink.class.php:197 msgid "External application JSON config" msgstr "External application JSON config" -#: inc/casemap.class.php:43 +#: inc/casemap.class.php:44 msgid "Map" msgstr "Map" @@ -311,187 +404,303 @@ msgid "ProcessMaker setup" msgstr "ProcessMaker setup" #: inc/config.class.php:159 -msgid "Server URL (must be in same domain than GLPI)" -msgstr "Server URL (must be in same domain than GLPI)" +#| msgid "Server URL (must be in same domain than GLPI)" +msgid "" +"Server URL (must be in same domain than GLPI, if GLPI is using HTTPS, PM " +"server must also use HTTPS)" +msgstr "" +"Server URL (must be in same domain than GLPI, if GLPI is using HTTPS, PM " +"server must also use HTTPS)" -#: inc/config.class.php:164 +#: inc/config.class.php:163 msgid "Common domain with GLPI" msgstr "Common domain with GLPI" -#: inc/config.class.php:193 +#: inc/config.class.php:192 msgid "None!" msgstr "None!" -#: inc/config.class.php:202 +#: inc/config.class.php:201 +msgid "Verify SSL certificate" +msgstr "Verify SSL certificate" + +#: inc/config.class.php:206 msgid "Workspace Name" msgstr "Workspace Name" -#: inc/config.class.php:207 +#: inc/config.class.php:211 msgid "Server administrator name" msgstr "Server administrator name" -#: inc/config.class.php:212 +#: inc/config.class.php:216 msgid "Server administrator password" msgstr "Server administrator password" -#: inc/config.class.php:218 inc/config.class.php:258 +#: inc/config.class.php:222 inc/config.class.php:262 msgid "Connection status" msgstr "Connection status" -#: inc/config.class.php:234 +#: inc/config.class.php:238 msgid "SQL server setup" msgstr "SQL server setup" -#: inc/config.class.php:237 +#: inc/config.class.php:241 msgid "SQL server (MariaDB or MySQL)" msgstr "SQL server (MariaDB or MySQL)" -#: inc/config.class.php:242 +#: inc/config.class.php:246 msgid "Database name" msgstr "Database name" -#: inc/config.class.php:247 +#: inc/config.class.php:251 msgid "SQL user" msgstr "SQL user" -#: inc/config.class.php:252 +#: inc/config.class.php:256 msgid "SQL password" msgstr "SQL password" -#: inc/config.class.php:269 +#: inc/config.class.php:273 msgid "Theme Name" msgstr "Theme Name" -#: inc/config.class.php:275 +#: inc/config.class.php:279 msgid "Main Task Category (edit to change name)" msgstr "Main Task Category (edit to change name)" -#: inc/config.class.php:282 +#: inc/config.class.php:286 msgid "Task Writer (edit to change name)" msgstr "Task Writer (edit to change name)" -#: inc/config.class.php:302 +#: inc/config.class.php:306 msgid "Group in ProcessMaker which will contain all GLPI users" msgstr "Group in ProcessMaker which will contain all GLPI users" -#: inc/config.class.php:331 +#: inc/config.class.php:321 inc/process.class.php:510 inc/process.class.php:637 +msgid "Max cases per item (0=unlimited)" +msgstr "Max cases per item (0=unlimited)" + +#: inc/config.class.php:340 msgid "Processmaker system information" msgstr "Processmaker system information" -#: inc/config.class.php:334 inc/config.class.php:344 +#: inc/config.class.php:343 inc/config.class.php:353 msgid "Version" msgstr "Version" -#: inc/config.class.php:335 +#: inc/config.class.php:344 msgid "Web server" msgstr "Web server" -#: inc/config.class.php:336 +#: inc/config.class.php:345 msgid "Server name" msgstr "Server name" -#: inc/config.class.php:337 +#: inc/config.class.php:346 msgid "PHP version" msgstr "PHP version" -#: inc/config.class.php:338 +#: inc/config.class.php:347 msgid "DB version" msgstr "DB version" -#: inc/config.class.php:339 +#: inc/config.class.php:348 msgid "DB server IP" msgstr "DB server IP" -#: inc/config.class.php:340 +#: inc/config.class.php:349 msgid "DB name" msgstr "DB name" -#: inc/config.class.php:341 +#: inc/config.class.php:350 msgid "User browser" msgstr "User browser" -#: inc/config.class.php:342 +#: inc/config.class.php:351 msgid "User IP" msgstr "User IP" -#: inc/config.class.php:344 +#: inc/config.class.php:353 msgid "Not yet!" msgstr "Not yet!" -#: inc/process.class.php:350 +#: inc/notificationtargetcase.class.php:26 +msgid "Send email" +msgstr "Send email" + +#: inc/notificationtargetcase.class.php:37 +msgid "eMail recipients" +msgstr "eMail recipients" + +#: inc/notificationtargetprocessmaker.class.php:60 +#| msgid "Process case" +#| msgid_plural "Process cases" +msgid "Process category" +msgstr "Process category" + +#: inc/notificationtargetprocessmaker.class.php:61 +#| msgid "Process case" +#| msgid_plural "Process cases" +msgid "Process category id" +msgstr "Process category id" + +#: inc/notificationtargetprocessmaker.class.php:62 +#| msgid "Process case task" +#| msgid_plural "Process case tasks" +msgid "Process category comment" +msgstr "Process category comment" + +#: inc/notificationtargetprocessmaker.class.php:63 +#| msgid "Case guid" +msgid "Case id" +msgstr "Case id" + +#: inc/notificationtargetprocessmaker.class.php:65 +#| msgid "Case deletion" +msgid "Case description" +msgstr "Case description" + +#: inc/notificationtargetprocessmaker.class.php:67 +msgid "Case variable 'XXX'" +msgstr "Case variable 'XXX'" + +#: inc/notificationtargetprocessmaker.class.php:68 +msgid "List of values in 'YYY' array" +msgstr "List of values in 'YYY' array" + +#: inc/notificationtargetprocessmaker.class.php:69 +msgid "Number of rows in 'YYY' array" +msgstr "Number of rows in 'YYY' array" + +#: inc/notificationtargetprocessmaker.class.php:70 +msgid "Value for colname in 'YYY' array" +msgstr "Value for colname in 'YYY' array" + +#: inc/notificationtargetprocessmaker.class.php:71 +msgid "Value for key in 'ZZZ' assoc array (1-dimension array)" +msgstr "Value for key in 'ZZZ' assoc array (1-dimension array)" + +#: inc/notificationtargetprocessmaker.class.php:73 +#| msgid "Item" +msgid "Item id" +msgstr "Item id" + +#: inc/notificationtargetprocessmaker.class.php:74 +#| msgid "Item" +msgid "Item URL" +msgstr "Item URL" + +#: inc/notificationtargetprocessmaker.class.php:75 +#| msgid "Item entity" +msgid "Item title" +msgstr "Item title" + +#: inc/notificationtargetprocessmaker.class.php:83 +msgid "XXX is to be replaced by any case variable names" +msgstr "XXX is to be replaced by any case variable names" + +#: inc/notificationtargetprocessmaker.class.php:86 +msgid "YYY is to be replaced by any array variables" +msgstr "YYY is to be replaced by any array variables" + +#: inc/notificationtargetprocessmaker.class.php:90 +msgid "" +"ZZZ is to be replaced by any assoc array variables (1-dimension array with " +"key/value pairs)" +msgstr "" +"ZZZ is to be replaced by any assoc array variables (1-dimension array with " +"key/value pairs)" + +#: inc/notificationtargettask.class.php:195 +#| msgid "Task delegation date" +msgid "Task action" +msgstr "Task action" + +#: inc/process.class.php:366 msgid "Synchronize Process List" msgstr "Synchronize Process List" -#: inc/process.class.php:427 inc/process.class.php:589 +#: inc/process.class.php:443 inc/process.class.php:617 msgid "Process GUID" msgstr "Process GUID" -#: inc/process.class.php:433 +#: inc/process.class.php:449 msgid "Process type" msgstr "Process type" -#: inc/process.class.php:439 +#: inc/process.class.php:455 msgid "Hide case num. & title" msgstr "Hide case num. & title" -#: inc/process.class.php:445 +#: inc/process.class.php:461 msgid "Insert Task Category" msgstr "Insert Task Category" -#: inc/process.class.php:457 +#: inc/process.class.php:473 msgid "Ticket type (self-service)" msgstr "Ticket type (self-service)" -#: inc/process.class.php:464 inc/process.class.php:609 +#: inc/process.class.php:480 inc/process.class.php:642 msgid "Visible in Incident for Central interface" msgstr "Visible in Incident for Central interface" -#: inc/process.class.php:470 inc/process.class.php:614 +#: inc/process.class.php:486 inc/process.class.php:647 msgid "Visible in Request for Central interface" msgstr "Visible in Request for Central interface" -#: inc/process.class.php:476 inc/process.class.php:666 +#: inc/process.class.php:492 inc/process.class.php:699 msgid "Visible in Change" msgstr "Visible in Change" -#: inc/process.class.php:482 inc/process.class.php:671 +#: inc/process.class.php:498 inc/process.class.php:704 msgid "Visible in Problem" msgstr "Visible in Problem" -#: inc/process.class.php:519 +#: inc/process.class.php:504 +msgid "Maintenance" +msgstr "Maintenance" + +#: inc/process.class.php:547 msgctxt "process_type" msgid "Classic" msgstr "Classic" -#: inc/process.class.php:520 +#: inc/process.class.php:548 msgctxt "process_type" msgid "BPMN" msgstr "BPMN" -#: inc/process.class.php:546 +#: inc/process.class.php:574 msgid "Processes" msgstr "Processes" -#: inc/process.class.php:599 +#: inc/process.class.php:627 msgid "Hide case number and title in task descriptions" msgstr "Hide case number and title in task descriptions" -#: inc/process.class.php:604 +#: inc/process.class.php:632 msgid "Insert Task Category comments in Task Description" msgstr "Insert Task Category comments in Task Description" -#: inc/process.class.php:619 +#: inc/process.class.php:652 msgid "ITIL Category for Self-service interface (left empty to disable)" msgstr "ITIL Category for Self-service interface (left empty to disable)" -#: inc/process.class.php:646 +#: inc/process.class.php:679 msgid "Type for Self-service interface" msgstr "Type for Self-service interface" -#: inc/process.class.php:676 +#: inc/process.class.php:709 msgid "Process type (to be changed only if not up-to-date)" msgstr "Process type (to be changed only if not up-to-date)" +#: inc/process.class.php:862 +#, php-format +#| msgid "" +#| "ProcessMaker plugin is under maintenance, please retry later, thank you." +msgid "Process '%s' is under maintenance, please retry later, thank you." +msgstr "Process '%s' is under maintenance, please retry later, thank you." + #: inc/process_profile.class.php:26 inc/process_profile.class.php:45 msgid "Authorizations" msgstr "Authorizations" @@ -510,35 +719,44 @@ msgctxt "errors" msgid "Can't create case: no rights for it!" msgstr "Can't create case: no rights for it!" -#: inc/processmaker.class.php:984 +#: inc/processmaker.class.php:1012 msgid "Syncs GLPI users and groups into ProcessMaker." msgstr "Syncs GLPI users and groups into ProcessMaker." -#: inc/processmaker.class.php:986 +#: inc/processmaker.class.php:1014 msgid "Cleaning of orphan cases." msgstr "Cleaning of orphan cases." -#: inc/processmaker.class.php:986 +#: inc/processmaker.class.php:1014 msgid "Number of days to keep orphan cases" msgstr "Number of days to keep orphan cases" -#: inc/processmaker.class.php:988 +#: inc/processmaker.class.php:1016 msgid "To apply task actions between cases." msgstr "To apply task actions between cases." -#: inc/processmaker.class.php:1696 +#: inc/processmaker.class.php:1720 msgid "Case title: " msgstr "Case title: " -#: inc/processmaker.class.php:2178 +#: inc/processmaker.class.php:2275 msgid "A 'Case' is running!" msgstr "A 'Case' is running!" -#: inc/processmaker.class.php:2179 +#: inc/processmaker.class.php:2276 msgid "You must manage it first (see 'Process - Case' tab)!" msgstr "You must manage it first (see 'Process - Case' tab)!" -#: inc/processmaker.class.php:3038 +#: inc/processmaker.class.php:2774 +#, php-format +msgid "" +"Unable to derivate case! Retry in a moment, or ask your administrator." +"
Error code: %u
Error message: %s
" +msgstr "" +"Unable to derivate case! Retry in a moment, or ask your administrator." +"
Error code: %u
Error message: %s
" + +#: inc/processmaker.class.php:3281 msgid "" "ProcessMaker plugin is under maintenance, please retry later, thank you." msgstr "" @@ -552,36 +770,62 @@ msgstr "Process configuration" msgid "Cases" msgstr "Cases" +#: inc/profile.class.php:23 +msgid "Ad Hoc user re-assign" +msgstr "Ad Hoc user re-assign" + #: inc/task.class.php:29 msgid "Process case task" msgid_plural "Process case tasks" msgstr[0] "Process case task" msgstr[1] "Process case tasks" -#: inc/task.class.php:258 +#: inc/task.class.php:278 msgid "Sub-case task(s)" msgstr "Sub-case task(s)" -#: inc/taskcategory.class.php:21 inc/taskcategory.class.php:31 +#: inc/taskcategory.class.php:24 inc/taskcategory.class.php:116 +#| msgid "Process case" +#| msgid_plural "Process cases" +msgid "Process task" +msgstr "Process task" + +#: inc/taskcategory.class.php:29 +#| msgid "Task List" +msgid "Task list" +msgstr "Task list" + +#: inc/taskcategory.class.php:39 msgid "Task List" msgstr "Task List" -#: inc/taskcategory.class.php:32 +#: inc/taskcategory.class.php:40 inc/taskcategory.class.php:118 msgid "Task name" msgstr "Task name" -#: inc/taskcategory.class.php:34 inc/taskcategory.class.php:60 +#: inc/taskcategory.class.php:42 inc/taskcategory.class.php:68 +#: inc/taskcategory.class.php:121 inc/taskcategory.class.php:155 msgid "Start" msgstr "Start" -#: inc/taskcategory.class.php:35 +#: inc/taskcategory.class.php:43 inc/taskcategory.class.php:122 msgid "Task GUID" msgstr "Task GUID" -#: inc/taskcategory.class.php:38 inc/taskcategory.class.php:78 +#: inc/taskcategory.class.php:46 inc/taskcategory.class.php:86 +#: inc/taskcategory.class.php:125 inc/taskcategory.class.php:173 msgid "Sub-process" msgstr "Sub-process" +#: inc/taskcategory.class.php:117 +#| msgid "Process case" +#| msgid_plural "Process cases" +msgid "Process name" +msgstr "Process name" + +#~ msgid "Delete case" +#~ msgstr "Delete case" + #~ msgid "Case item" #~ msgstr "Case item" diff --git a/processmaker.xml b/processmaker.xml index 21d3f63..5d98a03 100644 --- a/processmaker.xml +++ b/processmaker.xml @@ -27,7 +27,7 @@ 9.1 - 3.4.22 + 3.4.23 9.2 diff --git a/setup.php b/setup.php index 70591c4..16e1b51 100644 --- a/setup.php +++ b/setup.php @@ -1,6 +1,6 @@ Date: Wed, 7 Oct 2020 11:39:44 +0200 Subject: [PATCH 13/15] Added prepareInputForAdd and prepareInputForUpdate to unclean entries to prevent conversion of ['<', '>', '<', '>'] into ['&lt;', '&gt;', '<', '>']; Set version to 3.4.24 --- inc/caselink.class.php | 10 ++++++++++ processmaker.xml | 2 +- setup.php | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/inc/caselink.class.php b/inc/caselink.class.php index 00b341c..57886b5 100644 --- a/inc/caselink.class.php +++ b/inc/caselink.class.php @@ -249,6 +249,16 @@ function getSearchOptions() { return $tab; } + + function prepareInputForUpdate($input) { + return Toolbox::unclean_cross_side_scripting_deep($input); + } + + + function prepareInputForAdd($input) { + return Toolbox::unclean_cross_side_scripting_deep($input); + } + //static function getMenuContent() { // if (!Session::haveRight('entity', READ)) { diff --git a/processmaker.xml b/processmaker.xml index 5d98a03..a7826e5 100644 --- a/processmaker.xml +++ b/processmaker.xml @@ -27,7 +27,7 @@ 9.1 - 3.4.23 + 3.4.24 9.2 diff --git a/setup.php b/setup.php index 16e1b51..b7c9a56 100644 --- a/setup.php +++ b/setup.php @@ -1,6 +1,6 @@ Date: Wed, 7 Oct 2020 11:42:20 +0200 Subject: [PATCH 14/15] Added feature GLPI_TASK_PREVENT_REASSIGN to prevent assignment of a list of users (can be PM UID, GLPI id, GLPI logon) to a specific task (can be task list) Set version to 3.4.25 --- ajax/dropdownUsers.php | 5 ---- ajax/task_users.php | 2 +- inc/case.class.php | 24 +++++++++---------- inc/task.class.php | 54 +++++++++++++++++++++++++++++++++--------- inc/user.class.php | 26 ++++++++++++++++++++ processmaker.xml | 2 +- setup.php | 2 +- 7 files changed, 84 insertions(+), 31 deletions(-) diff --git a/ajax/dropdownUsers.php b/ajax/dropdownUsers.php index 2ad0286..ccf9afa 100644 --- a/ajax/dropdownUsers.php +++ b/ajax/dropdownUsers.php @@ -77,11 +77,6 @@ } } -if (!function_exists('dpuser_cmp')) { - function dpuser_cmp($a, $b) { - return strcasecmp($a, $b); - } -} $datas = []; diff --git a/ajax/task_users.php b/ajax/task_users.php index d0df073..e6b9393 100644 --- a/ajax/task_users.php +++ b/ajax/task_users.php @@ -47,7 +47,7 @@ PluginProcessmakerUser::dropdown( ['name' => 'users_id_recipient', 'value' => $_REQUEST['users_id'], - 'used' => [$_REQUEST['users_id']], + 'used' => $_REQUEST['used'], 'entity' => 0, //$item->fields["entities_id"], // not used, as any user can be assigned to any tasks 'entity_sons' => false, // not used, as any user can be assigned to any tasks 'right' => 'all', diff --git a/inc/case.class.php b/inc/case.class.php index 05e3a24..e35297c 100644 --- a/inc/case.class.php +++ b/inc/case.class.php @@ -314,13 +314,13 @@ function unassignCase($delIndex, $taskGuid, $tasktype, $tasks_id, $itemtype, $op // unescape some chars and replace CRLF, CR or LF by
$info = str_replace(["\\'", '\\"', '\r\n', '\r', '\n'], ["'", '"', '
', '
', '
'], $info); - $glpi_task->add([$foreignkey => $glpi_task->fields[$foreignkey], - 'is_private' => 1, + $glpi_task->add([$foreignkey => $glpi_task->fields[$foreignkey], + 'is_private' => 0, // a post-only user can't create private task 'taskcategories_id' => $pm_process->fields['taskcategories_id'], - 'content' => $DB->escape($info), - 'users_id' => $PM_SOAP->taskWriter, - 'state' => Planning::INFO, - 'users_id_tech' => Session::getLoginUserID(), + 'content' => $DB->escape($info), + 'users_id' => $PM_SOAP->taskWriter, + 'state' => Planning::INFO, + 'users_id_tech' => Session::getLoginUserID(), ]); return true; @@ -439,13 +439,13 @@ public function reassignTask ($delIndex, $newDelIndex, $delThread, $newDelThread // unescape some chars and replace CRLF, CR or LF by
$info = str_replace(["\\'", '\\"', '\r\n', '\r', '\n'], ["'", '"', '
', '
', '
'], $info); - $glpi_task->add([$foreignkey => $glpi_task->fields[$foreignkey], - 'is_private' => 1, + $glpi_task->add([$foreignkey => $glpi_task->fields[$foreignkey], + 'is_private' => 0, // a post-only user can't create private task 'taskcategories_id' => $pm_process->fields['taskcategories_id'], - 'content' => $DB->escape($info), - 'users_id' => $PM_SOAP->taskWriter, - 'state' => Planning::INFO, - 'users_id_tech' => Session::getLoginUserID(), + 'content' => $DB->escape($info), + 'users_id' => $PM_SOAP->taskWriter, + 'state' => Planning::INFO, + 'users_id_tech' => Session::getLoginUserID(), ]); } } diff --git a/inc/task.class.php b/inc/task.class.php index d254674..0e90117 100644 --- a/inc/task.class.php +++ b/inc/task.class.php @@ -332,17 +332,49 @@ static function displayTabContentForItem(CommonGLPI $case, $tabnum = 1, $withtem // to load users for task re-assign only when task is not a sub-case echo "
".__('Loading...')."
"; - echo ""; + + // try to get users whom can't be assigned to this task + // already assigned user can't be assigned again to this task + $current_assigned_user = PluginProcessmakerUser::getGLPIUserId($currentUser->userId); + // and then any forbiden users defined from the case itself + $casevariablevalues = $case->getVariables(['GLPI_TASK_PREVENT_REASSIGN']); + $prevent_assign = []; + if (array_key_exists( 'GLPI_TASK_PREVENT_REASSIGN', $casevariablevalues ) && $casevariablevalues[ 'GLPI_TASK_PREVENT_REASSIGN' ] != '') { + $prevent_assign = json_decode($casevariablevalues[ 'GLPI_TASK_PREVENT_REASSIGN' ], true); + } + + + $used_users = []; + $used_users[] = $current_assigned_user; + if (array_key_exists($currentUser->taskId, $prevent_assign)) { + if (is_array($prevent_assign[$currentUser->taskId])) { + foreach ($prevent_assign[$currentUser->taskId] as $pmuser) { + $usr_id = PluginProcessmakerUser::getGlpiIdFromAny($pmuser); + if ($usr_id) { + $used_users[] = $usr_id; + } + } + } else { + $usr_id = PluginProcessmakerUser::getGlpiIdFromAny($prevent_assign[$currentUser->taskId]); + if ($usr_id) { + $used_users[] = $usr_id; + } + } + } + + $data = "{ + cases_id : {$case->getID()}, + items_id : {$case->fields['items_id']}, + itemtype : '{$case->fields['itemtype']}', + tasktype : '{$task[$tabnum]['itemtype']}', + tasks_id : {$task[$tabnum]['items_id']}, + users_id : {$current_assigned_user}, + taskGuid : '{$currentUser->taskId}', + delIndex : {$task[$tabnum]['del_index']}, + delThread : {$currentUser->delThread}, + used : [".join(',', array_unique($used_users))."] + }"; + echo html::scriptBlock("$('#divUsers-{$task[$tabnum]['del_index']}').load('".$CFG_GLPI["root_doc"]."/plugins/processmaker/ajax/task_users.php', $data);"); } if (!$currentUser->userId || !$task[$tabnum]['del_index']) { diff --git a/inc/user.class.php b/inc/user.class.php index bd864a9..305c5a7 100644 --- a/inc/user.class.php +++ b/inc/user.class.php @@ -185,6 +185,7 @@ public static function getGLPIUserId($pmUserId) { return 0; } + /** * Summary of getPMUserId * returns processmaker user id for given GLPI user id @@ -199,6 +200,31 @@ public static function getPMUserId($glpiUserId) { return false; } + + /** + * Summary of getGlpiIdFromAny + * Returns the GLPI id of the user or false if not found + * Accept either PM GUID, GLPI logon, or GLPI ID + * @param $any + * @return mixed GLPI ID of the user or fasle if not found + */ + public static function getGlpiIdFromAny($any) { + $ret = self::getGLPIUserId($any); + if ($ret) { + return $ret; + } + $ret = self::getPMUserId($any); + if ($ret) { + return $any; + } + $usr = new User; + if ($usr->getFromDBbyName($any)) { + return $usr->getId(); + } + return false; + } + + ///** // * Summary of getNewPassword // * @param mixed $username diff --git a/processmaker.xml b/processmaker.xml index a7826e5..2a38ba1 100644 --- a/processmaker.xml +++ b/processmaker.xml @@ -27,7 +27,7 @@ 9.1 - 3.4.24 + 3.4.25 9.2 diff --git a/setup.php b/setup.php index b7c9a56..04a3dbb 100644 --- a/setup.php +++ b/setup.php @@ -1,6 +1,6 @@ Date: Mon, 12 Oct 2020 15:34:41 +0200 Subject: [PATCH 15/15] Fixed issue when user logon has digits in its name like generic users (ex: CNU_1005) Set version to 3.4.2 --- inc/task.class.php | 14 +++++--------- inc/user.class.php | 10 ++++++---- processmaker.xml | 2 +- setup.php | 2 +- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/inc/task.class.php b/inc/task.class.php index 0e90117..0099e47 100644 --- a/inc/task.class.php +++ b/inc/task.class.php @@ -347,15 +347,11 @@ static function displayTabContentForItem(CommonGLPI $case, $tabnum = 1, $withtem $used_users = []; $used_users[] = $current_assigned_user; if (array_key_exists($currentUser->taskId, $prevent_assign)) { - if (is_array($prevent_assign[$currentUser->taskId])) { - foreach ($prevent_assign[$currentUser->taskId] as $pmuser) { - $usr_id = PluginProcessmakerUser::getGlpiIdFromAny($pmuser); - if ($usr_id) { - $used_users[] = $usr_id; - } - } - } else { - $usr_id = PluginProcessmakerUser::getGlpiIdFromAny($prevent_assign[$currentUser->taskId]); + if (!is_array($prevent_assign[$currentUser->taskId])) { + $prevent_assign[$currentUser->taskId] = [$prevent_assign[$currentUser->taskId]]; + } + foreach ($prevent_assign[$currentUser->taskId] as $pmuser) { + $usr_id = PluginProcessmakerUser::getGlpiIdFromAny($pmuser); if ($usr_id) { $used_users[] = $usr_id; } diff --git a/inc/user.class.php b/inc/user.class.php index 305c5a7..2ed155f 100644 --- a/inc/user.class.php +++ b/inc/user.class.php @@ -193,9 +193,11 @@ public static function getGLPIUserId($pmUserId) { * @return string which is the uid of user in Processmaker database, or false if not found */ public static function getPMUserId($glpiUserId) { - $obj = new self; - if ($obj->getFromDB( Toolbox::cleanInteger($glpiUserId) )) { - return $obj->fields['pm_users_id']; + if (is_numeric($glpiUserId)) { + $obj = new self; + if ($obj->getFromDB($glpiUserId)) { + return $obj->fields['pm_users_id']; + } } return false; } @@ -206,7 +208,7 @@ public static function getPMUserId($glpiUserId) { * Returns the GLPI id of the user or false if not found * Accept either PM GUID, GLPI logon, or GLPI ID * @param $any - * @return mixed GLPI ID of the user or fasle if not found + * @return mixed GLPI ID of the user or false if not found */ public static function getGlpiIdFromAny($any) { $ret = self::getGLPIUserId($any); diff --git a/processmaker.xml b/processmaker.xml index 2a38ba1..75a951b 100644 --- a/processmaker.xml +++ b/processmaker.xml @@ -27,7 +27,7 @@ 9.1 - 3.4.25 + 3.4.26 9.2 diff --git a/setup.php b/setup.php index 04a3dbb..d451dba 100644 --- a/setup.php +++ b/setup.php @@ -1,6 +1,6 @@