diff --git a/calendar_events.php b/calendar_events.php index 8525db36..85c11277 100644 --- a/calendar_events.php +++ b/calendar_events.php @@ -41,18 +41,16 @@ while ($row = mysqli_fetch_array($sql)) { $client_id = intval($row['event_client_id']); require "calendar_event_edit_modal.php"; - } ?> +?> @@ -210,4 +206,4 @@ while ($row = mysqli_fetch_array($sql)) { // Update the end date field document.getElementById("event_add_end").value = new_end; } - + \ No newline at end of file diff --git a/cron_mail_queue.php b/cron_mail_queue.php index fd6782a1..6b13de1d 100644 --- a/cron_mail_queue.php +++ b/cron_mail_queue.php @@ -24,7 +24,7 @@ if ($config_enable_cron == 0) { } // Check Cron Key -if ( $argv[1] !== $config_cron_key ) { +if ($argv[1] !== $config_cron_key) { exit("Cron Key invalid -- Quitting.."); } @@ -37,7 +37,7 @@ $lock_file_path = "{$temp_dir}/itflow_mail_queue_{$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 10 minutes (600 seconds), delete and continue if ($file_age > 600) { unlink($lock_file_path); @@ -111,7 +111,7 @@ if (mysqli_num_rows($sql_queue) > 0) { // Update Message mysqli_query($mysqli, "UPDATE email_queue SET email_status = 3, email_sent_at = NOW(), email_attempts = 1 WHERE email_id = $email_id"); } - } + } } } @@ -131,6 +131,7 @@ if (mysqli_num_rows($sql_failed_queue) > 0) { $email_content = $row['email_content']; $email_queued_at = $row['email_queued_at']; $email_sent_at = $row['email_sent_at']; + $email_ics_str = $row['email_cal_str']; // Increment the attempts $email_attempts = intval($row['email_attempts']) + 1; @@ -155,7 +156,8 @@ if (mysqli_num_rows($sql_failed_queue) > 0) { $email_recipient, $email_recipient_name, $email_subject, - $email_content + $email_content, + $email_ics_str ); if ($mail !== true) { @@ -168,7 +170,7 @@ if (mysqli_num_rows($sql_failed_queue) > 0) { // Update Message mysqli_query($mysqli, "UPDATE email_queue SET email_status = 3, email_sent_at = NOW(), email_attempts = $email_attempts WHERE email_id = $email_id"); } - } + } } } diff --git a/dashboard.php b/dashboard.php index 963b0cee..27feae1c 100644 --- a/dashboard.php +++ b/dashboard.php @@ -34,7 +34,8 @@ $sql_years_select = mysqli_query( UNION DISTINCT SELECT YEAR(client_created_at) FROM clients UNION DISTINCT SELECT YEAR(user_created_at) FROM users ORDER BY all_years DESC -"); +" +); ?> @@ -50,27 +51,33 @@ $sql_years_select = mysqli_query( if (empty($year_select)) { $year_select = date('Y'); } - ?> - + ?> + - -
- > - -
- +
+ > + +
+ - = 2 && $config_module_enable_ticketing == 1) { ?> -
- > - -
- + = 2 && $config_module_enable_ticketing == 1) { ?> +
+ > + +
+ @@ -78,487 +85,489 @@ $sql_years_select = mysqli_query( if ($user_config_dashboard_financial_enable == 1) { -// Enforce accountant / admin role for the financial dashboard -if ($_SESSION['user_role'] != 3 && $_SESSION['user_role'] != 1) { - exit(''); -} + // Enforce accountant / admin role for the financial dashboard + if ($_SESSION['user_role'] != 3 && $_SESSION['user_role'] != 1) { + exit(''); + } -//Define var so it doesnt throw errors in logs -$largest_income_month = 0; + //Define var so it doesnt throw errors in logs + $largest_income_month = 0; -//Get Total income -$sql_total_payments_to_invoices = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS total_payments_to_invoices FROM payments WHERE YEAR(payment_date) = $year"); -$row = mysqli_fetch_array($sql_total_payments_to_invoices); -$total_payments_to_invoices = floatval($row['total_payments_to_invoices']); -//Do not grab transfer payment as these have a category_id of 0 -$sql_total_revenues = mysqli_query($mysqli, "SELECT SUM(revenue_amount) AS total_revenues FROM revenues WHERE YEAR(revenue_date) = $year AND revenue_category_id > 0"); -$row = mysqli_fetch_array($sql_total_revenues); -$total_revenues = floatval($row['total_revenues']); + //Get Total income + $sql_total_payments_to_invoices = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS total_payments_to_invoices FROM payments WHERE YEAR(payment_date) = $year"); + $row = mysqli_fetch_array($sql_total_payments_to_invoices); + $total_payments_to_invoices = floatval($row['total_payments_to_invoices']); + //Do not grab transfer payment as these have a category_id of 0 + $sql_total_revenues = mysqli_query($mysqli, "SELECT SUM(revenue_amount) AS total_revenues FROM revenues WHERE YEAR(revenue_date) = $year AND revenue_category_id > 0"); + $row = mysqli_fetch_array($sql_total_revenues); + $total_revenues = floatval($row['total_revenues']); -$total_income = $total_payments_to_invoices + $total_revenues; + $total_income = $total_payments_to_invoices + $total_revenues; -//Get Total expenses and do not grab transfer expenses as these have a vendor of 0 -$sql_total_expenses = mysqli_query($mysqli, "SELECT SUM(expense_amount) AS total_expenses FROM expenses WHERE expense_vendor_id > 0 AND YEAR(expense_date) = $year"); -$row = mysqli_fetch_array($sql_total_expenses); -$total_expenses = floatval($row['total_expenses']); + //Get Total expenses and do not grab transfer expenses as these have a vendor of 0 + $sql_total_expenses = mysqli_query($mysqli, "SELECT SUM(expense_amount) AS total_expenses FROM expenses WHERE expense_vendor_id > 0 AND YEAR(expense_date) = $year"); + $row = mysqli_fetch_array($sql_total_expenses); + $total_expenses = floatval($row['total_expenses']); -//Total up all the Invoices that are not draft or cancelled -$sql_invoice_totals = mysqli_query($mysqli, "SELECT SUM(invoice_amount) AS invoice_totals FROM invoices WHERE invoice_status NOT LIKE 'Draft' AND invoice_status NOT LIKE 'Cancelled' AND YEAR(invoice_date) = $year"); -$row = mysqli_fetch_array($sql_invoice_totals); -$invoice_totals = floatval($row['invoice_totals']); + //Total up all the Invoices that are not draft or cancelled + $sql_invoice_totals = mysqli_query($mysqli, "SELECT SUM(invoice_amount) AS invoice_totals FROM invoices WHERE invoice_status NOT LIKE 'Draft' AND invoice_status NOT LIKE 'Cancelled' AND YEAR(invoice_date) = $year"); + $row = mysqli_fetch_array($sql_invoice_totals); + $invoice_totals = floatval($row['invoice_totals']); -//Quaeries from Receivables -$sql_total_payments_to_invoices_all_years = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS total_payments_to_invoices_all_years FROM payments"); -$row = mysqli_fetch_array($sql_total_payments_to_invoices_all_years); -$total_payments_to_invoices_all_years = floatval($row['total_payments_to_invoices_all_years']); + //Quaeries from Receivables + $sql_total_payments_to_invoices_all_years = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS total_payments_to_invoices_all_years FROM payments"); + $row = mysqli_fetch_array($sql_total_payments_to_invoices_all_years); + $total_payments_to_invoices_all_years = floatval($row['total_payments_to_invoices_all_years']); -$sql_invoice_totals_all_years = mysqli_query($mysqli, "SELECT SUM(invoice_amount) AS invoice_totals_all_years FROM invoices WHERE invoice_status NOT LIKE 'Draft' AND invoice_status NOT LIKE 'Cancelled'"); -$row = mysqli_fetch_array($sql_invoice_totals_all_years); -$invoice_totals_all_years = floatval($row['invoice_totals_all_years']); + $sql_invoice_totals_all_years = mysqli_query($mysqli, "SELECT SUM(invoice_amount) AS invoice_totals_all_years FROM invoices WHERE invoice_status NOT LIKE 'Draft' AND invoice_status NOT LIKE 'Cancelled'"); + $row = mysqli_fetch_array($sql_invoice_totals_all_years); + $invoice_totals_all_years = floatval($row['invoice_totals_all_years']); -$receivables = $invoice_totals_all_years - $total_payments_to_invoices_all_years; + $receivables = $invoice_totals_all_years - $total_payments_to_invoices_all_years; -$profit = $total_income - $total_expenses; + $profit = $total_income - $total_expenses; -$sql_accounts = mysqli_query($mysqli, "SELECT * FROM accounts WHERE account_archived_at IS NULL ORDER BY account_name ASC"); + $sql_accounts = mysqli_query($mysqli, "SELECT * FROM accounts WHERE account_archived_at IS NULL ORDER BY account_name ASC"); -$sql_latest_invoice_payments = mysqli_query( - $mysqli, - "SELECT * FROM payments, invoices, clients + $sql_latest_invoice_payments = mysqli_query( + $mysqli, + "SELECT * FROM payments, invoices, clients WHERE payment_invoice_id = invoice_id AND invoice_client_id = client_id ORDER BY payment_id DESC LIMIT 5" -); + ); -$sql_latest_expenses = mysqli_query( - $mysqli, - "SELECT * FROM expenses, vendors, categories + $sql_latest_expenses = mysqli_query( + $mysqli, + "SELECT * FROM expenses, vendors, categories WHERE expense_vendor_id = vendor_id AND expense_category_id = category_id ORDER BY expense_id DESC LIMIT 5" -); + ); -//Get Yearly Recurring Income Total -$sql_recurring_yearly_total = mysqli_query($mysqli, "SELECT SUM(recurring_amount) AS recurring_yearly_total FROM recurring WHERE recurring_status = 1 AND recurring_frequency = 'year' AND YEAR(recurring_created_at) <= $year"); -$row = mysqli_fetch_array($sql_recurring_yearly_total); -$recurring_yearly_total = floatval($row['recurring_yearly_total']); + //Get Yearly Recurring Income Total + $sql_recurring_yearly_total = mysqli_query($mysqli, "SELECT SUM(recurring_amount) AS recurring_yearly_total FROM recurring WHERE recurring_status = 1 AND recurring_frequency = 'year' AND YEAR(recurring_created_at) <= $year"); + $row = mysqli_fetch_array($sql_recurring_yearly_total); + $recurring_yearly_total = floatval($row['recurring_yearly_total']); -//Get Monthly Recurring Income Total -$sql_recurring_monthly_total = mysqli_query($mysqli, "SELECT SUM(recurring_amount) AS recurring_monthly_total FROM recurring WHERE recurring_status = 1 AND recurring_frequency = 'month' AND YEAR(recurring_created_at) <= $year"); -$row = mysqli_fetch_array($sql_recurring_monthly_total); -$recurring_monthly_total = floatval($row['recurring_monthly_total']) + ($recurring_yearly_total / 12); + //Get Monthly Recurring Income Total + $sql_recurring_monthly_total = mysqli_query($mysqli, "SELECT SUM(recurring_amount) AS recurring_monthly_total FROM recurring WHERE recurring_status = 1 AND recurring_frequency = 'month' AND YEAR(recurring_created_at) <= $year"); + $row = mysqli_fetch_array($sql_recurring_monthly_total); + $recurring_monthly_total = floatval($row['recurring_monthly_total']) + ($recurring_yearly_total / 12); -//Get Yearly Recurring Expenses Total -$sql_recurring_expense_yearly_total = mysqli_query($mysqli, "SELECT SUM(recurring_expense_amount) AS recurring_expense_yearly_total FROM recurring_expenses WHERE recurring_expense_status = 1 AND recurring_expense_frequency = 2 AND YEAR(recurring_expense_created_at) <= $year"); -$row = mysqli_fetch_array($sql_recurring_expense_yearly_total); -$recurring_expense_yearly_total = floatval($row['recurring_expense_yearly_total']); + //Get Yearly Recurring Expenses Total + $sql_recurring_expense_yearly_total = mysqli_query($mysqli, "SELECT SUM(recurring_expense_amount) AS recurring_expense_yearly_total FROM recurring_expenses WHERE recurring_expense_status = 1 AND recurring_expense_frequency = 2 AND YEAR(recurring_expense_created_at) <= $year"); + $row = mysqli_fetch_array($sql_recurring_expense_yearly_total); + $recurring_expense_yearly_total = floatval($row['recurring_expense_yearly_total']); -//Get Monthly Recurring Expenses Total -$sql_recurring_expense_monthly_total = mysqli_query($mysqli, "SELECT SUM(recurring_expense_amount) AS recurring_expense_monthly_total FROM recurring_expenses WHERE recurring_expense_status = 1 AND recurring_expense_frequency = 1 AND YEAR(recurring_expense_created_at) <= $year"); -$row = mysqli_fetch_array($sql_recurring_expense_monthly_total); -$recurring_expense_monthly_total = floatval($row['recurring_expense_monthly_total']) + ($recurring_expense_yearly_total / 12); + //Get Monthly Recurring Expenses Total + $sql_recurring_expense_monthly_total = mysqli_query($mysqli, "SELECT SUM(recurring_expense_amount) AS recurring_expense_monthly_total FROM recurring_expenses WHERE recurring_expense_status = 1 AND recurring_expense_frequency = 1 AND YEAR(recurring_expense_created_at) <= $year"); + $row = mysqli_fetch_array($sql_recurring_expense_monthly_total); + $recurring_expense_monthly_total = floatval($row['recurring_expense_monthly_total']) + ($recurring_expense_yearly_total / 12); -//Get Total Miles Driven -$sql_miles_driven = mysqli_query($mysqli, "SELECT SUM(trip_miles) AS total_miles FROM trips WHERE YEAR(trip_date) = $year"); -$row = mysqli_fetch_array($sql_miles_driven); -$total_miles = floatval($row['total_miles']); + //Get Total Miles Driven + $sql_miles_driven = mysqli_query($mysqli, "SELECT SUM(trip_miles) AS total_miles FROM trips WHERE YEAR(trip_date) = $year"); + $row = mysqli_fetch_array($sql_miles_driven); + $total_miles = floatval($row['total_miles']); -if ($config_module_enable_ticketing && $config_module_enable_accounting) { - //Get Unbilled, closed tickets - $sql_unbilled_tickets = mysqli_query($mysqli, "SELECT COUNT('ticket_id') AS unbilled_tickets FROM tickets WHERE ticket_status = 'Closed' AND ticket_billable = 1 AND ticket_invoice_id = 0 AND YEAR(ticket_created_at) = $year"); - $row = mysqli_fetch_array($sql_unbilled_tickets); - $unbilled_tickets = intval($row['unbilled_tickets']); -} else { - //Get Total Recurring Invoices added - $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('recurring_id') AS recurring_invoices_added FROM recurring WHERE YEAR(recurring_created_at) = $year")); - $recurring_invoices_added = intval($row['recurring_invoices_added']); -} + if ($config_module_enable_ticketing && $config_module_enable_accounting) { + //Get Unbilled, closed tickets + $sql_unbilled_tickets = mysqli_query($mysqli, "SELECT COUNT('ticket_id') AS unbilled_tickets FROM tickets WHERE ticket_status = 'Closed' AND ticket_billable = 1 AND ticket_invoice_id = 0 AND YEAR(ticket_created_at) = $year"); + $row = mysqli_fetch_array($sql_unbilled_tickets); + $unbilled_tickets = intval($row['unbilled_tickets']); + } else { + //Get Total Recurring Invoices added + $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('recurring_id') AS recurring_invoices_added FROM recurring WHERE YEAR(recurring_created_at) = $year")); + $recurring_invoices_added = intval($row['recurring_invoices_added']); + } -//Get Total Clients added -$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('client_id') AS clients_added FROM clients WHERE YEAR(client_created_at) = $year AND client_archived_at IS NULL")); -$clients_added = intval($row['clients_added']); + //Get Total Clients added + $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('client_id') AS clients_added FROM clients WHERE YEAR(client_created_at) = $year AND client_archived_at IS NULL")); + $clients_added = intval($row['clients_added']); -//Get Total Vendors added -$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('vendor_id') AS vendors_added FROM vendors WHERE YEAR(vendor_created_at) = $year AND vendor_client_id = 0 AND vendor_template = 0 AND vendor_archived_at IS NULL")); -$vendors_added = intval($row['vendors_added']); + //Get Total Vendors added + $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('vendor_id') AS vendors_added FROM vendors WHERE YEAR(vendor_created_at) = $year AND vendor_client_id = 0 AND vendor_template = 0 AND vendor_archived_at IS NULL")); + $vendors_added = intval($row['vendors_added']); ?> - -
-
- - -
-

-

Income

-
- Receivables: -
-
- -
-
-
- - -
- - -
-

-

Expenses

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

-

Profit

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

-

Monthly Recurring Income

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

-

Monthly Recurring Expense

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

-

Unbilled Ticket 1 || $unbilled_tickets = 0) { echo "s";}?>

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

-

Recurring Invoices Added

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

-

New Clients

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

-

New Vendors

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

-

Miles Traveled

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

Cash Flow

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

Cash Flow

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

Income by Category (Top 5)

-
- +
+
+
+

Income by Category (Top 5)

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

Expenses by Category (Top 5)

-
- +
+
+
+

Expenses by Category (Top 5)

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

Expenses by Vendor (Top 5)

-
- +
+
+
+

Expenses by Vendor (Top 5)

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

Account Balances

-
- +
+
+
+

Account Balances

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

Latest Income

-
- + +
-
- - - - - - - - - - - - - - - - - - - -
DateCustomerInvoiceAmount
-
-
-
-
-
-
-

Latest Expenses

-
- +
+
+
+
+

Latest Income

+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + +
DateCustomerInvoiceAmount
-
- - - - - - - - - - - +
+
+
+

Latest Expenses

+
+ +
+
+
+
DateVendorCategoryAmount
+ + + + + + + + + + - - - - - - - - -
DateVendorCategoryAmount
-
-
-
-
-
-
-

Trip Flow

-
- - - - + ?> + + + + + + + + +
-
- +
+
+
+
+

Trip Flow

+
+ + + + +
+
+
+ +
-
-
+
@@ -568,280 +577,282 @@ $vendors_added = intval($row['vendors_added']); if ($user_config_dashboard_technical_enable == 1) { -// Get Total Clients added -$sql_clients = mysqli_fetch_assoc(mysqli_query( - $mysqli, - "SELECT COUNT('client_id') AS clients_added FROM clients + // Get Total Clients added + $sql_clients = mysqli_fetch_assoc(mysqli_query( + $mysqli, + "SELECT COUNT('client_id') AS clients_added FROM clients WHERE YEAR(client_created_at) = $year" -)); -$clients_added = $sql_clients['clients_added']; + )); + $clients_added = $sql_clients['clients_added']; -// Get Total contacts added -$sql_contacts = mysqli_fetch_assoc(mysqli_query( - $mysqli, - "SELECT COUNT('contact_id') AS contacts_added FROM contacts + // Get Total contacts added + $sql_contacts = mysqli_fetch_assoc(mysqli_query( + $mysqli, + "SELECT COUNT('contact_id') AS contacts_added FROM contacts WHERE YEAR(contact_created_at) = $year" -)); -$contacts_added = $sql_contacts['contacts_added']; + )); + $contacts_added = $sql_contacts['contacts_added']; -// Get Total assets added -$sql_assets = mysqli_fetch_assoc(mysqli_query( - $mysqli, - "SELECT COUNT('asset_id') AS assets_added FROM assets + // Get Total assets added + $sql_assets = mysqli_fetch_assoc(mysqli_query( + $mysqli, + "SELECT COUNT('asset_id') AS assets_added FROM assets WHERE YEAR(asset_created_at) = $year" -)); -$assets_added = $sql_assets['assets_added']; + )); + $assets_added = $sql_assets['assets_added']; -// Ticket count -$sql_tickets = mysqli_fetch_assoc(mysqli_query( - $mysqli, - "SELECT COUNT('ticket_id') AS active_tickets + // Ticket count + $sql_tickets = mysqli_fetch_assoc(mysqli_query( + $mysqli, + "SELECT COUNT('ticket_id') AS active_tickets FROM tickets WHERE ticket_status != 'Closed'" -)); -$active_tickets = $sql_tickets['active_tickets']; + )); + $active_tickets = $sql_tickets['active_tickets']; -// Your Ticket count -$sql_your_tickets = mysqli_fetch_assoc(mysqli_query( - $mysqli, - "SELECT COUNT('ticket_id') AS your_tickets + // Your Ticket count + $sql_your_tickets = mysqli_fetch_assoc(mysqli_query( + $mysqli, + "SELECT COUNT('ticket_id') AS your_tickets FROM tickets WHERE ticket_status != 'Closed' AND ticket_assigned_to = $session_user_id" -)); -$your_tickets = $sql_your_tickets['your_tickets']; + )); + $your_tickets = $sql_your_tickets['your_tickets']; -// Expiring domains (but not ones that have already expired) -$sql_domains_expiring = mysqli_fetch_assoc(mysqli_query( - $mysqli, - "SELECT COUNT('domain_id') as expiring_domains + // Expiring domains (but not ones that have already expired) + $sql_domains_expiring = mysqli_fetch_assoc(mysqli_query( + $mysqli, + "SELECT COUNT('domain_id') as expiring_domains FROM domains WHERE domain_expire IS NOT NULL AND domain_expire > CURRENT_DATE AND domain_expire < CURRENT_DATE + INTERVAL 30 DAY AND domain_archived_at IS NULL" -)); -$expiring_domains = $sql_domains_expiring['expiring_domains']; + )); + $expiring_domains = $sql_domains_expiring['expiring_domains']; -// Expiring Certificates (but not ones that have already expired) -$sql_certs_expiring = mysqli_fetch_assoc(mysqli_query( - $mysqli, - "SELECT COUNT('certificate_id') as expiring_certs + // Expiring Certificates (but not ones that have already expired) + $sql_certs_expiring = mysqli_fetch_assoc(mysqli_query( + $mysqli, + "SELECT COUNT('certificate_id') as expiring_certs FROM certificates WHERE certificate_expire IS NOT NULL AND certificate_expire > CURRENT_DATE AND certificate_expire < CURRENT_DATE + INTERVAL 30 DAY AND certificate_archived_at IS NULL" -)); -$expiring_certificates = $sql_certs_expiring['expiring_certs']; + )); + $expiring_certificates = $sql_certs_expiring['expiring_certs']; -$sql_your_tickets = mysqli_query( - $mysqli, - "SELECT * FROM tickets + $sql_your_tickets = mysqli_query( + $mysqli, + "SELECT * FROM tickets LEFT JOIN clients ON ticket_client_id = client_id LEFT JOIN contacts ON ticket_contact_id = contact_id WHERE ticket_assigned_to = $session_user_id AND ticket_status != 'Closed' ORDER BY ticket_number DESC" -); + ); ?> - -
+ + - - -
-
-
-
-

Your Open Tickets

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

Your Open Tickets

+
+ +
+
+
+
NumberSubjectClientContactPriorityStatusLast Response
+ + + + + + + + + + + + + Never

"; + } else { + $ticket_updated_at_display = "

Never

"; + } + } else { + $ticket_updated_at_display = "$ticket_updated_at_time_ago"; + } + $client_id = intval($row['ticket_client_id']); + $client_name = nullable_htmlentities($row['client_name']); + $contact_id = intval($row['ticket_contact_id']); + $contact_name = nullable_htmlentities($row['contact_name']); + if ($ticket_status == "Pending-Assignment") { + $ticket_status_color = "danger"; + } elseif ($ticket_status == "Assigned") { + $ticket_status_color = "primary"; + } elseif ($ticket_status == "In-Progress") { + $ticket_status_color = "success"; + } elseif ($ticket_status == "Closed") { + $ticket_status_color = "dark"; + } else { + $ticket_status_color = "secondary"; + } + + if ($ticket_priority == "High") { + $ticket_priority_color = "danger"; + } elseif ($ticket_priority == "Medium") { + $ticket_priority_color = "warning"; + } else { + $ticket_priority_color = "info"; + } + + if (empty($contact_name)) { + $contact_display = "-"; + } else { + $contact_display = "$contact_name"; + } + + ?> + + "> + + + + + + + + + + Never

"; - } else { - $ticket_updated_at_display = "

Never

"; } - } else { - $ticket_updated_at_display = "$ticket_updated_at_time_ago"; - } - $client_id = intval($row['ticket_client_id']); - $client_name = nullable_htmlentities($row['client_name']); - $contact_id = intval($row['ticket_contact_id']); - $contact_name = nullable_htmlentities($row['contact_name']); - if ($ticket_status == "Pending-Assignment") { - $ticket_status_color = "danger"; - } elseif ($ticket_status == "Assigned") { - $ticket_status_color = "primary"; - } elseif ($ticket_status == "In-Progress") { - $ticket_status_color = "success"; - } elseif ($ticket_status == "Closed") { - $ticket_status_color = "dark"; - } else{ - $ticket_status_color = "secondary"; - } - if ($ticket_priority == "High") { - $ticket_priority_color = "danger"; - } elseif ($ticket_priority == "Medium") { - $ticket_priority_color = "warning"; - } else{ - $ticket_priority_color = "info"; - } + ?> - if (empty($contact_name)) { - $contact_display = "-"; - } else { - $contact_display = "$contact_name"; - } + +
NumberSubjectClientContactPriorityStatusLast Response
+ + + +
+
- ?> +
- "> - - - - - - - - - - - - - - - - -
-
- -
- - + @@ -850,7 +861,7 @@ $sql_your_tickets = mysqli_query( +?> + \ No newline at end of file diff --git a/functions.php b/functions.php index f69cf424..c6fa2d29 100644 --- a/functions.php +++ b/functions.php @@ -15,7 +15,8 @@ use PHPMailer\PHPMailer\PHPMailer; use PHPMailer\PHPMailer\Exception; // Function to generate both crypto & URL safe random strings -function randomString($length = 16) { +function randomString($length = 16) +{ // Generate some cryptographically safe random bytes // Generate a little more than requested as we'll lose some later converting $random_bytes = random_bytes($length + 5); @@ -32,12 +33,13 @@ function randomString($length = 16) { } // Older keygen function - only used for TOTP currently -function key32gen() { +function key32gen() +{ $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; $chars .= "234567"; while (1) { $key = ''; - srand((double) microtime() * 1000000); + srand((float) microtime() * 1000000); for ($i = 0; $i < 32; $i++) { $key .= substr($chars, (rand() % (strlen($chars))), 1); } @@ -46,11 +48,13 @@ function key32gen() { return $key; } -function nullable_htmlentities($unsanitizedInput) { +function nullable_htmlentities($unsanitizedInput) +{ return htmlentities($unsanitizedInput ?? ''); } -function initials($str) { +function initials($str) +{ if (!empty($str)) { $ret = ''; foreach (explode(' ', $str) as $word) @@ -60,7 +64,8 @@ function initials($str) { } } -function removeDirectory($path) { +function removeDirectory($path) +{ if (!file_exists($path)) { return; } @@ -72,11 +77,13 @@ function removeDirectory($path) { rmdir($path); } -function getUserAgent() { +function getUserAgent() +{ return $_SERVER['HTTP_USER_AGENT']; } -function getIP() { +function getIP() +{ if (defined("CONST_GET_IP_METHOD")) { if (CONST_GET_IP_METHOD == "HTTP_X_FORWARDED_FOR") { $ip = getenv('HTTP_X_FORWARDED_FOR'); @@ -94,7 +101,8 @@ function getIP() { return $ip; } -function getWebBrowser($user_browser) { +function getWebBrowser($user_browser) +{ $browser = "Unknown Browser"; $browser_array = array( '/msie/i' => " Internet Explorer", @@ -112,7 +120,8 @@ function getWebBrowser($user_browser) { return $browser; } -function getOS($user_os) { +function getOS($user_os) +{ $os_platform = "Unknown OS"; $os_array = array( '/windows nt 10/i' => " Windows 10", @@ -139,7 +148,8 @@ function getOS($user_os) { return $os_platform; } -function getDevice() { +function getDevice() +{ $tablet_browser = 0; $mobile_browser = 0; if (preg_match('/(tablet|ipad|playbook)|(android(?!.*(mobi|opera mini)))/i', strtolower($_SERVER['HTTP_USER_AGENT']))) { @@ -153,22 +163,23 @@ function getDevice() { } $mobile_ua = strtolower(substr(getUserAgent(), 0, 4)); $mobile_agents = array( - 'w3c ','acs-','alav','alca','amoi','audi','avan','benq','bird','blac', - 'blaz','brew','cell','cldc','cmd-','dang','doco','eric','hipt','inno', - 'ipaq','java','jigs','kddi','keji','leno','lg-c','lg-d','lg-g','lge-', - 'maui','maxo','midp','mits','mmef','mobi','mot-','moto','mwbp','nec-', - 'newt','noki','palm','pana','pant','phil','play','port','prox', - 'qwap','sage','sams','sany','sch-','sec-','send','seri','sgh-','shar', - 'sie-','siem','smal','smar','sony','sph-','symb','t-mo','teli','tim-', - 'tosh','tsm-','upg1','upsi','vk-v','voda','wap-','wapa','wapi','wapp', - 'wapr','webc','winw','winw','xda ','xda-'); + 'w3c ', 'acs-', 'alav', 'alca', 'amoi', 'audi', 'avan', 'benq', 'bird', 'blac', + 'blaz', 'brew', 'cell', 'cldc', 'cmd-', 'dang', 'doco', 'eric', 'hipt', 'inno', + 'ipaq', 'java', 'jigs', 'kddi', 'keji', 'leno', 'lg-c', 'lg-d', 'lg-g', 'lge-', + 'maui', 'maxo', 'midp', 'mits', 'mmef', 'mobi', 'mot-', 'moto', 'mwbp', 'nec-', + 'newt', 'noki', 'palm', 'pana', 'pant', 'phil', 'play', 'port', 'prox', + 'qwap', 'sage', 'sams', 'sany', 'sch-', 'sec-', 'send', 'seri', 'sgh-', 'shar', + 'sie-', 'siem', 'smal', 'smar', 'sony', 'sph-', 'symb', 't-mo', 'teli', 'tim-', + 'tosh', 'tsm-', 'upg1', 'upsi', 'vk-v', 'voda', 'wap-', 'wapa', 'wapi', 'wapp', + 'wapr', 'webc', 'winw', 'winw', 'xda ', 'xda-' + ); if (in_array($mobile_ua, $mobile_agents)) { $mobile_browser++; } if (strpos(strtolower(getUserAgent()), 'opera mini') > 0) { $mobile_browser++; //Check for tablets on Opera Mini alternative headers - $stock_ua = strtolower(isset($_SERVER['HTTP_X_OPERAMINI_PHONE_UA'])?$_SERVER['HTTP_X_OPERAMINI_PHONE_UA']:(isset($_SERVER['HTTP_DEVICE_STOCK_UA'])?$_SERVER['HTTP_DEVICE_STOCK_UA']:'')); + $stock_ua = strtolower(isset($_SERVER['HTTP_X_OPERAMINI_PHONE_UA']) ? $_SERVER['HTTP_X_OPERAMINI_PHONE_UA'] : (isset($_SERVER['HTTP_DEVICE_STOCK_UA']) ? $_SERVER['HTTP_DEVICE_STOCK_UA'] : '')); if (preg_match('/(tablet|ipad|playbook)|(android(?!.*mobile))/i', $stock_ua)) { $tablet_browser++; } @@ -176,59 +187,58 @@ function getDevice() { if ($tablet_browser > 0) { //do something for tablet devices return 'Tablet'; - } - else if ($mobile_browser > 0) { + } else if ($mobile_browser > 0) { //do something for mobile devices return 'Mobile'; - } - else { + } else { //do something for everything else return 'Computer'; } } -function truncate($text, $chars) { +function truncate($text, $chars) +{ if (strlen($text) <= $chars) { return $text; } - $text = $text." "; + $text = $text . " "; $text = substr($text, 0, $chars); $lastSpacePos = strrpos($text, ' '); if ($lastSpacePos !== false) { $text = substr($text, 0, $lastSpacePos); } - return $text."..."; + return $text . "..."; } -function formatPhoneNumber($phoneNumber) { - $phoneNumber = $phoneNumber ? preg_replace('/[^0-9]/', '', $phoneNumber): ""; +function formatPhoneNumber($phoneNumber) +{ + $phoneNumber = $phoneNumber ? preg_replace('/[^0-9]/', '', $phoneNumber) : ""; if (strlen($phoneNumber) > 10) { - $countryCode = substr($phoneNumber, 0, strlen($phoneNumber)-10); + $countryCode = substr($phoneNumber, 0, strlen($phoneNumber) - 10); $areaCode = substr($phoneNumber, -10, 3); $nextThree = substr($phoneNumber, -7, 3); $lastFour = substr($phoneNumber, -4, 4); - $phoneNumber = '+'.$countryCode.' ('.$areaCode.') '.$nextThree.'-'.$lastFour; - } - else if (strlen($phoneNumber) == 10) { + $phoneNumber = '+' . $countryCode . ' (' . $areaCode . ') ' . $nextThree . '-' . $lastFour; + } else if (strlen($phoneNumber) == 10) { $areaCode = substr($phoneNumber, 0, 3); $nextThree = substr($phoneNumber, 3, 3); $lastFour = substr($phoneNumber, 6, 4); - $phoneNumber = '('.$areaCode.') '.$nextThree.'-'.$lastFour; - } - else if (strlen($phoneNumber) == 7) { + $phoneNumber = '(' . $areaCode . ') ' . $nextThree . '-' . $lastFour; + } else if (strlen($phoneNumber) == 7) { $nextThree = substr($phoneNumber, 0, 3); $lastFour = substr($phoneNumber, 3, 4); - $phoneNumber = $nextThree.'-'.$lastFour; + $phoneNumber = $nextThree . '-' . $lastFour; } return $phoneNumber; } -function mkdirMissing($dir) { +function mkdirMissing($dir) +{ if (!is_dir($dir)) { mkdir($dir); } @@ -236,7 +246,8 @@ function mkdirMissing($dir) { // Called during initial setup // Encrypts the master key with the user's password -function setupFirstUserSpecificKey($user_password, $site_encryption_master_key) { +function setupFirstUserSpecificKey($user_password, $site_encryption_master_key) +{ $iv = randomString(); $salt = randomString(); @@ -254,7 +265,8 @@ function setupFirstUserSpecificKey($user_password, $site_encryption_master_key) * New Users: Requires the admin setting up their account have a Specific/Session key configured * Password Changes: Will use the current info in the session. */ -function encryptUserSpecificKey($user_password) { +function encryptUserSpecificKey($user_password) +{ $iv = randomString(); $salt = randomString(); @@ -273,7 +285,6 @@ function encryptUserSpecificKey($user_password) { $ciphertext = openssl_encrypt($site_encryption_master_key, 'aes-128-cbc', $user_password_kdhash, 0, $iv); return $salt . $iv . $ciphertext; - } // Given a ciphertext (incl. IV) and the user's password, returns the site master key @@ -313,8 +324,8 @@ function generateUserSessionKey($site_encryption_master_key) include 'config.php'; if ($config_https_only) { - setcookie("user_encryption_session_key", "$user_encryption_session_key", ['path' => '/','secure' => true,'httponly' => true,'samesite' => 'None']); - } else{ + setcookie("user_encryption_session_key", "$user_encryption_session_key", ['path' => '/', 'secure' => true, 'httponly' => true, 'samesite' => 'None']); + } else { setcookie("user_encryption_session_key", $user_encryption_session_key, 0, "/"); $_SESSION['alert_message'] = "Unencrypted connection flag set: Using non-secure cookies."; } @@ -338,7 +349,6 @@ function decryptLoginEntry($login_password_ciphertext) // Decrypt the login password using the master key return openssl_decrypt($login_ciphertext, 'aes-128-cbc', $site_encryption_master_key, 0, $login_iv); - } // Encrypts a website/asset login password @@ -361,7 +371,8 @@ function encryptLoginEntry($login_password_cleartext) } // Get domain expiration date -function getDomainExpirationDate($name) { +function getDomainExpirationDate($name) +{ // Only run if we think the domain is valid if (!filter_var($name, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) { @@ -376,11 +387,9 @@ function getDomainExpirationDate($name) { if ($response) { if (is_array($response['expiration_date'])) { $expiry = new DateTime($response['expiration_date'][1]); - } - elseif (isset($response['expiration_date'])) { + } elseif (isset($response['expiration_date'])) { $expiry = new DateTime($response['expiration_date']); - } - else { + } else { return "NULL"; } @@ -392,7 +401,8 @@ function getDomainExpirationDate($name) { } // Get domain general info (whois + NS/A/MX records) -function getDomainRecords($name) { +function getDomainRecords($name) +{ $records = array(); @@ -417,7 +427,8 @@ function getDomainRecords($name) { // Used to automatically attempt to get SSL certificates as part of adding domains // The logic for the fetch (sync) button on the client_certificates page is in ajax.php, and allows ports other than 443 -function getSSL($name) { +function getSSL($name) +{ $certificate = array(); $certificate['success'] = false; @@ -452,7 +463,8 @@ function getSSL($name) { return $certificate; } -function strtoAZaz09($string) { +function strtoAZaz09($string) +{ // Gets rid of non-alphanumerics return preg_replace('/[^A-Za-z0-9_-]/', '', $string); @@ -460,11 +472,11 @@ function strtoAZaz09($string) { // Cross-Site Request Forgery check for sensitive functions // Validates the CSRF token provided matches the one in the users session -function validateCSRFToken($token) { +function validateCSRFToken($token) +{ if (hash_equals($token, $_SESSION['csrf_token'])) { return true; - } - else{ + } else { $_SESSION['alert_type'] = "warning"; $_SESSION['alert_message'] = "CSRF token verification failed. Try again, or log out to refresh your token."; header("Location: index.php"); @@ -479,7 +491,8 @@ function validateCSRFToken($token) { * Accountant - 1 */ -function validateAdminRole() { +function validateAdminRole() +{ if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] != 3) { $_SESSION['alert_type'] = "danger"; $_SESSION['alert_message'] = WORDING_ROLECHECK_FAILED; @@ -489,7 +502,8 @@ function validateAdminRole() { } // Validates a user is a tech (or admin). Stops page load and attempts to direct away from the page if not (i.e. user is an accountant) -function validateTechRole() { +function validateTechRole() +{ if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] == 1) { $_SESSION['alert_type'] = "danger"; $_SESSION['alert_message'] = WORDING_ROLECHECK_FAILED; @@ -499,7 +513,8 @@ function validateTechRole() { } // Validates a user is an accountant (or admin). Stops page load and attempts to direct away from the page if not (i.e. user is a tech) -function validateAccountantRole() { +function validateAccountantRole() +{ if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] == 2) { $_SESSION['alert_type'] = "danger"; $_SESSION['alert_message'] = WORDING_ROLECHECK_FAILED; @@ -509,7 +524,8 @@ function validateAccountantRole() { } // Send a single email to a single recipient -function sendSingleEmail($config_smtp_host, $config_smtp_username, $config_smtp_password, $config_smtp_encryption, $config_smtp_port, $from_email, $from_name, $to_email, $to_name, $subject, $body, $ics_str) { +function sendSingleEmail($config_smtp_host, $config_smtp_username, $config_smtp_password, $config_smtp_encryption, $config_smtp_port, $from_email, $from_name, $to_email, $to_name, $subject, $body, $ics_str) +{ $mail = new PHPMailer(true); @@ -601,12 +617,10 @@ function sendSingleEmail($config_smtp_host, $config_smtp_username, $config_smtp_ // Return true if this was successful return true; - } - - catch(Exception $e) { + } catch (Exception $e) { // If we couldn't send the message return the error, so we can log it in the database (truncated) error_log("ITFlow - Failed to send email: " . $mail->ErrorInfo); - return substr("Mailer Error: $mail->ErrorInfo", 0, 150)."..."; + return substr("Mailer Error: $mail->ErrorInfo", 0, 150) . "..."; } } @@ -660,7 +674,7 @@ function getInvoiceBadgeColor($invoice_status) $invoice_badge_color = "success"; } elseif ($invoice_status == "Cancelled") { $invoice_badge_color = "danger"; - } else{ + } else { $invoice_badge_color = "secondary"; } @@ -668,7 +682,8 @@ function getInvoiceBadgeColor($invoice_status) } // Pass $_FILE['file'] to check an uploaded file before saving it -function checkFileUpload($file, $allowed_extensions) { +function checkFileUpload($file, $allowed_extensions) +{ // Variables $name = $file['name']; $tmp = $file['tmp_name']; @@ -707,7 +722,8 @@ function checkFileUpload($file, $allowed_extensions) { return $secureFilename; } -function sanitizeInput($input) { +function sanitizeInput($input) +{ global $mysqli; // Remove HTML and PHP tags @@ -723,14 +739,16 @@ function sanitizeInput($input) { return $input; } -function sanitizeForEmail($data) { - $sanitized = htmlspecialchars($data); - $sanitized = strip_tags($sanitized); - $sanitized = trim($sanitized); - return $sanitized; +function sanitizeForEmail($data) +{ + $sanitized = htmlspecialchars($data); + $sanitized = strip_tags($sanitized); + $sanitized = trim($sanitized); + return $sanitized; } -function timeAgo($datetime) { +function timeAgo($datetime) +{ $time = strtotime($datetime); $difference = $time - time(); // Changed to handle future dates @@ -762,11 +780,13 @@ function timeAgo($datetime) { } // Function to remove Emojis in messages, this seems to break the mail queue -function removeEmoji($text){ +function removeEmoji($text) +{ return preg_replace('/\x{1F3F4}\x{E0067}\x{E0062}(?:\x{E0077}\x{E006C}\x{E0073}|\x{E0073}\x{E0063}\x{E0074}|\x{E0065}\x{E006E}\x{E0067})\x{E007F}|(?:\x{1F9D1}\x{1F3FF}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FF}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FF}\x{200D}\x{1FAF2})[\x{1F3FB}-\x{1F3FE}]|(?:\x{1F9D1}\x{1F3FE}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FE}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FE}\x{200D}\x{1FAF2})[\x{1F3FB}-\x{1F3FD}\x{1F3FF}]|(?:\x{1F9D1}\x{1F3FD}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FD}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FD}\x{200D}\x{1FAF2})[\x{1F3FB}\x{1F3FC}\x{1F3FE}\x{1F3FF}]|(?:\x{1F9D1}\x{1F3FC}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FC}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FC}\x{200D}\x{1FAF2})[\x{1F3FB}\x{1F3FD}-\x{1F3FF}]|(?:\x{1F9D1}\x{1F3FB}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FB}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FB}\x{200D}\x{1FAF2})[\x{1F3FC}-\x{1F3FF}]|\x{1F468}(?:\x{1F3FB}(?:\x{200D}(?:\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FF}]|\x{1F468}[\x{1F3FB}-\x{1F3FF}])|\x{200D}(?:\x{1F48B}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FF}]|\x{1F468}[\x{1F3FB}-\x{1F3FF}]))|\x{1F91D}\x{200D}\x{1F468}[\x{1F3FC}-\x{1F3FF}]|[\x{2695}\x{2696}\x{2708}]\x{FE0F}|[\x{2695}\x{2696}\x{2708}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]))?|[\x{1F3FC}-\x{1F3FF}]\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FF}]|\x{1F468}[\x{1F3FB}-\x{1F3FF}])|\x{200D}(?:\x{1F48B}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FF}]|\x{1F468}[\x{1F3FB}-\x{1F3FF}]))|\x{200D}(?:\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F468}|[\x{1F468}\x{1F469}]\x{200D}(?:\x{1F466}\x{200D}\x{1F466}|\x{1F467}\x{200D}[\x{1F466}\x{1F467}])|\x{1F466}\x{200D}\x{1F466}|\x{1F467}\x{200D}[\x{1F466}\x{1F467}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FF}\x{200D}(?:\x{1F91D}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FE}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FE}\x{200D}(?:\x{1F91D}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FD}\x{1F3FF}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FD}\x{200D}(?:\x{1F91D}\x{200D}\x{1F468}[\x{1F3FB}\x{1F3FC}\x{1F3FE}\x{1F3FF}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FC}\x{200D}(?:\x{1F91D}\x{200D}\x{1F468}[\x{1F3FB}\x{1F3FD}-\x{1F3FF}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])\x{FE0F}|\x{200D}(?:[\x{1F468}\x{1F469}]\x{200D}[\x{1F466}\x{1F467}]|[\x{1F466}\x{1F467}])|\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FF}|\x{1F3FE}|\x{1F3FD}|\x{1F3FC}|\x{200D}[\x{2695}\x{2696}\x{2708}])?|(?:\x{1F469}(?:\x{1F3FB}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}])|\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}]))|[\x{1F3FC}-\x{1F3FF}]\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}])|\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}])))|\x{1F9D1}[\x{1F3FB}-\x{1F3FF}]\x{200D}\x{1F91D}\x{200D}\x{1F9D1})[\x{1F3FB}-\x{1F3FF}]|\x{1F469}\x{200D}\x{1F469}\x{200D}(?:\x{1F466}\x{200D}\x{1F466}|\x{1F467}\x{200D}[\x{1F466}\x{1F467}])|\x{1F469}(?:\x{200D}(?:\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}])|\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}]))|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FF}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FE}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FD}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FC}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FB}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F9D1}(?:\x{200D}(?:\x{1F91D}\x{200D}\x{1F9D1}|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FF}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FE}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FD}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FC}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FB}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F469}\x{200D}\x{1F466}\x{200D}\x{1F466}|\x{1F469}\x{200D}\x{1F469}\x{200D}[\x{1F466}\x{1F467}]|\x{1F469}\x{200D}\x{1F467}\x{200D}[\x{1F466}\x{1F467}]|(?:\x{1F441}\x{FE0F}?\x{200D}\x{1F5E8}|\x{1F9D1}(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FB}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])|\x{1F469}(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FB}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])|\x{1F636}\x{200D}\x{1F32B}|\x{1F3F3}\x{FE0F}?\x{200D}\x{26A7}|\x{1F43B}\x{200D}\x{2744}|(?:[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93D}\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}][\x{1F3FB}-\x{1F3FF}]|[\x{1F46F}\x{1F9DE}\x{1F9DF}])\x{200D}[\x{2640}\x{2642}]|[\x{26F9}\x{1F3CB}\x{1F3CC}\x{1F575}](?:[\x{FE0F}\x{1F3FB}-\x{1F3FF}]\x{200D}[\x{2640}\x{2642}]|\x{200D}[\x{2640}\x{2642}])|\x{1F3F4}\x{200D}\x{2620}|[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93C}-\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}]\x{200D}[\x{2640}\x{2642}]|[\xA9\xAE\x{203C}\x{2049}\x{2122}\x{2139}\x{2194}-\x{2199}\x{21A9}\x{21AA}\x{231A}\x{231B}\x{2328}\x{23CF}\x{23ED}-\x{23EF}\x{23F1}\x{23F2}\x{23F8}-\x{23FA}\x{24C2}\x{25AA}\x{25AB}\x{25B6}\x{25C0}\x{25FB}\x{25FC}\x{25FE}\x{2600}-\x{2604}\x{260E}\x{2611}\x{2614}\x{2615}\x{2618}\x{2620}\x{2622}\x{2623}\x{2626}\x{262A}\x{262E}\x{262F}\x{2638}-\x{263A}\x{2640}\x{2642}\x{2648}-\x{2653}\x{265F}\x{2660}\x{2663}\x{2665}\x{2666}\x{2668}\x{267B}\x{267E}\x{267F}\x{2692}\x{2694}-\x{2697}\x{2699}\x{269B}\x{269C}\x{26A0}\x{26A7}\x{26AA}\x{26B0}\x{26B1}\x{26BD}\x{26BE}\x{26C4}\x{26C8}\x{26CF}\x{26D1}\x{26D3}\x{26E9}\x{26F0}-\x{26F5}\x{26F7}\x{26F8}\x{26FA}\x{2702}\x{2708}\x{2709}\x{270F}\x{2712}\x{2714}\x{2716}\x{271D}\x{2721}\x{2733}\x{2734}\x{2744}\x{2747}\x{2763}\x{27A1}\x{2934}\x{2935}\x{2B05}-\x{2B07}\x{2B1B}\x{2B1C}\x{2B55}\x{3030}\x{303D}\x{3297}\x{3299}\x{1F004}\x{1F170}\x{1F171}\x{1F17E}\x{1F17F}\x{1F202}\x{1F237}\x{1F321}\x{1F324}-\x{1F32C}\x{1F336}\x{1F37D}\x{1F396}\x{1F397}\x{1F399}-\x{1F39B}\x{1F39E}\x{1F39F}\x{1F3CD}\x{1F3CE}\x{1F3D4}-\x{1F3DF}\x{1F3F5}\x{1F3F7}\x{1F43F}\x{1F4FD}\x{1F549}\x{1F54A}\x{1F56F}\x{1F570}\x{1F573}\x{1F576}-\x{1F579}\x{1F587}\x{1F58A}-\x{1F58D}\x{1F5A5}\x{1F5A8}\x{1F5B1}\x{1F5B2}\x{1F5BC}\x{1F5C2}-\x{1F5C4}\x{1F5D1}-\x{1F5D3}\x{1F5DC}-\x{1F5DE}\x{1F5E1}\x{1F5E3}\x{1F5E8}\x{1F5EF}\x{1F5F3}\x{1F5FA}\x{1F6CB}\x{1F6CD}-\x{1F6CF}\x{1F6E0}-\x{1F6E5}\x{1F6E9}\x{1F6F0}\x{1F6F3}])\x{FE0F}|\x{1F441}\x{FE0F}?\x{200D}\x{1F5E8}|\x{1F9D1}(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FB}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])|\x{1F469}(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FB}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])|\x{1F3F3}\x{FE0F}?\x{200D}\x{1F308}|\x{1F469}\x{200D}\x{1F467}|\x{1F469}\x{200D}\x{1F466}|\x{1F636}\x{200D}\x{1F32B}|\x{1F3F3}\x{FE0F}?\x{200D}\x{26A7}|\x{1F635}\x{200D}\x{1F4AB}|\x{1F62E}\x{200D}\x{1F4A8}|\x{1F415}\x{200D}\x{1F9BA}|\x{1FAF1}(?:\x{1F3FF}|\x{1F3FE}|\x{1F3FD}|\x{1F3FC}|\x{1F3FB})?|\x{1F9D1}(?:\x{1F3FF}|\x{1F3FE}|\x{1F3FD}|\x{1F3FC}|\x{1F3FB})?|\x{1F469}(?:\x{1F3FF}|\x{1F3FE}|\x{1F3FD}|\x{1F3FC}|\x{1F3FB})?|\x{1F43B}\x{200D}\x{2744}|(?:[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93D}\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}][\x{1F3FB}-\x{1F3FF}]|[\x{1F46F}\x{1F9DE}\x{1F9DF}])\x{200D}[\x{2640}\x{2642}]|[\x{26F9}\x{1F3CB}\x{1F3CC}\x{1F575}](?:[\x{FE0F}\x{1F3FB}-\x{1F3FF}]\x{200D}[\x{2640}\x{2642}]|\x{200D}[\x{2640}\x{2642}])|\x{1F3F4}\x{200D}\x{2620}|\x{1F1FD}\x{1F1F0}|\x{1F1F6}\x{1F1E6}|\x{1F1F4}\x{1F1F2}|\x{1F408}\x{200D}\x{2B1B}|\x{2764}(?:\x{FE0F}\x{200D}[\x{1F525}\x{1FA79}]|\x{200D}[\x{1F525}\x{1FA79}])|\x{1F441}\x{FE0F}?|\x{1F3F3}\x{FE0F}?|[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93C}-\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}]\x{200D}[\x{2640}\x{2642}]|\x{1F1FF}[\x{1F1E6}\x{1F1F2}\x{1F1FC}]|\x{1F1FE}[\x{1F1EA}\x{1F1F9}]|\x{1F1FC}[\x{1F1EB}\x{1F1F8}]|\x{1F1FB}[\x{1F1E6}\x{1F1E8}\x{1F1EA}\x{1F1EC}\x{1F1EE}\x{1F1F3}\x{1F1FA}]|\x{1F1FA}[\x{1F1E6}\x{1F1EC}\x{1F1F2}\x{1F1F3}\x{1F1F8}\x{1F1FE}\x{1F1FF}]|\x{1F1F9}[\x{1F1E6}\x{1F1E8}\x{1F1E9}\x{1F1EB}-\x{1F1ED}\x{1F1EF}-\x{1F1F4}\x{1F1F7}\x{1F1F9}\x{1F1FB}\x{1F1FC}\x{1F1FF}]|\x{1F1F8}[\x{1F1E6}-\x{1F1EA}\x{1F1EC}-\x{1F1F4}\x{1F1F7}-\x{1F1F9}\x{1F1FB}\x{1F1FD}-\x{1F1FF}]|\x{1F1F7}[\x{1F1EA}\x{1F1F4}\x{1F1F8}\x{1F1FA}\x{1F1FC}]|\x{1F1F5}[\x{1F1E6}\x{1F1EA}-\x{1F1ED}\x{1F1F0}-\x{1F1F3}\x{1F1F7}-\x{1F1F9}\x{1F1FC}\x{1F1FE}]|\x{1F1F3}[\x{1F1E6}\x{1F1E8}\x{1F1EA}-\x{1F1EC}\x{1F1EE}\x{1F1F1}\x{1F1F4}\x{1F1F5}\x{1F1F7}\x{1F1FA}\x{1F1FF}]|\x{1F1F2}[\x{1F1E6}\x{1F1E8}-\x{1F1ED}\x{1F1F0}-\x{1F1FF}]|\x{1F1F1}[\x{1F1E6}-\x{1F1E8}\x{1F1EE}\x{1F1F0}\x{1F1F7}-\x{1F1FB}\x{1F1FE}]|\x{1F1F0}[\x{1F1EA}\x{1F1EC}-\x{1F1EE}\x{1F1F2}\x{1F1F3}\x{1F1F5}\x{1F1F7}\x{1F1FC}\x{1F1FE}\x{1F1FF}]|\x{1F1EF}[\x{1F1EA}\x{1F1F2}\x{1F1F4}\x{1F1F5}]|\x{1F1EE}[\x{1F1E8}-\x{1F1EA}\x{1F1F1}-\x{1F1F4}\x{1F1F6}-\x{1F1F9}]|\x{1F1ED}[\x{1F1F0}\x{1F1F2}\x{1F1F3}\x{1F1F7}\x{1F1F9}\x{1F1FA}]|\x{1F1EC}[\x{1F1E6}\x{1F1E7}\x{1F1E9}-\x{1F1EE}\x{1F1F1}-\x{1F1F3}\x{1F1F5}-\x{1F1FA}\x{1F1FC}\x{1F1FE}]|\x{1F1EB}[\x{1F1EE}-\x{1F1F0}\x{1F1F2}\x{1F1F4}\x{1F1F7}]|\x{1F1EA}[\x{1F1E6}\x{1F1E8}\x{1F1EA}\x{1F1EC}\x{1F1ED}\x{1F1F7}-\x{1F1FA}]|\x{1F1E9}[\x{1F1EA}\x{1F1EC}\x{1F1EF}\x{1F1F0}\x{1F1F2}\x{1F1F4}\x{1F1FF}]|\x{1F1E8}[\x{1F1E6}\x{1F1E8}\x{1F1E9}\x{1F1EB}-\x{1F1EE}\x{1F1F0}-\x{1F1F5}\x{1F1F7}\x{1F1FA}-\x{1F1FF}]|\x{1F1E7}[\x{1F1E6}\x{1F1E7}\x{1F1E9}-\x{1F1EF}\x{1F1F1}-\x{1F1F4}\x{1F1F6}-\x{1F1F9}\x{1F1FB}\x{1F1FC}\x{1F1FE}\x{1F1FF}]|\x{1F1E6}[\x{1F1E8}-\x{1F1EC}\x{1F1EE}\x{1F1F1}\x{1F1F2}\x{1F1F4}\x{1F1F6}-\x{1F1FA}\x{1F1FC}\x{1F1FD}\x{1F1FF}]|[#\*0-9]\x{FE0F}?\x{20E3}|\x{1F93C}[\x{1F3FB}-\x{1F3FF}]|\x{2764}\x{FE0F}?|[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93D}\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}][\x{1F3FB}-\x{1F3FF}]|[\x{26F9}\x{1F3CB}\x{1F3CC}\x{1F575}][\x{FE0F}\x{1F3FB}-\x{1F3FF}]?|\x{1F3F4}|[\x{270A}\x{270B}\x{1F385}\x{1F3C2}\x{1F3C7}\x{1F442}\x{1F443}\x{1F446}-\x{1F450}\x{1F466}\x{1F467}\x{1F46B}-\x{1F46D}\x{1F472}\x{1F474}-\x{1F476}\x{1F478}\x{1F47C}\x{1F483}\x{1F485}\x{1F48F}\x{1F491}\x{1F4AA}\x{1F57A}\x{1F595}\x{1F596}\x{1F64C}\x{1F64F}\x{1F6C0}\x{1F6CC}\x{1F90C}\x{1F90F}\x{1F918}-\x{1F91F}\x{1F930}-\x{1F934}\x{1F936}\x{1F977}\x{1F9B5}\x{1F9B6}\x{1F9BB}\x{1F9D2}\x{1F9D3}\x{1F9D5}\x{1FAC3}-\x{1FAC5}\x{1FAF0}\x{1FAF2}-\x{1FAF6}][\x{1F3FB}-\x{1F3FF}]|[\x{261D}\x{270C}\x{270D}\x{1F574}\x{1F590}][\x{FE0F}\x{1F3FB}-\x{1F3FF}]|[\x{261D}\x{270A}-\x{270D}\x{1F385}\x{1F3C2}\x{1F3C7}\x{1F408}\x{1F415}\x{1F43B}\x{1F442}\x{1F443}\x{1F446}-\x{1F450}\x{1F466}\x{1F467}\x{1F46B}-\x{1F46D}\x{1F472}\x{1F474}-\x{1F476}\x{1F478}\x{1F47C}\x{1F483}\x{1F485}\x{1F48F}\x{1F491}\x{1F4AA}\x{1F574}\x{1F57A}\x{1F590}\x{1F595}\x{1F596}\x{1F62E}\x{1F635}\x{1F636}\x{1F64C}\x{1F64F}\x{1F6C0}\x{1F6CC}\x{1F90C}\x{1F90F}\x{1F918}-\x{1F91F}\x{1F930}-\x{1F934}\x{1F936}\x{1F93C}\x{1F977}\x{1F9B5}\x{1F9B6}\x{1F9BB}\x{1F9D2}\x{1F9D3}\x{1F9D5}\x{1FAC3}-\x{1FAC5}\x{1FAF0}\x{1FAF2}-\x{1FAF6}]|[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93D}\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}]|[\x{1F46F}\x{1F9DE}\x{1F9DF}]|[\xA9\xAE\x{203C}\x{2049}\x{2122}\x{2139}\x{2194}-\x{2199}\x{21A9}\x{21AA}\x{231A}\x{231B}\x{2328}\x{23CF}\x{23ED}-\x{23EF}\x{23F1}\x{23F2}\x{23F8}-\x{23FA}\x{24C2}\x{25AA}\x{25AB}\x{25B6}\x{25C0}\x{25FB}\x{25FC}\x{25FE}\x{2600}-\x{2604}\x{260E}\x{2611}\x{2614}\x{2615}\x{2618}\x{2620}\x{2622}\x{2623}\x{2626}\x{262A}\x{262E}\x{262F}\x{2638}-\x{263A}\x{2640}\x{2642}\x{2648}-\x{2653}\x{265F}\x{2660}\x{2663}\x{2665}\x{2666}\x{2668}\x{267B}\x{267E}\x{267F}\x{2692}\x{2694}-\x{2697}\x{2699}\x{269B}\x{269C}\x{26A0}\x{26A7}\x{26AA}\x{26B0}\x{26B1}\x{26BD}\x{26BE}\x{26C4}\x{26C8}\x{26CF}\x{26D1}\x{26D3}\x{26E9}\x{26F0}-\x{26F5}\x{26F7}\x{26F8}\x{26FA}\x{2702}\x{2708}\x{2709}\x{270F}\x{2712}\x{2714}\x{2716}\x{271D}\x{2721}\x{2733}\x{2734}\x{2744}\x{2747}\x{2763}\x{27A1}\x{2934}\x{2935}\x{2B05}-\x{2B07}\x{2B1B}\x{2B1C}\x{2B55}\x{3030}\x{303D}\x{3297}\x{3299}\x{1F004}\x{1F170}\x{1F171}\x{1F17E}\x{1F17F}\x{1F202}\x{1F237}\x{1F321}\x{1F324}-\x{1F32C}\x{1F336}\x{1F37D}\x{1F396}\x{1F397}\x{1F399}-\x{1F39B}\x{1F39E}\x{1F39F}\x{1F3CD}\x{1F3CE}\x{1F3D4}-\x{1F3DF}\x{1F3F5}\x{1F3F7}\x{1F43F}\x{1F4FD}\x{1F549}\x{1F54A}\x{1F56F}\x{1F570}\x{1F573}\x{1F576}-\x{1F579}\x{1F587}\x{1F58A}-\x{1F58D}\x{1F5A5}\x{1F5A8}\x{1F5B1}\x{1F5B2}\x{1F5BC}\x{1F5C2}-\x{1F5C4}\x{1F5D1}-\x{1F5D3}\x{1F5DC}-\x{1F5DE}\x{1F5E1}\x{1F5E3}\x{1F5E8}\x{1F5EF}\x{1F5F3}\x{1F5FA}\x{1F6CB}\x{1F6CD}-\x{1F6CF}\x{1F6E0}-\x{1F6E5}\x{1F6E9}\x{1F6F0}\x{1F6F3}]|[\x{23E9}-\x{23EC}\x{23F0}\x{23F3}\x{25FD}\x{2693}\x{26A1}\x{26AB}\x{26C5}\x{26CE}\x{26D4}\x{26EA}\x{26FD}\x{2705}\x{2728}\x{274C}\x{274E}\x{2753}-\x{2755}\x{2757}\x{2795}-\x{2797}\x{27B0}\x{27BF}\x{2B50}\x{1F0CF}\x{1F18E}\x{1F191}-\x{1F19A}\x{1F201}\x{1F21A}\x{1F22F}\x{1F232}-\x{1F236}\x{1F238}-\x{1F23A}\x{1F250}\x{1F251}\x{1F300}-\x{1F320}\x{1F32D}-\x{1F335}\x{1F337}-\x{1F37C}\x{1F37E}-\x{1F384}\x{1F386}-\x{1F393}\x{1F3A0}-\x{1F3C1}\x{1F3C5}\x{1F3C6}\x{1F3C8}\x{1F3C9}\x{1F3CF}-\x{1F3D3}\x{1F3E0}-\x{1F3F0}\x{1F3F8}-\x{1F407}\x{1F409}-\x{1F414}\x{1F416}-\x{1F43A}\x{1F43C}-\x{1F43E}\x{1F440}\x{1F444}\x{1F445}\x{1F451}-\x{1F465}\x{1F46A}\x{1F479}-\x{1F47B}\x{1F47D}-\x{1F480}\x{1F484}\x{1F488}-\x{1F48E}\x{1F490}\x{1F492}-\x{1F4A9}\x{1F4AB}-\x{1F4FC}\x{1F4FF}-\x{1F53D}\x{1F54B}-\x{1F54E}\x{1F550}-\x{1F567}\x{1F5A4}\x{1F5FB}-\x{1F62D}\x{1F62F}-\x{1F634}\x{1F637}-\x{1F644}\x{1F648}-\x{1F64A}\x{1F680}-\x{1F6A2}\x{1F6A4}-\x{1F6B3}\x{1F6B7}-\x{1F6BF}\x{1F6C1}-\x{1F6C5}\x{1F6D0}-\x{1F6D2}\x{1F6D5}-\x{1F6D7}\x{1F6DD}-\x{1F6DF}\x{1F6EB}\x{1F6EC}\x{1F6F4}-\x{1F6FC}\x{1F7E0}-\x{1F7EB}\x{1F7F0}\x{1F90D}\x{1F90E}\x{1F910}-\x{1F917}\x{1F920}-\x{1F925}\x{1F927}-\x{1F92F}\x{1F93A}\x{1F93F}-\x{1F945}\x{1F947}-\x{1F976}\x{1F978}-\x{1F9B4}\x{1F9B7}\x{1F9BA}\x{1F9BC}-\x{1F9CC}\x{1F9D0}\x{1F9E0}-\x{1F9FF}\x{1FA70}-\x{1FA74}\x{1FA78}-\x{1FA7C}\x{1FA80}-\x{1FA86}\x{1FA90}-\x{1FAAC}\x{1FAB0}-\x{1FABA}\x{1FAC0}-\x{1FAC2}\x{1FAD0}-\x{1FAD9}\x{1FAE0}-\x{1FAE7}]/u', '', $text); } -function shortenClient($client) { +function shortenClient($client) +{ // Pre-process by removing any non-alphanumeric characters except for certain punctuations. $client = html_entity_decode($client); // Decode any HTML entities $client = str_replace("'", "", $client); // Removing all occurrences of ' @@ -807,7 +827,8 @@ function shortenClient($client) { return strtoupper(substr($shortened, 0, 3)); } -function roundToNearest15($time) { +function roundToNearest15($time) +{ // Validate the input time format if (!preg_match('/^(\d{2}):(\d{2}):(\d{2})$/', $time, $matches)) { return false; // or throw an exception @@ -836,7 +857,8 @@ function roundToNearest15($time) { } // Get the value of a setting from the database -function getSettingValue($mysqli, $setting_name) { +function getSettingValue($mysqli, $setting_name) +{ //if starts with config_ then get from config table if (substr($setting_name, 0, 7) == "config_") { $sql = mysqli_query($mysqli, "SELECT $setting_name FROM settings"); @@ -851,7 +873,8 @@ function getSettingValue($mysqli, $setting_name) { } } -function getMonthlyTax($tax_name, $month, $year, $mysqli) { +function getMonthlyTax($tax_name, $month, $year, $mysqli) +{ // SQL to calculate monthly tax $sql = "SELECT SUM(item_tax) AS monthly_tax FROM invoice_items LEFT JOIN invoices ON invoice_items.item_invoice_id = invoices.invoice_id @@ -863,7 +886,8 @@ function getMonthlyTax($tax_name, $month, $year, $mysqli) { return $row['monthly_tax'] ?? 0; } -function getQuarterlyTax($tax_name, $quarter, $year, $mysqli) { +function getQuarterlyTax($tax_name, $quarter, $year, $mysqli) +{ // Calculate start and end months for the quarter $start_month = ($quarter - 1) * 3 + 1; $end_month = $start_month + 2; @@ -879,7 +903,8 @@ function getQuarterlyTax($tax_name, $quarter, $year, $mysqli) { return $row['quarterly_tax'] ?? 0; } -function getTotalTax($tax_name, $year, $mysqli) { +function getTotalTax($tax_name, $year, $mysqli) +{ // SQL to calculate total tax $sql = "SELECT SUM(item_tax) AS total_tax FROM invoice_items LEFT JOIN invoices ON invoice_items.item_invoice_id = invoices.invoice_id @@ -892,14 +917,16 @@ function getTotalTax($tax_name, $year, $mysqli) { } //Get account currency code -function getAccountCurrencyCode($mysqli, $account_id) { +function getAccountCurrencyCode($mysqli, $account_id) +{ $sql = mysqli_query($mysqli, "SELECT account_currency_code FROM accounts WHERE account_id = $account_id"); $row = mysqli_fetch_array($sql); $account_currency_code = nullable_htmlentities($row['account_currency_code']); return $account_currency_code; } -function calculateAccountBalance($mysqli, $account_id) { +function calculateAccountBalance($mysqli, $account_id) +{ $sql_account = mysqli_query($mysqli, "SELECT * FROM accounts LEFT JOIN account_types ON accounts.account_type = account_types.account_type_id WHERE account_archived_at IS NULL AND account_id = $account_id ORDER BY account_name ASC; "); $row = mysqli_fetch_array($sql_account); $opening_balance = floatval($row['opening_balance']); @@ -927,7 +954,8 @@ function calculateAccountBalance($mysqli, $account_id) { } -function generateReadablePassword($security_level) { +function generateReadablePassword($security_level) +{ // Arrays of words $adjectives = ['Smart', 'Swift', 'Secure', 'Stable', 'Digital', 'Virtual', 'Active', 'Dynamic', 'Innovative', 'Efficient', 'Portable', 'Wireless', 'Rapid', 'Intuitive', 'Automated', 'Robust', 'Reliable', 'Sleek', 'Modern', 'Happy', 'Funny', 'Quick', 'Bright', 'Clever', 'Gentle', 'Brave', 'Calm', 'Eager', 'Fierce', 'Kind', 'Lucky', 'Proud', 'Silly', 'Witty', 'Bold', 'Curious', 'Elated', 'Gracious', 'Honest', 'Jolly', 'Merry', 'Noble', 'Optimistic', 'Playful', 'Quirky', 'Rustic', 'Steady', 'Tranquil', 'Upbeat']; $nouns = ['Computer', 'Laptop', 'Tablet', 'Server', 'Router', 'Software', 'Hardware', 'Pixel', 'Byte', 'App', 'Network', 'Cloud', 'Firewall', 'Email', 'Database', 'Folder', 'Document', 'Interface', 'Program', 'Gadget', 'Dinosaur', 'Tiger', 'Elephant', 'Kangaroo', 'Monkey', 'Unicorn', 'Dragon', 'Puppy', 'Kitten', 'Parrot', 'Lion', 'Bear', 'Fox', 'Wolf', 'Rabbit', 'Deer', 'Owl', 'Hedgehog', 'Turtle', 'Frog', 'Butterfly', 'Panda', 'Giraffe', 'Zebra', 'Peacock', 'Koala', 'Raccoon', 'Squirrel', 'Hippo', 'Rhino', 'Book', "Monitor"]; @@ -943,7 +971,7 @@ function generateReadablePassword($security_level) { // Combine to create a base password - if ($security_level > 2 ) { + if ($security_level > 2) { $password = "The" . $adj . $noun . $adv . $verb; } else { $password = $adj . $noun . $verb; @@ -962,7 +990,7 @@ function generateReadablePassword($security_level) { if ($security_level > 4) { $password = strtr($password, $mappings); } else { - // Randomly replace characters based on mappings + // Randomly replace characters based on mappings for ($i = 0; $i < strlen($password); $i++) { if (array_key_exists($password[$i], $mappings) && rand(0, 1)) { $password[$i] = $mappings[$password[$i]]; @@ -978,7 +1006,8 @@ function generateReadablePassword($security_level) { return $password; } -function addToMailQueue($mysqli, $data) { +function addToMailQueue($mysqli, $data) +{ foreach ($data as $email) { $from = strval($email['from']); @@ -1001,10 +1030,10 @@ function addToMailQueue($mysqli, $data) { } return true; - } -function calculateInvoiceBalance($mysqli, $invoice_id) { +function calculateInvoiceBalance($mysqli, $invoice_id) +{ $invoice_id_int = intval($invoice_id); $sql_invoice = mysqli_query($mysqli, "SELECT * FROM invoices WHERE invoice_id = $invoice_id_int"); $row = mysqli_fetch_array($sql_invoice); @@ -1014,7 +1043,8 @@ function calculateInvoiceBalance($mysqli, $invoice_id) { $mysqli, "SELECT SUM(payment_amount) AS total_payments FROM payments WHERE payment_invoice_id = $invoice_id - "); + " + ); $row = mysqli_fetch_array($sql_payments); $total_payments = floatval($row['total_payments']); @@ -1026,11 +1056,10 @@ function calculateInvoiceBalance($mysqli, $invoice_id) { } return $balance; - - } -function createiCalStr($datetime, $title, $description, $location) { +function createiCalStr($datetime, $title, $description, $location) +{ require_once "plugins/zapcal/zapcallib.php"; // Create the iCal object diff --git a/post/ticket.php b/post/ticket.php index be607124..59b3903b 100644 --- a/post/ticket.php +++ b/post/ticket.php @@ -1597,18 +1597,18 @@ if (isset($_POST['edit_ticket_schedule'])) {
")), 'cal_str' => $cal_str ]; - } + } - $response = addToMailQueue($mysqli, $data); + $response = addToMailQueue($mysqli, $data); - // Update ticket reply - mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = 'Ticket scheduled for $schedule', ticket_reply_type = 'Internal', ticket_reply_time_worked = '00:05:00', ticket_reply_by = $session_user_id, ticket_reply_ticket_id = $ticket_id"); + // Update ticket reply + mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = 'Ticket scheduled for $schedule', ticket_reply_type = 'Internal', ticket_reply_time_worked = '00:05:00', ticket_reply_by = $session_user_id, ticket_reply_ticket_id = $ticket_id"); - //Logging - mysqli_query( - $mysqli, - "INSERT INTO logs SET + //Logging + mysqli_query( + $mysqli, + "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Modify', log_description = '$session_name modified ticket schedule', @@ -1616,18 +1616,17 @@ if (isset($_POST['edit_ticket_schedule'])) { log_user_agent = '$session_user_agent', log_user_id = $session_user_id, log_entity_id = $ticket_id" - ); + ); - if(empty($conflicting_tickets)){ - $_SESSION['alert_message'] = "Ticket scheduled for $email_datetime"; - header("Location: " . $_SERVER["HTTP_REFERER"]); - } else { - $_SESSION['alert_type'] = "error"; - $_SESSION['alert_message'] = "Ticket scheduled for $email_datetime. Yet there are conflicting tickets scheduled for the same time:
" . implode(",
", $conflicting_tickets); - header("Location: calendar_events.php"); - } + if (empty($conflicting_tickets)) { + $_SESSION['alert_message'] = "Ticket scheduled for $email_datetime"; + header("Location: " . $_SERVER["HTTP_REFERER"]); + } else { + $_SESSION['alert_type'] = "error"; + $_SESSION['alert_message'] = "Ticket scheduled for $email_datetime. Yet there are conflicting tickets scheduled for the same time:
" . implode(",
", $conflicting_tickets); + header("Location: calendar_events.php"); + } - exit; + exit; } - diff --git a/scheduled_ticket_add_modal.php b/scheduled_ticket_add_modal.php index 199d4b0c..4f6dd74a 100644 --- a/scheduled_ticket_add_modal.php +++ b/scheduled_ticket_add_modal.php @@ -25,26 +25,28 @@ $contact_id = intval($row['contact_id']); $contact_name = nullable_htmlentities($row['contact_name']); $contact_primary = intval($row['contact_primary']); - if($contact_primary == 1) { + if ($contact_primary == 1) { $contact_primary_display = " (Primary)"; } else { $contact_primary_display = ""; } $contact_technical = intval($row['contact_technical']); - if($contact_technical == 1) { + if ($contact_technical == 1) { $contact_technical_display = " (Technical)"; } else { $contact_technical_display = ""; } $contact_title = nullable_htmlentities($row['contact_title']); - if(!empty($contact_title)) { + if (!empty($contact_title)) { $contact_title_display = " - $contact_title"; } else { $contact_title_display = ""; } - - ?> - + + ?> +
@@ -64,7 +66,7 @@ while ($row = mysqli_fetch_array($sql)) { $selectable_client_id = intval($row['client_id']); $client_name = nullable_htmlentities($row['client_name']); - ?> + ?> @@ -139,7 +141,7 @@ while ($row = mysqli_fetch_array($sql_assets)) { $asset_id_select = intval($row['asset_id']); $asset_name_select = nullable_htmlentities($row['asset_name']); - ?> + ?> @@ -161,4 +163,4 @@
-
+
\ No newline at end of file diff --git a/scheduled_ticket_edit_modal.php b/scheduled_ticket_edit_modal.php index e7ad4600..f1eed8bc 100644 --- a/scheduled_ticket_edit_modal.php +++ b/scheduled_ticket_edit_modal.php @@ -100,4 +100,4 @@
-
+
\ No newline at end of file diff --git a/scheduled_tickets.php b/scheduled_tickets.php index 54735a73..77b41b15 100644 --- a/scheduled_tickets.php +++ b/scheduled_tickets.php @@ -23,59 +23,60 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); ?> -
-
-

Recurring Tickets

-
-
- -
+
+
+

Recurring Tickets

+
+
+
+
-
+
-
-
+ +
-
-
- -
- -
+
+
+ +
+
-
- - - -
- -
+
-
+ -
- +
+
+ +
- - "> +
+ +
+ + +
+ "> - + - + + ?> @@ -133,8 +133,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); + -
@@ -89,9 +90,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
Next Run Date Action
@@ -116,8 +117,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - )"> + )">
+ - - -
- - +
-
- - + + +
+
+ + +
@@ -365,16 +375,16 @@ if (isset($_GET['ticket_id'])) {
- + -
-
-
- - +
+
+
+ + +
-
@@ -424,9 +434,15 @@ if (isset($_GET['ticket_id'])) { AND ticket_attachment_ticket_id = $ticket_id" ); - ?> + ?> -
mb-3"> +
mb-3">

@@ -442,7 +458,9 @@ if (isset($_GET['ticket_id'])) {
- +
@@ -490,11 +508,9 @@ if (isset($_GET['ticket_id'])) {
- @@ -569,7 +585,7 @@ if (isset($_GET['ticket_id'])) { $prev_ticket_id = intval($prev_ticket_row['ticket_id']); $prev_ticket_subject = nullable_htmlentities($prev_ticket_row['ticket_subject']); $prev_ticket_status = nullable_htmlentities($prev_ticket_row['ticket_status']); - ?> + ?>
@@ -580,7 +596,7 @@ if (isset($_GET['ticket_id'])) { Status:
- +
@@ -605,11 +621,10 @@ if (isset($_GET['ticket_id'])) { while ($ticket_watcher_row = mysqli_fetch_array($sql_ticket_watchers)) { $watcher_id = intval($ticket_watcher_row['watcher_id']); $ticket_watcher_email = nullable_htmlentities($ticket_watcher_row['watcher_email']); - ?> + ?> @@ -693,7 +708,7 @@ if (isset($_GET['ticket_id'])) {
- +
@@ -757,12 +772,12 @@ if (isset($_GET['ticket_id'])) { $service_ticket_status = nullable_htmlentities($row['ticket_status']); $service_ticket_created_at = nullable_htmlentities($row['ticket_created_at']); $service_ticket_updated_at = nullable_htmlentities($row['ticket_updated_at']); - ?> + ?>

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

-
@@ -774,9 +789,11 @@ if (isset($_GET['ticket_id'])) {
- + - +

@@ -824,7 +841,8 @@ if (isset($_GET['ticket_id'])) {
- +
@@ -837,18 +855,24 @@ if (isset($_GET['ticket_id'])) {
- > - +
- +
@@ -872,7 +896,7 @@ if (isset($_GET['ticket_id'])) {
- +?> + if ($ticket_status !== "Closed") { ?> @@ -920,5 +942,4 @@ if ($ticket_status !== "Closed") { ?> - - + \ No newline at end of file diff --git a/ticket_edit_schedule_modal.php b/ticket_edit_schedule_modal.php index 85bfa2cc..50e63263 100644 --- a/ticket_edit_schedule_modal.php +++ b/ticket_edit_schedule_modal.php @@ -16,11 +16,9 @@
- + - +
@@ -34,10 +32,8 @@
diff --git a/tickets.php b/tickets.php index a04cce3f..20046ae6 100644 --- a/tickets.php +++ b/tickets.php @@ -39,7 +39,7 @@ if (isset($_GET['assigned']) & !empty($_GET['assigned'])) { if ($_GET['assigned'] == 'unassigned') { $ticket_assigned_filter = 'AND ticket_assigned_to = 0'; } else { - $ticket_assigned_filter = 'AND ticket_assigned_to = '.intval($_GET['assigned']); + $ticket_assigned_filter = 'AND ticket_assigned_to = ' . intval($_GET['assigned']); } } else { // Default - any @@ -117,10 +117,11 @@ $user_active_assigned_tickets = intval($row['total_tickets_assigned']);
- +
- +
@@ -143,7 +144,7 @@ $user_active_assigned_tickets = intval($row['total_tickets_assigned']); Recurring Tickets | - +
- +
-
" id="advancedFilter"> +
" id="advancedFilter">
@@ -232,14 +235,28 @@ $user_active_assigned_tickets = intval($row['total_tickets_assigned']);
- + + + + + + +
@@ -247,17 +264,23 @@ $user_active_assigned_tickets = intval($row['total_tickets_assigned']);
- "> - - - - - - - "> + + + + + + + + } + ?> - - - - + + + + - + - Never

"; + while ($row = mysqli_fetch_array($sql)) { + $ticket_id = intval($row['ticket_id']); + $ticket_prefix = nullable_htmlentities($row['ticket_prefix']); + $ticket_number = intval($row['ticket_number']); + $ticket_subject = nullable_htmlentities($row['ticket_subject']); + $ticket_priority = nullable_htmlentities($row['ticket_priority']); + $ticket_status = nullable_htmlentities($row['ticket_status']); + $ticket_billable = intval($row['ticket_billable']); + $ticket_vendor_ticket_number = nullable_htmlentities($row['ticket_vendor_ticket_number']); + $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 == "Closed") { + $ticket_updated_at_display = "

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']); - $client_id = intval($row['ticket_client_id']); - $client_name = nullable_htmlentities($row['client_name']); - $contact_id = intval($row['ticket_contact_id']); - $contact_name = nullable_htmlentities($row['contact_name']); - $contact_title = nullable_htmlentities($row['contact_title']); - $contact_email = nullable_htmlentities($row['contact_email']); - $contact_phone = formatPhoneNumber($row['contact_phone']); - $contact_extension = nullable_htmlentities($row['contact_extension']); - $contact_mobile = formatPhoneNumber($row['contact_mobile']); - if ($ticket_status == "Pending-Assignment") { - $ticket_status_color = "danger"; - } elseif ($ticket_status == "Assigned") { - $ticket_status_color = "primary"; - } elseif ($ticket_status == "In-Progress") { - $ticket_status_color = "success"; - } elseif ($ticket_status == "Closed") { - $ticket_status_color = "dark"; - } elseif ($ticket_status == "Auto Close") { - $ticket_status_color = "dark"; - } elseif ($ticket_status == "Client-Replied") { - $ticket_status_color = "warning"; - } else{ - $ticket_status_color = "secondary"; - } - - if ($ticket_priority == "High") { - $ticket_priority_color = "danger"; - } elseif ($ticket_priority == "Medium") { - $ticket_priority_color = "warning"; - } else{ - $ticket_priority_color = "info"; - } - $ticket_assigned_to = intval($row['ticket_assigned_to']); - if (empty($ticket_assigned_to)) { - if ($ticket_status == "Closed") { - $ticket_assigned_to_display = "

Not Assigned

"; + $ticket_closed_at = nullable_htmlentities($row['ticket_closed_at']); + $client_id = intval($row['ticket_client_id']); + $client_name = nullable_htmlentities($row['client_name']); + $contact_id = intval($row['ticket_contact_id']); + $contact_name = nullable_htmlentities($row['contact_name']); + $contact_title = nullable_htmlentities($row['contact_title']); + $contact_email = nullable_htmlentities($row['contact_email']); + $contact_phone = formatPhoneNumber($row['contact_phone']); + $contact_extension = nullable_htmlentities($row['contact_extension']); + $contact_mobile = formatPhoneNumber($row['contact_mobile']); + if ($ticket_status == "Pending-Assignment") { + $ticket_status_color = "danger"; + } elseif ($ticket_status == "Assigned") { + $ticket_status_color = "primary"; + } elseif ($ticket_status == "In-Progress") { + $ticket_status_color = "success"; + } elseif ($ticket_status == "Closed") { + $ticket_status_color = "dark"; + } elseif ($ticket_status == "Auto Close") { + $ticket_status_color = "dark"; + } elseif ($ticket_status == "Client-Replied") { + $ticket_status_color = "warning"; } else { - $ticket_assigned_to_display = "

Not Assigned

"; + $ticket_status_color = "secondary"; } - } else { - $ticket_assigned_to_display = nullable_htmlentities($row['user_name']); - } - if (empty($contact_name)) { - $contact_display = "-"; - } else { - $contact_display = "$contact_name
$contact_email"; - } + if ($ticket_priority == "High") { + $ticket_priority_color = "danger"; + } elseif ($ticket_priority == "Medium") { + $ticket_priority_color = "warning"; + } else { + $ticket_priority_color = "info"; + } + $ticket_assigned_to = intval($row['ticket_assigned_to']); + if (empty($ticket_assigned_to)) { + if ($ticket_status == "Closed") { + $ticket_assigned_to_display = "

Not Assigned

"; + } else { + $ticket_assigned_to_display = "

Not Assigned

"; + } + } else { + $ticket_assigned_to_display = nullable_htmlentities($row['user_name']); + } - $asset_id = intval($row['ticket_asset_id']); - $vendor_id = intval($row['ticket_vendor_id']); + if (empty($contact_name)) { + $contact_display = "-"; + } else { + $contact_display = "$contact_name
$contact_email"; + } + + $asset_id = intval($row['ticket_asset_id']); + $vendor_id = intval($row['ticket_vendor_id']); ?> - "> - - - - "> + + + + - - + + $"; - } else { - echo "X"; } - ?> - - - - - - - + ?> + + + + + + + ?>
-
- -
-
Number - Subject - Client / Contact - Billable +
+
+ +
+
Number Subject + Client / Contact + Billable + Priority - Status - Assigned - Last Response - Created - Priority + Status + Assigned + Last Response + Created +
- -
- -
- -
- - - - - - - +
+ +
+ +
+ +
+ + + + + + + -
-
- +
+
+ + $"; + } else { + echo "X"; + } + ?> + - -
- -
+ +
+ +