diff --git a/composer.json b/composer.json index 25714d395..dc34bb592 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,8 @@ "ralouphie/getallheaders": "^2.0", "smarty/smarty": "2.6.30", "pdepend/pdepend": "@stable", - "chumper/zipper": "^1.0" + "chumper/zipper": "^1.0", + "php-imap/php-imap": "^3.0" }, "require-dev": { "fzaninotto/faker": "^1.7", diff --git a/workflow/engine/bin/actionsByEmailEmailResponse.php b/workflow/engine/bin/actionsByEmailEmailResponse.php new file mode 100644 index 000000000..e22f1ad68 --- /dev/null +++ b/workflow/engine/bin/actionsByEmailEmailResponse.php @@ -0,0 +1,2 @@ +actionsByEmailEmailResponse(); + break; } } catch (Exception $e) { $token = strtotime("now"); diff --git a/workflow/engine/content/translations/english/processmaker.en.po b/workflow/engine/content/translations/english/processmaker.en.po index a3009be9c..2c748fab6 100644 --- a/workflow/engine/content/translations/english/processmaker.en.po +++ b/workflow/engine/content/translations/english/processmaker.en.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: ProcessMaker (Branch 3.3.1)\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2018-10-23 16:48:05\n" +"PO-Revision-Date: 2018-11-23 20:16:26\n" "Last-Translator: \n" "Language-Team: Colosa Developers Team \n" "MIME-Version: 1.0\n" @@ -1705,6 +1705,24 @@ msgstr "The answer has been submitted. Thank you." msgid "Please add your comments above this section. Don't modify or delete this section." msgstr "Please add your comments above this section. Don't modify or delete this section." +# TRANSLATION +# LABEL/ID_ABE_CASE_NOTE_ANSWER +#: LABEL/ID_ABE_CASE_NOTE_ANSWER +msgid "Answer: {optionLabel}" +msgstr "Answer: {optionLabel}" + +# TRANSLATION +# LABEL/ID_ABE_CASE_NOTE_COMMENT +#: LABEL/ID_ABE_CASE_NOTE_COMMENT +msgid "Comment: {emailBody}" +msgstr "Comment: {emailBody}" + +# TRANSLATION +# LABEL/ID_ABE_CASE_NOTE_HEADER +#: LABEL/ID_ABE_CASE_NOTE_HEADER +msgid "{emailAccount} has completed a task from Actions by Email." +msgstr "{emailAccount} has completed a task from Actions by Email." + # TRANSLATION # LABEL/ID_ABE_FORM_ALREADY_FILLED #: LABEL/ID_ABE_FORM_ALREADY_FILLED @@ -1717,6 +1735,54 @@ msgstr "The form has already been filled and sent." msgid "The information was submitted. Thank you." msgstr "The information was submitted. Thank you." +# TRANSLATION +# LABEL/ID_ABE_LOG_ALREADY_ROUTED +#: LABEL/ID_ABE_LOG_ALREADY_ROUTED +msgid "The case was identified correctly but it was already routed." +msgstr "The case was identified correctly but it was already routed." + +# TRANSLATION +# LABEL/ID_ABE_LOG_CANNOT_BE_IDENTIFIED +#: LABEL/ID_ABE_LOG_CANNOT_BE_IDENTIFIED +msgid "The case cannot be identified." +msgstr "The case cannot be identified." + +# TRANSLATION +# LABEL/ID_ABE_LOG_CANNOT_READ +#: LABEL/ID_ABE_LOG_CANNOT_READ +msgid "The application cannot read the email receiver account." +msgstr "The application cannot read the email receiver account." + +# TRANSLATION +# LABEL/ID_ABE_LOG_PROCESSED_OK +#: LABEL/ID_ABE_LOG_PROCESSED_OK +msgid "The answer was processed correctly." +msgstr "The answer was processed correctly." + +# TRANSLATION +# LABEL/ID_ABE_LOG_ROUTING_FAILED +#: LABEL/ID_ABE_LOG_ROUTING_FAILED +msgid "The case was identified correctly but the routing process has failed." +msgstr "The case was identified correctly but the routing process has failed." + +# TRANSLATION +# LABEL/ID_ABE_RESPONSE_ALREADY_ROUTED +#: LABEL/ID_ABE_RESPONSE_ALREADY_ROUTED +msgid "Your answer cannot be processed because the case was already routed or is no longer assigned to you." +msgstr "Your answer cannot be processed because the case was already routed or is no longer assigned to you." + +# TRANSLATION +# LABEL/ID_ABE_RESPONSE_CANNOT_BE_IDENTIFIED +#: LABEL/ID_ABE_RESPONSE_CANNOT_BE_IDENTIFIED +msgid "Your answer cannot be processed, please try again and make sure to keep the subject code untouched." +msgstr "Your answer cannot be processed, please try again and make sure to keep the subject code untouched." + +# TRANSLATION +# LABEL/ID_ABE_RESPONSE_ROUTING_FAILED +#: LABEL/ID_ABE_RESPONSE_ROUTING_FAILED +msgid "Your answer cannot be processed because there is an error in the flow definition, please notify to this issue to the system administrator." +msgstr "Your answer cannot be processed because there is an error in the flow definition, please notify to this issue to the system administrator." + # TRANSLATION # LABEL/ID_ABE_RESPONSE_SENT #: LABEL/ID_ABE_RESPONSE_SENT @@ -13625,6 +13691,12 @@ msgstr "Execute a trigger when a case is deleted" msgid "Please press the \"Generate Link\" button." msgstr "Please press the \"Generate Link\" button." +# TRANSLATION +# LABEL/ID_MAFE_660cfca112471f69246e9942f5e8afd1 +#: LABEL/ID_MAFE_660cfca112471f69246e9942f5e8afd1 +msgid "Store email body in" +msgstr "Store email body in" + # TRANSLATION # LABEL/ID_MAFE_66285b36f894a5439af5a98e3e1a36d2 #: LABEL/ID_MAFE_66285b36f894a5439af5a98e3e1a36d2 @@ -14555,6 +14627,12 @@ msgstr "January" msgid "[LABEL/ID_MAFE_86fd9a7abc9f357e7fa206b2d42ec5ba] Destination Path" msgstr "Destination Path" +# TRANSLATION +# LABEL/ID_MAFE_8738321e65db027ee6476c0ab12d8cd5 +#: LABEL/ID_MAFE_8738321e65db027ee6476c0ab12d8cd5 +msgid "Store option in" +msgstr "Store option in" + # TRANSLATION # LABEL/ID_MAFE_87557f11575c0ad78e4e28abedc13b6e #: LABEL/ID_MAFE_87557f11575c0ad78e4e28abedc13b6e @@ -15365,6 +15443,12 @@ msgstr "Properties" msgid "[LABEL/ID_MAFE_9fced129522f128b2445a41fb0b6ef9f] checkbox" msgstr "checkbox" +# TRANSLATION +# LABEL/ID_MAFE_a036226d97d1d0d725d494a1431f322c +#: LABEL/ID_MAFE_a036226d97d1d0d725d494a1431f322c +msgid "Receiver account" +msgstr "Receiver account" + # TRANSLATION # LABEL/ID_MAFE_a05ac4341235111bd2cba5c3c105ba81 #: LABEL/ID_MAFE_a05ac4341235111bd2cba5c3c105ba81 @@ -17915,6 +17999,12 @@ msgstr "Users" msgid "validate" msgstr "validate" +# TRANSLATION +# LABEL/ID_MAFE_fa2b5b7c9bccd35e42cb74042690a091 +#: LABEL/ID_MAFE_fa2b5b7c9bccd35e42cb74042690a091 +msgid "Email response" +msgstr "Email response" + # TRANSLATION # LABEL/ID_MAFE_fa3c608c8fc755543f0d37afd6a42651 #: LABEL/ID_MAFE_fa3c608c8fc755543f0d37afd6a42651 @@ -25961,18 +26051,6 @@ msgstr "The uploaded file exceeds the upload_max_filesize directive in php.ini" msgid "The file has not been attached because the extension is not allowed or because the content doesn't correspond." msgstr "The file has not been attached because the extension is not allowed or because the content doesn't correspond." -# TRANSLATION -# LABEL/ID_UPLOAD_INVALID_DOC_MAX_FILESIZE -#: LABEL/ID_UPLOAD_INVALID_DOC_MAX_FILESIZE -msgid "File size exceeds the allowable limit of {0}" -msgstr "File size exceeds the allowable limit of {0}" - -# TRANSLATION -# LABEL/ID_UPLOAD_INVALID_DOC_TYPE_FILE -#: LABEL/ID_UPLOAD_INVALID_DOC_TYPE_FILE -msgid "Invalid file format, please upload a file with one of the following formats {0}" -msgstr "Invalid file format, please upload a file with one of the following formats {0}" - # TRANSLATION # LABEL/ID_UPLOAD_ERR_NO_FILE #: LABEL/ID_UPLOAD_ERR_NO_FILE @@ -26021,6 +26099,18 @@ msgstr "Upload from file" msgid "Upload image" msgstr "Upload image" +# TRANSLATION +# LABEL/ID_UPLOAD_INVALID_DOC_MAX_FILESIZE +#: LABEL/ID_UPLOAD_INVALID_DOC_MAX_FILESIZE +msgid "File size exceeds the allowable limit of {0}" +msgstr "File size exceeds the allowable limit of {0}" + +# TRANSLATION +# LABEL/ID_UPLOAD_INVALID_DOC_TYPE_FILE +#: LABEL/ID_UPLOAD_INVALID_DOC_TYPE_FILE +msgid "Invalid file format, please upload a file with one of the following formats {0}" +msgstr "Invalid file format, please upload a file with one of the following formats {0}" + # TRANSLATION # LABEL/ID_UPLOAD_LANGUAGE #: LABEL/ID_UPLOAD_LANGUAGE diff --git a/workflow/engine/data/mysql/insert.sql b/workflow/engine/data/mysql/insert.sql index a28d0e496..34caa7051 100644 --- a/workflow/engine/data/mysql/insert.sql +++ b/workflow/engine/data/mysql/insert.sql @@ -57085,6 +57085,17 @@ INSERT INTO TRANSLATION (TRN_CATEGORY,TRN_ID,TRN_LANG,TRN_VALUE,TRN_UPDATE_DATE ( 'LABEL','ID_ABE_FORM_ALREADY_FILLED','en','The form has already been filled and sent.','2017-06-09') , ( 'LABEL','ID_ABE_INFORMATION_SUBMITTED','en','The information was submitted. Thank you.','2017-06-19') , ( 'LABEL','ID_ABE_RESPONSE_SENT','en','The response has already been sent.','2017-06-19') , +( 'LABEL','ID_ABE_RESPONSE_CANNOT_BE_IDENTIFIED','en','Your answer cannot be processed, please try again and make sure to keep the subject code untouched.','2018-11-20') , +( 'LABEL','ID_ABE_RESPONSE_ALREADY_ROUTED','en','Your answer cannot be processed because the case was already routed or is no longer assigned to you.','2018-11-20') , +( 'LABEL','ID_ABE_RESPONSE_ROUTING_FAILED','en','Your answer cannot be processed because there is an error in the flow definition, please notify to this issue to the system administrator.','2018-11-20') , +( 'LABEL','ID_ABE_CASE_NOTE_ANSWER','en','Answer: {optionLabel}','2018-11-20') , +( 'LABEL','ID_ABE_CASE_NOTE_COMMENT','en','Comment: {emailBody}','2018-11-20') , +( 'LABEL','ID_ABE_CASE_NOTE_HEADER','en','{emailAccount} has completed a task from Actions by Email.','2018-11-20') , +( 'LABEL','ID_ABE_LOG_CANNOT_READ','en','The application cannot read the email receiver account.','2018-11-20') , +( 'LABEL','ID_ABE_LOG_PROCESSED_OK','en','The answer was processed correctly.','2018-11-20') , +( 'LABEL','ID_ABE_LOG_CANNOT_BE_IDENTIFIED','en','The case cannot be identified.','2018-11-20') , +( 'LABEL','ID_ABE_LOG_ALREADY_ROUTED','en','The case was identified correctly but it was already routed.','2018-11-20') , +( 'LABEL','ID_ABE_LOG_ROUTING_FAILED','en','The case was identified correctly but the routing process has failed.','2018-11-20') , ( 'LABEL','ID_ACCEPT','en','Accept','2014-01-15') , ( 'LABEL','ID_ACCESS_DENIED','en','Access Denied!','2015-12-03') , ( 'LABEL','ID_ACCOUNT','en','Account','2014-01-15') , diff --git a/workflow/engine/src/ProcessMaker/BusinessModel/ActionsByEmail/ResponseReader.php b/workflow/engine/src/ProcessMaker/BusinessModel/ActionsByEmail/ResponseReader.php new file mode 100644 index 000000000..8fdd862d1 --- /dev/null +++ b/workflow/engine/src/ProcessMaker/BusinessModel/ActionsByEmail/ResponseReader.php @@ -0,0 +1,308 @@ +messageResponseError; + } + + /** + * @param string $messageResponseError + */ + public function setMessageResponseError($messageResponseError) + { + $this->messageResponseError = $messageResponseError; + } + + /** + * Read the Action by Email listener inbox looking for new messages + */ + public function actionsByEmailEmailResponse() + { + try { + if (PMLicensedFeatures + ::getSingleton() + ->verifyfeature('zLhSk5TeEQrNFI2RXFEVktyUGpnczV1WEJNWVp6cjYxbTU3R29mVXVZNWhZQT0=')) { + require_once(PATH_DB . config("system.workspace") . PATH_SEP . "/db.php"); + $arraySystemConfiguration = System::getSystemConfiguration('', '', config("system.workspace")); + define('SYS_SKIN', $arraySystemConfiguration['default_skin']); + + $criteriaAbe = new Criteria(); + $criteriaAbe->add(AbeConfigurationPeer::ABE_TYPE, "RESPONSE"); + $resultAbe = AbeConfigurationPeer::doSelectRS($criteriaAbe); + $resultAbe->setFetchmode(ResultSet::FETCHMODE_ASSOC); + while ($resultAbe->next()) { + $dataAbe = $resultAbe->getRow(); + $this->getAllEmails($dataAbe); + } + } + } catch (Exception $e) { + Bootstrap::registerMonolog( + $this->channel, + $e->getCode(), + $e->getMessage(), + $this->case, + config("system.workspace"), + 'processmaker.log' + ); + } + } + + /** + * Decrypt password of Email Server + * @param array $emailSetup + * @return mixed|string + */ + private function decryptPassword(array $emailSetup) + { + $pass = isset($emailSetup['MESS_PASSWORD']) ? $emailSetup['MESS_PASSWORD'] : ''; + $passDec = G::decrypt($pass, 'EMAILENCRYPT'); + $auxPass = explode('hash:', $passDec); + if (count($auxPass) > 1) { + if (count($auxPass) == 2) { + $pass = $auxPass[1]; + } else { + array_shift($auxPass); + $pass = implode('', $auxPass); + } + } + return $pass; + } + + /** + * Get all Email of server listener + * @param array $dataAbe + */ + public function getAllEmails(array $dataAbe) + { + try { + $emailServer = new EmailServer(); + $emailSetup = (!is_null(EmailServerPeer::retrieveByPK($dataAbe['ABE_EMAIL_SERVER_RECEIVER_UID']))) ? + $emailServer->getEmailServer($dataAbe['ABE_EMAIL_SERVER_RECEIVER_UID'], true) : + $emailServer->getEmailServerDefault(); + if (empty($emailSetup)) { + throw (new Exception(G::LoadTranslation('ID_ABE_LOG_CANNOT_READ'), 500)); + } + $mailbox = new Mailbox( + '{'. $emailSetup['MESS_INCOMING_SERVER'] . ':' . $emailSetup['MESS_INCOMING_PORT'] . '/imap/ssl/novalidate-cert}INBOX', + $emailSetup['MESS_ACCOUNT'], + $this->decryptPassword($emailSetup) + ); + + // Read all messages into an array + $mailsIds = $mailbox->searchMailbox('ALL'); + if ($mailsIds) { + $mailsInfo = $mailbox->getMailsInfo($mailsIds); + // Get the first message and save its attachment(s) to disk: + foreach ($mailsIds as $key => $mailsId) { + /** @var IncomingMail $mail */ + $mail = $mailbox->getMail($mailsId); + preg_match("/{(.*)}/", $mail->textPlain, $matches); + if ($matches && $mailsInfo[$key]->seen === 0) { + $dataEmail = G::json_decode(Crypt::decryptString($matches[1]), true); + if (config("system.workspace") === $dataEmail['workspace']) { + $this->case = $dataEmail; + try { + $appDelegate = new AppDelegation(); + $alreadyRouted = $appDelegate->alreadyRouted($this->case["appUid"], $this->case["delIndex"]); + //Verify if the current case is already routed. + if ($alreadyRouted) { + $this->setMessageResponseError(G::LoadTranslation('ID_ABE_RESPONSE_ALREADY_ROUTED')); + throw (new Exception(G::LoadTranslation('ID_CASE_DELEGATION_ALREADY_CLOSED'), 400)); + } + $this->processABE($this->case, $mail, $dataAbe); + Bootstrap::registerMonolog( + $this->channel, + 100, // DEBUG + G::LoadTranslation('ID_ABE_LOG_PROCESSED_OK'), + $this->case, + config("system.workspace"), + 'processmaker.log' + ); + } catch (Exception $e) { + $this->sendMessageError( + $this->getMessageResponseError() ? $this->getMessageResponseError() : $e->getMessage(), + $this->case, + $mail, + $emailSetup + ); + Bootstrap::registerMonolog( + $this->channel, + $e->getCode(), + $e->getMessage(), + $this->case, + config("system.workspace"), + 'processmaker.log' + ); + } + } + } + } + } + } catch (Exception $e) { + Bootstrap::registerMonolog( + $this->channel, + $e->getCode(), + $e->getMessage(), + $this->case, + config("system.workspace"), + 'processmaker.log' + ); + } + } + + /** + * Derivation of the case with the mail information + * @param array $caseInfo + * @param IncomingMail $mail + * @param array $dataAbe + * @throws Exception + */ + public function processABE(array $caseInfo, IncomingMail $mail, array $dataAbe = []) + { + try { + $actionsByEmail = new ActionsByEmail(); + $actionsByEmail->verifyLogin($caseInfo['appUid'], $caseInfo['delIndex']); + + $case = new Cases(); + $caseFieldsABE = $case->loadCase($caseInfo['appUid'], $caseInfo['delIndex']); + + $actionsByEmailCore = new ActionsByEmailCoreClass(); + $actionField = str_replace( + $actionsByEmailCore->getPrefix(), + '', + $dataAbe['ABE_ACTION_FIELD'] + ); + $dataField = []; + $dataField[$actionField] = $caseInfo['fieldValue']; + $actionBodyField = str_replace( + $actionsByEmailCore->getPrefix(), + '', + $dataAbe['ABE_ACTION_BODY_FIELD'] + ); + $textPlain = $mail->textPlain; + $textPlain = substr($textPlain, 0, strpos($textPlain, "/=")); + $dataField[$actionBodyField] = $textPlain; + $caseFieldsABE['APP_DATA'] = array_merge($caseFieldsABE['APP_DATA'], $dataField); + + ChangeLog::getChangeLog() + ->getUsrIdByUsrUid($caseFieldsABE['CURRENT_USER_UID'], true) + ->setSourceId(ChangeLog::FromABE); + + $caseFieldsABE['CURRENT_DYNAFORM'] = ''; + $caseFieldsABE['USER_UID'] = $caseFieldsABE['CURRENT_USER_UID']; + $caseFieldsABE['OBJECT_TYPE'] = ''; + + $case->updateCase($caseInfo['appUid'], $caseFieldsABE); + + try { + $ws = new WsBase(); + $result = $ws->derivateCase( + $caseFieldsABE['CURRENT_USER_UID'], + $caseInfo['appUid'], + $caseInfo['delIndex'], + true + ); + $code = (is_array($result)) ? $result['status_code'] : $result->status_code; + if ($code != 0) { + throw new Exception( + "An error occurred while the application was being processed\n" . + "Error code: " . $result->status_code . "\nError message: " . $result->message + ); + } + } catch (Exception $e) { + $this->setMessageResponseError(G::LoadTranslation('ID_ABE_RESPONSE_ROUTING_FAILED')); + throw (new Exception(G::LoadTranslation('ID_ABE_LOG_ROUTING_FAILED'), 400)); + } + + //Save Cases Notes + if ($dataAbe['ABE_CASE_NOTE_IN_RESPONSE'] == 1) { + $customGrid = unserialize($dataAbe['ABE_CUSTOM_GRID']); + $fieldLabel = null; + foreach ($customGrid as $key => $value) { + if ($value['abe_custom_value'] == $caseInfo['fieldValue']) { + $fieldLabel = $value['abe_custom_label']; + break; + } + } + $appNotes = new AppNotes(); + $noteText = G::LoadTranslation('ID_ABE_CASE_NOTE_HEADER', ['emailAccount' => $mail->toString]) . "\n\n"; + $noteText .= G::LoadTranslation('ID_ABE_CASE_NOTE_ANSWER', ['optionLabel' => $fieldLabel ? $fieldLabel : $caseInfo['fieldValue']]) . "\n\n"; + $noteText .= G::LoadTranslation('ID_ABE_CASE_NOTE_COMMENT', ['emailBody' => $textPlain]); + $noteContent = addslashes($noteText); + $appNotes->postNewNote($caseInfo['appUid'], $caseFieldsABE['APP_DATA']['USER_LOGGED'], $noteContent, false); + } + } catch (Exception $e) { + if ($e->getCode() == 400) { + throw (new Exception($e->getMessage(), $e->getCode())); + } else { + $this->setMessageResponseError(G::LoadTranslation('ID_ABE_RESPONSE_CANNOT_BE_IDENTIFIED')); + throw (new Exception(G::LoadTranslation('ID_ABE_LOG_CANNOT_BE_IDENTIFIED'), 300)); + } + } + } + + /** + * Send an error message to the sender + * @param string $msgError + * @param array $caseInf + * @param IncomingMail $mail + * @param array $emailSetup + * @return \ProcessMaker\Util\Response|string|\WsResponse + */ + public function sendMessageError($msgError, array $caseInf, IncomingMail $mail, array $emailSetup) + { + $wsBase = new WsBase(); + $result = $wsBase->sendMessage( + $caseInf['appUid'], + $mail->toString, + $mail->fromAddress, + '', + '', + $mail->subject, + 'actionsByEmailErrorReply.html', + ['ACTIONS_BY_EMAIL_ERROR_MESSAGE' => $msgError], + null, + true, + $caseInf['delIndex'], + $emailSetup, + 0 + ); + return $result; + } +} diff --git a/workflow/engine/src/ProcessMaker/Services/Api/ActionsByEmail.php b/workflow/engine/src/ProcessMaker/Services/Api/ActionsByEmail.php index 339b5bb27..a1646e6ee 100644 --- a/workflow/engine/src/ProcessMaker/Services/Api/ActionsByEmail.php +++ b/workflow/engine/src/ProcessMaker/Services/Api/ActionsByEmail.php @@ -154,6 +154,11 @@ class ActionsByEmail extends Api $arrayData = $filesManager->addProcessFilesManager($proId, $userUid, $data); @copy(PATH_TPL . 'actionsByEmail' . PATH_SEP . 'actionsByEmail.html', $path . 'actionsByEmail.html'); } + if (!file_exists($path . 'actionsByEmailErrorReply.html')) { + $data = array('prf_content' => '', 'prf_filename' => 'actionsByEmailErrorReply.html', 'prf_path' => 'templates'); + $arrayData = $filesManager->addProcessFilesManager($proId, $userUid, $data); + @copy(PATH_TPL . 'actionsByEmail' . PATH_SEP . 'actionsByEmailErrorReply.html', $path . 'actionsByEmailErrorReply.html'); + } } $directory = dir($path); diff --git a/workflow/engine/templates/actionsByEmail/actionsByEmailErrorReply.html b/workflow/engine/templates/actionsByEmail/actionsByEmailErrorReply.html new file mode 100644 index 000000000..c8584c420 --- /dev/null +++ b/workflow/engine/templates/actionsByEmail/actionsByEmailErrorReply.html @@ -0,0 +1,20 @@ + + + + + + + +
ACTIONS BY EMAIL
+
+
+ @#ACTIONS_BY_EMAIL_ERROR_MESSAGE +
+
+
+ This is an automated email, please do not replay to it. +
+ www.processmaker.com +
+
\ No newline at end of file