From 2898084851149d1d14867370e86877fa5c3f2c62 Mon Sep 17 00:00:00 2001 From: ThaMunsta Date: Thu, 19 Sep 2024 23:51:17 -0400 Subject: [PATCH 001/249] Update mysql query for report_ticket_by_client.php Fix empty report --- report_ticket_by_client.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/report_ticket_by_client.php b/report_ticket_by_client.php index 02dcc48f..c4c157f1 100644 --- a/report_ticket_by_client.php +++ b/report_ticket_by_client.php @@ -130,17 +130,17 @@ $sql_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients $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'"); + $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'"); + $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'"); + $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']); From 677d80d770436d0d0a3020596dc0822eb15c777e Mon Sep 17 00:00:00 2001 From: wrongecho Date: Fri, 20 Sep 2024 10:44:22 +0100 Subject: [PATCH 002/249] Fix deleting tags --- post/tag.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post/tag.php b/post/tag.php index 91d6245d..1e37ce44 100644 --- a/post/tag.php +++ b/post/tag.php @@ -42,7 +42,7 @@ if (isset($_GET['delete_tag'])) { $tag_id = intval($_GET['delete_tag']); mysqli_query($mysqli,"DELETE FROM tags WHERE tag_id = $tag_id"); - mysqli_query($mysqli,"DELETE FROM client_tags WHERE client_tag_tag_id = $tag_id"); + mysqli_query($mysqli,"DELETE FROM client_tags WHERE tag_id = $tag_id"); //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Tag', log_action = 'Delete', log_description = '$tag_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); From 0886a6c41d7dd80837b28caf23dfd9902dea604b Mon Sep 17 00:00:00 2001 From: wrongecho Date: Fri, 20 Sep 2024 11:38:15 +0100 Subject: [PATCH 003/249] Client import - resiliency - Add some resiliency to the client import function to better account for blank fields (may also fix some import errors) - Fix the default settings page not loading due to the removal of account types --- post/client.php | 46 +++++++++++++++++++++++++++++++++++++++++-- settings_defaults.php | 2 +- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/post/client.php b/post/client.php index 548eb892..9349e0fa 100644 --- a/post/client.php +++ b/post/client.php @@ -418,66 +418,108 @@ if (isset($_POST["import_clients_csv"])) { $duplicate_detect = 1; } } + + $industry = ''; if (isset($column[1])) { $industry = sanitizeInput($column[1]); } + + $referral = ''; if (isset($column[2])) { $referral = sanitizeInput($column[2]); } + + $website = ''; if (isset($column[3])) { - $website = sanitizeInput($column[3]); + $website = sanitizeInput(preg_replace("(^https?://)", "", $column[3])); } + + $location_name = ''; if (isset($column[4])) { $location_name = sanitizeInput($column[4]); } + + $location_phone = ''; if (isset($column[5])) { - $location_phone = preg_replace("/[^0-9]/", '',$column[5]); + $location_phone = preg_replace("/[^0-9]/", '', $column[5]); } + + $address = ''; if (isset($column[6])) { $address = sanitizeInput($column[6]); } + + $city = ''; if (isset($column[7])) { $city = sanitizeInput($column[7]); } + + $state = ''; if (isset($column[8])) { $state = sanitizeInput($column[8]); } + + $zip = ''; if (isset($column[9])) { $zip = sanitizeInput($column[9]); } + + $country = ''; if (isset($column[10])) { $country = sanitizeInput($column[10]); } + + $contact_name = ''; if (isset($column[11])) { $contact_name = sanitizeInput($column[11]); } + + $title = ''; if (isset($column[12])) { $title = sanitizeInput($column[12]); } + + $contact_phone = ''; if (isset($column[13])) { $contact_phone = preg_replace("/[^0-9]/", '',$column[13]); } + + $contact_extension = ''; if (isset($column[14])) { $contact_extension = preg_replace("/[^0-9]/", '',$column[14]); } + + $contact_mobile = ''; if (isset($column[15])) { $contact_mobile = preg_replace("/[^0-9]/", '',$column[15]); } + + $contact_email = ''; if (isset($column[16])) { $contact_email = sanitizeInput($column[16]); } + + $hourly_rate = $config_default_hourly_rate; if (isset($column[17])) { $hourly_rate = floatval($column[17]); } + + $currency_code = sanitizeInput($session_company_currency); if (isset($column[18])) { $currency_code = sanitizeInput($column[18]); } + + $payment_terms = sanitizeInput($config_default_net_terms); if (isset($column[19])) { $payment_terms = intval($column[19]); } + + $tax_id_number = ''; if (isset($column[20])) { $tax_id_number = sanitizeInput($column[20]); } + + $abbreviation = ''; if (isset($column[21])) { $abbreviation = sanitizeInput($column[21]); } diff --git a/settings_defaults.php b/settings_defaults.php index 0326d351..2b799dbe 100644 --- a/settings_defaults.php +++ b/settings_defaults.php @@ -110,7 +110,7 @@ require_once "inc_all_admin.php"; From 10fafacefe7d68361fb0526c31f69ad76eba25db Mon Sep 17 00:00:00 2001 From: wrongecho Date: Fri, 20 Sep 2024 17:59:49 +0100 Subject: [PATCH 004/249] Custom Roles & Permissions Initial enforcement of custom roles & permissions - only on some pages via GET for now. --- accounts.php | 3 + admin_roles.php | 2 +- admin_side_nav.php | 6 + budget.php | 3 + check_login.php | 16 +- client_assets.php | 3 + client_certificates.php | 2 + client_documents.php | 2 + client_domains.php | 2 + client_invoices.php | 2 + client_logins.php | 3 + client_networks.php | 2 + client_payments.php | 2 + client_quotes.php | 2 + client_racks.php | 3 + client_recurring_invoices.php | 2 + client_recurring_tickets.php | 2 + client_services.php | 2 + client_side_nav.php | 401 +++++++++++++++++----------------- client_software.php | 2 + client_tickets.php | 3 + client_vendors.php | 2 + clients.php | 3 + expenses.php | 3 + functions.php | 8 +- inc_all_admin.php | 4 +- inc_all_client.php | 3 + inc_all_reports.php | 3 + invoices.php | 3 + payments.php | 2 + products.php | 3 + projects.php | 3 + quotes.php | 2 + recurring_expenses.php | 2 + recurring_invoices.php | 3 + recurring_tickets.php | 3 + report_assets.php | 3 +- reports_side_nav.php | 178 ++++++++------- revenues.php | 2 + side_nav.php | 110 +++++----- ticket.php | 3 + tickets.php | 3 + transfers.php | 3 + 43 files changed, 469 insertions(+), 345 deletions(-) diff --git a/accounts.php b/accounts.php index 9703d683..ff6cb037 100644 --- a/accounts.php +++ b/accounts.php @@ -6,6 +6,9 @@ $order = "ASC"; require_once "inc_all.php"; +// Perms +enforceUserPermission('module_financial'); + //Rebuild URL $url_query_strings_sort = http_build_query($get_copy); diff --git a/admin_roles.php b/admin_roles.php index 6a0e3f85..2a13b121 100644 --- a/admin_roles.php +++ b/admin_roles.php @@ -21,7 +21,7 @@ $sql = mysqli_query( $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); ?> -
Roles are not yet active/enforced - do not use.
+
Roles are still in development. Permissions may not be fully be enforced.
diff --git a/admin_side_nav.php b/admin_side_nav.php index 32c703bc..268e2d57 100644 --- a/admin_side_nav.php +++ b/admin_side_nav.php @@ -22,6 +22,12 @@

Users

+ + = 1) { ?> + - - + -

- - + +

+ + @@ -113,199 +112,207 @@ - + - + = 1) { ?> + - + - + - + - + - + 0) { ?> + + +

+ + - + 0) { ?> + + +

+ + - + - + + - + + - 2 && $config_module_enable_accounting == 1) { ?> + - + = 1) { ?> - + + - + - + + + = 1) { ?> + + - - - = 2 && $config_module_enable_ticketing == 1) { ?> - + = 1) { ?> - - + + = 1) { ?> + + + + + + + + - + = 1) { ?> - + = 1) { ?> - + + = 1) { ?> + + Date: Fri, 20 Sep 2024 13:01:41 -0400 Subject: [PATCH 005/249] Feature: Inititial functionality for sort order icons on table headers --- clients.php | 19 ++++++++++++++++--- pagination_head.php | 9 +++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/clients.php b/clients.php index 49cc56d2..18d7df4b 100644 --- a/clients.php +++ b/clients.php @@ -251,9 +251,22 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); "> - - - + + + diff --git a/pagination_head.php b/pagination_head.php index 600b4cb3..b92c013b 100644 --- a/pagination_head.php +++ b/pagination_head.php @@ -41,6 +41,15 @@ if (isset($_GET['order'])) { } } +// Set the order Icon +if ($sort) { + if ($order == "ASC") { + $order_icon = ""; + } else { + $order_icon = ""; + } +} + // Search if (isset($_GET['q'])) { $q = sanitizeInput($_GET['q']); From 3c9995cb01c9fb44ef0ab1a0d8d31b5220e937e4 Mon Sep 17 00:00:00 2001 From: wrongecho Date: Fri, 20 Sep 2024 18:09:00 +0100 Subject: [PATCH 006/249] Custom Roles & Permissions Initial enforcement of custom roles & permissions - only on some pages via GET for now. --- admin_roles.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin_roles.php b/admin_roles.php index 2a13b121..d332b52e 100644 --- a/admin_roles.php +++ b/admin_roles.php @@ -21,7 +21,7 @@ $sql = mysqli_query( $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); ?> -
Roles are still in development. Permissions may not be fully be enforced.
+
Roles are still in development. Permissions may not be fully enforced.
From 3d1e333ff074deb32317039185139c1b4cbeada0 Mon Sep 17 00:00:00 2001 From: wrongecho Date: Fri, 20 Sep 2024 18:17:59 +0100 Subject: [PATCH 007/249] Custom Roles & Permissions Initial enforcement of custom roles & permissions - only on some pages via GET for now. --- check_login.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/check_login.php b/check_login.php index d1be1ac3..73e52b26 100644 --- a/check_login.php +++ b/check_login.php @@ -40,7 +40,7 @@ $session_user_id = intval($_SESSION['user_id']); $sql = mysqli_query( $mysqli, - "SELECT * FROM USERS + "SELECT * FROM users LEFT JOIN user_settings ON users.user_id = user_settings.user_id LEFT JOIN user_roles ON user_settings.user_role = user_roles.user_role_id WHERE users.user_id = $session_user_id" From 8498f01f6c662da725c2c90992b194b507c99eef Mon Sep 17 00:00:00 2001 From: johnnyq Date: Fri, 20 Sep 2024 13:58:30 -0400 Subject: [PATCH 008/249] Rename pagination_head.php to filter_header.php as this is a more suitable name for its function --- pagination_head.php => filter_header.php | 2 +- inc_all.php | 2 +- inc_all_admin.php | 2 +- inc_all_client.php | 2 +- inc_all_reports.php | 2 +- inc_all_user.php | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) rename pagination_head.php => filter_header.php (99%) diff --git a/pagination_head.php b/filter_header.php similarity index 99% rename from pagination_head.php rename to filter_header.php index b92c013b..4fac5792 100644 --- a/pagination_head.php +++ b/filter_header.php @@ -1,6 +1,6 @@ diff --git a/inc_all_admin.php b/inc_all_admin.php index feb7c16a..15339593 100644 --- a/inc_all_admin.php +++ b/inc_all_admin.php @@ -20,4 +20,4 @@ require_once "inc_wrapper.php"; require_once "inc_alert_feedback.php"; -require_once "pagination_head.php"; +require_once "filter_header.php"; diff --git a/inc_all_client.php b/inc_all_client.php index 23bb4111..d8337b29 100644 --- a/inc_all_client.php +++ b/inc_all_client.php @@ -305,7 +305,7 @@ require_once "inc_alert_feedback.php"; require_once "inc_client_top_head.php"; -require_once "pagination_head.php"; +require_once "filter_header.php"; ?> diff --git a/inc_all_reports.php b/inc_all_reports.php index e3c75281..51bb53ad 100644 --- a/inc_all_reports.php +++ b/inc_all_reports.php @@ -19,7 +19,7 @@ require_once "inc_wrapper.php"; require_once "inc_alert_feedback.php"; -require_once "pagination_head.php"; +require_once "filter_header.php"; // Set variable default values $largest_income_month = 0; diff --git a/inc_all_user.php b/inc_all_user.php index 5ecd87d2..4716fdb4 100644 --- a/inc_all_user.php +++ b/inc_all_user.php @@ -16,4 +16,4 @@ require_once "inc_wrapper.php"; require_once "inc_alert_feedback.php"; -require_once "pagination_head.php"; +require_once "filter_header.php"; From 65b4f97e37698636c483a730bd145df95ffc46bf Mon Sep 17 00:00:00 2001 From: ThaMunsta Date: Fri, 20 Sep 2024 14:07:25 -0400 Subject: [PATCH 009/249] Update settings_online_payment.php Removed account type as I understand that isn't used anymore. --- settings_online_payment.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings_online_payment.php b/settings_online_payment.php index 16066f5a..94fef65a 100644 --- a/settings_online_payment.php +++ b/settings_online_payment.php @@ -50,7 +50,7 @@ require_once "inc_all_admin.php";
-
- - + - + - - - - + - From c02386f439191b6aa90abd4a9d4149f068ae1471 Mon Sep 17 00:00:00 2001 From: ThaMunsta Date: Fri, 20 Sep 2024 14:51:18 -0400 Subject: [PATCH 013/249] Update report_ticket_by_client.php Sorry I thought I had this sorted out the first time but the monthly report section was still wrong. --- report_ticket_by_client.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/report_ticket_by_client.php b/report_ticket_by_client.php index c4c157f1..5e5a4c9a 100644 --- a/report_ticket_by_client.php +++ b/report_ticket_by_client.php @@ -230,17 +230,17 @@ $sql_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients $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 MONTH(ticket_created_at) = $month AND ticket_client_id = $client_id AND ticket_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 MONTH(ticket_created_at) = $month 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 MONTH(ticket_created_at) = $month AND ticket_client_id = $client_id AND ticket_priority = 'Medium'"); + $sql_med_ticket_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS med_ticket_count FROM tickets WHERE YEAR(ticket_created_at) = $year AND MONTH(ticket_created_at) = $month 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 MONTH(ticket_created_at) = $month AND ticket_client_id = $client_id AND ticket_priority = 'High'"); + $sql_high_ticket_count = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS high_ticket_count FROM tickets WHERE YEAR(ticket_created_at) = $year AND MONTH(ticket_created_at) = $month 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']); From 3c4275e2c691959d088863d15e4302bcf5ffbd34 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Fri, 20 Sep 2024 15:14:28 -0400 Subject: [PATCH 014/249] Preappend admin_ to the settings files for consistency --- settings_ai.php => admin_settings_ai.php | 0 ..._company.php => admin_settings_company.php | 0 ...ds.php => admin_settings_custom_fields.php | 0 ...efaults.php => admin_settings_defaults.php | 0 ...ons.php => admin_settings_integrations.php | 0 ..._invoice.php => admin_settings_invoice.php | 0 ...ion.php => admin_settings_localization.php | 0 ...p => admin_settings_localization_array.php | 0 settings_mail.php => admin_settings_mail.php | 0 ...ance.php => admin_settings_maintenance.php | 0 ..._modules.php => admin_settings_modules.php | 0 ...ns.php => admin_settings_notifications.php | 0 ...t.php => admin_settings_online_payment.php | 0 ..._project.php => admin_settings_project.php | 0 ...ings_quote.php => admin_settings_quote.php | 0 ...ecurity.php => admin_settings_security.php | 0 ...emetry.php => admin_settings_telemetry.php | 0 ...ings_theme.php => admin_settings_theme.php | 0 ...gs_ticket.php => admin_settings_ticket.php | 0 admin_side_nav.php | 34 +++++++++---------- 20 files changed, 17 insertions(+), 17 deletions(-) rename settings_ai.php => admin_settings_ai.php (100%) rename settings_company.php => admin_settings_company.php (100%) rename settings_custom_fields.php => admin_settings_custom_fields.php (100%) rename settings_defaults.php => admin_settings_defaults.php (100%) rename settings_integrations.php => admin_settings_integrations.php (100%) rename settings_invoice.php => admin_settings_invoice.php (100%) rename settings_localization.php => admin_settings_localization.php (100%) rename settings_localization_array.php => admin_settings_localization_array.php (100%) rename settings_mail.php => admin_settings_mail.php (100%) rename settings_maintenance.php => admin_settings_maintenance.php (100%) rename settings_modules.php => admin_settings_modules.php (100%) rename settings_notifications.php => admin_settings_notifications.php (100%) rename settings_online_payment.php => admin_settings_online_payment.php (100%) rename settings_project.php => admin_settings_project.php (100%) rename settings_quote.php => admin_settings_quote.php (100%) rename settings_security.php => admin_settings_security.php (100%) rename settings_telemetry.php => admin_settings_telemetry.php (100%) rename settings_theme.php => admin_settings_theme.php (100%) rename settings_ticket.php => admin_settings_ticket.php (100%) diff --git a/settings_ai.php b/admin_settings_ai.php similarity index 100% rename from settings_ai.php rename to admin_settings_ai.php diff --git a/settings_company.php b/admin_settings_company.php similarity index 100% rename from settings_company.php rename to admin_settings_company.php diff --git a/settings_custom_fields.php b/admin_settings_custom_fields.php similarity index 100% rename from settings_custom_fields.php rename to admin_settings_custom_fields.php diff --git a/settings_defaults.php b/admin_settings_defaults.php similarity index 100% rename from settings_defaults.php rename to admin_settings_defaults.php diff --git a/settings_integrations.php b/admin_settings_integrations.php similarity index 100% rename from settings_integrations.php rename to admin_settings_integrations.php diff --git a/settings_invoice.php b/admin_settings_invoice.php similarity index 100% rename from settings_invoice.php rename to admin_settings_invoice.php diff --git a/settings_localization.php b/admin_settings_localization.php similarity index 100% rename from settings_localization.php rename to admin_settings_localization.php diff --git a/settings_localization_array.php b/admin_settings_localization_array.php similarity index 100% rename from settings_localization_array.php rename to admin_settings_localization_array.php diff --git a/settings_mail.php b/admin_settings_mail.php similarity index 100% rename from settings_mail.php rename to admin_settings_mail.php diff --git a/settings_maintenance.php b/admin_settings_maintenance.php similarity index 100% rename from settings_maintenance.php rename to admin_settings_maintenance.php diff --git a/settings_modules.php b/admin_settings_modules.php similarity index 100% rename from settings_modules.php rename to admin_settings_modules.php diff --git a/settings_notifications.php b/admin_settings_notifications.php similarity index 100% rename from settings_notifications.php rename to admin_settings_notifications.php diff --git a/settings_online_payment.php b/admin_settings_online_payment.php similarity index 100% rename from settings_online_payment.php rename to admin_settings_online_payment.php diff --git a/settings_project.php b/admin_settings_project.php similarity index 100% rename from settings_project.php rename to admin_settings_project.php diff --git a/settings_quote.php b/admin_settings_quote.php similarity index 100% rename from settings_quote.php rename to admin_settings_quote.php diff --git a/settings_security.php b/admin_settings_security.php similarity index 100% rename from settings_security.php rename to admin_settings_security.php diff --git a/settings_telemetry.php b/admin_settings_telemetry.php similarity index 100% rename from settings_telemetry.php rename to admin_settings_telemetry.php diff --git a/settings_theme.php b/admin_settings_theme.php similarity index 100% rename from settings_theme.php rename to admin_settings_theme.php diff --git a/settings_ticket.php b/admin_settings_ticket.php similarity index 100% rename from settings_ticket.php rename to admin_settings_ticket.php diff --git a/admin_side_nav.php b/admin_side_nav.php index 268e2d57..490d23dc 100644 --- a/admin_side_nav.php +++ b/admin_side_nav.php @@ -168,7 +168,7 @@ -
Client NamePrimary Location Primary Contact + + Client Name + + + + Primary Location + + + + Primary Contact + + + Billing Action
Number + + + Ticket + Subject / Tasks + + + Subject / Tasks + Client / Contact + + + Client / Contact + Billable - + + Billable + + Priority + + + Priority + Status - Assigned + + + Status + Last Response + + + Assigned + Created + + + Last Response + + + + Created +
"> - - + + - - + diff --git a/recurring_tickets.php b/recurring_tickets.php index 0700d77b..1518906f 100644 --- a/recurring_tickets.php +++ b/recurring_tickets.php @@ -90,11 +90,31 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - - - - - + + + + + From 23f776efa35ceb041782e030b8afcb7818860cc0 Mon Sep 17 00:00:00 2001 From: wrongecho Date: Fri, 20 Sep 2024 21:10:24 +0100 Subject: [PATCH 018/249] Permissions - clients and tickets Enforce new permissions model in clients and tickets --- client_add_modal.php | 1 + client_tickets.php | 6 ++-- clients.php | 26 +++++++------- inc_client_top_head.php | 25 +++++++------ post/client.php | 34 +++++++++++------- post/ticket.php | 80 ++++++++++++++++++++--------------------- ticket.php | 45 +++++++++++------------ ticket_edit_modal.php | 2 +- tickets.php | 8 +++-- 9 files changed, 121 insertions(+), 106 deletions(-) diff --git a/client_add_modal.php b/client_add_modal.php index db127307..ffc8a3c1 100644 --- a/client_add_modal.php +++ b/client_add_modal.php @@ -8,6 +8,7 @@
+ diff --git a/client_tickets.php b/client_tickets.php index 04d0d60e..2edc8f3b 100644 --- a/client_tickets.php +++ b/client_tickets.php @@ -119,7 +119,7 @@ $total_tickets_closed = intval($row['total_tickets_closed']);
- + = 2) { ?> @@ -237,8 +237,8 @@ $total_tickets_closed = intval($row['total_tickets_closed']); - - + + = 2) { ?> - + = 1) && $config_module_enable_accounting == 1) { ?> + = 2) { ?> @@ -387,7 +387,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
- Created: + Created:
@@ -425,8 +425,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - - + + = 1) && $config_module_enable_accounting == 1) { ?> - - + + = 2) { ?> From e0b088b76b515acfced6df17e8861c00aabcfbd9 Mon Sep 17 00:00:00 2001 From: wrongecho Date: Mon, 30 Sep 2024 21:31:04 +0100 Subject: [PATCH 051/249] Projects - Add archive and unarchive ability - Improve logic around Open > Close > Archive > Delete - Change to new perms system - Tidy and formatting - TODO: CSRF --- project_details.php | 489 ++++++++++++++++++++++---------------------- 1 file changed, 245 insertions(+), 244 deletions(-) diff --git a/project_details.php b/project_details.php index 04fe5153..c80b267a 100644 --- a/project_details.php +++ b/project_details.php @@ -22,7 +22,7 @@ if (isset($_GET['project_id'])) { } $row = mysqli_fetch_array($sql_project); - + $project_id = intval($row['project_id']); $project_prefix = nullable_htmlentities($row['project_prefix']); $project_number = intval($row['project_number']); @@ -50,7 +50,7 @@ if (isset($_GET['project_id'])) { $project_manager_display = "-"; } - if($project_completed_at) { + if ($project_completed_at) { $project_status_display = "Closed"; $project_completed_date_display = "
" . date('Y-m-d', strtotime($project_completed_at)) . "
"; } else { @@ -96,7 +96,7 @@ if (isset($_GET['project_id'])) { $completed_task_count = mysqli_num_rows($sql_tasks_completed); // Tasks Completed Percent - if($task_count) { + if ($task_count) { $tasks_completed_percent = round(($completed_task_count / $task_count) * 100); } @@ -121,290 +121,291 @@ if (isset($_GET['project_id'])) { // The user names in a comma-separated string $ticket_collaborators = nullable_htmlentities($row['user_names']); - + ?> - - + + - -
-
-
-
- -
-

$project_name"; ?>

-
+ +
+
+
+
+ +
+

$project_name"; ?>

+
+
-
-
-
-
-
-
- - -
- Total time worked: -
- -
- -
- -
- -
/
-
- - -
- -
/
-
- - -
- -
- -
- -
-
- - - Close - - - +
+
+
+
+
+ + +
+ Total time worked: +
-
-
-
-
- - - 0) { ?> -
+
+
-
Project Tickets
-
-
NumberProject + + Number + + + + Project + + Tickets / TasksDue + + Due + + Completed ClientSubjectPriorityFrequencyNext Run Date + + Client + + + + Subject + + + + Priority + + + + Frequency + + + + Next Run Date + + Action
Number Subject ContactBillable Priority

Management

Billing Action Billing Action
Balance @@ -443,8 +443,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
- + = 2) { ?> - -
+ = 1 && $config_module_enable_accounting == 1) { ?> +
Billing
Hourly Rate @@ -141,8 +143,8 @@
- -
+ = 1 && $config_module_enable_ticketing == 1) { ?> +
Support
Open Tickets @@ -156,6 +158,7 @@
+
diff --git a/post/client.php b/post/client.php index 9349e0fa..83ee15ce 100644 --- a/post/client.php +++ b/post/client.php @@ -6,11 +6,11 @@ if (isset($_POST['add_client'])) { + validateCSRFToken($_POST['csrf_token']); + enforceUserPermission('module_client', 2); + require_once 'post/client_model.php'; - - validateAdminRole(); - $location_phone = preg_replace("/[^0-9]/", '', $_POST['location_phone']); $address = sanitizeInput($_POST['address']); $city = sanitizeInput($_POST['city']); @@ -117,9 +117,9 @@ if (isset($_POST['add_client'])) { if (isset($_POST['edit_client'])) { - require_once 'post/client_model.php'; + enforceUserPermission('module_client', 2); - validateAdminRole(); + require_once 'post/client_model.php'; $client_id = intval($_POST['client_id']); @@ -153,7 +153,8 @@ if (isset($_POST['edit_client'])) { if (isset($_GET['archive_client'])) { - validateAdminRole(); + validateCSRFToken($_GET['csrf_token']); + enforceUserPermission('module_client', 2); $client_id = intval($_GET['archive_client']); @@ -175,6 +176,8 @@ if (isset($_GET['archive_client'])) { if (isset($_GET['undo_archive_client'])) { + enforceUserPermission('module_client', 2); + $client_id = intval($_GET['undo_archive_client']); // Get Client Name @@ -194,10 +197,8 @@ if (isset($_GET['undo_archive_client'])) { if (isset($_GET['delete_client'])) { - validateAdminRole(); - - // CSRF Check validateCSRFToken($_GET['csrf_token']); + enforceUserPermission('module_client', 3); $client_id = intval($_GET['delete_client']); @@ -337,6 +338,8 @@ if (isset($_GET['delete_client'])) { if (isset($_POST['export_clients_csv'])) { + enforceUserPermission('module_client', 1); + //get records from database $sql = mysqli_query($mysqli, "SELECT * FROM clients LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1 @@ -377,7 +380,7 @@ if (isset($_POST['export_clients_csv'])) { if (isset($_POST["import_clients_csv"])) { - validateTechRole(); + enforceUserPermission('module_client', 2); $file_name = $_FILES["file"]["tmp_name"]; $error = false; @@ -604,14 +607,21 @@ if (isset($_GET['download_clients_csv_template'])) { if (isset($_POST['export_client_pdf'])) { - validateAdminRole(); + // TODO: Enforce perms based on which individual boxes are ticked + enforceUserPermission('module_client', 3); + enforceUserPermission('module_support', 1); + enforceUserPermission('module_sales', 1); + enforceUserPermission('module_financial', 1); $client_id = intval($_POST['client_id']); $export_contacts = intval($_POST['export_contacts']); $export_locations = intval($_POST['export_locations']); $export_assets = intval($_POST['export_assets']); $export_software = intval($_POST['export_software']); - $export_logins = intval($_POST['export_logins']); + $export_logins = 0; + if (lookupUserPermission("module_credential") >= 1) { + $export_logins = intval($_POST['export_logins']); + } $export_networks = intval($_POST['export_networks']); $export_certificates = intval($_POST['export_certificates']); $export_domains = intval($_POST['export_domains']); diff --git a/post/ticket.php b/post/ticket.php index 9b799e7c..35230b04 100644 --- a/post/ticket.php +++ b/post/ticket.php @@ -6,7 +6,7 @@ if (isset($_POST['add_ticket'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $client_id = intval($_POST['client']); $assigned_to = intval($_POST['assigned_to']); @@ -186,7 +186,7 @@ if (isset($_POST['add_ticket'])) { if (isset($_POST['edit_ticket'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $ticket_id = intval($_POST['ticket_id']); $contact_id = intval($_POST['contact']); @@ -269,7 +269,7 @@ if (isset($_POST['edit_ticket'])) { if (isset($_POST['edit_ticket_priority'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $ticket_id = intval($_POST['ticket_id']); $priority = sanitizeInput($_POST['priority']); @@ -287,7 +287,7 @@ if (isset($_POST['edit_ticket_priority'])) { if (isset($_POST['edit_ticket_contact'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $ticket_id = intval($_POST['ticket_id']); $contact_id = intval($_POST['contact']); @@ -360,7 +360,7 @@ if (isset($_POST['edit_ticket_contact'])) { if (isset($_POST['add_ticket_watcher'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $ticket_id = intval($_POST['ticket_id']); $client_id = intval($_POST['client_id']); @@ -431,7 +431,7 @@ if (isset($_POST['add_ticket_watcher'])) { if (isset($_POST['edit_ticket_watchers'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $ticket_id = intval($_POST['ticket_id']); $client_id = intval($_POST['client_id']); @@ -460,7 +460,7 @@ if (isset($_POST['edit_ticket_watchers'])) { if (isset($_GET['delete_ticket_watcher'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $watcher_id = intval($_GET['delete_ticket_watcher']); @@ -474,7 +474,7 @@ if (isset($_GET['delete_ticket_watcher'])) { if (isset($_POST['edit_ticket_asset'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $ticket_id = intval($_POST['ticket_id']); $asset_id = intval($_POST['asset']); @@ -493,7 +493,7 @@ if (isset($_POST['edit_ticket_asset'])) { if (isset($_POST['edit_ticket_vendor'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $ticket_id = intval($_POST['ticket_id']); $vendor_id = intval($_POST['vendor']); @@ -512,7 +512,7 @@ if (isset($_POST['edit_ticket_vendor'])) { if (isset($_POST['edit_ticket_priority'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $ticket_id = intval($_POST['ticket_id']); $priority = sanitizeInput($_POST['priority']); @@ -530,8 +530,7 @@ if (isset($_POST['edit_ticket_priority'])) { if (isset($_POST['assign_ticket'])) { - // Role check - validateTechRole(); + enforceUserPermission('module_support', 2); // POST variables $ticket_id = intval($_POST['ticket_id']); @@ -630,7 +629,7 @@ if (isset($_POST['assign_ticket'])) { if (isset($_GET['delete_ticket'])) { - validateAdminRole(); + enforceUserPermission('module_support', 3); // CSRF Check validateCSRFToken($_GET['csrf_token']); @@ -668,8 +667,7 @@ if (isset($_GET['delete_ticket'])) { if (isset($_POST['bulk_assign_ticket'])) { - // Role check - validateTechRole(); + enforceUserPermission('module_support', 2); // POST variables $assign_to = intval($_POST['assign_to']); @@ -769,8 +767,7 @@ if (isset($_POST['bulk_assign_ticket'])) { if (isset($_POST['bulk_edit_ticket_priority'])) { - // Role check - validateTechRole(); + enforceUserPermission('module_support', 2); // POST variables $priority = sanitizeInput($_POST['bulk_priority']); @@ -809,8 +806,7 @@ if (isset($_POST['bulk_edit_ticket_priority'])) { if (isset($_POST['bulk_merge_tickets'])) { - // Role check - validateTechRole(); + enforceUserPermission('module_support', 2); $ticket_count = count($_POST['ticket_ids']); // Get a ticket count $merge_into_ticket_number = intval($_POST['merge_into_ticket_number']); // Parent ticket *number* @@ -868,8 +864,7 @@ if (isset($_POST['bulk_merge_tickets'])) { if (isset($_POST['bulk_resolve_tickets'])) { - // Role check - validateTechRole(); + enforceUserPermission('module_support', 2); // POST variables $details = mysqli_escape_string($mysqli, $_POST['bulk_details']); @@ -979,8 +974,7 @@ if (isset($_POST['bulk_resolve_tickets'])) { if (isset($_POST['bulk_ticket_reply'])) { - // Role check - validateTechRole(); + enforceUserPermission('module_support', 2); // POST variables $ticket_reply = mysqli_escape_string($mysqli, $_POST['bulk_reply_details']); @@ -1117,8 +1111,7 @@ if (isset($_POST['bulk_ticket_reply'])) { // Currenly not UI Frontend for this if (isset($_POST['bulk_add_ticket_project'])) { - // Role check - validateTechRole(); + enforceUserPermission('module_support', 2); // POST variables $project_id = intval($_POST['project_id']); @@ -1161,7 +1154,7 @@ if (isset($_POST['bulk_add_ticket_project'])) { if (isset($_POST['add_ticket_reply'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $ticket_id = intval($_POST['ticket_id']); $ticket_reply = mysqli_real_escape_string($mysqli, $_POST['ticket_reply']); @@ -1306,7 +1299,7 @@ if (isset($_POST['add_ticket_reply'])) { if (isset($_POST['edit_ticket_reply'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $ticket_reply_id = intval($_POST['ticket_reply_id']); $ticket_reply = mysqli_real_escape_string($mysqli, $_POST['ticket_reply']); @@ -1327,7 +1320,7 @@ if (isset($_POST['edit_ticket_reply'])) { if (isset($_GET['archive_ticket_reply'])) { - validateAdminRole(); + enforceUserPermission('module_support', 2); $ticket_reply_id = intval($_GET['archive_ticket_reply']); @@ -1344,7 +1337,7 @@ if (isset($_GET['archive_ticket_reply'])) { if (isset($_POST['merge_ticket'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $ticket_id = intval($_POST['ticket_id']); // Child ticket ID to be closed $merge_into_ticket_number = intval($_POST['merge_into_ticket_number']); // Parent ticket *number* @@ -1407,7 +1400,7 @@ if (isset($_POST['merge_ticket'])) { if (isset($_POST['change_client_ticket'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $ticket_id = intval($_POST['ticket_id']); $client_id = intval($_POST['new_client_id']); @@ -1429,7 +1422,7 @@ if (isset($_POST['change_client_ticket'])) { if (isset($_GET['resolve_ticket'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); // CSRF Check validateCSRFToken($_GET['csrf_token']); @@ -1521,7 +1514,7 @@ if (isset($_GET['resolve_ticket'])) { if (isset($_GET['close_ticket'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); // CSRF Check validateCSRFToken($_GET['csrf_token']); @@ -1612,7 +1605,7 @@ if (isset($_GET['close_ticket'])) { if (isset($_GET['reopen_ticket'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $ticket_id = intval($_GET['reopen_ticket']); @@ -1627,6 +1620,9 @@ if (isset($_GET['reopen_ticket'])) { if (isset($_POST['add_invoice_from_ticket'])) { + enforceUserPermission('module_support', 2); + enforceUserPermission('module_sales', 2); + $invoice_id = intval($_POST['invoice_id']); $ticket_id = intval($_POST['ticket_id']); $date = sanitizeInput($_POST['date']); @@ -1727,7 +1723,7 @@ if (isset($_POST['add_invoice_from_ticket'])) { if (isset($_POST['export_client_tickets_csv'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $client_id = intval($_POST['client_id']); @@ -1776,7 +1772,7 @@ if (isset($_POST['export_client_tickets_csv'])) { if (isset($_POST['add_recurring_ticket'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); require_once 'post/recurring_ticket_model.php'; @@ -1804,7 +1800,7 @@ if (isset($_POST['add_recurring_ticket'])) { if (isset($_POST['edit_recurring_ticket'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); require_once 'post/recurring_ticket_model.php'; @@ -1831,7 +1827,7 @@ if (isset($_POST['edit_recurring_ticket'])) { if (isset($_GET['delete_recurring_ticket'])) { - validateAdminRole(); + enforceUserPermission('module_support', 3); $scheduled_ticket_id = intval($_GET['delete_recurring_ticket']); @@ -1855,7 +1851,8 @@ if (isset($_GET['delete_recurring_ticket'])) { } if (isset($_POST['bulk_delete_scheduled_tickets']) || isset($_POST['bulk_delete_recurring_tickets'])) { - validateAdminRole(); + + enforceUserPermission('module_support', 3); validateCSRFToken($_POST['csrf_token']); $count = 0; // Default 0 @@ -1884,7 +1881,8 @@ if (isset($_POST['bulk_delete_scheduled_tickets']) || isset($_POST['bulk_delete_ if (isset($_POST['set_billable_status'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); + enforceUserPermission('module_sales', 2); $ticket_id = intval($_POST['ticket_id']); $billable_status = sanitizeInput($_POST['billable_status']); @@ -1916,7 +1914,7 @@ if (isset($_POST['set_billable_status'])) { if (isset($_POST['edit_ticket_schedule'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $ticket_id = intval($_POST['ticket_id']); $onsite = intval($_POST['onsite']); @@ -2094,7 +2092,7 @@ if (isset($_POST['edit_ticket_schedule'])) { if (isset($_GET['cancel_ticket_schedule'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $ticket_id = intval($_GET['cancel_ticket_schedule']); diff --git a/ticket.php b/ticket.php index db1b1a48..8785e406 100644 --- a/ticket.php +++ b/ticket.php @@ -361,10 +361,11 @@ if (isset($_GET['ticket_id'])) {
-
+ = 2) { ?> +
- + = 2) { ?> Invoice @@ -376,7 +377,6 @@ if (isset($_GET['ticket_id'])) { Reopen -   @@ -421,7 +421,7 @@ if (isset($_GET['ticket_id'])) { Change Client - + Delete @@ -432,6 +432,7 @@ if (isset($_GET['ticket_id'])) {
+
@@ -495,11 +496,11 @@ if (isset($_GET['ticket_id'])) { // Billable if ($config_module_enable_accounting) { ?> - + = 1) { ?>
Invoiced:
- + = 1) { ?>
- + = 2 && empty($ticket_resolved_at) && empty($ticket_closed_at)) { ?>
@@ -747,20 +748,20 @@ if (isset($_GET['ticket_id'])) { @@ -870,7 +871,7 @@ if (isset($_GET['ticket_id'])) {
- + = 2) { ?>
@@ -899,7 +900,7 @@ if (isset($_GET['ticket_id'])) {
- + = 2) { ?> @@ -908,7 +909,7 @@ if (isset($_GET['ticket_id'])) {
- + = 2) { ?> = 2 && empty($ticket_closed_at)) { require_once "ticket_edit_modal.php"; require_once "ticket_assign_modal.php"; @@ -1149,7 +1150,7 @@ if (isset($_GET['ticket_id'])) { require_once "ticket_merge_modal.php"; } - if ($config_module_enable_accounting) { + if (lookupUserPermission("module_support") >= 2 && lookupUserPermission("module_sales") >= 2 && $config_module_enable_accounting) { require_once "ticket_edit_billable_modal.php"; require_once "ticket_invoice_add_modal.php"; } diff --git a/ticket_edit_modal.php b/ticket_edit_modal.php index 092819a1..62f02cb6 100644 --- a/ticket_edit_modal.php +++ b/ticket_edit_modal.php @@ -98,7 +98,7 @@
- + = 2) { ?>
value="1" id="billableSwitch"> diff --git a/tickets.php b/tickets.php index 030df183..88610a07 100644 --- a/tickets.php +++ b/tickets.php @@ -154,7 +154,8 @@ $user_active_assigned_tickets = intval($row['total_tickets_assigned']); Unassigned Tickets | - +
@@ -328,7 +330,7 @@ $user_active_assigned_tickets = intval($row['total_tickets_assigned']); - + = 2) { ?>
Billable @@ -514,7 +516,7 @@ $user_active_assigned_tickets = intval($row['total_tickets_assigned']); - + = 2) { ?> Date: Fri, 20 Sep 2024 16:13:38 -0400 Subject: [PATCH 019/249] Add visual sort order cues to quotes --- quotes.php | 48 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/quotes.php b/quotes.php index b0704adb..f30624bd 100644 --- a/quotes.php +++ b/quotes.php @@ -89,14 +89,46 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); "> - - - - - - - - + + + + + + + + From b658cf09dce7642d00e5918abd2fe7771adb16c2 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Fri, 20 Sep 2024 16:15:05 -0400 Subject: [PATCH 020/249] Opps settings_localization_array.php should have not have amin_ prepended reverting --- ...ings_localization_array.php => settings_localization_array.php | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename admin_settings_localization_array.php => settings_localization_array.php (100%) diff --git a/admin_settings_localization_array.php b/settings_localization_array.php similarity index 100% rename from admin_settings_localization_array.php rename to settings_localization_array.php From b6e4d6dd3472ad1e1b76c75f260853d6395114ac Mon Sep 17 00:00:00 2001 From: wrongecho Date: Fri, 20 Sep 2024 21:40:05 +0100 Subject: [PATCH 021/249] Temp revert new admin check to allow DB updates --- inc_all_admin.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/inc_all_admin.php b/inc_all_admin.php index 15339593..500af82b 100644 --- a/inc_all_admin.php +++ b/inc_all_admin.php @@ -6,9 +6,10 @@ require_once "functions.php"; require_once "check_login.php"; -if (!isset($session_is_admin) || !$session_is_admin) { - exit(WORDING_ROLECHECK_FAILED . "
Tell your admin: Your role does not have admin access."); -} +validateAdminRole(); +//if (!isset($session_is_admin) || !$session_is_admin) { +// exit(WORDING_ROLECHECK_FAILED . "
Tell your admin: Your role does not have admin access."); +//} require_once "header.php"; From f9897e811ceb591077efc513e37513d34bf51266 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Fri, 20 Sep 2024 16:45:45 -0400 Subject: [PATCH 022/249] Added Sort order visual cue to invoices --- invoices.php | 48 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/invoices.php b/invoices.php index 5aafab69..de38e53e 100644 --- a/invoices.php +++ b/invoices.php @@ -209,14 +209,46 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
NumberScopeClientAmountDateExpireCategoryStatus + + Number + + + + Scope + + + + Client + + + + Amount + + + + Date + + + + Expire + + + + Category + + + + Status + + Action
"> - - - - - - - - + + + + + + + + From 2e74e1756cc7e493c04792748a2bd88dbbcc74b1 Mon Sep 17 00:00:00 2001 From: wrongecho Date: Fri, 20 Sep 2024 21:51:51 +0100 Subject: [PATCH 023/249] Show DB updates before git updates --- admin_update.php | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/admin_update.php b/admin_update.php index e41c4b31..01bd4325 100644 --- a/admin_update.php +++ b/admin_update.php @@ -32,24 +32,27 @@ $git_log = shell_exec("git log $repo_branch..origin/$repo_branch --pretty=format - -
Update App
-
FORCE Update App
+ CURRENT_DATABASE_VERSION) { ?> +
+ Ensure you have a current app & database backup before updating! +
+
+
Update Database
+
+ Current DB Version: +
+ Latest DB Version: +
+ Branch: +
CURRENT_DATABASE_VERSION) { ?> -
- Ensure you have a current app & database backup before updating! -
-
-
Update Database
-
- Current DB Version: -
- Latest DB Version: -
- Branch: + if (!empty($git_log)) { ?> + +
Update App
+
FORCE Update App
+

Current Database Version:

Current App Version:

From 9d0f55342235618931ad8d7855c29f69487aac3b Mon Sep 17 00:00:00 2001 From: wrongecho <32306651+wrongecho@users.noreply.github.com> Date: Fri, 20 Sep 2024 23:32:05 +0100 Subject: [PATCH 024/249] Update CODE_OF_CONDUCT.md rm discord --- CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 6e155a5f..1015954e 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -60,7 +60,7 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at -the forums / Discord. +the forums. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the From 04f09053970508e774cbd4e38bd47b17ff97234f Mon Sep 17 00:00:00 2001 From: johnnyq Date: Fri, 20 Sep 2024 20:00:21 -0400 Subject: [PATCH 025/249] Added sort order visual cues to every listing --- accounts.php | 12 +- admin_api.php | 30 +++- admin_categories.php | 10 +- admin_custom_links.php | 24 +++- admin_document_templates.php | 248 +++++++++++++++++----------------- admin_logs.php | 50 +++++-- admin_mail_queue.php | 42 +++++- admin_project_templates.php | 6 +- admin_roles.php | 27 +++- admin_software_templates.php | 18 ++- admin_tags.php | 12 +- admin_taxes.php | 12 +- admin_ticket_statuses.php | 18 ++- admin_ticket_templates.php | 6 +- admin_users.php | 28 +++- admin_vendor_templates.php | 12 +- client_assets.php | 101 +++++++++----- client_certificates.php | 24 +++- client_contacts.php | 15 +- client_documents.php | 16 ++- client_domains.php | 36 ++++- client_files.php | 12 +- client_invoices.php | 59 +++++--- client_locations.php | 24 +++- client_logins.php | 12 +- client_networks.php | 44 ++++-- client_payments.php | 48 +++++-- client_quotes.php | 42 +++++- client_recurring_invoices.php | 48 +++++-- client_recurring_tickets.php | 24 +++- client_software.php | 30 +++- client_tickets.php | 63 +++++++-- client_trips.php | 36 ++++- client_vendors.php | 6 +- expenses.php | 42 +++++- notifications_dismissed.php | 31 +---- payments.php | 48 +++++-- products.php | 37 ++++- recurring_expenses.php | 54 ++++++-- recurring_invoices.php | 54 ++++++-- report_assets.php | 106 ++++++++++----- report_domains.php | 42 +++++- revenues.php | 36 ++++- transfers.php | 36 ++++- trips.php | 42 +++++- vendors.php | 18 ++- 46 files changed, 1298 insertions(+), 443 deletions(-) diff --git a/accounts.php b/accounts.php index ff6cb037..f23a0f7e 100644 --- a/accounts.php +++ b/accounts.php @@ -45,8 +45,16 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
NumberScopeClientAmountDateDueCategoryStatus + + Number + + + + Scope + + + + Client + + + + Amount + + + + Date + + + + Due + + + + Category + + + + Status + + Action
"> - - + + diff --git a/admin_api.php b/admin_api.php index d5c8ae3e..285cae68 100644 --- a/admin_api.php +++ b/admin_api.php @@ -78,11 +78,31 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - - - - - + + + + + diff --git a/admin_categories.php b/admin_categories.php index c747ba79..0f75c33a 100644 --- a/admin_categories.php +++ b/admin_categories.php @@ -113,12 +113,12 @@ if (isset($_GET['archived'])) {
NameCurrency + + Name + + + + Currency + + Balance Action
NameClientSecretCreatedExpires + + Name + + + + Client + + + + Secret + + + + Created + + + + Expires + + Action
- "> + "> - diff --git a/admin_custom_links.php b/admin_custom_links.php index cef9d684..6ae720b1 100644 --- a/admin_custom_links.php +++ b/admin_custom_links.php @@ -50,10 +50,26 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
Name + + + Name + Color Action
"> - - - - + + + + diff --git a/admin_document_templates.php b/admin_document_templates.php index 2fe38285..7ba8f666 100644 --- a/admin_document_templates.php +++ b/admin_document_templates.php @@ -1,139 +1,139 @@
-
-

Document Templates

-
- -
-
-
- - -
- -
- +
+

Document Templates

+
+
-
- -
- -
-
NameOrderURI / New TabLocation + + Name + + + + Order + + + + URI / New Tab + + + + Location + + Action
- "> - - - - - - - - - - - - - - - - - - - - -
- Template Name - - Created - - Updated - - Action -
- -
-
- -
-
- -
-
- - +
+ +
+
+ +
+ +
+
+
+
+ +
+ + "> + + + + + + + + + + + + + + + + + + + + +
+ + Template Name + + + + Created + + + + Updated + + + Action +
+ +
+
+ +
+
+ +
+
+
+ +
- - - - + + diff --git a/admin_logs.php b/admin_logs.php index 124968ef..836f478c 100644 --- a/admin_logs.php +++ b/admin_logs.php @@ -200,16 +200,48 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); "> - - - - + + + + - - - - - + + + + + diff --git a/admin_mail_queue.php b/admin_mail_queue.php index 3ba993f6..9e21d092 100644 --- a/admin_mail_queue.php +++ b/admin_mail_queue.php @@ -102,13 +102,41 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - - - - - - - + + + + + + + diff --git a/admin_project_templates.php b/admin_project_templates.php index 779f948a..8dad0bbf 100644 --- a/admin_project_templates.php +++ b/admin_project_templates.php @@ -52,7 +52,11 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
TimestampUserClient + + Timestamp + + + + User + + + + Client + + TypeActionDescriptionIP AddressUser Agent + + Type + + + + Action + + + + Description + + + + IP Address + + + + User Agent + +
IDQueuedFromToSubjectStatusAttempts + + ID + + + + Queued + + + + From + + + + To + + + + Subject + + + + Status + + + + Attempts + + Action
"> - + diff --git a/admin_roles.php b/admin_roles.php index d332b52e..9d592db3 100644 --- a/admin_roles.php +++ b/admin_roles.php @@ -52,11 +52,24 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
Template + + Template + + Tickets Tasks Action
"> - - - - - + + + + @@ -76,14 +89,14 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); ?> - - +
NameDescriptionAdminUser count + + Name + + + + Description + + + + Admin + + + User count + Action
+
@@ -87,7 +87,7 @@ $sql_recent_logs = mysqli_query($mysqli, "SELECT * FROM logs
From 0c0cf2f427a265a24df6d2b8e02a76146c4bb6a2 Mon Sep 17 00:00:00 2001 From: Marcus Hill Date: Sun, 29 Sep 2024 19:08:59 +0100 Subject: [PATCH 045/249] Refactor POST handling. - Split into admin and user handlers, each admin page gets its own file now - Enforce role access once for admin POST requests - Automatically load POST logic for admin-based requests based on the referring page, otherwise automatically load all user request logic - Add support for using custom POST handlers --- post/admin/admin_settings_module.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post/admin/admin_settings_module.php b/post/admin/admin_settings_module.php index 8f572b7d..4d231c0a 100644 --- a/post/admin/admin_settings_module.php +++ b/post/admin/admin_settings_module.php @@ -6,7 +6,7 @@ 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']; + $config_whitelabel_key = sanitizeInput($_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"); From a414f279b119c76abdfb31c85e1d5181f5d52a8c Mon Sep 17 00:00:00 2001 From: wrongecho Date: Sun, 29 Sep 2024 21:35:33 +0100 Subject: [PATCH 046/249] Fix admin nav modules --- admin_side_nav.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin_side_nav.php b/admin_side_nav.php index 1a0d1aa0..9ee3171c 100644 --- a/admin_side_nav.php +++ b/admin_side_nav.php @@ -267,7 +267,7 @@
- - - - - - - - - - - - + 0) { ?> +
- while ($row = mysqli_fetch_array($sql_tickets)) { - $ticket_id = intval($row['ticket_id']); - $ticket_prefix = nullable_htmlentities($row['ticket_prefix']); - $ticket_number = nullable_htmlentities($row['ticket_number']); - $ticket_subject = nullable_htmlentities($row['ticket_subject']); - $ticket_priority = nullable_htmlentities($row['ticket_priority']); - $ticket_status = intval($row['ticket_status']); - $ticket_status_name = nullable_htmlentities($row['ticket_status_name']); - $ticket_status_color = nullable_htmlentities($row['ticket_status_color']); - $ticket_billable = intval($row['ticket_billable']); - $ticket_created_at = nullable_htmlentities($row['ticket_created_at']); - $ticket_created_at_time_ago = timeAgo($row['ticket_created_at']); - $ticket_updated_at = nullable_htmlentities($row['ticket_updated_at']); - $ticket_updated_at_time_ago = timeAgo($row['ticket_updated_at']); - if (empty($ticket_updated_at)) { - if ($ticket_status == 5) { - $ticket_updated_at_display = "

Never

"; +
Project Tickets
+
+
TicketPriorityStatusAssignedLast ResponseClient
+ + + + + + + + + + + + Never

"; + } else { + $ticket_updated_at_display = "

Never

"; + } } else { - $ticket_updated_at_display = "

Never

"; + $ticket_updated_at_display = "$ticket_updated_at_time_ago
$ticket_updated_at"; } - } else { - $ticket_updated_at_display = "$ticket_updated_at_time_ago
$ticket_updated_at"; - } - $ticket_closed_at = nullable_htmlentities($row['ticket_closed_at']); + $ticket_closed_at = nullable_htmlentities($row['ticket_closed_at']); - if ($ticket_priority == "High") { - $ticket_priority_display = "$ticket_priority"; - } elseif ($ticket_priority == "Medium") { - $ticket_priority_display = "$ticket_priority"; - } elseif ($ticket_priority == "Low") { - $ticket_priority_display = "$ticket_priority"; - } else{ - $ticket_priority_display = "-"; - } + if ($ticket_priority == "High") { + $ticket_priority_display = "$ticket_priority"; + } elseif ($ticket_priority == "Medium") { + $ticket_priority_display = "$ticket_priority"; + } elseif ($ticket_priority == "Low") { + $ticket_priority_display = "$ticket_priority"; + } else{ + $ticket_priority_display = "-"; + } - $ticket_assigned_to = intval($row['ticket_assigned_to']); - if (empty($ticket_assigned_to)) { - if ($ticket_status == 5) { - $ticket_assigned_to_display = "

Not Assigned

"; + $ticket_assigned_to = intval($row['ticket_assigned_to']); + if (empty($ticket_assigned_to)) { + if ($ticket_status == 5) { + $ticket_assigned_to_display = "

Not Assigned

"; + } else { + $ticket_assigned_to_display = "

Not Assigned

"; + } } else { - $ticket_assigned_to_display = "

Not Assigned

"; + $ticket_assigned_to_display = nullable_htmlentities($row['user_name']); } - } else { - $ticket_assigned_to_display = nullable_htmlentities($row['user_name']); - } - - $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']); - if (empty($contact_archived_at)) { - $contact_archived_display = ""; - } else { - $contact_archived_display = "Archived - "; - } - if (empty($contact_name)) { - $contact_display = "-"; - } else { - $contact_display = "$contact_archived_display$contact_name
$contact_email"; - } + $project_id = intval($row['ticket_project_id']); - // Get who last updated the ticket - to be shown in the last Response column - $ticket_reply_type = "Client"; // Default to client for unreplied tickets - $ticket_reply_by_display = ""; // Default none - $sql_ticket_reply = mysqli_query($mysqli, "SELECT ticket_reply_type, contact_name, user_name FROM ticket_replies + $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']); + if (empty($contact_archived_at)) { + $contact_archived_display = ""; + } else { + $contact_archived_display = "Archived - "; + } + if (empty($contact_name)) { + $contact_display = "-"; + } else { + $contact_display = "$contact_archived_display$contact_name
$contact_email"; + } + + // Get who last updated the ticket - to be shown in the last Response column + $ticket_reply_type = "Client"; // Default to client for unreplied tickets + $ticket_reply_by_display = ""; // Default none + $sql_ticket_reply = mysqli_query($mysqli, "SELECT ticket_reply_type, contact_name, user_name FROM ticket_replies LEFT JOIN users ON ticket_reply_by = user_id LEFT JOIN contacts ON ticket_reply_by = contact_id WHERE ticket_reply_ticket_id = $ticket_id AND ticket_reply_archived_at IS NULL ORDER BY ticket_reply_id DESC LIMIT 1" - ); - $row = mysqli_fetch_array($sql_ticket_reply); + ); + $row = mysqli_fetch_array($sql_ticket_reply); - if ($row) { - $ticket_reply_type = nullable_htmlentities($row['ticket_reply_type']); - if ($ticket_reply_type == "Client") { - $ticket_reply_by_display = nullable_htmlentities($row['contact_name']); - } else { - $ticket_reply_by_display = nullable_htmlentities($row['user_name']); + if ($row) { + $ticket_reply_type = nullable_htmlentities($row['ticket_reply_type']); + if ($ticket_reply_type == "Client") { + $ticket_reply_by_display = nullable_htmlentities($row['contact_name']); + } else { + $ticket_reply_by_display = nullable_htmlentities($row['user_name']); + } } - } + ?> + + + + + + + + + + + + + + + + + + + + + + + +
TicketPriorityStatusAssignedLast ResponseClient
+ + + + + + + +
+
+
+
+
+ + + +
+ + + 0) { ?> +
+
All Tasks
+ + - - - - - - - - - - - - - - - - - - -
- - - - + + + + + + + + - - -
-
-
-
- - + + -
- - - 0) { ?> -
-
All Tasks
- - - - - - -
- - - - - - - - -
-
- - +
- + - + Date: Mon, 30 Sep 2024 19:07:01 -0500 Subject: [PATCH 052/249] Changed regex to only exclude from postmaster or daemon. --- cron_ticket_email_parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cron_ticket_email_parser.php b/cron_ticket_email_parser.php index a8aa9183..63178223 100644 --- a/cron_ticket_email_parser.php +++ b/cron_ticket_email_parser.php @@ -477,7 +477,7 @@ if ($messages->count() > 0) { } } elseif ($config_ticket_email_parse_unknown_senders) { // Parse even if the sender is unknown - $bad_from_pattern = "/daemon|postmaster|reply|root|admin/i"; + $bad_from_pattern = "/daemon|postmaster/i"; if (!(preg_match($bad_from_pattern, $from_email))) { if (addTicket(0, $from_name, $from_email, 0, $date, $subject, $message_body, $message->getAttachments(), $original_message_file)) { $email_processed = true; From 7ccfad80de149a6c5a9d828dfb384ed1fe4dc9aa Mon Sep 17 00:00:00 2001 From: johnnyq Date: Tue, 1 Oct 2024 12:09:42 -0400 Subject: [PATCH 053/249] Add Client name to the paid notification when client pays via stripe --- guest_pay_invoice_stripe.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guest_pay_invoice_stripe.php b/guest_pay_invoice_stripe.php index d3eb6b72..0f855533 100644 --- a/guest_pay_invoice_stripe.php +++ b/guest_pay_invoice_stripe.php @@ -282,7 +282,7 @@ if (isset($_GET['invoice_id'], $_GET['url_key']) && !isset($_GET['payment_intent mysqli_query($mysqli, "INSERT INTO history SET history_status = 'Paid', history_description = 'Payment added - $ip - $os - $browser', history_invoice_id = $invoice_id"); // Notify - mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Invoice Paid', notification = 'Invoice $invoice_prefix$invoice_number has been paid - $ip - $os - $browser', notification_action = 'invoice.php?invoice_id=$invoice_id', notification_client_id = $pi_client_id"); + mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Invoice Paid', notification = 'Invoice $invoice_prefix$invoice_number has been paid by $client_name - $ip - $os - $browser', notification_action = 'invoice.php?invoice_id=$invoice_id', notification_client_id = $pi_client_id"); // Logging $extended_log_desc = ''; From 6a55abaf50046489d5ae10022c91039fe99b925c Mon Sep 17 00:00:00 2001 From: johnnyq Date: Tue, 1 Oct 2024 22:50:30 -0400 Subject: [PATCH 054/249] Fix Error 500 when adding or editing network --- post/user/network.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/post/user/network.php b/post/user/network.php index 6a0472e8..1d55e2d3 100644 --- a/post/user/network.php +++ b/post/user/network.php @@ -8,7 +8,7 @@ if (isset($_POST['add_network'])) { validateTechRole(); - require_once 'post/user/login_model.php'; + require_once 'post/user/network_model.php'; mysqli_query($mysqli,"INSERT INTO networks SET network_name = '$name', network_description = '$description', network_vlan = $vlan, network = '$network', network_subnet = '$subnet', network_gateway = '$gateway', network_primary_dns = '$primary_dns', network_secondary_dns = '$secondary_dns', network_dhcp_range = '$dhcp_range', network_notes = '$notes', network_location_id = $location_id, network_client_id = $client_id"); @@ -29,7 +29,7 @@ if (isset($_POST['edit_network'])) { $network_id = intval($_POST['network_id']); - require_once 'post/user/login_model.php'; + require_once 'post/user/network_model.php'; mysqli_query($mysqli,"UPDATE networks SET network_name = '$name', network_description = '$description', network_vlan = $vlan, network = '$network', network_subnet = '$subnet', network_gateway = '$gateway', network_primary_dns = '$primary_dns', network_secondary_dns = '$secondary_dns', network_dhcp_range = '$dhcp_range', network_notes = '$notes', network_location_id = $location_id WHERE network_id = $network_id"); From c352f6e3cb4238c4d5175f9b794ef9489b13cbae Mon Sep 17 00:00:00 2001 From: johnnyq Date: Wed, 2 Oct 2024 01:04:59 -0400 Subject: [PATCH 055/249] Do not show Send Invoice or Add Payment if Invoice Amount is 0 --- invoice.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/invoice.php b/invoice.php index 1453ee1b..6bb4dd2c 100644 --- a/invoice.php +++ b/invoice.php @@ -159,7 +159,7 @@ if (isset($_GET['invoice_id'])) {
- + @@ -176,7 +176,7 @@ if (isset($_GET['invoice_id'])) {
- + Add Payment From 4e9afd3e6bc8ca16078c3a1adb5900bc48e90a0f Mon Sep 17 00:00:00 2001 From: wrongecho Date: Wed, 2 Oct 2024 08:32:42 +0100 Subject: [PATCH 056/249] Certificates - perms and model Move certificates to the new permissions system Deduplicate add/edit using a model --- post/user/certificate.php | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/post/user/certificate.php b/post/user/certificate.php index a177974b..732aaca2 100644 --- a/post/user/certificate.php +++ b/post/user/certificate.php @@ -6,17 +6,9 @@ if (isset($_POST['add_certificate'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); - $client_id = intval($_POST['client_id']); - $name = sanitizeInput($_POST['name']); - $description = sanitizeInput($_POST['description']); - $domain = sanitizeInput($_POST['domain']); - $issued_by = sanitizeInput($_POST['issued_by']); - $expire = sanitizeInput($_POST['expire']); - $public_key = sanitizeInput($_POST['public_key']); - $notes = sanitizeInput($_POST['notes']); - $domain_id = intval($_POST['domain_id']); + require_once 'post/user/certificate_model.php'; // Parse public key data for a manually provided public key if (!empty($public_key) && (empty($expire) && empty($issued_by))) { @@ -49,18 +41,10 @@ if (isset($_POST['add_certificate'])) { if (isset($_POST['edit_certificate'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); + require_once 'post/user/certificate_model.php'; $certificate_id = intval($_POST['certificate_id']); - $name = sanitizeInput($_POST['name']); - $description = sanitizeInput($_POST['description']); - $domain = sanitizeInput($_POST['domain']); - $issued_by = sanitizeInput($_POST['issued_by']); - $expire = sanitizeInput($_POST['expire']); - $public_key = sanitizeInput($_POST['public_key']); - $notes = sanitizeInput($_POST['notes']); - $domain_id = intval($_POST['domain_id']); - $client_id = intval($_POST['client_id']); // Parse public key data for a manually provided public key if (!empty($public_key) && (empty($expire) && empty($issued_by))) { @@ -91,7 +75,7 @@ if (isset($_POST['edit_certificate'])) { if (isset($_GET['archive_certificate'])) { - validateTechRole(); + enforceUserPermission('module_support', 2); $certificate_id = intval($_GET['archive_certificate']); @@ -115,7 +99,7 @@ if (isset($_GET['archive_certificate'])) { if (isset($_GET['delete_certificate'])) { - validateAdminRole(); + enforceUserPermission('module_support', 3); $certificate_id = intval($_GET['delete_certificate']); @@ -138,7 +122,7 @@ if (isset($_GET['delete_certificate'])) { } if (isset($_POST['bulk_delete_certificates'])) { - validateAdminRole(); + enforceUserPermission('module_support', 3); validateCSRFToken($_POST['csrf_token']); $count = 0; // Default 0 @@ -169,7 +153,7 @@ if (isset($_POST['bulk_delete_certificates'])) { if (isset($_POST['export_client_certificates_csv'])) { - validateTechRole(); + enforceUserPermission('module_support'); $client_id = intval($_POST['client_id']); From 1c404b9cf1e5c5e2ae040514f3a66222f412c740 Mon Sep 17 00:00:00 2001 From: wrongecho Date: Wed, 2 Oct 2024 11:26:13 +0100 Subject: [PATCH 057/249] Add custom event handler to be triggered by some actions affecting tickets/quotes/invoices/contacts --- cron.php | 10 ++++-- cron_ticket_email_parser.php | 10 ++++-- functions.php | 8 +++++ guest_pay_invoice_stripe.php | 1 + guest_post.php | 10 ++++++ portal/portal_post.php | 31 +++++++++++++++-- post/user/contact.php | 7 +++- post/user/invoice.php | 10 +++++- post/user/quote.php | 10 ++++++ post/user/ticket.php | 66 +++++++++++++++++++++++++++++++++--- post/xcustom/readme.php | 6 ++-- xcustom/readme.php | 9 +++++ 12 files changed, 161 insertions(+), 17 deletions(-) create mode 100644 xcustom/readme.php diff --git a/cron.php b/cron.php index 703f673c..56799ec8 100644 --- a/cron.php +++ b/cron.php @@ -152,7 +152,7 @@ if ($config_whitelabel_enabled && !validateWhitelabelKey($config_whitelabel_key) // DOMAINS EXPIRING -if($config_enable_alert_domain_expire == 1){ +if ($config_enable_alert_domain_expire == 1) { $domainAlertArray = [1,7,14,30,90]; @@ -247,7 +247,7 @@ $sql_tickets_pending_assignment = mysqli_query($mysqli,"SELECT ticket_id FROM ti $tickets_pending_assignment = mysqli_num_rows($sql_tickets_pending_assignment); -if($tickets_pending_assignment > 0){ +if ($tickets_pending_assignment > 0) { mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Pending Tickets', notification = 'There are $tickets_pending_assignment new tickets pending assignment', notification_action = 'tickets.php?status=New'"); @@ -295,6 +295,8 @@ if (mysqli_num_rows($sql_scheduled_tickets) > 0) { // Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Create', log_description = 'System created recurring scheduled $frequency ticket - $subject', log_client_id = $client_id, log_user_id = $created_id"); + customAction('ticket_create', $id); + // Notifications // Get client/contact/ticket details @@ -415,6 +417,8 @@ while ($row = mysqli_fetch_array($sql_resolved_tickets_to_close)) { //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Ticket', log_action = 'Closed', log_description = '$ticket_prefix$ticket_number auto closed', log_entity_id = $ticket_id"); + customAction('ticket_close', $ticket_id); + //TODO: Add client notifs if $config_ticket_client_general_notifications is on } @@ -569,6 +573,8 @@ while ($row = mysqli_fetch_array($sql_recurring)) { mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Recurring Sent', notification = 'Recurring Invoice $config_invoice_prefix$new_invoice_number for $client_name Sent', notification_action = 'invoice.php?invoice_id=$new_invoice_id', notification_client_id = $client_id, notification_entity_id = $new_invoice_id"); + customAction('invoice_create', $new_invoice_id); + //Update recurring dates mysqli_query($mysqli, "UPDATE recurring SET recurring_last_sent = CURDATE(), recurring_next_date = DATE_ADD(CURDATE(), INTERVAL 1 $recurring_frequency) WHERE recurring_id = $recurring_id"); diff --git a/cron_ticket_email_parser.php b/cron_ticket_email_parser.php index 63178223..5adba2af 100644 --- a/cron_ticket_email_parser.php +++ b/cron_ticket_email_parser.php @@ -101,7 +101,6 @@ function addTicket($contact_id, $contact_name, $contact_email, $client_id, $date mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$ticket_prefix_esc', ticket_number = $ticket_number, ticket_subject = '$subject_esc', ticket_details = '$message_esc', ticket_priority = 'Low', ticket_status = 1, ticket_created_by = 0, ticket_contact_id = $contact_id, ticket_url_key = '$url_key', ticket_client_id = $client_id_esc"); $id = mysqli_insert_id($mysqli); - echo "Created new ticket.
"; mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Create', log_description = 'Email parser: Client contact $contact_email_esc created ticket $ticket_prefix_esc$ticket_number ($subject_esc) ($id)', log_client_id = $client_id_esc"); mkdirMissing('uploads/tickets/'); @@ -172,6 +171,9 @@ function addTicket($contact_id, $contact_name, $contact_email, $client_id, $date addToMailQueue($mysqli, $data); + // Custom action/notif handler + customAction('ticket_create', $id); + return true; } @@ -308,10 +310,12 @@ function addReply($from_email, $date, $subject, $ticket_number, $message, $attac mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 2, ticket_resolved_at = NULL WHERE ticket_id = $ticket_id AND ticket_client_id = $client_id LIMIT 1"); - echo "Updated existing ticket.
"; mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Client contact $from_email_esc updated ticket $config_ticket_prefix$ticket_number_esc ($subject)', log_client_id = $client_id"); + customAction('ticket_reply_client', $ticket_id); + return true; + } else { return false; } @@ -469,8 +473,8 @@ if ($messages->count() > 0) { mysqli_query($mysqli, "INSERT INTO contacts SET contact_name = '".mysqli_real_escape_string($mysqli, $contact_name)."', contact_email = '".mysqli_real_escape_string($mysqli, $contact_email)."', contact_notes = 'Added automatically via email parsing.', contact_password_hash = '$password', contact_client_id = $client_id"); $contact_id = mysqli_insert_id($mysqli); - echo "Created new contact.
"; mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Contact', log_action = 'Create', log_description = 'Email parser: created contact ".mysqli_real_escape_string($mysqli, $contact_name)."', log_client_id = $client_id"); + customAction('contact_create', $ticket_id); if (addTicket($contact_id, $contact_name, $contact_email, $client_id, $date, $subject, $message_body, $message->getAttachments(), $original_message_file)) { $email_processed = true; diff --git a/functions.php b/functions.php index 9434776c..fadf6ba2 100644 --- a/functions.php +++ b/functions.php @@ -1304,6 +1304,7 @@ function enforceUserPermission($module, $check_access_level = 1) { } } +// TODO: Probably remove this function enforceAdminPermission() { global $session_is_admin; if (!isset($session_is_admin) || !$session_is_admin) { @@ -1311,3 +1312,10 @@ function enforceAdminPermission() { } return true; } + +function customAction($trigger, $entity) { + chdir(dirname(__FILE__)); + if (file_exists(__DIR__ . "/xcustom/xcustom_action_handler.php")) { + include_once __DIR__ . "/xcustom/xcustom_action_handler.php"; + } +} diff --git a/guest_pay_invoice_stripe.php b/guest_pay_invoice_stripe.php index 0f855533..c0a1b307 100644 --- a/guest_pay_invoice_stripe.php +++ b/guest_pay_invoice_stripe.php @@ -283,6 +283,7 @@ if (isset($_GET['invoice_id'], $_GET['url_key']) && !isset($_GET['payment_intent // Notify mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Invoice Paid', notification = 'Invoice $invoice_prefix$invoice_number has been paid by $client_name - $ip - $os - $browser', notification_action = 'invoice.php?invoice_id=$invoice_id', notification_client_id = $pi_client_id"); + customAction('invoice_pay', $invoice_id); // Logging $extended_log_desc = ''; diff --git a/guest_post.php b/guest_post.php index bc4702e8..dfa360c7 100644 --- a/guest_post.php +++ b/guest_post.php @@ -23,6 +23,8 @@ if (isset($_GET['accept_quote'], $_GET['url_key'])) { mysqli_query($mysqli, "INSERT INTO history SET history_status = 'Accepted', history_description = 'Client accepted Quote!', history_quote_id = $quote_id"); + customAction('quote_accept', $quote_id); + $_SESSION['alert_message'] = "Quote Accepted"; header("Location: " . $_SERVER["HTTP_REFERER"]); @@ -45,6 +47,8 @@ if (isset($_GET['decline_quote'], $_GET['url_key'])) { mysqli_query($mysqli, "INSERT INTO history SET history_status = 'Declined', history_description = 'Client declined Quote!', history_quote_id = $quote_id"); + customAction('quote_decline', $quote_id); + $_SESSION['alert_type'] = "danger"; $_SESSION['alert_message'] = "Quote Declined"; @@ -73,6 +77,8 @@ if (isset($_GET['reopen_ticket'], $_GET['url_key'])) { //Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Replied', log_description = '$ticket_id reopened by client (guest)', log_ip = '$session_ip', log_user_agent = '$session_user_agent'"); + customAction('ticket_update', $ticket_id); + $_SESSION['alert_message'] = "Ticket reopened"; header("Location: " . $_SERVER["HTTP_REFERER"]); @@ -100,6 +106,8 @@ if (isset($_GET['close_ticket'], $_GET['url_key'])) { //Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Replied', log_description = '$ticket_id closed by client (guest)', log_ip = '$session_ip', log_user_agent = '$session_user_agent'"); + customAction('ticket_close', $ticket_id); + $_SESSION['alert_message'] = "Ticket closed"; header("Location: " . $_SERVER["HTTP_REFERER"]); @@ -130,6 +138,8 @@ if (isset($_GET['add_ticket_feedback'], $_GET['url_key'])) { $_SESSION['alert_message'] = "Feedback recorded - thank you"; header("Location: " . $_SERVER["HTTP_REFERER"]); + customAction('ticket_feedback', $ticket_id); + } else { echo "Invalid!!"; } diff --git a/portal/portal_post.php b/portal/portal_post.php index 7829e836..026a4eb0 100644 --- a/portal/portal_post.php +++ b/portal/portal_post.php @@ -37,7 +37,7 @@ if (isset($_POST['add_ticket'])) { mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1"); mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_subject = '$subject', ticket_details = '$details', ticket_priority = '$priority', ticket_status = 1, ticket_created_by = 0, ticket_contact_id = $contact, ticket_url_key = '$url_key', ticket_client_id = $client_id"); - $id = mysqli_insert_id($mysqli); + $ticket_id = mysqli_insert_id($mysqli); // Notify agent DL of the new ticket, if populated with a valid email if ($config_ticket_new_ticket_notification_email) { @@ -46,7 +46,7 @@ if (isset($_POST['add_ticket'])) { $details = removeEmoji($details); $email_subject = "ITFlow - New Ticket - $client_name: $subject"; - $email_body = "Hello,

This is a notification that a new ticket has been raised in ITFlow.
Client: $client_name
Priority: $priority
Link: https://$config_base_url/ticket.php?ticket_id=$id

$subject
$details"; + $email_body = "Hello,

This is a notification that a new ticket has been raised in ITFlow.
Client: $client_name
Priority: $priority
Link: https://$config_base_url/ticket.php?ticket_id=$ticket_id

$subject
$details"; // Queue Mail $data = [ @@ -62,10 +62,13 @@ if (isset($_POST['add_ticket'])) { addToMailQueue($mysqli, $data); } + // Custom action/notif handler + customAction('ticket_create', $ticket_id); + // Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Create', log_description = 'Client contact $session_contact_name created ticket $subject', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id"); - header("Location: ticket.php?id=" . $id); + header("Location: ticket.php?id=" . $ticket_id); } @@ -162,6 +165,9 @@ if (isset($_POST['add_ticket_comment'])) { } } + // Custom action/notif handler + customAction('ticket_reply_client', $ticket_id); + // Redirect back to original page header("Location: " . $_SERVER["HTTP_REFERER"]); @@ -187,6 +193,9 @@ if (isset($_POST['add_ticket_feedback'])) { mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Feedback', notification = '$session_contact_name rated ticket ID $ticket_id as bad', notification_client_id = $session_client_id"); } + // Custom action/notif handler + customAction('ticket_feedback', $ticket_id); + // Redirect header("Location: " . $_SERVER["HTTP_REFERER"]); } else { @@ -212,7 +221,12 @@ if (isset($_GET['resolve_ticket'])) { //Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Resolved', log_description = '$ticket_id resolved by client', log_ip = '$session_ip', log_user_agent = '$session_user_agent'"); + // Custom action/notif handler + customAction('ticket_resolve', $ticket_id); + exit; + header("Location: ticket.php?id=" . $ticket_id); + } else { // The client does not have access to this ticket - send them home header("Location: index.php"); @@ -235,7 +249,11 @@ if (isset($_GET['reopen_ticket'])) { //Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Replied', log_description = '$ticket_id reopened by client', log_ip = '$session_ip', log_user_agent = '$session_user_agent'"); + // Custom action/notif handler + customAction('ticket_update', $ticket_id); + header("Location: ticket.php?id=" . $ticket_id); + } else { // The client does not have access to this ticket - send them home header("Location: index.php"); @@ -258,6 +276,9 @@ if (isset($_GET['close_ticket'])) { //Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Closed', log_description = '$ticket_id closed by client', log_ip = '$session_ip', log_user_agent = '$session_user_agent'"); + // Custom action/notif handler + customAction('ticket_close', $ticket_id); + header("Location: ticket.php?id=" . $ticket_id); } else { // The client does not have access to this ticket - send them home @@ -303,6 +324,8 @@ if (isset($_POST['edit_contact'])) { $_SESSION['alert_message'] = "Contact updated"; header('Location: contacts.php'); + + customAction('contact_update', $ticket_id); } if (isset($_POST['add_contact'])) { @@ -317,6 +340,8 @@ if (isset($_POST['add_contact'])) { // 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"); + customAction('contact_create', $ticket_id); + $_SESSION['alert_message'] = "Contact created"; header('Location: contacts.php'); } diff --git a/post/user/contact.php b/post/user/contact.php index e6a509e0..1e03710d 100644 --- a/post/user/contact.php +++ b/post/user/contact.php @@ -10,7 +10,6 @@ if (isset($_POST['add_contact'])) { require_once 'post/user/contact_model.php'; - // Set password if (!empty($_POST['contact_password'])) { $password_hash = password_hash(trim($_POST['contact_password']), PASSWORD_DEFAULT); @@ -58,6 +57,8 @@ if (isset($_POST['add_contact'])) { //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Contact', log_action = 'Create', log_description = '$session_name created contact $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 = $contact_id"); + customAction('contact_create', $contact_id); + $_SESSION['alert_message'] = "Contact $name created"; header("Location: " . $_SERVER["HTTP_REFERER"]); @@ -173,6 +174,8 @@ if (isset($_POST['edit_contact'])) { //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Contact', log_action = 'Modify', log_description = '$session_name modified contact $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 = $contact_id"); + customAction('contact_update', $contact_id); + $_SESSION['alert_message'] = "Contact $name updated"; header("Location: " . $_SERVER["HTTP_REFERER"]); @@ -313,6 +316,8 @@ if (isset($_POST['bulk_edit_contact_role'])) { //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Contact', log_action = 'Modify', log_description = '$session_name updated $contact_name role', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $contact_id"); + customAction('contact_update', $contact_id); + } // End Assign Location Loop $_SESSION['alert_message'] = "You updated roles for $contact_count contacts"; diff --git a/post/user/invoice.php b/post/user/invoice.php index 0d03e3c1..6f125d64 100644 --- a/post/user/invoice.php +++ b/post/user/invoice.php @@ -31,6 +31,8 @@ if (isset($_POST['add_invoice'])) { //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Create', log_description = '$config_invoice_prefix$invoice_number', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + customAction('invoice_create', $invoice_id); + $_SESSION['alert_message'] = "Invoice added"; header("Location: invoice.php?invoice_id=$invoice_id"); @@ -117,6 +119,8 @@ if (isset($_POST['add_invoice_copy'])) { //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Create', log_description = 'Copied Invoice', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + customAction('invoice_create', $new_invoice_id); + $_SESSION['alert_message'] = "Invoice copied"; header("Location: invoice.php?invoice_id=$new_invoice_id"); @@ -748,6 +752,8 @@ if (isset($_POST['add_payment'])) { //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Create', log_description = '$payment_amount', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $payment_id"); + customAction('invoice_pay', $invoice_id); + if ($email_receipt == 1) { mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Email', log_description = 'Payment receipt for invoice $invoice_prefix$invoice_number queued to $contact_email Email ID: $email_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $payment_id"); } @@ -834,9 +840,9 @@ if (isset($_POST['add_bulk_payment'])) { mysqli_query($mysqli, $add_history_query); // Add to Email Body Invoice Portion - $email_body_invoices .= "
Invoice $invoice_prefix$invoice_number - Outstanding Amount: " . numfmt_format_currency($currency_format, $invoice_balance, $currency_code) . " - Payment Applied: " . numfmt_format_currency($currency_format, $payment_amount, $currency_code) . " - New Balance: " . numfmt_format_currency($currency_format, $remaining_invoice_balance, $currency_code); + customAction('invoice_pay', $invoice_id); } // End Invoice Loop @@ -1209,6 +1215,8 @@ if (isset($_GET['force_recurring'])) { //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Create', log_description = '$session_name forced recurring invoice into an invoice', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $new_invoice_id"); + customAction('invoice_create', $new_invoice_id); + $_SESSION['alert_message'] = "Recurring Invoice Forced"; header("Location: " . $_SERVER["HTTP_REFERER"]); diff --git a/post/user/quote.php b/post/user/quote.php index 62fef45c..6ec7fc8b 100644 --- a/post/user/quote.php +++ b/post/user/quote.php @@ -29,6 +29,8 @@ if (isset($_POST['add_quote'])) { //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Quote', log_action = 'Create', log_description = '$quote_prefix$quote_number', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + customAction('quote_create', $quote_id); + $_SESSION['alert_message'] = "Quote added"; header("Location: quote.php?quote_id=$quote_id"); @@ -86,6 +88,8 @@ if (isset($_POST['add_quote_copy'])) { //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Quote', log_action = 'Create', log_description = 'Copied Quote', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id"); + customAction('quote_create', $new_quote_id); + $_SESSION['alert_message'] = "Quote copied"; header("Location: quote.php?quote_id=$new_quote_id"); @@ -145,6 +149,8 @@ if (isset($_POST['add_quote_to_invoice'])) { //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Quote', log_action = 'Create', log_description = 'Quote copied to Invoice', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + customAction('invoice_create', $new_invoice_id); + $_SESSION['alert_message'] = "Quote copied to Invoice"; header("Location: invoice.php?invoice_id=$new_invoice_id"); @@ -345,6 +351,8 @@ if (isset($_GET['accept_quote'])) { //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Quote', log_action = 'Modify', log_description = 'Accepted Quote $quote_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + customAction('quote_accept', $quote_id); + $_SESSION['alert_message'] = "Quote accepted"; header("Location: " . $_SERVER["HTTP_REFERER"]); @@ -361,6 +369,8 @@ if (isset($_GET['decline_quote'])) { mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Cancelled', history_description = 'Quote declined!', history_quote_id = $quote_id"); + customAction('quote_decline', $quote_id); + //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Quote', log_action = 'Modify', log_description = 'Declined Quote $quote_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); diff --git a/post/user/ticket.php b/post/user/ticket.php index 6fb9e68d..1368a2c7 100644 --- a/post/user/ticket.php +++ b/post/user/ticket.php @@ -176,6 +176,9 @@ if (isset($_POST['add_ticket'])) { } } + // Custom action/notif handler + customAction('ticket_create', $ticket_id); + // Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Create', log_description = '$session_name created ticket $config_ticket_prefix$ticket_number - $ticket_subject', 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_id"); @@ -259,6 +262,9 @@ if (isset($_POST['edit_ticket'])) { addToMailQueue($mysqli, $data); } + // Custom action/notif handler + customAction('ticket_update', $ticket_id); + //Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Modify', log_description = '$session_name modified ticket $ticket_number - $subject', 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_id"); @@ -280,6 +286,8 @@ if (isset($_POST['edit_ticket_priority'])) { //Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Modify', log_description = '$session_name edited ticket priority', 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_id"); + customAction('ticket_update', $ticket_id); + $_SESSION['alert_message'] = "Ticket priority updated"; header("Location: " . $_SERVER["HTTP_REFERER"]); @@ -350,6 +358,9 @@ if (isset($_POST['edit_ticket_contact'])) { addToMailQueue($mysqli, $data); } + // Custom action/notif handler + customAction('ticket_update', $ticket_id); + //Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Modify', log_description = '$session_name changed contact for ticket $ticket_number', 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_id"); @@ -523,9 +534,14 @@ if (isset($_POST['edit_ticket_priority'])) { //Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Modify', log_description = '$session_name edited ticket priority', 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_id"); + // Custom action/notif handler + customAction('ticket_update', $ticket_id); + $_SESSION['alert_message'] = "Ticket priority updated"; header("Location: " . $_SERVER["HTTP_REFERER"]); + + customAction('ticket_update', $ticket_id); } if (isset($_POST['assign_ticket'])) { @@ -622,6 +638,8 @@ if (isset($_POST['assign_ticket'])) { } } + customAction('ticket_assign', $ticket_id); + $_SESSION['alert_message'] = "Ticket $ticket_prefix$ticket_number assigned to $agent_name"; header("Location: " . $_SERVER["HTTP_REFERER"]); @@ -661,6 +679,8 @@ if (isset($_GET['delete_ticket'])) { $_SESSION['alert_type'] = "error"; $_SESSION['alert_message'] = "Ticket $ticket_prefix$ticket_number along with all replies deleted"; + customAction('ticket_delete', $ticket_id); + header("Location: tickets.php"); } } @@ -723,6 +743,8 @@ if (isset($_POST['bulk_assign_ticket'])) { // Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Edit', log_description = '$session_name reassigned ticket $ticket_prefix$ticket_number - $ticket_subject to $agent_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 = $ticket_id"); + customAction('ticket_assign', $ticket_id); + $tickets_assigned_body .= "$ticket_prefix$ticket_number - $ticket_subject
"; } // End For Each Ticket ID Loop @@ -796,6 +818,8 @@ if (isset($_POST['bulk_edit_ticket_priority'])) { // Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Edit', log_description = '$session_name updated the priority on ticket $ticket_prefix$ticket_number - $ticket_subject from $current_ticket_priority to $priority', 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_id"); + + customAction('ticket_update', $ticket_id); } // End For Each Ticket ID Loop } @@ -850,6 +874,9 @@ if (isset($_POST['bulk_merge_tickets'])) { // Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Merged', log_description = 'Merged ticket $ticket_prefix$ticket_number into $ticket_prefix$merge_into_ticket_number', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + // Custom action/notif handler + customAction('ticket_merge', $ticket_id); + } } // End For Each Ticket ID Loop } @@ -901,6 +928,8 @@ if (isset($_POST['bulk_resolve_tickets'])) { // Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Resolve', log_description = '$session_name resolved $ticket_prefix$ticket_number - $ticket_subject in a bulk action', 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_id"); + customAction('ticket_resolve', $ticket_id); + // Client notification email if (!empty($config_smtp_host) && $config_ticket_client_general_notifications == 1 && $private_note == 0) { @@ -1012,15 +1041,23 @@ if (isset($_POST['bulk_ticket_reply'])) { // Update Ticket Status mysqli_query($mysqli, "UPDATE tickets SET ticket_status = '$ticket_status' WHERE ticket_id = $ticket_id"); + // Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket Reply', log_action = 'Create', log_description = '$session_name replied to ticket $ticket_prefix$ticket_number - $ticket_subject and was a $ticket_reply_type 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"); + + // Custom action/notif handler + if ($ticket_reply_type == 'Internal') { + customAction('ticket_reply_agent_internal', $ticket_id); + } else { + customAction('reply_reply_agent_public', $ticket_id); + } + // Resolve the ticket, if set if ($ticket_status == 4) { mysqli_query($mysqli, "UPDATE tickets SET ticket_resolved_at = NOW() WHERE ticket_id = $ticket_id"); mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Resolved', log_description = 'Ticket ID $ticket_id resolved', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id, log_entity_id = $ticket_id"); + customAction('ticket_resolve', $ticket_id); } - // Logging - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket Reply', log_action = 'Create', log_description = '$session_name replied to ticket $ticket_prefix$ticket_number - $ticket_subject and was a $ticket_reply_type 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"); - // Get Contact Details $sql = mysqli_query( $mysqli, @@ -1109,7 +1146,7 @@ if (isset($_POST['bulk_ticket_reply'])) { } -// Currenly not UI Frontend for this +// Currently not UI Frontend for this if (isset($_POST['bulk_add_ticket_project'])) { enforceUserPermission('module_support', 2); @@ -1290,6 +1327,13 @@ if (isset($_POST['add_ticket_reply'])) { mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Ticket', notification = '$session_name updated Ticket $ticket_prefix$ticket_number - Subject: $ticket_subject that you opened', notification_action = 'ticket.php?ticket_id=$ticket_id', notification_client_id = $client_id, notification_user_id = $ticket_created_by"); } + // Custom action/notif handler + if ($ticket_reply_type == 'Internal') { + customAction('ticket_reply_agent_internal', $ticket_id); + } else { + customAction('reply_reply_agent_public', $ticket_id); + } + // Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket Reply', log_action = 'Create', log_description = '$session_name replied to ticket $ticket_prefix$ticket_number - $ticket_subject and was a $ticket_reply_type 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"); @@ -1394,6 +1438,8 @@ if (isset($_POST['merge_ticket'])) { // Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Merged', log_description = 'Merged ticket $ticket_prefix$ticket_number into $ticket_prefix$merge_into_ticket_number', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + customAction('ticket_merge', $ticket_id); + $_SESSION['alert_message'] = "Ticket merged into $ticket_prefix$merge_into_ticket_number"; header("Location: " . $_SERVER["HTTP_REFERER"]); @@ -1416,6 +1462,8 @@ if (isset($_POST['change_client_ticket'])) { //Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket Reply', log_action = 'Modify', log_description = '$session_name modified ticket - client changed', 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_id"); + customAction('ticket_update', $ticket_id); + $_SESSION['alert_message'] = "Ticket client updated"; header("Location: " . $_SERVER["HTTP_REFERER"]); @@ -1435,6 +1483,8 @@ if (isset($_GET['resolve_ticket'])) { //Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Resolved', log_description = 'Ticket ID $ticket_id resolved', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id, log_entity_id = $ticket_id"); + customAction('ticket_resolve', $ticket_id); + // Client notification email if (!empty($config_smtp_host) && $config_ticket_client_general_notifications == 1) { @@ -1529,6 +1579,8 @@ if (isset($_GET['close_ticket'])) { //Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Closed', log_description = 'Ticket ID $ticket_id Closed', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id, log_entity_id = $ticket_id"); + customAction('ticket_close', $ticket_id); + // Client notification email if (!empty($config_smtp_host) && $config_ticket_client_general_notifications == 1) { @@ -1615,6 +1667,8 @@ if (isset($_GET['reopen_ticket'])) { //Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Reopened', log_description = 'Ticket ID $ticket_id reopened', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id, log_entity_id = $ticket_id"); + customAction('ticket_update', $ticket_id); + $_SESSION['alert_message'] = "Ticket re-opened"; header("Location: " . $_SERVER["HTTP_REFERER"]); } @@ -2079,6 +2133,8 @@ if (isset($_POST['edit_ticket_schedule'])) { log_entity_id = $ticket_id" ); + customAction('ticket_schedule', $ticket_id); + if (empty($conflicting_tickets)) { $_SESSION['alert_message'] = "Ticket scheduled for $email_datetime"; @@ -2226,6 +2282,8 @@ if (isset($_GET['cancel_ticket_schedule'])) { //Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Modify', log_description = '$session_name cancelled ticket schedule', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id, log_entity_id = $ticket_id"); + customAction('ticket_unschedule', $ticket_id); + $_SESSION['alert_message'] = "Ticket schedule cancelled"; header("Location: " . $_SERVER["HTTP_REFERER"]); diff --git a/post/xcustom/readme.php b/post/xcustom/readme.php index 264db542..bbfca6b4 100644 --- a/post/xcustom/readme.php +++ b/post/xcustom/readme.php @@ -3,11 +3,11 @@ /* - Custom Pages - - If you wish to add custom pages to ITFlow, add them to the root directory with the prefix "xcustom_" - e.g. If your page was called my_page_one, name it "xcustom_my_page_one.php" + If you wish to add custom pages to ITFlow, add them to the xcustom folder in the root directory with the prefix "xcustom_" + e.g. If your page was called my_page_one, name it "xcustom/xcustom_my_page_one.php" Note: If required, you can use the Custom Links module to have the page show on the user sidebar. To process POST data via your custom pages, create a file in this directory (post/xcustom) named after your page (e.g. xcustom_my_page_one.php). - The relevant file will be automatically loaded upon a POST request based on the referer - your form just needs to target the standard post.php. + The relevant file will be automatically loaded upon a POST request based on the referer - your form just needs to target the standard root/post.php. */ diff --git a/xcustom/readme.php b/xcustom/readme.php new file mode 100644 index 00000000..4179d055 --- /dev/null +++ b/xcustom/readme.php @@ -0,0 +1,9 @@ + Date: Wed, 2 Oct 2024 11:26:58 +0100 Subject: [PATCH 058/249] Certificates - perms and model Move certificates to the new permissions system Deduplicate add/edit using a model --- post/user/certificate_model.php | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 post/user/certificate_model.php diff --git a/post/user/certificate_model.php b/post/user/certificate_model.php new file mode 100644 index 00000000..2fc171ec --- /dev/null +++ b/post/user/certificate_model.php @@ -0,0 +1,10 @@ + Date: Wed, 2 Oct 2024 11:35:14 +0100 Subject: [PATCH 059/249] tidy --- xcustom/readme.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcustom/readme.php b/xcustom/readme.php index 4179d055..94b0e942 100644 --- a/xcustom/readme.php +++ b/xcustom/readme.php @@ -6,4 +6,4 @@ If you wish to add custom pages to ITFlow, add them to this directory with the prefix "xcustom_" -*/ \ No newline at end of file +*/ From fa6aa4318b65ef04912598b044bdea04bd7e3495 Mon Sep 17 00:00:00 2001 From: wrongecho Date: Wed, 2 Oct 2024 11:42:30 +0100 Subject: [PATCH 060/249] tidy --- portal/portal_post.php | 1 - 1 file changed, 1 deletion(-) diff --git a/portal/portal_post.php b/portal/portal_post.php index 026a4eb0..2afdd06f 100644 --- a/portal/portal_post.php +++ b/portal/portal_post.php @@ -223,7 +223,6 @@ if (isset($_GET['resolve_ticket'])) { // Custom action/notif handler customAction('ticket_resolve', $ticket_id); - exit; header("Location: ticket.php?id=" . $ticket_id); From 63579d5a8fbb1c593d3899b4b39eea7c03f418cc Mon Sep 17 00:00:00 2001 From: wrongecho Date: Wed, 2 Oct 2024 12:00:56 +0100 Subject: [PATCH 061/249] Certificate cron bugfix Don't try to update certificates if expiry is empty (connection error) --- cron_certificate_refresher.php | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/cron_certificate_refresher.php b/cron_certificate_refresher.php index 61a4a346..e7ffb1c4 100644 --- a/cron_certificate_refresher.php +++ b/cron_certificate_refresher.php @@ -34,7 +34,13 @@ if ( $argv[1] !== $config_cron_key ) { * ############################################################################################################### */ -$sql_certificates = mysqli_query($mysqli, "SELECT * FROM certificates WHERE certificate_archived_at IS NULL"); +$sql_certificates = mysqli_query( + $mysqli, + "SELECT * FROM certificates + LEFT JOIN clients ON certificates.certificate_client_id = clients.client_id + WHERE certificate_archived_at IS NULL + AND client_archived_at IS NULL" +); while ($row = mysqli_fetch_array($sql_certificates)) { $certificate_id = intval($row['certificate_id']); @@ -46,17 +52,18 @@ while ($row = mysqli_fetch_array($sql_certificates)) { $issued_by = sanitizeInput($certificate['issued_by']); $public_key = sanitizeInput($certificate['public_key']); - if (empty($expire)) { - $expire = "NULL"; - } else { + if (!empty($expire)) { + + echo "\n$domain\n"; + echo "$issued_by\n"; + echo "$expire\n"; + echo "$public_key\n\n"; + $expire = "'" . $expire . "'"; + mysqli_query($mysqli,"UPDATE certificates SET certificate_issued_by = '$issued_by', certificate_expire = $expire, certificate_public_key = '$public_key' WHERE certificate_id = $certificate_id"); + + } else { + error_log("Certificate Cron Error - Error updating $domain"); } - echo "\n$domain\n"; - echo "$issued_by\n"; - echo "$expire\n"; - echo "$public_key\n\n"; - - mysqli_query($mysqli,"UPDATE certificates SET certificate_issued_by = '$issued_by', certificate_expire = $expire, certificate_public_key = '$public_key' WHERE certificate_id = $certificate_id"); - } From 18889d228aa2bfcdf8c2363e76e855cf85d25908 Mon Sep 17 00:00:00 2001 From: wrongecho Date: Thu, 3 Oct 2024 19:42:48 +0100 Subject: [PATCH 062/249] Move account/asset post logic to new permissions system --- client_assets.php | 4 +++- post/user/account.php | 6 ++++++ post/user/asset.php | 48 ++++++++++++++++++++++++++++--------------- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/client_assets.php b/client_assets.php index 7c3569ee..f4c5ec1d 100644 --- a/client_assets.php +++ b/client_assets.php @@ -99,7 +99,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));

Assets

-
+ = 2) { ?> +
@@ -116,6 +117,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
+
diff --git a/post/user/account.php b/post/user/account.php index ba843736..dbf1eb98 100644 --- a/post/user/account.php +++ b/post/user/account.php @@ -5,6 +5,7 @@ */ if (isset($_POST['add_account'])) { + enforceUserPermission('module_financial', 2); validateCSRFToken($_POST['csrf_token']); $name = sanitizeInput($_POST['name']); @@ -24,6 +25,7 @@ if (isset($_POST['add_account'])) { } if (isset($_POST['edit_account'])) { + enforceUserPermission('module_financial', 2); validateCSRFToken($_POST['csrf_token']); $account_id = intval($_POST['account_id']); @@ -42,6 +44,8 @@ if (isset($_POST['edit_account'])) { } if (isset($_GET['archive_account'])) { + enforceUserPermission('module_financial', 2); + validateCSRFToken($_GET['csrf_token']); $account_id = intval($_GET['archive_account']); @@ -58,6 +62,8 @@ if (isset($_GET['archive_account'])) { // Not used anywhere? if (isset($_GET['delete_account'])) { + enforceUserPermission('module_financial', 3); + $account_id = intval($_GET['delete_account']); mysqli_query($mysqli,"DELETE FROM accounts WHERE account_id = $account_id"); diff --git a/post/user/asset.php b/post/user/asset.php index 1bbc735e..c7ff1881 100644 --- a/post/user/asset.php +++ b/post/user/asset.php @@ -6,8 +6,9 @@ if (isset($_POST['add_asset'])) { + enforceUserPermission('module_support', 2); + validateCSRFToken($_POST['csrf_token']); - validateTechRole(); $client_id = intval($_POST['client_id']); $name = sanitizeInput($_POST['name']); @@ -106,8 +107,9 @@ if (isset($_POST['add_asset'])) { if (isset($_POST['edit_asset'])) { + enforceUserPermission('module_support', 2); + validateCSRFToken($_POST['csrf_token']); - validateTechRole(); $asset_id = intval($_POST['asset_id']); $client_id = intval($_POST['client_id']); @@ -199,8 +201,9 @@ if (isset($_POST['edit_asset'])) { if (isset($_POST['change_client_asset'])) { + enforceUserPermission('module_support', 2); + validateCSRFToken($_POST['csrf_token']); - validateTechRole(); $current_asset_id = intval($_POST['current_asset_id']); $new_client_id = intval($_POST['new_client_id']); @@ -247,8 +250,9 @@ if (isset($_POST['change_client_asset'])) { if (isset($_GET['archive_asset'])) { + enforceUserPermission('module_support', 2); + validateCSRFToken($_GET['csrf_token']); - validateTechRole(); $asset_id = intval($_GET['archive_asset']); @@ -272,8 +276,9 @@ if (isset($_GET['archive_asset'])) { if (isset($_GET['unarchive_asset'])) { + enforceUserPermission('module_support', 2); + validateCSRFToken($_GET['csrf_token']); - validateTechRole(); $asset_id = intval($_GET['unarchive_asset']); @@ -296,8 +301,9 @@ if (isset($_GET['unarchive_asset'])) { if (isset($_GET['delete_asset'])) { + enforceUserPermission('module_support', 3); + validateCSRFToken($_GET['csrf_token']); - validateAdminRole(); $asset_id = intval($_GET['delete_asset']); @@ -324,8 +330,9 @@ if (isset($_GET['delete_asset'])) { if (isset($_POST['bulk_assign_asset_location'])) { + enforceUserPermission('module_support', 2); + validateCSRFToken($_POST['csrf_token']); - validateTechRole(); $location_id = intval($_POST['bulk_location_id']); @@ -364,8 +371,9 @@ if (isset($_POST['bulk_assign_asset_location'])) { if (isset($_POST['bulk_assign_asset_contact'])) { + enforceUserPermission('module_support', 2); + validateCSRFToken($_POST['csrf_token']); - validateTechRole(); $contact_id = intval($_POST['bulk_contact_id']); @@ -404,8 +412,9 @@ if (isset($_POST['bulk_assign_asset_contact'])) { if (isset($_POST['bulk_edit_asset_status'])) { + enforceUserPermission('module_support', 2); + validateCSRFToken($_POST['csrf_token']); - validateTechRole(); $status = sanitizeInput($_POST['bulk_status']); @@ -439,8 +448,9 @@ if (isset($_POST['bulk_edit_asset_status'])) { if (isset($_POST['bulk_archive_assets'])) { + enforceUserPermission('module_support', 2); + validateCSRFToken($_POST['csrf_token']); - validateAdminRole(); $count = 0; // Default 0 $asset_ids = $_POST['asset_ids']; // Get array of asset IDs to be deleted @@ -480,8 +490,9 @@ if (isset($_POST['bulk_archive_assets'])) { if (isset($_POST['bulk_unarchive_assets'])) { + enforceUserPermission('module_support', 2); + validateCSRFToken($_POST['csrf_token']); - validateAdminRole(); $count = 0; // Default 0 $asset_ids = $_POST['asset_ids']; // Get array of asset IDs to be deleted @@ -520,8 +531,9 @@ if (isset($_POST['bulk_unarchive_assets'])) { if (isset($_POST["import_client_assets_csv"])) { + enforceUserPermission('module_support', 2); + validateCSRFToken($_POST['csrf_token']); - validateTechRole(); $client_id = intval($_POST['client_id']); $file_name = $_FILES["file"]["tmp_name"]; @@ -667,8 +679,9 @@ if (isset($_GET['download_client_assets_csv_template'])) { if (isset($_POST['export_client_assets_csv'])) { + enforceUserPermission('module_support'); + validateCSRFToken($_POST['csrf_token']); - validateTechRole(); $client_id = intval($_POST['client_id']); @@ -717,8 +730,9 @@ if (isset($_POST['export_client_assets_csv'])) { if (isset($_POST['add_asset_interface'])) { + enforceUserPermission('module_support', 2); + validateCSRFToken($_POST['csrf_token']); - validateTechRole(); $asset_id = intval($_POST['asset_id']); @@ -754,8 +768,9 @@ if (isset($_POST['add_asset_interface'])) { if (isset($_POST['edit_asset_interface'])) { + enforceUserPermission('module_support', 2); + validateCSRFToken($_POST['csrf_token']); - validateTechRole(); $interface_id = intval($_POST['interface_id']); @@ -790,8 +805,9 @@ if (isset($_POST['edit_asset_interface'])) { if (isset($_GET['delete_asset_interface'])) { + enforceUserPermission('module_support', 2); + validateCSRFToken($_GET['csrf_token']); - validateAdminRole(); $interface_id = intval($_GET['delete_asset_interface']); From 4d7d9baba890e77265bd05e5475e56946ac6bad1 Mon Sep 17 00:00:00 2001 From: wrongecho Date: Thu, 3 Oct 2024 20:25:41 +0100 Subject: [PATCH 063/249] Tidy up the ticket_invoice_add_modal --- ticket_invoice_add_modal.php | 65 +++++++++--------------------------- 1 file changed, 16 insertions(+), 49 deletions(-) diff --git a/ticket_invoice_add_modal.php b/ticket_invoice_add_modal.php index a50b3a26..9db078d4 100644 --- a/ticket_invoice_add_modal.php +++ b/ticket_invoice_add_modal.php @@ -1,6 +1,4 @@ @@ -17,45 +15,27 @@ $sql_invoices = mysqli_query($mysqli, "SELECT * FROM invoices WHERE invoice_stat - - -
diff --git a/recurring_expenses.php b/recurring_expenses.php index c2596b4c..ca2a3d4b 100644 --- a/recurring_expenses.php +++ b/recurring_expenses.php @@ -157,6 +157,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $recurring_expense_last_sent_display = $recurring_expense_last_sent; } $recurring_expense_next_date = nullable_htmlentities($row['recurring_expense_next_date']); + $recurring_expense_next_month = date('n', strtotime($row['recurring_expense_next_date'])); $recurring_expense_status = intval($row['recurring_expense_status']); $recurring_expense_description = nullable_htmlentities($row['recurring_expense_description']); $recurring_expense_amount = floatval($row['recurring_expense_amount']); From 646b94d8ab7fc3728dc4a2a8734edccab7b3f706 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Thu, 17 Oct 2024 20:55:23 -0400 Subject: [PATCH 102/249] Initial work on new Mail Parser that uses the old php imap libraries and not the new webklex --- new_cron_ticket_email_parser.php | 634 +++++++++++++++++++++++++++++++ 1 file changed, 634 insertions(+) create mode 100644 new_cron_ticket_email_parser.php diff --git a/new_cron_ticket_email_parser.php b/new_cron_ticket_email_parser.php new file mode 100644 index 00000000..6b8e8570 --- /dev/null +++ b/new_cron_ticket_email_parser.php @@ -0,0 +1,634 @@ + Ticketing > Email-to-ticket parsing. See https://docs.itflow.org/ticket_email_parse -- Quitting.."); +} + +$argv = $_SERVER['argv']; + +// Check Cron Key +if ($argv[1] !== $config_cron_key) { + exit("Cron Key invalid -- Quitting.."); +} + +// Get system temp directory +$temp_dir = sys_get_temp_dir(); + +// Create the path for the lock file using the temp directory +$lock_file_path = "{$temp_dir}/itflow_email_parser_{$installation_id}.lock"; + +// Check for lock file to prevent concurrent script runs +if (file_exists($lock_file_path)) { + $file_age = time() - filemtime($lock_file_path); + + // If file is older than 5 minutes (300 seconds), delete and continue + if ($file_age > 300) { + unlink($lock_file_path); + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Cron-Email-Parser', log_action = 'Delete', log_description = 'Cron Email Parser detected a lock file was present but was over 5 minutes old so it removed it'"); + } else { + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Cron-Email-Parser', log_action = 'Locked', log_description = 'Cron Email Parser attempted to execute but was already executing, so instead it terminated.'"); + exit("Script is already running. Exiting."); + } +} + +// Create a lock file +file_put_contents($lock_file_path, "Locked"); + +// Allowed attachment extensions +$allowed_extensions = array('jpg', 'jpeg', 'gif', 'png', 'webp', 'pdf', 'txt', 'md', 'doc', 'docx', 'csv', 'xls', 'xlsx', 'xlsm', 'zip', 'tar', 'gz'); + +// Function to raise a new ticket for a given contact and email them confirmation (if configured) +function addTicket($contact_id, $contact_name, $contact_email, $client_id, $date, $subject, $message, $attachments, $original_message_file) { + global $mysqli, $config_app_name, $company_name, $company_phone, $config_ticket_prefix, $config_ticket_client_general_notifications, $config_ticket_new_ticket_notification_email, $config_base_url, $config_ticket_from_name, $config_ticket_from_email, $config_smtp_host, $config_smtp_port, $config_smtp_encryption, $config_smtp_username, $config_smtp_password, $allowed_extensions; + + $ticket_number_sql = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_ticket_next_number FROM settings WHERE company_id = 1")); + $ticket_number = intval($ticket_number_sql['config_ticket_next_number']); + $new_config_ticket_next_number = $ticket_number + 1; + mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1"); + + // Clean up the message + $message = trim($message); // Remove leading/trailing whitespace + + // Process inline images in the message + $message = processInlineImages($message, $attachments, $ticket_number); + + // Wrap the message in a div with controlled line height + $message = "Email from: $contact_name <$contact_email> at $date:-

$message
"; + + $ticket_prefix_esc = mysqli_real_escape_string($mysqli, $config_ticket_prefix); + $subject_esc = mysqli_real_escape_string($mysqli, $subject); + $message_esc = mysqli_real_escape_string($mysqli, $message); + $contact_email_esc = mysqli_real_escape_string($mysqli, $contact_email); + $client_id_esc = intval($client_id); + + //Generate a unique URL key for clients to access + $url_key = randomString(156); + + mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$ticket_prefix_esc', ticket_number = $ticket_number, ticket_subject = '$subject_esc', ticket_details = '$message_esc', ticket_priority = 'Low', ticket_status = 1, ticket_created_by = 0, ticket_contact_id = $contact_id, ticket_url_key = '$url_key', ticket_client_id = $client_id_esc"); + $id = mysqli_insert_id($mysqli); + + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Create', log_description = 'Email parser: Client contact $contact_email_esc created ticket $ticket_prefix_esc$ticket_number ($subject_esc) ($id)', log_client_id = $client_id_esc"); + + mkdirMissing('uploads/tickets/'); + $att_dir = "uploads/tickets/" . $id . "/"; + mkdirMissing($att_dir); + + rename("uploads/tmp/{$original_message_file}", "{$att_dir}/{$original_message_file}"); + $original_message_file_esc = mysqli_real_escape_string($mysqli, $original_message_file); + mysqli_query($mysqli, "INSERT INTO ticket_attachments SET ticket_attachment_name = 'Original-parsed-email.eml', ticket_attachment_reference_name = '$original_message_file_esc', ticket_attachment_ticket_id = $id"); + + // Process attachments (excluding inline images) + processAttachments($attachments, $att_dir, $id, null, $contact_email); + + $data = []; + if ($config_ticket_client_general_notifications == 1) { + $subject_email = "Ticket created - [$config_ticket_prefix$ticket_number] - $subject"; + $body = "##- Please type your reply above this line -##

Hello $contact_name,

Thank you for your email. A ticket regarding \"$subject\" has been automatically created for you.

Ticket: $config_ticket_prefix$ticket_number
Subject: $subject
Status: New
https://$config_base_url/portal/ticket.php?id=$id

--
$company_name - Support
$config_ticket_from_email
$company_phone"; + $data[] = [ + 'from' => $config_ticket_from_email, + 'from_name' => $config_ticket_from_name, + 'recipient' => $contact_email, + 'recipient_name' => $contact_name, + 'subject' => mysqli_real_escape_string($mysqli, $subject_email), + 'body' => mysqli_real_escape_string($mysqli, $body) + ]; + } + + if ($config_ticket_new_ticket_notification_email) { + if ($client_id == 0){ + $client_name = "Guest"; + } else { + $client_sql = mysqli_query($mysqli, "SELECT client_name FROM clients WHERE client_id = $client_id"); + $client_row = mysqli_fetch_array($client_sql); + $client_name = sanitizeInput($client_row['client_name']); + } + $email_subject = "$config_app_name - New Ticket - $client_name: $subject"; + $email_body = "Hello,

This is a notification that a new ticket has been raised in ITFlow.
Client: $client_name
Priority: Low (email parsed)
Link: https://$config_base_url/ticket.php?ticket_id=$id

--------------------------------

$subject
$message"; + + $data[] = [ + 'from' => $config_ticket_from_email, + 'from_name' => $config_ticket_from_name, + 'recipient' => $config_ticket_new_ticket_notification_email, + 'recipient_name' => $config_ticket_from_name, + 'subject' => mysqli_real_escape_string($mysqli, $email_subject), + 'body' => mysqli_real_escape_string($mysqli, $email_body) + ]; + } + + addToMailQueue($mysqli, $data); + + // Custom action/notif handler + customAction('ticket_create', $id); + + return true; +} + +// Add Reply Function +function addReply($from_email, $date, $subject, $ticket_number, $message, $attachments) { + global $mysqli, $config_app_name, $company_name, $company_phone, $config_ticket_prefix, $config_base_url, $config_ticket_from_name, $config_ticket_from_email, $config_smtp_host, $config_smtp_port, $config_smtp_encryption, $config_smtp_username, $config_smtp_password, $allowed_extensions; + + $ticket_reply_type = 'Client'; + // Clean up the message + $message_parts = explode("##- Please type your reply above this line -##", $message); + $message_body = $message_parts[0]; + $message_body = trim($message_body); // Remove leading/trailing whitespace + + // Process inline images in the message + $message_body = processInlineImages($message_body, $attachments, $ticket_number); + + // Wrap the message in a div with controlled line height + $message = "Email from: $from_email at $date:-

$message_body
"; + + $ticket_number_esc = intval($ticket_number); + $message_esc = mysqli_real_escape_string($mysqli, $message); + $from_email_esc = mysqli_real_escape_string($mysqli, $from_email); + + $row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT ticket_id, ticket_subject, ticket_status, ticket_contact_id, ticket_client_id, contact_email, client_name + FROM tickets + LEFT JOIN contacts on tickets.ticket_contact_id = contacts.contact_id + LEFT JOIN clients on tickets.ticket_client_id = clients.client_id + WHERE ticket_number = $ticket_number_esc LIMIT 1")); + + if ($row) { + $ticket_id = intval($row['ticket_id']); + $ticket_subject = sanitizeInput($row['ticket_subject']); + $ticket_status = sanitizeInput($row['ticket_status']); + $ticket_reply_contact = intval($row['ticket_contact_id']); + $ticket_contact_email = sanitizeInput($row['contact_email']); + $client_id = intval($row['ticket_client_id']); + $client_name = sanitizeInput($row['client_name']); + + if ($ticket_status == 5) { + $config_ticket_prefix_esc = mysqli_real_escape_string($mysqli, $config_ticket_prefix); + $ticket_number_esc = mysqli_real_escape_string($mysqli, $ticket_number); + $ticket_id_esc = intval($ticket_id); + $client_id_esc = intval($client_id); + + mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Ticket', notification = 'Email parser: $from_email attempted to re-open ticket $config_ticket_prefix_esc$ticket_number_esc (ID $ticket_id_esc) - check inbox manually to see email', notification_action = 'ticket.php?ticket_id=$ticket_id_esc', notification_client_id = $client_id_esc"); + + $email_subject = "Action required: This ticket is already closed"; + $email_body = "Hi there,

You've tried to reply to a ticket that is closed - we won't see your response.

Please raise a new ticket by sending a new e-mail to our support address below.

--
$company_name - Support
$config_ticket_from_email
$company_phone"; + + $data = [ + [ + 'from' => $config_ticket_from_email, + 'from_name' => $config_ticket_from_name, + 'recipient' => $from_email, + 'recipient_name' => $from_email, + 'subject' => mysqli_real_escape_string($mysqli, $email_subject), + 'body' => mysqli_real_escape_string($mysqli, $email_body) + ] + ]; + + addToMailQueue($mysqli, $data); + + return true; + } + + if (empty($ticket_contact_email) || $ticket_contact_email !== $from_email) { + $from_email_esc = mysqli_real_escape_string($mysqli, $from_email); + $row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT contact_id FROM contacts WHERE contact_email = '$from_email_esc' AND contact_client_id = $client_id LIMIT 1")); + if ($row) { + $ticket_reply_contact = intval($row['contact_id']); + } else { + $ticket_reply_type = 'Internal'; + $ticket_reply_contact = '0'; + $message = "WARNING: Contact email mismatch
$message"; + $message_esc = mysqli_real_escape_string($mysqli, $message); + } + } + + mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = '$message_esc', ticket_reply_type = '$ticket_reply_type', ticket_reply_time_worked = '00:00:00', ticket_reply_by = $ticket_reply_contact, ticket_reply_ticket_id = $ticket_id"); + $reply_id = mysqli_insert_id($mysqli); + + mkdirMissing('uploads/tickets/'); + $att_dir = "uploads/tickets/" . $ticket_id . "/"; + mkdirMissing($att_dir); + + // Process attachments (excluding inline images) + processAttachments($attachments, $att_dir, $ticket_id, $reply_id, $from_email); + + $ticket_assigned_to = mysqli_query($mysqli, "SELECT ticket_assigned_to FROM tickets WHERE ticket_id = $ticket_id LIMIT 1"); + + if ($ticket_assigned_to) { + $row = mysqli_fetch_array($ticket_assigned_to); + $ticket_assigned_to = intval($row['ticket_assigned_to']); + + if ($ticket_assigned_to) { + $tech_sql = mysqli_query($mysqli, "SELECT user_email, user_name FROM users WHERE user_id = $ticket_assigned_to LIMIT 1"); + $tech_row = mysqli_fetch_array($tech_sql); + $tech_email = sanitizeInput($tech_row['user_email']); + $tech_name = sanitizeInput($tech_row['user_name']); + + $email_subject = "$config_app_name - Ticket updated - [$config_ticket_prefix$ticket_number] $ticket_subject"; + $email_body = "Hello $tech_name,

A new reply has been added to the below ticket, check ITFlow for full details.

Client: $client_name
Ticket: $config_ticket_prefix$ticket_number
Subject: $ticket_subject

https://$config_base_url/ticket.php?ticket_id=$ticket_id"; + + $data = [ + [ + 'from' => $config_ticket_from_email, + 'from_name' => $config_ticket_from_name, + 'recipient' => $tech_email, + 'recipient_name' => $tech_name, + 'subject' => mysqli_real_escape_string($mysqli, $email_subject), + 'body' => mysqli_real_escape_string($mysqli, $email_body) + ] + ]; + + addToMailQueue($mysqli, $data); + } + } + + mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 2, ticket_resolved_at = NULL WHERE ticket_id = $ticket_id AND ticket_client_id = $client_id LIMIT 1"); + + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Client contact $from_email_esc updated ticket $config_ticket_prefix$ticket_number_esc ($subject)', log_client_id = $client_id"); + + customAction('ticket_reply_client', $ticket_id); + + return true; + + } else { + return false; + } +} + +// Function to process inline images in the HTML body +function processInlineImages($html_body, &$attachments, $ticket_number) { + global $config_base_url; + + // Create a mapping of Content-ID to attachment data + $inline_images = array(); + foreach ($attachments as $key => $attachment) { + if (!empty($attachment['content_id'])) { + $content_id = trim($attachment['content_id'], '<>'); + $inline_images[$content_id] = $attachment; + unset($attachments[$key]); // Remove inline images from attachments array + } + } + + // Replace cid references in the HTML body + if (!empty($inline_images)) { + // Ensure the images directory exists + $images_dir = "uploads/inline_images/"; + mkdirMissing($images_dir); + + foreach ($inline_images as $content_id => $attachment) { + $att_name = $attachment['filename']; + $att_extarr = explode('.', $att_name); + $att_extension = strtolower(end($att_extarr)); + + // Generate a unique filename + $att_saved_filename = md5(uniqid(rand(), true)) . '.' . $att_extension; + $att_saved_path = $images_dir . $att_saved_filename; + + // Save the inline image + file_put_contents($att_saved_path, $attachment['data']); + + // Update the HTML body to point to the saved image + $html_body = str_replace('cid:' . $content_id, "https://$config_base_url/$att_saved_path", $html_body); + } + } + + return $html_body; +} + +// Function to process attachments (excluding inline images) +function processAttachments($attachments, $att_dir, $ticket_id, $reply_id = null, $from_email = '') { + global $mysqli, $allowed_extensions, $config_ticket_prefix; + + foreach ($attachments as $attachment) { + $att_name = $attachment['filename']; + $att_extarr = explode('.', $att_name); + $att_extension = strtolower(end($att_extarr)); + + if (in_array($att_extension, $allowed_extensions)) { + $att_saved_filename = md5(uniqid(rand(), true)) . '.' . $att_extension; + $att_saved_path = $att_dir . $att_saved_filename; + file_put_contents($att_saved_path, $attachment['data']); + + $ticket_attachment_name = sanitizeInput($att_name); + $ticket_attachment_reference_name = sanitizeInput($att_saved_filename); + + $ticket_attachment_name_esc = mysqli_real_escape_string($mysqli, $ticket_attachment_name); + $ticket_attachment_reference_name_esc = mysqli_real_escape_string($mysqli, $ticket_attachment_reference_name); + + $reply_clause = $reply_id ? ", ticket_attachment_reply_id = $reply_id" : ""; + + mysqli_query($mysqli, "INSERT INTO ticket_attachments SET ticket_attachment_name = '$ticket_attachment_name_esc', ticket_attachment_reference_name = '$ticket_attachment_reference_name_esc', ticket_attachment_ticket_id = $ticket_id $reply_clause"); + } else { + $ticket_attachment_name_esc = mysqli_real_escape_string($mysqli, $att_name); + $from_email_esc = mysqli_real_escape_string($mysqli, $from_email); + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Blocked attachment $ticket_attachment_name_esc from Client contact $from_email_esc for ticket $config_ticket_prefix$ticket_id', log_client_id = $ticket_id"); + } + } +} + +// Function to construct the mailbox string +function getMailboxString($host, $port, $encryption, $validate_cert, $folder = 'INBOX') { + $mailbox = "{" . $host . ":" . $port . "/"; + + // Handle encryption + if ($encryption == 'ssl') { + $mailbox .= 'imap/ssl'; + } elseif ($encryption == 'tls') { + $mailbox .= 'imap/tls'; + } else { + $mailbox .= 'imap'; + } + + // Handle validate_cert + if (!$validate_cert) { + $mailbox .= '/novalidate-cert'; + } + + $mailbox .= '}' . $folder; + + return $mailbox; +} + +// Function to create a folder in the mailbox if it doesn't exist +function createMailboxFolder($imap, $folderName) { + $mailboxPath = "{" . $GLOBALS['config_imap_host'] . "}" . $folderName; + $folders = imap_list($imap, "{" . $GLOBALS['config_imap_host'] . "}", "*"); + $folderExists = false; + if ($folders) { + foreach ($folders as $folder) { + $folderShortName = str_replace("{" . $GLOBALS['config_imap_host'] . "}", '', $folder); + if ($folderShortName == $folderName) { + $folderExists = true; + break; + } + } + } + + if (!$folderExists) { + if (imap_createmailbox($imap, imap_utf7_encode($mailboxPath))) { + echo "Folder '$folderName' created successfully.\n"; + // Subscribe to the folder + imap_subscribe($imap, imap_utf7_encode($mailboxPath)); + } else { + echo "Error creating folder '$folderName': " . imap_last_error() . "\n"; + } + } +} + +// Function to get the body of the email +function getBody($imap, $email_number, $structure, &$attachments) { + $body = ''; + + if (!isset($structure->parts)) { // Simple message, no attachments + $body = imap_body($imap, $email_number); + if ($structure->encoding == 3) { // Base64 + $body = base64_decode($body); + } elseif ($structure->encoding == 4) { // Quoted-Printable + $body = quoted_printable_decode($body); + } + } else { + // Multipart message + $body = get_part($imap, $email_number, $structure, 0, $attachments); + } + + return $body; +} + +function get_part($imap, $email_number, $part, $part_no, &$attachments) { + $data = ''; + $params = array(); + if ($part->ifparameters) { + foreach ($part->parameters as $param) { + $params[strtolower($param->attribute)] = $param->value; + } + } + if ($part->ifdparameters) { + foreach ($part->dparameters as $param) { + $params[strtolower($param->attribute)] = $param->value; + } + } + + // Identify if the part is an attachment + $is_attachment = false; + $filename = ''; + $name = ''; + $content_id = ''; + + if ($part->ifdisposition) { + if (strtolower($part->disposition) == 'attachment' || strtolower($part->disposition) == 'inline') { + $is_attachment = true; + } + } + + if ($part->ifid) { + $content_id = trim($part->id, '<>'); + $is_attachment = true; + } + + if ($params) { + if (isset($params['filename']) || isset($params['name'])) { + $is_attachment = true; + $filename = isset($params['filename']) ? $params['filename'] : $params['name']; + } + } + + // If it's an attachment, process it + if ($is_attachment) { + $attachment = array( + 'filename' => $filename, + 'content_id' => $content_id, + 'data' => imap_fetchbody($imap, $email_number, $part_no ? $part_no : 1), + 'encoding' => $part->encoding + ); + + // Decode the data + if ($part->encoding == 3) { // Base64 + $attachment['data'] = base64_decode($attachment['data']); + } elseif ($part->encoding == 4) { // Quoted-Printable + $attachment['data'] = quoted_printable_decode($attachment['data']); + } + + $attachments[] = $attachment; + } + + // If the part is HTML, return it + if ($part->type == 0 && strtolower($part->subtype) == 'html') { + $data = imap_fetchbody($imap, $email_number, $part_no ? $part_no : 1); + if ($part->encoding == 3) { + $data = base64_decode($data); + } elseif ($part->encoding == 4) { + $data = quoted_printable_decode($data); + } + return $data; + } + + // If there are sub-parts, recursively get the HTML part + if (isset($part->parts) && count($part->parts)) { + $index = 1; + foreach ($part->parts as $sub_part) { + $prefix = $part_no ? $part_no . '.' . $index : $index; + $result = get_part($imap, $email_number, $sub_part, $prefix, $attachments); + if ($result) { + return $result; + } + $index++; + } + } + + return ''; +} + +// Now, connect to the IMAP server +$mailbox = getMailboxString($config_imap_host, $config_imap_port, $config_imap_encryption, true); + +$imap = imap_open($mailbox, $config_imap_username, $config_imap_password); + +if (!$imap) { + echo "Error connecting to IMAP server: " . imap_last_error(); + exit; +} + +// Create the "ITFlow" mailbox folder if it doesn't exist +createMailboxFolder($imap, 'ITFlow'); + +// Search for unseen emails +$emails = imap_search($imap, 'UNSEEN'); + +if ($emails) { + foreach ($emails as $email_number) { + $email_processed = false; + + // Fetch the email header + $header = imap_headerinfo($imap, $email_number); + + // Fetch the email structure + $structure = imap_fetchstructure($imap, $email_number); + + $attachments = array(); + + // Get the message body + $message_body = getBody($imap, $email_number, $structure, $attachments); + + // Get the raw message for saving + $raw_header = imap_fetchheader($imap, $email_number); + $raw_body = imap_body($imap, $email_number); + $eml_content = $raw_header . $raw_body; + + // Save the original message + $original_message_file = "processed-eml-" . randomString(200) . ".eml"; + mkdirMissing('uploads/tmp/'); + file_put_contents("uploads/tmp/{$original_message_file}", $eml_content); + + // Get the from address + $from = $header->from[0]; + $from_name = sanitizeInput(imap_utf8($from->personal ?? 'Unknown')); + $from_email = sanitizeInput($from->mailbox . '@' . $from->host); + + // Get the subject + $subject = sanitizeInput(imap_utf8($header->subject ?? 'No Subject')); + + // Get the date + $date = sanitizeInput($header->date ?? date('Y-m-d H:i:s')); + + $from_domain = explode("@", $from_email); + $from_domain = sanitizeInput(end($from_domain)); + + // Now process the message + if (preg_match("/\[$config_ticket_prefix\d+\]/", $subject, $ticket_number_match)) { + preg_match('/\d+/', $ticket_number_match[0], $ticket_number); + $ticket_number = intval($ticket_number[0]); + + if (addReply($from_email, $date, $subject, $ticket_number, $message_body, $attachments)) { + $email_processed = true; + } + } else { + $from_email_esc = mysqli_real_escape_string($mysqli, $from_email); + $any_contact_sql = mysqli_query($mysqli, "SELECT * FROM contacts WHERE contact_email = '$from_email_esc' LIMIT 1"); + $row = mysqli_fetch_array($any_contact_sql); + + if ($row) { + $contact_name = sanitizeInput($row['contact_name']); + $contact_id = intval($row['contact_id']); + $contact_email = sanitizeInput($row['contact_email']); + $client_id = intval($row['contact_client_id']); + + if (addTicket($contact_id, $contact_name, $contact_email, $client_id, $date, $subject, $message_body, $attachments, $original_message_file)) { + $email_processed = true; + } + } else { + $from_domain_esc = mysqli_real_escape_string($mysqli, $from_domain); + $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT * FROM domains WHERE domain_name = '$from_domain_esc' LIMIT 1")); + + if ($row && $from_domain == $row['domain_name']) { + $client_id = intval($row['domain_client_id']); + + $password = password_hash(randomString(), PASSWORD_DEFAULT); + $contact_name = $from_name; + $contact_email = $from_email; + mysqli_query($mysqli, "INSERT INTO contacts SET contact_name = '".mysqli_real_escape_string($mysqli, $contact_name)."', contact_email = '".mysqli_real_escape_string($mysqli, $contact_email)."', contact_notes = 'Added automatically via email parsing.', contact_password_hash = '$password', contact_client_id = $client_id"); + $contact_id = mysqli_insert_id($mysqli); + + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Contact', log_action = 'Create', log_description = 'Email parser: created contact ".mysqli_real_escape_string($mysqli, $contact_name)."', log_client_id = $client_id"); + customAction('contact_create', $contact_id); + + if (addTicket($contact_id, $contact_name, $contact_email, $client_id, $date, $subject, $message_body, $attachments, $original_message_file)) { + $email_processed = true; + } + } elseif ($config_ticket_email_parse_unknown_senders) { + // Parse even if the sender is unknown + $bad_from_pattern = "/daemon|postmaster/i"; + if (!(preg_match($bad_from_pattern, $from_email))) { + if (addTicket(0, $from_name, $from_email, 0, $date, $subject, $message_body, $attachments, $original_message_file)) { + $email_processed = true; + } + } + } + } + } + + if ($email_processed) { + imap_setflag_full($imap, $email_number, "\\Seen"); + imap_mail_move($imap, $email_number, 'ITFlow'); + // Expunge the mailbox to apply changes + imap_expunge($imap); + } else { + echo "Failed to process email - flagging for manual review.\n"; + imap_setflag_full($imap, $email_number, "\\Flagged"); + // No need to expunge here unless desired + } + + if (file_exists("uploads/tmp/{$original_message_file}")) { + unlink("uploads/tmp/{$original_message_file}"); + } + } +} + +// Close the IMAP connection +imap_close($imap); + +// Remove the lock file +unlink($lock_file_path); From 77ed7aa7ff1401ac40c55f2ec918076429163931 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Thu, 17 Oct 2024 23:15:39 -0400 Subject: [PATCH 103/249] Updated new cron mail parser --- new_cron_ticket_email_parser.php | 421 +++++++++++-------------------- 1 file changed, 150 insertions(+), 271 deletions(-) diff --git a/new_cron_ticket_email_parser.php b/new_cron_ticket_email_parser.php index 6b8e8570..bd00d6b5 100644 --- a/new_cron_ticket_email_parser.php +++ b/new_cron_ticket_email_parser.php @@ -49,10 +49,10 @@ $lock_file_path = "{$temp_dir}/itflow_email_parser_{$installation_id}.lock"; if (file_exists($lock_file_path)) { $file_age = time() - filemtime($lock_file_path); - // If file is older than 5 minutes (300 seconds), delete and continue + // If file is older than 3 minutes (180 seconds), delete and continue if ($file_age > 300) { unlink($lock_file_path); - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Cron-Email-Parser', log_action = 'Delete', log_description = 'Cron Email Parser detected a lock file was present but was over 5 minutes old so it removed it'"); + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Cron-Email-Parser', log_action = 'Delete', log_description = 'Cron Email Parser detected a lock file was present but was over 10 minutes old so it removed it'"); } else { mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Cron-Email-Parser', log_action = 'Locked', log_description = 'Cron Email Parser attempted to execute but was already executing, so instead it terminated.'"); exit("Script is already running. Exiting."); @@ -62,12 +62,33 @@ if (file_exists($lock_file_path)) { // Create a lock file file_put_contents($lock_file_path, "Locked"); +// PHP Mail Parser +use PhpMimeMailParser\Parser; + +require_once "plugins/php-mime-mail-parser/Contracts/CharsetManager.php"; + +require_once "plugins/php-mime-mail-parser/Contracts/Middleware.php"; + +require_once "plugins/php-mime-mail-parser/Attachment.php"; + +require_once "plugins/php-mime-mail-parser/Charset.php"; + +require_once "plugins/php-mime-mail-parser/Exception.php"; + +require_once "plugins/php-mime-mail-parser/Middleware.php"; + +require_once "plugins/php-mime-mail-parser/MiddlewareStack.php"; + +require_once "plugins/php-mime-mail-parser/MimePart.php"; + +require_once "plugins/php-mime-mail-parser/Parser.php"; + // Allowed attachment extensions $allowed_extensions = array('jpg', 'jpeg', 'gif', 'png', 'webp', 'pdf', 'txt', 'md', 'doc', 'docx', 'csv', 'xls', 'xlsx', 'xlsm', 'zip', 'tar', 'gz'); // Function to raise a new ticket for a given contact and email them confirmation (if configured) function addTicket($contact_id, $contact_name, $contact_email, $client_id, $date, $subject, $message, $attachments, $original_message_file) { - global $mysqli, $config_app_name, $company_name, $company_phone, $config_ticket_prefix, $config_ticket_client_general_notifications, $config_ticket_new_ticket_notification_email, $config_base_url, $config_ticket_from_name, $config_ticket_from_email, $config_smtp_host, $config_smtp_port, $config_smtp_encryption, $config_smtp_username, $config_smtp_password, $allowed_extensions; + global $mysqli, $config_app_name, $company_name, $company_phone, $config_ticket_prefix, $config_ticket_client_general_notifications, $config_ticket_new_ticket_notification_email, $config_base_url, $config_ticket_from_name, $config_ticket_from_email, $allowed_extensions; $ticket_number_sql = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_ticket_next_number FROM settings WHERE company_id = 1")); $ticket_number = intval($ticket_number_sql['config_ticket_next_number']); @@ -76,9 +97,8 @@ function addTicket($contact_id, $contact_name, $contact_email, $client_id, $date // Clean up the message $message = trim($message); // Remove leading/trailing whitespace - - // Process inline images in the message - $message = processInlineImages($message, $attachments, $ticket_number); + $message = preg_replace('/\s+/', ' ', $message); // Replace multiple spaces with a single space + $message = nl2br($message); // Convert newlines to
// Wrap the message in a div with controlled line height $message = "Email from: $contact_name <$contact_email> at $date:-

$message
"; @@ -105,8 +125,27 @@ function addTicket($contact_id, $contact_name, $contact_email, $client_id, $date $original_message_file_esc = mysqli_real_escape_string($mysqli, $original_message_file); mysqli_query($mysqli, "INSERT INTO ticket_attachments SET ticket_attachment_name = 'Original-parsed-email.eml', ticket_attachment_reference_name = '$original_message_file_esc', ticket_attachment_ticket_id = $id"); - // Process attachments (excluding inline images) - processAttachments($attachments, $att_dir, $id, null, $contact_email); + foreach ($attachments as $attachment) { + $att_name = $attachment->getFilename(); + $att_extarr = explode('.', $att_name); + $att_extension = strtolower(end($att_extarr)); + + if (in_array($att_extension, $allowed_extensions)) { + $att_saved_filename = md5(uniqid(rand(), true)) . '.' . $att_extension; + $att_saved_path = $att_dir . $att_saved_filename; + file_put_contents($att_saved_path, $attachment->getContent()); + + $ticket_attachment_name = sanitizeInput($att_name); + $ticket_attachment_reference_name = sanitizeInput($att_saved_filename); + + $ticket_attachment_name_esc = mysqli_real_escape_string($mysqli, $ticket_attachment_name); + $ticket_attachment_reference_name_esc = mysqli_real_escape_string($mysqli, $ticket_attachment_reference_name); + mysqli_query($mysqli, "INSERT INTO ticket_attachments SET ticket_attachment_name = '$ticket_attachment_name_esc', ticket_attachment_reference_name = '$ticket_attachment_reference_name_esc', ticket_attachment_ticket_id = $id"); + } else { + $ticket_attachment_name_esc = mysqli_real_escape_string($mysqli, $att_name); + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Blocked attachment $ticket_attachment_name_esc from Client contact $contact_email_esc for ticket $ticket_prefix_esc$ticket_number', log_client_id = $client_id_esc"); + } + } $data = []; if ($config_ticket_client_general_notifications == 1) { @@ -153,16 +192,15 @@ function addTicket($contact_id, $contact_name, $contact_email, $client_id, $date // Add Reply Function function addReply($from_email, $date, $subject, $ticket_number, $message, $attachments) { - global $mysqli, $config_app_name, $company_name, $company_phone, $config_ticket_prefix, $config_base_url, $config_ticket_from_name, $config_ticket_from_email, $config_smtp_host, $config_smtp_port, $config_smtp_encryption, $config_smtp_username, $config_smtp_password, $allowed_extensions; + global $mysqli, $config_app_name, $company_name, $company_phone, $config_ticket_prefix, $config_base_url, $config_ticket_from_name, $config_ticket_from_email, $allowed_extensions; $ticket_reply_type = 'Client'; // Clean up the message $message_parts = explode("##- Please type your reply above this line -##", $message); $message_body = $message_parts[0]; $message_body = trim($message_body); // Remove leading/trailing whitespace - - // Process inline images in the message - $message_body = processInlineImages($message_body, $attachments, $ticket_number); + $message_body = preg_replace('/\r\n|\r|\n/', ' ', $message_body); // Replace newlines with a space + $message_body = nl2br($message_body); // Convert remaining newlines to
// Wrap the message in a div with controlled line height $message = "Email from: $from_email at $date:-

$message_body
"; @@ -230,16 +268,32 @@ function addReply($from_email, $date, $subject, $ticket_number, $message, $attac $reply_id = mysqli_insert_id($mysqli); mkdirMissing('uploads/tickets/'); - $att_dir = "uploads/tickets/" . $ticket_id . "/"; - mkdirMissing($att_dir); + foreach ($attachments as $attachment) { + $att_name = $attachment->getFilename(); + $att_extarr = explode('.', $att_name); + $att_extension = strtolower(end($att_extarr)); - // Process attachments (excluding inline images) - processAttachments($attachments, $att_dir, $ticket_id, $reply_id, $from_email); + if (in_array($att_extension, $allowed_extensions)) { + $att_saved_filename = md5(uniqid(rand(), true)) . '.' . $att_extension; + $att_saved_path = "uploads/tickets/" . $ticket_id . "/" . $att_saved_filename; + file_put_contents($att_saved_path, $attachment->getContent()); - $ticket_assigned_to = mysqli_query($mysqli, "SELECT ticket_assigned_to FROM tickets WHERE ticket_id = $ticket_id LIMIT 1"); + $ticket_attachment_name = sanitizeInput($att_name); + $ticket_attachment_reference_name = sanitizeInput($att_saved_filename); - if ($ticket_assigned_to) { - $row = mysqli_fetch_array($ticket_assigned_to); + $ticket_attachment_name_esc = mysqli_real_escape_string($mysqli, $ticket_attachment_name); + $ticket_attachment_reference_name_esc = mysqli_real_escape_string($mysqli, $ticket_attachment_reference_name); + mysqli_query($mysqli, "INSERT INTO ticket_attachments SET ticket_attachment_name = '$ticket_attachment_name_esc', ticket_attachment_reference_name = '$ticket_attachment_reference_name_esc', ticket_attachment_reply_id = $reply_id, ticket_attachment_ticket_id = $ticket_id"); + } else { + $ticket_attachment_name_esc = mysqli_real_escape_string($mysqli, $att_name); + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Blocked attachment $ticket_attachment_name_esc from Client contact $from_email_esc for ticket $config_ticket_prefix$ticket_number_esc', log_client_id = $client_id"); + } + } + + $ticket_assigned_to_sql = mysqli_query($mysqli, "SELECT ticket_assigned_to FROM tickets WHERE ticket_id = $ticket_id LIMIT 1"); + + if ($ticket_assigned_to_sql) { + $row = mysqli_fetch_array($ticket_assigned_to_sql); $ticket_assigned_to = intval($row['ticket_assigned_to']); if ($ticket_assigned_to) { @@ -279,293 +333,111 @@ function addReply($from_email, $date, $subject, $ticket_number, $message, $attac } } -// Function to process inline images in the HTML body -function processInlineImages($html_body, &$attachments, $ticket_number) { - global $config_base_url; - - // Create a mapping of Content-ID to attachment data - $inline_images = array(); - foreach ($attachments as $key => $attachment) { - if (!empty($attachment['content_id'])) { - $content_id = trim($attachment['content_id'], '<>'); - $inline_images[$content_id] = $attachment; - unset($attachments[$key]); // Remove inline images from attachments array - } - } - - // Replace cid references in the HTML body - if (!empty($inline_images)) { - // Ensure the images directory exists - $images_dir = "uploads/inline_images/"; - mkdirMissing($images_dir); - - foreach ($inline_images as $content_id => $attachment) { - $att_name = $attachment['filename']; - $att_extarr = explode('.', $att_name); - $att_extension = strtolower(end($att_extarr)); - - // Generate a unique filename - $att_saved_filename = md5(uniqid(rand(), true)) . '.' . $att_extension; - $att_saved_path = $images_dir . $att_saved_filename; - - // Save the inline image - file_put_contents($att_saved_path, $attachment['data']); - - // Update the HTML body to point to the saved image - $html_body = str_replace('cid:' . $content_id, "https://$config_base_url/$att_saved_path", $html_body); - } - } - - return $html_body; -} - -// Function to process attachments (excluding inline images) -function processAttachments($attachments, $att_dir, $ticket_id, $reply_id = null, $from_email = '') { - global $mysqli, $allowed_extensions, $config_ticket_prefix; - - foreach ($attachments as $attachment) { - $att_name = $attachment['filename']; - $att_extarr = explode('.', $att_name); - $att_extension = strtolower(end($att_extarr)); - - if (in_array($att_extension, $allowed_extensions)) { - $att_saved_filename = md5(uniqid(rand(), true)) . '.' . $att_extension; - $att_saved_path = $att_dir . $att_saved_filename; - file_put_contents($att_saved_path, $attachment['data']); - - $ticket_attachment_name = sanitizeInput($att_name); - $ticket_attachment_reference_name = sanitizeInput($att_saved_filename); - - $ticket_attachment_name_esc = mysqli_real_escape_string($mysqli, $ticket_attachment_name); - $ticket_attachment_reference_name_esc = mysqli_real_escape_string($mysqli, $ticket_attachment_reference_name); - - $reply_clause = $reply_id ? ", ticket_attachment_reply_id = $reply_id" : ""; - - mysqli_query($mysqli, "INSERT INTO ticket_attachments SET ticket_attachment_name = '$ticket_attachment_name_esc', ticket_attachment_reference_name = '$ticket_attachment_reference_name_esc', ticket_attachment_ticket_id = $ticket_id $reply_clause"); - } else { - $ticket_attachment_name_esc = mysqli_real_escape_string($mysqli, $att_name); - $from_email_esc = mysqli_real_escape_string($mysqli, $from_email); - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Blocked attachment $ticket_attachment_name_esc from Client contact $from_email_esc for ticket $config_ticket_prefix$ticket_id', log_client_id = $ticket_id"); - } - } -} - -// Function to construct the mailbox string -function getMailboxString($host, $port, $encryption, $validate_cert, $folder = 'INBOX') { - $mailbox = "{" . $host . ":" . $port . "/"; - - // Handle encryption - if ($encryption == 'ssl') { - $mailbox .= 'imap/ssl'; - } elseif ($encryption == 'tls') { - $mailbox .= 'imap/tls'; - } else { - $mailbox .= 'imap'; - } - - // Handle validate_cert - if (!$validate_cert) { - $mailbox .= '/novalidate-cert'; - } - - $mailbox .= '}' . $folder; - - return $mailbox; -} - // Function to create a folder in the mailbox if it doesn't exist -function createMailboxFolder($imap, $folderName) { - $mailboxPath = "{" . $GLOBALS['config_imap_host'] . "}" . $folderName; - $folders = imap_list($imap, "{" . $GLOBALS['config_imap_host'] . "}", "*"); +function createMailboxFolder($imap, $mailbox, $folderName) { + $folders = imap_list($imap, $mailbox, '*'); $folderExists = false; - if ($folders) { + if ($folders !== false) { foreach ($folders as $folder) { - $folderShortName = str_replace("{" . $GLOBALS['config_imap_host'] . "}", '', $folder); - if ($folderShortName == $folderName) { + $folder = str_replace($mailbox, '', $folder); + if ($folder == $folderName) { $folderExists = true; break; } } } - if (!$folderExists) { - if (imap_createmailbox($imap, imap_utf7_encode($mailboxPath))) { - echo "Folder '$folderName' created successfully.\n"; - // Subscribe to the folder - imap_subscribe($imap, imap_utf7_encode($mailboxPath)); - } else { - echo "Error creating folder '$folderName': " . imap_last_error() . "\n"; - } + imap_createmailbox($imap, $mailbox . imap_utf7_encode($folderName)); + imap_subscribe($imap, $mailbox . $folderName); } } -// Function to get the body of the email -function getBody($imap, $email_number, $structure, &$attachments) { - $body = ''; +// Initialize IMAP connection +$validate_cert = true; // or false based on your configuration - if (!isset($structure->parts)) { // Simple message, no attachments - $body = imap_body($imap, $email_number); - if ($structure->encoding == 3) { // Base64 - $body = base64_decode($body); - } elseif ($structure->encoding == 4) { // Quoted-Printable - $body = quoted_printable_decode($body); - } - } else { - // Multipart message - $body = get_part($imap, $email_number, $structure, 0, $attachments); - } +$imap_encryption = $config_imap_encryption; // e.g., 'ssl' or 'tls' - return $body; +$mailbox = '{' . $config_imap_host . ':' . $config_imap_port . '/' . $imap_encryption; +if ($validate_cert) { + $mailbox .= '/validate-cert'; +} else { + $mailbox .= '/novalidate-cert'; } +$mailbox .= '}'; +$inbox_mailbox = $mailbox . 'INBOX'; -function get_part($imap, $email_number, $part, $part_no, &$attachments) { - $data = ''; - $params = array(); - if ($part->ifparameters) { - foreach ($part->parameters as $param) { - $params[strtolower($param->attribute)] = $param->value; - } - } - if ($part->ifdparameters) { - foreach ($part->dparameters as $param) { - $params[strtolower($param->attribute)] = $param->value; - } - } +$imap = imap_open($inbox_mailbox, $config_imap_username, $config_imap_password); - // Identify if the part is an attachment - $is_attachment = false; - $filename = ''; - $name = ''; - $content_id = ''; - - if ($part->ifdisposition) { - if (strtolower($part->disposition) == 'attachment' || strtolower($part->disposition) == 'inline') { - $is_attachment = true; - } - } - - if ($part->ifid) { - $content_id = trim($part->id, '<>'); - $is_attachment = true; - } - - if ($params) { - if (isset($params['filename']) || isset($params['name'])) { - $is_attachment = true; - $filename = isset($params['filename']) ? $params['filename'] : $params['name']; - } - } - - // If it's an attachment, process it - if ($is_attachment) { - $attachment = array( - 'filename' => $filename, - 'content_id' => $content_id, - 'data' => imap_fetchbody($imap, $email_number, $part_no ? $part_no : 1), - 'encoding' => $part->encoding - ); - - // Decode the data - if ($part->encoding == 3) { // Base64 - $attachment['data'] = base64_decode($attachment['data']); - } elseif ($part->encoding == 4) { // Quoted-Printable - $attachment['data'] = quoted_printable_decode($attachment['data']); - } - - $attachments[] = $attachment; - } - - // If the part is HTML, return it - if ($part->type == 0 && strtolower($part->subtype) == 'html') { - $data = imap_fetchbody($imap, $email_number, $part_no ? $part_no : 1); - if ($part->encoding == 3) { - $data = base64_decode($data); - } elseif ($part->encoding == 4) { - $data = quoted_printable_decode($data); - } - return $data; - } - - // If there are sub-parts, recursively get the HTML part - if (isset($part->parts) && count($part->parts)) { - $index = 1; - foreach ($part->parts as $sub_part) { - $prefix = $part_no ? $part_no . '.' . $index : $index; - $result = get_part($imap, $email_number, $sub_part, $prefix, $attachments); - if ($result) { - return $result; - } - $index++; - } - } - - return ''; -} - -// Now, connect to the IMAP server -$mailbox = getMailboxString($config_imap_host, $config_imap_port, $config_imap_encryption, true); - -$imap = imap_open($mailbox, $config_imap_username, $config_imap_password); - -if (!$imap) { +if ($imap === false) { echo "Error connecting to IMAP server: " . imap_last_error(); exit; } // Create the "ITFlow" mailbox folder if it doesn't exist -createMailboxFolder($imap, 'ITFlow'); +createMailboxFolder($imap, $mailbox, 'ITFlow'); -// Search for unseen emails +// Search for unseen messages $emails = imap_search($imap, 'UNSEEN'); -if ($emails) { +if ($emails !== false) { foreach ($emails as $email_number) { $email_processed = false; - // Fetch the email header - $header = imap_headerinfo($imap, $email_number); - - // Fetch the email structure - $structure = imap_fetchstructure($imap, $email_number); - - $attachments = array(); - - // Get the message body - $message_body = getBody($imap, $email_number, $structure, $attachments); - - // Get the raw message for saving - $raw_header = imap_fetchheader($imap, $email_number); - $raw_body = imap_body($imap, $email_number); - $eml_content = $raw_header . $raw_body; - - // Save the original message - $original_message_file = "processed-eml-" . randomString(200) . ".eml"; + // Save original message mkdirMissing('uploads/tmp/'); - file_put_contents("uploads/tmp/{$original_message_file}", $eml_content); + $original_message_file = "processed-eml-" . randomString(200) . ".eml"; - // Get the from address - $from = $header->from[0]; - $from_name = sanitizeInput(imap_utf8($from->personal ?? 'Unknown')); - $from_email = sanitizeInput($from->mailbox . '@' . $from->host); + $raw_message = imap_fetchheader($imap, $email_number) . imap_body($imap, $email_number); + file_put_contents("uploads/tmp/{$original_message_file}", $raw_message); - // Get the subject - $subject = sanitizeInput(imap_utf8($header->subject ?? 'No Subject')); + // Parse the message using php-mime-mail-parser + $parser = new \PhpMimeMailParser\Parser(); + $parser->setText($raw_message); - // Get the date - $date = sanitizeInput($header->date ?? date('Y-m-d H:i:s')); + // Get from address + $from_addresses = $parser->getAddresses('from'); + $from_email = sanitizeInput($from_addresses[0]['address'] ?? 'itflow-guest@example.com'); + $from_name = sanitizeInput($from_addresses[0]['display'] ?? 'Unknown'); $from_domain = explode("@", $from_email); $from_domain = sanitizeInput(end($from_domain)); - // Now process the message - if (preg_match("/\[$config_ticket_prefix\d+\]/", $subject, $ticket_number_match)) { - preg_match('/\d+/', $ticket_number_match[0], $ticket_number); - $ticket_number = intval($ticket_number[0]); + // Get subject + $subject = sanitizeInput($parser->getHeader('subject') ?? 'No Subject'); + + // Get date + $date = sanitizeInput($parser->getHeader('date') ?? date('Y-m-d H:i:s')); + + // Get message body + $message_body_html = $parser->getMessageBody('html'); + $message_body_text = $parser->getMessageBody('text'); + $message_body = $message_body_html ?: nl2br(htmlspecialchars($message_body_text)); + + // Handle inline images + $attachments = $parser->getAttachments(); + $inline_attachments = []; + foreach ($attachments as $attachment) { + if ($attachment->getContentDisposition() === 'inline' && $attachment->getContentID()) { + $cid = trim($attachment->getContentID(), '<>'); + $data = base64_encode($attachment->getContent()); + $mime = $attachment->getContentType(); // Use getContentType() instead of getMimeType() + $dataUri = "data:$mime;base64,$data"; + $message_body = str_replace("cid:$cid", $dataUri, $message_body); + } else { + $inline_attachments[] = $attachment; + } + } + $attachments = $inline_attachments; + + // Process the email + if (preg_match("/\[$config_ticket_prefix(\d+)\]/", $subject, $ticket_number_matches)) { + $ticket_number = intval($ticket_number_matches[1]); if (addReply($from_email, $date, $subject, $ticket_number, $message_body, $attachments)) { $email_processed = true; } } else { + // Check if the sender is a known contact $from_email_esc = mysqli_real_escape_string($mysqli, $from_email); $any_contact_sql = mysqli_query($mysqli, "SELECT * FROM contacts WHERE contact_email = '$from_email_esc' LIMIT 1"); $row = mysqli_fetch_array($any_contact_sql); @@ -580,12 +452,15 @@ if ($emails) { $email_processed = true; } } else { + // Check if the domain is associated with a client $from_domain_esc = mysqli_real_escape_string($mysqli, $from_domain); - $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT * FROM domains WHERE domain_name = '$from_domain_esc' LIMIT 1")); + $domain_sql = mysqli_query($mysqli, "SELECT * FROM domains WHERE domain_name = '$from_domain_esc' LIMIT 1"); + $row = mysqli_fetch_assoc($domain_sql); if ($row && $from_domain == $row['domain_name']) { $client_id = intval($row['domain_client_id']); + // Create a new contact $password = password_hash(randomString(), PASSWORD_DEFAULT); $contact_name = $from_name; $contact_email = $from_email; @@ -598,7 +473,7 @@ if ($emails) { if (addTicket($contact_id, $contact_name, $contact_email, $client_id, $date, $subject, $message_body, $attachments, $original_message_file)) { $email_processed = true; } - } elseif ($config_ticket_email_parse_unknown_senders) { + } elseif ($config_ticket_email_parse_unknown_senders) { // Parse even if the sender is unknown $bad_from_pattern = "/daemon|postmaster/i"; if (!(preg_match($bad_from_pattern, $from_email))) { @@ -611,24 +486,28 @@ if ($emails) { } if ($email_processed) { + // Mark the message as seen imap_setflag_full($imap, $email_number, "\\Seen"); + // Move the message to the 'ITFlow' folder imap_mail_move($imap, $email_number, 'ITFlow'); - // Expunge the mailbox to apply changes - imap_expunge($imap); } else { - echo "Failed to process email - flagging for manual review.\n"; + // Flag the message for manual review imap_setflag_full($imap, $email_number, "\\Flagged"); - // No need to expunge here unless desired } + // Delete the temporary message file if (file_exists("uploads/tmp/{$original_message_file}")) { unlink("uploads/tmp/{$original_message_file}"); } } } +// Expunge deleted mails +imap_expunge($imap); + // Close the IMAP connection imap_close($imap); // Remove the lock file unlink($lock_file_path); +?> From fe86ca0dd7d7aaf10761a1e44f17fc6e208f9a6c Mon Sep 17 00:00:00 2001 From: johnnyq Date: Thu, 17 Oct 2024 23:33:53 -0400 Subject: [PATCH 104/249] Bump php-mime-mail-parser from 8.0 to 8.0.4 --- plugins/php-mime-mail-parser/Parser.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/php-mime-mail-parser/Parser.php b/plugins/php-mime-mail-parser/Parser.php index 94c2a300..7f8a1de9 100644 --- a/plugins/php-mime-mail-parser/Parser.php +++ b/plugins/php-mime-mail-parser/Parser.php @@ -139,7 +139,7 @@ class Parser { // streams have to be cached to file first $meta = @stream_get_meta_data($stream); - if (!$meta || !$meta['mode'] || !in_array($meta['mode'], self::$readableModes, true) || $meta['eof']) { + if (!$meta || !$meta['mode'] || !in_array($meta['mode'], self::$readableModes, true)) { throw new Exception( 'setStream() expects parameter stream to be readable stream resource.' ); @@ -224,7 +224,7 @@ class Parser * * @param string $name Header name (case-insensitive) * - * @return string|array|bool + * @return string|bool * @throws Exception */ public function getRawHeader($name) @@ -446,11 +446,11 @@ class Parser } /** - * Return an array of associative arrays with the following keys `display`, `address`, `is_group` + * Return an array with the following keys display, address, is_group * * @param string $name Header name (case-insensitive) * - * @return array + * @return array */ public function getAddresses($name) { From d01d912154bc2764f4708697a9887645ab9e0857 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Fri, 18 Oct 2024 00:12:08 -0400 Subject: [PATCH 105/249] Move logout to a file logout.php in /post fixes issue where one cant logout if in admin section, also redirect to login page with or without login key if set --- post.php | 3 ++- post/logout.php | 30 ++++++++++++++++++++++++++++++ post/user/profile.php | 19 ------------------- 3 files changed, 32 insertions(+), 20 deletions(-) create mode 100644 post/logout.php diff --git a/post.php b/post.php index 3fc24216..f09d2141 100644 --- a/post.php +++ b/post.php @@ -55,12 +55,13 @@ if (str_contains($module, 'admin') && isset($session_is_admin) && $session_is_ad } +// Logout is the same for user and admin +require_once "post/logout.php"; // TODO: Move admin_update into the admin section to be auto-loaded // We can't do this until everyone has the new database fields added in 1.4.9 on Sept 14th 2024 require_once "post/admin_update.php"; // Load updater - // TODO: Find a home for these require_once "post/ai.php"; diff --git a/post/logout.php b/post/logout.php new file mode 100644 index 00000000..3645d4f6 --- /dev/null +++ b/post/logout.php @@ -0,0 +1,30 @@ + diff --git a/post/user/profile.php b/post/user/profile.php index 92269aab..a2eaf6e0 100644 --- a/post/user/profile.php +++ b/post/user/profile.php @@ -285,22 +285,3 @@ if (isset($_POST['revoke_your_2fa_remember_tokens'])) { header("Location: " . $_SERVER["HTTP_REFERER"]); } - -if (isset($_GET['logout'])) { - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Logout', log_action = 'Success', log_description = '$session_name logged out', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - mysqli_query($mysqli, "UPDATE users SET user_php_session = '' WHERE user_id = $session_user_id"); - - setcookie("PHPSESSID", '', time() - 3600, "/"); - unset($_COOKIE['PHPSESSID']); - - setcookie("user_encryption_session_key", '', time() - 3600, "/"); - unset($_COOKIE['user_encryption_session_key']); - - setcookie("user_extension_key", '', time() - 3600, "/"); - unset($_COOKIE['user_extension_key']); - - session_unset(); - session_destroy(); - - header('Location: login.php?key=' . $config_login_key_secret); -} From 6c1c5d6523f05eba4b62d190c4d6657a076c215c Mon Sep 17 00:00:00 2001 From: johnnyq Date: Fri, 18 Oct 2024 15:11:54 -0400 Subject: [PATCH 106/249] Simplify Ticket Template Creation by putting all fields into 1 modal tab instead of multiple and removing the ability to ass tasks via add ticket template, this should be done in the details --- admin_ticket_template_add_modal.php | 173 ++++++++++----------------- admin_ticket_template_edit_modal.php | 106 +++++++--------- post/admin/admin_ticket_template.php | 10 -- 3 files changed, 104 insertions(+), 185 deletions(-) diff --git a/admin_ticket_template_add_modal.php b/admin_ticket_template_add_modal.php index f9859a6d..c95fd372 100644 --- a/admin_ticket_template_add_modal.php +++ b/admin_ticket_template_add_modal.php @@ -9,121 +9,72 @@