diff --git a/admin/api_settings.php b/admin/api_settings.php
new file mode 100644
index 00000000..02c7a5ea
--- /dev/null
+++ b/admin/api_settings.php
@@ -0,0 +1,214 @@
+install folder from your server for security reasons then refresh this page!');
+}
+
+// Get all the required files and functions
+require(HESK_PATH . 'hesk_settings.inc.php');
+require(HESK_PATH . 'inc/common.inc.php');
+require(HESK_PATH . 'inc/admin_functions.inc.php');
+hesk_load_database_functions();
+
+hesk_session_start();
+hesk_dbConnect();
+hesk_isLoggedIn();
+
+// Check permissions for this feature
+hesk_checkPermission('can_man_settings');
+
+$modsForHesk_settings = mfh_getSettings();
+
+define('EXTRA_JS', '');
+// Print header
+require_once(HESK_PATH . 'inc/headerAdmin.inc.php');
+
+
+// Print main manage users page
+require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
+?>
+
+
+
+
+
+ API Information
+
+
+
+
+ API Version
+ |
+
+
+ |
+
+
+
+ External API
+ |
+
+
+
+
+
+
+
API Settings
+
+
+
+
+
+
+
+ User Security
+
+
+
+
+
+ Username |
+ Name |
+ Number of Tokens |
+ Actions |
+
+
+
+
+
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+ Generated Token:
+ NOTE: Please record this token, as this is the only time you will be able to view it!
+ |
+
+
+
+ All tokens for this user have been removed!
+ |
+
+
+
+
+
+
+
+
+
+
My canned response \r\n
%%HESK_ID%%
",
+ * "replyOrder": 10
+ * }
+ *
+ * @apiError (noTokenProvided) 400 No `X-Auth-Token` was provided where it is required
+ * @apiError (invalidXAuthToken) 401 The `X-Auth-Token` provided was invalid
+ */
+if ($request_method == 'GET') {
+ $token = get_header('X-Auth-Token');
+
+ try {
+ get_user_for_token($token, $hesk_settings);
+ } catch (AccessException $e) {
+ return http_response_code($e->getCode());
+ }
+
+ if (isset($_GET['id'])) {
+ $results = get_canned_response($hesk_settings, $_GET['id']);
+ } else {
+ $results = get_canned_response($hesk_settings);
+ }
+
+ if ($results == NULL) {
+ return http_response_code(404);
+ }
+ return output($results);
+}
+
+return http_response_code(405);
\ No newline at end of file
diff --git a/api/admin/ticket-template/index.php b/api/admin/ticket-template/index.php
new file mode 100644
index 00000000..dd25d3b6
--- /dev/null
+++ b/api/admin/ticket-template/index.php
@@ -0,0 +1,65 @@
+
My ticket template ",
+ * "displayOrder": 10
+ * }
+ *
+ * @apiError (noTokenProvided) 400 No `X-Auth-Token` was provided where it is required
+ * @apiError (invalidXAuthToken) 401 The `X-Auth-Token` provided was invalid
+ */
+if ($request_method == 'GET') {
+ $token = get_header('X-Auth-Token');
+
+ try {
+ get_user_for_token($token, $hesk_settings);
+ } catch (AccessException $e) {
+ return http_response_code($e->getCode());
+ }
+
+ if (isset($_GET['id'])) {
+ $results = get_ticket_template($hesk_settings, $_GET['id']);
+ } else {
+ $results = get_ticket_template($hesk_settings);
+ }
+
+ if ($results == NULL) {
+ return http_response_code(404);
+ }
+ return output($results);
+}
+
+return http_response_code(405);
\ No newline at end of file
diff --git a/api/admin/ticket/index.php b/api/admin/ticket/index.php
new file mode 100644
index 00000000..f347af43
--- /dev/null
+++ b/api/admin/ticket/index.php
@@ -0,0 +1,132 @@
+`false` otherwise
+ * @apiSuccess {Boolean} locked `true` if the ticket is locked
`false` otherwise
+ * @apiSuccess {Binary[]} attachments Array of attachments, in base-64 encoded binary
+ * @apiSuccess {Integer[]} merged Array of merged ticket IDs
+ * @apiSuccess {String} legacyAuditTrail HTML markup of the entire "Audit Trail" section
+ * @apiSuccess {String} custom1-20 Custom fields 1-20's values.
+ * @apiSuccess {Integer} linkedTo The ID of the ticket linked to this ticket
+ * @apiSuccess {String} latitude The latitudinal coordinate of the user's location, or one of the corresponding error codes.
+ * @apiSuccess {String} longitude The longitudinal coordinate of the user's location, or one of the corresponding error codes.
+ * @apiSuccess {Boolean} html `true` if the ticket was created with HTML encoding
`false` otherwise
+ * @apiSuccess {String} userAgent The user agent of the user who submitted the ticket
+ * @apiSuccess {Integer} screenResolutionWidth The width of the screen resolution of the user who submitted the ticket
+ * @apiSuccess {Integer} screenResolutionHeight The height of the screen resolution of the user who submitted the ticket
+ *
+ * @apiSuccessExample {json} Success-Response:
+ * HTTP/1.1 200 OK
+ * {
+ * "id": 22,
+ * "trackingId": "EVL-RRL-DUBG",
+ * "name": "Test",
+ * "email": "",
+ * "category": 1,
+ * "priority": 3,
+ * "subject": "test",
+ * "message": "test",
+ * "dateCreated": "2014-12-28 00:57:26",
+ * "articles": null,
+ * "ip": "127.0.0.1",
+ * "language": null,
+ * "status": 3,
+ * "owner": 1,
+ * "timeWorked": "00:05:07",
+ * "archive": true,
+ * "locked": true,
+ * "attachments": "",
+ * "merged": "",
+ * "legacyAuditTrail": "
2014-12-28 06:57:28 | ticket created by Your name (username)2014-12-31 21:00:59 | closed by Your name (username)2014-12-31 21:01:05 | status changed to Waiting reply by Your name (username)2014-12-31 21:01:58 | closed by Your name (username)2015-01-17 16:21:18 | closed by Your name (username)",
+ * "custom1": "1420671600",
+ * "custom2": "",
+ * "custom3": "",
+ * "custom4": "",
+ * "custom5": "",
+ * "custom6": "",
+ * "custom7": "",
+ * "custom8": "",
+ * "custom9": "",
+ * "custom10": "",
+ * "custom11": "",
+ * "custom12": "",
+ * "custom13": "",
+ * "custom14": "",
+ * "custom15": "",
+ * "custom16": "",
+ * "custom17": "",
+ * "custom18": "",
+ * "custom19": "",
+ * "custom20": "",
+ * "parent": 139,
+ * "latitude": "E-0",
+ * "longitude": "E-0",
+ * "html": false,
+ * "userAgent": null,
+ * "screenResolutionWidth": null,
+ * "screenResolutionHeight": null
+ * }
+ *
+ * @apiError (noTokenProvided) 400 No `X-Auth-Token` was provided where it is required
+ * @apiError (invalidXAuthToken) 401 The `X-Auth-Token` provided was invalid
+ */
+if ($request_method == 'GET') {
+ $token = get_header('X-Auth-Token');
+
+ try {
+ get_user_for_token($token, $hesk_settings);
+ } catch (AccessException $e) {
+ return http_response_code($e->getCode());
+ }
+
+ if (isset($_GET['id'])) {
+ $results = get_ticket_for_staff($hesk_settings, $_GET['id']);
+ } else {
+ $results = get_ticket_for_staff($hesk_settings);
+ }
+
+ if ($results == NULL) {
+ return http_response_code(404);
+ }
+ return output($results);
+}
+
+return http_response_code(405);
\ No newline at end of file
diff --git a/api/admin/user/index.php b/api/admin/user/index.php
new file mode 100644
index 00000000..4b980a21
--- /dev/null
+++ b/api/admin/user/index.php
@@ -0,0 +1,121 @@
+`false` otherwise
+ * @apiSuccess {String} name The user's name
+ * @apiSuccess {String} email The user's email address
+ * @apiSuccess {String} signature The user's signature, in plaintext
+ * @apiSuccess {Unknown} language ??? (Unknown)
+ * @apiSuccess {String[]} categories Ticket categories the user has access to. If the user is an admin, this list has one element: ""
+ * @apiSuccess {Integer} afterReply Action to perform after replying to a ticket:
+ * `0` - Show the ticket I just replied to
+ * `1` - Return to the main administration page
+ * `2` - Open next ticket that needs my reply
+ * @apiSuccess {Boolean} autoStart Automatically start timer when the user opens a ticket
+ * @apiSuccess {Boolean} notifyCustomerNew Select notify customer option in the new ticket form
+ * @apiSuccess {Boolean} notifyCustomerReply Select notify customer option in the ticket reply form
+ * @apiSuccess {Boolean} showSuggested Show what knowledgebase articles were suggested to customers
+ * @apiSuccess {Boolean} notifyNewUnassigned Notify the user when a new ticket is submitted with owner: Unassigned
+ * @apiSuccess {Boolean} notifyNewMy Notify the user when a new ticket is submitted and is assigned to the user
+ * @apiSuccess {Boolean} notifyAssigned Notify the user when a ticket is assigned to the user
+ * @apiSuccess {Boolean} notifyReplyUnassigned Notify the user when the client responds to a ticket with owner: Unassigned
+ * @apiSuccess {Boolean} notifyReplyMy Notify the user when the client responds to a ticket assigned to the user
+ * @apiSuccess {Boolean} notifyPm Notify the user when a private message is sent to the user
+ * @apiSuccess {Boolean} notifyNoteUnassigned Notify the user when someone adds a note to a ticket not assigned to the user
+ * @apiSuccess {Unknown} defaultList ??? (Currently unknown)
+ * @apiSuccess {Boolean} autoassign Tickets are auto-assigned to this user
+ * @apiSuccess {String[]} heskPrivileges Helpdesk features the user has access to. If the user is an admin, this list has one element: ""
+ * @apiSuccess {Integer} ratingNeg Total number of negative feedback to "Was this reply helpful?" on replies by this user
+ * @apiSuccess {Integer} ratingPos Total number of positive feedback to "Was this reply helpful?" on replies by this user
+ * @apiSuccess {String} rating The overall rating of the user, as a floating point decimal
+ * @apiSuccess {Integer} autorefresh The ticket table autorefresh time for the user, in milliseconds
+ * @apiSuccess {Boolean} active `true` if the user is active
`false` otherwise
+ *
+ * @apiSuccessExample {json} Success-Response:
+ * HTTP/1.1 200 OK
+ * {
+ * "id": 1,
+ * "username": "mkoch",
+ * "admin": true,
+ * "name": "Your name",
+ * "email": "mkoch227@gmail.com",
+ * "signature": "Sincerely,\r\n\r\nYour name\r\nYour website\r\nhttp://www.yourwebsite.com\r\n& < > ^ &",
+ * "language": null,
+ * "categories": [
+ * ""
+ * ],
+ * "afterReply": 0,
+ * "autoStart": true,
+ * "notifyCustomerNew": true,
+ * "notifyCustomerReply": true,
+ * "showSuggested": true,
+ * "notifyNewUnassigned": true,
+ * "notifyNewMy": true,
+ * "notifyReplyUnassigned": true,
+ * "notifyReplyMy": true,
+ * "notifyAssigned": true,
+ * "notifyPm": false,
+ * "notifyNote": true,
+ * "notifyNoteUnassigned": false,
+ * "defaultList": "",
+ * "autoassign": true,
+ * "heskPrivileges": [
+ * ""
+ * ],
+ * "ratingNeg": 0,
+ * "ratingPos": 0,
+ * "rating": "0",
+ * "autorefresh": 0,
+ * "active": true
+ * }
+ *
+ * @apiError (noTokenProvided) 400 No `X-Auth-Token` was provided where it is required
+ * @apiError (invalidXAuthToken) 401 The `X-Auth-Token` provided was invalid
+ */
+if ($request_method == 'GET') {
+ $token = get_header('X-Auth-Token');
+
+ try {
+ get_user_for_token($token, $hesk_settings);
+ } catch (AccessException $e) {
+ return http_response_code($e->getCode());
+ }
+
+ if (isset($_GET['id'])) {
+ $results = retrieve_user($hesk_settings, $_GET['id']);
+ } else {
+ $results = retrieve_user($hesk_settings);
+ }
+
+ if ($results == NULL) {
+ return http_response_code(404);
+ }
+ return output($results);
+}
+
+return http_response_code(405);
\ No newline at end of file
diff --git a/api/businesslogic/security_retriever.php b/api/businesslogic/security_retriever.php
new file mode 100644
index 00000000..742d97c7
--- /dev/null
+++ b/api/businesslogic/security_retriever.php
@@ -0,0 +1,9 @@
+`false` otherwise
+ * @apiSuccess {Integer} type `0` - Public
`1` - Private
+ * @apiSuccess {Integer} priority Default priority of tickets created in this category
+ * @apiSuccess {Integer} manager User ID of the category manager, or `null` if there is no manager.
+ *
+ * @apiSuccessExample {json} Success-Response:
+ * HTTP/1.1 200 OK
+ * {
+ * "id": 1,
+ * "name": "General",
+ * "displayOrder": 10,
+ * "autoassign": true,
+ * "type": 0,
+ * "priority": 2,
+ * "manager": null
+ * }
+ */
+if ($request_method == 'GET') {
+ if (isset($_GET['id'])) {
+ $results = get_category($hesk_settings, $_GET['id']);
+ } else {
+ $results = get_category($hesk_settings);
+ }
+
+ if ($results == NULL) {
+ return http_response_code(404);
+ }
+ return output($results);
+}
+
+return http_response_code(405);
diff --git a/api/common_api_doc.php b/api/common_api_doc.php
new file mode 100644
index 00000000..6212e53a
--- /dev/null
+++ b/api/common_api_doc.php
@@ -0,0 +1,18 @@
+EXPLAIN $query\n";
+
+ if ($res = @mysqli_query($hesk_db_link, $query))
+ {
+ return $res;
+ }
+ elseif ($hesk_settings['debug_mode'])
+ {
+ $message = $hesklang['mysql_said'] . ': ' . mysqli_error($hesk_db_link);
+ }
+ else
+ {
+ $message = $hesklang['contact_webmaster'] . $hesk_settings['webmaster_email'];
+ }
+ header('Content-Type: application/json');
+ print_error($hesklang['cant_sql'], $message);
+ die(http_response_code(500));
+} // END hesk_dbQuery()
+
+
+function hesk_dbFetchAssoc($res)
+{
+
+ return @mysqli_fetch_assoc($res);
+
+} // END hesk_FetchAssoc()
+
+
+function hesk_dbFetchRow($res)
+{
+
+ return @mysqli_fetch_row($res);
+
+} // END hesk_FetchRow()
+
+
+function hesk_dbResult($res, $row = 0, $column = 0)
+{
+ $i=0;
+ $res->data_seek(0);
+
+ while ($tmp = @mysqli_fetch_array($res, MYSQLI_NUM))
+ {
+ if ($i==$row)
+ {
+ return $tmp[$column];
+ }
+ $i++;
+ }
+
+ return '';
+
+} // END hesk_dbResult()
+
+
+function hesk_dbInsertID()
+{
+ global $hesk_db_link;
+
+ if ($lastid = @mysqli_insert_id($hesk_db_link))
+ {
+ return $lastid;
+ }
+
+} // END hesk_dbInsertID()
+
+
+function hesk_dbFreeResult($res)
+{
+
+ return @mysqli_free_result($res);
+
+} // END hesk_dbFreeResult()
+
+
+function hesk_dbNumRows($res)
+{
+
+ return @mysqli_num_rows($res);
+
+} // END hesk_dbNumRows()
+
+
+function hesk_dbAffectedRows()
+{
+ global $hesk_db_link;
+
+ return @mysqli_affected_rows($hesk_db_link);
+
+} // END hesk_dbAffectedRows()
diff --git a/api/core/headers.php b/api/core/headers.php
new file mode 100644
index 00000000..892fafb0
--- /dev/null
+++ b/api/core/headers.php
@@ -0,0 +1,9 @@
+ $value) {
+ if ($key != 'id') {
+ $lowercase_key = lcfirst($key);
+ $row[$lowercase_key] = $row[$key];
+ unset($row[$key]);
+ }
+ if ($key == 'id' || $lowercase_key == 'closable'
+ || $lowercase_key == 'key' || $lowercase_key == 'sort'
+ || $lowercase_key == 'textColor') {
+ continue;
+ }
+ $row[$lowercase_key] = $row[$lowercase_key] == true;
+ }
+
+ $language_sql = "SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "text_to_status_xref` "
+ . "WHERE `status_id` = ".intval($row['id']);
+
+ $language_rs = hesk_dbQuery($language_sql);
+ if (hesk_dbNumRows($language_rs) > 0) {
+ $row['key'] = NULL;
+ $row['keys'] = [];
+ }
+ while ($language_row = hesk_dbFetchAssoc($language_rs)) {
+ unset($language_row['id']);
+ unset($language_row['status_id']);
+ $row['keys'][] = $language_row;
+ }
+
+ $results[] = $row;
+ }
+
+ return $id == NULL ? $results : $results[0];
+}
\ No newline at end of file
diff --git a/api/dao/ticket_dao.php b/api/dao/ticket_dao.php
new file mode 100644
index 00000000..4047d2bf
--- /dev/null
+++ b/api/dao/ticket_dao.php
@@ -0,0 +1,45 @@
+
+ * `yes`: Both customers/staff,
+ * `conly`: Only customers,
+ * `sonly`: Only staff,
+ * `no`: No one
+ * @apiSuccess {String} key The language key. This is deprecated and should not be used.
+ * @apiSuccess {Object[]} keys The language strings for each language
+ * @apiSuccess {String} keys.language The language for the status name
+ * @apiSuccess {String} keys.text The translated string of the status
+ *
+ * @apiSuccessExample {json} Success-Response:
+ * HTTP/1.1 200 OK
+ * {
+ * "id": 0,
+ * "textColor": "#FF0000",
+ * "isNewTicketStatus": true,
+ * "isClosed": false,
+ * "isClosedByClient": false,
+ * "isCustomerReplyStatus": false,
+ * "isStaffClosedOption": false,
+ * "isStaffReopenedStatus": false,
+ * "isDefaultStaffReplyStatus": false,
+ * "lockedTicketStatus": false,
+ * "isAutocloseOption": false,
+ * "closable": "yes",
+ * "key": null,
+ * "keys": [
+ * {
+ * "language": "English",
+ * "text": "New"
+ * },
+ * {
+ * "language": "EspaƱol",
+ * "text": "Nuevo"
+ * }
+ * ]
+ * }
+ */
+if ($request_method == 'GET') {
+ if (isset($_GET['id'])) {
+ $results = get_status($hesk_settings, $_GET['id']);
+ } else {
+ $results = get_status($hesk_settings);
+ }
+
+ if ($results == NULL) {
+ return http_response_code(404);
+ }
+ return output($results);
+}
+
+return http_response_code(405);
\ No newline at end of file
diff --git a/api/ticket/index.php b/api/ticket/index.php
new file mode 100644
index 00000000..4f48fe32
--- /dev/null
+++ b/api/ticket/index.php
@@ -0,0 +1,108 @@
+`false` otherwise
+ * @apiSuccess {Boolean} locked `true` if the ticket is locked
`false` otherwise
+ * @apiSuccess {Binary[]} attachments Array of attachments, in base-64 encoded binary
+ * @apiSuccess {Integer[]} merged Array of merged ticket IDs
+ * @apiSuccess {String} custom1-20 Custom fields 1-20's values.
+ * @apiSuccess {Boolean} html `true` if the ticket was created with HTML encoding
`false` otherwise
+ *
+ * @apiSuccessExample {json} Success-Response:
+ * HTTP/1.1 200 OK
+ * {
+ * "id": 22,
+ * "trackingId": "EVL-RRL-DUBG",
+ * "name": "Test",
+ * "email": "",
+ * "category": 1,
+ * "priority": 3,
+ * "subject": "test",
+ * "message": "test",
+ * "dateCreated": "2014-12-28 00:57:26",
+ * "status": 3,
+ * "archive": true,
+ * "locked": true,
+ * "attachments": "",
+ * "merged": "",
+ * "custom1": "1420671600",
+ * "custom2": "",
+ * "custom3": "",
+ * "custom4": "",
+ * "custom5": "",
+ * "custom6": "",
+ * "custom7": "",
+ * "custom8": "",
+ * "custom9": "",
+ * "custom10": "",
+ * "custom11": "",
+ * "custom12": "",
+ * "custom13": "",
+ * "custom14": "",
+ * "custom15": "",
+ * "custom16": "",
+ * "custom17": "",
+ * "custom18": "",
+ * "custom19": "",
+ * "custom20": "",
+ * "html": false,
+ * }
+ *
+ * @apiError (noTokenProvided) 400 No `X-Auth-Token` was provided where it is required
+ * @apiError (invalidXAuthToken) 401 The `X-Auth-Token` provided was invalid
+ */
+if ($request_method == 'GET') {
+ $token = get_header('X-Auth-Token');
+
+ try {
+ get_user_for_token($token, $hesk_settings);
+ } catch (AccessException $e) {
+ return http_response_code($e->getCode());
+ }
+
+ if (isset($_GET['id'])) {
+ $results = get_ticket($hesk_settings, $_GET['id']);
+ } else {
+ return http_response_code(400);
+ }
+
+ if ($results == NULL) {
+ return http_response_code(404);
+ }
+ return output($results);
+}
+
+return http_response_code(405);
\ No newline at end of file
diff --git a/apidoc.json b/apidoc.json
new file mode 100644
index 00000000..1240c8db
--- /dev/null
+++ b/apidoc.json
@@ -0,0 +1,6 @@
+{
+ "name": "Mods for HESK API",
+ "version": "0.0.0",
+ "description": "Mods for HESK API",
+ "title": "Mods for HESK API"
+}
diff --git a/css/mods-for-hesk.css b/css/mods-for-hesk.css
index 8e62d840..fa9537dc 100644
--- a/css/mods-for-hesk.css
+++ b/css/mods-for-hesk.css
@@ -197,10 +197,6 @@ div.setupButtons {
padding: 10px 0;
}
-.hide {
- display: none;
-}
-
.font-size-90 {
font-size: .9em !important;
}
diff --git a/inc/common.inc.php b/inc/common.inc.php
index 90dbf58e..58cf9157 100644
--- a/inc/common.inc.php
+++ b/inc/common.inc.php
@@ -154,6 +154,31 @@ function hesk_load_database_functions()
} // END hesk_load_database_functions()
+function hesk_load_api_database_functions()
+{
+ require(HESK_PATH . 'api/core/json_error.php');
+ // Preferrably use the MySQLi functions
+ if (function_exists('mysqli_connect')) {
+ require(HESK_PATH . 'api/core/database_mysqli.inc.php');
+ } // Default to MySQL
+ else {
+ require(HESK_PATH . 'api/core/database.inc.php');
+ }
+} // END hesk_load_database_functions()
+
+
+function hesk_load_internal_api_database_functions()
+{
+ require(HESK_PATH . 'internal-api/core/json_error.php');
+ // Preferrably use the MySQLi functions
+ if (function_exists('mysqli_connect')) {
+ require(HESK_PATH . 'internal-api/core/database_mysqli.inc.php');
+ } // Default to MySQL
+ else {
+ require(HESK_PATH . 'internal-api/core/database.inc.php');
+ }
+} // END hesk_load_database_functions()
+
function hesk_unlink($file, $older_than = 0)
{
return (is_file($file) && (!$older_than || (time() - filectime($file)) > $older_than) && @unlink($file)) ? true : false;
diff --git a/inc/headerAdmin.inc.php b/inc/headerAdmin.inc.php
index f1504330..35698c70 100644
--- a/inc/headerAdmin.inc.php
+++ b/inc/headerAdmin.inc.php
@@ -89,6 +89,12 @@ $modsForHesk_settings = mfh_getSettings();
+
+