Add ability to reply via API
This commit is contained in:
parent
9ae259dff6
commit
6fa6c7b686
@ -48,33 +48,140 @@ class Helpers extends \BaseClass {
|
||||
// matches a xxxx://aaaaa.bbb.cccc. ...
|
||||
$text = preg_replace_callback(
|
||||
'#(^|[\n\t (>.])(' . "[a-z][a-z\d+]*:/{2}(?:(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})+|[0-9.]+|\[[a-z0-9.]+:[a-z0-9.]+:[a-z0-9.:]+\])(?::\d*)?(?:/(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?" . ')#iu',
|
||||
create_function(
|
||||
"\$matches",
|
||||
"return make_clickable_callback(MAGIC_URL_FULL, \$matches[1], \$matches[2], '', '$class', '$shortenLinks');"
|
||||
),
|
||||
function($matches) use ($class, $shortenLinks) {
|
||||
return self::makeClickableCallback(MAGIC_URL_FULL, $matches[1], $matches[2], '', $class, $shortenLinks);
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
||||
// matches a "www.xxxx.yyyy[/zzzz]" kinda lazy URL thing
|
||||
$text = preg_replace_callback(
|
||||
'#(^|[\n\t (>])(' . "www\.(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})+(?::\d*)?(?:/(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?" . ')#iu',
|
||||
create_function(
|
||||
"\$matches",
|
||||
"return make_clickable_callback(MAGIC_URL_WWW, \$matches[1], \$matches[2], '', '$class', '$shortenLinks');"
|
||||
),
|
||||
function($matches) use ($class, $shortenLinks) {
|
||||
return self::makeClickableCallback(MAGIC_URL_WWW, $matches[1], $matches[2], '', $class, $shortenLinks);
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
||||
// matches an email address
|
||||
$text = preg_replace_callback(
|
||||
'/(^|[\n\t (>])(' . '((?:[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*(?:[\w\!\#$\%\'\*\+\-\/\=\?\^\`{\|\}\~]|&)+)@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,63})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)' . ')/iu',
|
||||
create_function(
|
||||
"\$matches",
|
||||
"return make_clickable_callback(MAGIC_URL_EMAIL, \$matches[1], \$matches[2], '', '$class', '$shortenLinks');"
|
||||
),
|
||||
function($matches) use ($class, $shortenLinks) {
|
||||
return self::makeClickableCallback(MAGIC_URL_EMAIL, $matches[1], $matches[2], '', $class, $shortenLinks);
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
static function makeClickableCallback($type, $whitespace, $url, $relative_url, $class, $shortenLinks)
|
||||
{
|
||||
global $hesk_settings;
|
||||
|
||||
$orig_url = $url;
|
||||
$orig_relative = $relative_url;
|
||||
$append = '';
|
||||
$url = htmlspecialchars_decode($url);
|
||||
$relative_url = htmlspecialchars_decode($relative_url);
|
||||
|
||||
// make sure no HTML entities were matched
|
||||
$chars = array('<', '>', '"');
|
||||
$split = false;
|
||||
|
||||
foreach ($chars as $char) {
|
||||
$next_split = strpos($url, $char);
|
||||
if ($next_split !== false) {
|
||||
$split = ($split !== false) ? min($split, $next_split) : $next_split;
|
||||
}
|
||||
}
|
||||
|
||||
if ($split !== false) {
|
||||
// an HTML entity was found, so the URL has to end before it
|
||||
$append = substr($url, $split) . $relative_url;
|
||||
$url = substr($url, 0, $split);
|
||||
$relative_url = '';
|
||||
} else if ($relative_url) {
|
||||
// same for $relative_url
|
||||
$split = false;
|
||||
foreach ($chars as $char) {
|
||||
$next_split = strpos($relative_url, $char);
|
||||
if ($next_split !== false) {
|
||||
$split = ($split !== false) ? min($split, $next_split) : $next_split;
|
||||
}
|
||||
}
|
||||
|
||||
if ($split !== false) {
|
||||
$append = substr($relative_url, $split);
|
||||
$relative_url = substr($relative_url, 0, $split);
|
||||
}
|
||||
}
|
||||
|
||||
// if the last character of the url is a punctuation mark, exclude it from the url
|
||||
$last_char = ($relative_url) ? $relative_url[strlen($relative_url) - 1] : $url[strlen($url) - 1];
|
||||
|
||||
switch ($last_char) {
|
||||
case '.':
|
||||
case '?':
|
||||
case '!':
|
||||
case ':':
|
||||
case ',':
|
||||
$append = $last_char;
|
||||
if ($relative_url) {
|
||||
$relative_url = substr($relative_url, 0, -1);
|
||||
} else {
|
||||
$url = substr($url, 0, -1);
|
||||
}
|
||||
break;
|
||||
|
||||
// set last_char to empty here, so the variable can be used later to
|
||||
// check whether a character was removed
|
||||
default:
|
||||
$last_char = '';
|
||||
break;
|
||||
}
|
||||
|
||||
$short_url = ($hesk_settings['short_link'] && strlen($url) > 70 && $shortenLinks) ? substr($url, 0, 54) . ' ... ' . substr($url, -10) : $url;
|
||||
|
||||
switch ($type) {
|
||||
case MAGIC_URL_LOCAL:
|
||||
$tag = 'l';
|
||||
$relative_url = preg_replace('/[&?]sid=[0-9a-f]{32}$/', '', preg_replace('/([&?])sid=[0-9a-f]{32}&/', '$1', $relative_url));
|
||||
$url = $url . '/' . $relative_url;
|
||||
$text = $relative_url;
|
||||
|
||||
// this url goes to http://domain.tld/path/to/board/ which
|
||||
// would result in an empty link if treated as local so
|
||||
// don't touch it and let MAGIC_URL_FULL take care of it.
|
||||
if (!$relative_url) {
|
||||
return $whitespace . $orig_url . '/' . $orig_relative; // slash is taken away by relative url pattern
|
||||
}
|
||||
break;
|
||||
|
||||
case MAGIC_URL_FULL:
|
||||
$tag = 'm';
|
||||
$text = $short_url;
|
||||
break;
|
||||
|
||||
case MAGIC_URL_WWW:
|
||||
$tag = 'w';
|
||||
$url = 'http://' . $url;
|
||||
$text = $short_url;
|
||||
break;
|
||||
|
||||
case MAGIC_URL_EMAIL:
|
||||
$tag = 'e';
|
||||
$text = $short_url;
|
||||
$url = 'mailto:' . $url;
|
||||
break;
|
||||
}
|
||||
|
||||
$url = htmlspecialchars($url);
|
||||
$text = htmlspecialchars($text);
|
||||
$append = htmlspecialchars($append);
|
||||
|
||||
$html = "$whitespace<a href=\"$url\" target=\"blank\" $class>$text</a>$append";
|
||||
|
||||
return $html;
|
||||
} // END make_clickable_callback()
|
||||
}
|
13
api/BusinessLogic/Tickets/CustomerCreatedReplyModel.php
Normal file
13
api/BusinessLogic/Tickets/CustomerCreatedReplyModel.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace BusinessLogic\Tickets;
|
||||
|
||||
|
||||
class CustomerCreatedReplyModel {
|
||||
public $id;
|
||||
public $ticketId;
|
||||
public $replierName;
|
||||
public $message;
|
||||
public $dateCreated;
|
||||
public $html;
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
namespace BusinessLogic\Tickets;
|
||||
|
||||
|
||||
use BusinessLogic\Emails\Addressees;
|
||||
use BusinessLogic\Emails\EmailSenderHelper;
|
||||
use BusinessLogic\Emails\EmailTemplateRetriever;
|
||||
use BusinessLogic\Exceptions\ApiFriendlyException;
|
||||
@ -19,7 +20,7 @@ use DataAccess\Statuses\StatusGateway;
|
||||
use DataAccess\Tickets\ReplyGateway;
|
||||
use DataAccess\Tickets\TicketGateway;
|
||||
|
||||
class ReplyCreator {
|
||||
class ReplyCreator extends \BaseClass {
|
||||
private $statusGateway;
|
||||
private $ticketGateway;
|
||||
private $emailSenderHelper;
|
||||
@ -48,11 +49,10 @@ class ReplyCreator {
|
||||
* @param $replyRequest CreateReplyRequest
|
||||
* @param $heskSettings array
|
||||
* @param $modsForHeskSettings array
|
||||
* @param $userContext UserContext
|
||||
* @throws ApiFriendlyException
|
||||
* @throws \Exception
|
||||
*/
|
||||
function createReplyByCustomer($replyRequest, $heskSettings, $modsForHeskSettings, $userContext) {
|
||||
function createReplyByCustomer($replyRequest, $heskSettings, $modsForHeskSettings) {
|
||||
$ticket = $this->ticketGateway->getTicketByTrackingId($replyRequest->trackingId, $heskSettings);
|
||||
|
||||
if ($ticket === null) {
|
||||
@ -61,9 +61,19 @@ class ReplyCreator {
|
||||
}
|
||||
|
||||
$validationModel = new ValidationModel();
|
||||
if (!strlen($replyRequest->replyMessage)) {
|
||||
if ($replyRequest->replyMessage === null || trim($replyRequest->replyMessage) === '') {
|
||||
$validationModel->errorKeys[] = 'MESSAGE_REQUIRED';
|
||||
}
|
||||
|
||||
if ($heskSettings['email_view_ticket']) {
|
||||
if ($replyRequest->emailAddress === null || trim($replyRequest->emailAddress) === '') {
|
||||
$validationModel->errorKeys[] = 'EMAIL_REQUIRED';
|
||||
} elseif (!in_array($replyRequest->emailAddress, $ticket->email)) {
|
||||
$validationModel->errorKeys[] = 'EMAIL_NOT_FOUND_ON_TICKET';
|
||||
}
|
||||
}
|
||||
|
||||
if (count($validationModel->errorKeys) > 0) {
|
||||
throw new ValidationException($validationModel);
|
||||
}
|
||||
|
||||
@ -96,11 +106,40 @@ class ReplyCreator {
|
||||
}
|
||||
|
||||
$this->ticketGateway->updateMetadataForReply($ticket->id, $ticket->statusId, $heskSettings);
|
||||
$this->replyGateway->insertReply($ticket->id, $ticket->name, $replyRequest->replyMessage, $replyRequest->hasHtml, $heskSettings);
|
||||
$createdReply = $this->replyGateway->insertReply($ticket->id, $ticket->name, $replyRequest->replyMessage, $replyRequest->hasHtml, $heskSettings);
|
||||
|
||||
//-- Changing the ticket message to the reply's
|
||||
$ticket->message = $replyRequest->replyMessage;
|
||||
|
||||
// TODO Send the email.
|
||||
$addressees = new Addressees();
|
||||
if ($ticket->ownerId !== null && $ticket->ownerId !== 0) {
|
||||
$owner = $this->userGateway->getUserById($ticket->ownerId, $heskSettings);
|
||||
|
||||
if ($owner->notificationSettings->replyToMe) {
|
||||
$addressees->to[] = $owner->email;
|
||||
$language = $owner->language === null ? $heskSettings['language'] : $owner->language;
|
||||
$this->emailSenderHelper->sendEmailForTicket(EmailTemplateRetriever::NEW_REPLY_BY_CUSTOMER,
|
||||
$language,
|
||||
$addressees,
|
||||
$ticket,
|
||||
$heskSettings,
|
||||
$modsForHeskSettings);
|
||||
}
|
||||
} else {
|
||||
$users = $this->userGateway->getUsersForUnassignedReplyNotification($heskSettings);
|
||||
foreach ($users as $user) {
|
||||
$addressees->to[] = $user->email;
|
||||
$language = $user->language === null ? $heskSettings['language'] : $user->language;
|
||||
|
||||
$this->emailSenderHelper->sendEmailForTicket(EmailTemplateRetriever::NEW_REPLY_BY_CUSTOMER,
|
||||
$language,
|
||||
$addressees,
|
||||
$ticket,
|
||||
$heskSettings,
|
||||
$modsForHeskSettings);
|
||||
}
|
||||
}
|
||||
|
||||
return $createdReply;
|
||||
}
|
||||
}
|
@ -5,7 +5,9 @@ namespace Controllers\Tickets;
|
||||
|
||||
use BusinessLogic\Helpers;
|
||||
use BusinessLogic\Tickets\CreateReplyRequest;
|
||||
use BusinessLogic\Tickets\ReplyCreator;
|
||||
use Controllers\JsonRetriever;
|
||||
use DataAccess\Settings\ModsForHeskSettingsGateway;
|
||||
|
||||
class CustomerReplyController extends \BaseClass {
|
||||
function post($ticketId) {
|
||||
@ -20,5 +22,15 @@ class CustomerReplyController extends \BaseClass {
|
||||
$createReplyByCustomerModel->replyMessage = Helpers::safeArrayGet($jsonRequest, 'message');
|
||||
$createReplyByCustomerModel->hasHtml = Helpers::safeArrayGet($jsonRequest, 'html');
|
||||
$createReplyByCustomerModel->ipAddress = Helpers::safeArrayGet($jsonRequest, 'ip');
|
||||
|
||||
/* @var $modsForHeskSettingsGateway ModsForHeskSettingsGateway */
|
||||
$modsForHeskSettingsGateway = $applicationContext->get(ModsForHeskSettingsGateway::clazz());
|
||||
$modsForHesk_settings = $modsForHeskSettingsGateway->getAllSettings($hesk_settings);
|
||||
|
||||
/* @var $replyCreator ReplyCreator */
|
||||
$replyCreator = $applicationContext->get(ReplyCreator::clazz());
|
||||
$createdReply = $replyCreator->createReplyByCustomer($createReplyByCustomerModel, $hesk_settings, $modsForHesk_settings);
|
||||
|
||||
return output($createdReply, 201);
|
||||
}
|
||||
}
|
||||
|
@ -100,6 +100,21 @@ class UserGateway extends CommonDao {
|
||||
return $users;
|
||||
}
|
||||
|
||||
function getUsersForUnassignedReplyNotification($heskSettings) {
|
||||
$this->init();
|
||||
|
||||
$rs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "users` WHERE `notify_reply_unassigned` = '1' AND `active` = '1'");
|
||||
|
||||
$users = array();
|
||||
while ($row = hesk_dbFetchAssoc($rs)) {
|
||||
$users[] = UserContext::fromDataRow($row);
|
||||
}
|
||||
|
||||
$this->close();
|
||||
|
||||
return $users;
|
||||
}
|
||||
|
||||
function getManagerForCategory($categoryId, $heskSettings) {
|
||||
$this->init();
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace DataAccess\Tickets;
|
||||
|
||||
|
||||
use BusinessLogic\Tickets\CustomerCreatedReplyModel;
|
||||
use DataAccess\CommonDao;
|
||||
|
||||
class ReplyGateway extends CommonDao {
|
||||
@ -12,6 +13,21 @@ class ReplyGateway extends CommonDao {
|
||||
hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($heskSettings['db_pfix']) . "replies` (`replyto`,`name`,`message`,`dt`,`attachments`, `html`)
|
||||
VALUES ({$ticketId},'" . hesk_dbEscape($name) . "','" . hesk_dbEscape($message) . "',NOW(),'','" . $html . "')");
|
||||
|
||||
$customerCreatedReplyModel = new CustomerCreatedReplyModel();
|
||||
$id = hesk_dbInsertID();
|
||||
|
||||
$rs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "replies` WHERE `id` = " . intval($id));
|
||||
$row = hesk_dbFetchAssoc($rs);
|
||||
|
||||
$customerCreatedReplyModel->id = $row['id'];
|
||||
$customerCreatedReplyModel->message = $row['message'];
|
||||
$customerCreatedReplyModel->ticketId = $row['replyto'];
|
||||
$customerCreatedReplyModel->dateCreated = hesk_date($row['dt'], true);
|
||||
$customerCreatedReplyModel->html = $row['html'] === '1';
|
||||
$customerCreatedReplyModel->replierName = $row['name'];
|
||||
|
||||
$this->close();
|
||||
|
||||
return $customerCreatedReplyModel;
|
||||
}
|
||||
}
|
@ -312,8 +312,8 @@ class TicketGateway extends CommonDao {
|
||||
|
||||
$generatedFields = new TicketGatewayGeneratedFields();
|
||||
$generatedFields->id = $id;
|
||||
$generatedFields->dateCreated = $row['dt'];
|
||||
$generatedFields->dateModified = $row['lastchange'];
|
||||
$generatedFields->dateCreated = hesk_date($row['dt'], true);
|
||||
$generatedFields->dateModified = hesk_date($row['lastchange'], true);
|
||||
|
||||
$this->close();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user