diff --git a/README.md b/README.md index 1f27ea61..baf6d339 100644 --- a/README.md +++ b/README.md @@ -60,32 +60,6 @@ * Whilst we are confident the code is safe, nothing in life is 100% safe or risk-free. Use your best judgement before deciding to store highly confidential information in ITFlow. * We are hoping to have a stable 1.0 release by early 2024. - -### Built With - -* Backend / PHP libs - * PHP - * MariaDB - * PHPMailer - * HTML Purifier - * PHP Mime Mail Parser - * Zap Calendar - -* CSS - * Bootstrap - * AdminLTE - * fontawesome - -* JS Libraries - * chart.js - * moments.js - * jQuery - * pdfmake - * Select2 - * TinyMCE - * FullCalendar.io - - ## Getting Started @@ -144,6 +118,11 @@ If you want to improve ITFlow, feel free to fork the repo and create a pull requ +### Supporters +We’re incredibly grateful to the organizations and individuals who support the project - a big thank you to: +- CompuMatter +- JetBrains + ## License diff --git a/SECURITY.md b/SECURITY.md index b5f161ca..f374eeac 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -12,6 +12,8 @@ ITFlow is currently in beta and is a work in progress. We attempt to follow security best practices where possible, including [automated code scanning](https://sonarcloud.io/component_measures?id=itflow-org_itflow&metric=security_rating&view=list). +[![Security](https://sonarcloud.io/api/project_badges/measure?project=itflow-org_itflow&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=itflow-org_itflow) + ## Supported Versions | Version | Supported | @@ -25,4 +27,4 @@ We attempt to follow security best practices where possible, including [automate If you have discovered a security issue, please **[report it](https://github.com/itflow-org/itflow/security/advisories/new)** to us in as much detail as possible, so we can fix it. -You should expect to receive an initial acknowledgement within 72 hours. If you don't receive any feedback, we may have missed the initial email from GitHub (we're human!). Please raise a private forum discussion with johnny and wrongecho quoting ONLY the assigned GHSA ref. +You should expect to receive an initial acknowledgement within 72 hours. If you don't receive any feedback, we may have missed the initial email from GitHub (we're human!). Please raise a forum discussion quoting ONLY the assigned GHSA ref. diff --git a/account_add_modal.php b/account_add_modal.php index 08df7019..9af3da36 100644 --- a/account_add_modal.php +++ b/account_add_modal.php @@ -8,6 +8,8 @@
+ + -
- -
-
- -
- -
-
-
diff --git a/account_edit_modal.php b/account_edit_modal.php index 94847ec1..58df3b62 100644 --- a/account_edit_modal.php +++ b/account_edit_modal.php @@ -9,6 +9,7 @@
+ -
- -
-
- -
- -
-
-
diff --git a/accounts.php b/accounts.php index 2f65a9d4..9703d683 100644 --- a/accounts.php +++ b/accounts.php @@ -12,8 +12,7 @@ $url_query_strings_sort = http_build_query($get_copy); $sql = mysqli_query( $mysqli, "SELECT SQL_CALC_FOUND_ROWS * FROM accounts - LEFT JOIN account_types ON account_types.account_type_id = accounts.account_type - WHERE (account_name LIKE '%$q%' OR account_type_name LIKE '%$q%') + WHERE (account_name LIKE '%$q%') AND account_archived_at IS NULL ORDER BY $sort $order LIMIT $record_from, $record_to" ); @@ -44,7 +43,6 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); "> Name - Type Currency Balance Action @@ -59,8 +57,6 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $opening_balance = floatval($row['opening_balance']); $account_currency_code = nullable_htmlentities($row['account_currency_code']); $account_notes = nullable_htmlentities($row['account_notes']); - $account_type = intval($row['account_type']); - $account_type_name = nullable_htmlentities($row['account_type_name']); $sql_payments = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS total_payments FROM payments WHERE payment_account_id = $account_id"); $row = mysqli_fetch_array($sql_payments); @@ -79,7 +75,6 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - @@ -93,7 +88,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - + Archive diff --git a/admin_account_types.php b/admin_account_types.php deleted file mode 100644 index 88803e01..00000000 --- a/admin_account_types.php +++ /dev/null @@ -1,183 +0,0 @@ - - -
-
-

Finance Account Types

-
- -
-
-
- -
-
-
- -
- -
-
-
-
- - -
-
- -
- - - - - - - - - - - - - - - - - - - No Records Here"; - } - - ?> - -
Account Type ParentAccount Type NameDescription
- - - - - - - -
-
-
-
- - \ No newline at end of file diff --git a/admin_account_types_add_modal.php b/admin_account_types_add_modal.php deleted file mode 100644 index 7afd7645..00000000 --- a/admin_account_types_add_modal.php +++ /dev/null @@ -1,37 +0,0 @@ - \ No newline at end of file diff --git a/admin_account_types_edit_modal.php b/admin_account_types_edit_modal.php deleted file mode 100644 index 486a08e1..00000000 --- a/admin_account_types_edit_modal.php +++ /dev/null @@ -1,40 +0,0 @@ - \ No newline at end of file diff --git a/admin_bulk_mail.php b/admin_bulk_mail.php index d7febe65..5ad44629 100644 --- a/admin_bulk_mail.php +++ b/admin_bulk_mail.php @@ -28,6 +28,7 @@ $sql = mysqli_query($mysqli, "SELECT * FROM contacts
+
@@ -148,4 +149,4 @@ $sql = mysqli_query($mysqli, "SELECT * FROM contacts + +
\ No newline at end of file diff --git a/admin_custom_link_edit_modal.php b/admin_custom_link_edit_modal.php new file mode 100644 index 00000000..472cbe60 --- /dev/null +++ b/admin_custom_link_edit_modal.php @@ -0,0 +1,52 @@ + \ No newline at end of file diff --git a/admin_custom_links.php b/admin_custom_links.php new file mode 100644 index 00000000..e5daf063 --- /dev/null +++ b/admin_custom_links.php @@ -0,0 +1,115 @@ + + +
+
+

Custom Links

+
+ +
+
+ +
+
+
+
+
+ +
+ +
+
+
+
+
+
+
+ +
+
+ + "> + + + + + + + + + + + + + + + + + +
NameURIAction
+ + + + + + +
+
+ +
+
+ +close(); - return $tables; } diff --git a/admin_document_template_details.php b/admin_document_template_details.php index 4768ea51..592a4998 100644 --- a/admin_document_template_details.php +++ b/admin_document_template_details.php @@ -11,7 +11,7 @@ $purifier_config->set('URI.AllowedSchemes', ['data' => true, 'src' => true, 'htt $purifier = new HTMLPurifier($purifier_config); if (isset($_GET['document_id'])) { - $document_id = intval($_GET['document_id']); + $document_id = intval($_GET['document_id']); } $sql_document = mysqli_query($mysqli, "SELECT * FROM documents WHERE document_template = 1 AND document_id = $document_id"); @@ -26,36 +26,36 @@ $document_updated_at = nullable_htmlentities($row['document_updated_at']); ?> - + -
-
+
+
-

+

-
- +
+ +
+
+
+ +
-
-
- -
-
- +
+
"> diff --git a/admin_role_add_modal.php b/admin_role_add_modal.php new file mode 100644 index 00000000..e860128f --- /dev/null +++ b/admin_role_add_modal.php @@ -0,0 +1,58 @@ + diff --git a/admin_role_edit_modal.php b/admin_role_edit_modal.php new file mode 100644 index 00000000..98356760 --- /dev/null +++ b/admin_role_edit_modal.php @@ -0,0 +1,119 @@ + diff --git a/admin_roles.php b/admin_roles.php new file mode 100644 index 00000000..6a0e3f85 --- /dev/null +++ b/admin_roles.php @@ -0,0 +1,135 @@ + +
Roles are not yet active/enforced - do not use.
+ +
+
+

Roles

+
+
+ +
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+
+
+ "> + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionAdminUser countAction
+ +
+
+
+ + + +
+
+ +
+
+ + - - + diff --git a/admin_ticket_template_details.php b/admin_ticket_template_details.php index 1c38a709..796271ef 100644 --- a/admin_ticket_template_details.php +++ b/admin_ticket_template_details.php @@ -11,7 +11,7 @@ $purifier_config->set('URI.AllowedSchemes', ['data' => true, 'src' => true, 'htt $purifier = new HTMLPurifier($purifier_config); if (isset($_GET['ticket_template_id'])) { - $ticket_template_id = intval($_GET['ticket_template_id']); + $ticket_template_id = intval($_GET['ticket_template_id']); } $sql_ticket_templates = mysqli_query($mysqli, "SELECT * FROM ticket_templates WHERE ticket_template_id = $ticket_template_id"); @@ -30,101 +30,101 @@ $sql_task_templates = mysqli_query($mysqli, "SELECT * FROM task_templates WHERE ?> - + -
-
+
+
-
-
-

-
- -
-

-
+
+
+

+
+ +
+

+
+
+
+

+
+ +
+
+
+
+ +
-
-

-
- +
-
-
-
- -
+ +
+ +
+
+
Tasks
+
+
+
+ +
+
+
+ +
+ +
+ +
+
+
+
+ + + + + + + + +
+ + + + +
+
+
+ +
+
-
- -
- -
-
-
Tasks
-
-
-
- -
-
-
- -
- -
- -
-
-
-
- - - - - - - - -
- - - - -
-
-
- -
- -
- - + - - + + - -

Blank Page

-
-

This is a great starting point for new custom pages.

+ +

Blank Page

+
+

This is a great starting point for new custom pages.

$start_date"; +echo "

User Agent

"; +echo getUserAgent(); + ?> -
+
-
-
Requester
-
Sam Adams
+
+
Requester
+
Sam Adams
-
Created
-
+
Created
+
-
Last activity
-
-
+
Last activity
+
+
- -
+ +
$date_time"; +echo "Current Date and Time: $date_time"; ?> diff --git a/budget.php b/budget.php index 834ca0f5..d4ccf9c5 100644 --- a/budget.php +++ b/budget.php @@ -50,7 +50,7 @@ $grandTotal = 0;
@@ -69,9 +69,9 @@ $grandTotal = 0; - $month): + foreach ($months as $index => $month): $amount = getBudgetAmount($budgets, $category['category_id'], $index + 1); $rowTotal += $amount; $columnTotals[$index] += $amount; @@ -80,7 +80,7 @@ $grandTotal = 0; - diff --git a/budget_edit.php b/budget_edit.php index c25007af..fe448b0c 100644 --- a/budget_edit.php +++ b/budget_edit.php @@ -66,9 +66,9 @@ $grandTotal = 0; - $month): + foreach ($months as $index => $month): $amount = getBudgetAmount($budgets, $category['category_id'], $index + 1); $rowTotal += $amount; $columnTotals[$index] += $amount; @@ -77,7 +77,7 @@ $grandTotal = 0; - diff --git a/check_login.php b/check_login.php index 7a6730a4..301e98ea 100644 --- a/check_login.php +++ b/check_login.php @@ -52,6 +52,9 @@ if ($session_user_role == 3) { } else { $session_user_role_display = "Accountant"; } +if (isset($row['user_role_is_admin']) && $row['user_role_is_admin'] == 1) { + $session_is_admin = true; +} $session_user_config_force_mfa = intval($row['user_config_force_mfa']); $user_config_records_per_page = intval($row['user_config_records_per_page']); diff --git a/client_asset_add_modal.php b/client_asset_add_modal.php index 12b514d9..0128f34e 100644 --- a/client_asset_add_modal.php +++ b/client_asset_add_modal.php @@ -9,7 +9,7 @@
- + + + @@ -389,6 +390,8 @@
+

Asset ID:

+
diff --git a/client_asset_export_modal.php b/client_asset_export_modal.php index d02eb260..d26cd1f9 100644 --- a/client_asset_export_modal.php +++ b/client_asset_export_modal.php @@ -8,11 +8,12 @@ + + + - + - + + + @@ -147,11 +149,35 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); -
+
+
+ +
+
+
- - All Assets + + All Assets 0) { ?> @@ -176,7 +202,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); } ?>
- "> Archived @@ -218,6 +244,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
+ +
"> @@ -236,16 +264,43 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - + + + - - + + + + + + + + + + + + + - + + + + @@ -258,7 +313,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $asset_type = nullable_htmlentities($row['asset_type']); $asset_name = nullable_htmlentities($row['asset_name']); $asset_description = nullable_htmlentities($row['asset_description']); - if (empty($asset_description)) { + if ($asset_description) { $asset_description_display = "-"; } else { $asset_description_display = $asset_description; @@ -266,22 +321,22 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $asset_make = nullable_htmlentities($row['asset_make']); $asset_model = nullable_htmlentities($row['asset_model']); $asset_serial = nullable_htmlentities($row['asset_serial']); - if (empty($asset_serial)) { - $asset_serial_display = "-"; - } else { + if ($asset_serial) { $asset_serial_display = $asset_serial; + } else { + $asset_serial_display = "-"; } $asset_os = nullable_htmlentities($row['asset_os']); - if (empty($asset_os)) { - $asset_os_display = "-"; - } else { + if ($asset_os) { $asset_os_display = $asset_os; + } else { + $asset_os_display = "-"; } $asset_ip = nullable_htmlentities($row['interface_ip']); - if (empty($asset_ip)) { - $asset_ip_display = "-"; - } else { + if ($asset_ip) { $asset_ip_display = $asset_ip; + } else { + $asset_ip_display = "-"; } $asset_ipv6 = nullable_htmlentities($row['interface_ipv6']); $asset_nat_ip = nullable_htmlentities($row['interface_nat_ip']); @@ -290,15 +345,30 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $asset_uri_2 = nullable_htmlentities($row['asset_uri_2']); $asset_status = nullable_htmlentities($row['asset_status']); $asset_purchase_date = nullable_htmlentities($row['asset_purchase_date']); - $asset_warranty_expire = nullable_htmlentities($row['asset_warranty_expire']); - $asset_install_date = nullable_htmlentities($row['asset_install_date']); - if (empty($asset_install_date)) { - $asset_install_date_display = "-"; + if ($asset_purchase_date) { + $asset_purchase_date_display = $asset_purchase_date; } else { + $asset_purchase_date_display = "-"; + } + $asset_warranty_expire = nullable_htmlentities($row['asset_warranty_expire']); + if ($asset_warranty_expire) { + $asset_warranty_expire_display = $asset_warranty_expire; + } else { + $asset_warranty_expire_display = "-"; + } + $asset_install_date = nullable_htmlentities($row['asset_install_date']); + if ($asset_install_date) { $asset_install_date_display = $asset_install_date; + } else { + $asset_install_date_display = "-"; } $asset_photo = nullable_htmlentities($row['asset_photo']); $asset_physical_location = nullable_htmlentities($row['asset_physical_location']); + if ($asset_physical_location) { + $asset_physical_location_display = $asset_physical_location; + } else { + $asset_physical_location_display = "-"; + } $asset_notes = nullable_htmlentities($row['asset_notes']); $asset_created_at = nullable_htmlentities($row['asset_created_at']); $asset_archived_at = nullable_htmlentities($row['asset_archived_at']); @@ -367,15 +437,30 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); + - - + + + + + + + + + + + + + + + + + - + + - + + + + + + + + + + + + +
Serial OS IPInstall Date + IP + + Purchase Date + + Install Date + + Warranty Expire + Assigned To + Assigned To + Location + Physical Location + Status Action
@@ -462,19 +547,19 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); 2) { ?> - + Unarchive Transfer - + Archive - + Delete diff --git a/client_certificates.php b/client_certificates.php index 63a0f451..86eef3fe 100644 --- a/client_certificates.php +++ b/client_certificates.php @@ -26,12 +26,14 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
- - + 0) { ?> + + +
@@ -100,8 +102,27 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $certificate_expire = nullable_htmlentities($row['certificate_expire']); $certificate_created_at = nullable_htmlentities($row['certificate_created_at']); + $certificate_expire_ago = timeAgo($certificate_expire); + // Convert the expiry date to a timestamp + $certificate_expire_timestamp = strtotime($row['certificate_expire']); + $current_timestamp = time(); // Get current timestamp + + // Calculate the difference in days + $days_until_expiry = ($certificate_expire_timestamp - $current_timestamp) / (60 * 60 * 24); + + // Determine the class based on the number of days until expiry + if ($days_until_expiry <= 0) { + $tr_class = "table-secondary"; + } elseif ($days_until_expiry <= 14) { + $tr_class = "table-danger"; + } elseif ($days_until_expiry <= 90) { + $tr_class = "table-warning"; + } else { + $tr_class = ''; + } + ?> -
@@ -123,7 +144,10 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
+
+
+
+

Contact ID:

+ diff --git a/client_contacts.php b/client_contacts.php index c8eb15f3..2528b22c 100644 --- a/client_contacts.php +++ b/client_contacts.php @@ -90,7 +90,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); -
+
+
"> @@ -198,7 +199,10 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - + diff --git a/client_document_details.php b/client_document_details.php index 9f011a5b..b4c7433e 100644 --- a/client_document_details.php +++ b/client_document_details.php @@ -35,6 +35,7 @@ $document_updated_at = nullable_htmlentities($row['document_updated_at']); $document_archived_at = nullable_htmlentities($row['document_archived_at']); $document_folder_id = intval($row['document_folder_id']); $document_parent = intval($row['document_parent']); +$document_client_visible = intval($row['document_client_visible']); ?> @@ -292,6 +293,24 @@ $document_parent = intval($row['document_parent']); ?> + +
+
Portal Collaboration
+ +
+ +
Revisions
+
NameDepartment + Department + + Contact Location Action
diff --git a/client_domains.php b/client_domains.php index 13e8b051..83b06fb0 100644 --- a/client_domains.php +++ b/client_domains.php @@ -35,12 +35,14 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
- - + 0) { ?> + + +
@@ -60,7 +62,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
- "> Archived @@ -124,6 +126,24 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $domain_name = nullable_htmlentities($row['domain_name']); $domain_description = nullable_htmlentities($row['domain_description']); $domain_expire = nullable_htmlentities($row['domain_expire']); + $domain_expire_ago = timeAgo($domain_expire); + // Convert the expiry date to a timestamp + $domain_expire_timestamp = strtotime($row['domain_expire']); + $current_timestamp = time(); // Get current timestamp + + // Calculate the difference in days + $days_until_expiry = ($domain_expire_timestamp - $current_timestamp) / (60 * 60 * 24); + + // Determine the class based on the number of days until expiry + if ($days_until_expiry <= 0) { + $tr_class = "table-secondary"; + } elseif ($days_until_expiry <= 14) { + $tr_class = "table-danger"; + } elseif ($days_until_expiry <= 90) { + $tr_class = "table-warning"; + } else { + $tr_class = ''; + } $domain_registrar_name = nullable_htmlentities($row['registrar_name']); if($domain_registrar_name) { $domain_registrar_name_display = $domain_registrar_name; @@ -142,7 +162,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $domain_mailhost_name_display = $domain_mailhost_name ? $domain_mailhost_name : "-"; ?> -
+ - + "; - echo ""; - echo ""; - echo ""; - echo ""; - } -} - -?> - -
-
-

Balance Sheet

-
- -
-
-
-
- -
-

- -

-

Balance Sheet

-
As of
-
-
-
@@ -163,7 +183,10 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
+
+
+
+ - + 0) { ?> @@ -236,6 +331,7 @@ $sql_asset_retire = mysqli_query( 0 + || mysqli_num_rows($sql_certificates_expiring) > 0 || mysqli_num_rows($sql_asset_warranties_expiring) > 0 || mysqli_num_rows($sql_asset_retire) > 0 || mysqli_num_rows($sql_licenses_expiring) > 0 @@ -245,7 +341,17 @@ $sql_asset_retire = mysqli_query(
-
Upcoming Expirations (Within 90 Days)
+
Upcoming Expirations
+
+
+ + +
+
@@ -269,6 +375,24 @@ $sql_asset_retire = mysqli_query( +

+ + + -- +

+ + +

- + --

@@ -298,7 +422,7 @@ $sql_asset_retire = mysqli_query( ?>

- + --

@@ -331,6 +455,124 @@ $sql_asset_retire = mysqli_query( + + 0 + || mysqli_num_rows($sql_certificates_expired) > 0 + || mysqli_num_rows($sql_asset_warranties_expired) > 0 + || mysqli_num_rows($sql_asset_retired) > 0 + || mysqli_num_rows($sql_licenses_expired) > 0 + ) + { ?> + +
+ +
+
+
Expired
+
+
+ + +

+ + + -- +

+ + + +

+ + + -- +

+ + + +

+ + + -- +

+ + + + + +

+ + + -- +

+ + + + +

+ + + -- +

+ + + +
+
+
+ + + 0) { ?> @@ -419,6 +661,8 @@ $sql_asset_retire = mysqli_query(
+
+ + 0 OR $config_telemetry == 2) { +if ($config_telemetry > 0 || $config_telemetry == 2) { $current_version = exec("git rev-parse HEAD"); diff --git a/cron_certificate_refresher.php b/cron_certificate_refresher.php index 3d0e76e5..61a4a346 100644 --- a/cron_certificate_refresher.php +++ b/cron_certificate_refresher.php @@ -59,4 +59,4 @@ while ($row = mysqli_fetch_array($sql_certificates)) { mysqli_query($mysqli,"UPDATE certificates SET certificate_issued_by = '$issued_by', certificate_expire = $expire, certificate_public_key = '$public_key' WHERE certificate_id = $certificate_id"); -} \ No newline at end of file +} diff --git a/cron_mail_queue.php b/cron_mail_queue.php index b19f3f67..9dce6b1d 100644 --- a/cron_mail_queue.php +++ b/cron_mail_queue.php @@ -24,11 +24,13 @@ $argv = $_SERVER['argv']; // Check cron is enabled if ($config_enable_cron == 0) { + error_log("Mail queue error - Cron is not enabled"); exit("Cron: is not enabled -- Quitting.."); } // Check Cron Key if ($argv[1] !== $config_cron_key && $_GET['key'] !== $config_cron_key) { + error_log("Mail queue error - Invalid cron key supplied"); exit("Cron Key invalid -- Quitting.."); } @@ -63,8 +65,12 @@ file_put_contents($lock_file_path, "Locked"); // 2 Failed // 3 Sent +/* + * ############################################################################################################### + * Initial email send + * ############################################################################################################### + */ // Get Mail Queue that has status of Queued and send it to the function sendSingleEmail() located in functions.php - $sql_queue = mysqli_query($mysqli, "SELECT * FROM email_queue WHERE email_status = 0 AND email_queued_at <= NOW()"); if (mysqli_num_rows($sql_queue) > 0) { @@ -80,47 +86,68 @@ if (mysqli_num_rows($sql_queue) > 0) { $email_sent_at = $row['email_sent_at']; $email_ics_str = $row['email_cal_str']; - // Sanitized Input - $email_recipient_logging = sanitizeInput($row['email_recipient']); - $email_subject_logging = sanitizeInput($row['email_subject']); + // First, validate the sender email address + if (filter_var($email_from, FILTER_VALIDATE_EMAIL)) { - // Update the status to sending - mysqli_query($mysqli, "UPDATE email_queue SET email_status = 1 WHERE email_id = $email_id"); + // Sanitized Input + $email_recipient_logging = sanitizeInput($row['email_recipient']); + $email_subject_logging = sanitizeInput($row['email_subject']); - // Verify contact email is valid - if (filter_var($email_recipient, FILTER_VALIDATE_EMAIL)) { + // Update the status to sending + mysqli_query($mysqli, "UPDATE email_queue SET email_status = 1 WHERE email_id = $email_id"); - $mail = sendSingleEmail( - $config_smtp_host, - $config_smtp_username, - $config_smtp_password, - $config_smtp_encryption, - $config_smtp_port, - $email_from, - $email_from_name, - $email_recipient, - $email_recipient_name, - $email_subject, - $email_content, - $email_ics_str - ); + // Next, verify recipient email is valid + if (filter_var($email_recipient, FILTER_VALIDATE_EMAIL)) { - if ($mail !== true) { - // Update Message - Failure - mysqli_query($mysqli, "UPDATE email_queue SET email_status = 2, email_failed_at = NOW(), email_attempts = 1 WHERE email_id = $email_id"); + $mail = sendSingleEmail( + $config_smtp_host, + $config_smtp_username, + $config_smtp_password, + $config_smtp_encryption, + $config_smtp_port, + $email_from, + $email_from_name, + $email_recipient, + $email_recipient_name, + $email_subject, + $email_content, + $email_ics_str + ); - mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Mail', notification = 'Failed to send email to $email_recipient_logging'"); - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Mail', log_action = 'Error', log_description = 'Failed to send email to $email_recipient_logging regarding $email_subject_logging. $mail'"); + if ($mail !== true) { + // Update Message - Failure + mysqli_query($mysqli, "UPDATE email_queue SET email_status = 2, email_failed_at = NOW(), email_attempts = 1 WHERE email_id = $email_id"); + + mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Cron-Mail-Queue', notification = 'Failed to send email #$email_id to $email_recipient_logging'"); + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Cron-Mail-Queue', log_action = 'Error', log_description = 'Failed to send email #$email_id to $email_recipient_logging regarding $email_subject_logging. $mail'"); + } else { + // Update Message - Success + mysqli_query($mysqli, "UPDATE email_queue SET email_status = 3, email_sent_at = NOW(), email_attempts = 1 WHERE email_id = $email_id"); + } } else { - // Update Message - Success - mysqli_query($mysqli, "UPDATE email_queue SET email_status = 3, email_sent_at = NOW(), email_attempts = 1 WHERE email_id = $email_id"); + // Recipient email isn't valid, mark as failed and log the error + mysqli_query($mysqli, "UPDATE email_queue SET email_status = 2, email_attempts = 99 WHERE email_id = $email_id"); + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Cron-Mail-Queue', log_action = 'Error', log_description = 'Failed to send email #$email_id due to invalid recipient address. Email subject was: $email_subject_logging.'"); } + + } else { + error_log("Failed to send email due to invalid sender address (' $email_from ') - check configuration in settings."); + + $email_from_logging = sanitizeInput($row['email_from']); + mysqli_query($mysqli, "UPDATE email_queue SET email_status = 2, email_attempts = 99 WHERE email_id = $email_id"); + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Cron-Mail-Queue', log_action = 'Error', log_description = 'Failed to send email #$email_id due to invalid sender address: $email_from_logging - check configuration in settings.'"); + mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Mail', notification = 'Failed to send email #$email_id due to invalid sender address'"); } + } } -// +/* + * ############################################################################################################### + * Retries + * ############################################################################################################### + */ // Get Mail that failed to send and attempt to send Failed Mail up to 4 times every 30 mins $sql_failed_queue = mysqli_query($mysqli, "SELECT * FROM email_queue WHERE email_status = 2 AND email_attempts < 4 AND email_failed_at < NOW() + INTERVAL 30 MINUTE"); @@ -146,7 +173,7 @@ if (mysqli_num_rows($sql_failed_queue) > 0) { // Update the status to sending before actually sending mysqli_query($mysqli, "UPDATE email_queue SET email_status = 1 WHERE email_id = $email_id"); - // Verify contact email is valid + // Verify recipient email is valid if (filter_var($email_recipient, FILTER_VALIDATE_EMAIL)) { $mail = sendSingleEmail( @@ -167,9 +194,7 @@ if (mysqli_num_rows($sql_failed_queue) > 0) { if ($mail !== true) { // Update Message mysqli_query($mysqli, "UPDATE email_queue SET email_status = 2, email_failed_at = NOW(), email_attempts = $email_attempts WHERE email_id = $email_id"); - - mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Mail', notification = 'Failed to send email to $email_recipient_logging'"); - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Mail', log_action = 'Error', log_description = 'Failed to send email to $email_recipient_logging regarding $email_subject_logging. $mail'"); + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Cron-Mail-Queue', log_action = 'Error', log_description = 'Failed to re-send email #$email_id to $email_recipient_logging regarding $email_subject_logging. $mail'"); } else { // Update Message mysqli_query($mysqli, "UPDATE email_queue SET email_status = 3, email_sent_at = NOW(), email_attempts = $email_attempts WHERE email_id = $email_id"); @@ -178,5 +203,5 @@ if (mysqli_num_rows($sql_failed_queue) > 0) { } } -// Remove the lock file once mail has finished processing so it doesnt get overun causing possible duplicates +// Remove the lock file once mail has finished processing unlink($lock_file_path); diff --git a/cron_ticket_email_parser.php b/cron_ticket_email_parser.php index 20a13215..3ba4ee6c 100644 --- a/cron_ticket_email_parser.php +++ b/cron_ticket_email_parser.php @@ -345,7 +345,7 @@ function createMailboxFolder($client, $folderName) { } // Function to subscribe to a folder in the mailbox -function subscribeMailboxFolder($client, $folder) { +function subscribeMailboxFolder($folder) { if ($folder) { try { // Subscribe to the folder @@ -378,7 +378,7 @@ $client->connect(); $folder = createMailboxFolder($client, 'ITFlow'); // Subscribe to the "ITFlow" mailbox folder -subscribeMailboxFolder($client, $folder); +subscribeMailboxFolder($folder); // Possible names for the inbox folder $inboxNames = ['Inbox', 'INBOX', 'inbox']; diff --git a/dashboard.php b/dashboard.php index 9f0ca1ae..02f6ca70 100644 --- a/dashboard.php +++ b/dashboard.php @@ -597,7 +597,7 @@ if ($user_config_dashboard_technical_enable == 1) {
- +

New Assets

@@ -623,7 +623,7 @@ if ($user_config_dashboard_technical_enable == 1) {
- +

Expiring Domains

diff --git a/database_updates.php b/database_updates.php index 257b7c6b..0ca193ab 100644 --- a/database_updates.php +++ b/database_updates.php @@ -2131,10 +2131,88 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) { mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.4.5'"); } - // if (CURRENT_DATABASE_VERSION == '1.4.5') { - // // Insert queries here required to update to DB version 1.4.6 + if (CURRENT_DATABASE_VERSION == '1.4.5') { + mysqli_query($mysqli, "ALTER TABLE `settings` ADD `config_whitelabel_enabled` INT(11) NOT NULL DEFAULT '0' AFTER `config_phone_mask`"); + mysqli_query($mysqli, "ALTER TABLE `settings` ADD `config_whitelabel_key` TEXT NULL DEFAULT NULL AFTER `config_whitelabel_enabled`"); + + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.4.6'"); + } + + if (CURRENT_DATABASE_VERSION == '1.4.6') { + mysqli_query($mysqli, "CREATE TABLE `custom_links` ( + `custom_link_id` INT(11) NOT NULL AUTO_INCREMENT, + `custom_link_name` VARCHAR(200) NOT NULL, + `custom_link_description` TEXT DEFAULT NULL, + `custom_link_uri` VARCHAR(500) NOT NULL, + `custom_link_icon` VARCHAR(200) DEFAULT NULL, + `custom_link_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `custom_link_updated_at` DATETIME ON UPDATE CURRENT_TIMESTAMP NULL, + `custom_link_archived_at` DATETIME NULL, + PRIMARY KEY (`custom_link_id`) + )"); + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.4.7'"); + } + + if (CURRENT_DATABASE_VERSION == '1.4.7') { + mysqli_query($mysqli, "ALTER TABLE `documents` ADD `document_client_visible` INT(11) NOT NULL DEFAULT '1' AFTER `document_parent`"); + + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.4.8'"); + } + + if (CURRENT_DATABASE_VERSION == '1.4.8') { + mysqli_query($mysqli, "ALTER TABLE `settings` DROP `config_stripe_client_pays_fees`"); + + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.4.9'"); + } + + if (CURRENT_DATABASE_VERSION == '1.4.9') { + + // Add new "is admin" identifier on user roles + mysqli_query($mysqli, "ALTER TABLE `user_roles` ADD `user_role_is_admin` INT(11) NOT NULL DEFAULT '0' AFTER `user_role_description`"); + mysqli_query($mysqli, "UPDATE `user_roles` SET `user_role_is_admin` = '1' WHERE `user_role_id` = 3"); + + // Add modules + mysqli_query($mysqli, "CREATE TABLE `modules` ( + `module_id` INT(11) NOT NULL AUTO_INCREMENT, + `module_name` VARCHAR(200) NOT NULL, + `module_description` VARCHAR(200) NULL, + PRIMARY KEY (`module_id`) + )"); + + mysqli_query($mysqli, "INSERT INTO modules SET module_name = 'module_client', module_description = 'General client & contact management'"); + mysqli_query($mysqli, "INSERT INTO modules SET module_name = 'module_support', module_description = 'Access to ticketing, assets and documentation'"); + mysqli_query($mysqli, "INSERT INTO modules SET module_name = 'module_credential', module_description = 'Access to client credentials - usernames, passwords and 2FA codes'"); + mysqli_query($mysqli, "INSERT INTO modules SET module_name = 'module_sales', module_description = 'Access to quotes, invoices and products'"); + mysqli_query($mysqli, "INSERT INTO modules SET module_name = 'module_financial', module_description = 'Access to payments, accounts, expenses and budgets'"); + mysqli_query($mysqli, "INSERT INTO modules SET module_name = 'module_reporting', module_description = 'Access to all reports'"); + + // Add table for storing role<->module permissions + mysqli_query($mysqli, "CREATE TABLE `user_role_permissions` ( + `user_role_id` INT(11) NOT NULL, + `module_id` INT(11) NOT NULL, + `user_role_permission_level` INT(11) NOT NULL + )"); + + // Add default permissions for accountant role + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 1, user_role_permission_level = 1"); // Read clients + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 2, user_role_permission_level = 1"); // Read support + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 4, user_role_permission_level = 1"); // Read sales + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 5, user_role_permission_level = 2"); // Modify financial + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 6, user_role_permission_level = 1"); // Read reports + + // Add default permissions for tech role + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 1, user_role_permission_level = 2"); // Modify clients + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 2, user_role_permission_level = 2"); // Modify support + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 3, user_role_permission_level = 2"); // Modify credentials + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 4, user_role_permission_level = 2"); // Modify sales + + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.5.0'"); + } + + // if (CURRENT_DATABASE_VERSION == '1.5.0') { + // // Insert queries here required to update to DB version 1.5.1 // // Then, update the database to the next sequential version - // mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.4.6'"); + // mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.5.1'"); // } } else { diff --git a/database_version.php b/database_version.php index 02ae2a6a..f81f9fe0 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.4.5"); +DEFINE("LATEST_DATABASE_VERSION", "1.5.0"); diff --git a/db.sql b/db.sql index 2b4ba70f..9d9d4b14 100644 --- a/db.sql +++ b/db.sql @@ -66,7 +66,7 @@ CREATE TABLE `api_keys` ( `api_key_id` int(11) NOT NULL AUTO_INCREMENT, `api_key_name` varchar(255) NOT NULL, `api_key_secret` varchar(255) NOT NULL, - `api_key_decrypt_hash` varchar(255) NULL, + `api_key_decrypt_hash` varchar(200) NOT NULL, `api_key_created_at` datetime NOT NULL DEFAULT current_timestamp(), `api_key_expire` date NOT NULL, `api_key_client_id` int(11) NOT NULL DEFAULT 0, @@ -462,6 +462,26 @@ CREATE TABLE `custom_fields` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `custom_links` +-- + +DROP TABLE IF EXISTS `custom_links`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `custom_links` ( + `custom_link_id` int(11) NOT NULL AUTO_INCREMENT, + `custom_link_name` varchar(200) NOT NULL, + `custom_link_description` text DEFAULT NULL, + `custom_link_uri` varchar(500) NOT NULL, + `custom_link_icon` varchar(200) DEFAULT NULL, + `custom_link_created_at` datetime NOT NULL DEFAULT current_timestamp(), + `custom_link_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(), + `custom_link_archived_at` datetime DEFAULT NULL, + PRIMARY KEY (`custom_link_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + -- -- Table structure for table `custom_values` -- @@ -506,6 +526,7 @@ CREATE TABLE `documents` ( `document_content_raw` longtext NOT NULL, `document_important` tinyint(1) NOT NULL DEFAULT 0, `document_parent` int(11) NOT NULL DEFAULT 0, + `document_client_visible` int(11) NOT NULL DEFAULT 1, `document_created_at` datetime NOT NULL DEFAULT current_timestamp(), `document_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(), `document_archived_at` datetime DEFAULT NULL, @@ -868,6 +889,18 @@ CREATE TABLE `logs` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `modules` +-- + +DROP TABLE IF EXISTS `modules`; +CREATE TABLE IF NOT EXISTS `modules` ( + `module_id` int(11) NOT NULL AUTO_INCREMENT, + `module_name` varchar(200) NOT NULL, + `module_description` varchar(200) DEFAULT NULL, + PRIMARY KEY (`module_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; + -- -- Table structure for table `networks` -- @@ -1468,7 +1501,7 @@ CREATE TABLE `settings` ( `config_ticket_from_name` varchar(200) DEFAULT NULL, `config_ticket_from_email` varchar(200) DEFAULT NULL, `config_ticket_email_parse` tinyint(1) NOT NULL DEFAULT 0, - `config_ticket_email_parse_unknown_senders` tinyint(1) NOT NULL DEFAULT 0, + `config_ticket_email_parse_unknown_senders` int(1) NOT NULL DEFAULT 0, `config_ticket_client_general_notifications` tinyint(1) NOT NULL DEFAULT 1, `config_ticket_autoclose_hours` int(5) NOT NULL DEFAULT 72, `config_ticket_new_ticket_notification_email` varchar(200) DEFAULT NULL, @@ -1492,7 +1525,6 @@ CREATE TABLE `settings` ( `config_ai_url` varchar(250) DEFAULT NULL, `config_ai_api_key` varchar(250) DEFAULT NULL, `config_stripe_flat_fee` decimal(15,2) NOT NULL DEFAULT 0.30, - `config_stripe_client_pays_fees` tinyint(1) NOT NULL DEFAULT 0, `config_azure_client_id` varchar(200) DEFAULT NULL, `config_azure_client_secret` varchar(200) DEFAULT NULL, `config_module_enable_itdoc` tinyint(1) NOT NULL DEFAULT 1, @@ -1509,6 +1541,8 @@ CREATE TABLE `settings` ( `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, + `config_whitelabel_enabled` int(11) NOT NULL DEFAULT 0, + `config_whitelabel_key` text DEFAULT NULL, PRIMARY KEY (`company_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; @@ -1937,6 +1971,7 @@ 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_is_admin` int(11) NOT NULL DEFAULT 0, `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, @@ -1944,6 +1979,17 @@ CREATE TABLE `user_roles` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `user_role_permissions` +-- + +DROP TABLE IF EXISTS `user_role_permissions`; +CREATE TABLE IF NOT EXISTS `user_role_permissions` ( + `user_role_id` int(11) NOT NULL, + `module_id` int(11) NOT NULL, + `user_role_permission_level` int(11) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; + -- -- Table structure for table `user_settings` -- @@ -2071,4 +2117,4 @@ CREATE TABLE `vendors` ( /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2024-06-13 12:39:55 +-- Dump completed on 2024-09-05 16:21:24 diff --git a/document_edit_visibility_modal.php b/document_edit_visibility_modal.php new file mode 100644 index 00000000..a06e0ccc --- /dev/null +++ b/document_edit_visibility_modal.php @@ -0,0 +1,42 @@ + diff --git a/expense_export_modal.php b/expense_export_modal.php index ce84b56d..acc4befc 100644 --- a/expense_export_modal.php +++ b/expense_export_modal.php @@ -2,16 +2,86 @@
+ + + + + +
+
+ + + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ + +
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+ +
+ +
+
+ +
+ +
+
+ + +
+
+ + + + + + +
+
+ + + + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ + +
+
+
+
+ > + +
+
+
+
+
+
+ > + +
+
+
+
+ +
+ +
+
+ +
+ +
+
+ + Cannot edit the primary contact"; } else { ?> + + +
+
+ + + + +
+

Contacts

+ +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + +
NameEmailRoles
+ +
+ +
+ +purify($row['document_content']); +if ($row) { + $document_id = intval($row['document_id']); + $document_name = nullable_htmlentities($row['document_name']); + $document_content = $purifier->purify($row['document_content']); +} else { + header("Location: portal_post.php?logout"); + exit(); +} ?> + +

diff --git a/portal/documents.php b/portal/documents.php index a03c6127..b19b402a 100644 --- a/portal/documents.php +++ b/portal/documents.php @@ -13,9 +13,10 @@ if ($session_contact_primary == 0 && !$session_contact_is_technical_contact) { exit(); } -$documents_sql = mysqli_query($mysqli, "SELECT document_id, document_name, document_created_at, folder_name FROM documents LEFT JOIN folders ON document_folder_id = folder_id WHERE document_client_id = $session_client_id AND document_template = 0 AND document_archived_at IS NULL ORDER BY folder_id, document_name DESC"); +$documents_sql = mysqli_query($mysqli, "SELECT document_id, document_name, document_created_at, folder_name FROM documents LEFT JOIN folders ON document_folder_id = folder_id WHERE document_client_visible = 1 AND document_client_id = $session_client_id AND document_template = 0 AND document_archived_at IS NULL ORDER BY folder_id, document_name DESC"); ?> +

Documents

diff --git a/portal/domains.php b/portal/domains.php new file mode 100644 index 00000000..7021564e --- /dev/null +++ b/portal/domains.php @@ -0,0 +1,55 @@ + + +

Domains

+
+ +
+ + + + + + + + + + + + + + + + + + + + +
Domain NameExpiry
+ +
+ +
+ + +

Invoices

diff --git a/portal/login.php b/portal/login.php index feb0f1ae..6d6ce3db 100644 --- a/portal/login.php +++ b/portal/login.php @@ -154,7 +154,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['login'])) { -
Forgot password?
+
Forgot password?
@@ -178,6 +178,12 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['login'])) {
+Powered by ITFlow'; +} +?> + diff --git a/portal/portal_footer.php b/portal/portal_footer.php index 692d6cdb..7793f8e7 100644 --- a/portal/portal_footer.php +++ b/portal/portal_footer.php @@ -11,7 +11,15 @@

-

+

+ Powered by ITFlow'; + } + ?> +

+ @@ -56,4 +64,4 @@ - \ No newline at end of file + diff --git a/portal/portal_header.php b/portal/portal_header.php index 237204a1..1e6f6c55 100644 --- a/portal/portal_header.php +++ b/portal/portal_header.php @@ -18,11 +18,8 @@ header("X-Frame-Options: DENY"); // Legacy - - + + @@ -50,10 +47,10 @@ header("X-Frame-Options: DENY"); // Legacy - - + + - + + + + @@ -109,3 +115,22 @@ header("X-Frame-Options: DENY"); // Legacy
+ + +
+ + +
+ diff --git a/portal/portal_post.php b/portal/portal_post.php index 2fb50ae0..7829e836 100644 --- a/portal/portal_post.php +++ b/portal/portal_post.php @@ -287,3 +287,36 @@ if (isset($_POST['edit_profile'])) { } header('Location: index.php'); } + +if (isset($_POST['edit_contact'])) { + $contact_id = intval($_POST['contact_id']); + $contact_name = sanitizeInput($_POST['contact_name']); + $contact_email = sanitizeInput($_POST['contact_email']); + $contact_technical = intval($_POST['contact_technical']); + $contact_billing = intval($_POST['contact_billing']); + $contact_auth_method = sanitizeInput($_POST['contact_auth_method']); + + mysqli_query($mysqli, "UPDATE contacts SET contact_name = '$contact_name', contact_email = '$contact_email', contact_billing = $contact_billing, contact_technical = $contact_technical, contact_auth_method = '$contact_auth_method' WHERE contact_id = $contact_id AND contact_client_id = $session_client_id AND contact_archived_at IS NULL AND contact_primary = 0"); + + // Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Contact', log_action = 'Modify', log_description = 'Client $session_contact_name modified contact $contact_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $session_client_id, log_entity_id = $contact_id"); + + $_SESSION['alert_message'] = "Contact updated"; + header('Location: contacts.php'); +} + +if (isset($_POST['add_contact'])) { + $contact_name = sanitizeInput($_POST['contact_name']); + $contact_email = sanitizeInput($_POST['contact_email']); + $contact_technical = intval($_POST['contact_technical']); + $contact_billing = intval($_POST['contact_billing']); + $contact_auth_method = sanitizeInput($_POST['contact_auth_method']); + + mysqli_query($mysqli, "INSERT INTO contacts SET contact_name = '$contact_name', contact_email = '$contact_email', contact_billing = $contact_billing, contact_technical = $contact_technical, contact_auth_method = '$contact_auth_method', contact_client_id = $session_client_id"); + + // Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Contact', log_action = 'Create', log_description = 'Client $session_contact_name created contact $contact_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $session_client_id"); + + $_SESSION['alert_message'] = "Contact created"; + header('Location: contacts.php'); +} diff --git a/portal/quotes.php b/portal/quotes.php index dfb3ce52..64266fea 100644 --- a/portal/quotes.php +++ b/portal/quotes.php @@ -16,6 +16,7 @@ if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) { $quotes_sql = mysqli_query($mysqli, "SELECT * FROM quotes WHERE quote_client_id = $session_client_id AND quote_status != 'Draft' ORDER BY quote_date DESC"); ?> +

Quotes

diff --git a/portal/ticket.php b/portal/ticket.php index 24ca2205..be059896 100644 --- a/portal/ticket.php +++ b/portal/ticket.php @@ -21,18 +21,18 @@ if (isset($_GET['id']) && intval($_GET['id'])) { if ($session_contact_primary == 1 || $session_contact_is_technical_contact) { // For a primary / technical contact viewing all tickets $ticket_sql = mysqli_query($mysqli, - "SELECT * FROM tickets - LEFT JOIN users on ticket_assigned_to = user_id - LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id + "SELECT * FROM tickets + LEFT JOIN users on ticket_assigned_to = user_id + LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id WHERE ticket_id = $ticket_id AND ticket_client_id = $session_client_id" ); } else { // For a user viewing their own ticket $ticket_sql = mysqli_query($mysqli, - "SELECT * FROM tickets - LEFT JOIN users on ticket_assigned_to = user_id - LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id + "SELECT * FROM tickets + LEFT JOIN users on ticket_assigned_to = user_id + LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id WHERE ticket_id = $ticket_id AND ticket_client_id = $session_client_id AND ticket_contact_id = $session_contact_id" ); } @@ -238,7 +238,7 @@ if (isset($_GET['id']) && intval($_GET['id'])) { - New Ticket -

Raise a new ticket

+

Raise a new ticket

diff --git a/portal/tickets.php b/portal/tickets.php index 4fe60d06..045860b2 100644 --- a/portal/tickets.php +++ b/portal/tickets.php @@ -42,6 +42,7 @@ $total_tickets = intval($row['total_tickets']); ?> +

Tickets

diff --git a/post.php b/post.php index 610a0f66..1e868931 100644 --- a/post.php +++ b/post.php @@ -16,8 +16,6 @@ require_once "post/admin.php"; require_once "post/account.php"; -require_once "post/account_type.php"; - require_once "post/api.php"; require_once "post/asset.php"; diff --git a/post/account.php b/post/account.php index 1a5b3a6c..ba843736 100644 --- a/post/account.php +++ b/post/account.php @@ -5,14 +5,14 @@ */ if (isset($_POST['add_account'])) { + validateCSRFToken($_POST['csrf_token']); $name = sanitizeInput($_POST['name']); $opening_balance = floatval($_POST['opening_balance']); $currency_code = sanitizeInput($_POST['currency_code']); $notes = sanitizeInput($_POST['notes']); - $type = intval($_POST['type']); - mysqli_query($mysqli,"INSERT INTO accounts SET account_name = '$name', opening_balance = $opening_balance, account_currency_code = '$currency_code', account_type ='$type', account_notes = '$notes'"); + mysqli_query($mysqli,"INSERT INTO accounts SET account_name = '$name', opening_balance = $opening_balance, account_currency_code = '$currency_code', account_notes = '$notes'"); //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Account', log_action = 'Create', log_description = '$name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); @@ -24,13 +24,13 @@ if (isset($_POST['add_account'])) { } if (isset($_POST['edit_account'])) { + validateCSRFToken($_POST['csrf_token']); $account_id = intval($_POST['account_id']); $name = sanitizeInput($_POST['name']); - $type = intval($_POST['type']); $notes = sanitizeInput($_POST['notes']); - mysqli_query($mysqli,"UPDATE accounts SET account_name = '$name',account_type = '$type', account_notes = '$notes' WHERE account_id = $account_id"); + mysqli_query($mysqli,"UPDATE accounts SET account_name = '$name', account_notes = '$notes' WHERE account_id = $account_id"); //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Account', log_action = 'Modify', log_description = '$name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); @@ -42,6 +42,7 @@ if (isset($_POST['edit_account'])) { } if (isset($_GET['archive_account'])) { + validateCSRFToken($_GET['csrf_token']); $account_id = intval($_GET['archive_account']); mysqli_query($mysqli,"UPDATE accounts SET account_archived_at = NOW() WHERE account_id = $account_id"); @@ -55,6 +56,7 @@ if (isset($_GET['archive_account'])) { } +// Not used anywhere? if (isset($_GET['delete_account'])) { $account_id = intval($_GET['delete_account']); diff --git a/post/account_type.php b/post/account_type.php deleted file mode 100644 index 07fc39bd..00000000 --- a/post/account_type.php +++ /dev/null @@ -1,68 +0,0 @@ - $uri', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + + $_SESSION['alert_message'] = "Custom link successfully created!"; + + header("Location: " . $_SERVER["HTTP_REFERER"]); + +} + +if (isset($_POST['edit_custom_link'])) { + + $custom_link_id = intval($_POST['custom_link_id']); + $name = sanitizeInput($_POST['name']); + $uri = sanitizeInput($_POST['uri']); + $icon = preg_replace("/[^0-9a-zA-Z-]/", "", sanitizeInput($_POST['icon'])); + + mysqli_query($mysqli,"UPDATE custom_links SET custom_link_name = '$name', custom_link_uri = '$uri', custom_link_icon = '$icon' WHERE custom_link_id = $custom_link_id"); + + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Custom Link', log_action = 'Modify', log_description = '$session_name edited the custom link $name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + + $_SESSION['alert_message'] = "Custom Link modified"; + + header("Location: " . $_SERVER["HTTP_REFERER"]); + +} + +if (isset($_GET['delete_custom_link'])) { + $custom_link_id = intval($_GET['delete_custom_link']); + + mysqli_query($mysqli,"DELETE FROM custom_links WHERE custom_link_id = $custom_link_id"); + + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Custom Link', log_action = 'Delete', log_description = '$session_name deleted a custom link', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + + $_SESSION['alert_message'] = "Cusatom Link deleted!"; + $_SESSION['alert_type'] = "error"; + + header("Location: " . $_SERVER["HTTP_REFERER"]); + +} + +if (isset($_POST['add_role'])) { + validateCSRFToken($_POST['csrf_token']); + validateAdminRole(); + + $name = sanitizeInput($_POST['role_name']); + $description = sanitizeInput($_POST['role_description']); + $admin = intval($_POST['role_is_admin']); + + mysqli_query($mysqli, "INSERT INTO user_roles SET user_role_name = '$name', user_role_description = '$description', user_role_is_admin = $admin"); + + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Role', log_action = 'Create', log_description = '$session_name created the $name role', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + + $_SESSION['alert_message'] = "Role $name created"; + + header("Location: " . $_SERVER["HTTP_REFERER"]); + +} + +if (isset($_POST['edit_role'])) { + validateCSRFToken($_POST['csrf_token']); + validateAdminRole(); + + // Update role metadata + $role_id = sanitizeInput($_POST['role_id']); + $name = sanitizeInput($_POST['role_name']); + $description = sanitizeInput($_POST['role_description']); + $admin = intval($_POST['role_is_admin']); + mysqli_query($mysqli, "UPDATE user_roles SET user_role_name = '$name', user_role_description = '$description', user_role_is_admin = $admin WHERE user_role_id = $role_id"); + + // Update role access levels + mysqli_query($mysqli, "DELETE FROM user_role_permissions WHERE user_role_id = $role_id"); + foreach ($_POST as $key => $value) { + if (str_contains($key, '##module_')){ + $module_id = intval(explode('##', $key)[0]); + $access_level = intval($value); + + if ($access_level > 0) { + echo $key . ' with id ' . $module_id . " : ". $access_level . "\n"; + mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = $role_id, module_id = $module_id, user_role_permission_level = $access_level"); + } + } + + } + + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Role', log_action = 'Modify', log_description = '$session_name updated the $name role', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + + $_SESSION['alert_message'] = "Role $name updated"; + + header("Location: " . $_SERVER["HTTP_REFERER"]); +} diff --git a/post/asset.php b/post/asset.php index 20f00280..1bbc735e 100644 --- a/post/asset.php +++ b/post/asset.php @@ -6,6 +6,7 @@ if (isset($_POST['add_asset'])) { + validateCSRFToken($_POST['csrf_token']); validateTechRole(); $client_id = intval($_POST['client_id']); @@ -105,6 +106,7 @@ if (isset($_POST['add_asset'])) { if (isset($_POST['edit_asset'])) { + validateCSRFToken($_POST['csrf_token']); validateTechRole(); $asset_id = intval($_POST['asset_id']); @@ -197,6 +199,7 @@ if (isset($_POST['edit_asset'])) { if (isset($_POST['change_client_asset'])) { + validateCSRFToken($_POST['csrf_token']); validateTechRole(); $current_asset_id = intval($_POST['current_asset_id']); @@ -244,6 +247,7 @@ if (isset($_POST['change_client_asset'])) { if (isset($_GET['archive_asset'])) { + validateCSRFToken($_GET['csrf_token']); validateTechRole(); $asset_id = intval($_GET['archive_asset']); @@ -268,6 +272,7 @@ if (isset($_GET['archive_asset'])) { if (isset($_GET['unarchive_asset'])) { + validateCSRFToken($_GET['csrf_token']); validateTechRole(); $asset_id = intval($_GET['unarchive_asset']); @@ -291,6 +296,7 @@ if (isset($_GET['unarchive_asset'])) { if (isset($_GET['delete_asset'])) { + validateCSRFToken($_GET['csrf_token']); validateAdminRole(); $asset_id = intval($_GET['delete_asset']); @@ -318,6 +324,7 @@ if (isset($_GET['delete_asset'])) { if (isset($_POST['bulk_assign_asset_location'])) { + validateCSRFToken($_POST['csrf_token']); validateTechRole(); $location_id = intval($_POST['bulk_location_id']); @@ -357,6 +364,7 @@ if (isset($_POST['bulk_assign_asset_location'])) { if (isset($_POST['bulk_assign_asset_contact'])) { + validateCSRFToken($_POST['csrf_token']); validateTechRole(); $contact_id = intval($_POST['bulk_contact_id']); @@ -396,6 +404,7 @@ if (isset($_POST['bulk_assign_asset_contact'])) { if (isset($_POST['bulk_edit_asset_status'])) { + validateCSRFToken($_POST['csrf_token']); validateTechRole(); $status = sanitizeInput($_POST['bulk_status']); @@ -429,8 +438,9 @@ if (isset($_POST['bulk_edit_asset_status'])) { } if (isset($_POST['bulk_archive_assets'])) { + + validateCSRFToken($_POST['csrf_token']); validateAdminRole(); - //validateCSRFToken($_POST['csrf_token']); $count = 0; // Default 0 $asset_ids = $_POST['asset_ids']; // Get array of asset IDs to be deleted @@ -469,8 +479,9 @@ if (isset($_POST['bulk_archive_assets'])) { } if (isset($_POST['bulk_unarchive_assets'])) { + + validateCSRFToken($_POST['csrf_token']); validateAdminRole(); - //validateCSRFToken($_POST['csrf_token']); $count = 0; // Default 0 $asset_ids = $_POST['asset_ids']; // Get array of asset IDs to be deleted @@ -509,6 +520,7 @@ if (isset($_POST['bulk_unarchive_assets'])) { if (isset($_POST["import_client_assets_csv"])) { + validateCSRFToken($_POST['csrf_token']); validateTechRole(); $client_id = intval($_POST['client_id']); @@ -655,6 +667,7 @@ if (isset($_GET['download_client_assets_csv_template'])) { if (isset($_POST['export_client_assets_csv'])) { + validateCSRFToken($_POST['csrf_token']); validateTechRole(); $client_id = intval($_POST['client_id']); @@ -704,6 +717,7 @@ if (isset($_POST['export_client_assets_csv'])) { if (isset($_POST['add_asset_interface'])) { + validateCSRFToken($_POST['csrf_token']); validateTechRole(); $asset_id = intval($_POST['asset_id']); @@ -740,6 +754,7 @@ if (isset($_POST['add_asset_interface'])) { if (isset($_POST['edit_asset_interface'])) { + validateCSRFToken($_POST['csrf_token']); validateTechRole(); $interface_id = intval($_POST['interface_id']); @@ -775,6 +790,7 @@ if (isset($_POST['edit_asset_interface'])) { if (isset($_GET['delete_asset_interface'])) { + validateCSRFToken($_GET['csrf_token']); validateAdminRole(); $interface_id = intval($_GET['delete_asset_interface']); diff --git a/post/client.php b/post/client.php index 2524077e..548eb892 100644 --- a/post/client.php +++ b/post/client.php @@ -508,15 +508,15 @@ if (isset($_POST["import_clients_csv"])) { // Create Contact mysqli_query($mysqli, "INSERT INTO contacts SET contact_name = '$contact_name', contact_title = '$title', contact_phone = '$contact_phone', contact_extension = '$contact_extension', contact_mobile = '$contact_mobile', contact_email = '$contact_email', contact_primary = 1, contact_important = 1, contact_client_id = $client_id"); - + $row_count = $row_count + 1; - - }else{ - + + } else { + $duplicate_count = $duplicate_count + 1; - + } - + } fclose($file); @@ -583,6 +583,9 @@ if (isset($_POST['export_client_pdf'])) { $export_trips = intval($_POST['export_trips']); $export_logs = intval($_POST['export_logs']); + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Client', log_action = 'Export', log_description = '$session_name exported client data to a PDF file', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id"); + //get records from database $sql = mysqli_query($mysqli,"SELECT * FROM clients diff --git a/post/custom_field.php b/post/custom_field.php index db985f94..53220610 100644 --- a/post/custom_field.php +++ b/post/custom_field.php @@ -8,7 +8,6 @@ if(isset($_POST['create_custom_field'])){ require_once 'post/custom_field_model.php'; - $table = sanitizeInput($_POST['table']); mysqli_query($mysqli,"INSERT INTO custom_fields SET custom_field_table = '$table', custom_field_label = '$label', custom_field_type = '$type'"); @@ -26,7 +25,6 @@ if(isset($_POST['edit_custom_field'])){ require_once 'post/custom_field_model.php'; - $custom_field_id = intval($_POST['custom_field_id']); mysqli_query($mysqli,"UPDATE custom_fields SET custom_field_label = '$label', custom_field_type = '$type' WHERE custom_field_id = $custom_field_id"); diff --git a/post/document.php b/post/document.php index 728b290c..2cf3f4f2 100644 --- a/post/document.php +++ b/post/document.php @@ -438,6 +438,23 @@ if (isset($_POST['edit_document_template'])) { } +if (isset($_POST['document_visible'])) { + validateTechRole(); + + $document_id = intval($_POST['document_id']); + $document_visible = intval($_POST['document_visible']); + + mysqli_query($mysqli,"UPDATE documents SET document_client_visible = $document_visible WHERE document_id = $document_id"); + + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Document', log_action = 'Modify', log_description = '$session_name modified document visibility', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id, log_entity_id = $document_id"); + + $_SESSION['alert_message'] = "Document visibility updated"; + + header("Location: " . $_SERVER["HTTP_REFERER"]); + +} + if (isset($_GET['archive_document'])) { validateTechRole(); diff --git a/post/expense.php b/post/expense.php index 0ff648a2..d5805aee 100644 --- a/post/expense.php +++ b/post/expense.php @@ -215,9 +215,51 @@ if (isset($_POST['bulk_edit_expense_client'])) { header("Location: " . $_SERVER["HTTP_REFERER"]); } +if (isset($_POST['bulk_delete_expenses'])) { + validateAdminRole(); + validateCSRFToken($_POST['csrf_token']); + + $count = 0; // Default 0 + $expense_ids = $_POST['expense_ids']; // Get array of expense IDs to be deleted + $client_id = intval($_POST['client_id']); + + if (!empty($expense_ids)) { + + // Cycle through array and delete each expense + foreach ($expense_ids as $expense_id) { + + $expense_id = intval($expense_id); + + $sql = mysqli_query($mysqli,"SELECT * FROM expenses WHERE expense_id = $expense_id"); + $row = mysqli_fetch_array($sql); + $expense_receipt = sanitizeInput($row['expense_receipt']); + + unlink("uploads/expenses/$expense_receipt"); + + mysqli_query($mysqli, "DELETE FROM expenses WHERE expense_id = $expense_id"); + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Expense', log_action = 'Delete', log_description = '$session_name deleted a expense (bulk)', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $expense_id"); + + $count++; + } + + // Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Expense', log_action = 'Delete', log_description = '$session_name bulk deleted $count expenses', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id"); + + $_SESSION['alert_type'] = "error"; + $_SESSION['alert_message'] = "Deleted $count expense(s)"; + + } + + header("Location: " . $_SERVER["HTTP_REFERER"]); +} + if (isset($_POST['export_expenses_csv'])) { $date_from = sanitizeInput($_POST['date_from']); $date_to = sanitizeInput($_POST['date_to']); + $account = intval($_POST['account']); + $vendor = intval($_POST['vendor']); + $category = intval($_POST['category']); + if (!empty($date_from) && !empty($date_to)) { $date_query = "AND DATE(expense_date) BETWEEN '$date_from' AND '$date_to'"; $file_name_date = "$date_from-to-$date_to"; @@ -226,6 +268,29 @@ if (isset($_POST['export_expenses_csv'])) { $file_name_date = date('Y-m-d'); } + // Vendor Filter + if ($account) { + $account_query = "AND expense_account_id = $account"; + } else { + $account_query = ''; + } + + // Vendor Filter + if ($vendor) { + $vendor_query = "AND expense_vendor_id = $vendor"; + } else { + // Default - any + $vendor_query = ''; + } + + // Category Filter + if ($category) { + $category_query = "AND expense_category_id = $category"; + } else { + // Default - any + $category_query = ''; + } + //get records from database $sql = mysqli_query($mysqli,"SELECT * FROM expenses LEFT JOIN categories ON expense_category_id = category_id @@ -233,6 +298,9 @@ if (isset($_POST['export_expenses_csv'])) { LEFT JOIN accounts ON expense_account_id = account_id WHERE expense_vendor_id > 0 $date_query + $account_query + $vendor_query + $category_query ORDER BY expense_date DESC "); diff --git a/post/invoice.php b/post/invoice.php index 34190698..7b596b63 100644 --- a/post/invoice.php +++ b/post/invoice.php @@ -931,6 +931,9 @@ if (isset($_GET['delete_payment'])) { $_SESSION['alert_type'] = "error"; $_SESSION['alert_message'] = "Payment deleted"; + if ($config_stripe_enable) { + $_SESSION['alert_message'] = "Payment deleted - Stripe payments must be manually refunded in Stripe"; + } header("Location: " . $_SERVER["HTTP_REFERER"]); diff --git a/post/location.php b/post/location.php index e60cb3a4..660133a4 100644 --- a/post/location.php +++ b/post/location.php @@ -6,7 +6,7 @@ if(isset($_POST['add_location'])){ - validateAdminRole(); + validateTechRole(); require_once 'post/client_locations_model.php'; @@ -65,7 +65,7 @@ if(isset($_POST['add_location'])){ if(isset($_POST['edit_location'])){ - validateAdminRole(); + validateTechRole(); require_once 'post/client_locations_model.php'; @@ -160,6 +160,8 @@ if(isset($_GET['archive_location'])){ if(isset($_GET['unarchive_location'])){ + validateTechRole(); + $location_id = intval($_GET['unarchive_location']); // Get Location Name and Client ID for logging and alert message diff --git a/post/profile.php b/post/profile.php index 0ae14660..92269aab 100644 --- a/post/profile.php +++ b/post/profile.php @@ -49,10 +49,10 @@ if (isset($_POST['edit_your_user_details'])) { } // Check to see if a file is attached - if ($_FILES['file']['tmp_name'] != '') { - if ($new_file_name = checkFileUpload($_FILES['file'], array('jpg', 'jpeg', 'gif', 'png'))) { + if ($_FILES['avatar']['tmp_name'] != '') { + if ($new_file_name = checkFileUpload($_FILES['avatar'], array('jpg', 'jpeg', 'gif', 'png'))) { - $file_tmp_path = $_FILES['file']['tmp_name']; + $file_tmp_path = $_FILES['avatar']['tmp_name']; // directory in which the uploaded file will be moved $upload_file_dir = "uploads/users/$session_user_id/"; @@ -66,10 +66,9 @@ if (isset($_POST['edit_your_user_details'])) { mysqli_query($mysqli,"UPDATE users SET user_avatar = '$new_file_name' WHERE user_id = $session_user_id"); // Extended Logging - $extended_log_description .= ", profile picture updated"; + $extended_log_description .= ", avatar updated"; - $_SESSION['alert_message'] = 'File successfully uploaded.'; - }else{ + } else { $_SESSION['alert_type'] = "error"; $_SESSION['alert_message'] = 'There was an error moving the file to upload directory. Please make sure the upload directory is writable by web server.'; } @@ -90,6 +89,15 @@ if (isset($_POST['edit_your_user_details'])) { } } +if (isset($_GET['clear_your_user_avatar'])) { + validateCSRFToken($_GET['csrf_token']); + + mysqli_query($mysqli,"UPDATE users SET user_avatar = NULL WHERE user_id = $session_user_id"); + + $_SESSION['alert_message'] = "Avatar cleared"; + header("Location: " . $_SERVER["HTTP_REFERER"]); +} + if (isset($_POST['edit_your_user_password'])) { // CSRF Check @@ -199,7 +207,7 @@ if (isset($_POST['verify'])) { } -if(isset($_POST['enable_2fa'])){ +if (isset($_POST['enable_2fa'])){ // CSRF Check validateCSRFToken($_POST['csrf_token']); @@ -220,7 +228,7 @@ if(isset($_POST['enable_2fa'])){ } -if(isset($_POST['disable_2fa'])){ +if (isset($_POST['disable_2fa'])){ // CSRF Check validateCSRFToken($_POST['csrf_token']); diff --git a/post/project.php b/post/project.php index 1327be12..2750c086 100644 --- a/post/project.php +++ b/post/project.php @@ -146,25 +146,32 @@ if (isset($_POST['add_project_ticket'])) { validateTechRole(); $project_id = intval($_POST['project_id']); - $ticket_id = intval($_POST['ticket_id']); // Get Project Name $sql = mysqli_query($mysqli, "SELECT * FROM projects WHERE project_id = $project_id"); $row = mysqli_fetch_array($sql); $client_id = intval($row['project_client_id']); $project_name = sanitizeInput($row['project_name']); - - // Get Ticket Info - $sql = mysqli_query($mysqli, "SELECT * FROM tickets WHERE ticket_project_id = $project_id"); - $row = mysqli_fetch_array($sql); - $ticket_subject = sanitizeInput($row['ticket_subject']); - mysqli_query($mysqli, "UPDATE tickets SET ticket_project_id = $project_id WHERE ticket_id = $ticket_id"); + // Add Tickets + if (!empty($_POST['tickets'])) { + foreach ($_POST['tickets'] as $ticket) { + $ticket_id = intval($ticket); + + // Get Ticket Info + $sql = mysqli_query($mysqli, "SELECT * FROM tickets WHERE ticket_project_id = $project_id"); + $row = mysqli_fetch_array($sql); + $ticket_subject = sanitizeInput($row['ticket_subject']); + + mysqli_query($mysqli, "UPDATE tickets SET ticket_project_id = $project_id WHERE ticket_id = $ticket_id"); - // Logging - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Project', log_action = 'Edit', log_description = '$session_name added a ticket $ticket_subject to project $project_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 = $project_id"); + // Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Project', log_action = 'Edit', log_description = '$session_name added a ticket $ticket_subject to project $project_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 = $project_id"); - $_SESSION['alert_message'] = "You added Ticket $ticket_subject to $project_name"; + } + } + + $_SESSION['alert_message'] = "You added Tickets to $project_name"; header("Location: " . $_SERVER["HTTP_REFERER"]); } \ No newline at end of file diff --git a/post/rack.php b/post/rack.php index ec50dcdc..016f434c 100644 --- a/post/rack.php +++ b/post/rack.php @@ -235,7 +235,7 @@ if (isset($_POST['edit_rack_unit'])) { if (isset($_GET['remove_rack_unit'])) { - validateAdminRole(); + validateTechRole(); $unit_id = intval($_GET['remove_rack_unit']); diff --git a/post/setting.php b/post/setting.php index 721a327d..5305c740 100644 --- a/post/setting.php +++ b/post/setting.php @@ -124,17 +124,17 @@ if (isset($_POST['edit_mail_from_settings'])) { validateCSRFToken($_POST['csrf_token']); validateAdminRole(); - $config_mail_from_email = sanitizeInput($_POST['config_mail_from_email']); - $config_mail_from_name = sanitizeInput($_POST['config_mail_from_name']); + $config_mail_from_email = sanitizeInput(filter_var($_POST['config_mail_from_email'], FILTER_VALIDATE_EMAIL)); + $config_mail_from_name = sanitizeInput(preg_replace('/[^a-zA-Z0-9\s]/', '', $_POST['config_mail_from_name'])); - $config_invoice_from_email = sanitizeInput($_POST['config_invoice_from_email']); - $config_invoice_from_name = sanitizeInput($_POST['config_invoice_from_name']); + $config_invoice_from_email = sanitizeInput(filter_var($_POST['config_invoice_from_email'], FILTER_VALIDATE_EMAIL)); + $config_invoice_from_name = sanitizeInput(preg_replace('/[^a-zA-Z0-9\s]/', '', $_POST['config_invoice_from_name'])); - $config_quote_from_email = sanitizeInput($_POST['config_quote_from_email']); - $config_quote_from_name = sanitizeInput($_POST['config_quote_from_name']); + $config_quote_from_email = sanitizeInput(filter_var($_POST['config_quote_from_email'], FILTER_VALIDATE_EMAIL)); + $config_quote_from_name = sanitizeInput(preg_replace('/[^a-zA-Z0-9\s]/', '', $_POST['config_quote_from_name'])); - $config_ticket_from_email = sanitizeInput($_POST['config_ticket_from_email']); - $config_ticket_from_name = sanitizeInput($_POST['config_ticket_from_name']); + $config_ticket_from_email = sanitizeInput(filter_var($_POST['config_ticket_from_email'], FILTER_VALIDATE_EMAIL)); + $config_ticket_from_name = sanitizeInput(preg_replace('/[^a-zA-Z0-9\s]/', '', $_POST['config_ticket_from_name'])); mysqli_query($mysqli,"UPDATE settings SET config_mail_from_email = '$config_mail_from_email', config_mail_from_name = '$config_mail_from_name', config_invoice_from_email = '$config_invoice_from_email', config_invoice_from_name = '$config_invoice_from_name', config_quote_from_email = '$config_quote_from_email', config_quote_from_name = '$config_quote_from_name', config_ticket_from_email = '$config_ticket_from_email', config_ticket_from_name = '$config_ticket_from_name' WHERE company_id = 1"); @@ -458,9 +458,8 @@ if (isset($_POST['edit_online_payment_settings'])) { $config_stripe_expense_category = intval($_POST['config_stripe_expense_category']); $config_stripe_percentage_fee = floatval($_POST['config_stripe_percentage_fee']) / 100; $config_stripe_flat_fee = floatval($_POST['config_stripe_flat_fee']); - $config_stripe_client_pays_fees = intval($_POST['config_stripe_client_pays_fees']); - mysqli_query($mysqli,"UPDATE settings SET config_stripe_enable = $config_stripe_enable, config_stripe_publishable = '$config_stripe_publishable', config_stripe_secret = '$config_stripe_secret', config_stripe_account = $config_stripe_account, config_stripe_expense_vendor = $config_stripe_expense_vendor, config_stripe_expense_category = $config_stripe_expense_category, config_stripe_percentage_fee = $config_stripe_percentage_fee, config_stripe_flat_fee = $config_stripe_flat_fee, config_stripe_client_pays_fees = $config_stripe_client_pays_fees WHERE company_id = 1"); + mysqli_query($mysqli,"UPDATE settings SET config_stripe_enable = $config_stripe_enable, config_stripe_publishable = '$config_stripe_publishable', config_stripe_secret = '$config_stripe_secret', config_stripe_account = $config_stripe_account, config_stripe_expense_vendor = $config_stripe_expense_vendor, config_stripe_expense_category = $config_stripe_expense_category, config_stripe_percentage_fee = $config_stripe_percentage_fee, config_stripe_flat_fee = $config_stripe_flat_fee WHERE company_id = 1"); //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Settings', log_action = 'Modify', log_description = '$session_name modified online payment settings', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); @@ -524,9 +523,17 @@ if (isset($_POST['edit_module_settings'])) { $config_module_enable_ticketing = intval($_POST['config_module_enable_ticketing']); $config_module_enable_accounting = intval($_POST['config_module_enable_accounting']); $config_client_portal_enable = intval($_POST['config_client_portal_enable']); + $config_whitelabel_key = $_POST['config_whitelabel_key']; mysqli_query($mysqli,"UPDATE settings SET config_module_enable_itdoc = $config_module_enable_itdoc, config_module_enable_ticketing = $config_module_enable_ticketing, config_module_enable_accounting = $config_module_enable_accounting, config_client_portal_enable = $config_client_portal_enable WHERE company_id = 1"); + // Validate white label key + if (!empty($config_whitelabel_key && validateWhitelabelKey($config_whitelabel_key))) { + mysqli_query($mysqli, "UPDATE settings SET config_whitelabel_enabled = 1, config_whitelabel_key = '$config_whitelabel_key' WHERE company_id = 1"); + } else { + mysqli_query($mysqli, "UPDATE settings SET config_whitelabel_enabled = 0, config_whitelabel_key = '' WHERE company_id = 1"); + } + //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Settings', log_action = 'Modify', log_description = '$session_name modified module settings', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); diff --git a/post/ticket.php b/post/ticket.php index c1ad77b3..9b799e7c 100644 --- a/post/ticket.php +++ b/post/ticket.php @@ -1123,10 +1123,15 @@ if (isset($_POST['bulk_add_ticket_project'])) { // POST variables $project_id = intval($_POST['project_id']); + // Get Project Name + $sql = mysqli_query($mysqli, "SELECT * FROM projects WHERE project_id = $project_id"); + $row = mysqli_fetch_array($sql); + $project_name = sanitizeInput($row['project_name']); + // Get a Ticket Count $ticket_count = count($_POST['ticket_ids']); - // Assign Tech to Selected Tickets + // Assign Project to Selected Tickets if (!empty($_POST['ticket_ids'])) { foreach ($_POST['ticket_ids'] as $ticket_id) { $ticket_id = intval($ticket_id); @@ -1140,11 +1145,6 @@ if (isset($_POST['bulk_add_ticket_project'])) { $current_ticket_priority = sanitizeInput($row['ticket_priority']); $client_id = intval($row['ticket_client_id']); - // Get Project Name - $sql = mysqli_query($mysqli, "SELECT * FROM projects WHERE project_id = $project_id"); - $row = mysqli_fetch_array($sql); - $project_name = sanitizeInput($row['project_name']); - // Update ticket & insert reply mysqli_query($mysqli, "UPDATE tickets SET ticket_project_id = $project_id WHERE ticket_id = $ticket_id"); @@ -1310,11 +1310,12 @@ if (isset($_POST['edit_ticket_reply'])) { $ticket_reply_id = intval($_POST['ticket_reply_id']); $ticket_reply = mysqli_real_escape_string($mysqli, $_POST['ticket_reply']); + $ticket_reply_type = sanitizeInput($_POST['ticket_reply_type']); $ticket_reply_time_worked = sanitizeInput($_POST['time']); $client_id = intval($_POST['client_id']); - mysqli_query($mysqli, "UPDATE ticket_replies SET ticket_reply = '$ticket_reply', ticket_reply_time_worked = '$ticket_reply_time_worked' WHERE ticket_reply_id = $ticket_reply_id AND ticket_reply_type != 'Client'") or die(mysqli_error($mysqli)); + mysqli_query($mysqli, "UPDATE ticket_replies SET ticket_reply = '$ticket_reply', ticket_reply_type = '$ticket_reply_type', ticket_reply_time_worked = '$ticket_reply_time_worked' WHERE ticket_reply_id = $ticket_reply_id AND ticket_reply_type != 'Client'") or die(mysqli_error($mysqli)); //Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket Reply', log_action = 'Modify', log_description = '$session_name modified ticket reply', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $ticket_reply_id"); @@ -1736,7 +1737,13 @@ if (isset($_POST['export_client_tickets_csv'])) { $client_name = $row['client_name']; - $sql = mysqli_query($mysqli, "SELECT * FROM tickets WHERE ticket_client_id = $client_id ORDER BY ticket_number ASC"); + $sql = mysqli_query( + $mysqli, + "SELECT * FROM tickets + LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id + WHERE ticket_client_id = $client_id ORDER BY ticket_number ASC" + ); + if ($sql->num_rows > 0) { $delimiter = ","; $filename = $client_name . "-Tickets-" . date('Y-m-d') . ".csv"; @@ -1745,12 +1752,12 @@ if (isset($_POST['export_client_tickets_csv'])) { $f = fopen('php://memory', 'w'); //set column headers - $fields = array('Ticket Number', 'Priority', 'Status', 'Subject', 'Date Opened', 'Date Closed'); + $fields = array('Ticket Number', 'Priority', 'Status', 'Subject', 'Date Opened', 'Date Resolved', 'Date Closed'); fputcsv($f, $fields, $delimiter); //output each row of the data, format line as csv and write to file pointer while ($row = $sql->fetch_assoc()) { - $lineData = array($row['ticket_number'], $row['ticket_priority'], $row['ticket_status'], $row['ticket_subject'], $row['ticket_created_at'], $row['ticket_closed_at']); + $lineData = array($config_ticket_prefix . $row['ticket_number'], $row['ticket_priority'], $row['ticket_status_name'], $row['ticket_subject'], $row['ticket_created_at'], $row['ticket_resolved_at'], $row['ticket_closed_at']); fputcsv($f, $lineData, $delimiter); } diff --git a/products.php b/products.php index 1f5e966b..6b28f93e 100644 --- a/products.php +++ b/products.php @@ -124,6 +124,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
+
"> diff --git a/project_add_modal.php b/project_add_modal.php index a9d06f41..8e7fbc49 100644 --- a/project_add_modal.php +++ b/project_add_modal.php @@ -90,13 +90,13 @@
- +
- + $client_name
"; + } else { + $client_name_display = ""; + } $project_manager = intval($row['user_id']); $project_manager_name = nullable_htmlentities($row['user_name']); @@ -55,6 +60,7 @@ if (isset($_GET['project_id'])) { // Get Tickets $sql_tickets = mysqli_query($mysqli, "SELECT * FROM tickets LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id + LEFT JOIN clients ON ticket_client_id = client_id LEFT JOIN users ON ticket_assigned_to = user_id WHERE ticket_project_id = $project_id ORDER BY ticket_number ASC" ); @@ -131,27 +137,22 @@ if (isset($_GET['project_id'])) {
-

+

$project_name"; ?>

-
- -
-

- -
- - - -
- Total time worked: -
- -
+
+
+
+
+ + +
+ Total time worked:
+
@@ -226,12 +227,12 @@ if (isset($_GET['project_id'])) {
- - + + @@ -285,6 +286,9 @@ if (isset($_GET['project_id'])) { $project_id = intval($row['ticket_project_id']); + $client_id = intval($row['client_id']); + $client_name = nullable_htmlentities($row['client_name']); + $contact_name = nullable_htmlentities($row['contact_name']); $contact_email = nullable_htmlentities($row['contact_email']); $contact_archived_at = nullable_htmlentities($row['contact_archived_at']); @@ -324,16 +328,13 @@ if (isset($_GET['project_id'])) { - + - - - - @@ -350,6 +351,7 @@ if (isset($_GET['project_id'])) {
+ diff --git a/project_ticket_add_modal.php b/project_ticket_add_modal.php index 33c13b6b..f6172c57 100644 --- a/project_ticket_add_modal.php +++ b/project_ticket_add_modal.php @@ -17,16 +17,18 @@
- + - + $client_name"; + } else { + $client_name_display = "-"; + } $project_manager = intval($row['user_id']); if ($project_manager) { @@ -229,11 +234,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - + - - - - - + + + + + diff --git a/report_all_assets_by_client.php b/report_all_assets_by_client.php deleted file mode 100644 index 623af604..00000000 --- a/report_all_assets_by_client.php +++ /dev/null @@ -1,76 +0,0 @@ - - -
-
-

All Assets by Client

-
- -
-
-
-
-
NumberSubjectTicket Priority Status Assigned Last ResponseClient
- + + + + - -
- - - - ClientSubjectPriorityFrequencyNext Run DateClientSubjectPriorityFrequencyNext Run Date Action
- - - - - - - - - - - - - - - - - -
ClientAsset NameAsset Type - Asset Status -
- - - - - - - -
-
-
-
- - - -
-
-

All Assets by Client - with custom columns

-
- -
-
-
-
- - - - " . htmlspecialchars($col) . ""; - } - ?> - - - - "; - foreach ($selected_columns as $col) { - echo ""; - } - echo ""; - } - ?> - -
" . nullable_htmlentities($row[$col]) . "
-
- - - - - - - - - - - - - - - - -
-
- - + +
+
+

All Assets

+
+
+
+
+
+ + +
+ +
+
+ s"> +
+ +
+
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+ "> + Archived + + +
+
+ +
+
+
+ + All Assets + + 0) { ?> + Workstations + 0) { ?> + Servers + 0) { ?> + Virtual + 0) { ?> + Network + 0) { ?> + Other + +
+
+
+
+
+
+
+ + +
+ + "> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $contact_name"; + } else { + $contact_name_display = $contact_name; + } + + $location_name = nullable_htmlentities($row['location_name']); + if (empty($location_name)) { + $location_name = "-"; + } + $location_archived_at = nullable_htmlentities($row['location_archived_at']); + if ($location_archived_at) { + $location_name_display = "
$location_name
"; + } else { + $location_name_display = $location_name; + } + + $sql_logins = mysqli_query($mysqli, "SELECT * FROM logins WHERE login_asset_id = $asset_id"); + $login_count = mysqli_num_rows($sql_logins); + + ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
NameTypeModelSerialOS + IP + + Purchase Date + + Install Date + + Warranty Expire + + Assigned To + Location + Physical Location + StatusClientAction
+
+ +
+
+ +
+ +
+
+
+
+
+
+
+ +
+ +
+
+
+ 0) { ?> + + + + + + + + + +
+
+
+ +
+ +
+
+ + + + $account_id, - 'name' => $row['account_name'], - 'type' => $row['account_type_name'], - 'balance' => $balance - ]; -} - -function display_account_section($mysqli, $accounts, $type) { - foreach ($accounts[$type] as $account) { - global $currency_format; - global $currency_code; - $currency_code = getAccountCurrencyCode($mysqli, $account['id']); - echo "
{$account['type']}{$account['name']}" . numfmt_format_currency($currency_format, $account['balance'], $currency_code) . "
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
Total Assets
Total Liabilities
Total Equities
- -
-
-
- - \ No newline at end of file diff --git a/report_collections.php b/report_collections.php deleted file mode 100644 index 0321db7f..00000000 --- a/report_collections.php +++ /dev/null @@ -1,129 +0,0 @@ - 0 THEN - (IFNULL(SUM(invoices.invoice_amount), 0) - IFNULL(SUM(payments.payment_amount), 0) - IFNULL(recurring_totals.recurring_monthly_total, 0)) / recurring_totals.recurring_monthly_total - ELSE - 0 - END AS months_behind - FROM - clients - LEFT JOIN - invoices - ON - clients.client_id = invoices.invoice_client_id - AND invoices.invoice_status NOT LIKE 'Draft' - AND invoices.invoice_status NOT LIKE 'Cancelled' - LEFT JOIN - (SELECT - payment_invoice_id, - SUM(payment_amount) as payment_amount - FROM payments - GROUP BY payment_invoice_id) as payments - ON - invoices.invoice_id = payments.payment_invoice_id - LEFT JOIN - contacts - ON - clients.client_id = contacts.contact_client_id AND contacts.contact_billing = 1 - LEFT JOIN - (SELECT - recurring_client_id, - SUM(recurring_amount) AS recurring_monthly_total - FROM recurring - WHERE recurring_status = 1 AND recurring_frequency = 'month' - GROUP BY recurring_client_id) as recurring_totals - ON - clients.client_id = recurring_totals.recurring_client_id - GROUP BY - clients.client_id, - clients.client_name, - contacts.contact_phone, - recurring_totals.recurring_monthly_total - HAVING - balance > 0 AND months_behind >= 2 - ORDER BY - months_behind DESC;"; - $result_client_balance_report = mysqli_query($mysqli, $sql_client_balance_report); - - //get currency format from settings - $config_currency_code = getSettingValue($mysqli, "company_currency"); -?> - -
-
-

Collections

-
- -
-
-
-
-
- - - - - - - - - - - - - "; - echo ""; - echo ""; - echo ""; - echo ""; - echo ""; - echo ""; - echo ""; - } - ?> - -
Client NameBalanceBilling Contact PhoneMonthly Recurring AmountPast Due BalanceMonths Past Due
$client_name$formatted_balance$billing_contact_phone$formatted_recurring_monthly_total$formatted_behind_amount$months_behind
-
-
-
-
- - diff --git a/domains.php b/report_domains.php similarity index 86% rename from domains.php rename to report_domains.php index 862d33e6..8ec4c792 100644 --- a/domains.php +++ b/report_domains.php @@ -22,8 +22,9 @@ $sql = mysqli_query($mysqli, "SELECT SQL_CALC_FOUND_ROWS domains.*, LEFT JOIN vendors AS mailhost ON domains.domain_mailhost = mailhost.vendor_id LEFT JOIN vendors AS webhost ON domains.domain_webhost = webhost.vendor_id WHERE domain_archived_at IS NULL - AND (domain_name LIKE '%$q%' OR domain_description LIKE '%$q%' OR registrar.vendor_name LIKE '%$q%' OR dnshost.vendor_name LIKE '%$q%' OR mailhost.vendor_name LIKE '%$q%' OR webhost.vendor_name LIKE '%$q%' OR client_name LIKE '%$q%') - ORDER BY $sort $order LIMIT $record_from, $record_to"); + AND (domain_name LIKE '%$q%' OR domain_description LIKE '%$q%' OR registrar.vendor_name LIKE '%$q%' OR dnshost.vendor_name LIKE '%$q%' OR mailhost.vendor_name LIKE '%$q%' OR webhost.vendor_name LIKE '%$q%' OR client_name LIKE '%$q%') + ORDER BY $sort $order LIMIT $record_from, $record_to" +); $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); @@ -101,8 +102,28 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $domain_name = nullable_htmlentities($row['domain_name']); $domain_description = nullable_htmlentities($row['domain_description']); $domain_expire = nullable_htmlentities($row['domain_expire']); + + $domain_expire_ago = timeAgo($domain_expire); + // Convert the expiry date to a timestamp + $domain_expire_timestamp = strtotime($row['domain_expire']); + $current_timestamp = time(); // Get current timestamp + + // Calculate the difference in days + $days_until_expiry = ($domain_expire_timestamp - $current_timestamp) / (60 * 60 * 24); + + // Determine the class based on the number of days until expiry + if ($days_until_expiry <= 0) { + $tr_class = "table-secondary"; + } elseif ($days_until_expiry <= 14) { + $tr_class = "table-danger"; + } elseif ($days_until_expiry <= 90) { + $tr_class = "table-warning"; + } else { + $tr_class = ''; + } + $domain_registrar_name = nullable_htmlentities($row['registrar_name']); - if($domain_registrar_name) { + if ($domain_registrar_name) { $domain_registrar_name_display = $domain_registrar_name; } else { $domain_registrar_name_display = "-"; @@ -120,7 +141,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $client_name = nullable_htmlentities($row['client_name']); ?> -
@@ -128,7 +149,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
- )" data-target="#editDomainModal"> +
@@ -142,7 +163,10 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
+
+
+
- @@ -42,7 +41,7 @@ $sql_tax = mysqli_query($mysqli, - + + -
- - - - - - - - - - - - 0) { - - // Calculate average time to solve - $count = 0; - $total = 0; - while ($row = mysqli_fetch_array($sql_tickets)) { - $openedTime = new DateTime($row['ticket_created_at']); - $resolvedTime = new DateTime($row['ticket_resolved_at']); - - $total += ($resolvedTime->getTimestamp() - $openedTime->getTimestamp()); - $count++; - } - $avg_time_to_resolve = $total / $count; - - ?> - +
+
+

Yearly ()

+
+
+
+
ClientTickets raisedTickets resolvedTotal Time worked (H:M:S)Avg time to resolve
+ - - - - - + + + + + + + + + + - -
ClientTickets raisedBy priority: LowBy priority: MedBy priority: HighTickets resolvedTotal Time worked (H:M:S)Avg time to resolve
+ while ($row = mysqli_fetch_array($sql_clients)) { + $client_id = intval($row['client_id']); + $client_name = nullable_htmlentities($row['client_name']); + + // Calculate total tickets raised in period + $sql_ticket_raised_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS ticket_raised_count FROM tickets WHERE YEAR(ticket_created_at) = $year AND ticket_client_id = $client_id"); + $row = mysqli_fetch_array($sql_ticket_raised_count); + $ticket_raised_count = intval($row['ticket_raised_count']); + + // Calculate total tickets raised in period that are resolved + $sql_ticket_resolved_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS ticket_resolved_count FROM tickets WHERE YEAR(ticket_created_at) = $year AND ticket_client_id = $client_id AND ticket_resolved_at IS NOT NULL"); + $row = mysqli_fetch_array($sql_ticket_resolved_count); + $ticket_resolved_count = intval($row['ticket_resolved_count']); + + // Breakdown tickets for each priority - Low + $sql_low_ticket_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS low_ticket_count FROM TICKETS WHERE YEAR(ticket_created_at) = $year AND ticket_client_id = $client_id AND ticket_priority = 'Low'"); + $row = mysqli_fetch_array($sql_low_ticket_count); + $low_ticket_count = intval($row['low_ticket_count']); + + // Breakdown tickets for each priority - Low + $sql_med_ticket_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS med_ticket_count FROM TICKETS WHERE YEAR(ticket_created_at) = $year AND ticket_client_id = $client_id AND ticket_priority = 'Medium'"); + $row = mysqli_fetch_array($sql_med_ticket_count); + $med_ticket_count = intval($row['med_ticket_count']); + + // Breakdown tickets for each priority - Low + $sql_high_ticket_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS high_ticket_count FROM TICKETS WHERE YEAR(ticket_created_at) = $year AND ticket_client_id = $client_id AND ticket_priority = 'High'"); + $row = mysqli_fetch_array($sql_high_ticket_count); + $high_ticket_count = intval($row['high_ticket_count']); + + // Used to calculate average time to resolve tickets that were raised in period specified + $sql_tickets = mysqli_query($mysqli, "SELECT ticket_created_at, ticket_resolved_at FROM tickets WHERE YEAR(ticket_created_at) = $year AND ticket_client_id = $client_id AND ticket_resolved_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 || $ticket_resolved_count > 0) { + + $avg_time_to_resolve = '-'; + if ($ticket_resolved_count > 0) { + // Calculate average time to solve + $count = 0; + $total = 0; + while ($row = mysqli_fetch_array($sql_tickets)) { + $openedTime = new DateTime($row['ticket_created_at']); + $resolvedTime = new DateTime($row['ticket_resolved_at']); + + $total += ($resolvedTime->getTimestamp() - $openedTime->getTimestamp()); + $count++; + } + $avg_time_to_resolve = secondsToTime($total / $count); + } + + ?> + +
+
+
+ + + +
+
+

Monthly ()

+
+
+
+ + + + + + + + + + + + + + + 0 || $ticket_resolved_count > 0) { + + $avg_time_to_resolve = '-'; + if ($ticket_resolved_count > 0) { + // Calculate average time to solve + $count = 0; + $total = 0; + while ($row = mysqli_fetch_array($sql_tickets)) { + $openedTime = new DateTime($row['ticket_created_at']); + $resolvedTime = new DateTime($row['ticket_resolved_at']); + + $total += ($resolvedTime->getTimestamp() - $openedTime->getTimestamp()); + $count++; + } + $avg_time_to_resolve = secondsToTime($total / $count); + } + + ?> + + + + + + + + + + + + + +
ClientTickets raisedBy priority: LowBy priority: MedBy priority: HighTickets resolvedTotal Time worked (H:M:S)Avg time to resolve
+
+ +
+
+
diff --git a/reports_side_nav.php b/reports_side_nav.php index b6bfe839..718278b7 100644 --- a/reports_side_nav.php +++ b/reports_side_nav.php @@ -70,18 +70,6 @@

Profit & Loss

- - + + + + + + + diff --git a/ticket.php b/ticket.php index b4b1502e..61dd3f9e 100644 --- a/ticket.php +++ b/ticket.php @@ -331,7 +331,7 @@ if (isset($_GET['ticket_id'])) { // The user names in a comma-separated string $ticket_collaborators = nullable_htmlentities($row['user_names']); -?> + ?> - +
- +
- +
@@ -357,84 +357,84 @@ if (isset($_GET['ticket_id'])) {
- +
-
+
@@ -453,7 +453,7 @@ if (isset($_GET['ticket_id'])) { $row = mysqli_fetch_array($sql_closed_by); $ticket_closed_by_display = nullable_htmlentities($row['user_name']); } - ?> + ?>
Closed by:
@@ -464,9 +464,9 @@ if (isset($_GET['ticket_id'])) {
-
- Feedback: -
+
+ Feedback: +
@@ -493,32 +493,32 @@ if (isset($_GET['ticket_id'])) { // Billable if ($config_module_enable_accounting) { ?> -
- Invoiced: -
+
+ Invoiced: +
- +
- Tasks Completed% -
-
/
-
+ Tasks Completed% +
+
/
+
-
- -
+
+ +
0) { ?> @@ -560,13 +560,13 @@ if (isset($_GET['ticket_id'])) {

- Ticket Details + Ticket Details

- Subject: + Subject:

@@ -589,92 +589,92 @@ if (isset($_GET['ticket_id'])) {
-
- - + + + -
-
- - - +
+
+ + + +
-
- -
- -
- -
-
-
- +
- +
+
+
+ +
+
+ + +
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ + +
+
+
+ +
+
+ + + - + +
+
- -
-
-
- -
- -
- -
- -
- -
- -
- - -
-
-
- -
-
- - - - - -
-
- -
- - -
+ +
-
Responses
+
Responses:
+ ?> -
mb-3"> + +
mb-3">
-

-
+
+ +
User Avatar @@ -730,50 +727,49 @@ if (isset($_GET['ticket_id'])) { -
- +
+

- -
- + +
+
Time worked:
- -
- modified: -
- -
-
- -
- Time worked: -
- -
-
-

- - -
-
- + +
+ +
+ +
+ + + +
+ +
+
+ +
+
@@ -787,10 +783,12 @@ if (isset($_GET['ticket_id'])) { } ?>
-
- + + + -
-
Contact
-
- - -
- - -
- -
- -
- -
- -
- -
- -
- -
- - - $ticket_id ORDER BY ticket_id DESC LIMIT 1"; - $prev_ticket_row = mysqli_fetch_assoc(mysqli_query($mysqli, $sql_prev_ticket)); - - if ($prev_ticket_row) { - $prev_ticket_id = intval($prev_ticket_row['ticket_id']); - $prev_ticket_subject = nullable_htmlentities($prev_ticket_row['ticket_subject']); - $prev_ticket_status = nullable_htmlentities( getTicketStatusName($prev_ticket_row['ticket_status'])); - ?> - -
+
+
Contact
- Previous ticket: - + +
-
- Status: - -
- -
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + + $ticket_id ORDER BY ticket_id DESC LIMIT 1"; + $prev_ticket_row = mysqli_fetch_assoc(mysqli_query($mysqli, $sql_prev_ticket)); + + if ($prev_ticket_row) { + $prev_ticket_id = intval($prev_ticket_row['ticket_id']); + $prev_ticket_subject = nullable_htmlentities($prev_ticket_row['ticket_subject']); + $prev_ticket_status = nullable_htmlentities( getTicketStatusName($prev_ticket_row['ticket_status'])); + ?> + +
+
+ Previous ticket: + +
+
+ Status: + +
+ + +
@@ -893,15 +891,15 @@ if (isset($_GET['ticket_id'])) { $task_order = intval($row['task_order']); //$task_description = nullable_htmlentities($row['task_description']); // not in db yet $task_completed_at = nullable_htmlentities($row['task_completed_at']); - ?> + ?> - + - - - + + + @@ -932,10 +930,10 @@ if (isset($_GET['ticket_id'])) { - + require "task_edit_modal.php"; + } ?>
@@ -962,13 +960,13 @@ if (isset($_GET['ticket_id'])) {
- +
- +
Asset
@@ -1036,12 +1034,12 @@ if (isset($_GET['ticket_id'])) { $service_ticket_status = nullable_htmlentities($row['ticket_status']); $service_ticket_created_at = nullable_htmlentities($row['ticket_created_at']); $service_ticket_updated_at = nullable_htmlentities($row['ticket_updated_at']); - ?> + ?>

Ticket: $service_ticket_subject ($service_ticket_status)"; ?>

-
@@ -1105,19 +1103,19 @@ if (isset($_GET['ticket_id'])) { -
-
Project
-
- - -
+
+
Project
+
+ + +
- -
- + +
+ +
+
- -
@@ -1125,26 +1123,28 @@ if (isset($_GET['ticket_id'])) {
- + +
diff --git a/ticket_bulk_merge_modal.php b/ticket_bulk_merge_modal.php index 572f6207..c0c20484 100644 --- a/ticket_bulk_merge_modal.php +++ b/ticket_bulk_merge_modal.php @@ -8,7 +8,7 @@
- +