diff --git a/api/BusinessLogic/Tickets/CreateTicketByCustomerModel.php b/api/BusinessLogic/Tickets/CreateTicketByCustomerModel.php index aa6ab0ff..736ce29e 100644 --- a/api/BusinessLogic/Tickets/CreateTicketByCustomerModel.php +++ b/api/BusinessLogic/Tickets/CreateTicketByCustomerModel.php @@ -40,5 +40,23 @@ class CreateTicketByCustomerModel { */ public $customFields; + /** + * @var double[]|null + */ public $location; + + /** + * @var int[]|null + */ + public $suggestedKnowledgebaseArticleIds; + + /** + * @var string|null + */ + public $userAgent; + + /** + * @var int[]|null + */ + public $screenResolution; } \ No newline at end of file diff --git a/api/BusinessLogic/Tickets/Exceptions/UnableToGenerateTrackingIdException.php b/api/BusinessLogic/Tickets/Exceptions/UnableToGenerateTrackingIdException.php new file mode 100644 index 00000000..0d2718a8 --- /dev/null +++ b/api/BusinessLogic/Tickets/Exceptions/UnableToGenerateTrackingIdException.php @@ -0,0 +1,12 @@ +NOT handled here! + * * @param $ticketRequest CreateTicketByCustomerModel * @param $heskSettings array HESK settings * @param $modsForHeskSettings array Mods for HESK settings * @throws ValidationException When a required field in $ticket_request is missing + * */ function createTicketByCustomer($ticketRequest, $heskSettings, $modsForHeskSettings, $userContext) { $validationModel = $this->validate($ticketRequest, false, $heskSettings, $modsForHeskSettings, $userContext); @@ -47,6 +50,25 @@ class TicketCreator { } // Create the ticket + //-- TODO Get tracking ID + + //-- TODO handle message + + //-- TODO suggested kb articles + + //-- TODO autoassign logic + + //-- TODO latitude/longitude + + //-- TODO HTML flag + + //-- TODO Screen res / user agent + + //-- TODO Should ticket validation exist? + + //-- TODO Create the ticket + + //-- TODO return the freshly created ticket. Any extra stuff the web side does will be handled in submit_ticket.php } /** diff --git a/api/BusinessLogic/Tickets/TrackingIdGenerator.php b/api/BusinessLogic/Tickets/TrackingIdGenerator.php index 33793401..a97811ca 100644 --- a/api/BusinessLogic/Tickets/TrackingIdGenerator.php +++ b/api/BusinessLogic/Tickets/TrackingIdGenerator.php @@ -3,14 +3,134 @@ namespace BusinessLogic\Tickets; +use BusinessLogic\Tickets\Exceptions\UnableToGenerateTrackingIdException; +use DataAccess\Tickets\TicketGateway; + class TrackingIdGenerator { + /** + * @var $ticketGateway TicketGateway + */ private $ticketGateway; function __construct($ticketGateway) { $this->ticketGateway = $ticketGateway; } - function generateTrackingId() { + /** + * @param $heskSettings array + * @return string + */ + function generateTrackingId($heskSettings) { + $acceptableCharacters = 'AEUYBDGHJLMNPQRSTVWXZ123456789'; + /* Generate raw ID */ + $trackingId = ''; + + /* Let's avoid duplicate ticket ID's, try up to 3 times */ + for ($i = 1; $i <= 3; $i++) { + for ($i = 0; $i < 10; $i++) { + $trackingId .= $acceptableCharacters[mt_rand(0, 29)]; + } + + $trackingId = $this->formatTrackingId($trackingId); + + /* Check for duplicate IDs */ + $ticket = $this->ticketGateway->getTicketByTrackingId($trackingId, $heskSettings); + + if ($ticket === null) { + return $trackingId; + } + + /* A duplicate ID has been found! Let's try again (up to 2 more) */ + $trackingId = ''; + } + + /* No valid tracking ID, try one more time with microtime() */ + $trackingId = $acceptableCharacters[mt_rand(0, 29)]; + $trackingId .= $acceptableCharacters[mt_rand(0, 29)]; + $trackingId .= $acceptableCharacters[mt_rand(0, 29)]; + $trackingId .= $acceptableCharacters[mt_rand(0, 29)]; + $trackingId .= $acceptableCharacters[mt_rand(0, 29)]; + $trackingId .= substr(microtime(), -5); + + /* Format the ID to the correct shape and check wording */ + $trackingId = $this->formatTrackingId($trackingId); + + $ticket = $this->ticketGateway->getTicketByTrackingId($trackingId, $heskSettings); + + if ($ticket === null) { + return $trackingId; + } + + throw new UnableToGenerateTrackingIdException(); + } + + /** + * @param $id string + * @return string + */ + private function formatTrackingId($id) { + $acceptableCharacters = 'AEUYBDGHJLMNPQRSTVWXZ123456789'; + + $replace = $acceptableCharacters[mt_rand(0, 29)]; + $replace .= mt_rand(1, 9); + $replace .= $acceptableCharacters[mt_rand(0, 29)]; + + /* + Remove 3 letter bad words from ID + Possiblitiy: 1:27,000 + */ + $remove = array( + 'ASS', + 'CUM', + 'FAG', + 'FUK', + 'GAY', + 'SEX', + 'TIT', + 'XXX', + ); + + $id = str_replace($remove, $replace, $id); + + /* + Remove 4 letter bad words from ID + Possiblitiy: 1:810,000 + */ + $remove = array( + 'ANAL', + 'ANUS', + 'BUTT', + 'CAWK', + 'CLIT', + 'COCK', + 'CRAP', + 'CUNT', + 'DICK', + 'DYKE', + 'FART', + 'FUCK', + 'JAPS', + 'JERK', + 'JIZZ', + 'KNOB', + 'PISS', + 'POOP', + 'SHIT', + 'SLUT', + 'SUCK', + 'TURD', + + // Also, remove words that are known to trigger mod_security + 'WGET', + ); + + $replace .= mt_rand(1, 9); + $id = str_replace($remove, $replace, $id); + + /* Format the ID string into XXX-XXX-XXXX format for easier readability */ + $id = $id[0] . $id[1] . $id[2] . '-' . $id[3] . $id[4] . $id[5] . '-' . $id[6] . $id[7] . $id[8] . $id[9]; + + return $id; } } \ No newline at end of file diff --git a/api/DataAccess/Tickets/TicketGateway.php b/api/DataAccess/Tickets/TicketGateway.php index 9317aaae..c4a40a8c 100644 --- a/api/DataAccess/Tickets/TicketGateway.php +++ b/api/DataAccess/Tickets/TicketGateway.php @@ -7,12 +7,22 @@ use BusinessLogic\Tickets\Ticket; use DataAccess\CommonDao; class TicketGateway extends CommonDao { + /** + * @param $id int + * @param $heskSettings array + * @return Ticket|null + */ function getTicketById($id, $heskSettings) { $this->init(); $rs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "tickets` WHERE `id` = " . intval($id)); + + if (hesk_dbNumRows($rs) === 0) { + return null; + } + $row = hesk_dbFetchAssoc($rs); - $linkedTicketsRs = hesk_dbQuery("SELECT * FROM `hesk_tickets` WHERE `parent` = " . intval($id)); + $linkedTicketsRs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "tickets` WHERE `parent` = " . intval($id)); $ticket = Ticket::fromDatabaseRow($row, $linkedTicketsRs, $heskSettings); @@ -21,16 +31,55 @@ class TicketGateway extends CommonDao { return $ticket; } + /** + * @param $emailAddress string + * @param $heskSettings array + * @return array|null + */ function getTicketsByEmail($emailAddress, $heskSettings) { + $this->init(); + $rs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "tickets` WHERE `email` = '" . hesk_dbEscape($emailAddress) . "'"); + if (hesk_dbNumRows($rs) === 0) { + return null; + } + $tickets = array(); while ($row = hesk_dbFetchAssoc($rs)) { - $ticket = new Ticket(); + $linkedTicketsRs = + hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "tickets` WHERE `parent` = " . intval($row['id'])); - //-- TODO Finish this! + $tickets[] = Ticket::fromDatabaseRow($row, $linkedTicketsRs, $heskSettings); } + + $this->close(); + + return $tickets; + } + + /** + * @param $trackingId string + * @param $heskSettings array + * @return Ticket|null + */ + function getTicketByTrackingId($trackingId, $heskSettings) { + $this->init(); + + $rs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "tickets` WHERE `id` = " . intval($trackingId)); + if (hesk_dbNumRows($rs) === 0) { + return null; + } + + $row = hesk_dbFetchAssoc($rs); + $linkedTicketsRs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "tickets` WHERE `parent` = " . intval($trackingId)); + + $ticket = Ticket::fromDatabaseRow($row, $linkedTicketsRs, $heskSettings); + + $this->close(); + + return $ticket; } } \ No newline at end of file diff --git a/api/Tests/BusinessLogic/Tickets/TrackingIdGeneratorTest.php b/api/Tests/BusinessLogic/Tickets/TrackingIdGeneratorTest.php index c851b158..15cfb484 100644 --- a/api/Tests/BusinessLogic/Tickets/TrackingIdGeneratorTest.php +++ b/api/Tests/BusinessLogic/Tickets/TrackingIdGeneratorTest.php @@ -9,6 +9,7 @@ namespace BusinessLogic\Tickets; +use BusinessLogic\Tickets\Exceptions\UnableToGenerateTrackingIdException; use DataAccess\Tickets\TicketGateway; use PHPUnit\Framework\TestCase; @@ -18,6 +19,9 @@ class TrackingIdGeneratorTest extends TestCase { */ private $ticketGateway; + /** + * @var $trackingIdGenerator TrackingIdGenerator + */ private $trackingIdGenerator; function setUp() { @@ -28,10 +32,35 @@ class TrackingIdGeneratorTest extends TestCase { function testItReturnsTrackingIdInTheProperFormat() { //-- Arrange - $format = ''; + $this->ticketGateway->method('getTicketByTrackingId') + ->willReturn(null); + $acceptableCharacters = '[AEUYBDGHJLMNPQRSTVWXZ123456789]'; + $format = "/^{$acceptableCharacters}{3}-{$acceptableCharacters}{3}-{$acceptableCharacters}{4}$/"; //-- Act + $trackingId = $this->trackingIdGenerator->generateTrackingId(array()); //-- Assert + $this->assertThat($trackingId, $this->matchesRegularExpression($format)); } + + function testItThrowsAnExceptionWhenItWasUnableToGenerateAValidTrackingId() { + //-- Arrange + $exceptionThrown = false; + $this->ticketGateway->method('getTicketByTrackingId') + ->willReturn(new Ticket()); + + //-- Act + try { + $this->trackingIdGenerator->generateTrackingId(array()); + } catch (UnableToGenerateTrackingIdException $e) { + //-- Assert (1/2) + $exceptionThrown = true; + } + + //-- Assert (2/2) + $this->assertThat($exceptionThrown, $this->isTrue()); + } + + //-- Trying to test the database logic is tricky, so no tests here. }