Bootswatch, Summernote, and Captcheck mods for Mods for HESK (mods-for-hesk.com). In use at support.netsyms.com.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.php 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. <?php
  2. // Properly handle error logging, as well as a fatal error workaround
  3. require_once(__DIR__ . '/autoload.php');
  4. error_reporting(0);
  5. set_error_handler('errorHandler');
  6. set_exception_handler('exceptionHandler');
  7. register_shutdown_function('fatalErrorShutdownHandler');
  8. $userContext = null;
  9. function handle404() {
  10. print output(array(
  11. 'message' => "The endpoint '{$_SERVER['REQUEST_URI']}' was not found. Double-check your request and submit again.",
  12. 'uri' => $_SERVER['REQUEST_URI']
  13. ), 404);
  14. }
  15. function before() {
  16. assertApiIsEnabled();
  17. $internalUse = \BusinessLogic\Helpers::getHeader('X-INTERNAL-CALL');
  18. if ($internalUse === 'true') {
  19. buildUserContextFromSession();
  20. } else {
  21. $token = \BusinessLogic\Helpers::getHeader('X-AUTH-TOKEN');
  22. buildUserContext($token);
  23. }
  24. }
  25. function assertApiIsEnabled() {
  26. global $applicationContext, $hesk_settings;
  27. /* @var $apiChecker \BusinessLogic\Settings\ApiChecker */
  28. $apiChecker = $applicationContext->get[\BusinessLogic\Settings\ApiChecker::class];
  29. if (!$apiChecker->isApiEnabled($hesk_settings)) {
  30. print output(array('message' => 'API Disabled'), 404);
  31. die();
  32. }
  33. return;
  34. }
  35. function buildUserContextFromSession() {
  36. global $userContext;
  37. hesk_session_start();
  38. if (empty($_SESSION['id'])) {
  39. throw new \BusinessLogic\Exceptions\SessionNotActiveException();
  40. }
  41. /* @var $userContext \BusinessLogic\Security\UserContext */
  42. $userContext = \BusinessLogic\Security\UserContext::fromDataRow($_SESSION);
  43. }
  44. function buildUserContext($xAuthToken) {
  45. global $applicationContext, $userContext, $hesk_settings;
  46. /* @var $userContextBuilder \BusinessLogic\Security\UserContextBuilder */
  47. $userContextBuilder = $applicationContext->get[\BusinessLogic\Security\UserContextBuilder::class];
  48. $userContext = $userContextBuilder->buildUserContext($xAuthToken, $hesk_settings);
  49. }
  50. function errorHandler($errorNumber, $errorMessage, $errorFile, $errorLine) {
  51. exceptionHandler(new Exception(sprintf("%s:%d\n\n%s", $errorFile, $errorLine, $errorMessage)));
  52. }
  53. /**
  54. * @param $exception Exception
  55. */
  56. function exceptionHandler($exception) {
  57. global $applicationContext, $userContext, $hesk_settings;
  58. if (strpos($exception->getTraceAsString(), 'LoggingGateway') !== false) {
  59. //-- Suppress these for now, as it would cause issues to output two JSONs at one time.
  60. return;
  61. }
  62. /* @var $loggingGateway \DataAccess\Logging\LoggingGateway */
  63. $loggingGateway = $applicationContext->get[\DataAccess\Logging\LoggingGateway::class];
  64. // We don't cast API Friendly Exceptions as they're user-generated errors
  65. if (exceptionIsOfType($exception, \BusinessLogic\Exceptions\ApiFriendlyException::class)) {
  66. /* @var $castedException \BusinessLogic\Exceptions\ApiFriendlyException */
  67. $castedException = $exception;
  68. print_error($castedException->title, $castedException->getMessage(), $castedException->httpResponseCode);
  69. } elseif (exceptionIsOfType($exception, \Core\Exceptions\SQLException::class)) {
  70. /* @var $castedException \Core\Exceptions\SQLException */
  71. $castedException = $exception;
  72. $logId = tryToLog(getLoggingLocation($exception),
  73. "Fought an uncaught SQL exception: " . $castedException->failingQuery, $castedException->getTraceAsString(),
  74. $userContext, $hesk_settings);
  75. $logIdText = $logId === null ? "Additionally, the error could not be logged! :'(" : "Log ID: {$logId}";
  76. print_error("SQL Exception", "Fought an uncaught SQL exception. Check the logs for more information. {$logIdText}", $logId);
  77. } else {
  78. $logId = tryToLog(getLoggingLocation($exception),
  79. $exception->getMessage(), $exception->getTraceAsString(),
  80. $userContext, $hesk_settings);
  81. $logIdText = $logId === null ? "Additionally, the error could not be logged! :'(" : "Log ID: {$logId}";
  82. print_error("Exception Occurred", "Fought an uncaught exception. Check the logs for more information. {$logIdText}", $logId);
  83. }
  84. die();
  85. }
  86. /**
  87. * @param $location string
  88. * @param $message string
  89. * @param $stackTrace string
  90. * @param $userContext \BusinessLogic\Security\UserContext
  91. * @param $heskSettings array
  92. * @return int|null The inserted ID, or null if failed to log
  93. * @internal param Exception $exception
  94. */
  95. function tryToLog($location, $message, $stackTrace, $userContext, $heskSettings) {
  96. global $applicationContext;
  97. /* @var $loggingGateway \DataAccess\Logging\LoggingGateway */
  98. $loggingGateway = $applicationContext->get[\DataAccess\Logging\LoggingGateway::class];
  99. try {
  100. return $loggingGateway->logError($location, $message, $stackTrace, $userContext, $heskSettings);
  101. } catch (Exception $squished) {
  102. return null;
  103. }
  104. }
  105. /**
  106. * @param $exception Exception
  107. * @return string The location of the exception
  108. */
  109. function getLoggingLocation($exception) {
  110. // http://stackoverflow.com/a/9133897/1509431
  111. $trace = $exception->getTrace();
  112. $lastCall = $trace[0];
  113. $location = basename($lastCall['file'], '.php');
  114. if ($location === null || trim($location) === '') {
  115. $location = 'N/A';
  116. }
  117. return "REST API: {$location}";
  118. }
  119. /**
  120. * @param $exception Exception thrown exception
  121. * @param $class string The name of the expected exception type
  122. * @return bool
  123. */
  124. function exceptionIsOfType($exception, $class) {
  125. return is_a($exception, $class);
  126. }
  127. function fatalErrorShutdownHandler() {
  128. $last_error = error_get_last();
  129. if ($last_error['type'] === E_ERROR) {
  130. // fatal error
  131. errorHandler(E_ERROR, $last_error['message'], $last_error['file'], $last_error['line']);
  132. }
  133. }
  134. Link::before('before');
  135. Link::all(array(
  136. // Categories
  137. '/v1/categories' => \Controllers\Categories\CategoryController::class . '::printAllCategories',
  138. '/v1/categories/{i}' => \Controllers\Categories\CategoryController::class,
  139. // Tickets
  140. '/v1/tickets' => \Controllers\Tickets\CustomerTicketController::class,
  141. // Tickets - Staff
  142. '/v1/staff/tickets/{i}' => \Controllers\Tickets\StaffTicketController::class,
  143. // Attachments
  144. '/v1/staff/tickets/{i}/attachments' => \Controllers\Attachments\StaffTicketAttachmentsController::class,
  145. '/v1/staff/tickets/{i}/attachments/{i}' => \Controllers\Attachments\StaffTicketAttachmentsController::class,
  146. // Statuses
  147. '/v1/statuses' => \Controllers\Statuses\StatusController::class,
  148. // Settings
  149. '/v1/settings' => \Controllers\Settings\SettingsController::class,
  150. /* Internal use only routes */
  151. // Resend email response
  152. '/v1-internal/staff/tickets/{i}/resend-email' => \Controllers\Tickets\ResendTicketEmailToCustomerController::class,
  153. // Custom Navigation
  154. '/v1-internal/custom-navigation/all' => \Controllers\Navigation\CustomNavElementController::class . '::getAll',
  155. '/v1-internal/custom-navigation' => \Controllers\Navigation\CustomNavElementController::class,
  156. '/v1-internal/custom-navigation/{i}' => \Controllers\Navigation\CustomNavElementController::class,
  157. '/v1-internal/custom-navigation/{i}/sort/{s}' => \Controllers\Navigation\CustomNavElementController::class . '::sort',
  158. // Any URL that doesn't match goes to the 404 handler
  159. '404' => 'handle404'
  160. ));