Merge branch '340-post-customer-reply' into '3-4-0'
Add Customer Reply POST API Endpoint See merge request mike-koch/Mods-for-HESK!97master^2
commit
4a73524c32
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BusinessLogic\Tickets;
|
||||||
|
|
||||||
|
|
||||||
|
class CreateReplyRequest {
|
||||||
|
public $ticketId;
|
||||||
|
public $trackingId;
|
||||||
|
public $emailAddress;
|
||||||
|
public $replyMessage;
|
||||||
|
public $hasHtml;
|
||||||
|
public $ipAddress;
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BusinessLogic\Tickets;
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerCreatedReplyModel {
|
||||||
|
public $id;
|
||||||
|
public $ticketId;
|
||||||
|
public $replierName;
|
||||||
|
public $message;
|
||||||
|
public $dateCreated;
|
||||||
|
public $html;
|
||||||
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BusinessLogic\Tickets;
|
||||||
|
|
||||||
|
|
||||||
|
use BusinessLogic\Emails\Addressees;
|
||||||
|
use BusinessLogic\Emails\EmailSenderHelper;
|
||||||
|
use BusinessLogic\Emails\EmailTemplateRetriever;
|
||||||
|
use BusinessLogic\Exceptions\ApiFriendlyException;
|
||||||
|
use BusinessLogic\Exceptions\ValidationException;
|
||||||
|
use BusinessLogic\Helpers;
|
||||||
|
use BusinessLogic\Security\UserContext;
|
||||||
|
use BusinessLogic\Statuses\Closable;
|
||||||
|
use BusinessLogic\Statuses\DefaultStatusForAction;
|
||||||
|
use BusinessLogic\ValidationModel;
|
||||||
|
use DataAccess\AuditTrail\AuditTrailGateway;
|
||||||
|
use DataAccess\Security\LoginGateway;
|
||||||
|
use DataAccess\Security\UserGateway;
|
||||||
|
use DataAccess\Statuses\StatusGateway;
|
||||||
|
use DataAccess\Tickets\ReplyGateway;
|
||||||
|
use DataAccess\Tickets\TicketGateway;
|
||||||
|
|
||||||
|
class ReplyCreator extends \BaseClass {
|
||||||
|
private $statusGateway;
|
||||||
|
private $ticketGateway;
|
||||||
|
private $emailSenderHelper;
|
||||||
|
private $userGateway;
|
||||||
|
private $auditTrailGateway;
|
||||||
|
private $loginGateway;
|
||||||
|
private $replyGateway;
|
||||||
|
|
||||||
|
public function __construct(StatusGateway $statusGateway,
|
||||||
|
TicketGateway $ticketGateway,
|
||||||
|
EmailSenderHelper $emailSenderHelper,
|
||||||
|
UserGateway $userGateway,
|
||||||
|
AuditTrailGateway $auditTrailGateway,
|
||||||
|
LoginGateway $loginGateway,
|
||||||
|
ReplyGateway $replyGateway) {
|
||||||
|
$this->statusGateway = $statusGateway;
|
||||||
|
$this->ticketGateway = $ticketGateway;
|
||||||
|
$this->emailSenderHelper = $emailSenderHelper;
|
||||||
|
$this->userGateway = $userGateway;
|
||||||
|
$this->auditTrailGateway = $auditTrailGateway;
|
||||||
|
$this->loginGateway = $loginGateway;
|
||||||
|
$this->replyGateway = $replyGateway;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $replyRequest CreateReplyRequest
|
||||||
|
* @param $heskSettings array
|
||||||
|
* @param $modsForHeskSettings array
|
||||||
|
* @throws ApiFriendlyException
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
function createReplyByCustomer($replyRequest, $heskSettings, $modsForHeskSettings) {
|
||||||
|
$ticket = $this->ticketGateway->getTicketByTrackingId($replyRequest->trackingId, $heskSettings);
|
||||||
|
|
||||||
|
if ($ticket === null) {
|
||||||
|
throw new ApiFriendlyException("Ticket with tracking ID {$replyRequest->trackingId} not found.",
|
||||||
|
"Ticket not found", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$validationModel = new ValidationModel();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($modsForHeskSettings['rich_text_for_tickets_for_customers']) {
|
||||||
|
$replyRequest->replyMessage = Helpers::heskMakeUrl($replyRequest->replyMessage);
|
||||||
|
$replyRequest->replyMessage = nl2br($replyRequest->replyMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->loginGateway->isIpLockedOut($replyRequest->ipAddress, $heskSettings)) {
|
||||||
|
throw new ApiFriendlyException("The IP address entered has been locked out of the system for {$heskSettings['attempt_banmin']} minutes because of too many login failures",
|
||||||
|
"Locked Out",
|
||||||
|
403);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->ticketGateway->areRepliesBeingFlooded($replyRequest->ticketId, $replyRequest->ipAddress, $heskSettings)) {
|
||||||
|
throw new ApiFriendlyException("You have been locked out of the system for {$heskSettings['attempt_banmin']} minutes because of too many replies to a ticket.",
|
||||||
|
"Locked Out",
|
||||||
|
403);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If staff hasn't replied yet, don't change the status; otherwise set it to the status for customer replies
|
||||||
|
$currentStatus = $this->statusGateway->getStatusById($ticket->statusId, $heskSettings);
|
||||||
|
if ($currentStatus->closable === Closable::YES || $currentStatus->closable === Closable::CUSTOMERS_ONLY) {
|
||||||
|
$customerReplyStatus = $this->statusGateway->getStatusForDefaultAction(DefaultStatusForAction::CUSTOMER_REPLY, $heskSettings);
|
||||||
|
$defaultNewTicketStatus = $this->statusGateway->getStatusForDefaultAction(DefaultStatusForAction::NEW_TICKET, $heskSettings);
|
||||||
|
|
||||||
|
$ticket->statusId = $ticket->statusId === $defaultNewTicketStatus->id ?
|
||||||
|
$defaultNewTicketStatus->id :
|
||||||
|
$customerReplyStatus->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->ticketGateway->updateMetadataForReply($ticket->id, $ticket->statusId, $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;
|
||||||
|
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
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) {
|
||||||
|
global $applicationContext, $hesk_settings;
|
||||||
|
|
||||||
|
$jsonRequest = JsonRetriever::getJsonData();
|
||||||
|
|
||||||
|
$createReplyByCustomerModel = new CreateReplyRequest();
|
||||||
|
$createReplyByCustomerModel->id = $ticketId;
|
||||||
|
$createReplyByCustomerModel->emailAddress = Helpers::safeArrayGet($jsonRequest, 'email');
|
||||||
|
$createReplyByCustomerModel->trackingId = Helpers::safeArrayGet($jsonRequest, 'trackingId');
|
||||||
|
$createReplyByCustomerModel->replyMessage = Helpers::safeArrayGet($jsonRequest, 'message');
|
||||||
|
$createReplyByCustomerModel->hasHtml = Helpers::safeArrayGet($jsonRequest, 'html');
|
||||||
|
$createReplyByCustomerModel->ipAddress = Helpers::safeArrayGet($jsonRequest, 'ip');
|
||||||
|
|
||||||
|
if ($createReplyByCustomerModel->ipAddress === null) {
|
||||||
|
$createReplyByCustomerModel->ipAddress = hesk_getClientIP();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DataAccess\Security;
|
||||||
|
|
||||||
|
|
||||||
|
use DataAccess\CommonDao;
|
||||||
|
|
||||||
|
class LoginGateway extends CommonDao {
|
||||||
|
function isIpLockedOut($ipAddress, $heskSettings) {
|
||||||
|
$this->init();
|
||||||
|
|
||||||
|
$rs = hesk_dbQuery("SELECT `number` FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "logins`
|
||||||
|
WHERE `ip` = '" . hesk_dbEscape($ipAddress) . "'
|
||||||
|
AND `last_attempt` IS NOT NULL
|
||||||
|
AND DATE_ADD(`last_attempt`, INTERVAL ".intval($heskSettings['attempt_banmin'])." MINUTE ) > NOW() LIMIT 1");
|
||||||
|
|
||||||
|
$result = hesk_dbNumRows($rs) == 1 &&
|
||||||
|
hesk_dbResult($rs) >= $heskSettings['attempt_limit'];
|
||||||
|
|
||||||
|
$this->close();
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DataAccess\Tickets;
|
||||||
|
|
||||||
|
|
||||||
|
use BusinessLogic\Tickets\CustomerCreatedReplyModel;
|
||||||
|
use DataAccess\CommonDao;
|
||||||
|
|
||||||
|
class ReplyGateway extends CommonDao {
|
||||||
|
function insertReply($ticketId, $name, $message, $html, $heskSettings) {
|
||||||
|
$this->init();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue