diff --git a/workflow/engine/src/ProcessMaker/Project/Bpmn.php b/workflow/engine/src/ProcessMaker/Project/Bpmn.php index 3ebb318b1..7807860cc 100644 --- a/workflow/engine/src/ProcessMaker/Project/Bpmn.php +++ b/workflow/engine/src/ProcessMaker/Project/Bpmn.php @@ -1,36 +1,39 @@ array( - "PRJ_UID", "PRO_UID", "BOU_ELEMENT_TYPE", "BOU_REL_POSITION", - "BOU_SIZE_IDENTICAL", "DIA_UID", "BOU_UID", "ELEMENT_UID" + "PRJ_UID", + "PRO_UID", + "BOU_ELEMENT_TYPE", + "BOU_REL_POSITION", + "BOU_SIZE_IDENTICAL", + "DIA_UID", + "BOU_UID", + "ELEMENT_UID" ), "event" => array( - "PRJ_UID", "PRO_UID", "BOU_ELEMENT_TYPE", "BOU_REL_POSITION", - "BOU_SIZE_IDENTICAL", "DIA_UID", "BOU_UID", "ELEMENT_UID", "EVN_ATTACHED_TO", "EVN_CONDITION" + "PRJ_UID", + "PRO_UID", + "BOU_ELEMENT_TYPE", + "BOU_REL_POSITION", + "BOU_SIZE_IDENTICAL", + "DIA_UID", + "BOU_UID", + "ELEMENT_UID", + "EVN_ATTACHED_TO", + "EVN_CONDITION" ), - "gateway" => array("BOU_ELEMENT_TYPE", "BOU_REL_POSITION", "BOU_SIZE_IDENTICAL", "BOU_UID", - "DIA_UID", "ELEMENT_UID", "PRJ_UID", "PRO_UID" + "gateway" => array( + "BOU_ELEMENT_TYPE", + "BOU_REL_POSITION", + "BOU_SIZE_IDENTICAL", + "BOU_UID", + "DIA_UID", + "ELEMENT_UID", + "PRJ_UID", + "PRO_UID" ), "artifact" => array( - "PRJ_UID", "PRO_UID", "BOU_ELEMENT_TYPE", "BOU_REL_POSITION", - "BOU_SIZE_IDENTICAL", "DIA_UID", "BOU_UID", "ELEMENT_UID" + "PRJ_UID", + "PRO_UID", + "BOU_ELEMENT_TYPE", + "BOU_REL_POSITION", + "BOU_SIZE_IDENTICAL", + "DIA_UID", + "BOU_UID", + "ELEMENT_UID" ), - "flow" => array("PRJ_UID", "DIA_UID", "FLO_ELEMENT_DEST_PORT", "FLO_ELEMENT_ORIGIN_PORT"), - "data" => array("PRJ_UID"), - "participant" => array("PRJ_UID"), - "laneset" => array("BOU_ELEMENT_TYPE", "BOU_SIZE_IDENTICAL", "BOU_UID"), - "lane" => array("BOU_ELEMENT_TYPE", "BOU_SIZE_IDENTICAL", "BOU_UID") + "flow" => array( + "PRJ_UID", + "DIA_UID", + "FLO_ELEMENT_DEST_PORT", + "FLO_ELEMENT_ORIGIN_PORT" + ), + "data" => array( + "PRJ_UID" + ), + "participant" => array( + "PRJ_UID" + ), + "laneset" => array( + "BOU_ELEMENT_TYPE", + "BOU_SIZE_IDENTICAL", + "BOU_UID" + ), + "lane" => array( + "BOU_ELEMENT_TYPE", + "BOU_SIZE_IDENTICAL", + "BOU_UID" + ) ); private $arrayElementOriginChecked = array(); + protected $contextLog = array(); public function __construct($data = null) { if (! is_null($data)) { $this->create($data); } + //Define the variables for the logging + $info = array( + 'ip' => G::getIpAddress(), + 'workspace' => (defined("SYS_SYS"))? SYS_SYS : "Workspace undefined" + ); + $this->setContextLog($info); + } + + /** + * Get the $contextLog value. + * + * @return string + */ + public function getContextLog() + { + return $this->contextLog; + } + + /** + * Set the value of $contextLog. + * + * @param array $k + * @return void + */ + public function setContextLog($k) + { + $this->contextLog = array_merge($this->contextLog, $k); + } + + /** + * Bulk actions + * We will have actions in many projects + * + * @param array $data + * @return array $result, related to actions send + */ + public static function doBulk($data) + { + //We will review the data format + if (!is_array($data)) { + $isJson = is_string($data) && is_array(G::json_decode($data, true)); + if ($isJson) { + $data = G::json_decode($data, true); + } else { + return; + } + } + //We get the action and execute + $response = array(); + if (isset($data['data'])) { + foreach ($data['data'] as $key => $val) { + if (isset($val['action'])) { + switch ($val['action']) { + case 'delete': + $response[] = array( + 'action' => $val['action'], + 'data'=> self::doBulkDelete($val['data']) + ); + break; + default: + $response[] = array( + 'action' => $val['action'], + 'title' => 'Unknown action', + 'status' => '400', + 'detail' => "Unknown action.", + 'result' => false + ); + //todo, we can add more bulk actions + } + } + } + } + $result['data'] = $response; + + return $result; + } + + /** + * Bulk delete + * We will delete many projects in bulk + * + * @param array $data, array of projectUid + * @return array $response, information about the action with the projectUid + */ + public static function doBulkDelete($data) + { + //We reviewed the action in all projectUid + $response = array(); + foreach ($data as $key => $val) { + //Review if the type is "prj_uid" + if (isset($val['prj_uid']) && !empty($val['prj_uid']) ) { + //The project exist? + if (!Bpmn::exists($val['prj_uid'])) { + $response[] = array( + 'type' => $val['type'], + 'prj_uid' => $val['prj_uid'], + 'title' => 'Not found', + 'token' => strtotime("now"), + 'status' => '404', + 'detail' => "The row {$val['prj_uid']} in table Process doesn't exist!.", + 'result' => false + ); + continue; + } + //The project has cases? + $oBpmnWf = BpmnWorkflow::load($val['prj_uid']); + if (!$oBpmnWf->canRemove()) { + $response[] = array( + 'type' => $val['type'], + 'prj_uid' => $val['prj_uid'], + 'title' => 'Unable to delete project', + 'token' => strtotime("now"), + 'status' => '403', + 'detail' => "Project with prj_uid: {$val['prj_uid']} can not be deleted, it has started cases.", + 'result' => false + + ); + continue; + } + //We will to remove + $oBpmnWf = BpmnWorkflow::load($val['prj_uid']); + $oBpmnWf->remove(); + $response[] = array( + 'type' => $val['type'], + 'prj_uid' => $val['prj_uid'], + 'status' => '200', + 'result' => true + ); + } else { + //Is not defined the "prj_uid" + $response[] = array( + 'type' => $val['type'], + 'title' => 'Unknown field', + 'token' => strtotime("now"), + 'status' => '400', + 'detail' => "Unknown field.", + 'result' => false + + ); + } + } + $me = new self(); + $me->setContextLog($response); + $me->syslog( + 'DoBulkDelete', + 200, + 'Do bulk delete', + $me->getContextLog() + ); + + return $response; } public function exists($projectUid) @@ -107,8 +306,8 @@ class Bpmn extends Handler $me = new self(); $project = ProjectPeer::retrieveByPK($prjUid); - if (! is_object($project)) { - throw new Exception\ProjectNotFound($me, $prjUid); + if (!is_object($project)) { + throw new ProjectNotFound($me, $prjUid); } $me->project = $project; @@ -1463,5 +1662,31 @@ class Bpmn extends Handler throw $e; } } + + /** + * Logging information related to project + * When the user doDeleteBulk + * + * @param string $channel + * @param string $level + * @param string $message + * @param array $context + * + * @return void + * @throws Exception + */ + private function syslog( + $channel, + $level, + $message, + $context = array() + ) + { + try { + Bootstrap::registerMonolog($channel, $level, $message, $context, $context['workspace'], 'processmaker.log'); + } catch (Exception $e) { + throw $e; + } + } } diff --git a/workflow/engine/src/ProcessMaker/Services/Api/Project.php b/workflow/engine/src/ProcessMaker/Services/Api/Project.php index cc38f31a2..859e40511 100644 --- a/workflow/engine/src/ProcessMaker/Services/Api/Project.php +++ b/workflow/engine/src/ProcessMaker/Services/Api/Project.php @@ -2,6 +2,7 @@ namespace ProcessMaker\Services\Api; use Luracast\Restler\RestException; +use ProcessMaker\Project\Bpmn; use ProcessMaker\Services\Api; use \ProcessMaker\Project\Adapter; use \ProcessMaker\Util; @@ -11,6 +12,8 @@ use \ProcessMaker\BusinessModel\Migrator\GranularExporter; use \ProcessMaker\BusinessModel\Migrator\ExportObjects; use \ProcessMaker\Util\IO\HttpStream; use \ProcessMaker\Util\Common; +use ProcessMaker\Project\Adapter\BpmnWorkflow; +use Exception; /** * Class Project @@ -43,7 +46,7 @@ class Project extends Api $projects = Adapter\BpmnWorkflow::getList($start, $limit, $filter, CASE_LOWER); return DateTime::convertUtcToIso8601($projects, $this->arrayFieldIso8601); - } catch (\Exception $e) { + } catch (Exception $e) { throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); } } @@ -62,7 +65,7 @@ class Project extends Api $property = $userProperty->loadOrCreateIfNotExists($this->getUserId()); $project['usr_setting_designer'] = isset($property['USR_SETTING_DESIGNER']) ? \G::json_decode($property['USR_SETTING_DESIGNER']) : null; return DateTime::convertUtcToIso8601($project, $this->arrayFieldIso8601); - } catch (\Exception $e) { + } catch (Exception $e) { throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); } } @@ -87,7 +90,7 @@ class Project extends Api } Validator::throwExceptionIfDataNotMetIso8601Format($request_data, $this->arrayFieldIso8601); return Adapter\BpmnWorkflow::createFromStruct(DateTime::convertDataToUtc($request_data, $this->arrayFieldIso8601)); - } catch (\Exception $e) { + } catch (Exception $e) { throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); } } @@ -112,7 +115,7 @@ class Project extends Api Validator::throwExceptionIfDataNotMetIso8601Format($request_data, $this->arrayFieldIso8601); return Adapter\BpmnWorkflow::updateFromStruct($prj_uid, DateTime::convertDataToUtc($request_data, $this->arrayFieldIso8601)); - } catch (\Exception $e) { + } catch (Exception $e) { throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); } } @@ -120,13 +123,39 @@ class Project extends Api /** * @param string $prj_uid {@min 1}{@max 32} * @url DELETE /:prj_uid + * @throws Exception */ public function delete($prj_uid) { try { - $oBpmnWf = Adapter\BpmnWorkflow::load($prj_uid); - $oBpmnWf->remove(); - } catch (\Exception $e) { + if (Bpmn::exists($prj_uid)) { + $oBpmnWf = BpmnWorkflow::load($prj_uid); + $oBpmnWf->remove(); + } else { + throw new Exception("The project cannot be found or it was already deleted."); + } + } catch (Exception $e) { + throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); + } + } + + /** + * Bulk actions + * @url POST /bulk + * + * @access protected + * @class AccessControl {@permission PM_FACTORY} + * + * @param array $request_data + * @return array $response + * @throws Exception + */ + public function bulk($request_data) + { + try { + $response = Bpmn::doBulk($request_data); + return $response; + } catch (Exception $e) { throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); } } @@ -144,7 +173,7 @@ class Project extends Api $exportProcess= new ExportObjects(); $result = $exportProcess->objectList($prj_uid); return $result; - } catch (\Exception $e) { + } catch (Exception $e) { throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); } } @@ -224,7 +253,7 @@ class Project extends Api $response = $arrayData; return $response; - } catch (\Exception $e) { + } catch (Exception $e) { throw (new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage())); } } @@ -259,7 +288,7 @@ class Project extends Api return DateTime::convertUtcToIso8601($response, $this->arrayFieldIso8601); - } catch (\Exception $e) { + } catch (Exception $e) { throw (new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage())); } } @@ -279,7 +308,7 @@ class Project extends Api $process->setArrayFieldNameForException(array("processUid" => "prj_uid")); $arrayData = $process->update($prj_uid, DateTime::convertDataToUtc($request_data, $this->arrayFieldIso8601)); - } catch (\Exception $e) { + } catch (Exception $e) { throw (new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage())); } } @@ -317,7 +346,7 @@ class Project extends Api $response = $arrayData; return $response; - } catch (\Exception $e) { + } catch (Exception $e) { throw (new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage())); } } @@ -337,7 +366,7 @@ class Project extends Api $response = $process->getDynaForms($prj_uid); return DateTime::convertUtcToIso8601($response, $this->arrayFieldIso8601); - } catch (\Exception $e) { + } catch (Exception $e) { throw (new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage())); } } @@ -357,7 +386,7 @@ class Project extends Api $response = $process->getInputDocuments($prj_uid); return $response; - } catch (\Exception $e) { + } catch (Exception $e) { throw (new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage())); } } @@ -377,7 +406,7 @@ class Project extends Api $response = $process->getVariables("ALL", $prj_uid); return $response; - } catch (\Exception $e) { + } catch (Exception $e) { throw (new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage())); } } @@ -399,7 +428,7 @@ class Project extends Api $response = ($grid_uid == "")? $process->getVariables("GRID", $prj_uid) : $process->getVariables("GRIDVARS", $prj_uid, $grid_uid); return $response; - } catch (\Exception $e) { + } catch (Exception $e) { throw (new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage())); } } @@ -419,7 +448,7 @@ class Project extends Api $response = $process->getLibraries($prj_uid); return $response; - } catch (\Exception $e) { + } catch (Exception $e) { throw (new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage())); } } @@ -435,7 +464,7 @@ class Project extends Api $oRoute = new \Route(); $result = $oRoute->updateRouteOrder($request_data); return $result; - } catch (\Exception $e) { + } catch (Exception $e) { throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); } } @@ -451,7 +480,7 @@ class Project extends Api $oRoute = new \Route(); $result = $oRoute->updateRouteOrderFromProject($prj_uid); return $result; - } catch (\Exception $e) { + } catch (Exception $e) { throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); } } diff --git a/workflow/engine/templates/processes/main.js b/workflow/engine/templates/processes/main.js index 16e30d51c..07f5a895f 100644 --- a/workflow/engine/templates/processes/main.js +++ b/workflow/engine/templates/processes/main.js @@ -1,7 +1,5 @@ -/* - * @author: Erik A. Ortiz - * Aug 20th, 2010 - */ +// TODO: Move RCBase64 to an individual file +var RCBase64={keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t,r,s,o,i,n,a,h="",c=0;for(e=this.utf8_encode(e);c>2,i=(3&t)<<4|r>>4,n=(15&r)<<2|s>>6,a=63&s,isNaN(r)?n=a=64:isNaN(s)&&(a=64),h=h+this.keyStr.charAt(o)+this.keyStr.charAt(i)+this.keyStr.charAt(n)+this.keyStr.charAt(a);return h},decode:function(e){var t,r,s,o,i,n,a,h="",c=0;for(e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");c>4,r=(15&i)<<4|n>>2,s=(3&n)<<6|a,h+=String.fromCharCode(t),64!==n&&(h+=String.fromCharCode(r)),64!==a&&(h+=String.fromCharCode(s));return h=this.utf8_decode(h)},utf8_encode:function(e){e=e.replace(/\r\n/g,"\n");var t,r,s="";for(t=0;tr?s+=String.fromCharCode(r):r>127&&2048>r?(s+=String.fromCharCode(r>>6|192),s+=String.fromCharCode(63&r|128)):(s+=String.fromCharCode(r>>12|224),s+=String.fromCharCode(r>>6&63|128),s+=String.fromCharCode(63&r|128));return s},utf8_decode:function(e){for(var t="",r=0,s=0,o=0,i=0;rs?(t+=String.fromCharCode(s),r++):s>191&&224>s?(o=e.charCodeAt(r+1),t+=String.fromCharCode((31&s)<<6|63&o),r+=2):(o=e.charCodeAt(r+1),i=e.charCodeAt(r+2),t+=String.fromCharCode((15&s)<<12|(63&o)<<6|63&i),r+=3);return t}}; var processesGrid, store, comboCategory, @@ -936,89 +934,172 @@ editNewProcess = function(){ } } -deleteProcess = function(){ - var rows = processesGrid.getSelectionModel().getSelections(); - var i; - if( rows.length > 0 ) { - isValid = true; - errLog = Array(); - - //verify if the selected rows have not any started or delegated cases - for(i=0; i'; - } - Ext.MessageBox.show({ + }); +}; +/** + * Removes classic processes + * @param messageError + */ +deleteProcessClassic = function (messageError) { + var message = messageError || '', + result; + Ext.Ajax.request({ + url: 'processes_Delete', + success: function (response) { + Ext.MessageBox.hide(); + processesGrid.store.reload(); + result = Ext.util.JSON.decode(response.responseText); + + if (result) { + if (result.status != 0) { + message = message + result.msg; + } + } else { + message = message + response.responseText; + } + if (message) { + showMessageError(message); + } + }, + params: {PRO_UIDS: PRO_UIDS} + }); +}; +/** + * Displays an error message + * @param messageError + */ +showMessageError = function (messageError) { + Ext.MessageBox.hide(); + Ext.MessageBox.show({ title: _('ID_ERROR'), - msg: errMsg, + msg: messageError, buttons: Ext.MessageBox.OK, icon: Ext.MessageBox.ERROR - }); - } - } else { - Ext.Msg.show({ - title: _("ID_INFORMATION"), - msg: _('ID_NO_SELECTION_WARNING'), - buttons: Ext.Msg.INFO, - fn: function(){}, - animEl: 'elId', - icon: Ext.MessageBox.INFO, - buttons: Ext.MessageBox.OK }); - } +}; +/** + * Eliminate processes + */ +deleteProcess = function () { + var rows = processesGrid.getSelectionModel().getSelections(), + i, + e, + ids, + errLog, + isValid, + dataBulk = []; + + if (rows.length > 0) { + isValid = true; + errLog = []; + + //verify if the selected rows have not any started or delegated cases + for (i = 0; i < rows.length; i += 1) { + if (rows[i].get('CASES_COUNT') !== 0) { + errLog.push(i); + isValid = false; + break; + } + } + + if (isValid) { + ids = []; + for (i = 0; i < rows.length; i += 1) { + if (rows[i].get('PROJECT_TYPE') === 'classic') { + ids.push(rows[i].get('PRO_UID')); + } else { + dataBulk.push({ + type: 'bpmn', + prj_uid: rows[i].get('PRO_UID') + }); + } + } + + PRO_UIDS = ids.join(','); + + Ext.Msg.confirm( + _('ID_CONFIRM'), + (rows.length == 1) ? _('ID_PROCESS_DELETE_LABEL') : _('ID_PROCESS_DELETE_ALL_LABEL'), + function (btn, text) { + if (btn === 'yes') { + Ext.MessageBox.show({msg: _('ID_DELETING_ELEMENTS'), wait: true, waitConfig: {interval: 200}}); + deleteProcessBpmn( + function (response) { + Ext.MessageBox.hide(); + if (PRO_UIDS.length) { + deleteProcessClassic(); + PRO_UIDS.length = 0; + } + processesGrid.store.reload(); + }, + function (xhr) { + if (PRO_UIDS.length) { + deleteProcessClassic(Ext.util.JSON.decode(xhr.responseText).error.message); + } + showMessageError(Ext.util.JSON.decode(xhr.responseText).error.message); + }, + dataBulk + ); + } + } + ); + } else { + errMsg = ''; + for (i = 0; i < errLog.length; i += 1) { + e = _('ID_PROCESS_CANT_DELETE'); + e = e.replace('{0}', rows[errLog[i]].get('PRO_TITLE')); + e = e.replace('{1}', rows[errLog[i]].get('CASES_COUNT')); + errMsg += e + '
'; + } + Ext.MessageBox.show({ + title: _('ID_ERROR'), + msg: errMsg, + buttons: Ext.MessageBox.OK, + icon: Ext.MessageBox.ERROR + }); + } + } else { + Ext.Msg.show({ + title: _("ID_INFORMATION"), + msg: _('ID_NO_SELECTION_WARNING'), + buttons: Ext.Msg.INFO, + fn: function(){}, + animEl: 'elId', + icon: Ext.MessageBox.INFO, + buttons: Ext.MessageBox.OK + }); + } } var deleteCases = function(){ @@ -1028,14 +1109,14 @@ var deleteCases = function(){ PRO_UIDS, i; if( rows.length > 0 ) { - for(i=0; i