From b8c529c2eca3f67da6f771127b5e42c8626d7249 Mon Sep 17 00:00:00 2001 From: Hugo Sampaio Date: Sat, 27 Apr 2024 09:30:41 -0300 Subject: [PATCH 01/67] Enable URL Recovery from logout --- check_login.php | 5 ++++- login.php | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/check_login.php b/check_login.php index a0fd1573..acc89d32 100644 --- a/check_login.php +++ b/check_login.php @@ -18,7 +18,10 @@ 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?url=".urlencode($_SERVER["REQUEST_SCHEME"] . "://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]) ); exit; } diff --git a/login.php b/login.php index 60538546..f3f1532a 100644 --- a/login.php +++ b/login.php @@ -218,8 +218,10 @@ if (isset($_POST['login'])) { //} } - - header("Location: $config_start_page"); + if($_GET['url']) + header("Location: ".$_GET['url']); + else + header("Location: $config_start_page"); } else { From bab66bf769184d38722db00b4df7171ce61c78dd Mon Sep 17 00:00:00 2001 From: Hugo Sampaio Date: Fri, 3 May 2024 09:34:50 -0300 Subject: [PATCH 02/67] updated fixed domain url from config to prevent open redirect issue and encoded uri --- check_login.php | 3 +-- login.php | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/check_login.php b/check_login.php index acc89d32..eeb6d4d4 100644 --- a/check_login.php +++ b/check_login.php @@ -21,7 +21,7 @@ if (!isset($_SESSION['logged']) || !$_SESSION['logged']) { if($_SERVER["REQUEST_URI"] == "/") header("Location: login.php"); else - header("Location: login.php?url=".urlencode($_SERVER["REQUEST_SCHEME"] . "://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]) ); + header("Location: login.php?last_visited=" . base64_encode($_SERVER["REQUEST_URI"]) ); exit; } @@ -87,4 +87,3 @@ $num_notifications = $row['num']; //if ($session_user_config_force_mfa == 1 && $session_token == NULL) { // header("Location: force_mfa.php"); //} - diff --git a/login.php b/login.php index f3f1532a..93c564d2 100644 --- a/login.php +++ b/login.php @@ -218,8 +218,8 @@ if (isset($_POST['login'])) { //} } - if($_GET['url']) - header("Location: ".$_GET['url']); + if($_GET['last_visited']) + header("Location: ".$_SERVER["REQUEST_SCHEME"] . "://" . $config_base_url . base64_decode($_GET['last_visited']) ); else header("Location: $config_start_page"); From 43abd177ea459964d77238545090e93a07852648 Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Sat, 4 May 2024 18:04:24 +0100 Subject: [PATCH 03/67] Certificate checks - Allow custom ports --- ajax.php | 31 ++++++++----------------------- functions.php | 13 +++++++++++-- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/ajax.php b/ajax.php index 9476b40e..47011ac1 100644 --- a/ajax.php +++ b/ajax.php @@ -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"; } diff --git a/functions.php b/functions.php index c7bd7204..1fb36091 100644 --- a/functions.php +++ b/functions.php @@ -427,9 +427,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 +451,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); From cd668241a6c98c1f101a654c873ae0e3acfbfa2f Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Sat, 4 May 2024 21:02:33 +0100 Subject: [PATCH 04/67] Debugging help - Mention the PHP error log on the setup & debug pages. - Restructure the debug page to include the server info before the db structure table --- admin_debug.php | 96 ++++++++++++++++++++++++------------------------- setup.php | 3 +- 2 files changed, 49 insertions(+), 50 deletions(-) diff --git a/admin_debug.php b/admin_debug.php index 6caa6614..5c92c6b3 100644 --- a/admin_debug.php +++ b/admin_debug.php @@ -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'];

Debug

- -

Database Structure Check

- + +

Server Info

+ + "; + echo "MySQL Version: " . $mysqlVersion . "
"; + echo "Operating System: " . $operatingSystem . "
"; + echo "Web Server: " . $webServer . "
"; + echo "PHP Error Log: " . $errorLog + ?> +
+

File System

+ "; + echo "Total size of files in $folderPath and its subdirectories: " . $totalSizeMB . " MB"; + ?> + +
+ +

Database Structure Check

+

Database stats

- + "; echo "Total number of fields: " . $numFields . "
"; echo "Total number of rows: " . $numRows . "
"; echo "Current Database Version: " . CURRENT_DATABASE_VERSION . "
"; ?> - +

Table Stats

@@ -255,34 +279,8 @@ $webServer = $_SERVER['SERVER_SOFTWARE'];
-

Versions

- - "; - echo "MySQL Version: " . $mysqlVersion . "
"; - echo "Operating System: " . $operatingSystem . "
"; - echo "Web Server: " . $webServer; - - - ?> - -
- -

File System

- "; - echo "Total size of files in $folderPath and its subdirectories: " . $totalSizeMB . " MB"; - ?> - -
-

PHP Modules Installed

- + "; @@ -311,7 +309,7 @@ $webServer = $_SERVER['SERVER_SOFTWARE']; //Output the result echo $phpinfo; ?> - +
diff --git a/setup.php b/setup.php index 6dab7b80..3dbbdc8f 100644 --- a/setup.php +++ b/setup.php @@ -843,8 +843,9 @@ if (isset($_POST['add_telemetry'])) {
  • Please take a look over the install docs, if you haven't already
  • Don't hesitate to reach out on the forums if you need any assistance
  • +
  • Your PHP Error log is at:
-

A database must be created before proceeding - click on the button below to get started

+

A database must be created before proceeding - click on the button below to get started.



ITFlow is free software: you can redistribute and/or modify it under the terms of the GNU General Public License.
It is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose.

Date: Sat, 4 May 2024 21:32:03 +0100 Subject: [PATCH 05/67] Add total time worked to 'Tickets by client' report --- report_ticket_by_client.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/report_ticket_by_client.php b/report_ticket_by_client.php index 96b967e4..6f7879ad 100644 --- a/report_ticket_by_client.php +++ b/report_ticket_by_client.php @@ -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 Client Tickets raised Tickets closed + Time worked (H:M:S) Avg time to close @@ -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 + Date: Sat, 4 May 2024 21:36:59 +0100 Subject: [PATCH 06/67] Hide the running open ticket tracker as this feature is being removed --- top_nav.php | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/top_nav.php b/top_nav.php index d7db3424..6713fcd2 100644 --- a/top_nav.php +++ b/top_nav.php @@ -29,18 +29,6 @@ - - \ No newline at end of file + From 17eb51bd54b3310de30557284bbacdb0de90d1d4 Mon Sep 17 00:00:00 2001 From: Hugo Sampaio Date: Sat, 4 May 2024 19:23:39 -0300 Subject: [PATCH 07/67] Update check_login.php If standard --- check_login.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/check_login.php b/check_login.php index eeb6d4d4..297208c2 100644 --- a/check_login.php +++ b/check_login.php @@ -18,10 +18,11 @@ if (!isset($config_enable_setup) || $config_enable_setup == 1) { // Check user is logged in with a valid session if (!isset($_SESSION['logged']) || !$_SESSION['logged']) { - if($_SERVER["REQUEST_URI"] == "/") + if ($_SERVER["REQUEST_URI"] == "/") { header("Location: login.php"); - else + } else { header("Location: login.php?last_visited=" . base64_encode($_SERVER["REQUEST_URI"]) ); + } exit; } From 5280620c6da18da01a203de6e9e4b90f753f3983 Mon Sep 17 00:00:00 2001 From: Hugo Sampaio Date: Sat, 4 May 2024 19:25:10 -0300 Subject: [PATCH 08/67] Update login.php If standard --- login.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/login.php b/login.php index 93c564d2..aa8fed08 100644 --- a/login.php +++ b/login.php @@ -218,11 +218,11 @@ if (isset($_POST['login'])) { //} } - if($_GET['last_visited']) + if ($_GET['last_visited']) { header("Location: ".$_SERVER["REQUEST_SCHEME"] . "://" . $config_base_url . base64_decode($_GET['last_visited']) ); - else + } else { header("Location: $config_start_page"); - + } } else { // MFA is configured and needs to be confirmed, or was unsuccessful From 6c4235bdebd8079f42113a2b1d06d94ae74bdbb3 Mon Sep 17 00:00:00 2001 From: Hugo Sampaio Date: Mon, 6 May 2024 10:27:47 -0300 Subject: [PATCH 09/67] phone mask config prepare phone mask config --- post/setting.php | 3 ++- settings_defaults.php | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/post/setting.php b/post/setting.php index b78bcebf..b312c194 100644 --- a/post/setting.php +++ b/post/setting.php @@ -316,8 +316,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"); diff --git a/settings_defaults.php b/settings_defaults.php index 6cf80398..0326d351 100644 --- a/settings_defaults.php +++ b/settings_defaults.php @@ -218,6 +218,28 @@ require_once "inc_all_admin.php"; +
+ +
+
+ +
+ +
+
+
@@ -228,4 +250,3 @@ require_once "inc_all_admin.php"; Date: Mon, 6 May 2024 10:44:52 -0300 Subject: [PATCH 10/67] update db update db to store phone mask option --- database_updates.php | 58 ++++++++++++++++++++++++-------------------- database_version.php | 2 +- db.sql | 1 + 3 files changed, 34 insertions(+), 27 deletions(-) diff --git a/database_updates.php b/database_updates.php index 43f32d54..a4685591 100644 --- a/database_updates.php +++ b/database_updates.php @@ -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,59 +1831,65 @@ 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') { + // // Insert queries here required to update to DB version 1.2.10 // // 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, "UPDATE `settings` SET `config_current_database_version` = '1.2.10"); // } } else { diff --git a/database_version.php b/database_version.php index ac7b3dd1..aa46a4ac 100644 --- a/database_version.php +++ b/database_version.php @@ -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.2.9"); diff --git a/db.sql b/db.sql index 3b56fcd7..2e016f17 100644 --- a/db.sql +++ b/db.sql @@ -1375,6 +1375,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_number` 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 */; From b1efb76b3b9d6aa788540a036913596e11292ba5 Mon Sep 17 00:00:00 2001 From: Hugo Sampaio Date: Mon, 6 May 2024 11:31:38 -0300 Subject: [PATCH 11/67] Update functions.php handle phone_mask option --- functions.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/functions.php b/functions.php index 1fb36091..7bfb1deb 100644 --- a/functions.php +++ b/functions.php @@ -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) { @@ -885,7 +895,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 @@ -902,7 +912,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 @@ -915,7 +925,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 From ec94071167a0dafc1f74bd2b781ef7431a2a37c0 Mon Sep 17 00:00:00 2001 From: Hugo Sampaio Date: Mon, 6 May 2024 13:25:30 -0300 Subject: [PATCH 12/67] num active clients on menu show number of active clients um side menu --- inc_all.php | 4 ++++ side_nav.php | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/inc_all.php b/inc_all.php index c1a7e7ea..8a2b8fbf 100644 --- a/inc_all.php +++ b/inc_all.php @@ -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']; diff --git a/side_nav.php b/side_nav.php index f9c8dafc..9638e76e 100644 --- a/side_nav.php +++ b/side_nav.php @@ -20,7 +20,12 @@
= 2 && $config_module_enable_ticketing == 1) { ?> From 4779e8f923e56e0ad965f4cd8a367b4b6c193b05 Mon Sep 17 00:00:00 2001 From: Hugo Sampaio Date: Mon, 6 May 2024 13:42:11 -0300 Subject: [PATCH 13/67] add file ext .unf https://forum.itflow.org/d/835-allow-file-type added .unf for unifi backup file --- client_file_upload_modal.php | 4 ++-- post/file.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client_file_upload_modal.php b/client_file_upload_modal.php index 68e29748..3fcb869a 100644 --- a/client_file_upload_modal.php +++ b/client_file_upload_modal.php @@ -11,7 +11,7 @@ +
+ +
+
+ +
+ +
+ Leave Blank for Full access to all clients, no affect on users with the admin role. +
+
diff --git a/inc_all_client.php b/inc_all_client.php index 3ecda6fc..21338abd 100644 --- a/inc_all_client.php +++ b/inc_all_client.php @@ -12,7 +12,7 @@ if (isset($_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 denyed 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"); + 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!"; diff --git a/post/user.php b/post/user.php index c5eecb53..c5de8549 100644 --- a/post/user.php +++ b/post/user.php @@ -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"); } From 9876c33d2e1d2cd2ab15f82526eeaeb0b07d54ec Mon Sep 17 00:00:00 2001 From: johnnyq Date: Fri, 10 May 2024 14:01:20 -0400 Subject: [PATCH 21/67] Client Access: Allow to select Client Access Restrictions for existing users --- admin_user_add_modal.php | 2 +- admin_user_edit_modal.php | 23 +++++++++++++++++++++++ admin_users.php | 9 +++++++++ post/user.php | 9 +++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/admin_user_add_modal.php b/admin_user_add_modal.php index dc631f7b..be5cf3a2 100644 --- a/admin_user_add_modal.php +++ b/admin_user_add_modal.php @@ -66,7 +66,7 @@
- +
+ + + + + +
+ Leave Blank for Full access to all clients, no affect on users with the admin role. +
+
diff --git a/admin_users.php b/admin_users.php index ce764ede..83464a28 100644 --- a/admin_users.php +++ b/admin_users.php @@ -125,9 +125,18 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $last_login = "$log_created_at
$log_user_os
$log_user_browser
$log_ip
"; } + // 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); + + ?> diff --git a/post/user.php b/post/user.php index c5de8549..4b79ec7c 100644 --- a/post/user.php +++ b/post/user.php @@ -113,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); From 3e7f57f64ae35bccaabd525438a69b01d2d09a9c Mon Sep 17 00:00:00 2001 From: johnnyq Date: Fri, 10 May 2024 14:27:04 -0400 Subject: [PATCH 22/67] Add contact primary to the API edit ^Cd create --- api/v1/contacts/contact_model.php | 8 ++++++++ api/v1/contacts/create.php | 2 +- api/v1/contacts/update.php | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/api/v1/contacts/contact_model.php b/api/v1/contacts/contact_model.php index 8fa13b27..7cb724b4 100644 --- a/api/v1/contacts/contact_model.php +++ b/api/v1/contacts/contact_model.php @@ -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) { diff --git a/api/v1/contacts/create.php b/api/v1/contacts/create.php index 2599eaa2..cf51f7c9 100644 --- a/api/v1/contacts/create.php +++ b/api/v1/contacts/create.php @@ -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) { diff --git a/api/v1/contacts/update.php b/api/v1/contacts/update.php index 19ae914a..a5049f47 100644 --- a/api/v1/contacts/update.php +++ b/api/v1/contacts/update.php @@ -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) { From d202c7684a3250add552fa72628ef327875dc725 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Fri, 10 May 2024 21:56:13 -0400 Subject: [PATCH 23/67] Feature: Allow copying a quote from one client to another --- post/quote.php | 4 ++-- quote_copy_modal.php | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/post/quote.php b/post/quote.php index c0a4fa92..94c055d2 100644 --- a/post/quote.php +++ b/post/quote.php @@ -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"; diff --git a/quote_copy_modal.php b/quote_copy_modal.php index 4f06976c..d2cb6f3a 100644 --- a/quote_copy_modal.php +++ b/quote_copy_modal.php @@ -11,6 +11,28 @@ + +
+
+ + +
+
+ +
diff --git a/settings_ticket.php b/settings_ticket.php index 18ef56de..75b106fb 100644 --- a/settings_ticket.php +++ b/settings_ticket.php @@ -11,6 +11,7 @@ require_once "inc_all_admin.php"; +
@@ -39,6 +40,15 @@ require_once "inc_all_admin.php";
+ +
+
+ value="1" id="ticketBillableSwitch"> + +
+
+ +
value="1" id="ticketAutoCloseSwitch"> diff --git a/ticket_add_modal.php b/ticket_add_modal.php index 7b2922f1..9cb3f7cb 100644 --- a/ticket_add_modal.php +++ b/ticket_add_modal.php @@ -8,6 +8,7 @@
+ + +
+ + +
+
+ +
- -
- -
- -
- - -
- -
- -
- - +
+
-
+
@@ -592,40 +580,32 @@ if (isset($_GET['ticket_id'])) {
- -
- -
+
-
- -
- -
- -
- -
- -
- -
- +
+
-
-
- -
-
- - +
+ +
+ +
+ +
+ +
+ + +
- +
+ +
From 2149a33bd56f3d46638b37c2045cecd863713d90 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Thu, 30 May 2024 16:36:38 -0400 Subject: [PATCH 59/67] Added Email Client to ticket details, Public Comment does not email the client or watchers anything, but can view it in the ticker portal --- post/ticket.php | 9 +++++++-- ticket.php | 15 ++++++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/post/ticket.php b/post/ticket.php index 1228b23e..7e076210 100644 --- a/post/ticket.php +++ b/post/ticket.php @@ -1105,9 +1105,14 @@ if (isset($_POST['add_ticket_reply'])) { $client_id = intval($_POST['client_id']); + $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'; } @@ -1157,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)) { diff --git a/ticket.php b/ticket.php index bd89a7d0..2e88cb37 100644 --- a/ticket.php +++ b/ticket.php @@ -544,6 +544,9 @@ if (isset($_GET['ticket_id'])) { +
@@ -583,7 +586,7 @@ if (isset($_GET['ticket_id'])) {
-
+
@@ -604,7 +607,7 @@ if (isset($_GET['ticket_id'])) {
- +
@@ -630,10 +633,16 @@ if (isset($_GET['ticket_id'])) { +