Working on reply endpoint. Just need to send out the email.
This commit is contained in:
parent
669edf832c
commit
9ae259dff6
@ -4,6 +4,7 @@ namespace BusinessLogic\Tickets;
|
|||||||
|
|
||||||
|
|
||||||
class CreateReplyRequest {
|
class CreateReplyRequest {
|
||||||
|
public $ticketId;
|
||||||
public $trackingId;
|
public $trackingId;
|
||||||
public $emailAddress;
|
public $emailAddress;
|
||||||
public $replyMessage;
|
public $replyMessage;
|
||||||
|
@ -4,13 +4,19 @@ namespace BusinessLogic\Tickets;
|
|||||||
|
|
||||||
|
|
||||||
use BusinessLogic\Emails\EmailSenderHelper;
|
use BusinessLogic\Emails\EmailSenderHelper;
|
||||||
|
use BusinessLogic\Emails\EmailTemplateRetriever;
|
||||||
use BusinessLogic\Exceptions\ApiFriendlyException;
|
use BusinessLogic\Exceptions\ApiFriendlyException;
|
||||||
|
use BusinessLogic\Exceptions\ValidationException;
|
||||||
use BusinessLogic\Helpers;
|
use BusinessLogic\Helpers;
|
||||||
use BusinessLogic\Security\UserContext;
|
use BusinessLogic\Security\UserContext;
|
||||||
|
use BusinessLogic\Statuses\Closable;
|
||||||
|
use BusinessLogic\Statuses\DefaultStatusForAction;
|
||||||
use BusinessLogic\ValidationModel;
|
use BusinessLogic\ValidationModel;
|
||||||
use DataAccess\AuditTrail\AuditTrailGateway;
|
use DataAccess\AuditTrail\AuditTrailGateway;
|
||||||
|
use DataAccess\Security\LoginGateway;
|
||||||
use DataAccess\Security\UserGateway;
|
use DataAccess\Security\UserGateway;
|
||||||
use DataAccess\Statuses\StatusGateway;
|
use DataAccess\Statuses\StatusGateway;
|
||||||
|
use DataAccess\Tickets\ReplyGateway;
|
||||||
use DataAccess\Tickets\TicketGateway;
|
use DataAccess\Tickets\TicketGateway;
|
||||||
|
|
||||||
class ReplyCreator {
|
class ReplyCreator {
|
||||||
@ -19,17 +25,23 @@ class ReplyCreator {
|
|||||||
private $emailSenderHelper;
|
private $emailSenderHelper;
|
||||||
private $userGateway;
|
private $userGateway;
|
||||||
private $auditTrailGateway;
|
private $auditTrailGateway;
|
||||||
|
private $loginGateway;
|
||||||
|
private $replyGateway;
|
||||||
|
|
||||||
public function __construct(StatusGateway $statusGateway,
|
public function __construct(StatusGateway $statusGateway,
|
||||||
TicketGateway $ticketGateway,
|
TicketGateway $ticketGateway,
|
||||||
EmailSenderHelper $emailSenderHelper,
|
EmailSenderHelper $emailSenderHelper,
|
||||||
UserGateway $userGateway,
|
UserGateway $userGateway,
|
||||||
AuditTrailGateway $auditTrailGateway) {
|
AuditTrailGateway $auditTrailGateway,
|
||||||
|
LoginGateway $loginGateway,
|
||||||
|
ReplyGateway $replyGateway) {
|
||||||
$this->statusGateway = $statusGateway;
|
$this->statusGateway = $statusGateway;
|
||||||
$this->ticketGateway = $ticketGateway;
|
$this->ticketGateway = $ticketGateway;
|
||||||
$this->emailSenderHelper = $emailSenderHelper;
|
$this->emailSenderHelper = $emailSenderHelper;
|
||||||
$this->userGateway = $userGateway;
|
$this->userGateway = $userGateway;
|
||||||
$this->auditTrailGateway = $auditTrailGateway;
|
$this->auditTrailGateway = $auditTrailGateway;
|
||||||
|
$this->loginGateway = $loginGateway;
|
||||||
|
$this->replyGateway = $replyGateway;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,6 +50,7 @@ class ReplyCreator {
|
|||||||
* @param $modsForHeskSettings array
|
* @param $modsForHeskSettings array
|
||||||
* @param $userContext UserContext
|
* @param $userContext UserContext
|
||||||
* @throws ApiFriendlyException
|
* @throws ApiFriendlyException
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
function createReplyByCustomer($replyRequest, $heskSettings, $modsForHeskSettings, $userContext) {
|
function createReplyByCustomer($replyRequest, $heskSettings, $modsForHeskSettings, $userContext) {
|
||||||
$ticket = $this->ticketGateway->getTicketByTrackingId($replyRequest->trackingId, $heskSettings);
|
$ticket = $this->ticketGateway->getTicketByTrackingId($replyRequest->trackingId, $heskSettings);
|
||||||
@ -50,11 +63,44 @@ class ReplyCreator {
|
|||||||
$validationModel = new ValidationModel();
|
$validationModel = new ValidationModel();
|
||||||
if (!strlen($replyRequest->replyMessage)) {
|
if (!strlen($replyRequest->replyMessage)) {
|
||||||
$validationModel->errorKeys[] = 'MESSAGE_REQUIRED';
|
$validationModel->errorKeys[] = 'MESSAGE_REQUIRED';
|
||||||
|
|
||||||
|
throw new ValidationException($validationModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($modsForHeskSettings['rich_text_for_tickets_for_customers']) {
|
if ($modsForHeskSettings['rich_text_for_tickets_for_customers']) {
|
||||||
$replyRequest->replyMessage = Helpers::heskMakeUrl($replyRequest->replyMessage);
|
$replyRequest->replyMessage = Helpers::heskMakeUrl($replyRequest->replyMessage);
|
||||||
$replyRequest->replyMessage = nl2br($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);
|
||||||
|
$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.
|
||||||
}
|
}
|
||||||
}
|
}
|
24
api/Controllers/Tickets/CustomerReplyController.php
Normal file
24
api/Controllers/Tickets/CustomerReplyController.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Controllers\Tickets;
|
||||||
|
|
||||||
|
|
||||||
|
use BusinessLogic\Helpers;
|
||||||
|
use BusinessLogic\Tickets\CreateReplyRequest;
|
||||||
|
use Controllers\JsonRetriever;
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
24
api/DataAccess/Security/LoginGateway.php
Normal file
24
api/DataAccess/Security/LoginGateway.php
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -53,4 +53,22 @@ class StatusGateway extends CommonDao {
|
|||||||
|
|
||||||
return $statuses;
|
return $statuses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getStatusById($id, $heskSettings) {
|
||||||
|
$this->init();
|
||||||
|
|
||||||
|
$metaRs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "statuses` WHERE `ID` = " . $id);
|
||||||
|
|
||||||
|
$status = null;
|
||||||
|
if ($row = hesk_dbFetchAssoc($metaRs)) {
|
||||||
|
$languageRs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "text_to_status_xref`
|
||||||
|
WHERE `status_id` = " . intval($row['ID']));
|
||||||
|
|
||||||
|
$status = Status::fromDatabase($row, $languageRs);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->close();
|
||||||
|
|
||||||
|
return $status;
|
||||||
|
}
|
||||||
}
|
}
|
17
api/DataAccess/Tickets/ReplyGateway.php
Normal file
17
api/DataAccess/Tickets/ReplyGateway.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DataAccess\Tickets;
|
||||||
|
|
||||||
|
|
||||||
|
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 . "')");
|
||||||
|
|
||||||
|
$this->close();
|
||||||
|
}
|
||||||
|
}
|
@ -454,4 +454,34 @@ class TicketGateway extends CommonDao {
|
|||||||
|
|
||||||
$this->close();
|
$this->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function areRepliesBeingFlooded($id, $ip, $heskSettings) {
|
||||||
|
$this->init();
|
||||||
|
|
||||||
|
$result = false;
|
||||||
|
$res = hesk_dbQuery("SELECT `staffid` FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "replies` WHERE `replyto`='{$id}' AND `dt` > DATE_SUB(NOW(), INTERVAL 10 MINUTE) ORDER BY `id` ASC");
|
||||||
|
if (hesk_dbNumRows($res) > 0) {
|
||||||
|
$sequential_customer_replies = 0;
|
||||||
|
while ($tmp = hesk_dbFetchAssoc($res)) {
|
||||||
|
$sequential_customer_replies = $tmp['staffid'] ? 0 : $sequential_customer_replies + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sequential_customer_replies > 10) {
|
||||||
|
hesk_dbQuery("INSERT INTO `".hesk_dbEscape($heskSettings['db_pfix'])."logins` (`ip`, `number`) VALUES ('".hesk_dbEscape($ip)."', ".intval($heskSettings['attempt_limit'] + 1).")");
|
||||||
|
$result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->close();
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMetadataForReply($id, $status, $heskSettings) {
|
||||||
|
$this->init();
|
||||||
|
|
||||||
|
hesk_dbQuery("UPDATE `" . hesk_dbEscape($heskSettings['db_pfix']) . "tickets` SET `lastchange`=NOW(), `status`='{$status}', `replies`=`replies`+1, `lastreplier`='0' WHERE `id`='{$id}'");
|
||||||
|
|
||||||
|
$this->close();
|
||||||
|
}
|
||||||
}
|
}
|
@ -199,6 +199,7 @@ Link::all(array(
|
|||||||
'/v1-internal/categories/{i}/sort/{s}' => action(\Controllers\Categories\CategoryController::clazz() . '::sort', array(RequestMethod::POST), SecurityHandler::INTERNAL),
|
'/v1-internal/categories/{i}/sort/{s}' => action(\Controllers\Categories\CategoryController::clazz() . '::sort', array(RequestMethod::POST), SecurityHandler::INTERNAL),
|
||||||
// Tickets
|
// Tickets
|
||||||
'/v1/tickets' => action(\Controllers\Tickets\CustomerTicketController::clazz(), RequestMethod::all(), SecurityHandler::OPEN),
|
'/v1/tickets' => action(\Controllers\Tickets\CustomerTicketController::clazz(), RequestMethod::all(), SecurityHandler::OPEN),
|
||||||
|
'/v1/tickets/{i}/replies' => action(\Controllers\Tickets\CustomerReplyController::clazz(), array(RequestMethod::POST), SecurityHandler::OPEN),
|
||||||
// Tickets - Staff
|
// Tickets - Staff
|
||||||
'/v1/staff/tickets/{i}' => action(\Controllers\Tickets\StaffTicketController::clazz(), RequestMethod::all()),
|
'/v1/staff/tickets/{i}' => action(\Controllers\Tickets\StaffTicketController::clazz(), RequestMethod::all()),
|
||||||
'/v1/staff/tickets/{i}/due-date' => action(\Controllers\Tickets\StaffTicketController::clazz() . '::updateDueDate', array(RequestMethod::PATCH), SecurityHandler::INTERNAL_OR_AUTH_TOKEN),
|
'/v1/staff/tickets/{i}/due-date' => action(\Controllers\Tickets\StaffTicketController::clazz() . '::updateDueDate', array(RequestMethod::PATCH), SecurityHandler::INTERNAL_OR_AUTH_TOKEN),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user