mirror of
https://github.com/itflow-org/itflow
synced 2026-02-28 02:44:53 +00:00
Move portal to client and rename and reorganize some client portal files
This commit is contained in:
134
client/autopay.php
Normal file
134
client/autopay.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Auto-pay configuration for PTC/finance contacts
|
||||
*/
|
||||
|
||||
require_once "includes/inc_all.php";
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Initialize stripe
|
||||
require_once '../vendor/stripe-php-10.5.0/init.php';
|
||||
|
||||
// Get Stripe vars
|
||||
$stripe_vars = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_stripe_enable, config_stripe_publishable, config_stripe_secret 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']);
|
||||
|
||||
// Get client's StripeID from database
|
||||
$stripe_client_details = mysqli_fetch_array(mysqli_query($mysqli, "SELECT * FROM client_stripe WHERE client_id = $session_client_id LIMIT 1"));
|
||||
if ($stripe_client_details) {
|
||||
$stripe_id = sanitizeInput($stripe_client_details['stripe_id']);
|
||||
$stripe_pm = sanitizeInput($stripe_client_details['stripe_pm']);
|
||||
}
|
||||
|
||||
// Stripe not enabled in settings
|
||||
if (!$config_stripe_enable || !$config_stripe_publishable || !$config_stripe_secret) {
|
||||
echo "Stripe payment error - Stripe is not enabled, please talk to your helpdesk for further information.";
|
||||
include_once 'includes/footer.php';
|
||||
exit();
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<h3>AutoPay</h3>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-10">
|
||||
|
||||
<!-- Setup pt1: Stripe ID not found / auto-payment not configured -->
|
||||
<?php if (!$stripe_client_details || empty($stripe_id)) { ?>
|
||||
|
||||
<b>Save card details</b><br>
|
||||
In order to set up automatic payments, you must create a customer record in Stripe.<br>
|
||||
First, you must authorize Stripe to store your card details for the purpose of automatic payment.
|
||||
<br><br>
|
||||
|
||||
<div class="col-5">
|
||||
<form action="post.php" method="POST">
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" id="consent" name="consent" value="1" required>
|
||||
<label for="consent" class="custom-control-label">
|
||||
I grant consent for automatic payments
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button type="submit" class="form-control btn-success" name="create_stripe_customer">Create Stripe Customer Record</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php }
|
||||
|
||||
// Setup pt2: Stripe ID found / payment may be configured -->
|
||||
elseif (empty($stripe_pm)) { ?>
|
||||
|
||||
<b>Save card details</b><br>
|
||||
Please add the payment details you would like to save.<br>
|
||||
By adding payment details here, you grant consent for future automatic payments of invoices.<br><br>
|
||||
|
||||
<input type="hidden" id="stripe_publishable_key" value="<?php echo $config_stripe_publishable ?>">
|
||||
<script src="https://js.stripe.com/v3/"></script>
|
||||
<script src="../js/autopay_setup_stripe.js"></script>
|
||||
<div id="checkout">
|
||||
<!-- Checkout will insert the payment form here -->
|
||||
</div>
|
||||
|
||||
<?php }
|
||||
|
||||
// Manage the saved card
|
||||
else { ?>
|
||||
|
||||
<b>Manage saved card details</b>
|
||||
|
||||
<?php
|
||||
|
||||
try {
|
||||
// Initialize
|
||||
$stripe = new \Stripe\StripeClient($config_stripe_secret);
|
||||
|
||||
// Get payment method info (last 4 digits etc)
|
||||
$payment_method = $stripe->customers->retrievePaymentMethod(
|
||||
$stripe_id,
|
||||
$stripe_pm,
|
||||
[]
|
||||
);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
error_log("Stripe payment error - encountered exception when fetching payment method info for $stripe_pm: $error");
|
||||
logApp("Stripe", "error", "Exception when fetching payment method info for $stripe_pm: $error");
|
||||
}
|
||||
|
||||
$card_name = nullable_htmlentities($payment_method->billing_details->name);
|
||||
$card_brand = nullable_htmlentities($payment_method->card->display_brand);
|
||||
$card_last4 = nullable_htmlentities($payment_method->card->last4);
|
||||
$card_expires = nullable_htmlentities($payment_method->card->exp_month) . "/" . nullable_htmlentities($payment_method->card->exp_year);
|
||||
|
||||
?>
|
||||
|
||||
<ul><li><?php echo "$card_name - $card_brand card ending in $card_last4, expires $card_expires"; ?></li></ul>
|
||||
|
||||
<hr>
|
||||
<b>Actions</b><br>
|
||||
- <a href="post.php?stripe_remove_card&pm=<?php echo $stripe_pm; ?>">Remove saved card</a>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<?php
|
||||
require_once "includes/footer.php";
|
||||
61
client/certificates.php
Normal file
61
client/certificates.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Certificate listing for PTC / technical contacts
|
||||
*/
|
||||
|
||||
header("Content-Security-Policy: default-src 'self'");
|
||||
|
||||
require_once "includes/inc_all.php";
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_technical_contact) {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
$certificates_sql = mysqli_query($mysqli, "SELECT certificate_id, certificate_name, certificate_domain, certificate_issued_by, certificate_expire FROM certificates WHERE certificate_client_id = $session_client_id AND certificate_archived_at IS NULL ORDER BY certificate_expire ASC");
|
||||
?>
|
||||
|
||||
<h3>Web Certificates</h3>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-10">
|
||||
|
||||
<table class="table tabled-bordered border border-dark">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Certificate Name</th>
|
||||
<th>FQDN</th>
|
||||
<th>Issuer</th>
|
||||
<th>Expiry</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<?php
|
||||
while ($row = mysqli_fetch_array($certificates_sql)) {
|
||||
$certificate_name = nullable_htmlentities($row['certificate_name']);
|
||||
$certificate_domain = nullable_htmlentities($row['certificate_domain']);
|
||||
$certificate_issued_by = nullable_htmlentities($row['certificate_issued_by']);
|
||||
$certificate_expire = nullable_htmlentities($row['certificate_expire']);
|
||||
|
||||
?>
|
||||
|
||||
<tr>
|
||||
<td><?php echo $certificate_name; ?></td>
|
||||
<td><?php echo $certificate_domain; ?></td>
|
||||
<td><?php echo $certificate_issued_by; ?></td>
|
||||
<td><?php echo $certificate_expire; ?></td>
|
||||
</tr>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "includes/footer.php";
|
||||
79
client/check_login.php
Normal file
79
client/check_login.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Client Portal
|
||||
* Checks if the client is logged in or not
|
||||
*/
|
||||
|
||||
if (!isset($_SESSION)) {
|
||||
// HTTP Only cookies
|
||||
ini_set("session.cookie_httponly", true);
|
||||
if ($config_https_only) {
|
||||
// Tell client to only send cookie(s) over HTTPS
|
||||
ini_set("session.cookie_secure", true);
|
||||
}
|
||||
session_start();
|
||||
}
|
||||
|
||||
if (!isset($_SESSION['client_logged_in']) || !$_SESSION['client_logged_in']) {
|
||||
header("Location: login.php");
|
||||
die;
|
||||
}
|
||||
|
||||
// Check user type
|
||||
if ($_SESSION['user_type'] !== 2) {
|
||||
header("Location: login.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Set Timezone
|
||||
require_once "../inc_set_timezone.php";
|
||||
|
||||
// User IP & UA
|
||||
$session_ip = sanitizeInput(getIP());
|
||||
$session_user_agent = sanitizeInput($_SERVER['HTTP_USER_AGENT']);
|
||||
|
||||
|
||||
// Get info from session
|
||||
$session_client_id = intval($_SESSION['client_id']);
|
||||
$session_contact_id = intval($_SESSION['contact_id']);
|
||||
$session_user_id = intval($_SESSION['user_id']);
|
||||
|
||||
|
||||
// Get company info from database
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM companies WHERE company_id = 1");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
|
||||
$session_company_name = $row['company_name'];
|
||||
$session_company_country = $row['company_country'];
|
||||
$session_company_locale = $row['company_locale'];
|
||||
$session_company_currency = $row['company_currency'];
|
||||
$currency_format = numfmt_create($session_company_locale, NumberFormatter::CURRENCY);
|
||||
|
||||
|
||||
// Get contact info
|
||||
$contact_sql = mysqli_query($mysqli, "SELECT * FROM contacts WHERE contact_id = $session_contact_id AND contact_client_id = $session_client_id");
|
||||
$contact = mysqli_fetch_array($contact_sql);
|
||||
|
||||
$session_contact_name = sanitizeInput($contact['contact_name']);
|
||||
$session_contact_initials = initials($session_contact_name);
|
||||
$session_contact_title = sanitizeInput($contact['contact_title']);
|
||||
$session_contact_email = sanitizeInput($contact['contact_email']);
|
||||
$session_contact_photo = sanitizeInput($contact['contact_photo']);
|
||||
$session_contact_pin = sanitizeInput($contact['contact_pin']);
|
||||
$session_contact_primary = intval($contact['contact_primary']);
|
||||
|
||||
$session_contact_is_technical_contact = false;
|
||||
$session_contact_is_billing_contact = false;
|
||||
if ($contact['contact_technical'] == 1) {
|
||||
$session_contact_is_technical_contact = true;
|
||||
}
|
||||
if ($contact['contact_billing'] == 1) {
|
||||
$session_contact_is_billing_contact = true;
|
||||
}
|
||||
|
||||
// Get client info
|
||||
$client_sql = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_id = $session_client_id");
|
||||
$client = mysqli_fetch_array($client_sql);
|
||||
|
||||
$session_client_name = $client['client_name'];
|
||||
96
client/contact_add.php
Normal file
96
client/contact_add.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Contact management for PTC / technical contacts
|
||||
*/
|
||||
|
||||
header("Content-Security-Policy: default-src 'self'");
|
||||
|
||||
require_once "includes/inc_all.php";
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_technical_contact) {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<ol class="breadcrumb d-print-none">
|
||||
<li class="breadcrumb-item">
|
||||
<a href="index.php">Home</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="contacts.php">Contacts</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active">Add Contact</li>
|
||||
</ol>
|
||||
|
||||
<div class="col-md-6">
|
||||
<form action="post.php" method="post">
|
||||
<!-- Prevent undefined checkbox errors on submit -->
|
||||
<input type="hidden" name="contact_billing" value="0">
|
||||
<input type="hidden" name="contact_technical" value="0">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="contact_name" placeholder="Name" required maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Email <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-envelope"></i></span>
|
||||
</div>
|
||||
<input type="email" class="form-control" name="contact_email" placeholder="Email" required maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label>Roles:</label>
|
||||
<div class="form-row">
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="contactBillingCheckbox" name="contact_billing" value="1">
|
||||
<label class="custom-control-label" for="contactBillingCheckbox">Billing</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="contactTechnicalCheckbox" name="contact_technical" value="1">
|
||||
<label class="custom-control-label" for="contactTechnicalCheckbox">Technical</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Portal authentication</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user-circle"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2 authMethod" name="contact_auth_method">
|
||||
<option value="">- No portal access -</option>
|
||||
<option value="local">Local (Email and password)</option>
|
||||
<?php if (!empty($config_azure_client_id)) { ?>
|
||||
<option value="azure">Azure (Microsoft 365)</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" name="add_contact">Add</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<?php
|
||||
require_once "includes/footer.php";
|
||||
129
client/contact_edit.php
Normal file
129
client/contact_edit.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Contact management for PTC / technical contacts
|
||||
*/
|
||||
|
||||
header("Content-Security-Policy: default-src 'self'");
|
||||
|
||||
require_once "includes/inc_all.php";
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_technical_contact) {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Check for a contact ID
|
||||
if (!isset($_GET['id']) && !intval($_GET['id'])) {
|
||||
header("Location: contacts.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
$contact_id = intval($_GET['id']);
|
||||
|
||||
$sql_contact = mysqli_query(
|
||||
$mysqli, "SELECT contact_id, contact_name, contact_email, contact_primary, contact_technical, contact_billing, user_auth_method
|
||||
FROM contacts
|
||||
LEFT JOIN users ON user_id = contact_user_id
|
||||
WHERE contact_id = $contact_id AND contact_client_id = $session_client_id AND contacts.contact_archived_at IS NULL LIMIT 1"
|
||||
);
|
||||
|
||||
$row = mysqli_fetch_array($sql_contact);
|
||||
|
||||
if ($row) {
|
||||
$contact_id = intval($row['contact_id']);
|
||||
$contact_name = nullable_htmlentities($row['contact_name']);
|
||||
$contact_email = nullable_htmlentities($row['contact_email']);
|
||||
$contact_primary = intval($row['contact_primary']);
|
||||
$contact_technical = intval($row['contact_technical']);
|
||||
$contact_billing = intval($row['contact_billing']);
|
||||
$contact_auth_method = nullable_htmlentities($row['user_auth_method']);
|
||||
} else {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<ol class="breadcrumb d-print-none">
|
||||
<li class="breadcrumb-item">
|
||||
<a href="index.php">Home</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="contacts.php">Contacts</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active">Edit Contact</li>
|
||||
</ol>
|
||||
|
||||
<div class="col-md-6">
|
||||
<form action="post.php" method="post">
|
||||
<input type="hidden" name="contact_id" value="<?php echo $contact_id; ?>">
|
||||
<!-- Prevent undefined checkbox errors on submit -->
|
||||
<input type="hidden" name="contact_billing" value="0">
|
||||
<input type="hidden" name="contact_technical" value="0">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="contact_name" value="<?php echo nullable_htmlentities($contact_name) ?>" required maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Email <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-envelope"></i></span>
|
||||
</div>
|
||||
<input type="email" class="form-control" name="contact_email" value="<?php echo nullable_htmlentities($contact_email) ?>" required maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label>Roles:</label>
|
||||
<div class="form-row">
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="contactBillingCheckbox" name="contact_billing" value="1" <?php if ($contact_billing == 1) { echo "checked"; } ?>>
|
||||
<label class="custom-control-label" for="contactBillingCheckbox">Billing</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="contactTechnicalCheckbox" name="contact_technical" value="1" <?php if ($contact_technical == 1) { echo "checked"; } ?>>
|
||||
<label class="custom-control-label" for="contactTechnicalCheckbox">Technical</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Portal authentication</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user-circle"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2 authMethod" name="contact_auth_method">
|
||||
<option value="">- No portal access -</option>
|
||||
<option value="local" <?php if ($contact_auth_method == "local") { echo "selected"; } ?>>Local (Email and password)</option>
|
||||
<?php if (!empty($config_azure_client_id)) { ?>
|
||||
<option value="azure" <?php if ($contact_auth_method == "azure") { echo "selected"; } ?>>Azure (Microsoft 365)</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($contact_primary) { echo "<i>Cannot edit the primary contact</i>"; } else { ?>
|
||||
<button class="btn btn-primary" name="edit_contact">Save</button>
|
||||
<?php } ?>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<?php
|
||||
require_once "includes/footer.php";
|
||||
80
client/contacts.php
Normal file
80
client/contacts.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Contact management for PTC / technical contacts
|
||||
*/
|
||||
|
||||
header("Content-Security-Policy: default-src 'self'");
|
||||
|
||||
require_once "includes/inc_all.php";
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_technical_contact) {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
$contacts_sql = mysqli_query($mysqli, "SELECT contact_id, contact_name, contact_email, contact_primary, contact_technical, contact_billing FROM contacts WHERE contact_client_id = $session_client_id AND contacts.contact_archived_at IS NULL ORDER BY contact_created_at");
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>Contacts</h3>
|
||||
</div>
|
||||
<div class="col offset-6">
|
||||
<a href="contact_add.php" class="btn btn-primary" role="button"><i class="fas fa-plus mr-2"></i>New Contact</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-10">
|
||||
|
||||
<table class="table tabled-bordered border border-dark">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Roles</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<?php
|
||||
while ($row = mysqli_fetch_array($contacts_sql)) {
|
||||
$contact_id = intval($row['contact_id']);
|
||||
$contact_name = nullable_htmlentities($row['contact_name']);
|
||||
$contact_email = nullable_htmlentities($row['contact_email']);
|
||||
$contact_primary = intval($row['contact_primary']);
|
||||
$contact_technical = intval($row['contact_technical']);
|
||||
$contact_billing = intval($row['contact_billing']);
|
||||
|
||||
$contact_roles_display = '-';
|
||||
if ($contact_primary) {
|
||||
$contact_roles_display = 'Primary contact';
|
||||
} else if ($contact_technical && $contact_billing) {
|
||||
$contact_roles_display = 'Technical & Billing';
|
||||
} else if ($contact_technical) {
|
||||
$contact_roles_display = 'Technical';
|
||||
} else if ($contact_billing) {
|
||||
$contact_roles_display = 'Billing';
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<tr>
|
||||
<td><a href="contact_edit.php?id=<?php echo $contact_id?>"><?php echo $contact_name ?></a></td>
|
||||
<td><?php echo $contact_email; ?></td>
|
||||
<td><?php echo $contact_roles_display ?></td>
|
||||
</tr>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "includes/footer.php";
|
||||
71
client/document.php
Normal file
71
client/document.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Docs for PTC / technical contacts
|
||||
*/
|
||||
|
||||
header("Content-Security-Policy: default-src 'self'; img-src 'self' data:");
|
||||
|
||||
require_once "includes/inc_all.php";
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_technical_contact) {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
//Initialize the HTML Purifier to prevent XSS
|
||||
require_once "../plugins/htmlpurifier/HTMLPurifier.standalone.php";
|
||||
|
||||
$purifier_config = HTMLPurifier_Config::createDefault();
|
||||
$purifier_config->set('Cache.DefinitionImpl', null); // Disable cache by setting a non-existent directory or an invalid one
|
||||
$purifier_config->set('URI.AllowedSchemes', ['data' => true, 'src' => true, 'http' => true, 'https' => true]);
|
||||
$purifier = new HTMLPurifier($purifier_config);
|
||||
|
||||
// Check for a document ID
|
||||
if (!isset($_GET['id']) && !intval($_GET['id'])) {
|
||||
header("Location: documents.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
$document_id = intval($_GET['id']);
|
||||
$sql_document = mysqli_query($mysqli,
|
||||
"SELECT document_id, document_name, document_content
|
||||
FROM documents
|
||||
WHERE document_id = $document_id AND document_client_visible = 1 AND document_client_id = $session_client_id AND document_template = 0 AND document_archived_at IS NULL
|
||||
LIMIT 1"
|
||||
);
|
||||
|
||||
$row = mysqli_fetch_array($sql_document);
|
||||
|
||||
if ($row) {
|
||||
$document_id = intval($row['document_id']);
|
||||
$document_name = nullable_htmlentities($row['document_name']);
|
||||
$document_content = $purifier->purify($row['document_content']);
|
||||
} else {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<ol class="breadcrumb d-print-none">
|
||||
<li class="breadcrumb-item">
|
||||
<a href="index.php">Home</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="documents.php">Documents</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active">
|
||||
Document
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body prettyContent">
|
||||
<h3><?php echo $document_name; ?></h3>
|
||||
<?php echo $document_content; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "includes/footer.php";
|
||||
64
client/documents.php
Normal file
64
client/documents.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Docs for PTC / technical contacts
|
||||
*/
|
||||
|
||||
header("Content-Security-Policy: default-src 'self'");
|
||||
|
||||
require_once "includes/inc_all.php";
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_technical_contact) {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
$documents_sql = mysqli_query($mysqli, "SELECT document_id, document_name, document_created_at, folder_name FROM documents LEFT JOIN folders ON document_folder_id = folder_id WHERE document_client_visible = 1 AND document_client_id = $session_client_id AND document_template = 0 AND document_archived_at IS NULL ORDER BY folder_id, document_name DESC");
|
||||
?>
|
||||
|
||||
<h3>Documents</h3>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-10">
|
||||
|
||||
<table class="table tabled-bordered border border-dark">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Created</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<?php
|
||||
while ($row = mysqli_fetch_array($documents_sql)) {
|
||||
$document_id = intval($row['document_id']);
|
||||
$folder_name = nullable_htmlentities($row['folder_name']);
|
||||
$document_name = nullable_htmlentities($row['document_name']);
|
||||
$document_created_at = nullable_htmlentities($row['document_created_at']);
|
||||
|
||||
?>
|
||||
|
||||
<tr>
|
||||
<td><a href="document.php?id=<?php echo $document_id?>">
|
||||
<?php
|
||||
if (!empty($folder_name)) {
|
||||
echo "$folder_name / ";
|
||||
}
|
||||
echo $document_name;
|
||||
?>
|
||||
</a>
|
||||
</td>
|
||||
<td><?php echo $document_created_at; ?></td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "includes/footer.php";
|
||||
55
client/domains.php
Normal file
55
client/domains.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Domain listing for PTC / technical contacts
|
||||
*/
|
||||
|
||||
header("Content-Security-Policy: default-src 'self'");
|
||||
|
||||
require_once "includes/inc_all.php";
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_technical_contact) {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
$domains_sql = mysqli_query($mysqli, "SELECT domain_id, domain_name, domain_expire FROM domains WHERE domain_client_id = $session_client_id AND domain_archived_at IS NULL ORDER BY domain_expire ASC");
|
||||
?>
|
||||
|
||||
<h3>Domains</h3>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-10">
|
||||
|
||||
<table class="table tabled-bordered border border-dark">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Domain Name</th>
|
||||
<th>Expiry</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<?php
|
||||
while ($row = mysqli_fetch_array($domains_sql)) {
|
||||
$domain_name = nullable_htmlentities($row['domain_name']);
|
||||
$domain_expire = nullable_htmlentities($row['domain_expire']);
|
||||
|
||||
?>
|
||||
|
||||
<tr>
|
||||
<td><?php echo $domain_name; ?></td>
|
||||
<td><?php echo $domain_expire; ?></td>
|
||||
</tr>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "includes/footer.php";
|
||||
39
client/functions.php
Normal file
39
client/functions.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Functions
|
||||
*/
|
||||
|
||||
/*
|
||||
* Verifies a contact has access to a particular ticket ID, and that the ticket is in the correct state (open/closed) to perform an action
|
||||
*/
|
||||
function verifyContactTicketAccess($requested_ticket_id, $expected_ticket_state)
|
||||
{
|
||||
|
||||
// Access the global variables
|
||||
global $mysqli, $session_contact_id, $session_contact_primary, $session_contact_is_technical_contact, $session_client_id;
|
||||
|
||||
// Setup
|
||||
if ($expected_ticket_state == "Closed") {
|
||||
// Closed tickets
|
||||
$ticket_state_snippet = "ticket_status = 5";
|
||||
} else {
|
||||
// Open (working/hold) tickets
|
||||
$ticket_state_snippet = "ticket_status != 5";
|
||||
}
|
||||
|
||||
// Verify the contact has access to the provided ticket ID
|
||||
$row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT * FROM tickets WHERE ticket_id = $requested_ticket_id AND $ticket_state_snippet AND ticket_client_id = $session_client_id LIMIT 1"));
|
||||
if ($row) {
|
||||
$ticket_id = $row['ticket_id'];
|
||||
|
||||
if (intval($ticket_id) && ($session_contact_id == $row['ticket_contact_id'] || $session_contact_primary == 1 || $session_contact_is_technical_contact)) {
|
||||
// Client is ticket owner, primary contact, or a technical contact
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Client is NOT ticket owner or primary/tech contact
|
||||
return false;
|
||||
|
||||
}
|
||||
69
client/includes/footer.php
Normal file
69
client/includes/footer.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* HTML Footer
|
||||
*/
|
||||
?>
|
||||
|
||||
<!-- Close container -->
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<hr>
|
||||
|
||||
<p class="text-center">
|
||||
<?php
|
||||
echo nullable_htmlentities($session_company_name);
|
||||
if (!$config_whitelabel_enabled) {
|
||||
echo '<br><small class="text-muted">Powered by ITFlow</small>';
|
||||
}
|
||||
?>
|
||||
</p>
|
||||
|
||||
|
||||
<?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>
|
||||
|
||||
<!--- TinyMCE -->
|
||||
<script src="../plugins/tinymce/tinymce.min.js" referrerpolicy="origin"></script>
|
||||
|
||||
<script>
|
||||
|
||||
// Initialize TinyMCE
|
||||
tinymce.init({
|
||||
selector: '.tinymce',
|
||||
browser_spellcheck: true,
|
||||
resize: true,
|
||||
min_height: 300,
|
||||
max_height: 600,
|
||||
promotion: false,
|
||||
branding: false,
|
||||
menubar: false,
|
||||
statusbar: false,
|
||||
toolbar: [
|
||||
{ name: 'styles', items: [ 'styles' ] },
|
||||
{ name: 'formatting', items: [ 'bold', 'italic', 'forecolor' ] },
|
||||
{ name: 'lists', items: [ 'bullist', 'numlist' ] },
|
||||
{ name: 'alignment', items: [ 'alignleft', 'aligncenter', 'alignright', 'alignjustify' ] },
|
||||
{ name: 'indentation', items: [ 'outdent', 'indent' ] },
|
||||
{ name: 'table', items: [ 'table' ] },
|
||||
{ name: 'extra', items: [ 'fullscreen' ] }
|
||||
],
|
||||
mobile: {
|
||||
menubar: false,
|
||||
plugins: 'autosave lists autolink',
|
||||
toolbar: 'undo bold italic styles'
|
||||
},
|
||||
plugins: 'link image lists table code codesample fullscreen autoresize',
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<script src="../js/pretty_content.js"></script>
|
||||
|
||||
<script src="../js/confirm_modal.js"></script>
|
||||
139
client/includes/header.php
Normal file
139
client/includes/header.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* HTML Header
|
||||
*/
|
||||
|
||||
header("X-Frame-Options: DENY"); // Legacy
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title><?php echo nullable_htmlentities($session_company_name); ?> | Client Portal</title>
|
||||
|
||||
<!-- Tell the browser to be responsive to screen width -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="noindex">
|
||||
|
||||
<!-- 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 -->
|
||||
<link rel="stylesheet" href="../plugins/fontawesome-free/css/all.min.css">
|
||||
|
||||
<!-- Theme style -->
|
||||
<link rel="stylesheet" href="../plugins/adminlte/css/adminlte.min.css">
|
||||
|
||||
</head>
|
||||
|
||||
<!-- Navbar -->
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="index.php"><?php echo nullable_htmlentities($session_company_name); ?></a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item <?php if (basename($_SERVER['PHP_SELF']) == "index.php") {echo "active";} ?>">
|
||||
<a class="nav-link" href="index.php">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?php if (basename($_SERVER['PHP_SELF']) == "tickets.php" || basename($_SERVER['PHP_SELF']) == "ticket_add.php" || basename($_SERVER['PHP_SELF']) == "ticket.php") {echo "active";} ?>" href="tickets.php">Tickets</a>
|
||||
</li>
|
||||
|
||||
<?php if (($session_contact_primary == 1 || $session_contact_is_billing_contact) && $config_module_enable_accounting == 1) { ?>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle <?php echo in_array(basename($_SERVER['PHP_SELF']), ['invoices.php', 'quotes.php', 'autopay.php']) ? 'active' : ''; ?>" href="#" id="navbarDropdown1" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Finance
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown1">
|
||||
<a class="dropdown-item" href="invoices.php">Invoices</a>
|
||||
<a class="dropdown-item" href="quotes.php">Quotes</a>
|
||||
<a class="dropdown-item" href="autopay.php">Auto Payment</a>
|
||||
</div>
|
||||
</li>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($config_module_enable_itdoc && ($session_contact_primary == 1 || $session_contact_is_technical_contact)) { ?>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle <?php echo in_array(basename($_SERVER['PHP_SELF']), ['documents.php', 'contacts.php', 'domains.php', 'certificates.php']) ? 'active' : ''; ?>" href="#" id="navbarDropdown2" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Technical
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown2">
|
||||
<a class="dropdown-item" href="contacts.php">Contacts</a>
|
||||
<a class="dropdown-item" href="documents.php">Documents</a>
|
||||
<a class="dropdown-item" href="domains.php">Domains</a>
|
||||
<a class="dropdown-item" href="certificates.php">Certificates</a>
|
||||
<a class="dropdown-item" href="ticket_view_all.php">All tickets</a>
|
||||
</div>
|
||||
</li>
|
||||
<?php } ?>
|
||||
|
||||
</ul><!-- End left nav -->
|
||||
|
||||
<ul class="nav navbar-nav pull-right">
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown">
|
||||
<?php echo stripslashes(nullable_htmlentities($session_contact_name)); ?>
|
||||
</a>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="profile.php"><i class="fas fa-fw fa-user mr-2"></i>Account</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="post.php?logout"><i class="fas fa-fw fa-sign-out-alt mr-2"></i>Sign out</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<br>
|
||||
|
||||
<!-- Page content container -->
|
||||
<div class="container">
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-1 text-center">
|
||||
<?php if (!empty($session_contact_photo)) { ?>
|
||||
<img src="<?php echo "../uploads/clients/$session_client_id/$session_contact_photo"; ?>" alt="..." height="50" width="50" class="img-circle img-responsive">
|
||||
|
||||
<?php } else { ?>
|
||||
<span class="fa-stack fa-2x rounded-left">
|
||||
<i class="fa fa-circle fa-stack-2x text-secondary"></i>
|
||||
<span class="fa fa-stack-1x text-white"><?php echo $session_contact_initials; ?></span>
|
||||
</span>
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
<div class="col-md-11 p-0">
|
||||
<h4>Welcome, <strong><?php echo stripslashes(nullable_htmlentities($session_contact_name)); ?></strong>!</h4>
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?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'>×</button>
|
||||
</div>
|
||||
<?php
|
||||
|
||||
unset($_SESSION['alert_type']);
|
||||
unset($_SESSION['alert_message']);
|
||||
|
||||
}
|
||||
?>
|
||||
12
client/includes/inc_all.php
Normal file
12
client/includes/inc_all.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Includes for all pages (except login)
|
||||
*/
|
||||
|
||||
require_once '../config.php';
|
||||
require_once '../get_settings.php';
|
||||
require_once '../functions.php';
|
||||
require_once 'check_login.php';
|
||||
require_once 'functions.php';
|
||||
require_once "header.php";
|
||||
16
client/index.php
Normal file
16
client/index.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Landing / Home page for the client portal
|
||||
*/
|
||||
|
||||
header("Content-Security-Policy: default-src 'self'");
|
||||
|
||||
require_once "includes/inc_all.php";
|
||||
|
||||
?>
|
||||
<div class="col-md-2 offset-1">
|
||||
<a href="ticket_add.php" class="btn btn-primary btn-block">New ticket</a>
|
||||
</div>
|
||||
|
||||
<?php require_once "includes/footer.php"; ?>
|
||||
103
client/invoices.php
Normal file
103
client/invoices.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Invoices for PTC
|
||||
*/
|
||||
|
||||
header("Content-Security-Policy: default-src 'self'");
|
||||
|
||||
require_once "includes/inc_all.php";
|
||||
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
$invoices_sql = mysqli_query($mysqli, "SELECT * FROM invoices WHERE invoice_client_id = $session_client_id AND invoice_status != 'Draft' ORDER BY invoice_date DESC");
|
||||
?>
|
||||
|
||||
<h3>Invoices</h3>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-10">
|
||||
|
||||
<table class="table tabled-bordered border border-dark">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Scope</th>
|
||||
<th>Amount</th>
|
||||
<th>Date</th>
|
||||
<th>Due</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<?php
|
||||
while ($row = mysqli_fetch_array($invoices_sql)) {
|
||||
$invoice_id = intval($row['invoice_id']);
|
||||
$invoice_prefix = nullable_htmlentities($row['invoice_prefix']);
|
||||
$invoice_number = intval($row['invoice_number']);
|
||||
$invoice_scope = nullable_htmlentities($row['invoice_scope']);
|
||||
$invoice_status = nullable_htmlentities($row['invoice_status']);
|
||||
$invoice_date = nullable_htmlentities($row['invoice_date']);
|
||||
$invoice_due = nullable_htmlentities($row['invoice_due']);
|
||||
$invoice_amount = floatval($row['invoice_amount']);
|
||||
$invoice_url_key = nullable_htmlentities($row['invoice_url_key']);
|
||||
|
||||
if (empty($invoice_scope)) {
|
||||
$invoice_scope_display = "-";
|
||||
} else {
|
||||
$invoice_scope_display = $invoice_scope;
|
||||
}
|
||||
|
||||
$now = time();
|
||||
if (($invoice_status == "Sent" || $invoice_status == "Partial" || $invoice_status == "Viewed") && strtotime($invoice_due) + 86400 < $now) {
|
||||
$overdue_color = "text-danger font-weight-bold";
|
||||
} else {
|
||||
$overdue_color = "";
|
||||
}
|
||||
|
||||
if ($invoice_status == "Sent") {
|
||||
$invoice_badge_color = "warning text-white";
|
||||
} elseif ($invoice_status == "Viewed") {
|
||||
$invoice_badge_color = "info";
|
||||
} elseif ($invoice_status == "Partial") {
|
||||
$invoice_badge_color = "primary";
|
||||
} elseif ($invoice_status == "Paid") {
|
||||
$invoice_badge_color = "success";
|
||||
} elseif ($invoice_status == "Cancelled") {
|
||||
$invoice_badge_color = "danger";
|
||||
} else{
|
||||
$invoice_badge_color = "secondary";
|
||||
}
|
||||
?>
|
||||
|
||||
<tr>
|
||||
<td><a target="_blank" href="//<?php echo $config_base_url ?>/guest/guest_view_invoice.php?invoice_id=<?php echo "$invoice_id&url_key=$invoice_url_key"?>"> <?php echo "$invoice_prefix$invoice_number"; ?></a></td>
|
||||
<td><?php echo $invoice_scope_display; ?></td>
|
||||
<td><?php echo numfmt_format_currency($currency_format, $invoice_amount, $session_company_currency); ?></td>
|
||||
<td><?php echo $invoice_date; ?></td>
|
||||
<td class="<?php echo $overdue_color; ?>"><?php echo $invoice_due; ?></td>
|
||||
<td>
|
||||
<span class="p-2 badge badge-<?php echo $invoice_badge_color; ?>">
|
||||
<?php echo $invoice_status; ?>
|
||||
</span>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<?php
|
||||
require_once "includes/footer.php";
|
||||
|
||||
224
client/login.php
Normal file
224
client/login.php
Normal file
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Landing / Home page for the client portal
|
||||
*/
|
||||
|
||||
header("Content-Security-Policy: default-src 'self'");
|
||||
|
||||
require_once '../config.php';
|
||||
|
||||
require_once '../functions.php';
|
||||
|
||||
require_once '../get_settings.php';
|
||||
|
||||
if (!isset($_SESSION)) {
|
||||
// HTTP Only cookies
|
||||
ini_set("session.cookie_httponly", true);
|
||||
if ($config_https_only) {
|
||||
// Tell client to only send cookie(s) over HTTPS
|
||||
ini_set("session.cookie_secure", true);
|
||||
}
|
||||
session_start();
|
||||
}
|
||||
|
||||
// Set Timezone after session_start
|
||||
require_once "../inc_set_timezone.php";
|
||||
|
||||
// Check to see if client portal is enabled
|
||||
if($config_client_portal_enable == 0) {
|
||||
echo "Client Portal is Disabled";
|
||||
exit();
|
||||
}
|
||||
|
||||
$session_ip = sanitizeInput(getIP());
|
||||
$session_user_agent = sanitizeInput($_SERVER['HTTP_USER_AGENT']);
|
||||
|
||||
$sql_settings = mysqli_query($mysqli, "SELECT config_azure_client_id, config_login_message FROM settings WHERE company_id = 1");
|
||||
$settings = mysqli_fetch_array($sql_settings);
|
||||
$azure_client_id = $settings['config_azure_client_id'];
|
||||
$config_login_message = nullable_htmlentities($settings['config_login_message']);
|
||||
|
||||
$company_sql = mysqli_query($mysqli, "SELECT company_name, company_logo FROM companies WHERE company_id = 1");
|
||||
$company_results = mysqli_fetch_array($company_sql);
|
||||
$company_name = $company_results['company_name'];
|
||||
$company_logo = $company_results['company_logo'];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['login'])) {
|
||||
|
||||
$email = sanitizeInput($_POST['email']);
|
||||
$password = $_POST['password'];
|
||||
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
|
||||
header("HTTP/1.1 401 Unauthorized");
|
||||
|
||||
$_SESSION['login_message'] = 'Invalid e-mail';
|
||||
|
||||
} else {
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM users LEFT JOIN contacts ON user_id = contact_user_id WHERE user_email = '$email' AND user_archived_at IS NULL AND user_type = 2 AND user_status = 1 LIMIT 1");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$client_id = intval($row['contact_client_id']);
|
||||
$user_id = intval($row['user_id']);
|
||||
$session_user_id = $user_id; // to pass the user_id to logAction function
|
||||
$contact_id = intval($row['contact_id']);
|
||||
$user_email = sanitizeInput($row['user_email']);
|
||||
$user_auth_method = sanitizeInput($row['user_auth_method']);
|
||||
|
||||
if ($user_auth_method == 'local') {
|
||||
if (password_verify($password, $row['user_password'])) {
|
||||
|
||||
$_SESSION['client_logged_in'] = true;
|
||||
$_SESSION['client_id'] = $client_id;
|
||||
$_SESSION['user_id'] = $user_id;
|
||||
$_SESSION['user_type'] = 2;
|
||||
$_SESSION['contact_id'] = $contact_id;
|
||||
$_SESSION['login_method'] = "local";
|
||||
|
||||
header("Location: index.php");
|
||||
|
||||
// Logging
|
||||
logAction("Client Login", "Success", "Client contact $user_email successfully logged in locally", $client_id, $user_id);
|
||||
|
||||
} else {
|
||||
|
||||
// Logging
|
||||
logAction("Client Login", "Failed", "Failed client portal login attempt using $email (incorrect password for contact ID $contact_id)", $client_id, $user_id);
|
||||
|
||||
header("HTTP/1.1 401 Unauthorized");
|
||||
$_SESSION['login_message'] = 'Incorrect username or password.';
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Logging
|
||||
logAction("Client Login", "Failed", "Failed client portal login attempt using $email (invalid email/not allowed local auth)");
|
||||
|
||||
header("HTTP/1.1 401 Unauthorized");
|
||||
|
||||
$_SESSION['login_message'] = 'Incorrect username or password.';
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title><?php echo $company_name; ?> | Client Portal Login</title>
|
||||
|
||||
<!-- Tell the browser to be responsive to screen width -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="noindex">
|
||||
|
||||
<!-- 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 -->
|
||||
<link rel="stylesheet" href="../plugins/fontawesome-free/css/all.min.css">
|
||||
|
||||
<!-- Theme style -->
|
||||
<link rel="stylesheet" href="../plugins/adminlte/css/adminlte.min.css">
|
||||
|
||||
</head>
|
||||
|
||||
<body class="hold-transition login-page">
|
||||
<div class="login-box">
|
||||
<div class="login-logo">
|
||||
<?php if (!empty($company_logo)) { ?>
|
||||
<img alt="<?=$company_name?> logo" height="110" width="380" class="img-fluid" src="<?php echo "../uploads/settings/$company_logo"; ?>">
|
||||
<?php } else { ?>
|
||||
<b><?=$company_name?></b> <br>Client Portal Login</h2>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body login-card-body">
|
||||
<?php if(!empty($config_login_message)){ ?>
|
||||
<p class="login-box-msg px-0"><?php echo nl2br($config_login_message); ?></p>
|
||||
<?php } ?>
|
||||
<?php
|
||||
if (!empty($_SESSION['login_message'])) { ?>
|
||||
<p class="login-box-msg text-danger">
|
||||
<?php
|
||||
echo $_SESSION['login_message'];
|
||||
unset($_SESSION['login_message']);
|
||||
?>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<form method="post">
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" class="form-control" placeholder="Registered Client Email" name="email" required autofocus>
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<span class="fas fa-envelope"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<input type="password" class="form-control" placeholder="Client Password" name="password" required>
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<span class="fas fa-lock"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success btn-block mb-3" name="login">Sign in</button>
|
||||
|
||||
<hr>
|
||||
|
||||
<?php
|
||||
if (!empty($config_smtp_host)) { ?>
|
||||
<h5 class="text-center"><a href="login_reset.php">Forgot password?</a></h5>
|
||||
<?php } ?>
|
||||
|
||||
</form>
|
||||
|
||||
<?php
|
||||
if (!empty($azure_client_id)) { ?>
|
||||
<hr>
|
||||
<div class="col text-center">
|
||||
<a href="login_microsoft.php">
|
||||
<button type="button" class="btn btn-secondary">Login with Microsoft Entra</button>
|
||||
</a>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
</div>
|
||||
<!-- /.login-card-body -->
|
||||
|
||||
</div>
|
||||
<!-- /.div.card -->
|
||||
|
||||
</div>
|
||||
<!-- /.login-box -->
|
||||
|
||||
<?php
|
||||
if (!$config_whitelabel_enabled) {
|
||||
echo '<small class="text-muted">Powered by ITFlow</small>';
|
||||
}
|
||||
?>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- Prevents resubmit on refresh or back -->
|
||||
<script src="../js/login_prevent_resubmit.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
148
client/login_microsoft.php
Normal file
148
client/login_microsoft.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* OAuth Login via Microsoft IDP
|
||||
*/
|
||||
|
||||
require_once '../config.php';
|
||||
require_once '../functions.php';
|
||||
|
||||
if (!isset($_SESSION)) {
|
||||
// HTTP Only cookies
|
||||
ini_set("session.cookie_httponly", true);
|
||||
if ($config_https_only) {
|
||||
// Tell client to only send cookie(s) over HTTPS
|
||||
ini_set("session.cookie_secure", true);
|
||||
}
|
||||
session_start();
|
||||
}
|
||||
|
||||
// Set Timezone after session starts
|
||||
require_once "../inc_set_timezone.php";
|
||||
|
||||
$session_ip = sanitizeInput(getIP());
|
||||
$session_user_agent = sanitizeInput($_SERVER['HTTP_USER_AGENT']);
|
||||
|
||||
$sql_settings = mysqli_query($mysqli, "SELECT config_azure_client_id, config_azure_client_secret FROM settings WHERE company_id = 1");
|
||||
$settings = mysqli_fetch_array($sql_settings);
|
||||
|
||||
$client_id = $settings['config_azure_client_id'];
|
||||
$client_secret = $settings['config_azure_client_secret'];
|
||||
|
||||
$redirect_uri = "https://$config_base_url/portal/login_microsoft.php";
|
||||
|
||||
# https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
|
||||
$auth_code_url = "https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize";
|
||||
$token_grant_url = "https://login.microsoftonline.com/organizations/oauth2/v2.0/token";
|
||||
|
||||
// Initial Login Request, via Microsoft
|
||||
// Returns an authorization code if login was successful
|
||||
if ($_SERVER['REQUEST_METHOD'] == "GET") {
|
||||
|
||||
$params = array (
|
||||
'client_id' => $client_id,
|
||||
'redirect_uri' => $redirect_uri,
|
||||
'response_type' => 'code',
|
||||
'response_mode' =>'form_post',
|
||||
'scope' => 'https://graph.microsoft.com/User.Read',
|
||||
'state' => session_id());
|
||||
|
||||
header('Location: '.$auth_code_url.'?'.http_build_query($params));
|
||||
|
||||
}
|
||||
|
||||
// Login was successful, Microsoft has returned us an authorization code via POST
|
||||
// Request an access token using authorization code (& client secret) (server side)
|
||||
if (isset($_POST['code']) && $_POST['state'] == session_id()) {
|
||||
|
||||
$params = array (
|
||||
'client_id' =>$client_id,
|
||||
'code' => $_POST['code'],
|
||||
'redirect_uri' => $redirect_uri,
|
||||
'grant_type' => 'authorization_code',
|
||||
'client_secret' => $client_secret
|
||||
);
|
||||
|
||||
// Send request via CURL (server side) so user cannot see the client secret
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $token_grant_url);
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt(
|
||||
$ch,
|
||||
CURLOPT_POSTFIELDS,
|
||||
http_build_query($params)
|
||||
);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
#curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); // DEBUG ONLY - WAMP
|
||||
|
||||
$access_token_response = json_decode(curl_exec($ch), 1);
|
||||
|
||||
// Check if we have an access token
|
||||
// If we do, send a request to Microsoft Graph API to get user info
|
||||
if (isset($access_token_response['access_token'])) {
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array ('Authorization: Bearer '.$access_token_response['access_token'],
|
||||
'Content-type: application/json'));
|
||||
curl_setopt($ch, CURLOPT_URL, "https://graph.microsoft.com/v1.0/me/");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
#curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); // DEBUG ONLY - WAMP
|
||||
|
||||
$msgraph_response = json_decode(curl_exec($ch), 1);
|
||||
|
||||
if (isset($msgraph_response['error'])) {
|
||||
// Something went wrong verifying the token/using the Graph API - quit
|
||||
echo "Error with MS Graph API. Details:";
|
||||
var_dump($msgraph_response['error']);
|
||||
exit();
|
||||
|
||||
} elseif (isset($msgraph_response['id'])) {
|
||||
|
||||
$upn = mysqli_real_escape_string($mysqli, $msgraph_response["userPrincipalName"]);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM users LEFT JOIN contacts ON user_id = contact_user_id WHERE user_email = '$upn' AND user_archived_at IS NULL AND user_type = 2 AND user_status = 1 LIMIT 1");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$client_id = intval($row['contact_client_id']);
|
||||
$user_id = intval($row['user_id']);
|
||||
$session_user_id = $user_id; // to pass the user_id to logAction function
|
||||
$contact_id = intval($row['contact_id']);
|
||||
$user_email = sanitizeInput($row['user_email']);
|
||||
$user_auth_method = sanitizeInput($row['user_auth_method']);
|
||||
|
||||
if ($user_auth_method == 'azure') {
|
||||
|
||||
$_SESSION['client_logged_in'] = true;
|
||||
$_SESSION['client_id'] = $client_id;
|
||||
$_SESSION['user_id'] = $user_id;
|
||||
$_SESSION['user_type'] = 2;
|
||||
$_SESSION['contact_id'] = $contact_id;
|
||||
$_SESSION['login_method'] = "azure";
|
||||
|
||||
// Logging
|
||||
logAction("Client Login", "Success", "Client contact $upn successfully logged in via Entra", $client_id, $user_id);
|
||||
|
||||
header("Location: index.php");
|
||||
|
||||
} else {
|
||||
|
||||
$_SESSION['login_message'] = 'Something went wrong with logging you in: Your account is not configured for Entra SSO. Please ensure you are setup in ITFlow as a contact and have Entra SSO configured.';
|
||||
|
||||
header("Location: index.php");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
header('Location: index.php');
|
||||
|
||||
} else {
|
||||
|
||||
echo "Error getting access_token";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If the user is just sat on the page, redirect them to log in to try again
|
||||
if (empty($_GET)) {
|
||||
echo "<script> setTimeout(function() { window.location = \"login.php\"; },1000);</script>";
|
||||
}
|
||||
303
client/login_reset.php
Normal file
303
client/login_reset.php
Normal file
@@ -0,0 +1,303 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Password reset page
|
||||
*/
|
||||
|
||||
header("Content-Security-Policy: default-src 'self'");
|
||||
|
||||
require_once '../config.php';
|
||||
require_once '../functions.php';
|
||||
require_once '../get_settings.php';
|
||||
|
||||
|
||||
if (empty($config_smtp_host)) {
|
||||
header("Location: login.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Check to see if client portal is enabled
|
||||
if($config_client_portal_enable == 0) {
|
||||
echo "Client Portal is Disabled";
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!isset($_SESSION)) {
|
||||
// HTTP Only cookies
|
||||
ini_set("session.cookie_httponly", true);
|
||||
if ($config_https_only) {
|
||||
// Tell client to only send cookie(s) over HTTPS
|
||||
ini_set("session.cookie_secure", true);
|
||||
}
|
||||
session_start();
|
||||
}
|
||||
|
||||
// Set Timezone after session
|
||||
require_once "../inc_set_timezone.php";
|
||||
|
||||
$ip = sanitizeInput(getIP());
|
||||
$user_agent = sanitizeInput($_SERVER['HTTP_USER_AGENT']);
|
||||
|
||||
// Get Company Info
|
||||
$company_sql = mysqli_query($mysqli, "SELECT company_name, company_phone FROM companies WHERE company_id = 1");
|
||||
$company_results = mysqli_fetch_array($company_sql);
|
||||
$company_name = sanitizeInput($company_results['company_name']);
|
||||
$company_phone = sanitizeInput(formatPhoneNumber($company_results['company_phone']));
|
||||
$company_name_display = $company_results['company_name'];
|
||||
|
||||
// Get settings from get_settings.php and sanitize them
|
||||
$config_ticket_from_name = sanitizeInput($config_ticket_from_name);
|
||||
$config_ticket_from_email = sanitizeInput($config_ticket_from_email);
|
||||
$config_mail_from_name = sanitizeInput($config_mail_from_name);
|
||||
$config_mail_from_email = sanitizeInput($config_mail_from_email);
|
||||
$config_base_url = sanitizeInput($config_base_url);
|
||||
|
||||
DEFINE("WORDING_ERROR", "Something went wrong! Your link may have expired. Please request a new password reset e-mail.");
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == "POST") {
|
||||
|
||||
/*
|
||||
* Send password reset email
|
||||
*/
|
||||
if (isset($_POST['password_reset_email_request'])) {
|
||||
|
||||
$email = sanitizeInput($_POST['email']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT contact_id, contact_name, user_email, contact_client_id, user_id FROM users LEFT JOIN contacts ON user_id = contact_user_id WHERE user_email = '$email' AND user_auth_method = 'local' AND user_type = 2 AND user_status = 1 AND user_archived_at IS NULL LIMIT 1");
|
||||
$row = mysqli_fetch_assoc($sql);
|
||||
|
||||
if ($row['user_email'] == $email) {
|
||||
$id = intval($row['contact_id']);
|
||||
$user_id = intval($row['user_id']);
|
||||
$name = sanitizeInput($row['contact_name']);
|
||||
$client = intval($row['contact_client_id']);
|
||||
|
||||
$token = randomString(156);
|
||||
$url = "https://$config_base_url/portal/login_reset.php?email=$email&token=$token&client=$client";
|
||||
mysqli_query($mysqli, "UPDATE users SET user_password_reset_token = '$token' WHERE user_id = $user_id LIMIT 1");
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Contact', log_action = 'Modify', log_description = 'Sent a portal password reset e-mail for $email.', log_ip = '$ip', log_user_agent = '$user_agent', log_client_id = $client");
|
||||
|
||||
// Send reset email
|
||||
$subject = "Password reset for $company_name Client Portal";
|
||||
$body = "Hello $name,<br><br>Someone (probably you) has requested a new password for your account on $company_name\'s Client Portal. <br><br><b>Please <a href=\'$url\'>click here</a> to reset your password.</b> <br><br>Alternatively, copy and paste this URL into your browser:<br> $url<br><br><i>If you didn\'t request this change, you can safely ignore this email.</i><br><br>--<br>$company_name - Support<br>$config_ticket_from_email<br>$company_phone";
|
||||
|
||||
$data = [
|
||||
[
|
||||
'from' => $config_mail_from_email,
|
||||
'from_name' => $config_mail_from_name,
|
||||
'recipient' => $email,
|
||||
'recipient_name' => $name,
|
||||
'subject' => $subject,
|
||||
'body' => $body
|
||||
]
|
||||
];
|
||||
$mail = addToMailQueue($data);
|
||||
|
||||
// Error handling
|
||||
if ($mail !== true) {
|
||||
mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Mail', notification = 'Failed to send email to $email'");
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Mail', log_action = 'Error', log_description = 'Failed to send email to $email regarding $subject. $mail'");
|
||||
}
|
||||
//End Mail IF
|
||||
}
|
||||
|
||||
$_SESSION['login_message'] = "If your account exists, a reset link is on it's way! Please allow a few minutes for it to reach you.";
|
||||
|
||||
/*
|
||||
* Link is being used - Perform password reset
|
||||
*/
|
||||
} elseif (isset($_POST['password_reset_set_password'])) {
|
||||
|
||||
if (!isset($_POST['new_password']) || !isset($_POST['email']) || !isset($_POST['token']) || !isset($_POST['client'])) {
|
||||
$_SESSION['login_message'] = WORDING_ERROR;
|
||||
}
|
||||
|
||||
$token = sanitizeInput($_POST['token']);
|
||||
$email = sanitizeInput($_POST['email']);
|
||||
$client = intval($_POST['client']);
|
||||
|
||||
// Query user
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM users LEFT JOIN contacts ON user_id = contact_user_id WHERE user_email = '$email' AND user_password_reset_token = '$token' AND contact_client_id = $client AND user_auth_method = 'local' AND user_type = 2 AND user_status = 1 AND user_archived_at IS NULL LIMIT 1");
|
||||
$user_row = mysqli_fetch_array($sql);
|
||||
$contact_id = intval($user_row['contact_id']);
|
||||
$user_id = intval($user_row['user_id']);
|
||||
$name = sanitizeInput($user_row['contact_name']);
|
||||
|
||||
// Ensure the token is correct
|
||||
if (sha1($user_row['user_password_reset_token']) == sha1($token)) {
|
||||
|
||||
// Set password, invalidate token, logging
|
||||
$password = password_hash($_POST['new_password'], PASSWORD_DEFAULT);
|
||||
mysqli_query($mysqli, "UPDATE users SET user_password = '$password', user_password_reset_token = NULL WHERE user_id = $user_id LIMIT 1");
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Contact User', log_action = 'Modify', log_description = 'Reset portal password for $email.', log_ip = '$ip', log_user_agent = '$user_agent', log_client_id = $client, log_user_id = $user_id");
|
||||
|
||||
// Send confirmation email
|
||||
$subject = "Password reset confirmation for $company_name Client Portal";
|
||||
$body = "Hello $name,<br><br>Your password for your account on $company_name\'s Client Portal was successfully reset. You should be all set! <br><br><b>If you didn\'t reset your password, please get in touch ASAP.</b><br><br>--<br>$company_name - Support<br>$config_ticket_from_email<br>$company_phone";
|
||||
|
||||
|
||||
$data = [
|
||||
[
|
||||
'from' => $config_mail_from_email,
|
||||
'from_name' => $config_mail_from_name,
|
||||
'recipient' => $email,
|
||||
'recipient_name' => $name,
|
||||
'subject' => $subject,
|
||||
'body' => $body
|
||||
]
|
||||
];
|
||||
|
||||
$mail = addToMailQueue($data);
|
||||
|
||||
// Error handling
|
||||
if ($mail !== true) {
|
||||
mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Mail', notification = 'Failed to send email to $email'");
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Mail', log_action = 'Error', log_description = 'Failed to send email to $email regarding $subject. $mail'");
|
||||
}
|
||||
|
||||
// Redirect to login page
|
||||
$_SESSION['login_message'] = "Password reset successfully!";
|
||||
header("Location: login.php");
|
||||
exit();
|
||||
|
||||
} else {
|
||||
$_SESSION['login_message'] = WORDING_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title><?php echo nullable_htmlentities($company_name_display); ?> | Password Reset</title>
|
||||
|
||||
<!-- Tell the browser to be responsive to screen width -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="noindex">
|
||||
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="../plugins/fontawesome-free/css/all.min.css">
|
||||
|
||||
<!--
|
||||
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 } ?>
|
||||
|
||||
<!-- Theme style -->
|
||||
<link rel="stylesheet" href="../plugins/adminlte/css/adminlte.min.css">
|
||||
|
||||
</head>
|
||||
|
||||
<body class="hold-transition login-page">
|
||||
<div class="login-box">
|
||||
<div class="login-logo"><b><?php echo nullable_htmlentities($company_name_display); ?></b> <br>Password Reset</h2></div>
|
||||
<div class="card">
|
||||
<div class="card-body login-card-body">
|
||||
|
||||
<form method="post">
|
||||
|
||||
<?php
|
||||
/*
|
||||
* Password reset form
|
||||
*/
|
||||
if (isset($_GET['token']) && isset($_GET['email']) && isset($_GET['client'])) {
|
||||
|
||||
$token = sanitizeInput($_GET['token']);
|
||||
$email = sanitizeInput($_GET['email']);
|
||||
$client = intval($_GET['client']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM users LEFT JOIN contacts ON user_id = contact_user_id WHERE user_email = '$email' AND user_password_reset_token = '$token' AND contact_client_id = $client LIMIT 1");
|
||||
$user_row = mysqli_fetch_array($sql);
|
||||
|
||||
// Sanity check
|
||||
if (sha1($user_row['user_password_reset_token']) == sha1($token)) { ?>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<input type="password" class="form-control" placeholder="New Password" name="new_password" required minlength="8">
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<span class="fas fa-lock"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="token" value="<?php echo $token; ?>">
|
||||
<input type="hidden" name="email" value="<?php echo $email; ?>">
|
||||
<input type="hidden" name="client" value="<?php echo $client; ?>">
|
||||
|
||||
<button type="submit" class="btn btn-success btn-block mb-3" name="password_reset_set_password">Reset password</button>
|
||||
|
||||
|
||||
<?php } else {
|
||||
|
||||
$_SESSION['login_message'] = WORDING_ERROR;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Else: Just show the form to request a reset token email
|
||||
*/
|
||||
} else { ?>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<input type="email" class="form-control" placeholder="Registered Client Email" name="email" required autofocus>
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<span class="fas fa-envelope"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success btn-block mb-3" name="password_reset_email_request">Reset my password</button>
|
||||
|
||||
<?php }
|
||||
?>
|
||||
|
||||
</form>
|
||||
|
||||
<p class="login-box-msg text-danger">
|
||||
<?php
|
||||
// Show feedback from session
|
||||
if (!empty($_SESSION['login_message'])) {
|
||||
echo nullable_htmlentities($_SESSION['login_message']);
|
||||
unset($_SESSION['login_message']);
|
||||
}
|
||||
?>
|
||||
</p>
|
||||
|
||||
<a href="login.php">Back to login</a>
|
||||
|
||||
|
||||
</div>
|
||||
<!-- /.login-card-body -->
|
||||
|
||||
</div>
|
||||
<!-- /.div.card -->
|
||||
|
||||
</div>
|
||||
<!-- /.login-box -->
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- Prevents resubmit on refresh or back -->
|
||||
<script src="../js/login_prevent_resubmit.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
685
client/post.php
Normal file
685
client/post.php
Normal file
@@ -0,0 +1,685 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Process GET/POST requests
|
||||
*/
|
||||
|
||||
require_once '../config.php';
|
||||
require_once '../get_settings.php';
|
||||
require_once '../functions.php';
|
||||
require_once 'check_login.php';
|
||||
require_once 'functions.php';
|
||||
|
||||
if (isset($_POST['add_ticket'])) {
|
||||
|
||||
$subject = sanitizeInput($_POST['subject']);
|
||||
$details = mysqli_real_escape_string($mysqli, ($_POST['details']));
|
||||
|
||||
// Get settings from get_settings.php
|
||||
$config_ticket_prefix = sanitizeInput($config_ticket_prefix);
|
||||
$config_ticket_from_name = sanitizeInput($config_ticket_from_name);
|
||||
$config_ticket_from_email = sanitizeInput($config_ticket_from_email);
|
||||
$config_base_url = sanitizeInput($config_base_url);
|
||||
$config_ticket_new_ticket_notification_email = filter_var($config_ticket_new_ticket_notification_email, FILTER_VALIDATE_EMAIL);
|
||||
|
||||
//Generate a unique URL key for clients to access
|
||||
$url_key = randomString(156);
|
||||
|
||||
// Ensure priority is low/med/high (as can be user defined)
|
||||
if ($_POST['priority'] !== "Low" && $_POST['priority'] !== "Medium" && $_POST['priority'] !== "High") {
|
||||
$priority = "Low";
|
||||
} else {
|
||||
$priority = sanitizeInput($_POST['priority']);
|
||||
}
|
||||
|
||||
// Get the next Ticket Number and add 1 for the new ticket number
|
||||
$ticket_number = $config_ticket_next_number;
|
||||
$new_config_ticket_next_number = $config_ticket_next_number + 1;
|
||||
mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1");
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_subject = '$subject', ticket_details = '$details', ticket_priority = '$priority', ticket_status = 1, ticket_billable = $config_ticket_default_billable, ticket_created_by = 0, ticket_contact_id = $session_contact_id, ticket_url_key = '$url_key', ticket_client_id = $session_client_id");
|
||||
$ticket_id = mysqli_insert_id($mysqli);
|
||||
|
||||
// Notify agent DL of the new ticket, if populated with a valid email
|
||||
if ($config_ticket_new_ticket_notification_email) {
|
||||
|
||||
$client_name = sanitizeInput($session_client_name);
|
||||
$details = removeEmoji($details);
|
||||
|
||||
$email_subject = "ITFlow - New Ticket - $client_name: $subject";
|
||||
$email_body = "Hello, <br><br>This is a notification that a new ticket has been raised in ITFlow. <br>Client: $client_name<br>Priority: $priority<br>Link: https://$config_base_url/ticket.php?ticket_id=$ticket_id <br><br><b>$subject</b><br>$details";
|
||||
|
||||
// Queue Mail
|
||||
$data = [
|
||||
[
|
||||
'from' => $config_ticket_from_email,
|
||||
'from_name' => $config_ticket_from_name,
|
||||
'recipient' => $config_ticket_new_ticket_notification_email,
|
||||
'recipient_name' => $config_ticket_from_name,
|
||||
'subject' => $email_subject,
|
||||
'body' => $email_body,
|
||||
]
|
||||
];
|
||||
addToMailQueue($data);
|
||||
}
|
||||
|
||||
// Custom action/notif handler
|
||||
customAction('ticket_create', $ticket_id);
|
||||
|
||||
// Logging
|
||||
logAction("Ticket", "Create", "$session_contact_name created ticket $config_ticket_prefix$ticket_number - $subject from the client portal", $session_client_id, $ticket_id);
|
||||
|
||||
header("Location: ticket.php?id=" . $ticket_id);
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['add_ticket_comment'])) {
|
||||
|
||||
$ticket_id = intval($_POST['ticket_id']);
|
||||
$comment = mysqli_real_escape_string($mysqli, $_POST['comment']);
|
||||
|
||||
// After stripping bad HTML, check the comment isn't just empty
|
||||
if (empty($comment)) {
|
||||
header("Location: " . $_SERVER["HTTP_REFERER"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Verify the contact has access to the provided ticket ID
|
||||
if (verifyContactTicketAccess($ticket_id, "Open")) {
|
||||
|
||||
// Add the comment
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = '$comment', ticket_reply_type = 'Client', ticket_reply_by = $session_contact_id, ticket_reply_ticket_id = $ticket_id");
|
||||
|
||||
$ticket_reply_id = mysqli_insert_id($mysqli);
|
||||
|
||||
// Update Ticket Last Response Field & set ticket to open as client has replied
|
||||
mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 2 WHERE ticket_id = $ticket_id AND ticket_client_id = $session_client_id LIMIT 1");
|
||||
|
||||
|
||||
// Get ticket details & Notify the assigned tech (if any)
|
||||
$ticket_details = mysqli_fetch_array(mysqli_query($mysqli, "SELECT * FROM tickets LEFT JOIN clients ON ticket_client_id = client_id WHERE ticket_id = $ticket_id LIMIT 1"));
|
||||
|
||||
$ticket_number = intval($ticket_details['ticket_number']);
|
||||
$ticket_assigned_to = intval($ticket_details['ticket_assigned_to']);
|
||||
$ticket_subject = sanitizeInput($ticket_details['ticket_subject']);
|
||||
$client_name = sanitizeInput($ticket_details['client_name']);
|
||||
|
||||
if ($ticket_details && $ticket_assigned_to !== 0) {
|
||||
|
||||
// Get tech details
|
||||
$tech_details = mysqli_fetch_array(mysqli_query($mysqli, "SELECT user_email, user_name FROM users WHERE user_id = $ticket_assigned_to LIMIT 1"));
|
||||
$tech_email = sanitizeInput($tech_details['user_email']);
|
||||
$tech_name = sanitizeInput($tech_details['user_name']);
|
||||
|
||||
$subject = "$config_app_name Ticket updated - [$config_ticket_prefix$ticket_number] $ticket_subject";
|
||||
$body = "Hello $tech_name,<br><br>A new reply has been added to the below ticket, check ITFlow for full details.<br><br>Client: $client_name<br>Ticket: $config_ticket_prefix$ticket_number<br>Subject: $ticket_subject<br><br>https://$config_base_url/ticket.php?ticket_id=$ticket_id";
|
||||
|
||||
$data = [
|
||||
[
|
||||
'from' => $config_ticket_from_email,
|
||||
'from_name' => $config_ticket_from_name,
|
||||
'recipient' => $tech_email,
|
||||
'recipient_name' => $tech_name,
|
||||
'subject' => $subject,
|
||||
'body' => $body
|
||||
]
|
||||
];
|
||||
|
||||
addToMailQueue($data);
|
||||
|
||||
}
|
||||
|
||||
// Store any attached any files
|
||||
if (!empty($_FILES)) {
|
||||
|
||||
// Define & create directories, as required
|
||||
mkdirMissing('../uploads/tickets/');
|
||||
$upload_file_dir = "../uploads/tickets/" . $ticket_id . "/";
|
||||
mkdirMissing($upload_file_dir);
|
||||
|
||||
for ($i = 0; $i < count($_FILES['file']['name']); $i++) {
|
||||
// Extract file details for this iteration
|
||||
$single_file = [
|
||||
'name' => $_FILES['file']['name'][$i],
|
||||
'type' => $_FILES['file']['type'][$i],
|
||||
'tmp_name' => $_FILES['file']['tmp_name'][$i],
|
||||
'error' => $_FILES['file']['error'][$i],
|
||||
'size' => $_FILES['file']['size'][$i]
|
||||
];
|
||||
|
||||
if ($ticket_attachment_ref_name = checkFileUpload($single_file, array('jpg', 'jpeg', 'gif', 'png', 'webp', 'pdf', 'txt', 'md', 'doc', 'docx', 'odt', 'csv', 'xls', 'xlsx', 'ods', 'pptx', 'odp', 'zip', 'tar', 'gz', 'xml', 'msg', 'json', 'wav', 'mp3', 'ogg', 'mov', 'mp4', 'av1', 'ovpn'))) {
|
||||
|
||||
$file_tmp_path = $_FILES['file']['tmp_name'][$i];
|
||||
|
||||
$file_name = sanitizeInput($_FILES['file']['name'][$i]);
|
||||
$extarr = explode('.', $_FILES['file']['name'][$i]);
|
||||
$file_extension = sanitizeInput(strtolower(end($extarr)));
|
||||
|
||||
// Define destination file path
|
||||
$dest_path = $upload_file_dir . $ticket_attachment_ref_name;
|
||||
|
||||
move_uploaded_file($file_tmp_path, $dest_path);
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_attachments SET ticket_attachment_name = '$file_name', ticket_attachment_reference_name = '$ticket_attachment_ref_name', ticket_attachment_reply_id = $ticket_reply_id, ticket_attachment_ticket_id = $ticket_id");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Custom action/notif handler
|
||||
customAction('ticket_reply_client', $ticket_id);
|
||||
|
||||
// Redirect back to original page
|
||||
header("Location: " . $_SERVER["HTTP_REFERER"]);
|
||||
|
||||
} else {
|
||||
// The client does not have access to this ticket
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST['add_ticket_feedback'])) {
|
||||
$ticket_id = intval($_POST['ticket_id']);
|
||||
$feedback = sanitizeInput($_POST['add_ticket_feedback']);
|
||||
|
||||
// Verify the contact has access to the provided ticket ID
|
||||
if (verifyContactTicketAccess($ticket_id, "Closed")) {
|
||||
|
||||
// Add feedback
|
||||
mysqli_query($mysqli, "UPDATE tickets SET ticket_feedback = '$feedback' WHERE ticket_id = $ticket_id AND ticket_client_id = $session_client_id LIMIT 1");
|
||||
|
||||
// Notify on bad feedback
|
||||
if ($feedback == "Bad") {
|
||||
$ticket_details = mysqli_fetch_array(mysqli_query($mysqli, "SELECT ticket_number FROM tickets WHERE ticket_id = $ticket_id LIMIT 1"));
|
||||
$ticket_number = intval($ticket_details['ticket_number']);
|
||||
appNotify("Feedback", "$session_contact_name rated ticket $config_ticket_prefix$ticket_number as bad (ID: $ticket_id)", "ticket.php?ticket_id=$ticket_id", $session_client_id, $ticket_id);
|
||||
}
|
||||
|
||||
// Custom action/notif handler
|
||||
customAction('ticket_feedback', $ticket_id);
|
||||
|
||||
// Redirect
|
||||
header("Location: " . $_SERVER["HTTP_REFERER"]);
|
||||
} else {
|
||||
// The client does not have access to this ticket
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['resolve_ticket'])) {
|
||||
$ticket_id = intval($_GET['resolve_ticket']);
|
||||
|
||||
// Get ticket details for logging
|
||||
$row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT * FROM tickets WHERE ticket_id = $ticket_id LIMIT 1"));
|
||||
|
||||
$ticket_prefix = sanitizeInput($row['ticket_prefix']);
|
||||
$ticket_number = intval($row['ticket_number']);
|
||||
|
||||
// Verify the contact has access to the provided ticket ID
|
||||
if (verifyContactTicketAccess($ticket_id, "Open")) {
|
||||
|
||||
// Resolve the ticket
|
||||
mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 4, ticket_resolved_at = NOW() WHERE ticket_id = $ticket_id AND ticket_client_id = $session_client_id");
|
||||
|
||||
// Add reply
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = 'Ticket resolved by $session_contact_name.', ticket_reply_type = 'Client', ticket_reply_by = $session_contact_id, ticket_reply_ticket_id = $ticket_id");
|
||||
|
||||
// Logging
|
||||
logAction("Ticket", "Edit", "$session_contact_name marked ticket $ticket_prefix$ticket_number as resolved in the client portal", $session_client_id, $ticket_id);
|
||||
|
||||
// Custom action/notif handler
|
||||
customAction('ticket_resolve', $ticket_id);
|
||||
|
||||
header("Location: ticket.php?id=" . $ticket_id);
|
||||
|
||||
} else {
|
||||
// The client does not have access to this ticket - send them home
|
||||
header("Location: index.php");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['reopen_ticket'])) {
|
||||
$ticket_id = intval($_GET['reopen_ticket']);
|
||||
|
||||
// Get ticket details for logging
|
||||
$row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT * FROM tickets WHERE ticket_id = $ticket_id LIMIT 1"));
|
||||
|
||||
$ticket_prefix = sanitizeInput($row['ticket_prefix']);
|
||||
$ticket_number = intval($row['ticket_number']);
|
||||
|
||||
// Verify the contact has access to the provided ticket ID
|
||||
if (verifyContactTicketAccess($ticket_id, "Open")) {
|
||||
|
||||
// Re-open ticket
|
||||
mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 2, ticket_resolved_at = NULL WHERE ticket_id = $ticket_id AND ticket_client_id = $session_client_id");
|
||||
|
||||
// Add reply
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = 'Ticket reopened by $session_contact_name.', ticket_reply_type = 'Client', ticket_reply_by = $session_contact_id, ticket_reply_ticket_id = $ticket_id");
|
||||
|
||||
// Logging
|
||||
logAction("Ticket", "Edit", "$session_contact_name reopend ticket $ticket_prefix$ticket_number in the client portal", $session_client_id, $ticket_id);
|
||||
|
||||
// Custom action/notif handler
|
||||
customAction('ticket_update', $ticket_id);
|
||||
|
||||
header("Location: ticket.php?id=" . $ticket_id);
|
||||
|
||||
} else {
|
||||
// The client does not have access to this ticket - send them home
|
||||
header("Location: index.php");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['close_ticket'])) {
|
||||
$ticket_id = intval($_GET['close_ticket']);
|
||||
|
||||
// Get ticket details for logging
|
||||
$row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT * FROM tickets WHERE ticket_id = $ticket_id LIMIT 1"));
|
||||
|
||||
$ticket_prefix = sanitizeInput($row['ticket_prefix']);
|
||||
$ticket_number = intval($row['ticket_number']);
|
||||
|
||||
// Verify the contact has access to the provided ticket ID
|
||||
if (verifyContactTicketAccess($ticket_id, "Open")) {
|
||||
|
||||
// Fully close ticket
|
||||
mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 5, ticket_closed_at = NOW() WHERE ticket_id = $ticket_id AND ticket_client_id = $session_client_id");
|
||||
|
||||
// Add reply
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = 'Ticket closed by $session_contact_name.', ticket_reply_type = 'Client', ticket_reply_by = $session_contact_id, ticket_reply_ticket_id = $ticket_id");
|
||||
|
||||
// Logging
|
||||
logAction("Ticket", "Edit", "$session_contact_name closed ticket $ticket_prefix$ticket_number in the client portal", $session_client_id, $ticket_id);
|
||||
|
||||
// Custom action/notif handler
|
||||
customAction('ticket_close', $ticket_id);
|
||||
|
||||
header("Location: ticket.php?id=" . $ticket_id);
|
||||
} else {
|
||||
// The client does not have access to this ticket - send them home
|
||||
header("Location: index.php");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['logout'])) {
|
||||
setcookie("PHPSESSID", '', time() - 3600, "/");
|
||||
unset($_COOKIE['PHPSESSID']);
|
||||
|
||||
session_unset();
|
||||
session_destroy();
|
||||
|
||||
header('Location: login.php');
|
||||
}
|
||||
|
||||
if (isset($_POST['edit_profile'])) {
|
||||
$new_password = $_POST['new_password'];
|
||||
if (!empty($new_password)) {
|
||||
$password_hash = password_hash($new_password, PASSWORD_DEFAULT);
|
||||
mysqli_query($mysqli, "UPDATE users SET user_password = '$password_hash' WHERE user_id = $session_user_id");
|
||||
|
||||
// Logging
|
||||
logAction("Contact", "Edit", "Client contact $session_contact_name edited their profile/password in the client portal", $session_client_id, $session_contact_id);
|
||||
}
|
||||
header('Location: index.php');
|
||||
}
|
||||
|
||||
if (isset($_POST['add_contact'])) {
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_technical_contact) {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
$contact_name = sanitizeInput($_POST['contact_name']);
|
||||
$contact_email = sanitizeInput($_POST['contact_email']);
|
||||
$contact_technical = intval($_POST['contact_technical']);
|
||||
$contact_billing = intval($_POST['contact_billing']);
|
||||
$contact_auth_method = sanitizeInput($_POST['contact_auth_method']);
|
||||
|
||||
// Check the email isn't already in use
|
||||
$sql = mysqli_query($mysqli, "SELECT user_id FROM users WHERE user_email = '$contact_email'");
|
||||
if ($sql && mysqli_num_rows($sql) > 0) {
|
||||
$_SESSION['alert_type'] = "danger";
|
||||
$_SESSION['alert_message'] = "Cannot add contact as that email address is already in use";
|
||||
header('Location: contact_add.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Create user account with rand password for the contact
|
||||
$contact_user_id = 0;
|
||||
if ($contact_name && $contact_email && $contact_auth_method) {
|
||||
|
||||
$password_hash = password_hash(randomString(), PASSWORD_DEFAULT);
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO users SET user_name = '$contact_name', user_email = '$contact_email', user_password = '$password_hash', user_auth_method = '$contact_auth_method', user_type = 2");
|
||||
|
||||
$contact_user_id = mysqli_insert_id($mysqli);
|
||||
}
|
||||
|
||||
// Create contact record
|
||||
mysqli_query($mysqli, "INSERT INTO contacts SET contact_name = '$contact_name', contact_email = '$contact_email', contact_billing = $contact_billing, contact_technical = $contact_technical, contact_client_id = $session_client_id, contact_user_id = $contact_user_id");
|
||||
$contact_id = mysqli_insert_id($mysqli);
|
||||
|
||||
// Logging
|
||||
logAction("Contact", "Create", "Client contact $session_contact_name created contact $contact_name in the client portal", $session_client_id, $contact_id);
|
||||
|
||||
customAction('contact_create', $contact_id);
|
||||
|
||||
$_SESSION['alert_message'] = "Contact $contact_name created";
|
||||
|
||||
header('Location: contacts.php');
|
||||
}
|
||||
|
||||
if (isset($_POST['edit_contact'])) {
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_technical_contact) {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
$contact_id = intval($_POST['contact_id']);
|
||||
$contact_name = sanitizeInput($_POST['contact_name']);
|
||||
$contact_email = sanitizeInput($_POST['contact_email']);
|
||||
$contact_technical = intval($_POST['contact_technical']);
|
||||
$contact_billing = intval($_POST['contact_billing']);
|
||||
$contact_auth_method = sanitizeInput($_POST['contact_auth_method']);
|
||||
|
||||
// Get the existing contact_user_id - we look it up ourselves so the user can't just overwrite random users
|
||||
$sql = mysqli_query($mysqli,"SELECT contact_user_id FROM contacts WHERE contact_id = $contact_id AND contact_client_id = $session_client_id");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$contact_user_id = intval($row['contact_user_id']);
|
||||
|
||||
// Check the email isn't already in use
|
||||
$sql = mysqli_query($mysqli, "SELECT user_id FROM users WHERE user_email = '$contact_email' AND user_id != $contact_user_id");
|
||||
if ($sql && mysqli_num_rows($sql) > 0) {
|
||||
$_SESSION['alert_type'] = "danger";
|
||||
$_SESSION['alert_message'] = "Cannot update contact as that email address is already in use";
|
||||
header('Location: contact_edit.php?id=' . $contact_id);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Update Existing User
|
||||
if ($contact_user_id > 0) {
|
||||
mysqli_query($mysqli, "UPDATE users SET user_name = '$contact_name', user_email = '$contact_email', user_auth_method = '$contact_auth_method' WHERE user_id = $contact_user_id");
|
||||
|
||||
// Else, create New User
|
||||
} elseif ($contact_user_id == 0 && $contact_name && $contact_email && $contact_auth_method) {
|
||||
$password_hash = password_hash(randomString(), PASSWORD_DEFAULT);
|
||||
mysqli_query($mysqli, "INSERT INTO users SET user_name = '$contact_name', user_email = '$contact_email', user_password = '$password_hash', user_auth_method = '$contact_auth_method', user_type = 2");
|
||||
|
||||
$contact_user_id = mysqli_insert_id($mysqli);
|
||||
}
|
||||
|
||||
// Update contact
|
||||
mysqli_query($mysqli, "UPDATE contacts SET contact_name = '$contact_name', contact_email = '$contact_email', contact_billing = $contact_billing, contact_technical = $contact_technical, contact_user_id = $contact_user_id WHERE contact_id = $contact_id AND contact_client_id = $session_client_id AND contact_archived_at IS NULL AND contact_primary = 0");
|
||||
|
||||
// Logging
|
||||
logAction("Contact", "Edit", "Client contact $session_contact_name edited contact $contact_name in the client portal", $session_client_id, $contact_id);
|
||||
|
||||
$_SESSION['alert_message'] = "Contact $contact_name updated";
|
||||
|
||||
header('Location: contacts.php');
|
||||
|
||||
customAction('contact_update', $contact_id);
|
||||
}
|
||||
|
||||
if (isset($_POST['create_stripe_customer'])) {
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Get Stripe vars
|
||||
$stripe_vars = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_stripe_enable, config_stripe_publishable, config_stripe_secret FROM settings WHERE company_id = 1"));
|
||||
$config_stripe_enable = intval($stripe_vars['config_stripe_enable']);
|
||||
$config_stripe_secret = nullable_htmlentities($stripe_vars['config_stripe_secret']);
|
||||
|
||||
if (!$config_stripe_enable) {
|
||||
header("Location: autopay.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Include stripe SDK
|
||||
require_once '../vendor/stripe-php-10.5.0/init.php';
|
||||
|
||||
// Get client's StripeID from database (should be none)
|
||||
$stripe_client_details = mysqli_fetch_array(mysqli_query($mysqli, "SELECT stripe_id FROM client_stripe WHERE client_id = $session_client_id LIMIT 1"));
|
||||
if (!$stripe_client_details) {
|
||||
|
||||
try {
|
||||
// Initiate Stripe
|
||||
$stripe = new \Stripe\StripeClient($config_stripe_secret);
|
||||
|
||||
// Create customer
|
||||
$customer = $stripe->customers->create([
|
||||
'name' => $session_client_name,
|
||||
'email' => $session_contact_email,
|
||||
'metadata' => [
|
||||
'itflow_client_id' => $session_client_id,
|
||||
'consent' => $session_contact_name
|
||||
]
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
error_log("Stripe payment error - encountered exception when creating customer record for $session_client_name: $error");
|
||||
logApp("Stripe", "error", "Exception creating customer $session_client_name: $error");
|
||||
}
|
||||
|
||||
// Get & Store customer ID
|
||||
$stripe_id = sanitizeInput($customer->id);
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO client_stripe SET client_id = $session_client_id, stripe_id = '$stripe_id'");
|
||||
|
||||
// Logging
|
||||
logAction("Stripe", "Create", "$session_contact_name created Stripe customer for $session_client_name as $stripe_id and authorised future automatic payments", $session_client_id, $session_client_id);
|
||||
|
||||
$_SESSION['alert_message'] = "Stripe customer created, thank you for your consent";
|
||||
|
||||
} else {
|
||||
$_SESSION['alert_type'] = "danger";
|
||||
$_SESSION['alert_message'] = "Stripe customer already exists";
|
||||
}
|
||||
|
||||
header('Location: autopay.php');
|
||||
}
|
||||
|
||||
if (isset($_GET['create_stripe_checkout'])) {
|
||||
|
||||
// This page is called by the autopay_setup_stripe.js, it returns a checkout session client secret
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Get Stripe vars
|
||||
$stripe_vars = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_stripe_enable, config_stripe_publishable, config_stripe_secret FROM settings WHERE company_id = 1"));
|
||||
$config_stripe_enable = intval($stripe_vars['config_stripe_enable']);
|
||||
$config_stripe_secret = nullable_htmlentities($stripe_vars['config_stripe_secret']);
|
||||
|
||||
if (!$config_stripe_enable) {
|
||||
header("Location: autopay.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Client Currency
|
||||
$client_currency_details = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT client_currency_code FROM clients WHERE client_id = $session_client_id LIMIT 1"));
|
||||
$client_currency = $client_currency_details['client_currency_code'];
|
||||
|
||||
// Define return URL that user is redirected to once payment method is verified by Stripe
|
||||
$return_url = "https://$config_base_url/client/post.php?stripe_save_card&session_id={CHECKOUT_SESSION_ID}";
|
||||
|
||||
try {
|
||||
// Initialize stripe
|
||||
require_once '../vendor/stripe-php-10.5.0/init.php';
|
||||
$stripe = new \Stripe\StripeClient($config_stripe_secret);
|
||||
|
||||
// Create checkout session (server side)
|
||||
$checkout_session = $stripe->checkout->sessions->create([
|
||||
'currency' => $client_currency,
|
||||
'mode' => 'setup',
|
||||
'ui_mode' => 'embedded',
|
||||
'return_url' => $return_url,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
error_log("Stripe payment error - encountered exception when creating checkout session: $error");
|
||||
logApp("Stripe", "error", "Exception creating checkout: $error");
|
||||
}
|
||||
|
||||
// Return the client secret to the js script
|
||||
echo json_encode(array('clientSecret' => $checkout_session->client_secret));
|
||||
|
||||
// No redirect & no point logging this
|
||||
}
|
||||
|
||||
if (isset($_GET['stripe_save_card'])) {
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Get Stripe vars
|
||||
$stripe_vars = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_stripe_enable, config_stripe_publishable, config_stripe_secret FROM settings WHERE company_id = 1"));
|
||||
$config_stripe_enable = intval($stripe_vars['config_stripe_enable']);
|
||||
$config_stripe_secret = nullable_htmlentities($stripe_vars['config_stripe_secret']);
|
||||
|
||||
if (!$config_stripe_enable) {
|
||||
header("Location: autopay.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Get session ID from URL
|
||||
$checkout_session_id = sanitizeInput($_GET['session_id']);
|
||||
|
||||
// Get client's StripeID from database
|
||||
$stripe_client_details = mysqli_fetch_array(mysqli_query($mysqli, "SELECT stripe_id FROM client_stripe WHERE client_id = $session_client_id LIMIT 1"));
|
||||
$client_stripe_id = sanitizeInput($stripe_client_details['stripe_id']);
|
||||
|
||||
try {
|
||||
// Initialize stripe
|
||||
require_once '../vendor/stripe-php-10.5.0/init.php';
|
||||
$stripe = new \Stripe\StripeClient($config_stripe_secret);
|
||||
|
||||
// Retrieve checkout session
|
||||
$checkout_session = $stripe->checkout->sessions->retrieve($checkout_session_id,[]);
|
||||
|
||||
// Get setup intent
|
||||
$setup_intent_id = $checkout_session->setup_intent;
|
||||
|
||||
// Retrieve the setup intent details
|
||||
$setup_intent = $stripe->setupIntents->retrieve($setup_intent_id, []);
|
||||
|
||||
// Get the payment method token
|
||||
$payment_method = sanitizeInput($setup_intent->payment_method);
|
||||
|
||||
// Attach the payment method to the client in Stripe
|
||||
$stripe->paymentMethods->attach($payment_method, ['customer' => $client_stripe_id]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
error_log("Stripe payment error - encountered exception when adding payment method info: $error");
|
||||
logApp("Stripe", "error", "Exception adding payment method: $error");
|
||||
}
|
||||
|
||||
// Update ITFlow
|
||||
mysqli_query($mysqli, "UPDATE client_stripe SET stripe_pm = '$payment_method' WHERE client_id = $session_client_id LIMIT 1");
|
||||
|
||||
// Get some card/payment method details for the email/logging
|
||||
$payment_method_details = $stripe->paymentMethods->retrieve($payment_method);
|
||||
$card_info = sanitizeInput($payment_method_details->card->display_brand) . " " . sanitizeInput($payment_method_details->card->last4);
|
||||
|
||||
// Send email confirmation
|
||||
|
||||
// Company Details & Settings
|
||||
$sql_settings = mysqli_query($mysqli, "SELECT * FROM companies, settings WHERE companies.company_id = settings.company_id AND companies.company_id = 1");
|
||||
$row = mysqli_fetch_array($sql_settings);
|
||||
$company_name = sanitizeInput($row['company_name']);
|
||||
$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_base_url = sanitizeInput($config_base_url);
|
||||
|
||||
if (!empty($config_smtp_host)) {
|
||||
$subject = "Payment method saved";
|
||||
$body = "Hello $session_contact_name,<br><br>We’re writing to confirm that your payment details have been securely stored with Stripe, our trusted payment processor.<br><br>By agreeing to save your payment information, you have authorized us to automatically bill your card ($card_info) for any future invoices. The payment details you’ve provided are securely stored with Stripe and will be used solely for invoices. We do not have access to your full card details.<br><br>You may update or remove your payment information at any time using the portal.<br><br>Thank you for your business!<br><br>--<br>$company_name - Billing Department<br>$config_invoice_from_email<br>$company_phone";
|
||||
|
||||
$data = [
|
||||
[
|
||||
'from' => $config_invoice_from_email,
|
||||
'from_name' => $config_invoice_from_name,
|
||||
'recipient' => $session_contact_email,
|
||||
'recipient_name' => $session_contact_name,
|
||||
'subject' => $subject,
|
||||
'body' => $body,
|
||||
]
|
||||
];
|
||||
|
||||
$mail = addToMailQueue($data);
|
||||
|
||||
}
|
||||
|
||||
// Logging
|
||||
logAction("Stripe", "Update", "$session_contact_name added saved card ($card_info) for future automatic payments (PM: $payment_method)", $session_client_id, $session_client_id);
|
||||
|
||||
// Redirect
|
||||
$_SESSION['alert_message'] = "Card saved - thank you";
|
||||
header('Location: autopay.php');
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['stripe_remove_card'])) {
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Get Stripe vars
|
||||
$stripe_vars = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_stripe_enable, config_stripe_publishable, config_stripe_secret FROM settings WHERE company_id = 1"));
|
||||
$config_stripe_enable = intval($stripe_vars['config_stripe_enable']);
|
||||
$config_stripe_secret = nullable_htmlentities($stripe_vars['config_stripe_secret']);
|
||||
|
||||
if (!$config_stripe_enable) {
|
||||
header("Location: autopay.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
$payment_method = sanitizeInput($_GET['pm']);
|
||||
|
||||
try {
|
||||
// Initialize stripe
|
||||
require_once '../vendor/stripe-php-10.5.0/init.php';
|
||||
$stripe = new \Stripe\StripeClient($config_stripe_secret);
|
||||
|
||||
// Detach PM
|
||||
$stripe->paymentMethods->detach($payment_method, []);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
error_log("Stripe payment error - encountered exception when removing payment method info for $payment_method: $error");
|
||||
logApp("Stripe", "error", "Exception removing payment method for $payment_method: $error");
|
||||
}
|
||||
|
||||
// Remove payment method from ITFlow
|
||||
mysqli_query($mysqli, "UPDATE client_stripe SET stripe_pm = NULL WHERE client_id = $session_client_id LIMIT 1");
|
||||
|
||||
// Logging & Redirect
|
||||
logAction("Stripe", "Update", "$session_contact_name deleted saved card (PM: $payment_method)", $session_client_id, $session_client_id);
|
||||
|
||||
$_SESSION['alert_message'] = "Card removed";
|
||||
header('Location: autopay.php');
|
||||
}
|
||||
50
client/profile.php
Normal file
50
client/profile.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* User profile
|
||||
*/
|
||||
|
||||
header("Content-Security-Policy: default-src 'self'");
|
||||
|
||||
require_once 'includes/inc_all.php';
|
||||
|
||||
?>
|
||||
|
||||
<h2>Profile</h2>
|
||||
|
||||
<p>Name: <?php echo stripslashes(nullable_htmlentities($session_contact_name)); ?></p>
|
||||
<p>Email: <?php echo $session_contact_email ?></p>
|
||||
<p>PIN: <?php echo $session_contact_pin ?></p>
|
||||
<p>Client: <?php echo $session_client_name ?></p>
|
||||
<br>
|
||||
<p>Client Primary Contact: <?php if ($session_contact_primary == 1) {echo "Yes"; } else {echo "No";} ?></p>
|
||||
<p>Client Technical Contact: <?php if ($session_contact_is_technical_contact) {echo "Yes"; } else {echo "No";} ?></p>
|
||||
<p>Client Billing Contact: <?php if ($session_contact_is_billing_contact == $session_contact_id) {echo "Yes"; } else {echo "No";} ?></p>
|
||||
|
||||
|
||||
<p>Login via: <?php echo $_SESSION['login_method'] ?> </p>
|
||||
|
||||
|
||||
<!-- // Show option to change password if auth provider is local -->
|
||||
<?php if ($_SESSION['login_method'] == 'local'): ?>
|
||||
<hr>
|
||||
<div class="col-md-6">
|
||||
<h4>Password</h4>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<div class="form-group">
|
||||
<label>New Password</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-lock"></i></span>
|
||||
</div>
|
||||
<input type="password" class="form-control" minlength="8" required data-toggle="password" name="new_password" placeholder="Leave blank for no change" autocomplete="new-password">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" name="edit_profile" class="btn btn-primary text-bold mt-3"><i class="fas fa-check mr-2"></i>Save password</button>
|
||||
</form>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<?php
|
||||
require_once 'includes/footer.php';
|
||||
|
||||
91
client/quotes.php
Normal file
91
client/quotes.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Quotes for PTC / billing contacts
|
||||
*/
|
||||
|
||||
header("Content-Security-Policy: default-src 'self'");
|
||||
|
||||
require_once "includes/inc_all.php";
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
$quotes_sql = mysqli_query($mysqli, "SELECT * FROM quotes WHERE quote_client_id = $session_client_id AND quote_status != 'Draft' ORDER BY quote_date DESC");
|
||||
?>
|
||||
|
||||
<h3>Quotes</h3>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-10">
|
||||
|
||||
<table class="table tabled-bordered border border-dark">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Scope</th>
|
||||
<th>Amount</th>
|
||||
<th>Date</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<?php
|
||||
while ($row = mysqli_fetch_array($quotes_sql)) {
|
||||
$quote_id = intval($row['quote_id']);
|
||||
$quote_prefix = nullable_htmlentities($row['quote_prefix']);
|
||||
$quote_number = intval($row['quote_number']);
|
||||
$quote_scope = nullable_htmlentities($row['quote_scope']);
|
||||
$quote_status = nullable_htmlentities($row['quote_status']);
|
||||
$quote_date = nullable_htmlentities($row['quote_date']);
|
||||
$quote_amount = floatval($row['quote_amount']);
|
||||
$quote_url_key = nullable_htmlentities($row['quote_url_key']);
|
||||
|
||||
if (empty($quote_scope)) {
|
||||
$quote_scope_display = "-";
|
||||
} else {
|
||||
$quote_scope_display = $quote_scope;
|
||||
}
|
||||
|
||||
if ($quote_status == "Sent") {
|
||||
$quote_badge_color = "warning text-white";
|
||||
} elseif ($quote_status == "Viewed") {
|
||||
$quote_badge_color = "primary";
|
||||
} elseif ($quote_status == "Accepted") {
|
||||
$quote_badge_color = "success";
|
||||
} elseif ($quote_status == "Declined") {
|
||||
$quote_badge_color = "danger";
|
||||
} elseif ($quote_status == "Invoiced") {
|
||||
$quote_badge_color = "info";
|
||||
} else {
|
||||
$quote_badge_color = "secondary";
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<tr>
|
||||
<td><a target="_blank" href="//<?php echo $config_base_url ?>/guest/guest_view_quote.php?quote_id=<?php echo "$quote_id&url_key=$quote_url_key"?>"> <?php echo "$quote_prefix$quote_number"; ?></a></td>
|
||||
<td><?php echo $quote_scope_display; ?></td>
|
||||
<td><?php echo numfmt_format_currency($currency_format, $quote_amount, $session_company_currency); ?></td>
|
||||
<td><?php echo $quote_date; ?></td>
|
||||
<td>
|
||||
<span class="p-2 badge badge-<?php echo $quote_badge_color; ?>">
|
||||
<?php echo $quote_status; ?>
|
||||
</span>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "includes/footer.php";
|
||||
287
client/ticket.php
Normal file
287
client/ticket.php
Normal file
@@ -0,0 +1,287 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Ticket detail page
|
||||
*/
|
||||
|
||||
require_once "includes/inc_all.php";
|
||||
|
||||
//Initialize the HTML Purifier to prevent XSS
|
||||
require "../plugins/htmlpurifier/HTMLPurifier.standalone.php";
|
||||
|
||||
$purifier_config = HTMLPurifier_Config::createDefault();
|
||||
$purifier_config->set('Cache.DefinitionImpl', null); // Disable cache by setting a non-existent directory or an invalid one
|
||||
$purifier_config->set('URI.AllowedSchemes', ['data' => true, 'src' => true, 'http' => true, 'https' => true]);
|
||||
$purifier = new HTMLPurifier($purifier_config);
|
||||
|
||||
$allowed_extensions = array('jpg', 'jpeg', 'gif', 'png', 'webp', 'pdf', 'txt', 'md', 'doc', 'docx', 'csv', 'xls', 'xlsx', 'xlsm', 'zip', 'tar', 'gz');
|
||||
|
||||
if (isset($_GET['id']) && intval($_GET['id'])) {
|
||||
$ticket_id = intval($_GET['id']);
|
||||
|
||||
if ($session_contact_primary == 1 || $session_contact_is_technical_contact) {
|
||||
// For a primary / technical contact viewing all tickets
|
||||
$ticket_sql = mysqli_query($mysqli,
|
||||
"SELECT * FROM tickets
|
||||
LEFT JOIN users on ticket_assigned_to = user_id
|
||||
LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id
|
||||
WHERE ticket_id = $ticket_id AND ticket_client_id = $session_client_id"
|
||||
);
|
||||
|
||||
} else {
|
||||
// For a user viewing their own ticket
|
||||
$ticket_sql = mysqli_query($mysqli,
|
||||
"SELECT * FROM tickets
|
||||
LEFT JOIN users on ticket_assigned_to = user_id
|
||||
LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id
|
||||
WHERE ticket_id = $ticket_id AND ticket_client_id = $session_client_id AND ticket_contact_id = $session_contact_id"
|
||||
);
|
||||
}
|
||||
|
||||
$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']);
|
||||
|
||||
// Get Ticket Attachments (not associated with a specific reply)
|
||||
$sql_ticket_attachments = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT * FROM ticket_attachments
|
||||
WHERE ticket_attachment_reply_id IS NULL
|
||||
AND ticket_attachment_ticket_id = $ticket_id"
|
||||
);
|
||||
|
||||
// Get Tasks
|
||||
$sql_tasks = mysqli_query( $mysqli, "SELECT * FROM tasks WHERE task_ticket_id = $ticket_id ORDER BY task_order ASC, task_id ASC");
|
||||
$task_count = mysqli_num_rows($sql_tasks);
|
||||
|
||||
// Get Completed Task Count
|
||||
$sql_tasks_completed = mysqli_query($mysqli,
|
||||
"SELECT * FROM tasks
|
||||
WHERE task_ticket_id = $ticket_id
|
||||
AND task_completed_at IS NOT NULL"
|
||||
);
|
||||
$completed_task_count = mysqli_num_rows($sql_tasks_completed);
|
||||
|
||||
?>
|
||||
|
||||
<ol class="breadcrumb d-print-none">
|
||||
<li class="breadcrumb-item">
|
||||
<a href="index.php">Home</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="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 ?>
|
||||
<?php
|
||||
if (empty($ticket_resolved_at) && $task_count == $completed_task_count) { ?>
|
||||
<a href="post.php?resolve_ticket=<?php echo $ticket_id; ?>" class="btn btn-sm btn-outline-success float-right text-white confirm-link"><i class="fas fa-fw fa-check text-success"></i> Resolve ticket</a>
|
||||
<?php } ?>
|
||||
</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_closed_at)) { ?>
|
||||
|
||||
<?php if ($task_count) { ?>
|
||||
<strong>Tasks: </strong> <?php echo $completed_task_count . " / " .$task_count ?>
|
||||
<br>
|
||||
<?php } ?>
|
||||
|
||||
<?php if (!empty($ticket_assigned_to)) { ?>
|
||||
<strong>Assigned to: </strong> <?php echo $ticket_assigned_to ?>
|
||||
<?php } ?>
|
||||
|
||||
<?php } ?>
|
||||
</p>
|
||||
<?php echo $ticket_details ?>
|
||||
|
||||
<?php
|
||||
while ($ticket_attachment = mysqli_fetch_array($sql_ticket_attachments)) {
|
||||
$name = nullable_htmlentities($ticket_attachment['ticket_attachment_name']);
|
||||
$ref_name = nullable_htmlentities($ticket_attachment['ticket_attachment_reference_name']);
|
||||
echo "<hr class=''><i class='fas fa-fw fa-paperclip text-secondary mr-1'></i>$name | <a target='_blank' href='https://$config_base_url/uploads/tickets/$ticket_id/$ref_name'><i class='fas fa-fw fa-external-link-alt mr-1'></i>View</a>";
|
||||
}
|
||||
?>
|
||||
</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 -->
|
||||
|
||||
<form action="post.php" enctype="multipart/form-data" method="post">
|
||||
<input type="hidden" name="ticket_id" value="<?php echo $ticket_id ?>">
|
||||
<div class="form-group">
|
||||
<textarea class="form-control tinymce" name="comment" placeholder="Add comments.."></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="file" class="form-control-file" name="file[]" multiple id="fileInput" accept=".jpg, .jpeg, .gif, .png, .webp, .pdf, .txt, .md, .doc, .docx, .odt, .csv, .xls, .xlsx, .ods, .pptx, .odp, .zip, .tar, .gz, .xml, .msg, .json, .wav, .mp3, .ogg, .mov, .mp4, .av1, .ovpn">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" name="add_ticket_comment">Reply</button>
|
||||
</form>
|
||||
|
||||
<?php } elseif (empty($ticket_closed_at)) { ?>
|
||||
<!-- Re-open -->
|
||||
|
||||
<h4>Your ticket has been resolved</h4>
|
||||
|
||||
<div class="col-6">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<a href="post.php?reopen_ticket=<?php echo $ticket_id; ?>" 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="post.php?close_ticket=<?php echo $ticket_id; ?>" class="btn btn-success btn-lg confirm-link"><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>
|
||||
|
||||
<form action="post.php" method="post">
|
||||
<input type="hidden" name="ticket_id" value="<?php echo $ticket_id ?>">
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-lg" name="add_ticket_feedback" value="Good" onclick="this.form.submit()">
|
||||
<span class="fa fa-smile" aria-hidden="true"></span> Good
|
||||
</button>
|
||||
|
||||
<button type="submit" class="btn btn-danger btn-lg" name="add_ticket_feedback" value="Bad" onclick="this.form.submit()">
|
||||
<span class="fa fa-frown" aria-hidden="true"></span> Bad
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<?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/$session_client_id/$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";
|
||||
}
|
||||
|
||||
// Get attachments for this reply
|
||||
$sql_ticket_reply_attachments = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT * FROM ticket_attachments
|
||||
WHERE ticket_attachment_reply_id = $ticket_reply_id
|
||||
AND ticket_attachment_ticket_id = $ticket_id"
|
||||
);
|
||||
?>
|
||||
|
||||
<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; ?>
|
||||
|
||||
<?php
|
||||
while ($ticket_attachment = mysqli_fetch_array($sql_ticket_reply_attachments)) {
|
||||
$name = nullable_htmlentities($ticket_attachment['ticket_attachment_name']);
|
||||
$ref_name = nullable_htmlentities($ticket_attachment['ticket_attachment_reference_name']);
|
||||
echo "<hr><i class='fas fa-fw fa-paperclip text-secondary mr-1'></i>$name | <a target='_blank' href='https://$config_base_url/uploads/tickets/$ticket_id/$ref_name'><i class='fas fa-fw fa-external-link-alt mr-1'></i>View</a>";
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<script src="../js/pretty_content.js"></script>
|
||||
|
||||
<?php
|
||||
} else {
|
||||
echo "Ticket ID not found!";
|
||||
}
|
||||
|
||||
} else {
|
||||
header("Location: index.php");
|
||||
}
|
||||
|
||||
require_once "includes/footer.php";
|
||||
|
||||
|
||||
62
client/ticket_add.php
Normal file
62
client/ticket_add.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* New ticket form
|
||||
*/
|
||||
|
||||
require_once 'includes/inc_all.php';
|
||||
|
||||
?>
|
||||
|
||||
<ol class="breadcrumb d-print-none">
|
||||
<li class="breadcrumb-item">
|
||||
<a href="index.php">Home</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="tickets.php">Tickets</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active">New Ticket</li>
|
||||
</ol>
|
||||
|
||||
<h3>Raise a new ticket</h3>
|
||||
|
||||
<div class="col-md-8">
|
||||
<form action="post.php" method="post">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Subject <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="subject" placeholder="Subject" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Priority <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-thermometer-half"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="priority" required>
|
||||
<option>Low</option>
|
||||
<option>Medium</option>
|
||||
<option>High</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Details <strong class="text-danger">*</strong></label>
|
||||
<textarea class="form-control tinymce" name="details"></textarea>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" name="add_ticket">Raise ticket</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once 'includes/footer.php';
|
||||
|
||||
78
client/ticket_view_all.php
Normal file
78
client/ticket_view_all.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Primary contact view: all tickets
|
||||
*/
|
||||
|
||||
require_once 'includes/inc_all.php';
|
||||
|
||||
|
||||
if ($session_contact_primary == 0 && !$session_contact_is_technical_contact) {
|
||||
header("Location: post.php?logout");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Ticket status from GET
|
||||
if (!isset($_GET['status']) || ($_GET['status']) == 'Open') {
|
||||
// Default to showing open
|
||||
$status = 'Open';
|
||||
$ticket_status_snippet = "ticket_closed_at IS NULL";
|
||||
} elseif (isset($_GET['status']) && ($_GET['status']) == 'Closed') {
|
||||
$status = 'Closed';
|
||||
$ticket_status_snippet = "ticket_closed_at IS NOT NULL";
|
||||
} else {
|
||||
$status = '%';
|
||||
$ticket_status_snippet = "ticket_status LIKE '%'";
|
||||
}
|
||||
|
||||
$all_tickets = mysqli_query($mysqli, "SELECT ticket_id, ticket_prefix, ticket_number, ticket_subject, ticket_status_name, contact_name FROM tickets LEFT JOIN contacts ON ticket_contact_id = contact_id LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id WHERE $ticket_status_snippet AND ticket_client_id = $session_client_id ORDER BY ticket_id DESC");
|
||||
?>
|
||||
|
||||
<h2>All tickets</h2>
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<form method="get">
|
||||
<label>Ticket Status</label>
|
||||
<select class="form-control" name="status" onchange="this.form.submit()">
|
||||
<option value="%" <?php if ($status == "%") {echo "selected";}?> >Any</option>
|
||||
<option value="Open" <?php if ($status == "Open") {echo "selected";}?> >Open</option>
|
||||
<option value="Closed" <?php if ($status == "Closed") {echo "selected";}?> >Closed</option>
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Subject</th>
|
||||
<th scope="col">Contact</th>
|
||||
<th scope="col">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<?php
|
||||
while ($row = mysqli_fetch_array($all_tickets)) {
|
||||
$ticket_id = intval($row['ticket_id']);
|
||||
$ticket_prefix = nullable_htmlentities($row['ticket_prefix']);
|
||||
$ticket_number = intval($row['ticket_number']);
|
||||
$ticket_subject = nullable_htmlentities($row['ticket_subject']);
|
||||
$ticket_status = nullable_htmlentities($row['ticket_status_name']);
|
||||
$ticket_contact_name = nullable_htmlentities($row['contact_name']);
|
||||
|
||||
echo "<tr>";
|
||||
echo "<td> <a href='ticket.php?id=$ticket_id'> $ticket_prefix$ticket_id</a></td>";
|
||||
echo "<td> <a href='ticket.php?id=$ticket_id'> $ticket_subject</a></td>";
|
||||
echo "<td>$ticket_contact_name</td>";
|
||||
echo "<td>$ticket_status</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once 'includes/footer.php';
|
||||
|
||||
113
client/tickets.php
Normal file
113
client/tickets.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
/*
|
||||
* Client Portal
|
||||
* Landing / Home page for the client portal
|
||||
*/
|
||||
|
||||
header("Content-Security-Policy: default-src 'self'");
|
||||
|
||||
require_once "includes/inc_all.php";
|
||||
|
||||
|
||||
// Ticket status from GET
|
||||
if (!isset($_GET['status']) || ($_GET['status']) == 'Open') {
|
||||
// Default to showing open
|
||||
$status = 'Open';
|
||||
$ticket_status_snippet = "ticket_closed_at IS NULL";
|
||||
} elseif (isset($_GET['status']) && ($_GET['status']) == 'Closed') {
|
||||
$status = 'Closed';
|
||||
$ticket_status_snippet = "ticket_closed_at IS NOT NULL";
|
||||
} else {
|
||||
$status = '%';
|
||||
$ticket_status_snippet = "ticket_status LIKE '%'";
|
||||
}
|
||||
|
||||
$contact_tickets = mysqli_query($mysqli, "SELECT ticket_id, ticket_prefix, ticket_number, ticket_subject, ticket_status_name FROM tickets LEFT JOIN contacts ON ticket_contact_id = contact_id LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id WHERE $ticket_status_snippet AND ticket_contact_id = $session_contact_id AND ticket_client_id = $session_client_id ORDER BY ticket_id DESC");
|
||||
|
||||
//Get Total tickets closed
|
||||
$sql_total_tickets_closed = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS total_tickets_closed FROM tickets WHERE ticket_closed_at IS NOT NULL AND ticket_client_id = $session_client_id AND ticket_contact_id = $session_contact_id");
|
||||
$row = mysqli_fetch_array($sql_total_tickets_closed);
|
||||
$total_tickets_closed = intval($row['total_tickets_closed']);
|
||||
|
||||
//Get Total tickets open
|
||||
$sql_total_tickets_open = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS total_tickets_open FROM tickets WHERE ticket_closed_at IS NULL AND ticket_client_id = $session_client_id AND ticket_contact_id = $session_contact_id");
|
||||
$row = mysqli_fetch_array($sql_total_tickets_open);
|
||||
$total_tickets_open = intval($row['total_tickets_open']);
|
||||
|
||||
//Get Total tickets
|
||||
$sql_total_tickets = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS total_tickets FROM tickets WHERE ticket_client_id = $session_client_id AND ticket_contact_id = $session_contact_id");
|
||||
$row = mysqli_fetch_array($sql_total_tickets);
|
||||
$total_tickets = intval($row['total_tickets']);
|
||||
|
||||
|
||||
?>
|
||||
|
||||
<h3>Tickets</h3>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-10">
|
||||
|
||||
<table class="table tabled-bordered border border-dark">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Subject</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<?php
|
||||
while ($row = mysqli_fetch_array($contact_tickets)) {
|
||||
$ticket_id = intval($row['ticket_id']);
|
||||
$ticket_prefix = nullable_htmlentities($row['ticket_prefix']);
|
||||
$ticket_number = intval($row['ticket_number']);
|
||||
$ticket_subject = nullable_htmlentities($row['ticket_subject']);
|
||||
$ticket_status = nullable_htmlentities($row['ticket_status_name']);
|
||||
?>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="ticket.php?id=<?php echo $ticket_id; ?>"><?php echo "$ticket_prefix$ticket_number"; ?></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="ticket.php?id=<?php echo $ticket_id; ?>"><?php echo $ticket_subject; ?></a>
|
||||
</td>
|
||||
<td><?php echo $ticket_status; ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
|
||||
<a href="ticket_add.php" class="btn btn-primary btn-block">New ticket</a>
|
||||
|
||||
<hr>
|
||||
|
||||
<a href="?status=Open" class="btn btn-danger btn-block p-3 mb-3 text-left">My Open tickets | <strong><?php echo $total_tickets_open ?></strong></a>
|
||||
|
||||
<a href="?status=Closed" class="btn btn-success btn-block p-3 mb-3 text-left">Closed tickets | <strong><?php echo $total_tickets_closed ?></strong></a>
|
||||
|
||||
<a href="?status=%" class="btn btn-secondary btn-block p-3 mb-3 text-left">All my tickets | <strong><?php echo $total_tickets ?></strong></a>
|
||||
<?php
|
||||
if ($session_contact_primary == 1 || $session_contact_is_technical_contact) {
|
||||
?>
|
||||
|
||||
<hr>
|
||||
|
||||
<a href="ticket_view_all.php" class="btn btn-dark btn-block p-2 mb-3">All Tickets</a>
|
||||
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once "includes/footer.php";
|
||||
?>
|
||||
Reference in New Issue
Block a user