Merge branch 'master' into techbar

This commit is contained in:
ThaMunsta
2024-05-31 17:05:58 -04:00
68 changed files with 4427 additions and 579 deletions

View File

@@ -93,6 +93,12 @@ if (isset($_GET['archived'])) {
echo 'btn-default';
} ?>">Payment
Method</a>
<a href="?category=Ticket"
class="btn <?php if ($category == 'Ticket') {
echo 'btn-primary';
} else {
echo 'btn-default';
} ?>">Ticket</a>
<a href="?archived=1"
class="btn <?php if (isset($_GET['archived'])) {
echo 'btn-primary';
@@ -125,7 +131,7 @@ if (isset($_GET['archived'])) {
$category_id = intval($row['category_id']);
$category_name = nullable_htmlentities($row['category_name']);
$category_color = nullable_htmlentities($row['category_color']);
?>
<tr>
<td><a class="text-dark" href="#" data-toggle="modal"
@@ -170,7 +176,7 @@ if (isset($_GET['archived'])) {
</tr>
<?php
include "admin_category_edit_modal.php";
}

View File

@@ -39,7 +39,7 @@ function countFilesInDirectory($dir) {
// Function to compare two arrays recursively and return the differences
function arrayDiffRecursive($array1, $array2) {
$diff = array();
foreach ($array1 as $key => $value) {
if (is_array($value)) {
if (!isset($array2[$key]) || !is_array($array2[$key])) {
@@ -56,7 +56,7 @@ function arrayDiffRecursive($array1, $array2) {
}
}
}
return $diff;
}
@@ -64,17 +64,17 @@ function arrayDiffRecursive($array1, $array2) {
function loadTableStructuresFromSQLDumpURL($fileURL) {
$context = stream_context_create(array('http' => array('header' => 'Accept: application/octet-stream')));
$fileContent = file_get_contents($fileURL, false, $context);
if ($fileContent === false) {
return null;
}
$structure = array();
$queries = explode(";", $fileContent);
foreach ($queries as $query) {
$query = trim($query);
if (!empty($query)) {
if (preg_match("/^CREATE TABLE `(.*)` \((.*)\)$/s", $query, $matches)) {
$tableName = $matches[1];
@@ -83,7 +83,7 @@ function loadTableStructuresFromSQLDumpURL($fileURL) {
}
}
}
return $structure;
}
@@ -91,31 +91,31 @@ function loadTableStructuresFromSQLDumpURL($fileURL) {
function fetchDatabaseStructureFromServer() {
global $mysqli;
$tables = array();
// Fetch table names
$result = $mysqli->query("SHOW TABLES");
if ($result->num_rows > 0) {
while ($row = $result->fetch_row()) {
$tableName = $row[0];
$tables[$tableName] = array();
}
}
// Fetch table structures
foreach ($tables as $tableName => &$table) {
$result = $mysqli->query("SHOW CREATE TABLE `$tableName`");
if ($result->num_rows > 0) {
$row = $result->fetch_row();
$table['structure'] = $row[1];
}
}
//$mysqli->close();
return $tables;
}
@@ -185,11 +185,12 @@ while ($row = $tablesResult->fetch_row()) {
//Get loaded PHP modules
$loadedModules = get_loaded_extensions();
//Get Versions
//Get Server Info / Service versions
$phpVersion = phpversion();
$mysqlVersion = $mysqli->server_version;
$operatingSystem = shell_exec('uname -a');
$operatingSystem = php_uname();
$webServer = $_SERVER['SERVER_SOFTWARE'];
$errorLog = ini_get('error_log');
?>
@@ -198,20 +199,43 @@ $webServer = $_SERVER['SERVER_SOFTWARE'];
<h3 class="card-title"><i class="fas fa-fw fa-bug mr-2"></i>Debug</h3>
</div>
<div class="card-body">
<h3>Database Structure Check</h3>
<h3>Server Info</h3>
<?php
echo "PHP version: " . $phpVersion . "<br>";
echo "MySQL Version: " . $mysqlVersion . "<br>";
echo "Operating System: " . $operatingSystem . "<br>";
echo "Web Server: " . $webServer . "<br>";
echo "PHP Error Log: " . $errorLog
?>
<hr>
<h3>File System</h3>
<?php
$result = countFilesInDirectory($folderPath);
$totalFiles = $result['count'];
$totalSizeMB = round($result['size'] / (1024 * 1024), 2);
echo "Total number of files in $folderPath and its subdirectories: " . $totalFiles . "<br>";
echo "Total size of files in $folderPath and its subdirectories: " . $totalSizeMB . " MB";
?>
<hr>
<h3>Database Structure Check</h3>
<h4>Database stats</h4>
<?php
echo "Number of tables: " . $numTables . "<br>";
echo "Total number of fields: " . $numFields . "<br>";
echo "Total number of rows: " . $numRows . "<br>";
echo "Current Database Version: " . CURRENT_DATABASE_VERSION . "<br>";
?>
<hr>
<h4>Table Stats</h4>
@@ -255,34 +279,8 @@ $webServer = $_SERVER['SERVER_SOFTWARE'];
<hr>
<h3>Versions</h3>
<?php
echo "PHP version: " . $phpVersion . "<br>";
echo "MySQL Version: " . $mysqlVersion . "<br>";
echo "Operating System: " . $operatingSystem . "<br>";
echo "Web Server: " . $webServer;
?>
<hr>
<h3>File System</h3>
<?php
$result = countFilesInDirectory($folderPath);
$totalFiles = $result['count'];
$totalSizeMB = round($result['size'] / (1024 * 1024), 2);
echo "Total number of files in $folderPath and its subdirectories: " . $totalFiles . "<br>";
echo "Total size of files in $folderPath and its subdirectories: " . $totalSizeMB . " MB";
?>
<hr>
<h3>PHP Modules Installed</h3>
<?php
foreach ($loadedModules as $module) {
echo $module . "<br>";
@@ -311,7 +309,7 @@ $webServer = $_SERVER['SERVER_SOFTWARE'];
//Output the result
echo $phpinfo;
?>
<hr>
</div>
</div>

View File

@@ -30,6 +30,7 @@
<select class="form-control select2" name="type" required>
<option value="">- Type -</option>
<option value="1">Client Tag</option>
<option value="2">Location Tag</option>
</select>
</div>
</div>

View File

@@ -30,6 +30,7 @@
<select class="form-control select2" name="type" required>
<option value="">- Type -</option>
<option value="1" <?php if ($tag_type == 1) { echo "selected"; } ?>>Client Tag</option>
<option value="2" <?php if ($tag_type == 2) { echo "selected"; } ?>>Location Tag</option>
</select>
</div>
</div>

View File

@@ -62,6 +62,13 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
$tag_id = intval($row['tag_id']);
$tag_name = nullable_htmlentities($row['tag_name']);
$tag_type = intval($row['tag_type']);
if ( $tag_type == 1) {
$tag_type_display = "Client Tag";
} elseif ( $tag_type == 2) {
$tag_type_display = "Location Tag";
} else {
$tag_type_display = "Unknown Tag";
}
$tag_color = nullable_htmlentities($row['tag_color']);
$tag_icon = nullable_htmlentities($row['tag_icon']);
@@ -72,7 +79,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<span class='badge text-light p-2 mr-1' style="background-color: <?php echo $tag_color; ?>"><i class="fa fa-fw fa-<?php echo $tag_icon; ?> mr-2"></i><?php echo $tag_name; ?></span>
</a>
</td>
<td><?php echo $tag_type; ?></td>
<td><?php echo $tag_type_display; ?></td>
<td>
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">

View File

@@ -3,19 +3,12 @@ require_once "inc_all_admin.php";
require_once "database_version.php";
require_once "config.php";
$updates = fetchUpdates();
// Fetch the latest code changes but don't apply them
exec("git fetch", $output, $result);
$latest_version = exec("git rev-parse origin/$repo_branch");
$current_version = exec("git rev-parse HEAD");
if ($current_version == $latest_version) {
$update_message = "No Updates available";
} else {
$update_message = "New Updates are Available [$latest_version]";
}
$latest_version = $updates->latest_version;
$current_version = $updates->current_version;
$result = $updates->result;
$git_log = shell_exec("git log $repo_branch..origin/$repo_branch --pretty=format:'<tr><td>%h</td><td>%ar</td><td>%s</td></tr>'");

View File

@@ -55,13 +55,42 @@
</div>
<select class="form-control select2" name="role" required>
<option value="">- Role -</option>
<option value="3">Administrator</option>
<option value="2">Technician</option>
<option value="1">Accountant</option>
<?php
$sql_user_roles = mysqli_query($mysqli, "SELECT * FROM user_roles WHERE user_role_archived_at IS NULL");
while ($row = mysqli_fetch_array($sql_user_roles)) {
$user_role_id = intval($row['user_role_id']);
$user_role_name = nullable_htmlentities($row['user_role_name']);
?>
<option value="<?php echo $user_role_id; ?>"><?php echo $user_role_name; ?></option>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label>Restrict Client Access</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-users"></i></span>
</div>
<select class="form-control select2" name="clients[]" data-placeholder="Restrict Client Access" multiple>
<?php
$sql_client_select = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
while ($row = mysqli_fetch_array($sql_client_select)) {
$client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']);
?>
<option value="<?php echo $client_id; ?>"><?php echo $client_name; ?></option>
<?php } ?>
</select>
</div>
<small class="text-muted">Leave Blank for Full access to all clients, no affect on users with the admin role.</small>
</div>
<div class="form-group">
<label>Avatar</label>
<input type="file" class="form-control-file" accept="image/*;capture=camera" name="file">

View File

@@ -67,23 +67,43 @@
<span class="input-group-text"><i class="fa fa-fw fa-user-shield"></i></span>
</div>
<select class="form-control select2" name="role" required>
<option value="">- Role -</option>
<option <?php if ($user_role == 3) {
echo "selected";
} ?> value="3">Administrator
</option>
<option <?php if ($user_role == 2) {
echo "selected";
} ?> value="2">Technician
</option>
<option <?php if ($user_role == 1) {
echo "selected";
} ?> value="1">Accountant
</option>
<?php
$sql_user_roles = mysqli_query($mysqli, "SELECT * FROM user_roles WHERE user_role_archived_at IS NULL");
while ($row = mysqli_fetch_array($sql_user_roles)) {
$user_role_id = intval($row['user_role_id']);
$user_role_name = nullable_htmlentities($row['user_role_name']);
?>
<option <?php if ($user_role == $user_role_id) {echo "selected";} ?> value="<?php echo $user_role_id; ?>"><?php echo $user_role_name; ?></option>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label>Restrict Client Access</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-users"></i></span>
</div>
<select class="form-control select2" name="clients[]" data-placeholder="Restrict Client Access" multiple>
<?php
$sql_client_select = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
while ($row = mysqli_fetch_array($sql_client_select)) {
$client_id_select = intval($row['client_id']);
$client_name_select = nullable_htmlentities($row['client_name']);
?>
<option <?php if (in_array($client_id_select, $client_access_array)) { echo "selected"; } ?> value="<?php echo $client_id_select; ?>"><?php echo $client_name_select; ?></option>
<?php } ?>
</select>
</div>
<small class="text-muted">Leave Blank for Full access to all clients, no affect on users with the admin role.</small>
</div>
<div class="form-group">
<label>Avatar</label>
<input type="file" class="form-control-file" accept="image/*;capture=camera" name="file">

View File

@@ -12,8 +12,9 @@ $url_query_strings_sort = http_build_query($get_copy);
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS * FROM users, user_settings
"SELECT SQL_CALC_FOUND_ROWS * FROM users, user_settings, user_roles
WHERE users.user_id = user_settings.user_id
AND user_settings.user_role = user_roles.user_role_id
AND (user_name LIKE '%$q%' OR user_email LIKE '%$q%')
AND user_archived_at IS NULL
ORDER BY $sort $order LIMIT $record_from, $record_to"
@@ -98,13 +99,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
}
$user_config_force_mfa = intval($row['user_config_force_mfa']);
$user_role = $row['user_role'];
if ($user_role == 3) {
$user_role_display = "Administrator";
} elseif ($user_role == 2) {
$user_role_display = "Technician";
} else {
$user_role_display = "Accountant";
}
$user_role_display = nullable_htmlentities($row['user_role_name']);
$user_initials = nullable_htmlentities(initials($user_name));
$sql_last_login = mysqli_query(
@@ -125,9 +120,18 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
$last_login = "$log_created_at<small class='text-secondary'><div class='mt-1'>$log_user_os</div><div class='mt-1'>$log_user_browser</div><div class='mt-1'><i class='fa fa-fw fa-globe'></i> $log_ip</div></small>";
}
// Get User Client Access Permissions
$user_client_access_sql = mysqli_query($mysqli,"SELECT client_id FROM user_permissions WHERE user_id = $user_id");
$client_access_array = [];
while ($row = mysqli_fetch_assoc($user_client_access_sql)) {
$client_access_array[] = intval($row['client_id']);
}
$sql_remember_tokens = mysqli_query($mysqli, "SELECT * FROM remember_tokens WHERE remember_token_user_id = $user_id");
$remember_token_count = mysqli_num_rows($sql_remember_tokens);
?>
<tr>
<td class="text-center">

View File

@@ -19,37 +19,22 @@ require_once "rfc6238.php";
* Fetches SSL certificates from remote hosts & returns the relevant info (issuer, expiry, public key)
*/
if (isset($_GET['certificate_fetch_parse_json_details'])) {
// PHP doesn't appreciate attempting SSL sockets to non-existent domains
if (empty($_GET['domain'])) {
exit();
}
$domain = $_GET['domain'];
// FQDNs in database shouldn't have a URL scheme, adding one
$domain = "https://".$domain;
$name = $_GET['domain'];
// Parse host and port
$url = parse_url($domain, PHP_URL_HOST);
$port = parse_url($domain, PHP_URL_PORT);
// Default port
if (!$port) {
$port = "443";
}
// Get SSL cert for domain (if exists)
$certificate = getSSL($name);
// Get certificate (using verify peer false to allow for self-signed certs)
$socket = "ssl://$url:$port";
$get = stream_context_create(array("ssl" => array("capture_peer_cert" => true, "verify_peer" => false,)));
$read = stream_socket_client($socket, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $get);
$cert = stream_context_get_params($read);
$cert_public_key_obj = openssl_x509_parse($cert['options']['ssl']['peer_certificate']);
openssl_x509_export($cert['options']['ssl']['peer_certificate'], $export);
// Process data
if ($cert_public_key_obj) {
if ($certificate['success'] == "TRUE") {
$response['success'] = "TRUE";
$response['expire'] = date('Y-m-d', $cert_public_key_obj['validTo_time_t']);
$response['issued_by'] = strip_tags($cert_public_key_obj['issuer']['O']);
$response['public_key'] = $export; //nl2br
$response['expire'] = $certificate['expire'];
$response['issued_by'] = $certificate['issued_by'];
$response['public_key'] = $certificate['public_key'];
} else {
$response['success'] = "FALSE";
}

View File

@@ -74,6 +74,14 @@ if (isset($_POST['contact_auth_method'])) {
$auth_method = '';
}
if (isset($_POST['contact_primary'])) {
$primary = intval($_POST['contact_primary']);
} elseif ($contact_row) {
$primary = $contact_row['contact_primary'];
} else {
$primary = '0';
}
if (isset($_POST['contact_important'])) {
$important = intval($_POST['contact_important']);
} elseif ($contact_row) {

View File

@@ -20,7 +20,7 @@ if (!empty($name) && !empty($email) && !empty($client_id)) {
if (mysqli_num_rows($email_duplication_sql) == 0) {
// Insert contact
$insert_sql = mysqli_query($mysqli, "INSERT INTO contacts SET contact_name = '$name', contact_title = '$title', contact_department = '$department', contact_email = '$email', contact_phone = '$phone', contact_extension = '$extension', contact_mobile = '$mobile', contact_notes = '$notes', contact_auth_method = '$auth_method', contact_important = '$important', contact_billing = '$billing', contact_technical = '$technical', contact_location_id = $location_id, contact_client_id = $client_id");
$insert_sql = mysqli_query($mysqli, "INSERT INTO contacts SET contact_name = '$name', contact_title = '$title', contact_department = '$department', contact_email = '$email', contact_phone = '$phone', contact_extension = '$extension', contact_mobile = '$mobile', contact_notes = '$notes', contact_auth_method = '$auth_method', contact_primary = '$primary', contact_important = '$important', contact_billing = '$billing', contact_technical = '$technical', contact_location_id = $location_id, contact_client_id = $client_id");
// Check insert & get insert ID
if ($insert_sql) {

View File

@@ -19,7 +19,7 @@ if (!empty($contact_id)) {
require_once 'contact_model.php';
$update_sql = mysqli_query($mysqli, "UPDATE contacts SET contact_name = '$name', contact_title = '$title', contact_department = '$department', contact_email = '$email', contact_phone = '$phone', contact_extension = '$extension', contact_mobile = '$mobile', contact_notes = '$notes', contact_auth_method = '$auth_method', contact_important = '$important', contact_billing = '$billing', contact_technical = '$technical', contact_location_id = $location_id, contact_client_id = $client_id WHERE contact_id = $contact_id LIMIT 1");
$update_sql = mysqli_query($mysqli, "UPDATE contacts SET contact_name = '$name', contact_title = '$title', contact_department = '$department', contact_email = '$email', contact_phone = '$phone', contact_extension = '$extension', contact_mobile = '$mobile', contact_notes = '$notes', contact_auth_method = '$auth_method', contact_primary = '$primary', contact_important = '$important', contact_billing = '$billing', contact_technical = '$technical', contact_location_id = $location_id, contact_client_id = $client_id WHERE contact_id = $contact_id LIMIT 1");
// Check insert & get insert ID
if ($update_sql) {

View File

@@ -180,6 +180,29 @@ while ($row = mysqli_fetch_array($sql)) {
echo "{ id: $event_id, title: $event_title, start: $event_start, color: '$event_color', url: 'ticket.php?ticket_id=$event_id' },";
}
// Recurring Tickets
$sql = mysqli_query($mysqli, "SELECT * FROM clients
LEFT JOIN scheduled_tickets ON client_id = scheduled_ticket_client_id
LEFT JOIN users ON scheduled_ticket_assigned_to = user_id"
);
while ($row = mysqli_fetch_array($sql)) {
$event_id = intval($row['scheduled_ticket_id']);
$client_id = intval($row['client_id']);
$username = $row['user_name'];
$frequency = $row['scheduled_ticket_frequency'];
if (empty($username)) {
$username = "";
} else {
//Limit to characters and add ...
$username = "[". substr($row['user_name'], 0, 9) . "...]";
}
$event_title = json_encode("R Ticket ($frequency) - " . $row['scheduled_ticket_subject'] . " " . $username);
$event_start = json_encode($row['scheduled_ticket_next_run']);
echo "{ id: $event_id, title: $event_title, start: $event_start, color: '$event_color', url: 'client_recurring_tickets.php?client_id=$client_id' },";
}
//Tickets Scheduled
$sql = mysqli_query($mysqli, "SELECT * FROM clients
LEFT JOIN tickets ON client_id = ticket_client_id
@@ -238,6 +261,14 @@ while ($row = mysqli_fetch_array($sql)) {
?>
],
eventOrder: 'allDay,start,-duration,title',
<?php
// User preference for Calendar start day (Sunday/Monday)
// Fetch User Dashboard Settings
$row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT user_config_calendar_first_day FROM user_settings WHERE user_id = $session_user_id"));
$user_config_calendar_first_day = intval($row['user_config_calendar_first_day']);
?>
firstDay: <?php echo $user_config_calendar_first_day ?>,
});
calendar.render();

View File

@@ -10,7 +10,8 @@ if (!isset($_SESSION)) {
session_start();
}
//Check to see if setup is enabled
// Check to see if setup is enabled
if (!isset($config_enable_setup) || $config_enable_setup == 1) {
header("Location: setup.php");
exit;
@@ -18,13 +19,19 @@ if (!isset($config_enable_setup) || $config_enable_setup == 1) {
// Check user is logged in with a valid session
if (!isset($_SESSION['logged']) || !$_SESSION['logged']) {
header("Location: login.php");
if ($_SERVER["REQUEST_URI"] == "/") {
header("Location: login.php");
} else {
header("Location: login.php?last_visited=" . base64_encode($_SERVER["REQUEST_URI"]) );
}
exit;
}
// Set Timezone
require_once "inc_set_timezone.php";
// User IP & UA
$session_ip = sanitizeInput(getIP());
$session_user_agent = sanitizeInput($_SERVER['HTTP_USER_AGENT']);
@@ -56,9 +63,37 @@ $session_company_country = $row['company_country'];
$session_company_locale = $row['company_locale'];
$session_company_currency = $row['company_currency'];
//Set Currency Format
// Set Currency Format
$currency_format = numfmt_create($session_company_locale, NumberFormatter::CURRENCY);
try {
// Get User Client Access Permissions
$user_client_access_sql = "SELECT client_id FROM user_permissions WHERE user_id = $session_user_id";
$user_client_access_result = mysqli_query($mysqli, $user_client_access_sql);
$client_access_array = [];
while ($row = mysqli_fetch_assoc($user_client_access_result)) {
$client_access_array[] = $row['client_id'];
}
$client_access_string = implode(',', $client_access_array);
// Role / Client Access Permission Check
if ($session_user_role < 3 && !empty($client_access_string)) {
$access_permission_query = "AND client_id IN ($client_access_string)";
} else {
$access_permission_query = "";
}
} catch (Exception $e) {
// Handle exception
error_log('MySQL error: ' . $e->getMessage());
$access_permission_query = ""; // Ensure safe default if query fails
}
// Include the settings vars
require_once "get_settings.php";
@@ -73,15 +108,17 @@ if ($iPod || $iPhone || $iPad) {
$session_map_source = "google";
}
//Check if mobile device
// Check if mobile device
$session_mobile = isMobile();
//Get Notification Count for the badge on the top nav
// Get Notification Count for the badge on the top nav
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('notification_id') AS num FROM notifications WHERE (notification_user_id = $session_user_id OR notification_user_id = 0) AND notification_dismissed_at IS NULL"));
$num_notifications = $row['num'];
// FORCE MFA Setup
//if ($session_user_config_force_mfa == 1 && $session_token == NULL) {
// header("Location: force_mfa.php");
//}

View File

@@ -33,17 +33,31 @@ if (!empty($_GET['folder_id'])) {
// Set Folder Location Var used when creating folders
$folder_location = 0;
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS * FROM documents
LEFT JOIN users ON document_created_by = user_id
WHERE document_client_id = $client_id
AND document_template = 0
AND document_folder_id = $folder
AND document_archived_at IS NULL
$query_snippet
ORDER BY $sort $order LIMIT $record_from, $record_to"
);
if ($get_folder_id == 0 && $_GET["q"]) {
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS * FROM documents
LEFT JOIN users ON document_created_by = user_id
WHERE document_client_id = $client_id
AND document_template = 0
AND document_archived_at IS NULL
$query_snippet
ORDER BY $sort $order LIMIT $record_from, $record_to"
);
}else{
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS * FROM documents
LEFT JOIN users ON document_created_by = user_id
WHERE document_client_id = $client_id
AND document_template = 0
AND document_folder_id = $folder
AND document_archived_at IS NULL
$query_snippet
ORDER BY $sort $order LIMIT $record_from, $record_to"
);
}
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));

View File

@@ -180,6 +180,29 @@ while ($row = mysqli_fetch_array($sql)) {
echo "{ id: $event_id, title: $event_title, start: $event_start, color: '$event_color', url: 'ticket.php?ticket_id=$event_id' },";
}
// Recurring Tickets
$sql = mysqli_query($mysqli, "SELECT * FROM clients
LEFT JOIN scheduled_tickets ON client_id = scheduled_ticket_client_id
LEFT JOIN users ON scheduled_ticket_assigned_to = user_id"
);
while ($row = mysqli_fetch_array($sql)) {
$event_id = intval($row['scheduled_ticket_id']);
$client_id = intval($row['client_id']);
$username = $row['user_name'];
$frequency = $row['scheduled_ticket_frequency'];
if (empty($username)) {
$username = "";
} else {
//Limit to characters and add ...
$username = "[". substr($row['user_name'], 0, 9) . "...]";
}
$event_title = json_encode("R Ticket ($frequency) - " . $row['scheduled_ticket_subject'] . " " . $username);
$event_start = json_encode($row['scheduled_ticket_next_run']);
echo "{ id: $event_id, title: $event_title, start: $event_start, color: '$event_color', url: 'client_recurring_tickets.php?client_id=$client_id' },";
}
//Tickets Scheduled
$sql = mysqli_query($mysqli, "SELECT * FROM clients
LEFT JOIN tickets ON client_id = ticket_client_id

View File

@@ -11,7 +11,7 @@
<input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<label>Description</label>
<div class="input-group">
@@ -47,7 +47,7 @@
</div>
<div class="form-group">
<input type="file" class="form-control-file" name="file[]" multiple id="fileInput" accept=".jpg, .jpeg, .gif, .png, .webp, .pdf, .txt, .md, .doc, .docx, .odt, .csv, .xls, .xlsx, .ods, .pptx, .odp, .zip, .tar, .gz, .xml, .msg, .json, .wav, .mp3, .ogg, .mov, .mp4, .av1, .ovpn, .cfg, .ps1, .vsdx, .drawio, .pfx">
<input type="file" class="form-control-file" name="file[]" multiple id="fileInput" accept=".jpg, .jpeg, .gif, .png, .webp, .pdf, .txt, .md, .doc, .docx, .odt, .csv, .xls, .xlsx, .ods, .pptx, .odp, .zip, .tar, .gz, .xml, .msg, .json, .wav, .mp3, .ogg, .mov, .mp4, .av1, .ovpn, .cfg, .ps1, .vsdx, .drawio, .pfx, .unf">
</div>
<small class="text-secondary">Up to 20 files can be uploaded at once by holding down CTRL and selecting files</small>

View File

@@ -39,16 +39,29 @@ if ($view == 1) {
// Set Folder Location Var used when creating folders
$folder_location = 1;
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS * FROM files
WHERE file_client_id = $client_id
AND file_folder_id = $folder_id
AND file_archived_at IS NULL
AND (file_name LIKE '%$q%' OR file_ext LIKE '%$q%' OR file_description LIKE '%$q%')
$query_images
ORDER BY $sort $order LIMIT $record_from, $record_to"
);
if ($get_folder_id == 0 && $_GET["q"]) {
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS * FROM files
WHERE file_client_id = $client_id
AND file_archived_at IS NULL
AND (file_name LIKE '%$q%' OR file_ext LIKE '%$q%' OR file_description LIKE '%$q%')
$query_images
ORDER BY $sort $order LIMIT $record_from, $record_to"
);
}else{
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS * FROM files
WHERE file_client_id = $client_id
AND file_folder_id = $folder_id
AND file_archived_at IS NULL
AND (file_name LIKE '%$q%' OR file_ext LIKE '%$q%' OR file_description LIKE '%$q%')
$query_images
ORDER BY $sort $order LIMIT $record_from, $record_to"
);
}
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));

View File

@@ -168,10 +168,6 @@
</div>
</div>
<div class="form-group">
<textarea class="form-control" rows="6" name="notes" placeholder="Notes, eg Parking Info, Building Access etc"></textarea>
</div>
</div>
<div class="tab-pane fade" id="pills-notes">
@@ -180,6 +176,27 @@
<textarea class="form-control" rows="12" name="notes" placeholder="Notes, eg Parking Info, Building Access etc"></textarea>
</div>
<div class="form-group">
<label>Tags</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tags"></i></span>
</div>
<select class="form-control select2" name="tags[]" data-placeholder="Add some tags" multiple>
<?php
$sql_tags_select = mysqli_query($mysqli, "SELECT * FROM tags WHERE tag_type = 2 ORDER BY tag_name ASC");
while ($row = mysqli_fetch_array($sql_tags_select)) {
$tag_id_select = intval($row['tag_id']);
$tag_name_select = nullable_htmlentities($row['tag_name']);
?>
<option value="<?php echo $tag_id_select; ?>"><?php echo $tag_name_select; ?></option>
<?php } ?>
</select>
</div>
</div>
</div>
</div>

View File

@@ -194,6 +194,27 @@
<textarea class="form-control" rows="12" name="notes" placeholder="Notes, eg Parking Info, Building Access etc"><?php echo $location_notes; ?></textarea>
</div>
<div class="form-group">
<label>Tags</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tags"></i></span>
</div>
<select class="form-control select2" name="tags[]" data-placeholder="Add some tags" multiple>
<?php
$sql_tags_select = mysqli_query($mysqli, "SELECT * FROM tags WHERE tag_type = 2 ORDER BY tag_name ASC");
while ($row = mysqli_fetch_array($sql_tags_select)) {
$tag_id_select = intval($row['tag_id']);
$tag_name_select = nullable_htmlentities($row['tag_name']);
?>
<option value="<?php echo $tag_id_select; ?>" <?php if (in_array($tag_id_select, $location_tag_id_array)) { echo "selected"; } ?>><?php echo $tag_name_select; ?></option>
<?php } ?>
</select>
</div>
</div>
</div>
</div>

View File

@@ -12,10 +12,13 @@ $url_query_strings_sort = http_build_query($get_copy);
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS * FROM locations
"SELECT SQL_CALC_FOUND_ROWS locations.*, GROUP_CONCAT(tag_name) FROM locations
LEFT JOIN location_tags ON location_tags.location_id = locations.location_id
LEFT JOIN tags ON tags.tag_id = location_tags.tag_id
WHERE location_client_id = $client_id
AND location_$archive_query
AND (location_name LIKE '%$q%' OR location_description LIKE '%$q%' OR location_address LIKE '%$q%' OR location_phone LIKE '%$phone_query%')
AND (location_name LIKE '%$q%' OR location_description LIKE '%$q%' OR location_address LIKE '%$q%' OR location_phone LIKE '%$phone_query%' OR tag_name LIKE '%$q%')
GROUP BY location_id
ORDER BY location_primary DESC, $sort $order LIMIT $record_from, $record_to"
);
@@ -118,6 +121,29 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
$location_primary_display = "";
}
// Tags
$location_tag_name_display_array = array();
$location_tag_id_array = array();
$sql_location_tags = mysqli_query($mysqli, "SELECT * FROM location_tags LEFT JOIN tags ON location_tags.tag_id = tags.tag_id WHERE location_tags.location_id = $location_id ORDER BY tag_name ASC");
while ($row = mysqli_fetch_array($sql_location_tags)) {
$location_tag_id = intval($row['tag_id']);
$location_tag_name = nullable_htmlentities($row['tag_name']);
$location_tag_color = nullable_htmlentities($row['tag_color']);
if (empty($location_tag_color)) {
$location_tag_color = "dark";
}
$location_tag_icon = nullable_htmlentities($row['tag_icon']);
if (empty($location_tag_icon)) {
$location_tag_icon = "tag";
}
$location_tag_id_array[] = $location_tag_id;
$location_tag_name_display_array[] = "<a href='client_locations.php?client_id=$client_id&q=$location_tag_name'><span class='badge text-light p-1 mr-1' style='background-color: $location_tag_color;'><i class='fa fa-fw fa-$location_tag_icon mr-2'></i>$location_tag_name</span></a>";
}
$location_tags_display = implode('', $location_tag_name_display_array);
?>
<tr>
<td>
@@ -128,6 +154,12 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div <?php if($location_primary) { echo "class='text-bold'"; } ?>><?php echo $location_name; ?></div>
<div><small class="text-secondary"><?php echo $location_description; ?></small></div>
<div><?php echo $location_primary_display; ?></div>
<?php
if (!empty($location_tags_display)) { ?>
<div class="mt-1">
<?php echo $location_tags_display; ?>
</div>
<?php } ?>
</div>
</div>
</a>

View File

@@ -89,8 +89,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
$login_uri = nullable_htmlentities($row['login_uri']);
if (empty($login_uri)) {
$login_uri_display = "-";
} else {
$login_uri_display = "$login_uri<button class='btn btn-sm clipboardjs' type='button' data-clipboard-text='$login_uri'><i class='far fa-copy text-secondary'></i></button>";
} else {
$login_uri_display = truncate($login_uri,40) . "<button class='btn btn-sm clipboardjs' type='button' data-clipboard-text='$login_uri'><i class='far fa-copy text-secondary'></i></button>";
}
$login_uri_2 = nullable_htmlentities($row['login_uri_2']);
$login_username = nullable_htmlentities(decryptLoginEntry($row['login_username']));

View File

@@ -44,23 +44,24 @@ $url_query_strings_sort = http_build_query($get_copy);
$sql = mysqli_query(
$mysqli,
"
SELECT SQL_CALC_FOUND_ROWS clients.*, contacts.*, locations.*, GROUP_CONCAT(tags.tag_name) AS tag_names
SELECT SQL_CALC_FOUND_ROWS clients.*, contacts.*, locations.*, GROUP_CONCAT(tag_name)
FROM clients
LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1
LEFT JOIN locations ON clients.client_id = locations.location_client_id AND location_primary = 1
LEFT JOIN client_tags ON client_tags.client_tag_client_id = clients.client_id
LEFT JOIN tags ON tags.tag_id = client_tags.client_tag_tag_id
WHERE (clients.client_name LIKE '%$q%' OR clients.client_type LIKE '%$q%' OR clients.client_referral LIKE '%$q%'
OR contacts.contact_email LIKE '%$q%' OR contacts.contact_name LIKE '%$q%' OR contacts.contact_phone LIKE '%$phone_query%'
OR contacts.contact_mobile LIKE '%$phone_query%' OR locations.location_address LIKE '%$q%'
OR locations.location_city LIKE '%$q%' OR locations.location_state LIKE '%$q%' OR locations.location_zip LIKE '%$q%'
OR tags.tag_name LIKE '%$q%' OR clients.client_tax_id_number LIKE '%$q%')
AND clients.client_$archive_query
AND DATE(clients.client_created_at) BETWEEN '$dtf' AND '$dtt'
AND clients.client_lead = $leads
LEFT JOIN client_tags ON client_tags.client_id = clients.client_id
LEFT JOIN tags ON tags.tag_id = client_tags.tag_id
WHERE (client_name LIKE '%$q%' OR client_type LIKE '%$q%' OR client_referral LIKE '%$q%'
OR contact_email LIKE '%$q%' OR contact_name LIKE '%$q%' OR contact_phone LIKE '%$phone_query%'
OR contact_mobile LIKE '%$phone_query%' OR location_address LIKE '%$q%'
OR location_city LIKE '%$q%' OR location_state LIKE '%$q%' OR location_zip LIKE '%$q%'
OR tag_name LIKE '%$q%' OR client_tax_id_number LIKE '%$q%')
AND client_$archive_query
AND DATE(client_created_at) BETWEEN '$dtf' AND '$dtt'
AND client_lead = $leads
$access_permission_query
$industry_query
$referral_query
GROUP BY clients.client_id
GROUP BY client_id
ORDER BY $sort $order
LIMIT $record_from, $record_to
");
@@ -245,7 +246,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
$client_tag_name_display_array = array();
$client_tag_id_array = array();
$sql_client_tags = mysqli_query($mysqli, "SELECT * FROM client_tags LEFT JOIN tags ON client_tags.client_tag_tag_id = tags.tag_id WHERE client_tags.client_tag_client_id = $client_id ORDER BY tag_name ASC");
$sql_client_tags = mysqli_query($mysqli, "SELECT * FROM client_tags LEFT JOIN tags ON client_tags.tag_id = tags.tag_id WHERE client_id = $client_id ORDER BY tag_name ASC");
while ($row = mysqli_fetch_array($sql_client_tags)) {
$client_tag_id = intval($row['tag_id']);

View File

@@ -252,6 +252,7 @@ if (mysqli_num_rows($sql_scheduled_tickets) > 0) {
$details = mysqli_real_escape_string($mysqli, $row['scheduled_ticket_details']);
$priority = sanitizeInput($row['scheduled_ticket_priority']);
$frequency = sanitizeInput(strtolower($row['scheduled_ticket_frequency']));
$billable = intval($row['scheduled_ticket_billable']);
$created_id = intval($row['scheduled_ticket_created_by']);
$assigned_id = intval($row['scheduled_ticket_assigned_to']);
$client_id = intval($row['scheduled_ticket_client_id']);
@@ -272,7 +273,7 @@ if (mysqli_num_rows($sql_scheduled_tickets) > 0) {
mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1");
// Raise the ticket
mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_subject = '$subject', ticket_details = '$details', ticket_priority = '$priority', ticket_status = '$ticket_status', ticket_created_by = $created_id, ticket_assigned_to = $assigned_id, ticket_contact_id = $contact_id, ticket_client_id = $client_id, ticket_asset_id = $asset_id");
mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_subject = '$subject', ticket_details = '$details', ticket_priority = '$priority', ticket_status = '$ticket_status', ticket_billable = $billable, ticket_created_by = $created_id, ticket_assigned_to = $assigned_id, ticket_contact_id = $contact_id, ticket_client_id = $client_id, ticket_asset_id = $asset_id");
$id = mysqli_insert_id($mysqli);
// Logging
@@ -733,7 +734,7 @@ while ($row = mysqli_fetch_array($sql_recurring_expenses)) {
// TELEMETRY
if ($config_telemetry > 0 OR $config_telemetry = 2) {
if ($config_telemetry > 0 OR $config_telemetry == 2) {
$current_version = exec("git rev-parse HEAD");
@@ -979,6 +980,19 @@ if ($config_telemetry > 0 OR $config_telemetry = 2) {
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Cron', log_action = 'Telemetry', log_description = 'Cron sent telemetry results to ITFlow Developers'");
}
// Fetch Updates
$updates = fetchUpdates();
$update_message = $updates->update_message;
if ($updates->current_version !== $updates->latest_version) {
// Send Alert to inform Updates Available
mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Update', notification = '$update_message', notification_action = 'admin_update.php'");
}
/*
* ###############################################################################################################
* FINISH UP

View File

@@ -1,13 +0,0 @@
/* Custom CSS Styling for time tracking */
/* Small bit of space between the ticket status and time tracking inputs */
.custom-tt-horizontal-spacing {
width: 20px; /* Adjust the width as needed for smaller spacing */
display: inline-block;
}
/* Adjust Hour, min, and second fields to be close together */
.custom-tt-width {
width: 50px; /* Adjust the width as needed */
padding: 0; /* Remove padding to maintain the desired width */
}

View File

@@ -335,7 +335,7 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
if (CURRENT_DATABASE_VERSION == '0.2.0') {
//Insert queries here required to update to DB version 0.2.1
mysqli_query($mysqli, "ALTER TABLE `vendors`
mysqli_query($mysqli, "ALTER TABLE `vendors`
ADD `vendor_hours` VARCHAR(200) NULL DEFAULT NULL AFTER `vendor_website`,
ADD `vendor_sla` VARCHAR(200) NULL DEFAULT NULL AFTER `vendor_hours`,
ADD `vendor_code` VARCHAR(200) NULL DEFAULT NULL AFTER `vendor_sla`,
@@ -343,11 +343,11 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
");
mysqli_query($mysqli, "ALTER TABLE `vendors`
DROP `vendor_country`,
DROP `vendor_address`,
DROP `vendor_city`,
DROP `vendor_state`,
DROP `vendor_zip`,
DROP `vendor_country`,
DROP `vendor_address`,
DROP `vendor_city`,
DROP `vendor_state`,
DROP `vendor_zip`,
DROP `vendor_global`
");
@@ -355,7 +355,7 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
mysqli_query($mysqli, "CREATE TABLE `vendor_templates` (`vendor_template_id` int(11) AUTO_INCREMENT PRIMARY KEY,
`vendor_template_name` varchar(200) NOT NULL,
`vendor_template_description` varchar(200) NULL DEFAULT NULL,
`vendor_template_phone` varchar(200) NULL DEFAULT NULL,
`vendor_template_phone` varchar(200) NULL DEFAULT NULL,
`vendor_template_email` varchar(200) NULL DEFAULT NULL,
`vendor_template_website` varchar(200) NULL DEFAULT NULL,
`vendor_template_hours` varchar(200) NULL DEFAULT NULL,
@@ -397,7 +397,7 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
mysqli_query($mysqli, "CREATE TABLE `interfaces` (`interface_id` int(11) AUTO_INCREMENT PRIMARY KEY,
`interface_number` int(11) NULL DEFAULT NULL,
`interface_description` varchar(200) NULL DEFAULT NULL,
`interface_connected_asset` varchar(200) NULL DEFAULT NULL,
`interface_connected_asset` varchar(200) NULL DEFAULT NULL,
`interface_ip` varchar(200) NULL DEFAULT NULL,
`interface_created_at` datetime DEFAULT CURRENT_TIMESTAMP,
`interface_updated_at` datetime NULL ON UPDATE CURRENT_TIMESTAMP,
@@ -1802,24 +1802,24 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
mysqli_query($mysqli, "UPDATE ticket_statuses SET ticket_status_color = '#28a745' WHERE ticket_status_id = 3"); // On Hold
mysqli_query($mysqli, "UPDATE ticket_statuses SET ticket_status_color = '#343a40' WHERE ticket_status_id = 4"); // Auto Close
mysqli_query($mysqli, "UPDATE ticket_statuses SET ticket_status_color = '#343a40' WHERE ticket_status_id = 5"); // Closed
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.1.9'");
}
if (CURRENT_DATABASE_VERSION == '1.1.9') {
mysqli_query($mysqli, "ALTER TABLE `settings` ADD `config_login_remember_me_expire` INT(11) NOT NULL DEFAULT 3 AFTER `config_login_key_secret`");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.2.0'");
}
if (CURRENT_DATABASE_VERSION == '1.2.0') {
mysqli_query($mysqli, "ALTER TABLE `ticket_templates` ADD `ticket_template_order` INT(11) NOT NULL DEFAULT 0 AFTER `ticket_template_details`");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.2.1'");
}
if (CURRENT_DATABASE_VERSION == '1.2.1') {
// Ticket Templates can have many project templates and Project Template can have have many ticket template, so instead create a many to many table relationship
mysqli_query($mysqli, "ALTER TABLE `ticket_templates` DROP `ticket_template_order`");
mysqli_query($mysqli, "ALTER TABLE `ticket_templates` DROP `ticket_template_project_template_id`");
@@ -1831,61 +1831,121 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
`ticket_template_order` INT(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`ticket_template_id`,`project_template_id`)
)");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.2.2'");
}
if (CURRENT_DATABASE_VERSION == '1.2.2') {
mysqli_query($mysqli, "ALTER TABLE `tasks` DROP `task_description`");
mysqli_query($mysqli, "ALTER TABLE `task_templates` DROP `task_template_description`");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.2.3'");
}
if (CURRENT_DATABASE_VERSION == '1.2.3') {
mysqli_query($mysqli, "ALTER TABLE `projects` ADD `project_manager` INT(11) NOT NULL DEFAULT 0 AFTER `project_due`");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.2.4'");
}
if (CURRENT_DATABASE_VERSION == '1.2.4') {
mysqli_query($mysqli, "ALTER TABLE `settings` ADD `config_project_prefix` VARCHAR(200) NOT NULL DEFAULT 'PRJ-' AFTER `config_default_hourly_rate`");
mysqli_query($mysqli, "ALTER TABLE `settings` ADD `config_project_next_number` INT(11) NOT NULL DEFAULT 1 AFTER `config_project_prefix`");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.2.5'");
}
if (CURRENT_DATABASE_VERSION == '1.2.5') {
mysqli_query($mysqli, "ALTER TABLE `projects` ADD `project_prefix` VARCHAR(200) DEFAULT NULL AFTER `project_id`");
mysqli_query($mysqli, "ALTER TABLE `projects` ADD `project_number` INT(11) NOT NULL DEFAULT 1 AFTER `project_prefix`");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.2.6'");
}
if (CURRENT_DATABASE_VERSION == '1.2.6') {
mysqli_query($mysqli, "ALTER TABLE `domains` ADD `domain_dnshost` INT(11) NOT NULL DEFAULT 0 AFTER `domain_webhost`");
mysqli_query($mysqli, "ALTER TABLE `domains` ADD `domain_mailhost` INT(11) NOT NULL DEFAULT 0 AFTER `domain_dnshost`");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.2.7'");
}
if (CURRENT_DATABASE_VERSION == '1.2.7') {
mysqli_query($mysqli, "ALTER TABLE `recurring` ADD `recurring_invoice_email_notify` TINYINT(1) NOT NULL DEFAULT 1 AFTER `recurring_note`");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.2.8'");
}
// if (CURRENT_DATABASE_VERSION == '1.2.8') {
// // Insert queries here required to update to DB version 1.2.9
if (CURRENT_DATABASE_VERSION == '1.2.8') {
mysqli_query($mysqli, "ALTER TABLE `settings` ADD `config_phone_mask` TINYINT(1) NOT NULL DEFAULT 1 AFTER `config_destructive_deletes_enable`");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.2.9'");
}
if (CURRENT_DATABASE_VERSION == '1.2.9') {
mysqli_query($mysqli, "CREATE TABLE `user_permissions` (`user_id` int(11) NOT NULL,`client_id` int(11) NOT NULL, PRIMARY KEY (`user_id`,`client_id`))");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.3.0'");
}
if (CURRENT_DATABASE_VERSION == '1.3.0') {
mysqli_query($mysqli, "CREATE TABLE `user_roles` (
`user_role_id` INT(11) NOT NULL AUTO_INCREMENT,
`user_role_name` VARCHAR(200) NOT NULL,
`user_role_description` VARCHAR(200) NULL DEFAULT NULL,
`user_role_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`user_role_updated_at` DATETIME ON UPDATE CURRENT_TIMESTAMP NULL,
`user_role_archived_at` DATETIME NULL,
PRIMARY KEY (`user_role_id`)
)");
mysqli_query($mysqli, "INSERT INTO `user_roles` SET user_role_id = 1, user_role_name = 'Accountant', user_role_description = 'Built-in - Limited access to financial-focused modules'");
mysqli_query($mysqli, "INSERT INTO `user_roles` SET user_role_id = 2, user_role_name = 'Technician', user_role_description = 'Built-in - Limited access to technical-focused modules'");
mysqli_query($mysqli, "INSERT INTO `user_roles` SET user_role_id = 3, user_role_name = 'Administrator', user_role_description = 'Built-in - Full administrative access to all modules (including user management)'");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.3.1'");
}
if (CURRENT_DATABASE_VERSION == '1.3.1') {
mysqli_query($mysqli, "ALTER TABLE `user_settings` ADD `user_config_calendar_first_day` TINYINT(1) NOT NULL DEFAULT 0 AFTER `user_config_dashboard_technical_enable`");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.3.2'");
}
if (CURRENT_DATABASE_VERSION == '1.3.2') {
mysqli_query($mysqli, "ALTER TABLE `settings` ADD `config_ticket_default_billable` TINYINT(1) NOT NULL DEFAULT 0 AFTER `config_ticket_new_ticket_notification_email`");
mysqli_query($mysqli, "ALTER TABLE `scheduled_tickets` ADD `scheduled_ticket_billable` TINYINT(1) NOT NULL DEFAULT 0 AFTER `scheduled_ticket_frequency`");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.3.3'");
}
if (CURRENT_DATABASE_VERSION == '1.3.3') {
// // Insert queries here required to update to DB version 1.3.3
// // Then, update the database to the next sequential version
// mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.2.9");
mysqli_query($mysqli, "CREATE TABLE `location_tags` (`location_id` int(11) NOT NULL,`tag_id` int(11) NOT NULL, PRIMARY KEY (`location_id`,`tag_id`))");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.3.4'");
}
if (CURRENT_DATABASE_VERSION == '1.3.4') {
mysqli_query($mysqli, "ALTER TABLE `client_tags` CHANGE `client_tag_client_id` `client_id` INT(11) NOT NULL");
mysqli_query($mysqli, "ALTER TABLE `client_tags` CHANGE `client_tag_tag_id` `tag_id` INT(11) NOT NULL");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.3.5'");
}
// if (CURRENT_DATABASE_VERSION == '1.3.5') {
// // Insert queries here required to update to DB version 1.3.6
// // Then, update the database to the next sequential version
// mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.3.6'");
// }
} else {
// Up-to-date
}

View File

@@ -5,4 +5,4 @@
* It is used in conjunction with database_updates.php
*/
DEFINE("LATEST_DATABASE_VERSION", "1.2.8");
DEFINE("LATEST_DATABASE_VERSION", "1.3.5");

58
db.sql
View File

@@ -250,9 +250,9 @@ DROP TABLE IF EXISTS `client_tags`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `client_tags` (
`client_tag_client_id` int(11) NOT NULL,
`client_tag_tag_id` int(11) NOT NULL,
PRIMARY KEY (`client_tag_client_id`,`client_tag_tag_id`)
`client_id` int(11) NOT NULL,
`tag_id` int(11) NOT NULL,
PRIMARY KEY (`client_id`,`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -751,6 +751,20 @@ CREATE TABLE `invoices` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `location_tags`
--
DROP TABLE IF EXISTS `location_tags`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `location_tags` (
`location_id` int(11) NOT NULL,
`tag_id` int(11) NOT NULL,
PRIMARY KEY (`location_id`,`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `locations`
--
@@ -1156,6 +1170,7 @@ CREATE TABLE `scheduled_tickets` (
`scheduled_ticket_details` longtext NOT NULL,
`scheduled_ticket_priority` varchar(200) DEFAULT NULL,
`scheduled_ticket_frequency` varchar(10) NOT NULL,
`scheduled_ticket_billable` tinyint(1) NOT NULL DEFAULT 0,
`scheduled_ticket_start_date` date NOT NULL,
`scheduled_ticket_next_run` date NOT NULL,
`scheduled_ticket_created_at` datetime NOT NULL DEFAULT current_timestamp(),
@@ -1341,6 +1356,7 @@ CREATE TABLE `settings` (
`config_ticket_autoclose` tinyint(1) NOT NULL DEFAULT 0,
`config_ticket_autoclose_hours` int(5) NOT NULL DEFAULT 72,
`config_ticket_new_ticket_notification_email` varchar(200) DEFAULT NULL,
`config_ticket_default_billable` tinyint(1) NOT NULL DEFAULT 0,
`config_enable_cron` tinyint(1) NOT NULL DEFAULT 0,
`config_cron_key` varchar(255) DEFAULT NULL,
`config_recurring_auto_send_invoice` tinyint(1) NOT NULL DEFAULT 1,
@@ -1375,6 +1391,7 @@ CREATE TABLE `settings` (
`config_telemetry` tinyint(1) DEFAULT 0,
`config_timezone` varchar(200) NOT NULL DEFAULT 'America/New_York',
`config_destructive_deletes_enable` tinyint(1) NOT NULL DEFAULT 0,
`config_phone_mask` tinyint(1) NOT NULL DEFAULT 1,
PRIMARY KEY (`company_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -1776,6 +1793,38 @@ CREATE TABLE `trips` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `user_permissions`
--
DROP TABLE IF EXISTS `user_permissions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `user_permissions` (
`user_id` int(11) NOT NULL,
`client_id` int(11) NOT NULL,
PRIMARY KEY (`user_id`,`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `user_roles`
--
DROP TABLE IF EXISTS `user_roles`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `user_roles` (
`user_role_id` int(11) NOT NULL AUTO_INCREMENT,
`user_role_name` varchar(200) NOT NULL,
`user_role_description` varchar(200) DEFAULT NULL,
`user_role_created_at` datetime NOT NULL DEFAULT current_timestamp(),
`user_role_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(),
`user_role_archived_at` datetime DEFAULT NULL,
PRIMARY KEY (`user_role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `user_settings`
--
@@ -1790,6 +1839,7 @@ CREATE TABLE `user_settings` (
`user_config_records_per_page` int(11) NOT NULL DEFAULT 10,
`user_config_dashboard_financial_enable` tinyint(1) NOT NULL DEFAULT 0,
`user_config_dashboard_technical_enable` tinyint(1) NOT NULL DEFAULT 0,
`user_config_calendar_first_day` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -1902,4 +1952,4 @@ CREATE TABLE `vendors` (
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2024-04-12 19:37:23
-- Dump completed on 2024-05-31 16:45:46

View File

@@ -36,8 +36,9 @@ require_once "header.php";
if (!empty($session_token)) {
//Generate QR Code based off the generated key
print sprintf('<img src="%s"/>', TokenAuth6238::getBarCodeUrl($session_name, ' ', $session_token, $_SERVER['SERVER_NAME']));
// Generate QR Code
$data = "otpauth://totp/ITFlow:$session_email?secret=$session_token";
print "<img src='plugins/barcode/barcode.php?f=png&s=qr&d=$data'>";
echo "<p class='text-secondary'>$session_token</p>";
}

View File

@@ -212,6 +212,16 @@ function truncate($text, $chars)
function formatPhoneNumber($phoneNumber)
{
global $mysqli;
// Get Phone Mask Option
$phone_mask = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_phone_mask FROM settings WHERE company_id = 1"))[0];
if ($phone_mask == 0) {
return $phoneNumber;
}
$phoneNumber = $phoneNumber ? preg_replace('/[^0-9]/', '', $phoneNumber) : "";
if (strlen($phoneNumber) > 10) {
@@ -427,9 +437,18 @@ function getDomainRecords($name)
// Used to automatically attempt to get SSL certificates as part of adding domains
// The logic for the fetch (sync) button on the client_certificates page is in ajax.php, and allows ports other than 443
function getSSL($name)
function getSSL($full_name)
{
// Parse host and port
$name = parse_url("//$full_name", PHP_URL_HOST);
$port = parse_url("//$full_name", PHP_URL_PORT);
// Default port
if (!$port) {
$port = "443";
}
$certificate = array();
$certificate['success'] = false;
@@ -442,7 +461,7 @@ function getSSL($name)
}
// Get SSL/TSL certificate (using verify peer false to allow for self-signed certs) for domain on default port
$socket = "ssl://$name:443";
$socket = "ssl://$name:$port";
$get = stream_context_create(array("ssl" => array("capture_peer_cert" => true, "verify_peer" => false,)));
$read = stream_socket_client($socket, $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $get);
@@ -881,7 +900,7 @@ function getSettingValue($mysqli, $setting_name)
function getMonthlyTax($tax_name, $month, $year, $mysqli)
{
// SQL to calculate monthly tax
$sql = "SELECT SUM(item_tax) AS monthly_tax FROM invoice_items
$sql = "SELECT SUM(item_tax) AS monthly_tax FROM invoice_items
LEFT JOIN invoices ON invoice_items.item_invoice_id = invoices.invoice_id
LEFT JOIN payments ON invoices.invoice_id = payments.payment_invoice_id
WHERE YEAR(payments.payment_date) = $year AND MONTH(payments.payment_date) = $month
@@ -898,7 +917,7 @@ function getQuarterlyTax($tax_name, $quarter, $year, $mysqli)
$end_month = $start_month + 2;
// SQL to calculate quarterly tax
$sql = "SELECT SUM(item_tax) AS quarterly_tax FROM invoice_items
$sql = "SELECT SUM(item_tax) AS quarterly_tax FROM invoice_items
LEFT JOIN invoices ON invoice_items.item_invoice_id = invoices.invoice_id
LEFT JOIN payments ON invoices.invoice_id = payments.payment_invoice_id
WHERE YEAR(payments.payment_date) = $year AND MONTH(payments.payment_date) BETWEEN $start_month AND $end_month
@@ -911,7 +930,7 @@ function getQuarterlyTax($tax_name, $quarter, $year, $mysqli)
function getTotalTax($tax_name, $year, $mysqli)
{
// SQL to calculate total tax
$sql = "SELECT SUM(item_tax) AS total_tax FROM invoice_items
$sql = "SELECT SUM(item_tax) AS total_tax FROM invoice_items
LEFT JOIN invoices ON invoice_items.item_invoice_id = invoices.invoice_id
LEFT JOIN payments ON invoices.invoice_id = payments.payment_invoice_id
WHERE YEAR(payments.payment_date) = $year
@@ -1172,3 +1191,34 @@ function getTicketStatusName($ticket_status) {
return "Unknown";
}
function fetchUpdates() {
global $repo_branch;
// Fetch the latest code changes but don't apply them
exec("git fetch", $output, $result);
$latest_version = exec("git rev-parse origin/$repo_branch");
$current_version = exec("git rev-parse HEAD");
if ($current_version == $latest_version) {
$update_message = "No Updates available";
} else {
$update_message = "New Updates are Available [$latest_version]";
}
$updates = new stdClass();
$updates->output = $output;
$updates->result = $result;
$updates->current_version = $current_version;
$updates->latest_version = $latest_version;
$updates->update_message = $update_message;
return $updates;
}

View File

@@ -72,6 +72,7 @@ $config_ticket_client_general_notifications = intval($row['config_ticket_client_
$config_ticket_autoclose = intval($row['config_ticket_autoclose']);
$config_ticket_autoclose_hours = intval($row['config_ticket_autoclose_hours']);
$config_ticket_new_ticket_notification_email = $row['config_ticket_new_ticket_notification_email'];
$config_ticket_default_billable = intval($row['config_ticket_default_billable']);
// Cron
$config_enable_cron = intval($row['config_enable_cron']);

View File

@@ -24,6 +24,7 @@ if (isset($_GET['query'])) {
LEFT JOIN locations ON clients.client_id = locations.location_client_id AND location_primary = 1
WHERE client_archived_at IS NULL
AND client_name LIKE '%$query%'
$access_permission_query
ORDER BY client_id DESC LIMIT 5"
);
@@ -35,6 +36,7 @@ if (isset($_GET['query'])) {
OR contact_email LIKE '%$query%'
OR contact_phone LIKE '%$phone_query%'
OR contact_mobile LIKE '%$phone_query%')
$access_permission_query
ORDER BY contact_id DESC LIMIT 5"
);
@@ -43,6 +45,7 @@ if (isset($_GET['query'])) {
WHERE vendor_archived_at IS NULL
AND vendor_template = 0
AND (vendor_name LIKE '%$query%' OR vendor_phone LIKE '%$phone_query%')
$access_permission_query
ORDER BY vendor_id DESC LIMIT 5"
);
@@ -50,6 +53,7 @@ if (isset($_GET['query'])) {
LEFT JOIN clients ON domain_client_id = client_id
WHERE domain_archived_at IS NULL
AND domain_name LIKE '%$query%'
$access_permission_query
ORDER BY domain_id DESC LIMIT 5"
);
@@ -63,6 +67,7 @@ if (isset($_GET['query'])) {
LEFT JOIN clients on document_client_id = clients.client_id
WHERE document_archived_at IS NULL
AND MATCH(document_content_raw) AGAINST ('$query')
$access_permission_query
ORDER BY document_id DESC LIMIT 5"
);
@@ -72,6 +77,7 @@ if (isset($_GET['query'])) {
WHERE file_archived_at IS NULL
AND (file_name LIKE '%$query%'
OR file_description LIKE '%$query%')
$access_permission_query
ORDER BY file_id DESC LIMIT 5"
);
@@ -81,6 +87,7 @@ if (isset($_GET['query'])) {
WHERE ticket_archived_at IS NULL
AND (ticket_subject LIKE '%$query%'
OR ticket_number = '$ticket_num_query')
$access_permission_query
ORDER BY ticket_id DESC LIMIT 5"
);
@@ -88,6 +95,7 @@ if (isset($_GET['query'])) {
LEFT JOIN clients ON scheduled_ticket_client_id = client_id
WHERE scheduled_ticket_subject LIKE '%$query%'
OR scheduled_ticket_details LIKE '%$query%'
$access_permission_query
ORDER BY scheduled_ticket_id DESC LIMIT 5"
);
@@ -96,6 +104,7 @@ if (isset($_GET['query'])) {
LEFT JOIN clients ON login_client_id = client_id
WHERE login_archived_at IS NULL
AND (login_name LIKE '%$query%' OR login_description LIKE '%$query%')
$access_permission_query
ORDER BY login_id DESC LIMIT 5"
);
@@ -104,6 +113,7 @@ if (isset($_GET['query'])) {
LEFT JOIN categories ON invoice_category_id = category_id
WHERE invoice_archived_at IS NULL
AND (CONCAT(invoice_prefix,invoice_number) LIKE '%$query%' OR invoice_scope LIKE '%$query%')
$access_permission_query
ORDER BY invoice_number DESC LIMIT 5"
);
@@ -113,6 +123,7 @@ if (isset($_GET['query'])) {
LEFT JOIN clients ON asset_client_id = client_id
WHERE asset_archived_at IS NULL
AND (asset_name LIKE '%$query%' OR asset_description LIKE '%$query%' OR asset_type LIKE '%$query%' OR asset_make LIKE '%$query%' OR asset_model LIKE '%$query%' OR asset_serial LIKE '%$query%' OR asset_os LIKE '%$query%' OR asset_ip LIKE '%$query%' OR asset_nat_ip LIKE '%$query%' OR asset_mac LIKE '%$query%' OR asset_status LIKE '%$query%')
$access_permission_query
ORDER BY asset_name DESC LIMIT 5"
);
@@ -121,6 +132,7 @@ if (isset($_GET['query'])) {
LEFT JOIN clients ON ticket_client_id = client_id
WHERE ticket_reply_archived_at IS NULL
AND (ticket_reply LIKE '%$query%')
$access_permission_query
ORDER BY ticket_id DESC, ticket_reply_id ASC LIMIT 20"
);

View File

@@ -15,6 +15,12 @@ $user_agent = sanitizeInput($_SERVER['HTTP_USER_AGENT']);
$os = sanitizeInput(getOS($user_agent));
$browser = sanitizeInput(getWebBrowser($user_agent));
// Get Company Name
$sql = mysqli_query($mysqli, "SELECT company_name FROM companies WHERE company_id = 1");
$row = mysqli_fetch_array($sql);
$session_company_name = $row['company_name'];
?>
<!DOCTYPE html>
@@ -25,7 +31,7 @@ $browser = sanitizeInput(getWebBrowser($user_agent));
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="robots" content="noindex">
<title><?php echo nullable_htmlentities($config_app_name); ?></title>
<title><?php echo nullable_htmlentities($session_company_name); ?></title>
<!--
Favicon

View File

@@ -12,6 +12,10 @@ require_once "top_nav.php";
// Get Main Side Bar Badge Counts
// Active Clients Count
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('client_id') AS num FROM clients WHERE client_archived_at IS NULL"));
$num_active_clients = $row['num'];
// Active Ticket Count
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('ticket_id') AS num FROM tickets WHERE ticket_archived_at IS NULL AND ticket_closed_at IS NULL AND ticket_status != 4"));
$num_active_tickets = $row['num'];

View File

@@ -6,14 +6,22 @@ require_once "functions.php";
require_once "check_login.php";
require_once "header.php";
require_once "top_nav.php";
if (isset($_GET['client_id'])) {
$client_id = intval($_GET['client_id']);
// Check to see if the logged in user has permission to access this client (Admins have access to all no matter what perms are set)
if(!in_array($client_id, $client_access_array) AND !empty($client_access_string) AND $session_user_role < 3) {
// Logging
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Client', log_action = 'Access', log_description = '$session_name was denied permission from accessing client', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $client_id");
$_SESSION['alert_type'] = "error";
$_SESSION['alert_message'] = "Access Denied - You do not have permission to access that client!";
echo "<script>window.history.back();</script>";
exit();
}
$sql = mysqli_query($mysqli, "UPDATE clients SET client_accessed_at = NOW() WHERE client_id = $client_id");
$sql = mysqli_query(
@@ -69,7 +77,7 @@ if (isset($_GET['client_id'])) {
$client_tag_name_display_array = array();
$client_tag_id_array = array();
$sql_client_tags = mysqli_query($mysqli, "SELECT * FROM client_tags LEFT JOIN tags ON client_tags.client_tag_tag_id = tags.tag_id WHERE client_tags.client_tag_client_id = $client_id ORDER BY tag_name ASC");
$sql_client_tags = mysqli_query($mysqli, "SELECT * FROM client_tags LEFT JOIN tags ON client_tags.tag_id = tags.tag_id WHERE client_id = $client_id ORDER BY tag_name ASC");
while ($row = mysqli_fetch_array($sql_client_tags)) {
$client_tag_id = intval($row['tag_id']);
@@ -231,6 +239,10 @@ if (isset($_GET['client_id'])) {
}
}
require_once "header.php";
require_once "top_nav.php";
require_once "client_side_nav.php";
require_once "inc_wrapper.php";

View File

@@ -1,147 +0,0 @@
// ticketCounter.js
(function() {
function getRunningTicketCount() {
let count = 0;
for (let i = 0; i < localStorage.length; i++) {
let key = localStorage.key(i);
if (key.includes("ticket-timer-running")) {
let isRunning = JSON.parse(localStorage.getItem(key));
if (isRunning) {
count++;
}
}
}
return count;
}
function updateTicketCountDisplay() {
let count = getRunningTicketCount();
let countDisplay = document.getElementById("runningTicketsCount");
if (countDisplay) {
countDisplay.innerText = count;
}
if (count == 0) {
countDisplay.classList.remove('badge-danger');
} else {
//check to see if more than one ticket
if (count > 1) {
countDisplay.classList.add('badge-danger');
}
//if count is one, check to see if its open in the current window by looking at the post variable ticket_id in url
if (count == 1) {
let urlParams = new URLSearchParams(window.location.search);
let ticketID = urlParams.get('ticket_id');
console.log(ticketID);
// If ticket number equals one in local storage, then add badge-danger class
if (localStorage.getItem("ticket-timer-running-") == ticketID) {
countDisplay.classList.add('badge-danger');
} else {
countDisplay.classList.remove('badge-danger');
}
}
}
}
function getElapsedSeconds(ticketID) {
let storedStartTime = parseInt(localStorage.getItem(ticketID + "-startTime") || "0");
let pausedTime = parseInt(localStorage.getItem(ticketID + "-pausedTime") || "0");
if (!storedStartTime) return pausedTime;
let timeSinceStart = Math.floor((Date.now() - storedStartTime) / 1000);
return pausedTime + timeSinceStart;
}
function formatTime(seconds) {
let hours = Math.floor(seconds / 3600);
let minutes = Math.floor((seconds % 3600) / 60);
let secs = seconds % 60;
return `${hours}h ${minutes}m ${secs}s`;
}
function loadOpenTickets() {
let openTicketsContainer = document.getElementById('openTicketsContainer');
openTicketsContainer.innerHTML = ''; // Clear existing content
for (let i = 0; i < localStorage.length; i++) {
let key = localStorage.key(i);
if (key.startsWith("ticket-timer-running-")) {
let ticketID = key.replace("ticket-timer-running-", "");
let isRunning = JSON.parse(localStorage.getItem(key));
let ticketDiv = document.createElement('div');
ticketDiv.classList.add('card', 'card-outline', 'mb-3');
// Add class based on ticket status
ticketDiv.classList.add(isRunning ? 'card-info' : 'card-warning');
ticketDiv.id = 'ticket-' + ticketID;
let elapsedSecs = getElapsedSeconds(ticketID);
let timeString = formatTime(elapsedSecs);
ticketDiv.innerHTML = `
<div class="card-header">
<h3 class="card-title">Ticket ID: ${ticketID}</h3>
<a href="/ticket.php?ticket_id=${ticketID}" class="btn btn-primary float-right">View Ticket</a>
</div>
<div class="card-body">
<p id="time-${ticketID}">Total Time: ${timeString}</p>
</div>
`;
openTicketsContainer.appendChild(ticketDiv);
}
}
requestAnimationFrame(() => updateRunningTickets());
}
function updateRunningTickets() {
let runningTickets = document.querySelectorAll('[id^="ticket-"]');
runningTickets.forEach(ticket => {
let ticketID = ticket.id.replace("ticket-", "");
let isRunning = JSON.parse(localStorage.getItem("ticket-timer-running-" + ticketID));
if (isRunning) {
let updatedTime = formatTime(getElapsedSeconds(ticketID));
document.getElementById('time-' + ticketID).innerText = 'Total Time: ' + updatedTime;
}
});
requestAnimationFrame(updateRunningTickets);
}
function clearAllTimers() {
// Collect keys to be removed
let keysToRemove = [];
for (let i = 0; i < localStorage.length; i++) {
let key = localStorage.key(i);
if (key.startsWith("ticket-timer-running-") || key.endsWith("-startTime") || key.endsWith("-pausedTime")) {
keysToRemove.push(key);
}
}
// Remove collected keys
keysToRemove.forEach(key => localStorage.removeItem(key));
// Update the display and redirect
updateTicketCountDisplay();
window.location.href = "/tickets.php";
}
// Initial update on script load
updateTicketCountDisplay();
// update every 10 seconds
setInterval(updateTicketCountDisplay, 10000);
// Add event listener to modal
document.addEventListener('DOMContentLoaded', function() {
let modal = document.getElementById('openTicketsModal');
if (modal) {
$('#openTicketsModal').on('show.bs.modal', loadOpenTickets);
}
});
// Add event listener to clear all timers button
document.getElementById('clearAllTimers').addEventListener('click', clearAllTimers);
})();

View File

@@ -19,6 +19,7 @@ function populateRecurringTicketEditModal(client_id, ticket_id) {
document.getElementById("editHeader").innerText = " Edit Recurring ticket: " + ticket.scheduled_ticket_subject;
document.getElementById("editTicketId").value = ticket_id;
document.getElementById("editClientId").value = client_id;
document.getElementById("editTicketBillable").value = ticket.scheduled_ticket_billable;
document.getElementById("editTicketSubject").value = ticket.scheduled_ticket_subject;
document.getElementById("editTicketNextRun").value = ticket.scheduled_ticket_next_run;
tinyMCE.get('editTicketDetails').setContent(ticket.scheduled_ticket_details);

View File

@@ -1,19 +0,0 @@
// Ticket.php - Changes the wording of the "Respond" button to "Add note" if reply is not a public update (based on checkbox)
// Get Internal/Public Checkbox
let checkbox = document.getElementById('ticket_reply_type_checkbox');
// Get Respond button
let respond = document.getElementById('ticket_add_reply');
// When checkbox is checked/unchecked, update button wording
checkbox.addEventListener('change', e => {
if (e.target.checked) {
// Public reply
respond.innerHTML = "<i class=\"fas fa-paper-plane mr-2\"></i>Respond";
} else {
// Internal note
respond.innerHTML = "<i class=\"fas fa-sticky-note mr-2\"></i>Add note";
}
});

View File

@@ -8,8 +8,6 @@
var ticketID = getCurrentTicketID();
var elapsedSecs = getElapsedSeconds();
updateRunningTicketsCount();
function getCurrentTicketID() {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('ticket_id');
@@ -55,7 +53,6 @@
timerInterval = setInterval(countTime, 1000);
isPaused = false;
document.getElementById("startStopTimer").innerText = "Pause";
updateRunningTicketsCount();
localStorage.setItem("ticket-timer-running-" + ticketID, "true");
}
@@ -70,7 +67,6 @@
localStorage.removeItem(getLocalStorageKey("startTime"));
isPaused = true;
document.getElementById("startStopTimer").innerText = "Start";
updateRunningTicketsCount();
localStorage.setItem("ticket-timer-running-" + ticketID, "false");
}
@@ -91,7 +87,6 @@
document.getElementById("startStopTimer").innerText = "Start";
}
localStorage.setItem("ticket-timer-running-" + ticketID, "false");
updateRunningTicketsCount();
}
function forceResetTimer() {
@@ -102,7 +97,7 @@
displayTime();
document.getElementById("startStopTimer").innerText = "Start";
}
function handleInputFocus() {
if (!isPaused) {
pauseTimer();
@@ -114,7 +109,7 @@
const minutes = parseInt(document.getElementById("minutes").value, 10) || 0;
const seconds = parseInt(document.getElementById("seconds").value, 10) || 0;
elapsedSecs = (hours * 3600) + (minutes * 60) + seconds;
// Update local storage so the manually entered time is retained even if the page is reloaded.
if (!timerInterval) {
localStorage.setItem(getLocalStorageKey("pausedTime"), elapsedSecs.toString());
@@ -125,18 +120,6 @@
}
}
function updateRunningTicketsCount() {
let runningTickets = parseInt(document.getElementById('runningTicketsCount').innerText, 10);
if (!isPaused && timerInterval) {
runningTickets += 1;
} else {
runningTickets = Math.max(0, runningTickets - 1);
}
document.getElementById('runningTicketsCount').innerText = runningTickets.toString();
}
// Function to check status and pause timer
function checkStatusAndPauseTimer() {
var status = document.querySelector('select[name="status"]').value;
@@ -148,7 +131,7 @@
document.getElementById("hours").addEventListener('change', updateTimeFromInput);
document.getElementById("minutes").addEventListener('change', updateTimeFromInput);
document.getElementById("seconds").addEventListener('change', updateTimeFromInput);
document.getElementById("hours").addEventListener('focus', handleInputFocus);
document.getElementById("minutes").addEventListener('focus', handleInputFocus);
document.getElementById("seconds").addEventListener('focus', handleInputFocus);
@@ -184,11 +167,11 @@
} else if (localStorage.getItem(getLocalStorageKey("startTime"))) {
startTimer();
}
// Check and pause timer if status is pending
checkStatusAndPauseTimer();
} catch (error) {
console.error("There was an issue initializing the timer:", error);
}
});
})();
})();

View File

@@ -159,7 +159,7 @@ if (isset($_POST['login'])) {
if (isset($_POST['remember_me'])) {
// TODO: Record the UA and IP a token is generated from so that can be shown later on
$newRememberToken = bin2hex(random_bytes(64));
setcookie('rememberme', $newRememberToken, time() + 86400*2, "/", null, true, true);
setcookie('rememberme', $newRememberToken, time() + 86400*$config_login_remember_me_expire, "/", null, true, true);
mysqli_query($mysqli, "INSERT INTO remember_tokens SET remember_token_user_id = $user_id, remember_token_token = '$newRememberToken'");
$extended_log .= ", generated a new remember-me token";
@@ -218,9 +218,11 @@ if (isset($_POST['login'])) {
//}
}
header("Location: $config_start_page");
if ($_GET['last_visited']) {
header("Location: ".$_SERVER["REQUEST_SCHEME"] . "://" . $config_base_url . base64_decode($_GET['last_visited']) );
} else {
header("Location: $config_start_page");
}
} else {
// MFA is configured and needs to be confirmed, or was unsuccessful

3352
plugins/barcode/barcode.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,7 @@ if (isset($_GET['ai_reword'])) {
$inputJSON = file_get_contents('php://input');
$input = json_decode($inputJSON, TRUE); // Convert JSON into array.
$promptText = "You are an experienced technician at a help desk, training a new technician. You are helping rewrite response for clarity and professionalism, but dont make it too wordy.";
$promptText = "reword with html format";
$userText = $input['text'];
// Preparing the data for the OpenAI Chat API request.

View File

@@ -65,7 +65,7 @@ if (isset($_POST['add_client'])) {
if (isset($_POST['tags'])) {
foreach($_POST['tags'] as $tag) {
$tag = intval($tag);
mysqli_query($mysqli, "INSERT INTO client_tags SET client_tag_client_id = $client_id, client_tag_tag_id = $tag");
mysqli_query($mysqli, "INSERT INTO client_tags SET client_id = $client_id, tag_id = $tag");
}
}
@@ -135,12 +135,12 @@ if (isset($_POST['edit_client'])) {
// Tags
// Delete existing tags
mysqli_query($mysqli, "DELETE FROM client_tags WHERE client_tag_client_id = $client_id");
mysqli_query($mysqli, "DELETE FROM client_tags WHERE client_id = $client_id");
// Add new tags
foreach($_POST['tags'] as $tag) {
$tag = intval($tag);
mysqli_query($mysqli, "INSERT INTO client_tags SET client_tag_client_id = $client_id, client_tag_tag_id = $tag");
mysqli_query($mysqli, "INSERT INTO client_tags SET client_id = $client_id, tag_id = $tag");
}
// Logging
@@ -297,6 +297,9 @@ if (isset($_GET['delete_client'])) {
mysqli_query($mysqli, "DELETE FROM trips WHERE trip_client_id = $client_id");
mysqli_query($mysqli, "DELETE FROM vendors WHERE vendor_client_id = $client_id");
// Delete tags
mysqli_query($mysqli, "DELETE FROM client_tags WHERE client_id = $client_id");
//Delete Client Files
removeDirectory('uploads/clients/$client_id');

View File

@@ -23,7 +23,7 @@ if (isset($_POST['upload_files'])) {
'size' => $_FILES['file']['size'][$i]
];
if ($file_reference_name = checkFileUpload($single_file, array('jpg', 'jpeg', 'gif', 'png', 'webp', 'pdf', 'txt', 'md', 'doc', 'docx', 'odt', 'csv', 'xls', 'xlsx', 'ods', 'pptx', 'odp', 'zip', 'tar', 'gz', 'xml', 'msg', 'json', 'wav', 'mp3', 'ogg', 'mov', 'mp4', 'av1', 'ovpn', 'cfg', 'ps1', 'vsdx', 'drawio', 'pfx', 'pages', 'numbers'))) {
if ($file_reference_name = checkFileUpload($single_file, array('jpg', 'jpeg', 'gif', 'png', 'webp', 'pdf', 'txt', 'md', 'doc', 'docx', 'odt', 'csv', 'xls', 'xlsx', 'ods', 'pptx', 'odp', 'zip', 'tar', 'gz', 'xml', 'msg', 'json', 'wav', 'mp3', 'ogg', 'mov', 'mp4', 'av1', 'ovpn', 'cfg', 'ps1', 'vsdx', 'drawio', 'pfx', 'pages', 'numbers', 'unf'))) {
$file_tmp_path = $_FILES['file']['tmp_name'][$i];

View File

@@ -19,6 +19,14 @@ if(isset($_POST['add_location'])){
$location_id = mysqli_insert_id($mysqli);
// Add Tags
if (isset($_POST['tags'])) {
foreach($_POST['tags'] as $tag) {
$tag = intval($tag);
mysqli_query($mysqli, "INSERT INTO location_tags SET location_id = $location_id, tag_id = $tag");
}
}
// Update Primay location in clients if primary location is checked
if ($location_primary == 1) {
mysqli_query($mysqli,"UPDATE locations SET location_primary = 0 WHERE location_client_id = $client_id");
@@ -82,6 +90,16 @@ if(isset($_POST['edit_location'])){
mysqli_query($mysqli,"UPDATE locations SET location_primary = 1 WHERE location_id = $location_id");
}
// Tags
// Delete existing tags
mysqli_query($mysqli, "DELETE FROM location_tags WHERE location_id = $location_id");
// Add new tags
foreach($_POST['tags'] as $tag) {
$tag = intval($tag);
mysqli_query($mysqli, "INSERT INTO location_tags SET location_id = $location_id, tag_id = $tag");
}
//Check to see if a file is attached
if($_FILES['file']['tmp_name'] != ''){
@@ -174,6 +192,10 @@ if(isset($_GET['delete_location'])){
mysqli_query($mysqli,"DELETE FROM locations WHERE location_id = $location_id");
// Tags
// Delete existing tags
mysqli_query($mysqli, "DELETE FROM location_tags WHERE location_id = $location_id");
//Logging
mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Location', log_action = 'Delete', log_description = '$session_name deleted location $location_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $location_id");

View File

@@ -144,11 +144,18 @@ if (isset($_POST['edit_your_user_password'])) {
header('Location: post.php?logout');
}
if (isset($_POST['edit_your_user_browser_extention'])) {
if (isset($_POST['edit_your_user_preferences'])) {
// CSRF Check
validateCSRFToken($_POST['csrf_token']);
$calendar_first_day = intval($_POST['calendar_first_day']);
// Calendar
if (isset($calendar_first_day)) {
mysqli_query($mysqli, "UPDATE user_settings SET user_config_calendar_first_day = $calendar_first_day WHERE user_id = $session_user_id");
}
// Enable extension access, only if it isn't already setup (user doesn't have cookie)
if (isset($_POST['extension']) && $_POST['extension'] == 'Yes') {
if (!isset($_COOKIE['user_extension_key'])) {

View File

@@ -36,6 +36,7 @@ if (isset($_POST['add_quote'])) {
if (isset($_POST['add_quote_copy'])) {
$quote_id = intval($_POST['quote_id']);
$client_id = intval($_POST['client']);
$date = sanitizeInput($_POST['date']);
$expire = sanitizeInput($_POST['expire']);
@@ -51,7 +52,6 @@ if (isset($_POST['add_quote_copy'])) {
$quote_currency_code = sanitizeInput($row['quote_currency_code']);
$quote_scope = sanitizeInput($row['quote_scope']);
$quote_note = sanitizeInput($row['quote_note']);
$client_id = intval($row['quote_client_id']);
$category_id = intval($row['quote_category_id']);
//Generate a unique URL key for clients to access
@@ -80,7 +80,7 @@ if (isset($_POST['add_quote_copy'])) {
}
//Logging
mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Quote', log_action = 'Create', log_description = 'Copied Quote', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id");
mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Quote', log_action = 'Create', log_description = 'Copied Quote', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id");
$_SESSION['alert_message'] = "Quote copied";

View File

@@ -5,6 +5,7 @@ $subject = sanitizeInput($_POST['subject']);
$priority = sanitizeInput($_POST['priority']);
$details = mysqli_real_escape_string($mysqli, $_POST['details']);
$frequency = sanitizeInput($_POST['frequency']);
$billable = intval($_POST['billable']);
$asset_id = "0";
if (isset($_POST['asset'])) {

View File

@@ -286,11 +286,12 @@ if (isset($_POST['edit_ticket_settings'])) {
$config_ticket_prefix = sanitizeInput($_POST['config_ticket_prefix']);
$config_ticket_next_number = intval($_POST['config_ticket_next_number']);
$config_ticket_email_parse = intval($_POST['config_ticket_email_parse']);
$config_ticket_default_billable = intval($_POST['config_ticket_default_billable']);
$config_ticket_autoclose = intval($_POST['config_ticket_autoclose']);
$config_ticket_autoclose_hours = intval($_POST['config_ticket_autoclose_hours']);
$config_ticket_new_ticket_notification_email = sanitizeInput($_POST['config_ticket_new_ticket_notification_email']);
mysqli_query($mysqli,"UPDATE settings SET config_ticket_prefix = '$config_ticket_prefix', config_ticket_next_number = $config_ticket_next_number, config_ticket_email_parse = $config_ticket_email_parse, config_ticket_autoclose = $config_ticket_autoclose, config_ticket_autoclose_hours = $config_ticket_autoclose_hours, config_ticket_new_ticket_notification_email = '$config_ticket_new_ticket_notification_email' WHERE company_id = 1");
mysqli_query($mysqli,"UPDATE settings SET config_ticket_prefix = '$config_ticket_prefix', config_ticket_next_number = $config_ticket_next_number, config_ticket_email_parse = $config_ticket_email_parse, config_ticket_autoclose = $config_ticket_autoclose, config_ticket_autoclose_hours = $config_ticket_autoclose_hours, config_ticket_new_ticket_notification_email = '$config_ticket_new_ticket_notification_email', config_ticket_default_billable = $config_ticket_default_billable WHERE company_id = 1");
//Logging
mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Settings', log_action = 'Modify', log_description = '$session_name modified ticket settings', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id");
@@ -316,8 +317,9 @@ if (isset($_POST['edit_default_settings'])) {
$calendar = intval($_POST['calendar']);
$net_terms = intval($_POST['net_terms']);
$hourly_rate = floatval($_POST['hourly_rate']);
$phone_mask = intval($_POST['phone_mask']);
mysqli_query($mysqli,"UPDATE settings SET config_start_page = '$start_page', config_default_expense_account = $expense_account, config_default_payment_account = $payment_account, config_default_payment_method = '$payment_method', config_default_expense_payment_method = '$expense_payment_method', config_default_transfer_from_account = $transfer_from_account, config_default_transfer_to_account = $transfer_to_account, config_default_calendar = $calendar, config_default_net_terms = $net_terms, config_default_hourly_rate = $hourly_rate WHERE company_id = 1");
mysqli_query($mysqli,"UPDATE settings SET config_start_page = '$start_page', config_default_expense_account = $expense_account, config_default_payment_account = $payment_account, config_default_payment_method = '$payment_method', config_default_expense_payment_method = '$expense_payment_method', config_default_transfer_from_account = $transfer_from_account, config_default_transfer_to_account = $transfer_to_account, config_default_calendar = $calendar, config_default_net_terms = $net_terms, config_default_hourly_rate = $hourly_rate, config_phone_mask = $phone_mask WHERE company_id = 1");
//Logging
mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Settings', log_action = 'Modify', log_description = '$session_name modified default settings', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id");

View File

@@ -188,6 +188,7 @@ if (isset($_POST['edit_ticket'])) {
$ticket_id = intval($_POST['ticket_id']);
$contact_id = intval($_POST['contact']);
$notify = intval($_POST['contact_notify']);
$category = intval($_POST['category']);
$subject = sanitizeInput($_POST['subject']);
$billable = intval($_POST['billable']);
$priority = sanitizeInput($_POST['priority']);
@@ -200,7 +201,7 @@ if (isset($_POST['edit_ticket'])) {
$client_id = intval($_POST['client_id']);
$ticket_number = sanitizeInput($_POST['ticket_number']);
mysqli_query($mysqli, "UPDATE tickets SET ticket_subject = '$subject', ticket_priority = '$priority', ticket_billable = $billable, ticket_details = '$details', ticket_vendor_ticket_number = '$vendor_ticket_number', ticket_contact_id = $contact_id, ticket_vendor_id = $vendor_id, ticket_location_id = $location_id, ticket_asset_id = $asset_id, ticket_project_id = $project_id WHERE ticket_id = $ticket_id");
mysqli_query($mysqli, "UPDATE tickets SET ticket_category = $category, ticket_subject = '$subject', ticket_priority = '$priority', ticket_billable = $billable, ticket_details = '$details', ticket_vendor_ticket_number = '$vendor_ticket_number', ticket_contact_id = $contact_id, ticket_vendor_id = $vendor_id, ticket_location_id = $location_id, ticket_asset_id = $asset_id, ticket_project_id = $project_id WHERE ticket_id = $ticket_id");
// Notify new contact if selected
if ($notify && !empty($config_smtp_host)) {
@@ -1104,9 +1105,14 @@ if (isset($_POST['add_ticket_reply'])) {
$client_id = intval($_POST['client_id']);
if (isset($_POST['public_reply_type'])) {
$send_email = 0;
if ($_POST['public_reply_type'] == 1 ){
$ticket_reply_type = 'Public';
} else {
} elseif ($_POST['public_reply_type'] == 2 ) {
$ticket_reply_type = 'Public';
$send_email = 1;
} else {
$ticket_reply_type = 'Internal';
}
@@ -1156,7 +1162,7 @@ if (isset($_POST['add_ticket_reply'])) {
$company_phone = sanitizeInput(formatPhoneNumber($row['company_phone']));
// Send e-mail to client if public update & email is set up
if ($ticket_reply_type == 'Public' && !empty($config_smtp_host)) {
if ($ticket_reply_type == 'Public' && $send_email == 1 && !empty($config_smtp_host)) {
if (filter_var($contact_email, FILTER_VALIDATE_EMAIL)) {
@@ -1596,7 +1602,7 @@ if (isset($_POST['add_recurring_ticket'])) {
}
// Add scheduled ticket
mysqli_query($mysqli, "INSERT INTO scheduled_tickets SET scheduled_ticket_subject = '$subject', scheduled_ticket_details = '$details', scheduled_ticket_priority = '$priority', scheduled_ticket_frequency = '$frequency', scheduled_ticket_start_date = '$start_date', scheduled_ticket_next_run = '$start_date', scheduled_ticket_assigned_to = $assigned_to, scheduled_ticket_created_by = $session_user_id, scheduled_ticket_client_id = $client_id, scheduled_ticket_contact_id = $contact_id, scheduled_ticket_asset_id = $asset_id");
mysqli_query($mysqli, "INSERT INTO scheduled_tickets SET scheduled_ticket_subject = '$subject', scheduled_ticket_details = '$details', scheduled_ticket_priority = '$priority', scheduled_ticket_frequency = '$frequency', scheduled_ticket_billable = $billable, scheduled_ticket_start_date = '$start_date', scheduled_ticket_next_run = '$start_date', scheduled_ticket_assigned_to = $assigned_to, scheduled_ticket_created_by = $session_user_id, scheduled_ticket_client_id = $client_id, scheduled_ticket_contact_id = $contact_id, scheduled_ticket_asset_id = $asset_id");
$scheduled_ticket_id = mysqli_insert_id($mysqli);
@@ -1625,7 +1631,7 @@ if (isset($_POST['edit_recurring_ticket'])) {
}
// Edit scheduled ticket
mysqli_query($mysqli, "UPDATE scheduled_tickets SET scheduled_ticket_subject = '$subject', scheduled_ticket_details = '$details', scheduled_ticket_priority = '$priority', scheduled_ticket_frequency = '$frequency', scheduled_ticket_next_run = '$next_run_date', scheduled_ticket_assigned_to = $assigned_to, scheduled_ticket_asset_id = $asset_id, scheduled_ticket_contact_id = $contact_id WHERE scheduled_ticket_id = $scheduled_ticket_id");
mysqli_query($mysqli, "UPDATE scheduled_tickets SET scheduled_ticket_subject = '$subject', scheduled_ticket_details = '$details', scheduled_ticket_priority = '$priority', scheduled_ticket_frequency = '$frequency', scheduled_ticket_billable = $billable, scheduled_ticket_next_run = '$next_run_date', scheduled_ticket_assigned_to = $assigned_to, scheduled_ticket_asset_id = $asset_id, scheduled_ticket_contact_id = $contact_id WHERE scheduled_ticket_id = $scheduled_ticket_id");
// Logging
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Recurring Ticket', log_action = 'Modify', log_description = '$session_name modified recurring ticket for $subject - $frequency', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $scheduled_ticket_id");

View File

@@ -18,6 +18,14 @@ if (isset($_POST['add_user'])) {
$user_id = mysqli_insert_id($mysqli);
// Add Client Access Permissions if set
if (!empty($_POST['clients'])) {
foreach($_POST['clients'] as $client_id) {
$client_id = intval($client_id);
mysqli_query($mysqli,"INSERT INTO user_permissions SET user_id = $user_id, client_id = $client_id");
}
}
if (!file_exists("uploads/users/$user_id/")) {
mkdir("uploads/users/$user_id");
}
@@ -105,6 +113,15 @@ if (isset($_POST['edit_user'])) {
$user_id = intval($_POST['user_id']);
$new_password = trim($_POST['new_password']);
// Update Client Access
mysqli_query($mysqli,"DELETE FROM user_permissions WHERE user_id = $user_id");
if (!empty($_POST['clients'])) {
foreach($_POST['clients'] as $client_id) {
$client_id = intval($client_id);
mysqli_query($mysqli,"INSERT INTO user_permissions SET user_id = $user_id, client_id = $client_id");
}
}
// Get current Avatar
$sql = mysqli_query($mysqli, "SELECT user_avatar FROM users WHERE user_id = $user_id");
$row = mysqli_fetch_array($sql);

View File

@@ -11,6 +11,28 @@
<input type="hidden" name="quote_id" value="<?php echo $quote_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<label>Client <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-users"></i></span>
</div>
<select class="form-control select2" name="client" required>
<?php
$sql_client_select = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
while ($row = mysqli_fetch_array($sql_client_select)) {
$client_id_select = intval($row['client_id']);
$client_name_select = nullable_htmlentities($row['client_name']);
?>
<option <?php if ($client_id == $client_id_select) { echo "selected"; } ?> value="<?php echo $client_id_select; ?>"><?php echo $client_name_select; ?></option>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label>Set Date for New Quote <strong class="text-danger">*</strong></label>
<div class="input-group">

View File

@@ -11,6 +11,7 @@
<?php if (isset($client_id)) { ?>
<input type="hidden" name="client" value="<?php echo $client_id; ?>>">
<?php } ?>
<input type="hidden" name="billable" value="0">
<div class="modal-body bg-white">
@@ -86,6 +87,15 @@
</div>
</div>
<?php if ($config_module_enable_accounting) { ?>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="billable" <?php if ($config_ticket_default_billable == 1) { echo "checked"; } ?> value="1" id="billable">
<label class="custom-control-label" for="billable">Mark Billable</label>
</div>
</div>
<?php } ?>
</div>

View File

@@ -10,6 +10,7 @@
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="scheduled_ticket_id" id="editTicketId">
<input type="hidden" name="client" id="editClientId">
<input type="hidden" name="billable" value="0">
<div class="modal-body bg-white">
@@ -73,6 +74,15 @@
</div>
</div>
<?php if ($config_module_enable_accounting) { ?>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="billable" id="editTicketBillable" value="1">
<label class="custom-control-label" for="editTicketBillable">Mark Billable</label>
</div>
</div>
<?php } ?>
</div>
<div class="tab-pane fade" id="pills-edit-contacts">

View File

@@ -50,7 +50,7 @@ if (isset($_GET['year'])) {
$sql_ticket_years = mysqli_query($mysqli, "SELECT DISTINCT YEAR(ticket_created_at) AS ticket_year FROM tickets ORDER BY ticket_year DESC");
$sql_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients ORDER BY client_name ASC");
$sql_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
?>
@@ -79,6 +79,7 @@ $sql_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients
<th>Client</th>
<th class="text-right">Tickets raised</th>
<th class="text-right">Tickets closed</th>
<th class="text-right">Time worked <i>(H:M:S)</i></th>
<th class="text-right">Avg time to close</th>
</tr>
</thead>
@@ -101,6 +102,11 @@ $sql_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients
// Used to calculate average time to close tickets that were raised in period specified
$sql_tickets = mysqli_query($mysqli, "SELECT ticket_created_at, ticket_closed_at FROM tickets WHERE YEAR(ticket_created_at) = $year AND ticket_client_id = $client_id AND ticket_closed_at IS NOT NULL");
// Calculate total time tracked towards tickets in the period
$sql_time = mysqli_query($mysqli, "SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(ticket_reply_time_worked))) as total_time FROM ticket_replies LEFT JOIN tickets ON tickets.ticket_id = ticket_replies.ticket_reply_ticket_id WHERE YEAR(ticket_created_at) = $year AND ticket_client_id = $client_id AND ticket_reply_time_worked IS NOT NULL");
$row = mysqli_fetch_array($sql_time);
$ticket_total_time_worked = nullable_htmlentities($row['total_time']);
if ($ticket_raised_count > 0) {
// Calculate average time to solve
@@ -120,6 +126,7 @@ $sql_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients
<td><?php echo $client_name; ?></td>
<td class="text-right"><?php echo $ticket_raised_count; ?></td>
<td class="text-right"><?php echo $ticket_closed_count; ?></td>
<td class="text-right"><?php echo $ticket_total_time_worked; ?></td>
<td class="text-right"><?php echo secondsToTime($total); ?></td>
</tr>
<?php

View File

@@ -58,14 +58,6 @@ class TokenAuth6238 {
return $result;
}
public static function getBarCodeUrl($username, $domain, $secretkey, $issuer) {
$url = "https://chart.apis.google.com/chart";
$url = $url."?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/";
$url = $url.$username . "@" . $domain . "%3Fsecret%3D" . $secretkey . '%26issuer%3D' . rawurlencode($issuer);
return $url;
}
private static function oath_hotp ($key, $counter, $debug=false) {
$result = "";
$orgcounter = $counter;

View File

@@ -218,6 +218,28 @@ require_once "inc_all_admin.php";
</div>
</div>
<div class="form-group">
<label>Phone Mask</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-phone"></i></span>
</div>
<select class="form-control select2" name="phone_mask">
<?php
$sql = mysqli_query($mysqli, "SELECT config_phone_mask FROM settings WHERE company_id = 1");
while ($row = mysqli_fetch_array($sql)) {
$phone_mask = intval($row['config_phone_mask']);
} ?>
<option <?php if ($phone_mask == 1) {
echo "selected";
}?> value=1>Enable</option>
<option <?php if ($phone_mask == 0) {
echo "selected";
}?> value=0>Disabled</option>
</select>
</div>
</div>
<hr>
<button type="submit" name="edit_default_settings" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
@@ -228,4 +250,3 @@ require_once "inc_all_admin.php";
<?php
require_once "footer.php";

View File

@@ -11,6 +11,7 @@ require_once "inc_all_admin.php";
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="config_ticket_email_parse" value="0">
<input type="hidden" name="config_ticket_autoclose" value="0">
<input type="hidden" name="config_ticket_default_billable" value="0">
<div class="form-group">
<label>Ticket Prefix</label>
@@ -39,6 +40,15 @@ require_once "inc_all_admin.php";
</div>
</div>
<?php if ($config_module_enable_accounting) { ?>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="config_ticket_default_billable" <?php if ($config_ticket_default_billable == 1) { echo "checked"; } ?> value="1" id="ticketBillableSwitch<?php echo $ticket_id; ?>">
<label class="custom-control-label" for="ticketBillableSwitch<?php echo $ticket_id; ?>">Default to Billable <small class="text-secondary">(This will check the billable box on all new tickets)</small></label>
</div>
</div>
<?php } ?>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="config_ticket_autoclose" <?php if($config_ticket_autoclose == 1){ echo "checked"; } ?> value="1" id="ticketAutoCloseSwitch">

View File

@@ -302,6 +302,11 @@ if (isset($_POST['add_company_settings'])) {
mysqli_query($mysqli, "INSERT INTO ticket_statuses SET ticket_status_name = 'Auto Close', ticket_status_color = '#343a40'"); // 4
mysqli_query($mysqli, "INSERT INTO ticket_statuses SET ticket_status_name = 'Closed', ticket_status_color = '#343a40'"); // 5
// Add default roles
mysqli_query($mysqli, "INSERT INTO `user_roles` SET user_role_id = 1, user_role_name = 'Accountant', user_role_description = 'Built-in - Limited access to financial-focused modules'");
mysqli_query($mysqli, "INSERT INTO `user_roles` SET user_role_id = 2, user_role_name = 'Technician', user_role_description = 'Built-in - Limited access to technical-focused modules'");
mysqli_query($mysqli, "INSERT INTO `user_roles` SET user_role_id = 3, user_role_name = 'Administrator', user_role_description = 'Built-in - Full administrative access to all modules (including user management)'");
$_SESSION['alert_message'] = "Company <strong>$name</strong> created!";
@@ -843,8 +848,9 @@ if (isset($_POST['add_telemetry'])) {
<ul>
<li>Please take a look over the install <a href="https://docs.itflow.org/installation">docs</a>, if you haven't already</li>
<li>Don't hesitate to reach out on the <a href="https://forum.itflow.org/t/support" target="_blank">forums</a> if you need any assistance</li>
<li><i>Your PHP Error log is at: <?php echo ini_get('error_log') ?></i></li>
</ul>
<br><p>A database must be created before proceeding - click on the button below to get started</p>
<br><p>A database must be created before proceeding - click on the button below to get started.</p>
<br><hr>
<p class="text-muted">ITFlow is <b>free software</b>: you can redistribute and/or modify it under the terms of the <a href="https://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank">GNU General Public License</a>. <br> It is distributed in the hope that it will be useful, but <b>without any warranty</b>; without even the implied warranty of merchantability or fitness for a particular purpose.</p>
<?php

View File

@@ -20,7 +20,12 @@
<li class="nav-item">
<a href="clients.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "clients.php") { echo "active"; } ?>">
<i class="nav-icon fas fa-user-friends"></i>
<p>Clients</p>
<p>
Clients
<?php if ($num_active_clients) { ?>
<span class="right badge text-light"><?php echo $num_active_clients; ?></span>
<?php } ?>
</p>
</a>
</li>
<?php if ($session_user_role >= 2 && $config_module_enable_ticketing == 1) { ?>

View File

@@ -1,13 +1,6 @@
<?php
require_once "inc_all.php";
?>
<!-- Custom styling of time tracking elements -->
<link rel="stylesheet" type="text/css" href="css/ticket_time_tracking.css">
<?php
// Initialize the HTML Purifier to prevent XSS
require "plugins/htmlpurifier/HTMLPurifier.standalone.php";
@@ -30,6 +23,7 @@ if (isset($_GET['ticket_id'])) {
LEFT JOIN projects ON ticket_project_id = project_id
LEFT JOIN invoices ON ticket_invoice_id = invoice_id
LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id
LEFT JOIN categories ON ticket_category = category_id
WHERE ticket_id = $ticket_id LIMIT 1"
);
@@ -54,7 +48,8 @@ if (isset($_GET['ticket_id'])) {
$ticket_prefix = nullable_htmlentities($row['ticket_prefix']);
$ticket_number = intval($row['ticket_number']);
$ticket_category = nullable_htmlentities($row['ticket_category']);
$ticket_category = intval($row['ticket_category']);
$ticket_category_display = htmlentities($row['category_name']);
$ticket_subject = nullable_htmlentities($row['ticket_subject']);
$ticket_details = $purifier->purify($row['ticket_details']);
$ticket_priority = nullable_htmlentities($row['ticket_priority']);
@@ -181,7 +176,7 @@ if (isset($_GET['ticket_id'])) {
// Client Tags
$client_tag_name_display_array = array();
$client_tag_id_array = array();
$sql_client_tags = mysqli_query($mysqli, "SELECT * FROM client_tags LEFT JOIN tags ON client_tags.client_tag_tag_id = tags.tag_id WHERE client_tags.client_tag_client_id = $client_id ORDER BY tag_name ASC");
$sql_client_tags = mysqli_query($mysqli, "SELECT * FROM client_tags LEFT JOIN tags ON client_tags.tag_id = tags.tag_id WHERE client_id = $client_id ORDER BY tag_name ASC");
while ($row = mysqli_fetch_array($sql_client_tags)) {
$client_tag_id = intval($row['tag_id']);
@@ -206,6 +201,22 @@ if (isset($_GET['ticket_id'])) {
$row = mysqli_fetch_array($ticket_responses_sql);
$ticket_responses = intval($row['ticket_responses']);
$ticket_all_comments_sql = mysqli_query($mysqli, "SELECT COUNT(ticket_reply_id) AS ticket_all_comments_count FROM ticket_replies WHERE ticket_reply_archived_at IS NULL AND ticket_reply_ticket_id = $ticket_id");
$row = mysqli_fetch_array($ticket_all_comments_sql);
$ticket_all_comments_count = intval($row['ticket_all_comments_count']);
$ticket_internal_notes_sql = mysqli_query($mysqli, "SELECT COUNT(ticket_reply_id) AS ticket_internal_notes_count FROM ticket_replies WHERE ticket_reply_archived_at IS NULL AND ticket_reply_type = 'Internal' AND ticket_reply_ticket_id = $ticket_id");
$row = mysqli_fetch_array($ticket_internal_notes_sql);
$ticket_internal_notes_count = intval($row['ticket_internal_notes_count']);
$ticket_public_comments_sql = mysqli_query($mysqli, "SELECT COUNT(ticket_reply_id) AS ticket_public_comments_count FROM ticket_replies WHERE ticket_reply_archived_at IS NULL AND (ticket_reply_type = 'Public' OR ticket_reply_type = 'Client') AND ticket_reply_ticket_id = $ticket_id");
$row = mysqli_fetch_array($ticket_public_comments_sql);
$ticket_public_comments_count = intval($row['ticket_public_comments_count']);
$ticket_events_sql = mysqli_query($mysqli, "SELECT COUNT(ticket_reply_id) AS ticket_events_count FROM ticket_replies WHERE ticket_reply_archived_at IS NULL AND ticket_reply_type = 'Event' AND ticket_reply_ticket_id = $ticket_id");
$row = mysqli_fetch_array($ticket_events_sql);
$ticket_events_count = intval($row['ticket_events_count']);
// Get & format asset warranty expiry
$date = date('Y-m-d H:i:s');
@@ -371,13 +382,6 @@ if (isset($_GET['ticket_id'])) {
</div>
<?php }
// Time tracking
if ($ticket_total_reply_time) { ?>
<div class="mt-1">
<i class="far fa-fw fa-clock text-secondary mr-2"></i>Total time worked: <?php echo $ticket_total_reply_time; ?>
</div>
<?php }
// Billable
if ($config_module_enable_accounting) { ?>
<?php if($invoice_id) { ?>
@@ -404,18 +408,33 @@ if (isset($_GET['ticket_id'])) {
</div>
<div class="col-sm-3">
<?php if($task_count) { ?>
<?php if ($task_count) { ?>
Tasks Completed<span class="float-right text-bold"><?php echo $tasks_completed_percent; ?>%</span>
<div class="progress mt-2" style="height: 20px;">
<div class="progress-bar" style="width: <?php echo $tasks_completed_percent; ?>%;"><?php echo $completed_task_count; ?> / <?php echo $task_count; ?></div>
</div>
<?php } ?>
<?php if($ticket_collaborators) { ?>
<?php
// Time tracking
if ($ticket_total_reply_time) { ?>
<div class="mt-1">
<i class="far fa-fw fa-clock text-secondary mr-2"></i>Total time worked: <?php echo $ticket_total_reply_time; ?>
</div>
<?php } ?>
<?php if ($ticket_collaborators) { ?>
<div class="mt-2">
<i class="fas fa-fw fa-users mr-2 text-secondary"></i><?php echo $ticket_collaborators; ?>
</div>
<?php } ?>
<?php if ($ticket_category > 0) { ?>
<div class="mt-2">
<i class="fas fa-fw fa-layer-group mr-2 text-secondary"></i><?php echo $ticket_category_display; ?>
</div>
<?php } ?>
</div>
<div class="col-sm-3">
@@ -451,7 +470,7 @@ if (isset($_GET['ticket_id'])) {
<?php }
if (empty($ticket_closed_at)) { ?>
<?php if($task_count == $completed_task_count) { ?>
<?php if ($task_count == $completed_task_count) { ?>
<a href="post.php?close_ticket=<?php echo $ticket_id; ?>" class="btn btn-dark btn-sm confirm-link" id="ticket_close">
<i class="fas fa-fw fa-gavel mr-2"></i>Close
</a>
@@ -513,28 +532,39 @@ if (isset($_GET['ticket_id'])) {
<!-- Only show ticket reply modal if status is not closed -->
<?php if (empty($ticket_closed_at)) { ?>
<form class="mb-3 d-print-none" action="post.php" method="post" autocomplete="off">
<input type="hidden" name="ticket_id" id="ticket_id" value="<?php echo $ticket_id; ?>">
<input type="hidden" name="client_id" id="client_id" value="<?php echo $client_id; ?>">
<div class="form-group">
<div class="btn-group btn-group-toggle" data-toggle="buttons">
<label class="btn btn-light active">
<input type="radio" name="public_reply_type" value="2" checked>Public Comment & Email
</label>
<label class="btn btn-light">
<input type="radio" name="public_reply_type" value="1">Public Comment
</label>
<label class="btn btn-light">
<input type="radio" name="public_reply_type" value="0">Internal Note
</label>
</div>
<?php if ($config_ai_enable) { ?>
<div class="form-group">
<textarea class="form-control tinymceai" id="textInput" name="ticket_reply" placeholder="Type a response"></textarea>
</div>
<div class="mb-3">
<button id="rewordButton" class="btn btn-secondary" type="button"><i class="fas fa-fw fa-robot mr-2"></i>AI Reword</button>
<button id="undoButton" class="btn btn-secondary" type="button" style="display:none;"><i class="fas fa-fw fa-redo-alt mr-2"></i>Undo</button>
</div>
<?php } else { ?>
<div class="form-group">
<textarea class="form-control tinymce" name="ticket_reply" placeholder="Type a response"></textarea>
</div>
<div class="float-right">
<button id="rewordButton" class="btn btn-secondary" type="button"><i class="fas fa-fw fa-robot mr-2"></i>AI Reword</button>
<button id="undoButton" class="btn btn-secondary" type="button" style="display:none;"><i class="fas fa-fw fa-redo-alt mr-2"></i>Undo</button>
</div>
<?php } ?>
</div>
<div class="form-group">
<textarea class="form-control tinymce<?php if ($config_ai_enable) { echo "ai"; } ?>" id="textInput" name="ticket_reply" placeholder="Type a response"></textarea>
</div>
<div class="form-row">
<div class="col-md-2">
<div class="col-md-4">
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-thermometer-half"></i></span>
@@ -554,69 +584,32 @@ if (isset($_GET['ticket_id'])) {
</div>
</div>
<div class="custom-tt-horizontal-spacing"></div> <!-- Add custom class for smaller spacing -->
<!-- Time Tracking -->
<div class="col-sm-3 col-lg-2">
<div class="col-md-6">
<div class="input-group mb-3">
<div class="form-row">
<div class="input-group custom-tt-width">
<input type="text" class="form-control" inputmode="numeric" id="hours" name="hours" placeholder="Hrs" min="0" max="23" pattern="0?[0-9]|1[0-9]|2[0-3]">
</div>
<div class="input-group custom-tt-width">
<input type="text" class="form-control" inputmode="numeric" id="minutes" name="minutes" placeholder="Mins" min="0" max="59" pattern="[0-5]?[0-9]">
</div>
<div class="input-group custom-tt-width">
<input type="text" class="form-control" inputmode="numeric" id="seconds" name="seconds" placeholder="Secs" min="0" max="59" pattern="[0-5]?[0-9]">
</div>
<div class="input-group pr-0 col-2">
<input type="text" class="form-control" inputmode="numeric" id="hours" name="hours" placeholder="Hrs" min="0" max="23" pattern="0?[0-9]|1[0-9]|2[0-3]">
</div>
<div class="input-group px-0 col-2">
<input type="text" class="form-control" inputmode="numeric" id="minutes" name="minutes" placeholder="Mins" min="0" max="59" pattern="[0-5]?[0-9]">
</div>
<div class="input-group px-0 col-2">
<input type="text" class="form-control" inputmode="numeric" id="seconds" name="seconds" placeholder="Secs" min="0" max="59" pattern="[0-5]?[0-9]">
</div>
<div class="btn-group">
<button type="button" class="btn btn-success" id="startStopTimer"><i class="fas fa-fw fa-pause"></i></button>
<button type="button" class="btn btn-danger" id="resetTimer"><i class="fas fa-fw fa-redo-alt"></i></button>
</div>
</div>
</div>
<!-- Timer Controls -->
<div class="col-sm-2">
<div class="btn-group">
<button type="button" class="btn btn-success" id="startStopTimer"><i class="fas fa-fw fa-pause"></i></button>
<button type="button" class="btn btn-danger" id="resetTimer"><i class="fas fa-fw fa-redo-alt"></i></button>
</div>
</div>
<?php
// Set the initial ticket response type (private/internal note)
// Future updates of the wording/icon are done by Javascript
// Public responses by default (maybe configurable in future?)
$ticket_reply_button_wording = "Respond";
$ticket_reply_button_check = "checked";
$ticket_reply_button_icon = "paper-plane";
// Internal responses by default if 1) the contact email is empty or 2) the contact email matches the agent responding
if (empty($contact_email) || $contact_email == $session_email) {
// Internal
$ticket_reply_button_wording = "Add note";
$ticket_reply_button_check = "";
$ticket_reply_button_icon = "sticky-note";
} ?>
<div class="col-md-2">
<div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="ticket_reply_type_checkbox" name="public_reply_type" value="1" <?php echo $ticket_reply_button_check ?>>
<label class="custom-control-label" for="ticket_reply_type_checkbox">Public Update<br><small class="text-secondary">(Emails contact)</small></label>
</div>
</div>
</div>
<div class="col-md-2">
<button type="submit" id="ticket_add_reply" name="add_ticket_reply" class="btn btn-primary text-bold"><i class="fas fa-<?php echo $ticket_reply_button_icon ?> mr-2"></i><?php echo $ticket_reply_button_wording ?></button>
<div class="float-right">
<button type="submit" id="ticket_add_reply" name="add_ticket_reply" class="btn btn-primary btn-block text-bold"><i class="fas fa-check mr-2"></i>Submit</button>
</div>
</div>
</div>
@@ -625,7 +618,52 @@ if (isset($_GET['ticket_id'])) {
<!-- End IF for reply modal -->
<?php } ?>
<?php if($ticket_responses) { ?><h5 class="mb-4">Responses (<?php echo $ticket_responses; ?>)</h5><?php } ?>
<!-- Ticket Responses -->
<ul class="nav nav-tabs" id="ticketComments">
<li class="nav-item">
<button class="nav-link active" id="all-comments-tab" data-toggle="tab" data-target="#allComments" type="button">
All Comments
<span class="right badge badge-pill badge-dark ml-2"><?php echo $ticket_all_comments_count; ?></span>
</button>
</li>
<li class="nav-item">
<button class="nav-link" id="public-comments-tab" data-toggle="tab" data-target="#publicComments" type="button">
Public
<span class="right badge badge-pill badge-dark ml-2"><?php echo $ticket_public_comments_count; ?></span>
</button>
</li>
<li class="nav-item">
<button class="nav-link" id="notes-tab" data-toggle="tab" data-target="#notes" type="button">
Internal Notes
<span class="right badge badge-pill badge-dark ml-2"><?php echo $ticket_internal_notes_count; ?></span>
</button>
</li>
<li class="nav-item">
<button class="nav-link" id="public-comments-tab" data-toggle="tab" data-target="#publicComments" type="button">
Client Communication
<span class="right badge badge-pill badge-dark ml-2"><?php echo $ticket_public_comments_count; ?></span>
</button>
</li>
<li class="nav-item ml-auto">
<button class="nav-link" id="events-tab" data-toggle="tab" data-target="#events" type="button">
Events
<span class="right badge badge-pill badge-dark ml-2"><?php echo $ticket_responses; ?></span>
</button>
</li>
<li class="nav-item">
<button class="nav-link" id="tasks-tab" data-toggle="tab" data-target="#tasks" type="button">
Tasks
<span class="right badge badge-pill badge-dark ml-2"><?php echo $task_count; ?></span>
</button>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="allComments">All Comments</div>
<div class="tab-pane fade" id="publicComments">Public Comments</div>
<div class="tab-pane fade" id="notes">Internal Notes</div>
<div class="tab-pane fade" id="events">Events</div>
<div class="tab-pane fade" id="tasks">Tasks</div>
</div>
<!-- Ticket replies -->
<?php
@@ -837,7 +875,7 @@ if (isset($_GET['ticket_id'])) {
?>
<tr>
<td>
<?php if($task_completed_at) { ?>
<?php if ($task_completed_at) { ?>
<i class="far fa-fw fa-check-square text-primary"></i>
<?php } else { ?>
<a href="post.php?complete_task=<?php echo $task_id; ?>">
@@ -1099,7 +1137,6 @@ require_once "footer.php";
<!-- Ticket collision detect JS (jQuery is called in footer, so collision detection script MUST be below it) -->
<script src="js/ticket_collision_detection.js"></script>
<script src="js/ticket_button_respond_note.js"></script>
<?php } ?>
<script src="js/pretty_content.js"></script>

View File

@@ -8,6 +8,7 @@
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="billable" value="0">
<div class="modal-body bg-white">
<?php if (isset($_GET['client_id'])) { ?>
@@ -139,6 +140,15 @@
</div>
</div>
<?php if ($config_module_enable_accounting) { ?>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="billable" <?php if ($config_ticket_default_billable == 1) { echo "checked"; } ?> value="1" id="billableSwitch">
<label class="custom-control-label" for="billableSwitch">Mark Billable</label>
</div>
</div>
<?php } ?>
</div>
<?php if (isset($_GET['client_id'])) { ?>

View File

@@ -12,6 +12,7 @@
<input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
<input type="hidden" name="ticket_number" value="<?php echo "$ticket_prefix$ticket_number"; ?>">
<input type="hidden" name="contact_notify" value="0"> <!-- Default 0 -->
<input type="hidden" name="billable" value="0">
<div class="modal-body bg-white">
<ul class="nav nav-pills nav-justified mb-3">
@@ -55,37 +56,57 @@
<textarea class="form-control tinymce" rows="8" name="details"><?php echo $ticket_details; ?></textarea>
</div>
<div class="form-group">
<label>Priority <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-thermometer-half"></i></span>
<div class="row">
<div class="col">
<div class="form-group">
<label>Priority <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-thermometer-half"></i></span>
</div>
<select class="form-control select2" name="priority" required>
<option <?php if ($ticket_priority == 'Low') { echo "selected"; } ?> >Low</option>
<option <?php if ($ticket_priority == 'Medium') { echo "selected"; } ?> >Medium</option>
<option <?php if ($ticket_priority == 'High') { echo "selected"; } ?> >High</option>
</select>
</div>
</div>
</div>
<div class="col">
<div class="form-group">
<label>Category</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-layer-group"></i></span>
</div>
<select class="form-control select2" name="category">
<option value="">- Ticket Category -</option>
<?php
$sql_categories = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_type = 'Ticket' AND categories.category_archived_at IS NULL");
while ($row = mysqli_fetch_array($sql_categories)) {
$category_id = intval($row['category_id']);
$category_name = nullable_htmlentities($row['category_name']);
?>
<option <?php if ($ticket_category == $category_id) {echo "selected";} ?> value="<?php echo $category_id; ?>"><?php echo $category_name; ?></option>
<?php } ?>
</select>
</div>
</div>
<select class="form-control select2" name="priority" required>
<option <?php if ($ticket_priority == 'Low') { echo "selected"; } ?> >Low</option>
<option <?php if ($ticket_priority == 'Medium') { echo "selected"; } ?> >Medium</option>
<option <?php if ($ticket_priority == 'High') { echo "selected"; } ?> >High</option>
</select>
</div>
</div>
<?php if ($config_module_enable_accounting) {
?>
<?php if ($config_module_enable_accounting) { ?>
<div class="form-group">
<label>Billable</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-money-bill"></i></span>
</div>
<select class="form-control" name="billable">
<option <?php if ($ticket_billable == 1) { echo "selected"; } ?> value="1">Yes</option>
<option <?php if ($ticket_billable == 0) { echo "selected"; } ?> value="0">No</option>
</select>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="billable" <?php if ($ticket_billable == 1) { echo "checked"; } ?> value="1" id="billableSwitch<?php echo $ticket_id; ?>">
<label class="custom-control-label" for="billableSwitch<?php echo $ticket_id; ?>">Mark Billable</label>
</div>
</div>
<?php } ?>
</div>
<div class="tab-pane fade" id="pills-contacts<?php echo $ticket_id; ?>">

View File

@@ -284,7 +284,7 @@ $user_active_assigned_tickets = intval($row['total_tickets_assigned']);
</td>
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ticket_number&order=<?php echo $disp; ?>">Number</a>
</th>
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ticket_subject&order=<?php echo $disp; ?>">Subject</a>
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ticket_subject&order=<?php echo $disp; ?>">Subject / Tasks</a>
</th>
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=client_name&order=<?php echo $disp; ?>">Client / Contact</a>
</th>
@@ -395,6 +395,23 @@ $user_active_assigned_tickets = intval($row['total_tickets_assigned']);
$ticket_reply_created_at_time_ago = timeAgo($ticket_reply_created_at);
}
// Get Tasks
$sql_tasks = mysqli_query( $mysqli, "SELECT * FROM tasks WHERE task_ticket_id = $ticket_id ORDER BY task_created_at ASC");
$task_count = mysqli_num_rows($sql_tasks);
// Get Completed Task Count
$sql_tasks_completed = mysqli_query($mysqli,
"SELECT * FROM tasks
WHERE task_ticket_id = $ticket_id
AND task_completed_at IS NOT NULL"
);
$completed_task_count = mysqli_num_rows($sql_tasks_completed);
// Tasks Completed Percent
if($task_count) {
$tasks_completed_percent = round(($completed_task_count / $task_count) * 100);
}
?>
<tr class="<?php if(empty($ticket_closed_at) && empty($ticket_updated_at)) { echo "text-bold"; }?> <?php if (empty($ticket_closed_at) && $ticket_reply_type == "Client") { echo "table-warning"; } ?>">
@@ -418,6 +435,12 @@ $user_active_assigned_tickets = intval($row['total_tickets_assigned']);
<!-- Ticket Subject -->
<td>
<a href="ticket.php?ticket_id=<?php echo $ticket_id; ?>"><?php echo $ticket_subject; ?></a>
<?php if($task_count) { ?>
<div class="progress mt-2" style="height: 20px;">
<div class="progress-bar" style="width: <?php echo $tasks_completed_percent; ?>%;"><?php echo $completed_task_count; ?> / <?php echo $task_count; ?></div>
</div>
<?php } ?>
</td>
<!-- Ticket Contact -->

View File

@@ -29,18 +29,6 @@
<!-- Right navbar links -->
<ul class="navbar-nav ml-auto">
<?php if ($config_module_enable_ticketing == 1) { ?>
<li class="nav-item">
<a class="nav-link" href="#" data-toggle="modal" data-target="#openTicketsModal">
<i class="fas fa-hourglass-half"></i>
<span class="badge" id="runningTicketsCount">0</span>
</a>
</li>
<?php } ?>
<!-- New Notifications Dropdown -->
<?php
$sql_notifications = mysqli_query($mysqli, "SELECT * FROM notifications
@@ -84,7 +72,7 @@
<small class="text-secondary"><?php echo $notification; ?></small>
</a>
</div>
<?php } ?>
<div class="dropdown-divider"></div>
@@ -114,7 +102,7 @@
<?php } ?>
<!-- End New Notifications Dropdown -->
<li class="nav-item dropdown user-menu">
<a href="#" class="nav-link" data-toggle="dropdown">
<?php if (empty($session_avatar)) { ?>
@@ -151,7 +139,7 @@
</ul>
</nav>
<?php if ($config_module_enable_ticketing == 1) {
<?php if ($config_module_enable_ticketing == 1) {
include_once "top_nav_tickets_modal.php";
} ?>
<!-- /.navbar -->
<!-- /.navbar -->

View File

@@ -1,30 +1,46 @@
<?php
require_once "inc_all_user.php";
$row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT user_config_calendar_first_day FROM user_settings WHERE user_id = $session_user_id"));
$user_config_calendar_first_day = intval($row['user_config_calendar_first_day']);
?>
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-globe mr-2"></i>Browser Extension</h3>
<h3 class="card-title"><i class="fas fa-fw fa-globe mr-2"></i>Preferences</h3>
</div>
<div class="card-body">
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="form-group">
<label>Calendar starts on<strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar-day"></i></span>
</div>
<select class="form-control select2" name="calendar_first_day" required>
<option <?php if ($user_config_calendar_first_day == '0') { echo "selected"; } ?> value="0" >Sunday</option>
<option <?php if ($user_config_calendar_first_day == '1') { echo "selected"; } ?> value="1" >Monday</option>
</select>
</div>
</div>
<?php if ($session_user_role > 1) { ?>
<div class="form-group">
<div class="form-check">
<input type="checkbox" class="form-check-input" name="extension" id="extension" value="Yes" <?php if (isset($_COOKIE['user_extension_key'])) {echo "checked";} ?>>
<input disabled="disabled" type="checkbox" class="form-check-input" name="extension" id="extension" value="Yes" <?php if (isset($_COOKIE['user_extension_key'])) {echo "checked";} ?>>
<label class="form-check-label" for="extension">Enable Browser Extention?</label>
<p class="small">Note: You must log out and back in again for these changes take effect.</p>
<p class="small">Not currently in use / Note: You must log out and back in again for these changes take effect.</p>
</div>
</div>
<?php } ?>
<button type="submit" name="edit_your_user_browser_extension" class="btn btn-primary btn-block mt-3"><i class="fas fa-check mr-2"></i>Save</button>
<button type="submit" name="edit_your_user_preferences" class="btn btn-primary btn-block mt-3"><i class="fas fa-check mr-2"></i>Save</button>
</form>

View File

@@ -59,10 +59,12 @@ $remember_token_count = mysqli_num_rows($sql_remember_tokens);
if (!empty($session_token)) {
//Generate QR Code based off the generated key
print sprintf('<img src="%s"/>', TokenAuth6238::getBarCodeUrl($session_name, ' ', $session_token, $_SERVER['SERVER_NAME']));
// Generate QR Code
$data = "otpauth://totp/ITFlow:$session_email?secret=$session_token";
print "<img src='plugins/barcode/barcode.php?f=png&s=qr&d=$data'>";
echo "<p class='text-secondary'>$session_token</p>";
}
?>