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 + + + Disabled + Enabled +
+
+
+
+

API Settings

+
+ +
+
+
+
+ +
+ + + + + + + + + + +
+
+
+
+
+
+
+ User Security +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
UsernameNameNumber of TokensActions
+ + + + + + + + + +
+ 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(); + +