Fix index.php, Move Guest files to /Guest directory, fix adminlte path

This commit is contained in:
johnnyq
2025-01-11 15:48:10 -05:00
parent 08b91ead25
commit 7a23cf245d
13 changed files with 69 additions and 69 deletions

124
guest/guest_ajax.php Normal file
View File

@@ -0,0 +1,124 @@
<?php
/*
* guest_ajax.php
* Similar to post.php/ajax.php, but for unauthenticated requests using Asynchronous JavaScript
* Always returns data in JSON format, unless otherwise specified
*/
require_once "../config.php";
// Set Timezone
require_once "../inc_set_timezone.php";
require_once "../functions.php";
require_once "../rfc6238.php";
/*
* Creates & Returns a Stripe Payment Intent for a particular invoice ID
*/
if (isset($_GET['stripe_create_pi'])) {
// Response header
header('Content-Type: application/json');
// Params from POST (guest_pay_invoice_stripe.js)
$jsonStr = file_get_contents('php://input');
$jsonObj = json_decode($jsonStr, true);
$invoice_id = intval($jsonObj['invoice_id']);
$url_key = sanitizeInput($jsonObj['url_key']);
// Query invoice details
$invoice_sql = mysqli_query(
$mysqli,
"SELECT * FROM invoices
LEFT JOIN clients ON invoice_client_id = client_id
WHERE invoice_id = $invoice_id
AND invoice_url_key = '$url_key'
AND invoice_status != 'Draft'
AND invoice_status != 'Paid'
AND invoice_status != 'Cancelled'
LIMIT 1"
);
if (!$invoice_sql || mysqli_num_rows($invoice_sql) !== 1) {
exit("Invalid Invoice ID/SQL query");
}
// Invoice exists - get details for payment
$row = mysqli_fetch_array($invoice_sql);
$invoice_prefix = nullable_htmlentities($row['invoice_prefix']);
$invoice_number = intval($row['invoice_number']);
$invoice_amount = floatval($row['invoice_amount']);
$invoice_currency_code = nullable_htmlentities($row['invoice_currency_code']);
$client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']);
$config_sql = mysqli_query($mysqli, "SELECT * FROM settings WHERE company_id = 1");
$config_row = mysqli_fetch_array($config_sql);
$config_stripe_percentage_fee = floatval($config_row['config_stripe_percentage_fee']);
$config_stripe_flat_fee = floatval($config_row['config_stripe_flat_fee']);
// Add up all the payments for the invoice and get the total amount paid to the invoice
$sql_amount_paid = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS amount_paid FROM payments WHERE payment_invoice_id = $invoice_id");
$row = mysqli_fetch_array($sql_amount_paid);
$amount_paid = floatval($row['amount_paid']);
$balance_to_pay = $invoice_amount - $amount_paid;
$balance_to_pay = round($balance_to_pay, 2);
if (intval($balance_to_pay) == 0) {
exit("No balance outstanding");
}
// Setup Stripe
require_once '../vendor/stripe-php-10.5.0/init.php';
$row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_stripe_enable, config_stripe_secret, config_stripe_account FROM settings WHERE company_id = 1"));
if ($row['config_stripe_enable'] == 0 || $row['config_stripe_account'] == 0) {
exit("Stripe not enabled / configured");
}
$config_stripe_secret = $row['config_stripe_secret'];
$pi_description = "ITFlow: $client_name payment of $invoice_currency_code $balance_to_pay for $invoice_prefix$invoice_number";
// Create a PaymentIntent with amount, currency and client details
try {
\Stripe\Stripe::setApiKey($config_stripe_secret);
$paymentIntent = \Stripe\PaymentIntent::create([
'amount' => intval($balance_to_pay * 100), // Times by 100 as Stripe expects values in cents
'currency' => $invoice_currency_code,
'description' => $pi_description,
'metadata' => [
'itflow_client_id' => $client_id,
'itflow_client_name' => $client_name,
'itflow_invoice_number' => $invoice_prefix . $invoice_number,
'itflow_invoice_id' => $invoice_id,
],
'automatic_payment_methods' => [
'enabled' => true,
],
]);
$output = [
'clientSecret' => $paymentIntent->client_secret,
];
echo json_encode($output);
} catch (Error $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
}
if (isset($_GET['get_totp_token'])) {
$otp = TokenAuth6238::getTokenCode(strtoupper($_GET['totp_secret']));
echo json_encode($otp);
}

View File

@@ -0,0 +1,77 @@
<?php
// Not including the guest header as we don't want any HTML output
require_once "../config.php";
// Set Timezone
require_once "../inc_set_timezone.php";
require_once "../functions.php";
$session_ip = sanitizeInput(getIP());
$session_user_agent = sanitizeInput($_SERVER['HTTP_USER_AGENT']);
if (isset($_GET['id']) && isset($_GET['key'])) {
$item_id = intval($_GET['id']);
$item_key = sanitizeInput($_GET['key']);
$sql = mysqli_query($mysqli, "SELECT * FROM shared_items WHERE item_id = $item_id AND item_key = '$item_key' AND item_expire_at > NOW() LIMIT 1");
$row = mysqli_fetch_array($sql);
$item_active = intval($row['item_active']);
$item_type = sanitizeInput($row['item_type']);
$item_views = intval($row['item_views']);
$item_view_limit = intval($row['item_view_limit']);
$item_related_id = intval($row['item_related_id']);
$client_id = intval($row['item_client_id']);
// Check result
if (mysqli_num_rows($sql) !== 1 || !$row) {
exit("Item cannot be viewed at this time (disabled or invalid).");
}
// Check it is a file
if ($item_type !== "File") {
exit("Item cannot be viewed at this time (Bad item type: expected File but got $item_type).");
}
// Check item sharing link is active
if ($item_active != "1") {
exit("Item cannot be viewed at this time (disabled).");
}
// Check view limit (if not unlimited)
if ($item_view_limit !== 0) {
// Not unlimited
if ($item_views >= $item_view_limit) {
// Views exceed
exit("Item cannot be viewed at this time (view limit exceeded).");
}
}
$file_sql = mysqli_query($mysqli, "SELECT * FROM files WHERE file_id = $item_related_id AND file_client_id = $client_id LIMIT 1");
$file_row = mysqli_fetch_array($file_sql);
if (mysqli_num_rows($file_sql) !== 1 || !$file_row) {
exit("Item cannot be viewed at this time (No file, may have been deleted).");
}
$file_name = sanitizeInput($file_row['file_name']);
$file_reference_name = sanitizeInput($file_row['file_reference_name']);
$client_id = intval($file_row['file_client_id']);
$file_path = "../uploads/clients/$client_id/$file_reference_name";
// Display file as download
$mime_type = mime_content_type($file_path);
header('Content-type: '.$mime_type);
header('Content-Disposition: attachment; filename=' . $file_name);
readfile($file_path);
// Update file view count
$new_item_views = $item_views + 1;
mysqli_query($mysqli, "UPDATE shared_items SET item_views = $new_item_views WHERE item_id = $item_id");
//Logging
logAction("Share", "View", "Downloaded shared file $file_name via link", $client_id);
}

30
guest/guest_footer.php Normal file
View File

@@ -0,0 +1,30 @@
</div><!-- /.container-fluid -->
</div>
<!-- /.content -->
</div>
<!-- /.content-wrapper -->
</div>
<!-- ./wrapper -->
<!-- REQUIRED SCRIPTS -->
<?php require_once "../includes/inc_confirm_modal.php"; ?>
<!-- jQuery -->
<script src="../plugins/jquery/jquery.min.js"></script>
<!-- Bootstrap 4 -->
<script src="../plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- AdminLTE App -->
<script src="../plugins/adminlte/js/adminlte.min.js"></script>
<!-- Custom js -->
<script src="../plugins/tempusdominus-bootstrap-4/js/tempusdominus-bootstrap-4.min.js"></script>
<script src="../plugins/moment/moment.min.js"></script>
<script src='../plugins/daterangepicker/daterangepicker.js'></script>
<script src='../plugins/select2/js/select2.min.js'></script>
<script src='../plugins/inputmask/inputmask.min.js'></script>
<script src="../js/app.js"></script>
<script src="../js/confirm_modal.js"></script>
</body>
</html>

81
guest/guest_header.php Normal file
View File

@@ -0,0 +1,81 @@
<?php
require_once "../config.php";
require_once "../functions.php";
session_start();
// Set Timezone
require_once "../inc_set_timezone.php";
$ip = sanitizeInput(getIP());
$user_agent = sanitizeInput($_SERVER['HTTP_USER_AGENT']);
$os = sanitizeInput(getOS($user_agent));
$browser = sanitizeInput(getWebBrowser($user_agent));
// Get Company Name
$sql = mysqli_query($mysqli, "SELECT company_name FROM companies WHERE company_id = 1");
$row = mysqli_fetch_array($sql);
$session_company_name = $row['company_name'];
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="robots" content="noindex">
<title><?php echo nullable_htmlentities($session_company_name); ?></title>
<!--
Favicon
If Fav Icon exists else use the default one
-->
<?php if(file_exists('../uploads/favicon.ico')) { ?>
<link rel="icon" type="image/x-icon" href="../uploads/favicon.ico">
<?php } ?>
<!-- Font Awesome Icons -->
<link rel="stylesheet" href="../plugins/fontawesome-free/css/all.min.css">
<!-- Theme style -->
<link rel="stylesheet" href="../plugins/adminlte/css/adminlte.min.css">
<!-- Custom Style Sheet -->
<link href="../plugins/tempusdominus-bootstrap-4/css/tempusdominus-bootstrap-4.min.css" rel="stylesheet" type="text/css">
<link href="../plugins/select2/css/select2.min.css" rel="stylesheet" type="text/css">
<link href="../plugins/select2-bootstrap4-theme/select2-bootstrap4.min.css" rel="stylesheet" type="text/css">
<link href='../plugins/daterangepicker/daterangepicker.css' rel='stylesheet' />
</head>
<body class="layout-top-nav">
<div class="wrapper text-sm">
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<!-- Main content -->
<div class="content">
<div class="container">
<?php
//Alert Feedback
if (!empty($_SESSION['alert_message'])) {
if (!isset($_SESSION['alert_type'])) {
$_SESSION['alert_type'] = "info";
}
?>
<div class="alert alert-<?php echo $_SESSION['alert_type']; ?>" id="alert">
<?php echo nullable_htmlentities($_SESSION['alert_message']); ?>
<button class='close' data-dismiss='alert'>&times;</button>
</div>
<?php
unset($_SESSION['alert_type']);
unset($_SESSION['alert_message']);
}
?>

View File

@@ -0,0 +1,360 @@
<?php
require_once 'guest_header.php';
// Define wording
DEFINE("WORDING_PAYMENT_FAILED", "<br><h2>There was an error verifying your payment. Please contact us for more information before attempting payment again.</h2>");
// Setup Stripe
$stripe_vars = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_stripe_enable, config_stripe_publishable, config_stripe_secret, config_stripe_account, config_stripe_expense_vendor, config_stripe_expense_category, config_stripe_percentage_fee, config_stripe_flat_fee FROM settings WHERE company_id = 1"));
$config_stripe_enable = intval($stripe_vars['config_stripe_enable']);
$config_stripe_publishable = nullable_htmlentities($stripe_vars['config_stripe_publishable']);
$config_stripe_secret = nullable_htmlentities($stripe_vars['config_stripe_secret']);
$config_stripe_account = intval($stripe_vars['config_stripe_account']);
$config_stripe_expense_vendor = intval($stripe_vars['config_stripe_expense_vendor']);
$config_stripe_expense_category = intval($stripe_vars['config_stripe_expense_category']);
$config_stripe_percentage_fee = floatval($stripe_vars['config_stripe_percentage_fee']);
$config_stripe_flat_fee = floatval($stripe_vars['config_stripe_flat_fee']);
// Check Stripe is configured
if ($config_stripe_enable == 0 || $config_stripe_account == 0 || empty($config_stripe_publishable) || empty($config_stripe_secret)) {
echo "<br><h2>Stripe payments not enabled/configured</h2>";
require_once 'guest_footer.php';
error_log("Stripe payment error - disabled. Check payments are enabled, Expense account is set, Stripe publishable and secret keys are configured.");
exit();
}
// Show payment form
// Users are directed to this page with the invoice_id and url_key params to make a payment
if (isset($_GET['invoice_id'], $_GET['url_key']) && !isset($_GET['payment_intent'])) {
$invoice_url_key = sanitizeInput($_GET['url_key']);
$invoice_id = intval($_GET['invoice_id']);
// Query invoice details
$sql = mysqli_query(
$mysqli,
"SELECT * FROM invoices
LEFT JOIN clients ON invoice_client_id = client_id
WHERE invoice_id = $invoice_id
AND invoice_url_key = '$invoice_url_key'
AND invoice_status != 'Draft'
AND invoice_status != 'Paid'
AND invoice_status != 'Cancelled'
LIMIT 1"
);
// Ensure we have a valid invoice
if (!$sql || mysqli_num_rows($sql) !== 1) {
echo "<br><h2>Oops, something went wrong! Please ensure you have the correct URL and have not already paid this invoice.</h2>";
require_once 'guest_footer.php';
error_log("Stripe payment error - Invoice with ID $invoice_id is unknown/not eligible to be paid.");
exit();
}
// Process invoice, client and company details/settings
$row = mysqli_fetch_array($sql);
$invoice_id = intval($row['invoice_id']);
$invoice_prefix = nullable_htmlentities($row['invoice_prefix']);
$invoice_number = intval($row['invoice_number']);
$invoice_status = nullable_htmlentities($row['invoice_status']);
$invoice_date = nullable_htmlentities($row['invoice_date']);
$invoice_due = nullable_htmlentities($row['invoice_due']);
$invoice_discount = floatval($row['invoice_discount_amount']);
$invoice_amount = floatval($row['invoice_amount']);
$invoice_currency_code = nullable_htmlentities($row['invoice_currency_code']);
$client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']);
$sql = mysqli_query($mysqli, "SELECT * FROM companies, settings WHERE companies.company_id = settings.company_id AND companies.company_id = 1");
$row = mysqli_fetch_array($sql);
$company_locale = nullable_htmlentities($row['company_locale']);
// Add up all the payments for the invoice and get the total amount paid to the invoice
$sql_amount_paid = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS amount_paid FROM payments WHERE payment_invoice_id = $invoice_id");
$row = mysqli_fetch_array($sql_amount_paid);
$amount_paid = floatval($row['amount_paid']);
$balance_to_pay = $invoice_amount - $amount_paid;
//Round balance to pay to 2 decimal places
$balance_to_pay = round($balance_to_pay, 2);
// Get invoice items
$sql_invoice_items = mysqli_query($mysqli, "SELECT * FROM invoice_items WHERE item_invoice_id = $invoice_id ORDER BY item_id ASC");
// Set Currency Formatting
$currency_format = numfmt_create($company_locale, NumberFormatter::CURRENCY);
?>
<!-- Include Stripe JS (must be Stripe-hosted, not local) -->
<script src="https://js.stripe.com/v3/"></script>
<!-- jQuery -->
<script src="plugins/jquery/jquery.min.js"></script>
<div class="row pt-5">
<!-- Show invoice details -->
<div class="col-sm">
<h3>Payment for Invoice: <?php echo $invoice_prefix . $invoice_number ?></h3>
<br>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Product</th>
<th class="text-center">Qty</th>
<th class="text-right">Total</th>
</tr>
</thead>
<tbody>
<?php
$item_total = 0;
while ($row = mysqli_fetch_array($sql_invoice_items)) {
$item_name = nullable_htmlentities($row['item_name']);
$item_quantity = floatval($row['item_quantity']);
$item_total = floatval($row['item_total']);
?>
<tr>
<td><?php echo $item_name; ?></td>
<td class="text-center"><?php echo $item_quantity; ?></td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $item_total, $invoice_currency_code); ?></td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
<br>
<i><?php if ($invoice_discount > 0){ echo "Discount: " . numfmt_format_currency($currency_format, $invoice_discount, $invoice_currency_code); } ?>
</i>
<br>
<i><?php if (intval($amount_paid) > 0) { ?> Already paid: <?php echo numfmt_format_currency($currency_format, $amount_paid, $invoice_currency_code); } ?></i>
</div>
<!-- End invoice details-->
<!-- Show Stripe payment form -->
<div class="col-sm offset-sm-1">
<h1>Payment Total:</h1>
<form id="payment-form">
<h1><?php echo numfmt_format_currency($currency_format, $balance_to_pay, $invoice_currency_code); ?></h1>
<input type="hidden" id="stripe_publishable_key" value="<?php echo $config_stripe_publishable ?>">
<input type="hidden" id="invoice_id" value="<?php echo $invoice_id ?>">
<input type="hidden" id="url_key" value="<?php echo $invoice_url_key ?>">
<br>
<div id="link-authentication-element">
<!--Stripe.js injects the Link Authentication Element-->
</div>
<div id="payment-element">
<!--Stripe.js injects the Payment Element-->
</div>
<br>
<button type="submit" id="submit" class="btn btn-primary btn-lg btn-block text-bold" hidden="hidden">
<div class="spinner hidden" id="spinner"></div>
<span id="button-text"><i class="fas fa-check mr-2"></i>Pay Invoice</span>
</button>
<div id="payment-message" class="hidden"></div>
</form>
</div>
<!-- End Stripe payment form -->
</div>
<!-- Include local JS that powers stripe -->
<script src="../js/guest_pay_invoice_stripe.js"></script>
<?php
// Process payment & redirect user back to invoice
// (Stripe will redirect back to this page upon payment success with the payment_intent and payment_intent_client_secret params set
} elseif (isset($_GET['payment_intent'], $_GET['payment_intent_client_secret'])) {
// Params from GET
$pi_id = sanitizeInput($_GET['payment_intent']);
$pi_cs = $_GET['payment_intent_client_secret'];
// Initialize stripe
require_once '../vendor/stripe-php-10.5.0/init.php';
\Stripe\Stripe::setApiKey($config_stripe_secret);
// Check details of the PI
$pi_obj = \Stripe\PaymentIntent::retrieve($pi_id);
if ($pi_obj->client_secret !== $pi_cs) {
error_log("Stripe payment error - Payment intent ID/Secret mismatch for $pi_id");
exit(WORDING_PAYMENT_FAILED);
} elseif ($pi_obj->status !== "succeeded") {
exit(WORDING_PAYMENT_FAILED);
} elseif ($pi_obj->amount !== $pi_obj->amount_received) {
// The invoice wasn't paid in full
// this should be flagged for manual review as would indicate something weird happening
error_log("Stripe payment error - payment amount does not match amount paid for $pi_id");
exit(WORDING_PAYMENT_FAILED);
}
// Get details from PI
$pi_date = date('Y-m-d', $pi_obj->created);
$pi_invoice_id = intval($pi_obj->metadata->itflow_invoice_id);
$pi_client_id = intval($pi_obj->metadata->itflow_client_id);
$pi_amount_paid = floatval(($pi_obj->amount_received / 100));
$pi_currency = strtoupper(sanitizeInput($pi_obj->currency));
$pi_livemode = $pi_obj->livemode;
// Get/Check invoice (& client/primary contact)
$invoice_sql = mysqli_query(
$mysqli,
"SELECT * FROM invoices
LEFT JOIN clients ON invoice_client_id = client_id
LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1
WHERE invoice_id = $pi_invoice_id
AND invoice_status != 'Draft'
AND invoice_status != 'Paid'
AND invoice_status != 'Cancelled'
LIMIT 1"
);
if (!$invoice_sql || mysqli_num_rows($invoice_sql) !== 1) {
error_log("Stripe payment error - Invoice with ID $invoice_id is unknown/not eligible to be paid. PI $pi_id");
exit(WORDING_PAYMENT_FAILED);
}
// Invoice exists - get details
$row = mysqli_fetch_array($invoice_sql);
$invoice_id = intval($row['invoice_id']);
$invoice_prefix = sanitizeInput($row['invoice_prefix']);
$invoice_number = intval($row['invoice_number']);
$invoice_amount = floatval($row['invoice_amount']);
$invoice_currency_code = sanitizeInput($row['invoice_currency_code']);
$invoice_url_key = sanitizeInput($row['invoice_url_key']);
$client_id = intval($row['client_id']);
$client_name = sanitizeInput($row['client_name']);
$contact_name = sanitizeInput($row['contact_name']);
$contact_email = sanitizeInput($row['contact_email']);
$sql_company = mysqli_query($mysqli, "SELECT * FROM companies WHERE company_id = 1");
$row = mysqli_fetch_array($sql_company);
$company_name = sanitizeInput($row['company_name']);
$company_phone = sanitizeInput(formatPhoneNumber($row['company_phone']));
$company_locale = sanitizeInput($row['company_locale']);
// Set Currency Formatting
$currency_format = numfmt_create($company_locale, NumberFormatter::CURRENCY);
// Add up all the payments for the invoice and get the total amount paid to the invoice already (if any)
$sql_amount_paid_previously = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS amount_paid FROM payments WHERE payment_invoice_id = $invoice_id");
$row = mysqli_fetch_array($sql_amount_paid_previously);
$amount_paid_previously = $row['amount_paid'];
$balance_to_pay = $invoice_amount - $amount_paid_previously;
// Check to see if Expense Fields are configured to create Stripe payment expense
if ($config_stripe_expense_vendor > 0 && $config_stripe_expense_category > 0) {
// Calculate gateway expense fee
$gateway_fee = round($balance_to_pay * $config_stripe_percentage_fee + $config_stripe_flat_fee, 2);
// Add Expense
mysqli_query($mysqli,"INSERT INTO expenses SET expense_date = '$pi_date', expense_amount = $gateway_fee, expense_currency_code = '$invoice_currency_code', expense_account_id = $config_stripe_account, expense_vendor_id = $config_stripe_expense_vendor, expense_client_id = $client_id, expense_category_id = $config_stripe_expense_category, expense_description = 'Stripe Transaction for Invoice $invoice_prefix$invoice_number In the Amount of $balance_to_pay', expense_reference = 'Stripe - $pi_id'");
}
// Round balance to pay to 2 decimal places
$balance_to_pay = round($balance_to_pay, 2);
// Sanity check that the amount paid is exactly the invoice outstanding balance
if (intval($balance_to_pay) !== intval($pi_amount_paid)) {
error_log("Stripe payment error - Invoice balance does not match amount paid for $pi_id");
exit(WORDING_PAYMENT_FAILED);
}
// Apply payment
// Update Invoice Status
mysqli_query($mysqli, "UPDATE invoices SET invoice_status = 'Paid' WHERE invoice_id = $invoice_id");
// Add Payment to History
mysqli_query($mysqli, "INSERT INTO payments SET payment_date = '$pi_date', payment_amount = $pi_amount_paid, payment_currency_code = '$pi_currency', payment_account_id = $config_stripe_account, payment_method = 'Stripe', payment_reference = 'Stripe - $pi_id', payment_invoice_id = $invoice_id");
mysqli_query($mysqli, "INSERT INTO history SET history_status = 'Paid', history_description = 'Payment added - $ip - $os - $browser', history_invoice_id = $invoice_id");
// Notify
appNotify("Invoice Paid", "Invoice $invoice_prefix$invoice_number has been paid by $client_name - $ip - $os - $browser", "invoice.php?invoice_id=$invoice_id", $pi_client_id);
customAction('invoice_pay', $invoice_id);
// Logging
$extended_log_desc = '';
if (!$pi_livemode) {
$extended_log_desc = '(DEV MODE)';
}
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Payment', log_action = 'Create', log_description = 'Stripe payment of $pi_currency $pi_amount_paid against invoice $invoice_prefix$invoice_number - $pi_id $extended_log_desc', log_ip = '$ip', log_user_agent = '$user_agent', log_client_id = $pi_client_id");
// Send email receipt
$sql_settings = mysqli_query($mysqli, "SELECT * FROM settings WHERE company_id = 1");
$row = mysqli_fetch_array($sql_settings);
$config_smtp_host = $row['config_smtp_host'];
$config_smtp_port = intval($row['config_smtp_port']);
$config_smtp_encryption = $row['config_smtp_encryption'];
$config_smtp_username = $row['config_smtp_username'];
$config_smtp_password = $row['config_smtp_password'];
$config_invoice_from_name = sanitizeInput($row['config_invoice_from_name']);
$config_invoice_from_email = sanitizeInput($row['config_invoice_from_email']);
$config_invoice_paid_notification_email = sanitizeInput($row['config_invoice_paid_notification_email']);
$config_base_url = sanitizeInput($config_base_url);
if (!empty($config_smtp_host)) {
$subject = "Payment Received - Invoice $invoice_prefix$invoice_number";
$body = "Hello $contact_name,<br><br>We have received your payment in the amount of " . $pi_currency . $pi_amount_paid . " for invoice <a href=\'https://$config_base_url/guest_view_invoice.php?invoice_id=$invoice_id&url_key=$invoice_url_key\'>$invoice_prefix$invoice_number</a>. Please keep this email as a receipt for your records.<br><br>Amount: " . numfmt_format_currency($currency_format, $pi_amount_paid, $invoice_currency_code) . "<br>Balance: " . numfmt_format_currency($currency_format, '0', $invoice_currency_code) . "<br><br>Thank you for your business!<br><br><br>~<br>$company_name - Billing<br>$config_invoice_from_email<br>$company_phone";
$data = [
[
'from' => $config_invoice_from_email,
'from_name' => $config_invoice_from_name,
'recipient' => $contact_email,
'recipient_name' => $contact_name,
'subject' => $subject,
'body' => $body,
]
];
// Email the internal notification address too
if (!empty($config_invoice_paid_notification_email)) {
$subject = "Payment Received - $client_name - Invoice $invoice_prefix$invoice_number";
$body = "Hello, <br><br>This is a notification that an invoice has been paid in ITFlow. Below is a copy of the receipt sent to the client:-<br><br>--------<br><br>Hello $contact_name,<br><br>We have received your payment in the amount of " . $pi_currency . $pi_amount_paid . " for invoice <a href=\'https://$config_base_url/guest_view_invoice.php?invoice_id=$invoice_id&url_key=$invoice_url_key\'>$invoice_prefix$invoice_number</a>. Please keep this email as a receipt for your records.<br><br>Amount: " . numfmt_format_currency($currency_format, $pi_amount_paid, $invoice_currency_code) . "<br>Balance: " . numfmt_format_currency($currency_format, '0', $invoice_currency_code) . "<br><br>Thank you for your business!<br><br><br>~<br>$company_name - Billing<br>$config_invoice_from_email<br>$company_phone";
$data[] = [
'from' => $config_invoice_from_email,
'from_name' => $config_invoice_from_name,
'recipient' => $config_invoice_paid_notification_email,
'recipient_name' => $contact_name,
'subject' => $subject,
'body' => $body,
];
}
$mail = addToMailQueue($mysqli, $data);
// Email logging
mysqli_query($mysqli, "INSERT INTO history SET history_status = 'Sent', history_description = 'Emailed Receipt!', history_invoice_id = $invoice_id");
}
// Redirect user to invoice
header('Location: //' . $config_base_url . '/guest_view_invoice.php?invoice_id=' . $pi_invoice_id . '&url_key=' . $invoice_url_key);
} else {
exit(WORDING_PAYMENT_FAILED);
}
require_once 'guest_footer.php';

198
guest/guest_post.php Normal file
View File

@@ -0,0 +1,198 @@
<?php
require_once "../config.php";
require_once "../functions.php";
session_start();
require_once "../inc_set_timezone.php"; // Must be included after session_start to work
if (isset($_GET['accept_quote'], $_GET['url_key'])) {
$quote_id = intval($_GET['accept_quote']);
$url_key = sanitizeInput($_GET['url_key']);
// Select only the necessary fields
$sql = mysqli_query($mysqli, "SELECT quote_prefix, quote_number, client_name, client_id FROM quotes LEFT JOIN clients ON quote_client_id = client_id WHERE quote_id = $quote_id AND quote_url_key = '$url_key'");
if (mysqli_num_rows($sql) == 1) {
$row = mysqli_fetch_array($sql);
$quote_prefix = sanitizeInput($row['quote_prefix']);
$quote_number = intval($row['quote_number']);
$client_name = sanitizeInput($row['client_name']);
$client_id = intval($row['client_id']);
mysqli_query($mysqli, "UPDATE quotes SET quote_status = 'Accepted' WHERE quote_id = $quote_id");
mysqli_query($mysqli, "INSERT INTO history SET history_status = 'Accepted', history_description = 'Client accepted Quote!', history_quote_id = $quote_id");
// Notification
appNotify("Quote Accepted", "Quote $quote_prefix$quote_number has been accepted by $client_name", "quote.php?quote_id=$quote_id", $client_id);
customAction('quote_accept', $quote_id);
// Internal email notification
$sql_company = mysqli_query($mysqli, "SELECT company_name FROM companies WHERE company_id = 1");
$row = mysqli_fetch_array($sql_company);
$company_name = sanitizeInput($row['company_name']);
$sql_settings = mysqli_query($mysqli, "SELECT * FROM settings WHERE company_id = 1");
$row = mysqli_fetch_array($sql_settings);
$config_smtp_host = $row['config_smtp_host'];
$config_smtp_port = intval($row['config_smtp_port']);
$config_smtp_encryption = $row['config_smtp_encryption'];
$config_smtp_username = $row['config_smtp_username'];
$config_smtp_password = $row['config_smtp_password'];
$config_quote_from_name = sanitizeInput($row['config_quote_from_name']);
$config_quote_from_email = sanitizeInput($row['config_quote_from_email']);
$config_quote_notification_email = sanitizeInput($row['config_quote_notification_email']);
$config_base_url = sanitizeInput($config_base_url);
if (!empty($config_smtp_host) && !empty($config_quote_notification_email)) {
$subject = "Quote Accepted - $client_name - Quote $quote_prefix$quote_number";
$body = "Hello, <br><br>This is a notification that a quote has been accepted in ITFlow. <br><br>Client: $client_name<br>Quote: <a href=\'https://$config_base_url/quote.php?quote_id=$quote_id\'>$quote_prefix$quote_number</a><br><br>~<br>$company_name - Billing<br>$config_quote_from_email";
$data[] = [
'from' => $config_quote_from_email,
'from_name' => $config_quote_from_name,
'recipient' => $config_quote_notification_email,
'subject' => $subject,
'body' => $body,
];
$mail = addToMailQueue($mysqli, $data);
}
$_SESSION['alert_message'] = "Quote Accepted";
header("Location: " . $_SERVER["HTTP_REFERER"]);
} else {
echo "Invalid!!";
}
}
if (isset($_GET['decline_quote'], $_GET['url_key'])) {
$quote_id = intval($_GET['decline_quote']);
$url_key = sanitizeInput($_GET['url_key']);
// Select only the necessary fields
$sql = mysqli_query($mysqli, "SELECT quote_prefix, quote_number, client_name, client_id FROM quotes LEFT JOIN clients ON quote_client_id = client_id WHERE quote_id = $quote_id AND quote_url_key = '$url_key'");
if (mysqli_num_rows($sql) == 1) {
$row = mysqli_fetch_array($sql);
$quote_prefix = sanitizeInput($row['quote_prefix']);
$quote_number = intval($row['quote_number']);
$client_name = sanitizeInput($row['client_name']);
$client_id = intval($row['client_id']);
mysqli_query($mysqli, "UPDATE quotes SET quote_status = 'Declined' WHERE quote_id = $quote_id");
mysqli_query($mysqli, "INSERT INTO history SET history_status = 'Declined', history_description = 'Client declined Quote!', history_quote_id = $quote_id");
// Notification
appNotify("Quote Declined", "Quote $quote_prefix$quote_number has been declined by $client_name", "quote.php?quote_id=$quote_id", $client_id);
customAction('quote_decline', $quote_id);
// Internal email notification
$sql_company = mysqli_query($mysqli, "SELECT company_name FROM companies WHERE company_id = 1");
$row = mysqli_fetch_array($sql_company);
$company_name = sanitizeInput($row['company_name']);
$sql_settings = mysqli_query($mysqli, "SELECT * FROM settings WHERE company_id = 1");
$row = mysqli_fetch_array($sql_settings);
$config_smtp_host = $row['config_smtp_host'];
$config_smtp_port = intval($row['config_smtp_port']);
$config_smtp_encryption = $row['config_smtp_encryption'];
$config_smtp_username = $row['config_smtp_username'];
$config_smtp_password = $row['config_smtp_password'];
$config_quote_from_name = sanitizeInput($row['config_quote_from_name']);
$config_quote_from_email = sanitizeInput($row['config_quote_from_email']);
$config_quote_notification_email = sanitizeInput($row['config_quote_notification_email']);
$config_base_url = sanitizeInput($config_base_url);
if (!empty($config_smtp_host) && !empty($config_quote_notification_email)) {
$subject = "Quote Declined - $client_name - Quote $quote_prefix$quote_number";
$body = "Hello, <br><br>This is a notification that a quote has been declined in ITFlow. <br><br>Client: $client_name<br>Quote: <a href=\'https://$config_base_url/quote.php?quote_id=$quote_id\'>$quote_prefix$quote_number</a><br><br>~<br>$company_name - Billing<br>$config_quote_from_email";
$data[] = [
'from' => $config_quote_from_email,
'from_name' => $config_quote_from_name,
'recipient' => $config_quote_notification_email,
'subject' => $subject,
'body' => $body,
];
$mail = addToMailQueue($mysqli, $data);
}
$_SESSION['alert_type'] = "danger";
$_SESSION['alert_message'] = "Quote Declined";
header("Location: " . $_SERVER["HTTP_REFERER"]);
} else {
echo "Invalid!!";
}
}
if (isset($_GET['reopen_ticket'], $_GET['url_key'])) {
$ticket_id = intval($_GET['ticket_id']);
$url_key = sanitizeInput($_GET['url_key']);
// Select only the necessary fields
$sql = mysqli_query($mysqli, "SELECT ticket_id FROM tickets WHERE ticket_id = $ticket_id AND ticket_url_key = '$url_key' AND ticket_resolved_at IS NOT NULL AND ticket_closed_at IS NULL");
if (mysqli_num_rows($sql) == 1) {
// Update the ticket
mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 2, ticket_resolved_at = NULL WHERE ticket_id = $ticket_id AND ticket_url_key = '$url_key'");
// Add reply
mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = 'Ticket reopened by client (guest URL).', ticket_reply_type = 'Internal', ticket_reply_by = 0, ticket_reply_ticket_id = $ticket_id");
// Logging
customAction('ticket_update', $ticket_id);
$_SESSION['alert_message'] = "Ticket reopened";
header("Location: " . $_SERVER["HTTP_REFERER"]);
} else {
echo "Invalid!!";
}
}
if (isset($_GET['close_ticket'], $_GET['url_key'])) {
$ticket_id = intval($_GET['ticket_id']);
$url_key = sanitizeInput($_GET['url_key']);
// Select only the necessary fields
$sql = mysqli_query($mysqli, "SELECT ticket_id FROM tickets WHERE ticket_id = $ticket_id AND ticket_url_key = '$url_key' AND ticket_resolved_at IS NOT NULL AND ticket_closed_at IS NULL");
if (mysqli_num_rows($sql) == 1) {
// Update the ticket
mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 5, ticket_closed_at = NOW() WHERE ticket_id = $ticket_id AND ticket_url_key = '$url_key'");
// Add reply
mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = 'Ticket closed by client (guest URL).', ticket_reply_type = 'Internal', ticket_reply_by = 0, ticket_reply_ticket_id = $ticket_id");
// Logging
customAction('ticket_close', $ticket_id);
$_SESSION['alert_message'] = "Ticket closed";
header("Location: " . $_SERVER["HTTP_REFERER"]);
} else {
echo "Invalid!!";
}
}
if (isset($_GET['add_ticket_feedback'], $_GET['url_key'])) {
$ticket_id = intval($_GET['ticket_id']);
$url_key = sanitizeInput($_GET['url_key']);
$feedback = sanitizeInput($_GET['feedback']);
// Select only the necessary fields
$sql = mysqli_query($mysqli, "SELECT ticket_id FROM tickets WHERE ticket_id = $ticket_id AND ticket_url_key = '$url_key' AND ticket_closed_at IS NOT NULL");
if (mysqli_num_rows($sql) == 1) {
// Add feedback
mysqli_query($mysqli, "UPDATE tickets SET ticket_feedback = '$feedback' WHERE ticket_id = $ticket_id AND ticket_url_key = '$url_key'");
// Notify on bad feedback
if ($feedback == "Bad") {
appNotify("Feedback", "Guest rated ticket ID $ticket_id as bad", "ticket.php?ticket_id=$ticket_id");
}
$_SESSION['alert_message'] = "Feedback recorded - thank you";
header("Location: " . $_SERVER["HTTP_REFERER"]);
customAction('ticket_feedback', $ticket_id);
} else {
echo "Invalid!!";
}
}
?>

View File

@@ -0,0 +1,919 @@
<?php
require_once "guest_header.php";
if (!isset($_GET['invoice_id'], $_GET['url_key'])) {
echo "<br><h2>Oops, something went wrong! Please raise a ticket if you believe this is an error.</h2>";
require_once "guest_footer.php";
exit();
}
$url_key = sanitizeInput($_GET['url_key']);
$invoice_id = intval($_GET['invoice_id']);
$sql = mysqli_query(
$mysqli,
"SELECT * FROM invoices
LEFT JOIN clients ON invoice_client_id = client_id
LEFT JOIN locations ON clients.client_id = locations.location_client_id AND location_primary = 1
LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1
WHERE invoice_id = $invoice_id
AND invoice_url_key = '$url_key'"
);
if (mysqli_num_rows($sql) !== 1) {
// Invalid invoice/key
echo "<br><h2>Oops, something went wrong! Please raise a ticket if you believe this is an error.</h2>";
require_once "guest_footer.php";
exit();
}
$row = mysqli_fetch_array($sql);
$invoice_id = intval($row['invoice_id']);
$invoice_prefix = nullable_htmlentities($row['invoice_prefix']);
$invoice_number = intval($row['invoice_number']);
$invoice_status = nullable_htmlentities($row['invoice_status']);
$invoice_date = nullable_htmlentities($row['invoice_date']);
$invoice_due = nullable_htmlentities($row['invoice_due']);
$invoice_discount = floatval($row['invoice_discount_amount']);
$invoice_amount = floatval($row['invoice_amount']);
$invoice_currency_code = nullable_htmlentities($row['invoice_currency_code']);
$invoice_note = nullable_htmlentities($row['invoice_note']);
$invoice_category_id = intval($row['invoice_category_id']);
$client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']);
$client_name_escaped = sanitizeInput($row['client_name']);
$location_address = nullable_htmlentities($row['location_address']);
$location_city = nullable_htmlentities($row['location_city']);
$location_state = nullable_htmlentities($row['location_state']);
$location_zip = nullable_htmlentities($row['location_zip']);
$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']);
$client_website = nullable_htmlentities($row['client_website']);
$client_currency_code = nullable_htmlentities($row['client_currency_code']);
$client_net_terms = intval($row['client_net_terms']);
if ($client_net_terms == 0) {
$client_net_terms = intval($row['config_default_net_terms']);
}
$sql = mysqli_query($mysqli, "SELECT * FROM companies, settings WHERE companies.company_id = settings.company_id AND companies.company_id = 1");
$row = mysqli_fetch_array($sql);
$company_name = nullable_htmlentities($row['company_name']);
$company_address = nullable_htmlentities($row['company_address']);
$company_city = nullable_htmlentities($row['company_city']);
$company_state = nullable_htmlentities($row['company_state']);
$company_zip = nullable_htmlentities($row['company_zip']);
$company_phone = formatPhoneNumber($row['company_phone']);
$company_email = nullable_htmlentities($row['company_email']);
$company_website = nullable_htmlentities($row['company_website']);
$company_logo = nullable_htmlentities($row['company_logo']);
if (!empty($company_logo)) {
$company_logo_base64 = base64_encode(file_get_contents("../uploads/settings/$company_logo"));
}
$company_locale = nullable_htmlentities($row['company_locale']);
$config_invoice_footer = nullable_htmlentities($row['config_invoice_footer']);
$config_stripe_enable = intval($row['config_stripe_enable']);
$config_stripe_percentage_fee = floatval($row['config_stripe_percentage_fee']);
$config_stripe_flat_fee = floatval($row['config_stripe_flat_fee']);
//Set Currency Format
$currency_format = numfmt_create($company_locale, NumberFormatter::CURRENCY);
$invoice_tally_total = 0; // Default
//Set Badge color based off of invoice status
$invoice_badge_color = getInvoiceBadgeColor($invoice_status);
//Update status to Viewed only if invoice_status = "Sent"
if ($invoice_status == 'Sent') {
mysqli_query($mysqli, "UPDATE invoices SET invoice_status = 'Viewed' WHERE invoice_id = $invoice_id");
}
//Mark viewed in history
mysqli_query($mysqli, "INSERT INTO history SET history_status = '$invoice_status', history_description = 'Invoice viewed - $ip - $os - $browser', history_invoice_id = $invoice_id");
if ($invoice_status !== 'Paid') {
appNotify("Invoice Viewed", "Invoice $invoice_prefix$invoice_number has been viewed by $client_name_escaped - $ip - $os - $browser", "invoice.php?invoice_id=$invoice_id", $client_id);
}
$sql_payments = mysqli_query($mysqli, "SELECT * FROM payments, accounts WHERE payment_account_id = account_id AND payment_invoice_id = $invoice_id ORDER BY payments.payment_id DESC");
//Add up all the payments for the invoice and get the total amount paid to the invoice
$sql_amount_paid = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS amount_paid FROM payments WHERE payment_invoice_id = $invoice_id");
$row = mysqli_fetch_array($sql_amount_paid);
$amount_paid = floatval($row['amount_paid']);
// Calculate the balance owed
$balance = $invoice_amount - $amount_paid;
//check to see if overdue
$invoice_color = $invoice_badge_color; // Default
if ($invoice_status !== "Paid" && $invoice_status !== "Draft" && $invoice_status !== "Cancelled") {
$unixtime_invoice_due = strtotime($invoice_due) + 86400;
if ($unixtime_invoice_due < time()) {
$invoice_color = "text-danger";
}
}
// Invoice individual items
$sql_invoice_items = mysqli_query($mysqli, "SELECT * FROM invoice_items WHERE item_invoice_id = $invoice_id ORDER BY item_order ASC");
// Get Total Account Balance
//Add up all the payments for the invoice and get the total amount paid to the invoice
$sql_invoice_amounts = mysqli_query($mysqli, "SELECT SUM(invoice_amount) AS invoice_amounts FROM invoices WHERE invoice_client_id = $client_id AND invoice_status NOT LIKE 'Draft' AND invoice_status NOT LIKE 'Cancelled' ");
$row = mysqli_fetch_array($sql_invoice_amounts);
$account_balance = floatval($row['invoice_amounts']);
$sql_amount_paid = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS amount_paid FROM payments, invoices WHERE payment_invoice_id = invoice_id AND invoice_client_id = $client_id");
$row = mysqli_fetch_array($sql_amount_paid);
$account_amount_paid = floatval($row['amount_paid']);
$account_balance = $account_balance - $account_amount_paid;
//set Text color on balance
if ($balance > 0) {
$balance_text_color = "text-danger font-weight-bold";
} else {
$balance_text_color = "";
}
?>
<div class="card">
<div class="card-header bg-light d-print-none">
<div class="row">
<div class="col-6">
<h4 class="mt-1">Account Balance: <b><?php echo numfmt_format_currency($currency_format, $account_balance, $invoice_currency_code); ?></b></h4>
</div>
<div class="col-6">
<div class="float-right">
<a class="btn btn-default" href="#" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</a>
<a class="btn btn-default" href="#" onclick="pdfMake.createPdf(docDefinition).download('<?php echo strtoAZaz09(html_entity_decode("$invoice_date-$company_name-Invoice-$invoice_prefix$invoice_number")); ?>');"><i class="fa fa-fw fa-download mr-2"></i>Download</a>
<?php
if ($invoice_status !== "Paid" && $invoice_status !== "Cancelled" && $invoice_status !== "Draft" && $config_stripe_enable == 1) { ?>
<a class="btn btn-success" href="guest_pay_invoice_stripe.php?invoice_id=<?php echo $invoice_id; ?>&url_key=<?php echo $url_key; ?>"><i class="fa fa-fw fa-credit-card mr-2"></i>Pay Now </a>
<?php } ?>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="row mb-4">
<div class="col-2">
<img class="img-fluid" src="<?php echo "../uploads/settings/$company_logo"; ?>">
</div>
<div class="col-10">
<?php if ($invoice_status == "Paid") { ?>
<div class="ribbon-wrapper">
<div class="ribbon bg-success">
<?php echo $invoice_status; ?>
</div>
</div>
<?php } ?>
<?php if ($invoice_status == "Cancelled") { ?>
<div class="ribbon-wrapper">
<div class="ribbon bg-danger">
<?php echo $invoice_status; ?>
</div>
</div>
<?php } ?>
<h3 class="text-right mt-5"><strong>Invoice</strong><br><small class="text-secondary"><?php echo "$invoice_prefix$invoice_number"; ?></small></h3>
</div>
</div>
<div class="row mb-4">
<div class="col">
<ul class="list-unstyled">
<li><h4><strong><?php echo $company_name; ?></strong></h4></li>
<li><?php echo $company_address; ?></li>
<li><?php echo "$company_city $company_state $company_zip"; ?></li>
<li><?php echo $company_phone; ?></li>
<li><?php echo $company_email; ?></li>
</ul>
</div>
<div class="col">
<ul class="list-unstyled text-right">
<li><h4><strong><?php echo $client_name; ?></strong></h4></li>
<li><?php echo $location_address; ?></li>
<li><?php echo "$location_city $location_state $location_zip"; ?></li>
<li><?php echo "$contact_phone $contact_extension"; ?></li>
<li><?php echo $contact_mobile; ?></li>
<li><?php echo $contact_email; ?></li>
</ul>
</div>
</div>
<div class="row mb-4">
<div class="col-sm-8">
</div>
<div class="col-sm-4">
<table class="table">
<tr>
<td>Date</td>
<td class="text-right"><?php echo $invoice_date; ?></td>
</tr>
<tr class="text-bold">
<td>Due</td>
<td class="text-right"><?php echo $invoice_due; ?></td>
</tr>
</table>
</div>
</div>
<div class="row mb-4">
<div class="col-md-12">
<div class="card">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Product</th>
<th>Description</th>
<th class="text-center">Qty</th>
<th class="text-right">Price</th>
<th class="text-right">Tax</th>
<th class="text-right">Total</th>
</tr>
</thead>
<tbody>
<?php
$total_tax = 0.00;
$sub_total = 0.00 - $invoice_discount;
while ($row = mysqli_fetch_array($sql_invoice_items)) {
$item_id = intval($row['item_id']);
$item_name = nullable_htmlentities($row['item_name']);
$item_description = nullable_htmlentities($row['item_description']);
$item_quantity = floatval($row['item_quantity']);
$item_price = floatval($row['item_price']);
$item_tax = floatval($row['item_tax']);
$item_total = floatval($row['item_total']);
$total_tax = $item_tax + $total_tax;
$sub_total = $item_price * $item_quantity + $sub_total;
?>
<tr>
<td><?php echo $item_name; ?></td>
<td><?php echo nl2br($item_description); ?></td>
<td class="text-center"><?php echo $item_quantity; ?></td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $item_price, $invoice_currency_code); ?></td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $item_tax, $invoice_currency_code); ?></td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $item_total, $invoice_currency_code); ?></td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-sm-7">
<?php if (!empty($invoice_note)) { ?>
<div class="card">
<div class="card-body">
<?php echo nl2br($invoice_note); ?>
</div>
</div>
<?php } ?>
</div>
<div class="col-sm-3 offset-sm-2">
<table class="table table-borderless">
<tbody>
<tr class="border-bottom">
<td>Subtotal</td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $sub_total, $invoice_currency_code); ?></td>
</tr>
<?php
if ($invoice_discount > 0) {
?>
<tr class="border-bottom">
<td>Discount</td>
<td class="text-right">-<?php echo numfmt_format_currency($currency_format, $invoice_discount, $invoice_currency_code); ?></td>
</tr>
<?php
}
?>
<?php if ($total_tax > 0) { ?>
<tr class="border-bottom">
<td>Tax</td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $total_tax, $invoice_currency_code); ?></td>
</tr>
<?php } ?>
<tr class="border-bottom">
<td>Total</td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $invoice_amount, $invoice_currency_code); ?></td>
</tr>
<?php if ($amount_paid > 0) { ?>
<tr class="border-bottom">
<td><div class="text-success">Paid</div></td>
<td class="text-right text-success"><?php echo numfmt_format_currency($currency_format, $amount_paid, $invoice_currency_code); ?></td>
</tr>
<?php
}
?>
<tr class="border-bottom">
<td><strong>Balance</strong></td>
<td class="text-right"><strong><?php echo numfmt_format_currency($currency_format, $balance, $invoice_currency_code); ?></strong></td>
</tr>
</tbody>
</table>
</div>
</div>
<hr class="mt-5">
<div class="text-center"><?php echo nl2br($config_invoice_footer); ?></div>
</div>
</div>
<script src='../plugins/pdfmake/pdfmake.min.js'></script>
<script src='../plugins/pdfmake/vfs_fonts.js'></script>
<script>
var docDefinition = {
info: {
title: <?php echo json_encode(html_entity_decode($company_name) . "- Invoice") ?>,
author: <?php echo json_encode(html_entity_decode($company_name)) ?>
},
//watermark: {text: '<?php echo $invoice_status; ?>', color: 'lightgrey', opacity: 0.3, bold: true, italics: false},
content: [
// Header
{
columns: [
<?php if (!empty($company_logo_base64)) { ?>
{
image: <?php echo json_encode("data:image;base64,$company_logo_base64") ?>,
width: 120
},
<?php } ?>
[
{
text: 'Invoice',
style: 'invoiceTitle',
width: '*'
},
{
text: <?php echo json_encode(html_entity_decode("$invoice_prefix$invoice_number")) ?>,
style: 'invoiceNumber',
width: '*'
},
],
],
},
// Billing Headers
{
columns: [
{
text: <?php echo json_encode(html_entity_decode($company_name)) ?>,
style: 'invoiceBillingTitle',
},
{
text: <?php echo json_encode(html_entity_decode($client_name)) ?>,
style: 'invoiceBillingTitleClient',
},
]
},
// Billing Address
{
columns: [
{
text: <?php echo json_encode(html_entity_decode("$company_address \n $company_city $company_state $company_zip \n $company_phone \n $company_website")) ?>,
style: 'invoiceBillingAddress'
},
{
text: <?php echo json_encode(html_entity_decode("$location_address \n $location_city $location_state $location_zip \n $contact_email \n $contact_phone")) ?>,
style: 'invoiceBillingAddressClient'
},
]
},
//Invoice Dates Table
{
table: {
// headers are automatically repeated if the table spans over multiple pages
// you can declare how many rows should be treated as headers
headerRows: 0,
widths: [ '*',80, 80 ],
body: [
// Total
[
{
text: '',
rowSpan: 3
},
{},
{},
],
[
{},
{
text: 'Date',
style: 'invoiceDateTitle'
},
{
text: <?php echo json_encode(html_entity_decode($invoice_date)) ?>,
style: 'invoiceDateValue'
},
],
[
{},
{
text: 'Due',
style: 'invoiceDueDateTitle'
},
{
text: <?php echo json_encode(html_entity_decode($invoice_due)) ?>,
style: 'invoiceDueDateValue'
},
],
]
}, // table
layout: 'lightHorizontalLines'
},
// Line breaks
'\n\n',
// Items
{
table: {
// headers are automatically repeated if the table spans over multiple pages
// you can declare how many rows should be treated as headers
headerRows: 1,
widths: [ '*', 40, 'auto', 'auto', 80 ],
body: [
// Table Header
[
{
text: 'Product',
style: [ 'itemsHeader', 'left']
},
{
text: 'Qty',
style: [ 'itemsHeader', 'center']
},
{
text: 'Price',
style: [ 'itemsHeader', 'right']
},
{
text: 'Tax',
style: [ 'itemsHeader', 'right']
},
{
text: 'Total',
style: [ 'itemsHeader', 'right']
}
],
// Items
<?php
$total_tax = 0;
$sub_total = 0;
$sql_invoice_items = mysqli_query($mysqli, "SELECT * FROM invoice_items WHERE item_invoice_id = $invoice_id ORDER BY item_order ASC");
while ($row = mysqli_fetch_array($sql_invoice_items)) {
$item_name = $row['item_name'];
$item_description = $row['item_description'];
$item_quantity = $row['item_quantity'];
$item_price = $row['item_price'];
$item_subtotal = $row['item_price'];
$item_tax = $row['item_tax'];
$item_total = $row['item_total'];
$tax_id = $row['item_tax_id'];
$total_tax = $item_tax + $total_tax;
$sub_total = $item_price * $item_quantity + $sub_total;
?>
// Item
[
[
{
text: <?php echo json_encode($item_name) ?>,
style:'itemTitle'
},
{
text: <?php echo json_encode($item_description) ?>,
style:'itemDescription'
}
],
{
text: <?php echo json_encode($item_quantity) ?>,
style: 'itemQty'
},
{
text: <?php echo json_encode(numfmt_format_currency($currency_format, $item_price, $invoice_currency_code)) ?>,
style: 'itemNumber'
},
{
text: <?php echo json_encode(numfmt_format_currency($currency_format, $item_tax, $invoice_currency_code)) ?>,
style: 'itemNumber'
},
{
text: <?php echo json_encode(numfmt_format_currency($currency_format, $item_total, $invoice_currency_code)) ?>,
style: 'itemNumber'
}
],
<?php
}
?>
// END Items
]
}, // table
layout: 'lightHorizontalLines'
},
// TOTAL
{
table: {
// headers are automatically repeated if the table spans over multiple pages
// you can declare how many rows should be treated as headers
headerRows: 0,
widths: [ '*','auto', 80 ],
body: [
// Total
[
{
text: 'Notes',
style: 'notesTitle'
},
{},
{}
],
[
{
rowSpan: '*',
text: <?php echo json_encode(html_entity_decode($invoice_note)) ?>,
style: 'notesText'
},
{
text: 'Subtotal',
style: 'itemsFooterSubTitle'
},
{
text: <?php echo json_encode(numfmt_format_currency($currency_format, $sub_total, $invoice_currency_code)) ?>,
style: 'itemsFooterSubValue'
}
],
<?php if ($invoice_discount > 0) { ?>
[
{},
{
text: 'Discount',
style: 'itemsFooterSubTitle'
},
{
text: <?php echo json_encode(numfmt_format_currency($currency_format, -$invoice_discount, $invoice_currency_code)) ?>,
style: 'itemsFooterSubValue'
}
],
<?php } ?>
<?php if ($total_tax > 0) { ?>
[
{},
{
text: 'Tax',
style: 'itemsFooterSubTitle'
},
{
text: <?php echo json_encode(numfmt_format_currency($currency_format, $total_tax, $invoice_currency_code)) ?>,
style: 'itemsFooterSubValue'
}
],
<?php } ?>
[
{},
{
text: 'Total',
style: 'itemsFooterSubTitle'
},
{
text: <?php echo json_encode(numfmt_format_currency($currency_format, $invoice_amount, $invoice_currency_code)) ?>,
style: 'itemsFooterSubValue'
}
],
<?php if ($amount_paid > 0) { ?>
[
{},
{
text: 'Paid',
style: 'itemsFooterSubTitle'
},
{
text: <?php echo json_encode(numfmt_format_currency($currency_format, $amount_paid, $invoice_currency_code)) ?>,
style: 'itemsFooterSubValue'
}
],
<?php } ?>
[
{},
{
text: 'Balance',
style: 'itemsFooterTotalTitle'
},
{
text: <?php echo json_encode(numfmt_format_currency($currency_format, $balance, $invoice_currency_code)) ?>,
style: 'itemsFooterTotalTitle'
}
],
]
}, // table
layout: 'lightHorizontalLines'
},
// TERMS / FOOTER
{
text: <?php echo json_encode(html_entity_decode($config_invoice_footer)) ?>,
style: 'documentFooterCenter'
}
], //End Content,
styles: {
// Document Footer
documentFooterCenter: {
fontSize: 9,
margin: [10,50,10,10],
alignment: 'center'
},
// Invoice Title
invoiceTitle: {
fontSize: 18,
bold: true,
alignment: 'right',
margin: [0,0,0,3]
},
// Invoice Number
invoiceNumber: {
fontSize: 14,
alignment: 'right'
},
// Billing Headers
invoiceBillingTitle: {
fontSize: 14,
bold: true,
alignment: 'left',
margin: [0,20,0,5]
},
invoiceBillingTitleClient: {
fontSize: 14,
bold: true,
alignment: 'right',
margin: [0,20,0,5]
},
// Billing Details
invoiceBillingAddress: {
fontSize: 10,
lineHeight: 1.2
},
invoiceBillingAddressClient: {
fontSize: 10,
lineHeight: 1.2,
alignment: 'right',
margin: [0,0,0,30]
},
// Invoice Date
invoiceDateTitle: {
fontSize: 10,
alignment: 'left',
margin: [0,5,0,5]
},
invoiceDateValue: {
fontSize: 10,
alignment: 'right',
margin: [0,5,0,5]
},
// Invoice Due Date
invoiceDueDateTitle: {
fontSize: 10,
bold: true,
alignment: 'left',
margin: [0,5,0,5]
},
invoiceDueDateValue: {
fontSize: 10,
bold: true,
alignment: 'right',
margin: [0,5,0,5]
},
// Items Header
itemsHeader: {
fontSize: 10,
margin: [0,5,0,5],
bold: true,
alignment: 'right'
},
// Item Title
itemTitle: {
fontSize: 10,
bold: true,
margin: [0,5,0,3]
},
itemDescription: {
italics: true,
fontSize: 9,
lineHeight: 1.1,
margin: [0,3,0,5]
},
itemQty: {
fontSize: 10,
margin: [0,5,0,5],
alignment: 'center'
},
itemNumber: {
fontSize: 10,
margin: [0,5,0,5],
alignment: 'right'
},
itemTotal: {
fontSize: 10,
margin: [0,5,0,5],
bold: true,
alignment: 'right'
},
// Items Footer (Subtotal, Total, Tax, etc)
itemsFooterSubTitle: {
fontSize: 10,
margin: [0,5,0,5],
alignment: 'right'
},
itemsFooterSubValue: {
fontSize: 10,
margin: [0,5,0,5],
bold: false,
alignment: 'right'
},
itemsFooterTotalTitle: {
fontSize: 10,
margin: [0,5,0,5],
bold: true,
alignment: 'right'
},
itemsFooterTotalValue: {
fontSize: 10,
margin: [0,5,0,5],
bold: true,
alignment: 'right'
},
notesTitle: {
fontSize: 10,
bold: true,
margin: [0,5,0,5]
},
notesText: {
fontSize: 9,
margin: [0,5,50,5]
},
left: {
alignment: 'left'
},
center: {
alignment: 'center'
},
},
defaultStyle: {
columnGap: 20
}
}
</script>
<?php
// CURRENT INVOICES
$sql_current_invoices = mysqli_query($mysqli, "SELECT * FROM invoices WHERE invoice_client_id = $client_id AND invoice_due > CURDATE() AND(invoice_status = 'Sent' OR invoice_status = 'Viewed' OR invoice_status = 'Partial') ORDER BY invoice_number DESC");
$current_invoices_count = mysqli_num_rows($sql_current_invoices);
if ($current_invoices_count > 0) { ?>
<div class="card d-print-none card-dark">
<div class="card-header">
<strong><i class="fas fa-fw fa-clock mr-2"></i><b><?php echo $current_invoices_count; ?></b> Current Invoices</strong>
</div>
<div card="card-body">
<table class="table table-sm">
<thead>
<tr>
<th class="text-center">Invoice</th>
<th>Date</th>
<th>Due</th>
<th class="text-right">Amount</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql_current_invoices)) {
$invoice_id = intval($row['invoice_id']);
$invoice_prefix = nullable_htmlentities($row['invoice_prefix']);
$invoice_number = intval($row['invoice_number']);
$invoice_date = nullable_htmlentities($row['invoice_date']);
$invoice_due = nullable_htmlentities($row['invoice_due']);
$invoice_amount = floatval($row['invoice_amount']);
$invoice_currency_code = nullable_htmlentities($row['invoice_currency_code']);
$invoice_url_key = nullable_htmlentities($row['invoice_url_key']);
$invoice_tally_total = $invoice_amount + $invoice_tally_total;
$difference = strtotime($invoice_due) - time();
$days = floor($difference / (60*60*24));
?>
<tr <?php if ($_GET['invoice_id'] == $invoice_id) { echo "class='table-primary'"; } ?>>
<th class="text-center"><a href="guest_view_invoice.php?invoice_id=<?php echo $invoice_id; ?>&url_key=<?php echo $invoice_url_key; ?>"><?php echo "$invoice_prefix$invoice_number"; ?></a></th>
<td><?php echo $invoice_date; ?></td>
<td><?php echo $invoice_due; ?> (Due in <?php echo $days; ?> Days)</td>
<td class="text-right text-bold"><?php echo numfmt_format_currency($currency_format, $invoice_amount, $invoice_currency_code); ?></td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
<?php
}
?>
<?php
// OUTSTANDING INVOICES
$sql_outstanding_invoices = mysqli_query($mysqli, "SELECT * FROM invoices WHERE invoice_client_id = $client_id AND invoice_due < CURDATE() AND(invoice_status = 'Sent' OR invoice_status = 'Viewed' OR invoice_status = 'Partial') ORDER BY invoice_date DESC");
$outstanding_invoices_count = mysqli_num_rows($sql_outstanding_invoices);
if ($outstanding_invoices_count > 0) { ?>
<div class="card d-print-none card-danger">
<div class="card-header">
<strong><i class="fa fa-fw fa-exclamation-triangle mr-2"></i><b><?php echo $outstanding_invoices_count; ?></b> Outstanding Invoices</strong>
</div>
<div card="card-body">
<table class="table table-sm">
<thead>
<tr>
<th class="text-center">Invoice</th>
<th>Date</th>
<th>Due</th>
<th class="text-right">Amount</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql_outstanding_invoices)) {
$invoice_id = intval($row['invoice_id']);
$invoice_prefix = nullable_htmlentities($row['invoice_prefix']);
$invoice_number = intval($row['invoice_number']);
$invoice_date = nullable_htmlentities($row['invoice_date']);
$invoice_due = nullable_htmlentities($row['invoice_due']);
$invoice_amount = floatval($row['invoice_amount']);
$invoice_currency_code = nullable_htmlentities($row['invoice_currency_code']);
$invoice_url_key = nullable_htmlentities($row['invoice_url_key']);
$invoice_tally_total = $invoice_amount + $invoice_tally_total;
$difference = time() - strtotime($invoice_due);
$days = floor($difference / (60*60*24));
?>
<tr <?php if ($_GET['invoice_id'] == $invoice_id) { echo "class='table-primary'"; } ?>>
<th class="text-center"><a href="guest_view_invoice.php?invoice_id=<?php echo $invoice_id; ?>&url_key=<?php echo $invoice_url_key; ?>"><?php echo "$invoice_prefix$invoice_number"; ?></a></th>
<td><?php echo $invoice_date; ?></td>
<td class="text-danger"><?php echo $invoice_due; ?> (Over Due by <?php echo $days; ?> Days)</td>
<td class="text-right text-bold"><?php echo numfmt_format_currency($currency_format, $invoice_amount, $invoice_currency_code); ?></td>
</tr>
<?php
}
?>
</tbody>
</table>
</div>
</div>
<?php } // End previous unpaid invoices
require_once "guest_footer.php";

277
guest/guest_view_item.php Normal file
View File

@@ -0,0 +1,277 @@
<?php
header('Expires: Sun, 01 Jan 2014 00:00:00 GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
require_once "guest_header.php";
//Initialize the HTML Purifier to prevent XSS
require "../plugins/htmlpurifier/HTMLPurifier.standalone.php";
$purifier_config = HTMLPurifier_Config::createDefault();
$purifier_config->set('URI.AllowedSchemes', ['data' => true, 'src' => true, 'http' => true, 'https' => true]);
$purifier = new HTMLPurifier($purifier_config);
$sql = mysqli_query($mysqli, "SELECT * FROM companies, settings WHERE companies.company_id = settings.company_id AND companies.company_id = 1");
$row = mysqli_fetch_array($sql);
$company_name = nullable_htmlentities($row['company_name']);
$company_address = nullable_htmlentities($row['company_address']);
$company_city = nullable_htmlentities($row['company_city']);
$company_state = nullable_htmlentities($row['company_state']);
$company_zip = nullable_htmlentities($row['company_zip']);
$company_phone = formatPhoneNumber($row['company_phone']);
$company_email = nullable_htmlentities($row['company_email']);
$company_website = nullable_htmlentities($row['company_website']);
$company_logo = nullable_htmlentities($row['company_logo']);
$company_locale = nullable_htmlentities($row['company_locale']);
$config_invoice_footer = nullable_htmlentities($row['config_invoice_footer']);
//Set Currency Format
$currency_format = numfmt_create($company_locale, NumberFormatter::CURRENCY);
?>
<?php
if (!isset($_GET['id']) || !isset($_GET['key'])) {
echo "<div class='alert alert-danger'>Incorrect URL.</div>";
include "guest_footer.php";
exit();
}
$item_id = intval($_GET['id']);
$item_key = sanitizeInput($_GET['key']);
$sql = mysqli_query($mysqli, "SELECT * FROM shared_items WHERE item_id = $item_id AND item_key = '$item_key' AND item_expire_at > NOW() LIMIT 1");
$row = mysqli_fetch_array($sql);
// Check we got a result
if (mysqli_num_rows($sql) !== 1 || !$row) {
echo "<div class='alert alert-danger' >No item to view. Check with the person that sent you this link to ensure it is correct and has not expired.</div>";
include "guest_footer.php";
exit();
}
// Check item share is active & hasn't been viewed too many times but allow 0 views as that is consider infinite views
if ($row['item_active'] !== "1" || ($row['item_view_limit'] > 0 && $row['item_views'] >= $row['item_view_limit'])) {
echo "<div class='alert alert-danger'>Item cannot be viewed at this time. Check with the person that sent you this link to ensure it is correct and has not expired.</div>";
include "guest_footer.php";
exit();
}
// If we got here, we have valid information
$item_type = nullable_htmlentities($row['item_type']);
$item_related_id = intval($row['item_related_id']);
$item_encrypted_credential = nullable_htmlentities($row['item_encrypted_credential']);
$item_note = nullable_htmlentities($row['item_note']);
$item_recipient = nullable_htmlentities($row['item_recipient']);
$item_views = intval($row['item_views']);
$item_view_limit = intval($row['item_view_limit']);
$item_created = nullable_htmlentities($row['item_created_at']);
$item_expire = date('Y-m-d h:i A', strtotime($row['item_expire_at']));
$client_id = intval($row['item_client_id']);
// Create in-app notification
$item_type_sql_escaped = sanitizeInput($row['item_type']);
$item_recipient_sql_escaped = sanitizeInput($row['item_recipient']);
appNotify("Share Viewed", "$item_type_sql_escaped has been viewed by $item_recipient_sql_escaped", "client_overview.php?client_id=$client_id", $client_id);
?>
<?php
if (!empty($company_logo)) { ?>
<img alt="<?=nullable_htmlentities($company_name)?> logo" height="40" width="80" class="img-fluid" src="<?php echo "../uploads/settings/$company_logo"; ?>">
<?php
} else {
echo "<h3>$company_name</h3>";
}
?>
<div class="card mt-2">
<div class="card-header bg-dark">
<div class="card-title">
<h6><small>Secure link intended for:</small><br><strong><?php echo $item_recipient ?></strong></h6>
</div>
<div class="card-tools">
<div>
<?php echo "Viewed: <strong>$item_views</strong> Times"; ?>
</div>
<div>
<?php echo "Expires: <strong>$item_expire</strong>"; ?>
</div>
</div>
</div>
<div class="card-body">
<?php
if ($item_type == "Document") {
$doc_sql = mysqli_query($mysqli, "SELECT * FROM documents WHERE document_id = $item_related_id AND document_client_id = $client_id LIMIT 1");
$doc_row = mysqli_fetch_array($doc_sql);
if (mysqli_num_rows($doc_sql) !== 1 || !$doc_row) {
echo "<div class='alert alert-danger'>Error retrieving document to view.</div>";
require_once "guest_footer.php";
exit();
}
$doc_title = nullable_htmlentities($doc_row['document_name']);
$doc_title_escaped = sanitizeInput($doc_row['document_name']);
$doc_content = $purifier->purify($doc_row['document_content']);
echo "<h3>$doc_title</h3>";
echo $doc_content;
// Update document view count
$new_item_views = $item_views + 1;
mysqli_query($mysqli, "UPDATE shared_items SET item_views = $new_item_views WHERE item_id = $item_id");
// Logging
$name = mysqli_real_escape_string($mysqli, $doc_title);
logAction("Share", "View", "Viewed shared $item_type $doc_title_escaped via link", $client_id);
} elseif ($item_type == "File") {
$file_sql = mysqli_query($mysqli, "SELECT * FROM files WHERE file_id = $item_related_id AND file_client_id = $client_id LIMIT 1");
$file_row = mysqli_fetch_array($file_sql);
if (mysqli_num_rows($file_sql) !== 1 || !$file_row) {
echo "<div class='alert alert-danger'>Error retrieving file.</div>";
include "guest_footer.php";
exit();
}
$file_name = nullable_htmlentities($file_row['file_name']);
echo "<h3>A file has been shared with you</h3>";
if (!empty($item_note)) {
echo "<p class='lead'>Note: <i>$item_note</i></p>";
}
echo "<a href='guest_download_file.php?id=$item_id&key=$item_key'>Download $file_name</a>";
} elseif ($item_type == "Login") {
$encryption_key = $_GET['ek'];
$login_sql = mysqli_query($mysqli, "SELECT * FROM logins WHERE login_id = $item_related_id AND login_client_id = $client_id LIMIT 1");
$login_row = mysqli_fetch_array($login_sql);
if (mysqli_num_rows($login_sql) !== 1 || !$login_row) {
echo "<div class='alert alert-danger'>Error retrieving login.</div>";
include "guest_footer.php";
exit();
}
$login_id = intval($login_row['login_id']);
$login_name = nullable_htmlentities($login_row['login_name']);
$login_uri = nullable_htmlentities($login_row['login_uri']);
$username_iv = substr($row['item_encrypted_username'], 0, 16);
$username_ciphertext = substr($row['item_encrypted_username'], 16);
$login_username = nullable_htmlentities(openssl_decrypt($username_ciphertext, 'aes-128-cbc', $encryption_key, 0, $username_iv));
$password_iv = substr($row['item_encrypted_credential'], 0, 16);
$password_ciphertext = substr($row['item_encrypted_credential'], 16);
$login_password = nullable_htmlentities(openssl_decrypt($password_ciphertext, 'aes-128-cbc', $encryption_key, 0, $password_iv));
$login_otp = nullable_htmlentities($login_row['login_otp_secret']);
$login_otp_secret = nullable_htmlentities($login_row['login_otp_secret']);
$login_id_with_secret = '"' . $login_row['login_id'] . '","' . $login_row['login_otp_secret'] . '"';
if (empty($login_otp_secret)) {
$otp_display = "-";
} else {
$otp_display = "<span onmouseenter='showOTP($login_id_with_secret)'><i class='far fa-clock'></i> <span id='otp_$login_id'><i>Hover..</i></span></span>";
}
$login_notes = nullable_htmlentities($login_row['login_note']);
?>
<h5><?php echo $login_name; ?></h5>
<table class="table col-md-3">
<tr>
<th>URL</th>
<td><?php echo $login_uri; ?></td>
</tr>
<tr>
<th>Username</th>
<td><?php echo $login_username ?></td>
</tr>
<tr>
<th>Password</th>
<td><?php echo $login_password ?></td>
</tr>
<?php if(!empty($login_otp_secret)){ ?>
<tr>
<th>2FA (TOTP)</th>
<td><?php echo $otp_display ?></td>
</tr>
<?php } ?>
</table>
<script>
function showOTP(id, secret) {
//Send a GET request to ajax.php as guest_ajax.php?get_totp_token=true&totp_secret=SECRET
jQuery.get(
"guest_ajax.php",
{get_totp_token: 'true', totp_secret: secret},
function(data) {
//If we get a response from post.php, parse it as JSON
const token = JSON.parse(data);
document.getElementById("otp_" + id).innerText = token
}
);
}
function generatePassword() {
document.getElementById("password").value = "<?php echo randomString(); ?>"
}
</script>
<?php
// Update login view count
$new_item_views = $item_views + 1;
mysqli_query($mysqli, "UPDATE shared_items SET item_views = $new_item_views WHERE item_id = $item_id");
// Logging
$name = sanitizeInput($login_row['login_name']);
logAction("Share", "View", "Viewed shared $item_type $name via link", $client_id);
}
?>
<hr>
<em>
This message and any attachments are confidential and intended for the specified recipient(s) only. If you are not the intended recipient, please notify us immediately with the contact info below. Unauthorized use, disclosure, or distribution is prohibited.
</em>
</div>
<div class="card-footer">
<?php echo "<i class='fas fa-phone fa-fw mr-2'></i>$company_phone | <i class='fas fa-globe fa-fw mr-2 ml-2'></i>$company_website"; ?>
</div>
<?php
require_once "guest_footer.php";
?>

716
guest/guest_view_quote.php Normal file
View File

@@ -0,0 +1,716 @@
<?php
require_once "guest_header.php";
if (!isset($_GET['quote_id'], $_GET['url_key'])) {
echo "<br><h2>Oops, something went wrong! Please raise a ticket if you believe this is an error.</h2>";
require_once "guest_footer.php";
exit();
}
$url_key = sanitizeInput($_GET['url_key']);
$quote_id = intval($_GET['quote_id']);
$sql = mysqli_query(
$mysqli,
"SELECT * FROM quotes
LEFT JOIN clients ON quote_client_id = client_id
LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1
LEFT JOIN locations ON clients.client_id = locations.location_client_id AND location_primary = 1
WHERE quote_id = $quote_id
AND quote_url_key = '$url_key'"
);
if (mysqli_num_rows($sql) !== 1) {
// Invalid quote/key
echo "<br><h2>Oops, something went wrong! Please raise a ticket if you believe this is an error.</h2>";
require_once "guest_footer.php";
exit();
}
$row = mysqli_fetch_array($sql);
$quote_id = intval($row['quote_id']);
$quote_prefix = nullable_htmlentities($row['quote_prefix']);
$quote_number = intval($row['quote_number']);
$quote_status = nullable_htmlentities($row['quote_status']);
$quote_date = nullable_htmlentities($row['quote_date']);
$quote_expire = nullable_htmlentities($row['quote_expire']);
$quote_discount = floatval($row['quote_discount_amount']);
$quote_amount = floatval($row['quote_amount']);
$quote_currency_code = nullable_htmlentities($row['quote_currency_code']);
$quote_note = nullable_htmlentities($row['quote_note']);
$client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']);
$client_name_escaped = sanitizeInput($row['client_name']);
$location_address = nullable_htmlentities($row['location_address']);
$location_city = nullable_htmlentities($row['location_city']);
$location_state = nullable_htmlentities($row['location_state']);
$location_zip = nullable_htmlentities($row['location_zip']);
$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']);
$client_website = nullable_htmlentities($row['client_website']);
$client_currency_code = nullable_htmlentities($row['client_currency_code']);
$client_net_terms = intval($row['client_net_terms']);
if ($client_net_terms == 0) {
$client_net_terms = intval($row['config_default_net_terms']);
}
$sql = mysqli_query($mysqli, "SELECT * FROM companies, settings WHERE companies.company_id = settings.company_id AND companies.company_id = 1");
$row = mysqli_fetch_array($sql);
$company_name = nullable_htmlentities($row['company_name']);
$company_address = nullable_htmlentities($row['company_address']);
$company_city = nullable_htmlentities($row['company_city']);
$company_state = nullable_htmlentities($row['company_state']);
$company_zip = nullable_htmlentities($row['company_zip']);
$company_phone = formatPhoneNumber($row['company_phone']);
$company_email = nullable_htmlentities($row['company_email']);
$company_website = nullable_htmlentities($row['company_website']);
$company_logo = nullable_htmlentities($row['company_logo']);
if (!empty($company_logo)) {
$company_logo_base64 = base64_encode(file_get_contents("../uploads/settings/$company_logo"));
}
$company_locale = nullable_htmlentities($row['company_locale']);
$config_quote_footer = nullable_htmlentities($row['config_quote_footer']);
//Set Currency Format
$currency_format = numfmt_create($company_locale, NumberFormatter::CURRENCY);
//Update status to Viewed only if invoice_status = "Sent"
if ($quote_status == 'Sent') {
mysqli_query($mysqli, "UPDATE quotes SET quote_status = 'Viewed' WHERE quote_id = $quote_id");
}
//Mark viewed in history
mysqli_query($mysqli, "INSERT INTO history SET history_status = '$quote_status', history_description = 'Quote viewed - $ip - $os - $browser', history_quote_id = $quote_id");
if ($quote_status == "Draft" || $quote_status == "Sent" || $quote_status == "Viewed") {
appNotify("Quote Viewed", "Quote $quote_prefix$quote_number has been viewed by $client_name_escaped - $ip - $os - $browser", "quote.php?quote_id=$quote_id", $client_id);
}
?>
<div class="card">
<div class="card-header d-print-none">
<div class="float-right">
<a class="btn btn-primary" href="#" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</a>
<a class="btn btn-primary" href="#" onclick="pdfMake.createPdf(docDefinition).download('<?php echo strtoAZaz09(html_entity_decode("$quote_date-$company_name-QUOTE-$quote_prefix$quote_number")); ?>');">
<i class="fa fa-fw fa-download mr-2"></i>Download
</a>
</div>
</div>
<div class="card-body">
<div class="row mb-4">
<div class="col-sm-2">
<img class="img-fluid" src="<?php echo "../uploads/settings/$company_logo"; ?>">
</div>
<div class="col-sm-10">
<?php if ($quote_status == "Accepted" || $quote_status == "Declined") { ?>
<div class="ribbon-wrapper">
<div class="ribbon bg-success <?php if ($quote_status == 'Declined') { echo 'bg-danger'; } ?>">
<?php echo $quote_status; ?>
</div>
</div>
<?php } ?>
<h3 class="text-right mt-5"><strong>Quote</strong><br><small class="text-secondary"><?php echo "$quote_prefix$quote_number"; ?></small></h3>
</div>
</div>
<div class="row mb-4">
<div class="col-sm">
<ul class="list-unstyled">
<li><h4><strong><?php echo $company_name; ?></strong></h4></li>
<li><?php echo $company_address; ?></li>
<li><?php echo "$company_city $company_state $company_zip"; ?></li>
<li><?php echo $company_phone; ?></li>
<li><?php echo $company_email; ?></li>
</ul>
</div>
<div class="col-sm">
<ul class="list-unstyled text-right">
<li><h4><strong><?php echo $client_name; ?></strong></h4></li>
<li><?php echo $location_address; ?></li>
<li><?php echo "$location_city $location_state $location_zip"; ?></li>
<li><?php echo "$contact_phone $contact_extension"; ?></li>
<li><?php echo $contact_mobile; ?></li>
<li><?php echo $contact_email; ?></li>
</ul>
</div>
</div>
<div class="row mb-4">
<div class="col-sm-8">
</div>
<div class="col-sm-4">
<table class="table">
<tr>
<td>Date</td>
<td class="text-right"><?php echo $quote_date; ?></td>
</tr>
<tr class="text-bold">
<td>Expire</td>
<td class="text-right"><?php echo $quote_expire; ?></td>
</tr>
</table>
</div>
</div>
<?php $sql_items = mysqli_query($mysqli, "SELECT * FROM invoice_items WHERE item_quote_id = $quote_id ORDER BY item_order ASC"); ?>
<div class="row mb-4">
<div class="col-md-12">
<div class="card">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Product</th>
<th>Description</th>
<th class="text-center">Qty</th>
<th class="text-right">Price</th>
<th class="text-right">Tax</th>
<th class="text-right">Total</th>
</tr>
</thead>
<tbody>
<?php
$total_tax = $sub_total = 0; // Default 0
while ($row = mysqli_fetch_array($sql_items)) {
$item_id = intval($row['item_id']);
$item_name = nullable_htmlentities($row['item_name']);
$item_description = nullable_htmlentities($row['item_description']);
$item_quantity = floatval($row['item_quantity']);
$item_price = floatval($row['item_price']);
$item_tax = floatval($row['item_tax']);
$item_total = floatval($row['item_total']);
$total_tax = $item_tax + $total_tax;
$sub_total = $item_price * $item_quantity + $sub_total;
?>
<tr>
<td><?php echo $item_name; ?></td>
<td><?php echo nl2br($item_description); ?></td>
<td class="text-center"><?php echo $item_quantity; ?></td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $item_price, $quote_currency_code); ?></td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $item_tax, $quote_currency_code); ?></td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $item_total, $quote_currency_code); ?></td>
</tr>
<?php
}
?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-sm-7">
<?php if (!empty($quote_note)) { ?>
<div class="card">
<div class="card-body">
<?php echo nl2br($quote_note); ?>
</div>
</div>
<?php } ?>
</div>
<div class="col-sm-3 offset-sm-2">
<table class="table table-borderless">
<tbody>
<tr class="border-bottom">
<td>Subtotal</td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $sub_total, $quote_currency_code); ?></td>
</tr>
<?php if ($quote_discount > 0) { ?>
<tr class="border-bottom">
<td>Discount</td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, -$quote_discount, $quote_currency_code); ?></td>
</tr>
<?php } ?>
<?php if ($total_tax > 0) { ?>
<tr class="border-bottom">
<td>Tax</td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $total_tax, $quote_currency_code); ?></td>
</tr>
<?php } ?>
<tr class="border-bottom">
<td><strong>Total</strong></td>
<td class="text-right"><strong><?php echo numfmt_format_currency($currency_format, $quote_amount, $quote_currency_code); ?></strong></td>
</tr>
</tbody>
</table>
</div>
</div>
<hr class="mt-5">
<div class="text-center"><?php echo nl2br($config_quote_footer); ?></div>
<div class="">
<?php
if ($quote_status == "Sent" || $quote_status == "Viewed" && strtotime($quote_expire) > strtotime("now")) {
?>
<a class="btn btn-success confirm-link" href="guest_post.php?accept_quote=<?php echo $quote_id; ?>&url_key=<?php echo $url_key; ?>">
<i class="fas fa-fw fa-thumbs-up mr-2"></i>Accept
</a>
<a class="btn btn-danger confirm-link" href="guest_post.php?decline_quote=<?php echo $quote_id; ?>&url_key=<?php echo $url_key; ?>">
<i class="fas fa-fw fa-thumbs-down mr-2"></i>Decline
</a>
<?php } ?>
</div>
</div>
</div>
<script src='../plugins/pdfmake/pdfmake.min.js'></script>
<script src='../plugins/pdfmake/vfs_fonts.js'></script>
<script>
var docDefinition = {
info: {
title: <?php echo json_encode(html_entity_decode($company_name) . "- Quote") ?>,
author: <?php echo json_encode(html_entity_decode($company_name)) ?>
},
//watermark: {text: '<?php echo $quote_status; ?>', color: 'lightgrey', opacity: 0.3, bold: true, italics: false},
content: [
// Header
{
columns: [
<?php if (!empty($company_logo_base64)) { ?>
{
image: <?php echo json_encode("data:image;base64,$company_logo_base64") ?>,
width: 120
},
<?php } ?>
[
{
text: 'Quote',
style: 'invoiceTitle',
width: '*'
},
{
text: <?php echo json_encode(html_entity_decode("$quote_prefix$quote_number")) ?>,
style: 'invoiceNumber',
width: '*'
},
],
],
},
// Billing Headers
{
columns: [
{
text: <?php echo json_encode(html_entity_decode($company_name)) ?>,
style: 'invoiceBillingTitle'
},
{
text: <?php echo json_encode(html_entity_decode($client_name)) ?>,
style: 'invoiceBillingTitleClient'
},
]
},
// Billing Address
{
columns: [
{
text: <?php echo json_encode(html_entity_decode("$company_address \n $company_city $company_state $company_zip \n $company_phone \n $company_website")) ?>,
style: 'invoiceBillingAddress'
},
{
text: <?php echo json_encode(html_entity_decode("$location_address \n $location_city $location_state $location_zip \n $contact_email \n $contact_phone")) ?>,
style: 'invoiceBillingAddressClient'
},
]
},
//Invoice Dates Table
{
table: {
// headers are automatically repeated if the table spans over multiple pages
// you can declare how many rows should be treated as headers
headerRows: 0,
widths: [ '*',80, 80 ],
body: [
// Total
[
{
text: '',
rowSpan: 3
},
{},
{},
],
[
{},
{
text: 'Date',
style: 'invoiceDateTitle'
},
{
text: <?php echo json_encode(html_entity_decode($quote_date)) ?>,
style: 'invoiceDateValue'
},
],
[
{},
{
text: 'Expire',
style: 'invoiceDueDateTitle'
},
{
text: <?php echo json_encode(html_entity_decode($quote_expire)) ?>,
style: 'invoiceDueDateValue'
},
],
]
}, // table
layout: 'lightHorizontalLines'
},
// Line breaks
'\n\n',
// Items
{
table: {
// headers are automatically repeated if the table spans over multiple pages
// you can declare how many rows should be treated as headers
headerRows: 1,
widths: [ '*', 40, 'auto', 'auto', 80 ],
body: [
// Table Header
[
{
text: 'Product',
style: [ 'itemsHeader', 'left']
},
{
text: 'Qty',
style: [ 'itemsHeader', 'center']
},
{
text: 'Price',
style: [ 'itemsHeader', 'right']
},
{
text: 'Tax',
style: [ 'itemsHeader', 'right']
},
{
text: 'Total',
style: [ 'itemsHeader', 'right']
}
],
// Items
<?php
$total_tax = 0;
$sub_total = 0;
$sql_invoice_items = mysqli_query($mysqli, "SELECT * FROM invoice_items WHERE item_quote_id = $quote_id ORDER BY item_order ASC");
while ($row = mysqli_fetch_array($sql_invoice_items)) {
$item_name = $row['item_name'];
$item_description = $row['item_description'];
$item_quantity = $row['item_quantity'];
$item_price = $row['item_price'];
$item_subtotal = $row['item_price'];
$item_tax = $row['item_tax'];
$item_total = $row['item_total'];
$tax_id = $row['item_tax_id'];
$total_tax = $item_tax + $total_tax;
$sub_total = $item_price * $item_quantity + $sub_total;
?>
// Item
[
[
{
text: <?php echo json_encode($item_name) ?>,
style: 'itemTitle'
},
{
text: <?php echo json_encode($item_description) ?>,
style: 'itemDescription'
}
],
{
text: <?php echo $item_quantity ?>,
style: 'itemQty'
},
{
text: <?php echo json_encode(numfmt_format_currency($currency_format, $item_price, $quote_currency_code)) ?>,
style: 'itemNumber'
},
{
text: <?php echo json_encode(numfmt_format_currency($currency_format, $item_tax, $quote_currency_code)) ?>,
style: 'itemNumber'
},
{
text: <?php echo json_encode(numfmt_format_currency($currency_format, $item_total, $quote_currency_code)) ?>,
style: 'itemNumber'
}
],
<?php
}
?>
// END Items
]
}, // table
layout: 'lightHorizontalLines'
},
// TOTAL
{
table: {
// headers are automatically repeated if the table spans over multiple pages
// you can declare how many rows should be treated as headers
headerRows: 0,
widths: [ '*','auto', 80 ],
body: [
// Total
[
{
text: 'Notes',
style:'notesTitle'
},
{},
{}
],
[
{
rowSpan: '*',
text: <?php echo json_encode(html_entity_decode($quote_note)) ?>,
style: 'notesText'
},
{
text: 'Subtotal',
style: 'itemsFooterSubTitle'
},
{
text: <?php echo json_encode(numfmt_format_currency($currency_format, $sub_total, $quote_currency_code)) ?>,
style: 'itemsFooterSubValue'
}
],
<?php if ($quote_discount > 0) { ?>
[
{},
{
text: 'Discount',
style: 'itemsFooterSubTitle'
},
{
text: <?php echo json_encode(numfmt_format_currency($currency_format, -$quote_discount, $quote_currency_code)) ?>,
style: 'itemsFooterSubValue'
}
],
<?php } ?>
<?php if ($total_tax > 0) { ?>
[
{},
{
text: 'Tax',
style: 'itemsFooterSubTitle'
},
{
text: <?php echo json_encode(numfmt_format_currency($currency_format, $total_tax, $quote_currency_code)) ?>,
style: 'itemsFooterSubValue'
}
],
<?php } ?>
[
{},
{
text: 'Total',
style: 'itemsFooterTotalTitle'
},
{
text: <?php echo json_encode(numfmt_format_currency($currency_format, $quote_amount, $quote_currency_code)) ?>,
style: 'itemsFooterTotalValue'
}
],
]
}, // table
layout: 'lightHorizontalLines'
},
// TERMS / FOOTER
{
text: <?php echo json_encode("$config_quote_footer"); ?>,
style: 'documentFooterCenter'
}
], //End Content,
styles: {
// Document Footer
documentFooterCenter: {
fontSize: 9,
margin: [10,50,10,10],
alignment: 'center'
},
// Invoice Title
invoiceTitle: {
fontSize: 18,
bold: true,
alignment: 'right',
margin: [0,0,0,3]
},
// Invoice Number
invoiceNumber: {
fontSize: 14,
alignment: 'right'
},
// Billing Headers
invoiceBillingTitle: {
fontSize: 14,
bold: true,
alignment: 'left',
margin: [0,20,0,5]
},
invoiceBillingTitleClient: {
fontSize: 14,
bold: true,
alignment: 'right',
margin: [0,20,0,5]
},
// Billing Details
invoiceBillingAddress: {
fontSize: 10,
lineHeight: 1.2
},
invoiceBillingAddressClient: {
fontSize: 10,
lineHeight: 1.2,
alignment: 'right',
margin: [0,0,0,30]
},
// Invoice Dates
invoiceDateTitle: {
fontSize: 10,
alignment: 'left',
margin: [0,5,0,5]
},
invoiceDateValue: {
fontSize: 10,
alignment: 'right',
margin: [0,5,0,5]
},
// Invoice Due Dates
invoiceDueDateTitle: {
fontSize: 10,
bold: true,
alignment: 'left',
margin: [0,5,0,5]
},
invoiceDueDateValue: {
fontSize: 10,
bold: true,
alignment: 'right',
margin: [0,5,0,5]
},
// Items Header
itemsHeader: {
fontSize: 10,
margin: [0,5,0,5],
bold: true,
alignment: 'right'
},
// Item Title
itemTitle: {
fontSize: 10,
bold: true,
margin: [0,5,0,3]
},
itemDescription: {
italics: true,
fontSize: 9,
lineHeight: 1.1,
margin: [0,3,0,5]
},
itemQty: {
fontSize: 10,
margin: [0,5,0,5],
alignment: 'center'
},
itemNumber: {
fontSize: 10,
margin: [0,5,0,5],
alignment: 'right'
},
itemTotal: {
fontSize: 10,
margin: [0,5,0,5],
bold: true,
alignment: 'right'
},
// Items Footer (Subtotal, Total, Tax, etc)
itemsFooterSubTitle: {
fontSize: 10,
margin: [0,5,0,5],
alignment: 'right'
},
itemsFooterSubValue: {
fontSize: 10,
margin: [0,5,0,5],
bold: false,
alignment: 'right'
},
itemsFooterTotalTitle: {
fontSize: 10,
margin: [0,5,0,5],
bold: true,
alignment: 'right'
},
itemsFooterTotalValue: {
fontSize: 10,
margin: [0,5,0,5],
bold: true,
alignment: 'right'
},
notesTitle: {
fontSize: 10,
bold: true,
margin: [0,5,0,5]
},
notesText: {
fontSize: 9,
margin: [0,5,50,5]
},
left: {
alignment: 'left'
},
center: {
alignment: 'center'
},
},
defaultStyle: {
columnGap: 20,
}
}
</script>
<?php
require_once "guest_footer.php";

220
guest/guest_view_ticket.php Normal file
View File

@@ -0,0 +1,220 @@
<?php
require_once "guest_header.php";
//Initialize the HTML Purifier to prevent XSS
require "../plugins/htmlpurifier/HTMLPurifier.standalone.php";
$purifier_config = HTMLPurifier_Config::createDefault();
$purifier_config->set('URI.AllowedSchemes', ['data' => true, 'src' => true, 'http' => true, 'https' => true]);
$purifier = new HTMLPurifier($purifier_config);
if (!isset($_GET['ticket_id'], $_GET['url_key'])) {
echo "<br><h2>Oops, something went wrong! Please raise a ticket if you believe this is an error.</h2>";
require_once "guest_footer.php";
exit();
}
// Company info
$company_sql_row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT company_phone, company_website FROM companies, settings WHERE companies.company_id = settings.company_id AND companies.company_id = 1"));
$company_phone = formatPhoneNumber($company_sql_row['company_phone']);
$company_website = nullable_htmlentities($company_sql_row['company_website']);
$url_key = sanitizeInput($_GET['url_key']);
$ticket_id = intval($_GET['ticket_id']);
$ticket_sql = mysqli_query($mysqli,
"SELECT * FROM tickets
LEFT JOIN users on ticket_assigned_to = user_id
LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id
WHERE ticket_id = $ticket_id AND ticket_url_key = '$url_key'"
);
if (mysqli_num_rows($ticket_sql) !== 1) {
// Invalid invoice/key
echo "<br><h2>Oops, something went wrong! Please raise a ticket if you believe this is an error.</h2>";
require_once "guest_footer.php";
exit();
}
$ticket_row = mysqli_fetch_array($ticket_sql);
if ($ticket_row) {
$ticket_prefix = nullable_htmlentities($ticket_row['ticket_prefix']);
$ticket_number = intval($ticket_row['ticket_number']);
$ticket_status = nullable_htmlentities($ticket_row['ticket_status_name']);
$ticket_priority = nullable_htmlentities($ticket_row['ticket_priority']);
$ticket_subject = nullable_htmlentities($ticket_row['ticket_subject']);
$ticket_details = $purifier->purify($ticket_row['ticket_details']);
$ticket_assigned_to = nullable_htmlentities($ticket_row['user_name']);
$ticket_resolved_at = nullable_htmlentities($ticket_row['ticket_resolved_at']);
$ticket_closed_at = nullable_htmlentities($ticket_row['ticket_closed_at']);
$ticket_feedback = nullable_htmlentities($ticket_row['ticket_feedback']);
?>
<ol class="breadcrumb d-print-none">
<li class="breadcrumb-item">
<a href="portal/index.php">Portal Home</a>
</li>
<li class="breadcrumb-item">
<a href="portal/tickets.php">Tickets</a>
</li>
<li class="breadcrumb-item active">Ticket <?php echo $ticket_prefix . $ticket_number; ?></li>
</ol>
<div class="card">
<div class="card-header bg-dark text-center">
<h4 class="mt-1">
Ticket <?php echo $ticket_prefix, $ticket_number ?>
</h4>
</div>
<div class="card-body prettyContent">
<h5><strong>Subject:</strong> <?php echo $ticket_subject ?></h5>
<hr>
<p>
<strong>State:</strong> <?php echo $ticket_status ?>
<br>
<strong>Priority:</strong> <?php echo $ticket_priority ?>
<br>
<?php if (!empty($ticket_assigned_to) && empty($ticket_closed_at)) { ?>
<strong>Assigned to: </strong> <?php echo $ticket_assigned_to ?>
<?php } ?>
</p>
<?php echo $ticket_details ?>
</div>
</div>
<hr>
<!-- Either show the reply comments box, option to re-open ticket, show ticket smiley feedback or thanks for feedback -->
<?php if (empty($ticket_resolved_at)) { ?>
<!-- Reply - guest users should email or login so we know exactly who replied -->
<h6><i>Please <a href="portal">log in</a> or reply to the ticket via email to respond</i></h6>
<?php } elseif (empty($ticket_closed_at)) { ?>
<!-- Re-open -->
<h4>Your ticket has been resolved</h4>
<div class="col-4">
<div class="row">
<div class="col">
<a href="guest_post.php?reopen_ticket&ticket_id=<?php echo $ticket_id; ?>&url_key=<?php echo $url_key ?>" class="btn btn-secondary btn-lg"><i class="fas fa-fw fa-redo text-white"></i> Reopen ticket</a>
</div>
<div class="col">
<a href="guest_post.php?close_ticket=&ticket_id=<?php echo $ticket_id; ?>&url_key=<?php echo $url_key ?>" class="btn btn-success btn-lg"><i class="fas fa-fw fa-gavel text-white"></i> Close ticket</a>
</div>
</div>
</div>
<br>
<?php } elseif (empty($ticket_feedback)) { ?>
<h4>Ticket closed. Please rate your ticket</h4>
<div class="col-4">
<div class="row">
<div class="col">
<a href="guest_post.php?add_ticket_feedback&ticket_id=<?php echo $ticket_id; ?>&url_key=<?php echo $url_key ?>&feedback=Good" class="btn btn-success btn-lg"><i class="fas fa-fw fa-smile text-white"></i> Good</a>
</div>
<div class="col">
<a href="guest_post.php?add_ticket_feedback&ticket_id=<?php echo $ticket_id; ?>&url_key=<?php echo $url_key ?>&feedback=Bad" class="btn btn-danger btn-lg"><i class="fas fa-fw fa-frown text-white"></i> Bad</a>
</div>
</div>
</div>
<br>
<?php } else { ?>
<h4>Rated <?php echo $ticket_feedback ?> -- Thanks for your feedback!</h4>
<?php } ?>
<!-- End comments/reopen/feedback -->
<hr>
<br>
<?php
$sql = mysqli_query($mysqli, "SELECT * 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 AND ticket_reply_type != 'Internal' ORDER BY ticket_reply_id DESC");
while ($row = mysqli_fetch_array($sql)) {
$ticket_reply_id = intval($row['ticket_reply_id']);
$ticket_reply = $purifier->purify($row['ticket_reply']);
$ticket_reply_created_at = nullable_htmlentities($row['ticket_reply_created_at']);
$ticket_reply_updated_at = nullable_htmlentities($row['ticket_reply_updated_at']);
$ticket_reply_by = intval($row['ticket_reply_by']);
$ticket_reply_type = $row['ticket_reply_type'];
if ($ticket_reply_type == "Client") {
$ticket_reply_by_display = nullable_htmlentities($row['contact_name']);
$user_initials = initials($row['contact_name']);
$user_avatar = $row['contact_photo'];
$avatar_link = "../uploads/clients/$ticket_reply_by/$user_avatar";
} else {
$ticket_reply_by_display = nullable_htmlentities($row['user_name']);
$user_id = intval($row['user_id']);
$user_avatar = $row['user_avatar'];
$user_initials = initials($row['user_name']);
$avatar_link = "../uploads/users/$user_id/$user_avatar";
}
?>
<div class="card card-outline <?php if ($ticket_reply_type == 'Client') { echo "card-warning"; } else { echo "card-info"; } ?> mb-3">
<div class="card-header">
<h3 class="card-title">
<div class="media">
<?php
if (!empty($user_avatar)) {
?>
<img src="<?php echo $avatar_link ?>" alt="User Avatar" class="img-size-50 mr-3 img-circle">
<?php
} else {
?>
<span class="fa-stack fa-2x">
<i class="fa fa-circle fa-stack-2x text-secondary"></i>
<span class="fa fa-stack-1x text-white"><?php echo $user_initials; ?></span>
</span>
<?php
}
?>
<div class="media-body">
<?php echo $ticket_reply_by_display; ?>
<br>
<small class="text-muted"><?php echo $ticket_reply_created_at; ?> <?php if (!empty($ticket_reply_updated_at)) { echo "(edited: $ticket_reply_updated_at)"; } ?></small>
</div>
</div>
</h3>
</div>
<div class="card-body prettyContent">
<?php echo $ticket_reply; ?>
</div>
</div>
<?php
}
?>
<script src="../js/pretty_content.js"></script>
<?php } else {
echo "Ticket ID not found!";
} ?>
<div class="card-footer">
<?php echo "<i class='fas fa-phone fa-fw mr-2'></i>$company_phone | <i class='fas fa-globe fa-fw mr-2 ml-2'></i>$company_website"; ?>
</div>
<?php
require_once "guest_footer.php";